blob: 4a241d7b458e6e6d9ead29c3ed4a069bc349f611 [file] [log] [blame]
John Garrya2ca53b2019-12-09 22:08:09 +08001// SPDX-License-Identifier: GPL-2.0-only
2//
3// HiSilicon SPI NOR V3XX Flash Controller Driver for hi16xx chipsets
4//
5// Copyright (c) 2019 HiSilicon Technologies Co., Ltd.
6// Author: John Garry <john.garry@huawei.com>
7
8#include <linux/acpi.h>
9#include <linux/bitops.h>
John Garry34e608b2020-02-28 23:18:51 +080010#include <linux/dmi.h>
John Garrya2ca53b2019-12-09 22:08:09 +080011#include <linux/iopoll.h>
12#include <linux/module.h>
13#include <linux/platform_device.h>
14#include <linux/slab.h>
15#include <linux/spi/spi.h>
16#include <linux/spi/spi-mem.h>
17
18#define HISI_SFC_V3XX_VERSION (0x1f8)
19
Yicong Yang59fc9ad2020-04-17 15:48:27 +080020#define HISI_SFC_V3XX_INT_STAT (0x120)
Yicong Yang59fc9ad2020-04-17 15:48:27 +080021#define HISI_SFC_V3XX_INT_CLR (0x12c)
John Garrya2ca53b2019-12-09 22:08:09 +080022#define HISI_SFC_V3XX_CMD_CFG (0x300)
23#define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9
24#define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8)
25#define HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK BIT(7)
26#define HISI_SFC_V3XX_CMD_CFG_DUMMY_CNT_OFF 4
27#define HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK BIT(3)
28#define HISI_SFC_V3XX_CMD_CFG_CS_SEL_OFF 1
29#define HISI_SFC_V3XX_CMD_CFG_START_MSK BIT(0)
30#define HISI_SFC_V3XX_CMD_INS (0x308)
31#define HISI_SFC_V3XX_CMD_ADDR (0x30c)
32#define HISI_SFC_V3XX_CMD_DATABUF0 (0x400)
33
Yicong Yangaac6edf2020-09-24 20:24:29 +080034/* Common definition of interrupt bit masks */
35#define HISI_SFC_V3XX_INT_MASK_ALL (0x1ff) /* all the masks */
36#define HISI_SFC_V3XX_INT_MASK_PP_ERR BIT(2) /* page progrom error */
37#define HISI_SFC_V3XX_INT_MASK_IACCES BIT(5) /* error visiting inaccessible/
38 * protected address
39 */
40
Yicong Yang2c8af6a2020-09-24 20:24:27 +080041/* IO Mode definition in HISI_SFC_V3XX_CMD_CFG */
42#define HISI_SFC_V3XX_STD (0 << 17)
43#define HISI_SFC_V3XX_DIDO (1 << 17)
44#define HISI_SFC_V3XX_DIO (2 << 17)
45#define HISI_SFC_V3XX_FULL_DIO (3 << 17)
46#define HISI_SFC_V3XX_QIQO (5 << 17)
47#define HISI_SFC_V3XX_QIO (6 << 17)
48#define HISI_SFC_V3XX_FULL_QIO (7 << 17)
49
50/*
51 * The IO modes lookup table. hisi_sfc_v3xx_io_modes[(z - 1) / 2][y / 2][x / 2]
52 * stands for x-y-z mode, as described in SFDP terminology. -EIO indicates
53 * an invalid mode.
54 */
55static const int hisi_sfc_v3xx_io_modes[2][3][3] = {
56 {
57 { HISI_SFC_V3XX_DIDO, HISI_SFC_V3XX_DIDO, HISI_SFC_V3XX_DIDO },
58 { HISI_SFC_V3XX_DIO, HISI_SFC_V3XX_FULL_DIO, -EIO },
59 { -EIO, -EIO, -EIO },
60 },
61 {
62 { HISI_SFC_V3XX_QIQO, HISI_SFC_V3XX_QIQO, HISI_SFC_V3XX_QIQO },
63 { -EIO, -EIO, -EIO },
64 { HISI_SFC_V3XX_QIO, -EIO, HISI_SFC_V3XX_FULL_QIO },
65 },
66};
67
John Garrya2ca53b2019-12-09 22:08:09 +080068struct hisi_sfc_v3xx_host {
69 struct device *dev;
70 void __iomem *regbase;
71 int max_cmd_dword;
72};
73
74#define HISI_SFC_V3XX_WAIT_TIMEOUT_US 1000000
75#define HISI_SFC_V3XX_WAIT_POLL_INTERVAL_US 10
76
77static int hisi_sfc_v3xx_wait_cmd_idle(struct hisi_sfc_v3xx_host *host)
78{
79 u32 reg;
80
81 return readl_poll_timeout(host->regbase + HISI_SFC_V3XX_CMD_CFG, reg,
82 !(reg & HISI_SFC_V3XX_CMD_CFG_START_MSK),
83 HISI_SFC_V3XX_WAIT_POLL_INTERVAL_US,
84 HISI_SFC_V3XX_WAIT_TIMEOUT_US);
85}
86
87static int hisi_sfc_v3xx_adjust_op_size(struct spi_mem *mem,
88 struct spi_mem_op *op)
89{
90 struct spi_device *spi = mem->spi;
91 struct hisi_sfc_v3xx_host *host;
92 uintptr_t addr = (uintptr_t)op->data.buf.in;
93 int max_byte_count;
94
95 host = spi_controller_get_devdata(spi->master);
96
97 max_byte_count = host->max_cmd_dword * 4;
98
99 if (!IS_ALIGNED(addr, 4) && op->data.nbytes >= 4)
100 op->data.nbytes = 4 - (addr % 4);
101 else if (op->data.nbytes > max_byte_count)
102 op->data.nbytes = max_byte_count;
103
104 return 0;
105}
106
107/*
Yicong Yang2c8af6a2020-09-24 20:24:27 +0800108 * The controller only supports Standard SPI mode, Duall mode and
109 * Quad mode. Double sanitize the ops here to avoid OOB access.
110 */
111static bool hisi_sfc_v3xx_supports_op(struct spi_mem *mem,
112 const struct spi_mem_op *op)
113{
114 if (op->data.buswidth > 4 || op->dummy.buswidth > 4 ||
115 op->addr.buswidth > 4 || op->cmd.buswidth > 4)
116 return false;
117
118 return spi_mem_default_supports_op(mem, op);
119}
120
121/*
John Garrya2ca53b2019-12-09 22:08:09 +0800122 * memcpy_{to,from}io doesn't gurantee 32b accesses - which we require for the
123 * DATABUF registers -so use __io{read,write}32_copy when possible. For
124 * trailing bytes, copy them byte-by-byte from the DATABUF register, as we
125 * can't clobber outside the source/dest buffer.
126 *
127 * For efficient data read/write, we try to put any start 32b unaligned data
128 * into a separate transaction in hisi_sfc_v3xx_adjust_op_size().
129 */
130static void hisi_sfc_v3xx_read_databuf(struct hisi_sfc_v3xx_host *host,
131 u8 *to, unsigned int len)
132{
133 void __iomem *from;
134 int i;
135
136 from = host->regbase + HISI_SFC_V3XX_CMD_DATABUF0;
137
138 if (IS_ALIGNED((uintptr_t)to, 4)) {
139 int words = len / 4;
140
141 __ioread32_copy(to, from, words);
142
143 len -= words * 4;
144 if (len) {
145 u32 val;
146
147 to += words * 4;
148 from += words * 4;
149
150 val = __raw_readl(from);
151
152 for (i = 0; i < len; i++, val >>= 8, to++)
153 *to = (u8)val;
154 }
155 } else {
156 for (i = 0; i < DIV_ROUND_UP(len, 4); i++, from += 4) {
157 u32 val = __raw_readl(from);
158 int j;
159
160 for (j = 0; j < 4 && (j + (i * 4) < len);
161 to++, val >>= 8, j++)
162 *to = (u8)val;
163 }
164 }
165}
166
167static void hisi_sfc_v3xx_write_databuf(struct hisi_sfc_v3xx_host *host,
168 const u8 *from, unsigned int len)
169{
170 void __iomem *to;
171 int i;
172
173 to = host->regbase + HISI_SFC_V3XX_CMD_DATABUF0;
174
175 if (IS_ALIGNED((uintptr_t)from, 4)) {
176 int words = len / 4;
177
178 __iowrite32_copy(to, from, words);
179
180 len -= words * 4;
181 if (len) {
182 u32 val = 0;
183
184 to += words * 4;
185 from += words * 4;
186
187 for (i = 0; i < len; i++, from++)
188 val |= *from << i * 8;
189 __raw_writel(val, to);
190 }
191
192 } else {
193 for (i = 0; i < DIV_ROUND_UP(len, 4); i++, to += 4) {
194 u32 val = 0;
195 int j;
196
197 for (j = 0; j < 4 && (j + (i * 4) < len);
198 from++, j++)
199 val |= *from << j * 8;
200 __raw_writel(val, to);
201 }
202 }
203}
204
Yicong Yangf6d27372020-09-24 20:24:28 +0800205static int hisi_sfc_v3xx_start_bus(struct hisi_sfc_v3xx_host *host,
206 const struct spi_mem_op *op,
207 u8 chip_select)
John Garrya2ca53b2019-12-09 22:08:09 +0800208{
Yicong Yangf6d27372020-09-24 20:24:28 +0800209 int len = op->data.nbytes, buswidth_mode;
210 u32 config = 0;
John Garrya2ca53b2019-12-09 22:08:09 +0800211
212 if (op->addr.nbytes)
213 config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK;
214
Yicong Yang2c8af6a2020-09-24 20:24:27 +0800215 if (op->data.buswidth == 0 || op->data.buswidth == 1) {
216 buswidth_mode = HISI_SFC_V3XX_STD;
217 } else {
218 int data_idx, addr_idx, cmd_idx;
219
220 data_idx = (op->data.buswidth - 1) / 2;
221 addr_idx = op->addr.buswidth / 2;
222 cmd_idx = op->cmd.buswidth / 2;
223 buswidth_mode = hisi_sfc_v3xx_io_modes[data_idx][addr_idx][cmd_idx];
John Garry8fe21d62020-02-28 23:18:50 +0800224 }
Yicong Yang2c8af6a2020-09-24 20:24:27 +0800225 if (buswidth_mode < 0)
226 return buswidth_mode;
227 config |= buswidth_mode;
John Garry8fe21d62020-02-28 23:18:50 +0800228
John Garrya2ca53b2019-12-09 22:08:09 +0800229 if (op->data.dir != SPI_MEM_NO_DATA) {
230 config |= (len - 1) << HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF;
231 config |= HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK;
232 }
233
Yicong Yangf6d27372020-09-24 20:24:28 +0800234 if (op->data.dir == SPI_MEM_DATA_IN)
John Garrya2ca53b2019-12-09 22:08:09 +0800235 config |= HISI_SFC_V3XX_CMD_CFG_RW_MSK;
236
237 config |= op->dummy.nbytes << HISI_SFC_V3XX_CMD_CFG_DUMMY_CNT_OFF |
238 chip_select << HISI_SFC_V3XX_CMD_CFG_CS_SEL_OFF |
239 HISI_SFC_V3XX_CMD_CFG_START_MSK;
240
241 writel(op->addr.val, host->regbase + HISI_SFC_V3XX_CMD_ADDR);
242 writel(op->cmd.opcode, host->regbase + HISI_SFC_V3XX_CMD_INS);
243
244 writel(config, host->regbase + HISI_SFC_V3XX_CMD_CFG);
245
Yicong Yangf6d27372020-09-24 20:24:28 +0800246 return 0;
247}
248
249static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host,
250 const struct spi_mem_op *op,
251 u8 chip_select)
252{
253 u32 int_stat;
254 int ret;
255
256 if (op->data.dir == SPI_MEM_DATA_OUT)
257 hisi_sfc_v3xx_write_databuf(host, op->data.buf.out, op->data.nbytes);
258
259 ret = hisi_sfc_v3xx_start_bus(host, op, chip_select);
260 if (ret)
261 return ret;
262
John Garrya2ca53b2019-12-09 22:08:09 +0800263 ret = hisi_sfc_v3xx_wait_cmd_idle(host);
264 if (ret)
265 return ret;
266
Yicong Yang59fc9ad2020-04-17 15:48:27 +0800267 /*
268 * The interrupt status register indicates whether an error occurs
269 * after per operation. Check it, and clear the interrupts for
270 * next time judgement.
271 */
272 int_stat = readl(host->regbase + HISI_SFC_V3XX_INT_STAT);
Yicong Yangaac6edf2020-09-24 20:24:29 +0800273 writel(HISI_SFC_V3XX_INT_MASK_ALL,
Yicong Yang59fc9ad2020-04-17 15:48:27 +0800274 host->regbase + HISI_SFC_V3XX_INT_CLR);
275
Yicong Yangaac6edf2020-09-24 20:24:29 +0800276 if (int_stat & HISI_SFC_V3XX_INT_MASK_IACCES) {
Yicong Yang59fc9ad2020-04-17 15:48:27 +0800277 dev_err(host->dev, "fail to access protected address\n");
278 return -EIO;
279 }
280
Yicong Yangaac6edf2020-09-24 20:24:29 +0800281 if (int_stat & HISI_SFC_V3XX_INT_MASK_PP_ERR) {
Yicong Yang59fc9ad2020-04-17 15:48:27 +0800282 dev_err(host->dev, "page program operation failed\n");
283 return -EIO;
284 }
285
John Garrya2ca53b2019-12-09 22:08:09 +0800286 if (op->data.dir == SPI_MEM_DATA_IN)
Yicong Yangf6d27372020-09-24 20:24:28 +0800287 hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, op->data.nbytes);
John Garrya2ca53b2019-12-09 22:08:09 +0800288
289 return 0;
290}
291
292static int hisi_sfc_v3xx_exec_op(struct spi_mem *mem,
293 const struct spi_mem_op *op)
294{
295 struct hisi_sfc_v3xx_host *host;
296 struct spi_device *spi = mem->spi;
297 u8 chip_select = spi->chip_select;
298
299 host = spi_controller_get_devdata(spi->master);
300
301 return hisi_sfc_v3xx_generic_exec_op(host, op, chip_select);
302}
303
304static const struct spi_controller_mem_ops hisi_sfc_v3xx_mem_ops = {
305 .adjust_op_size = hisi_sfc_v3xx_adjust_op_size,
Yicong Yang2c8af6a2020-09-24 20:24:27 +0800306 .supports_op = hisi_sfc_v3xx_supports_op,
John Garrya2ca53b2019-12-09 22:08:09 +0800307 .exec_op = hisi_sfc_v3xx_exec_op,
308};
309
John Garry34e608b2020-02-28 23:18:51 +0800310static int hisi_sfc_v3xx_buswidth_override_bits;
311
312/*
313 * ACPI FW does not allow us to currently set the device buswidth, so quirk it
314 * depending on the board.
315 */
316static int __init hisi_sfc_v3xx_dmi_quirk(const struct dmi_system_id *d)
317{
318 hisi_sfc_v3xx_buswidth_override_bits = SPI_RX_QUAD | SPI_TX_QUAD;
319
320 return 0;
321}
322
323static const struct dmi_system_id hisi_sfc_v3xx_dmi_quirk_table[] = {
324 {
325 .callback = hisi_sfc_v3xx_dmi_quirk,
326 .matches = {
327 DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
328 DMI_MATCH(DMI_PRODUCT_NAME, "D06"),
329 },
330 },
331 {
332 .callback = hisi_sfc_v3xx_dmi_quirk,
333 .matches = {
334 DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
335 DMI_MATCH(DMI_PRODUCT_NAME, "TaiShan 2280 V2"),
336 },
337 },
338 {
339 .callback = hisi_sfc_v3xx_dmi_quirk,
340 .matches = {
341 DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
342 DMI_MATCH(DMI_PRODUCT_NAME, "TaiShan 200 (Model 2280)"),
343 },
344 },
345 {}
346};
347
John Garrya2ca53b2019-12-09 22:08:09 +0800348static int hisi_sfc_v3xx_probe(struct platform_device *pdev)
349{
350 struct device *dev = &pdev->dev;
351 struct hisi_sfc_v3xx_host *host;
352 struct spi_controller *ctlr;
353 u32 version;
354 int ret;
355
356 ctlr = spi_alloc_master(&pdev->dev, sizeof(*host));
357 if (!ctlr)
358 return -ENOMEM;
359
360 ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD |
361 SPI_TX_DUAL | SPI_TX_QUAD;
362
John Garry34e608b2020-02-28 23:18:51 +0800363 ctlr->buswidth_override_bits = hisi_sfc_v3xx_buswidth_override_bits;
364
John Garrya2ca53b2019-12-09 22:08:09 +0800365 host = spi_controller_get_devdata(ctlr);
366 host->dev = dev;
367
368 platform_set_drvdata(pdev, host);
369
370 host->regbase = devm_platform_ioremap_resource(pdev, 0);
371 if (IS_ERR(host->regbase)) {
372 ret = PTR_ERR(host->regbase);
373 goto err_put_master;
374 }
375
376 ctlr->bus_num = -1;
377 ctlr->num_chipselect = 1;
378 ctlr->mem_ops = &hisi_sfc_v3xx_mem_ops;
379
380 version = readl(host->regbase + HISI_SFC_V3XX_VERSION);
381
382 switch (version) {
383 case 0x351:
384 host->max_cmd_dword = 64;
385 break;
386 default:
387 host->max_cmd_dword = 16;
388 break;
389 }
390
391 ret = devm_spi_register_controller(dev, ctlr);
392 if (ret)
393 goto err_put_master;
394
395 dev_info(&pdev->dev, "hw version 0x%x\n", version);
396
397 return 0;
398
399err_put_master:
400 spi_master_put(ctlr);
401 return ret;
402}
403
404#if IS_ENABLED(CONFIG_ACPI)
405static const struct acpi_device_id hisi_sfc_v3xx_acpi_ids[] = {
406 {"HISI0341", 0},
407 {}
408};
409MODULE_DEVICE_TABLE(acpi, hisi_sfc_v3xx_acpi_ids);
410#endif
411
412static struct platform_driver hisi_sfc_v3xx_spi_driver = {
413 .driver = {
414 .name = "hisi-sfc-v3xx",
415 .acpi_match_table = ACPI_PTR(hisi_sfc_v3xx_acpi_ids),
416 },
417 .probe = hisi_sfc_v3xx_probe,
418};
419
John Garry34e608b2020-02-28 23:18:51 +0800420static int __init hisi_sfc_v3xx_spi_init(void)
421{
422 dmi_check_system(hisi_sfc_v3xx_dmi_quirk_table);
423
424 return platform_driver_register(&hisi_sfc_v3xx_spi_driver);
425}
426
427static void __exit hisi_sfc_v3xx_spi_exit(void)
428{
429 platform_driver_unregister(&hisi_sfc_v3xx_spi_driver);
430}
431
432module_init(hisi_sfc_v3xx_spi_init);
433module_exit(hisi_sfc_v3xx_spi_exit);
John Garrya2ca53b2019-12-09 22:08:09 +0800434
435MODULE_LICENSE("GPL");
436MODULE_AUTHOR("John Garry <john.garry@huawei.com>");
437MODULE_DESCRIPTION("HiSilicon SPI NOR V3XX Flash Controller Driver for hi16xx chipsets");