Merge tag 'perf-tools-2020-08-14' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / spi / spi-hisi-sfc-v3xx.c
1 // 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>
10 #include <linux/dmi.h>
11 #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
20 #define HISI_SFC_V3XX_INT_STAT (0x120)
21 #define HISI_SFC_V3XX_INT_STAT_PP_ERR BIT(2)
22 #define HISI_SFC_V3XX_INT_STAT_ADDR_IACCES BIT(5)
23 #define HISI_SFC_V3XX_INT_CLR (0x12c)
24 #define HISI_SFC_V3XX_INT_CLR_CLEAR (0xff)
25 #define HISI_SFC_V3XX_CMD_CFG (0x300)
26 #define HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT (1 << 17)
27 #define HISI_SFC_V3XX_CMD_CFG_DUAL_IO (2 << 17)
28 #define HISI_SFC_V3XX_CMD_CFG_FULL_DIO (3 << 17)
29 #define HISI_SFC_V3XX_CMD_CFG_QUAD_IN_QUAD_OUT (5 << 17)
30 #define HISI_SFC_V3XX_CMD_CFG_QUAD_IO (6 << 17)
31 #define HISI_SFC_V3XX_CMD_CFG_FULL_QIO (7 << 17)
32 #define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9
33 #define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8)
34 #define HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK BIT(7)
35 #define HISI_SFC_V3XX_CMD_CFG_DUMMY_CNT_OFF 4
36 #define HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK BIT(3)
37 #define HISI_SFC_V3XX_CMD_CFG_CS_SEL_OFF 1
38 #define HISI_SFC_V3XX_CMD_CFG_START_MSK BIT(0)
39 #define HISI_SFC_V3XX_CMD_INS (0x308)
40 #define HISI_SFC_V3XX_CMD_ADDR (0x30c)
41 #define HISI_SFC_V3XX_CMD_DATABUF0 (0x400)
42
43 struct hisi_sfc_v3xx_host {
44         struct device *dev;
45         void __iomem *regbase;
46         int max_cmd_dword;
47 };
48
49 #define HISI_SFC_V3XX_WAIT_TIMEOUT_US           1000000
50 #define HISI_SFC_V3XX_WAIT_POLL_INTERVAL_US     10
51
52 static int hisi_sfc_v3xx_wait_cmd_idle(struct hisi_sfc_v3xx_host *host)
53 {
54         u32 reg;
55
56         return readl_poll_timeout(host->regbase + HISI_SFC_V3XX_CMD_CFG, reg,
57                                   !(reg & HISI_SFC_V3XX_CMD_CFG_START_MSK),
58                                   HISI_SFC_V3XX_WAIT_POLL_INTERVAL_US,
59                                   HISI_SFC_V3XX_WAIT_TIMEOUT_US);
60 }
61
62 static int hisi_sfc_v3xx_adjust_op_size(struct spi_mem *mem,
63                                         struct spi_mem_op *op)
64 {
65         struct spi_device *spi = mem->spi;
66         struct hisi_sfc_v3xx_host *host;
67         uintptr_t addr = (uintptr_t)op->data.buf.in;
68         int max_byte_count;
69
70         host = spi_controller_get_devdata(spi->master);
71
72         max_byte_count = host->max_cmd_dword * 4;
73
74         if (!IS_ALIGNED(addr, 4) && op->data.nbytes >= 4)
75                 op->data.nbytes = 4 - (addr % 4);
76         else if (op->data.nbytes > max_byte_count)
77                 op->data.nbytes = max_byte_count;
78
79         return 0;
80 }
81
82 /*
83  * memcpy_{to,from}io doesn't gurantee 32b accesses - which we require for the
84  * DATABUF registers -so use __io{read,write}32_copy when possible. For
85  * trailing bytes, copy them byte-by-byte from the DATABUF register, as we
86  * can't clobber outside the source/dest buffer.
87  *
88  * For efficient data read/write, we try to put any start 32b unaligned data
89  * into a separate transaction in hisi_sfc_v3xx_adjust_op_size().
90  */
91 static void hisi_sfc_v3xx_read_databuf(struct hisi_sfc_v3xx_host *host,
92                                        u8 *to, unsigned int len)
93 {
94         void __iomem *from;
95         int i;
96
97         from = host->regbase + HISI_SFC_V3XX_CMD_DATABUF0;
98
99         if (IS_ALIGNED((uintptr_t)to, 4)) {
100                 int words = len / 4;
101
102                 __ioread32_copy(to, from, words);
103
104                 len -= words * 4;
105                 if (len) {
106                         u32 val;
107
108                         to += words * 4;
109                         from += words * 4;
110
111                         val = __raw_readl(from);
112
113                         for (i = 0; i < len; i++, val >>= 8, to++)
114                                 *to = (u8)val;
115                 }
116         } else {
117                 for (i = 0; i < DIV_ROUND_UP(len, 4); i++, from += 4) {
118                         u32 val = __raw_readl(from);
119                         int j;
120
121                         for (j = 0; j < 4 && (j + (i * 4) < len);
122                              to++, val >>= 8, j++)
123                                 *to = (u8)val;
124                 }
125         }
126 }
127
128 static void hisi_sfc_v3xx_write_databuf(struct hisi_sfc_v3xx_host *host,
129                                         const u8 *from, unsigned int len)
130 {
131         void __iomem *to;
132         int i;
133
134         to = host->regbase + HISI_SFC_V3XX_CMD_DATABUF0;
135
136         if (IS_ALIGNED((uintptr_t)from, 4)) {
137                 int words = len / 4;
138
139                 __iowrite32_copy(to, from, words);
140
141                 len -= words * 4;
142                 if (len) {
143                         u32 val = 0;
144
145                         to += words * 4;
146                         from += words * 4;
147
148                         for (i = 0; i < len; i++, from++)
149                                 val |= *from << i * 8;
150                         __raw_writel(val, to);
151                 }
152
153         } else {
154                 for (i = 0; i < DIV_ROUND_UP(len, 4); i++, to += 4) {
155                         u32 val = 0;
156                         int j;
157
158                         for (j = 0; j < 4 && (j + (i * 4) < len);
159                              from++, j++)
160                                 val |= *from << j * 8;
161                         __raw_writel(val, to);
162                 }
163         }
164 }
165
166 static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host,
167                                          const struct spi_mem_op *op,
168                                          u8 chip_select)
169 {
170         int ret, len = op->data.nbytes;
171         u32 int_stat, config = 0;
172
173         if (op->addr.nbytes)
174                 config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK;
175
176         switch (op->data.buswidth) {
177         case 0 ... 1:
178                 break;
179         case 2:
180                 if (op->addr.buswidth <= 1) {
181                         config |= HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT;
182                 } else if (op->addr.buswidth == 2) {
183                         if (op->cmd.buswidth <= 1) {
184                                 config |= HISI_SFC_V3XX_CMD_CFG_DUAL_IO;
185                         } else if (op->cmd.buswidth == 2) {
186                                 config |= HISI_SFC_V3XX_CMD_CFG_FULL_DIO;
187                         } else {
188                                 return -EIO;
189                         }
190                 } else {
191                         return -EIO;
192                 }
193                 break;
194         case 4:
195                 if (op->addr.buswidth <= 1) {
196                         config |= HISI_SFC_V3XX_CMD_CFG_QUAD_IN_QUAD_OUT;
197                 } else if (op->addr.buswidth == 4) {
198                         if (op->cmd.buswidth <= 1) {
199                                 config |= HISI_SFC_V3XX_CMD_CFG_QUAD_IO;
200                         } else if (op->cmd.buswidth == 4) {
201                                 config |= HISI_SFC_V3XX_CMD_CFG_FULL_QIO;
202                         } else {
203                                 return -EIO;
204                         }
205                 } else {
206                         return -EIO;
207                 }
208                 break;
209         default:
210                 return -EOPNOTSUPP;
211         }
212
213         if (op->data.dir != SPI_MEM_NO_DATA) {
214                 config |= (len - 1) << HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF;
215                 config |= HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK;
216         }
217
218         if (op->data.dir == SPI_MEM_DATA_OUT)
219                 hisi_sfc_v3xx_write_databuf(host, op->data.buf.out, len);
220         else if (op->data.dir == SPI_MEM_DATA_IN)
221                 config |= HISI_SFC_V3XX_CMD_CFG_RW_MSK;
222
223         config |= op->dummy.nbytes << HISI_SFC_V3XX_CMD_CFG_DUMMY_CNT_OFF |
224                   chip_select << HISI_SFC_V3XX_CMD_CFG_CS_SEL_OFF |
225                   HISI_SFC_V3XX_CMD_CFG_START_MSK;
226
227         writel(op->addr.val, host->regbase + HISI_SFC_V3XX_CMD_ADDR);
228         writel(op->cmd.opcode, host->regbase + HISI_SFC_V3XX_CMD_INS);
229
230         writel(config, host->regbase + HISI_SFC_V3XX_CMD_CFG);
231
232         ret = hisi_sfc_v3xx_wait_cmd_idle(host);
233         if (ret)
234                 return ret;
235
236         /*
237          * The interrupt status register indicates whether an error occurs
238          * after per operation. Check it, and clear the interrupts for
239          * next time judgement.
240          */
241         int_stat = readl(host->regbase + HISI_SFC_V3XX_INT_STAT);
242         writel(HISI_SFC_V3XX_INT_CLR_CLEAR,
243                host->regbase + HISI_SFC_V3XX_INT_CLR);
244
245         if (int_stat & HISI_SFC_V3XX_INT_STAT_ADDR_IACCES) {
246                 dev_err(host->dev, "fail to access protected address\n");
247                 return -EIO;
248         }
249
250         if (int_stat & HISI_SFC_V3XX_INT_STAT_PP_ERR) {
251                 dev_err(host->dev, "page program operation failed\n");
252                 return -EIO;
253         }
254
255         if (op->data.dir == SPI_MEM_DATA_IN)
256                 hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, len);
257
258         return 0;
259 }
260
261 static int hisi_sfc_v3xx_exec_op(struct spi_mem *mem,
262                                  const struct spi_mem_op *op)
263 {
264         struct hisi_sfc_v3xx_host *host;
265         struct spi_device *spi = mem->spi;
266         u8 chip_select = spi->chip_select;
267
268         host = spi_controller_get_devdata(spi->master);
269
270         return hisi_sfc_v3xx_generic_exec_op(host, op, chip_select);
271 }
272
273 static const struct spi_controller_mem_ops hisi_sfc_v3xx_mem_ops = {
274         .adjust_op_size = hisi_sfc_v3xx_adjust_op_size,
275         .exec_op = hisi_sfc_v3xx_exec_op,
276 };
277
278 static int hisi_sfc_v3xx_buswidth_override_bits;
279
280 /*
281  * ACPI FW does not allow us to currently set the device buswidth, so quirk it
282  * depending on the board.
283  */
284 static int __init hisi_sfc_v3xx_dmi_quirk(const struct dmi_system_id *d)
285 {
286         hisi_sfc_v3xx_buswidth_override_bits = SPI_RX_QUAD | SPI_TX_QUAD;
287
288         return 0;
289 }
290
291 static const struct dmi_system_id hisi_sfc_v3xx_dmi_quirk_table[]  = {
292         {
293         .callback = hisi_sfc_v3xx_dmi_quirk,
294         .matches = {
295                 DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
296                 DMI_MATCH(DMI_PRODUCT_NAME, "D06"),
297         },
298         },
299         {
300         .callback = hisi_sfc_v3xx_dmi_quirk,
301         .matches = {
302                 DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
303                 DMI_MATCH(DMI_PRODUCT_NAME, "TaiShan 2280 V2"),
304         },
305         },
306         {
307         .callback = hisi_sfc_v3xx_dmi_quirk,
308         .matches = {
309                 DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
310                 DMI_MATCH(DMI_PRODUCT_NAME, "TaiShan 200 (Model 2280)"),
311         },
312         },
313         {}
314 };
315
316 static int hisi_sfc_v3xx_probe(struct platform_device *pdev)
317 {
318         struct device *dev = &pdev->dev;
319         struct hisi_sfc_v3xx_host *host;
320         struct spi_controller *ctlr;
321         u32 version;
322         int ret;
323
324         ctlr = spi_alloc_master(&pdev->dev, sizeof(*host));
325         if (!ctlr)
326                 return -ENOMEM;
327
328         ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD |
329                           SPI_TX_DUAL | SPI_TX_QUAD;
330
331         ctlr->buswidth_override_bits = hisi_sfc_v3xx_buswidth_override_bits;
332
333         host = spi_controller_get_devdata(ctlr);
334         host->dev = dev;
335
336         platform_set_drvdata(pdev, host);
337
338         host->regbase = devm_platform_ioremap_resource(pdev, 0);
339         if (IS_ERR(host->regbase)) {
340                 ret = PTR_ERR(host->regbase);
341                 goto err_put_master;
342         }
343
344         ctlr->bus_num = -1;
345         ctlr->num_chipselect = 1;
346         ctlr->mem_ops = &hisi_sfc_v3xx_mem_ops;
347
348         version = readl(host->regbase + HISI_SFC_V3XX_VERSION);
349
350         switch (version) {
351         case 0x351:
352                 host->max_cmd_dword = 64;
353                 break;
354         default:
355                 host->max_cmd_dword = 16;
356                 break;
357         }
358
359         ret = devm_spi_register_controller(dev, ctlr);
360         if (ret)
361                 goto err_put_master;
362
363         dev_info(&pdev->dev, "hw version 0x%x\n", version);
364
365         return 0;
366
367 err_put_master:
368         spi_master_put(ctlr);
369         return ret;
370 }
371
372 #if IS_ENABLED(CONFIG_ACPI)
373 static const struct acpi_device_id hisi_sfc_v3xx_acpi_ids[] = {
374         {"HISI0341", 0},
375         {}
376 };
377 MODULE_DEVICE_TABLE(acpi, hisi_sfc_v3xx_acpi_ids);
378 #endif
379
380 static struct platform_driver hisi_sfc_v3xx_spi_driver = {
381         .driver = {
382                 .name   = "hisi-sfc-v3xx",
383                 .acpi_match_table = ACPI_PTR(hisi_sfc_v3xx_acpi_ids),
384         },
385         .probe  = hisi_sfc_v3xx_probe,
386 };
387
388 static int __init hisi_sfc_v3xx_spi_init(void)
389 {
390         dmi_check_system(hisi_sfc_v3xx_dmi_quirk_table);
391
392         return platform_driver_register(&hisi_sfc_v3xx_spi_driver);
393 }
394
395 static void __exit hisi_sfc_v3xx_spi_exit(void)
396 {
397         platform_driver_unregister(&hisi_sfc_v3xx_spi_driver);
398 }
399
400 module_init(hisi_sfc_v3xx_spi_init);
401 module_exit(hisi_sfc_v3xx_spi_exit);
402
403 MODULE_LICENSE("GPL");
404 MODULE_AUTHOR("John Garry <john.garry@huawei.com>");
405 MODULE_DESCRIPTION("HiSilicon SPI NOR V3XX Flash Controller Driver for hi16xx chipsets");