1ba2fc167SMatthew Gerlach // SPDX-License-Identifier: GPL-2.0
2ba2fc167SMatthew Gerlach //
3ba2fc167SMatthew Gerlach // DFL bus driver for Altera SPI Master
4ba2fc167SMatthew Gerlach //
5ba2fc167SMatthew Gerlach // Copyright (C) 2020 Intel Corporation, Inc.
6ba2fc167SMatthew Gerlach //
7ba2fc167SMatthew Gerlach // Authors:
8ba2fc167SMatthew Gerlach // Matthew Gerlach <matthew.gerlach@linux.intel.com>
9ba2fc167SMatthew Gerlach //
10ba2fc167SMatthew Gerlach
11ba2fc167SMatthew Gerlach #include <linux/types.h>
12ba2fc167SMatthew Gerlach #include <linux/kernel.h>
13ba2fc167SMatthew Gerlach #include <linux/module.h>
14ba2fc167SMatthew Gerlach #include <linux/stddef.h>
15ba2fc167SMatthew Gerlach #include <linux/errno.h>
16ba2fc167SMatthew Gerlach #include <linux/platform_device.h>
17ba2fc167SMatthew Gerlach #include <linux/io.h>
18ba2fc167SMatthew Gerlach #include <linux/bitfield.h>
19ba2fc167SMatthew Gerlach #include <linux/io-64-nonatomic-lo-hi.h>
20ba2fc167SMatthew Gerlach #include <linux/regmap.h>
21ba2fc167SMatthew Gerlach #include <linux/spi/spi.h>
22ba2fc167SMatthew Gerlach #include <linux/spi/altera.h>
23ba2fc167SMatthew Gerlach #include <linux/dfl.h>
24ba2fc167SMatthew Gerlach
25ba2fc167SMatthew Gerlach #define FME_FEATURE_ID_MAX10_SPI 0xe
26ba2fc167SMatthew Gerlach #define FME_FEATURE_REV_MAX10_SPI_N5010 0x1
27ba2fc167SMatthew Gerlach
28ba2fc167SMatthew Gerlach #define SPI_CORE_PARAMETER 0x8
29ba2fc167SMatthew Gerlach #define SHIFT_MODE BIT_ULL(1)
30ba2fc167SMatthew Gerlach #define SHIFT_MODE_MSB 0
31ba2fc167SMatthew Gerlach #define SHIFT_MODE_LSB 1
32ba2fc167SMatthew Gerlach #define DATA_WIDTH GENMASK_ULL(7, 2)
33ba2fc167SMatthew Gerlach #define NUM_CHIPSELECT GENMASK_ULL(13, 8)
34ba2fc167SMatthew Gerlach #define CLK_POLARITY BIT_ULL(14)
35ba2fc167SMatthew Gerlach #define CLK_PHASE BIT_ULL(15)
36ba2fc167SMatthew Gerlach #define PERIPHERAL_ID GENMASK_ULL(47, 32)
37ba2fc167SMatthew Gerlach #define SPI_CLK GENMASK_ULL(31, 22)
38ba2fc167SMatthew Gerlach #define SPI_INDIRECT_ACC_OFST 0x10
39ba2fc167SMatthew Gerlach
40ba2fc167SMatthew Gerlach #define INDIRECT_ADDR (SPI_INDIRECT_ACC_OFST+0x0)
41ba2fc167SMatthew Gerlach #define INDIRECT_WR BIT_ULL(8)
42ba2fc167SMatthew Gerlach #define INDIRECT_RD BIT_ULL(9)
43ba2fc167SMatthew Gerlach #define INDIRECT_RD_DATA (SPI_INDIRECT_ACC_OFST+0x8)
44ba2fc167SMatthew Gerlach #define INDIRECT_DATA_MASK GENMASK_ULL(31, 0)
45ba2fc167SMatthew Gerlach #define INDIRECT_DEBUG BIT_ULL(32)
46ba2fc167SMatthew Gerlach #define INDIRECT_WR_DATA (SPI_INDIRECT_ACC_OFST+0x10)
47ba2fc167SMatthew Gerlach #define INDIRECT_TIMEOUT 10000
48ba2fc167SMatthew Gerlach
indirect_bus_reg_read(void * context,unsigned int reg,unsigned int * val)49ba2fc167SMatthew Gerlach static int indirect_bus_reg_read(void *context, unsigned int reg,
50ba2fc167SMatthew Gerlach unsigned int *val)
51ba2fc167SMatthew Gerlach {
52ba2fc167SMatthew Gerlach void __iomem *base = context;
53ba2fc167SMatthew Gerlach int loops;
54ba2fc167SMatthew Gerlach u64 v;
55ba2fc167SMatthew Gerlach
56ba2fc167SMatthew Gerlach writeq((reg >> 2) | INDIRECT_RD, base + INDIRECT_ADDR);
57ba2fc167SMatthew Gerlach
58ba2fc167SMatthew Gerlach loops = 0;
59ba2fc167SMatthew Gerlach while ((readq(base + INDIRECT_ADDR) & INDIRECT_RD) &&
60ba2fc167SMatthew Gerlach (loops++ < INDIRECT_TIMEOUT))
61ba2fc167SMatthew Gerlach cpu_relax();
62ba2fc167SMatthew Gerlach
63ba2fc167SMatthew Gerlach if (loops >= INDIRECT_TIMEOUT) {
64ba2fc167SMatthew Gerlach pr_err("%s timed out %d\n", __func__, loops);
65ba2fc167SMatthew Gerlach return -ETIME;
66ba2fc167SMatthew Gerlach }
67ba2fc167SMatthew Gerlach
68ba2fc167SMatthew Gerlach v = readq(base + INDIRECT_RD_DATA);
69ba2fc167SMatthew Gerlach
70ba2fc167SMatthew Gerlach *val = v & INDIRECT_DATA_MASK;
71ba2fc167SMatthew Gerlach
72ba2fc167SMatthew Gerlach return 0;
73ba2fc167SMatthew Gerlach }
74ba2fc167SMatthew Gerlach
indirect_bus_reg_write(void * context,unsigned int reg,unsigned int val)75ba2fc167SMatthew Gerlach static int indirect_bus_reg_write(void *context, unsigned int reg,
76ba2fc167SMatthew Gerlach unsigned int val)
77ba2fc167SMatthew Gerlach {
78ba2fc167SMatthew Gerlach void __iomem *base = context;
79ba2fc167SMatthew Gerlach int loops;
80ba2fc167SMatthew Gerlach
81ba2fc167SMatthew Gerlach writeq(val, base + INDIRECT_WR_DATA);
82ba2fc167SMatthew Gerlach writeq((reg >> 2) | INDIRECT_WR, base + INDIRECT_ADDR);
83ba2fc167SMatthew Gerlach
84ba2fc167SMatthew Gerlach loops = 0;
85ba2fc167SMatthew Gerlach while ((readq(base + INDIRECT_ADDR) & INDIRECT_WR) &&
86ba2fc167SMatthew Gerlach (loops++ < INDIRECT_TIMEOUT))
87ba2fc167SMatthew Gerlach cpu_relax();
88ba2fc167SMatthew Gerlach
89ba2fc167SMatthew Gerlach if (loops >= INDIRECT_TIMEOUT) {
90ba2fc167SMatthew Gerlach pr_err("%s timed out %d\n", __func__, loops);
91ba2fc167SMatthew Gerlach return -ETIME;
92ba2fc167SMatthew Gerlach }
93ba2fc167SMatthew Gerlach return 0;
94ba2fc167SMatthew Gerlach }
95ba2fc167SMatthew Gerlach
96ba2fc167SMatthew Gerlach static const struct regmap_config indirect_regbus_cfg = {
97ba2fc167SMatthew Gerlach .reg_bits = 32,
98ba2fc167SMatthew Gerlach .reg_stride = 4,
99ba2fc167SMatthew Gerlach .val_bits = 32,
100ba2fc167SMatthew Gerlach .fast_io = true,
101ba2fc167SMatthew Gerlach .max_register = 24,
102ba2fc167SMatthew Gerlach
103ba2fc167SMatthew Gerlach .reg_write = indirect_bus_reg_write,
104ba2fc167SMatthew Gerlach .reg_read = indirect_bus_reg_read,
105ba2fc167SMatthew Gerlach };
106ba2fc167SMatthew Gerlach
config_spi_host(void __iomem * base,struct spi_controller * host)107*ec168190SYang Yingliang static void config_spi_host(void __iomem *base, struct spi_controller *host)
108ba2fc167SMatthew Gerlach {
109ba2fc167SMatthew Gerlach u64 v;
110ba2fc167SMatthew Gerlach
111ba2fc167SMatthew Gerlach v = readq(base + SPI_CORE_PARAMETER);
112ba2fc167SMatthew Gerlach
113*ec168190SYang Yingliang host->mode_bits = SPI_CS_HIGH;
114ba2fc167SMatthew Gerlach if (FIELD_GET(CLK_POLARITY, v))
115*ec168190SYang Yingliang host->mode_bits |= SPI_CPOL;
116ba2fc167SMatthew Gerlach if (FIELD_GET(CLK_PHASE, v))
117*ec168190SYang Yingliang host->mode_bits |= SPI_CPHA;
118ba2fc167SMatthew Gerlach
119*ec168190SYang Yingliang host->num_chipselect = FIELD_GET(NUM_CHIPSELECT, v);
120*ec168190SYang Yingliang host->bits_per_word_mask =
121ba2fc167SMatthew Gerlach SPI_BPW_RANGE_MASK(1, FIELD_GET(DATA_WIDTH, v));
122ba2fc167SMatthew Gerlach }
123ba2fc167SMatthew Gerlach
dfl_spi_altera_probe(struct dfl_device * dfl_dev)124ba2fc167SMatthew Gerlach static int dfl_spi_altera_probe(struct dfl_device *dfl_dev)
125ba2fc167SMatthew Gerlach {
1264f45f340SMartin Hundebøll struct spi_board_info board_info = { 0 };
127ba2fc167SMatthew Gerlach struct device *dev = &dfl_dev->dev;
128*ec168190SYang Yingliang struct spi_controller *host;
129ba2fc167SMatthew Gerlach struct altera_spi *hw;
130ba2fc167SMatthew Gerlach void __iomem *base;
1318e3ca32fSChristophe JAILLET int err;
132ba2fc167SMatthew Gerlach
133*ec168190SYang Yingliang host = devm_spi_alloc_host(dev, sizeof(struct altera_spi));
134*ec168190SYang Yingliang if (!host)
135ba2fc167SMatthew Gerlach return -ENOMEM;
136ba2fc167SMatthew Gerlach
137*ec168190SYang Yingliang host->bus_num = -1;
138ba2fc167SMatthew Gerlach
139*ec168190SYang Yingliang hw = spi_controller_get_devdata(host);
140ba2fc167SMatthew Gerlach
141ba2fc167SMatthew Gerlach hw->dev = dev;
142ba2fc167SMatthew Gerlach
143ba2fc167SMatthew Gerlach base = devm_ioremap_resource(dev, &dfl_dev->mmio_res);
144ba2fc167SMatthew Gerlach
145532259bfSZou Wei if (IS_ERR(base))
146ba2fc167SMatthew Gerlach return PTR_ERR(base);
147ba2fc167SMatthew Gerlach
148*ec168190SYang Yingliang config_spi_host(base, host);
149ba2fc167SMatthew Gerlach dev_dbg(dev, "%s cs %u bpm 0x%x mode 0x%x\n", __func__,
150*ec168190SYang Yingliang host->num_chipselect, host->bits_per_word_mask,
151*ec168190SYang Yingliang host->mode_bits);
152ba2fc167SMatthew Gerlach
153ba2fc167SMatthew Gerlach hw->regmap = devm_regmap_init(dev, NULL, base, &indirect_regbus_cfg);
154ba2fc167SMatthew Gerlach if (IS_ERR(hw->regmap))
155ba2fc167SMatthew Gerlach return PTR_ERR(hw->regmap);
156ba2fc167SMatthew Gerlach
157ba2fc167SMatthew Gerlach hw->irq = -EINVAL;
158ba2fc167SMatthew Gerlach
159*ec168190SYang Yingliang altera_spi_init_host(host);
160ba2fc167SMatthew Gerlach
161*ec168190SYang Yingliang err = devm_spi_register_controller(dev, host);
1628e3ca32fSChristophe JAILLET if (err)
163*ec168190SYang Yingliang return dev_err_probe(dev, err, "%s failed to register spi host\n",
1648e3ca32fSChristophe JAILLET __func__);
165ba2fc167SMatthew Gerlach
1664f45f340SMartin Hundebøll if (dfl_dev->revision == FME_FEATURE_REV_MAX10_SPI_N5010)
1674f45f340SMartin Hundebøll strscpy(board_info.modalias, "m10-n5010", SPI_NAME_SIZE);
1684f45f340SMartin Hundebøll else
1694f45f340SMartin Hundebøll strscpy(board_info.modalias, "m10-d5005", SPI_NAME_SIZE);
1704f45f340SMartin Hundebøll
1714f45f340SMartin Hundebøll board_info.max_speed_hz = 12500000;
1724f45f340SMartin Hundebøll board_info.bus_num = 0;
1734f45f340SMartin Hundebøll board_info.chip_select = 0;
1744f45f340SMartin Hundebøll
175*ec168190SYang Yingliang if (!spi_new_device(host, &board_info)) {
176ba2fc167SMatthew Gerlach dev_err(dev, "%s failed to create SPI device: %s\n",
1774f45f340SMartin Hundebøll __func__, board_info.modalias);
178ba2fc167SMatthew Gerlach }
179ba2fc167SMatthew Gerlach
180ba2fc167SMatthew Gerlach return 0;
181ba2fc167SMatthew Gerlach }
182ba2fc167SMatthew Gerlach
183ba2fc167SMatthew Gerlach static const struct dfl_device_id dfl_spi_altera_ids[] = {
184ba2fc167SMatthew Gerlach { FME_ID, FME_FEATURE_ID_MAX10_SPI },
185ba2fc167SMatthew Gerlach { }
186ba2fc167SMatthew Gerlach };
187ba2fc167SMatthew Gerlach
188ba2fc167SMatthew Gerlach static struct dfl_driver dfl_spi_altera_driver = {
189ba2fc167SMatthew Gerlach .drv = {
190ba2fc167SMatthew Gerlach .name = "dfl-spi-altera",
191ba2fc167SMatthew Gerlach },
192ba2fc167SMatthew Gerlach .id_table = dfl_spi_altera_ids,
193ba2fc167SMatthew Gerlach .probe = dfl_spi_altera_probe,
194ba2fc167SMatthew Gerlach };
195ba2fc167SMatthew Gerlach
196ba2fc167SMatthew Gerlach module_dfl_driver(dfl_spi_altera_driver);
197ba2fc167SMatthew Gerlach
198ba2fc167SMatthew Gerlach MODULE_DEVICE_TABLE(dfl, dfl_spi_altera_ids);
199ba2fc167SMatthew Gerlach MODULE_DESCRIPTION("DFL spi altera driver");
200ba2fc167SMatthew Gerlach MODULE_AUTHOR("Intel Corporation");
201ba2fc167SMatthew Gerlach MODULE_LICENSE("GPL v2");
202