Merge tag 'amd-drm-fixes-5.6-2020-02-26' of git://people.freedesktop.org/~agd5f/linux...
[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/iopoll.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/slab.h>
14 #include <linux/spi/spi.h>
15 #include <linux/spi/spi-mem.h>
16
17 #define HISI_SFC_V3XX_VERSION (0x1f8)
18
19 #define HISI_SFC_V3XX_CMD_CFG (0x300)
20 #define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9
21 #define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8)
22 #define HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK BIT(7)
23 #define HISI_SFC_V3XX_CMD_CFG_DUMMY_CNT_OFF 4
24 #define HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK BIT(3)
25 #define HISI_SFC_V3XX_CMD_CFG_CS_SEL_OFF 1
26 #define HISI_SFC_V3XX_CMD_CFG_START_MSK BIT(0)
27 #define HISI_SFC_V3XX_CMD_INS (0x308)
28 #define HISI_SFC_V3XX_CMD_ADDR (0x30c)
29 #define HISI_SFC_V3XX_CMD_DATABUF0 (0x400)
30
31 struct hisi_sfc_v3xx_host {
32         struct device *dev;
33         void __iomem *regbase;
34         int max_cmd_dword;
35 };
36
37 #define HISI_SFC_V3XX_WAIT_TIMEOUT_US           1000000
38 #define HISI_SFC_V3XX_WAIT_POLL_INTERVAL_US     10
39
40 static int hisi_sfc_v3xx_wait_cmd_idle(struct hisi_sfc_v3xx_host *host)
41 {
42         u32 reg;
43
44         return readl_poll_timeout(host->regbase + HISI_SFC_V3XX_CMD_CFG, reg,
45                                   !(reg & HISI_SFC_V3XX_CMD_CFG_START_MSK),
46                                   HISI_SFC_V3XX_WAIT_POLL_INTERVAL_US,
47                                   HISI_SFC_V3XX_WAIT_TIMEOUT_US);
48 }
49
50 static int hisi_sfc_v3xx_adjust_op_size(struct spi_mem *mem,
51                                         struct spi_mem_op *op)
52 {
53         struct spi_device *spi = mem->spi;
54         struct hisi_sfc_v3xx_host *host;
55         uintptr_t addr = (uintptr_t)op->data.buf.in;
56         int max_byte_count;
57
58         host = spi_controller_get_devdata(spi->master);
59
60         max_byte_count = host->max_cmd_dword * 4;
61
62         if (!IS_ALIGNED(addr, 4) && op->data.nbytes >= 4)
63                 op->data.nbytes = 4 - (addr % 4);
64         else if (op->data.nbytes > max_byte_count)
65                 op->data.nbytes = max_byte_count;
66
67         return 0;
68 }
69
70 /*
71  * memcpy_{to,from}io doesn't gurantee 32b accesses - which we require for the
72  * DATABUF registers -so use __io{read,write}32_copy when possible. For
73  * trailing bytes, copy them byte-by-byte from the DATABUF register, as we
74  * can't clobber outside the source/dest buffer.
75  *
76  * For efficient data read/write, we try to put any start 32b unaligned data
77  * into a separate transaction in hisi_sfc_v3xx_adjust_op_size().
78  */
79 static void hisi_sfc_v3xx_read_databuf(struct hisi_sfc_v3xx_host *host,
80                                        u8 *to, unsigned int len)
81 {
82         void __iomem *from;
83         int i;
84
85         from = host->regbase + HISI_SFC_V3XX_CMD_DATABUF0;
86
87         if (IS_ALIGNED((uintptr_t)to, 4)) {
88                 int words = len / 4;
89
90                 __ioread32_copy(to, from, words);
91
92                 len -= words * 4;
93                 if (len) {
94                         u32 val;
95
96                         to += words * 4;
97                         from += words * 4;
98
99                         val = __raw_readl(from);
100
101                         for (i = 0; i < len; i++, val >>= 8, to++)
102                                 *to = (u8)val;
103                 }
104         } else {
105                 for (i = 0; i < DIV_ROUND_UP(len, 4); i++, from += 4) {
106                         u32 val = __raw_readl(from);
107                         int j;
108
109                         for (j = 0; j < 4 && (j + (i * 4) < len);
110                              to++, val >>= 8, j++)
111                                 *to = (u8)val;
112                 }
113         }
114 }
115
116 static void hisi_sfc_v3xx_write_databuf(struct hisi_sfc_v3xx_host *host,
117                                         const u8 *from, unsigned int len)
118 {
119         void __iomem *to;
120         int i;
121
122         to = host->regbase + HISI_SFC_V3XX_CMD_DATABUF0;
123
124         if (IS_ALIGNED((uintptr_t)from, 4)) {
125                 int words = len / 4;
126
127                 __iowrite32_copy(to, from, words);
128
129                 len -= words * 4;
130                 if (len) {
131                         u32 val = 0;
132
133                         to += words * 4;
134                         from += words * 4;
135
136                         for (i = 0; i < len; i++, from++)
137                                 val |= *from << i * 8;
138                         __raw_writel(val, to);
139                 }
140
141         } else {
142                 for (i = 0; i < DIV_ROUND_UP(len, 4); i++, to += 4) {
143                         u32 val = 0;
144                         int j;
145
146                         for (j = 0; j < 4 && (j + (i * 4) < len);
147                              from++, j++)
148                                 val |= *from << j * 8;
149                         __raw_writel(val, to);
150                 }
151         }
152 }
153
154 static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host,
155                                          const struct spi_mem_op *op,
156                                          u8 chip_select)
157 {
158         int ret, len = op->data.nbytes;
159         u32 config = 0;
160
161         if (op->addr.nbytes)
162                 config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK;
163
164         if (op->data.dir != SPI_MEM_NO_DATA) {
165                 config |= (len - 1) << HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF;
166                 config |= HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK;
167         }
168
169         if (op->data.dir == SPI_MEM_DATA_OUT)
170                 hisi_sfc_v3xx_write_databuf(host, op->data.buf.out, len);
171         else if (op->data.dir == SPI_MEM_DATA_IN)
172                 config |= HISI_SFC_V3XX_CMD_CFG_RW_MSK;
173
174         config |= op->dummy.nbytes << HISI_SFC_V3XX_CMD_CFG_DUMMY_CNT_OFF |
175                   chip_select << HISI_SFC_V3XX_CMD_CFG_CS_SEL_OFF |
176                   HISI_SFC_V3XX_CMD_CFG_START_MSK;
177
178         writel(op->addr.val, host->regbase + HISI_SFC_V3XX_CMD_ADDR);
179         writel(op->cmd.opcode, host->regbase + HISI_SFC_V3XX_CMD_INS);
180
181         writel(config, host->regbase + HISI_SFC_V3XX_CMD_CFG);
182
183         ret = hisi_sfc_v3xx_wait_cmd_idle(host);
184         if (ret)
185                 return ret;
186
187         if (op->data.dir == SPI_MEM_DATA_IN)
188                 hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, len);
189
190         return 0;
191 }
192
193 static int hisi_sfc_v3xx_exec_op(struct spi_mem *mem,
194                                  const struct spi_mem_op *op)
195 {
196         struct hisi_sfc_v3xx_host *host;
197         struct spi_device *spi = mem->spi;
198         u8 chip_select = spi->chip_select;
199
200         host = spi_controller_get_devdata(spi->master);
201
202         return hisi_sfc_v3xx_generic_exec_op(host, op, chip_select);
203 }
204
205 static const struct spi_controller_mem_ops hisi_sfc_v3xx_mem_ops = {
206         .adjust_op_size = hisi_sfc_v3xx_adjust_op_size,
207         .exec_op = hisi_sfc_v3xx_exec_op,
208 };
209
210 static int hisi_sfc_v3xx_probe(struct platform_device *pdev)
211 {
212         struct device *dev = &pdev->dev;
213         struct hisi_sfc_v3xx_host *host;
214         struct spi_controller *ctlr;
215         u32 version;
216         int ret;
217
218         ctlr = spi_alloc_master(&pdev->dev, sizeof(*host));
219         if (!ctlr)
220                 return -ENOMEM;
221
222         ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD |
223                           SPI_TX_DUAL | SPI_TX_QUAD;
224
225         host = spi_controller_get_devdata(ctlr);
226         host->dev = dev;
227
228         platform_set_drvdata(pdev, host);
229
230         host->regbase = devm_platform_ioremap_resource(pdev, 0);
231         if (IS_ERR(host->regbase)) {
232                 ret = PTR_ERR(host->regbase);
233                 goto err_put_master;
234         }
235
236         ctlr->bus_num = -1;
237         ctlr->num_chipselect = 1;
238         ctlr->mem_ops = &hisi_sfc_v3xx_mem_ops;
239
240         version = readl(host->regbase + HISI_SFC_V3XX_VERSION);
241
242         switch (version) {
243         case 0x351:
244                 host->max_cmd_dword = 64;
245                 break;
246         default:
247                 host->max_cmd_dword = 16;
248                 break;
249         }
250
251         ret = devm_spi_register_controller(dev, ctlr);
252         if (ret)
253                 goto err_put_master;
254
255         dev_info(&pdev->dev, "hw version 0x%x\n", version);
256
257         return 0;
258
259 err_put_master:
260         spi_master_put(ctlr);
261         return ret;
262 }
263
264 #if IS_ENABLED(CONFIG_ACPI)
265 static const struct acpi_device_id hisi_sfc_v3xx_acpi_ids[] = {
266         {"HISI0341", 0},
267         {}
268 };
269 MODULE_DEVICE_TABLE(acpi, hisi_sfc_v3xx_acpi_ids);
270 #endif
271
272 static struct platform_driver hisi_sfc_v3xx_spi_driver = {
273         .driver = {
274                 .name   = "hisi-sfc-v3xx",
275                 .acpi_match_table = ACPI_PTR(hisi_sfc_v3xx_acpi_ids),
276         },
277         .probe  = hisi_sfc_v3xx_probe,
278 };
279
280 module_platform_driver(hisi_sfc_v3xx_spi_driver);
281
282 MODULE_LICENSE("GPL");
283 MODULE_AUTHOR("John Garry <john.garry@huawei.com>");
284 MODULE_DESCRIPTION("HiSilicon SPI NOR V3XX Flash Controller Driver for hi16xx chipsets");