1 // SPDX-License-Identifier: GPL-2.0-only
3 // HiSilicon SPI NOR V3XX Flash Controller Driver for hi16xx chipsets
5 // Copyright (c) 2019 HiSilicon Technologies Co., Ltd.
6 // Author: John Garry <john.garry@huawei.com>
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>
17 #define HISI_SFC_V3XX_VERSION (0x1f8)
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)
31 struct hisi_sfc_v3xx_host
{
33 void __iomem
*regbase
;
37 #define HISI_SFC_V3XX_WAIT_TIMEOUT_US 1000000
38 #define HISI_SFC_V3XX_WAIT_POLL_INTERVAL_US 10
40 static int hisi_sfc_v3xx_wait_cmd_idle(struct hisi_sfc_v3xx_host
*host
)
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
);
50 static int hisi_sfc_v3xx_adjust_op_size(struct spi_mem
*mem
,
51 struct spi_mem_op
*op
)
53 struct spi_device
*spi
= mem
->spi
;
54 struct hisi_sfc_v3xx_host
*host
;
55 uintptr_t addr
= (uintptr_t)op
->data
.buf
.in
;
58 host
= spi_controller_get_devdata(spi
->master
);
60 max_byte_count
= host
->max_cmd_dword
* 4;
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
;
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.
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().
79 static void hisi_sfc_v3xx_read_databuf(struct hisi_sfc_v3xx_host
*host
,
80 u8
*to
, unsigned int len
)
85 from
= host
->regbase
+ HISI_SFC_V3XX_CMD_DATABUF0
;
87 if (IS_ALIGNED((uintptr_t)to
, 4)) {
90 __ioread32_copy(to
, from
, words
);
99 val
= __raw_readl(from
);
101 for (i
= 0; i
< len
; i
++, val
>>= 8, to
++)
105 for (i
= 0; i
< DIV_ROUND_UP(len
, 4); i
++, from
+= 4) {
106 u32 val
= __raw_readl(from
);
109 for (j
= 0; j
< 4 && (j
+ (i
* 4) < len
);
110 to
++, val
>>= 8, j
++)
116 static void hisi_sfc_v3xx_write_databuf(struct hisi_sfc_v3xx_host
*host
,
117 const u8
*from
, unsigned int len
)
122 to
= host
->regbase
+ HISI_SFC_V3XX_CMD_DATABUF0
;
124 if (IS_ALIGNED((uintptr_t)from
, 4)) {
127 __iowrite32_copy(to
, from
, words
);
136 for (i
= 0; i
< len
; i
++, from
++)
137 val
|= *from
<< i
* 8;
138 __raw_writel(val
, to
);
142 for (i
= 0; i
< DIV_ROUND_UP(len
, 4); i
++, to
+= 4) {
146 for (j
= 0; j
< 4 && (j
+ (i
* 4) < len
);
148 val
|= *from
<< j
* 8;
149 __raw_writel(val
, to
);
154 static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host
*host
,
155 const struct spi_mem_op
*op
,
158 int ret
, len
= op
->data
.nbytes
;
162 config
|= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK
;
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
;
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
;
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
;
178 writel(op
->addr
.val
, host
->regbase
+ HISI_SFC_V3XX_CMD_ADDR
);
179 writel(op
->cmd
.opcode
, host
->regbase
+ HISI_SFC_V3XX_CMD_INS
);
181 writel(config
, host
->regbase
+ HISI_SFC_V3XX_CMD_CFG
);
183 ret
= hisi_sfc_v3xx_wait_cmd_idle(host
);
187 if (op
->data
.dir
== SPI_MEM_DATA_IN
)
188 hisi_sfc_v3xx_read_databuf(host
, op
->data
.buf
.in
, len
);
193 static int hisi_sfc_v3xx_exec_op(struct spi_mem
*mem
,
194 const struct spi_mem_op
*op
)
196 struct hisi_sfc_v3xx_host
*host
;
197 struct spi_device
*spi
= mem
->spi
;
198 u8 chip_select
= spi
->chip_select
;
200 host
= spi_controller_get_devdata(spi
->master
);
202 return hisi_sfc_v3xx_generic_exec_op(host
, op
, chip_select
);
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
,
210 static int hisi_sfc_v3xx_probe(struct platform_device
*pdev
)
212 struct device
*dev
= &pdev
->dev
;
213 struct hisi_sfc_v3xx_host
*host
;
214 struct spi_controller
*ctlr
;
218 ctlr
= spi_alloc_master(&pdev
->dev
, sizeof(*host
));
222 ctlr
->mode_bits
= SPI_RX_DUAL
| SPI_RX_QUAD
|
223 SPI_TX_DUAL
| SPI_TX_QUAD
;
225 host
= spi_controller_get_devdata(ctlr
);
228 platform_set_drvdata(pdev
, host
);
230 host
->regbase
= devm_platform_ioremap_resource(pdev
, 0);
231 if (IS_ERR(host
->regbase
)) {
232 ret
= PTR_ERR(host
->regbase
);
237 ctlr
->num_chipselect
= 1;
238 ctlr
->mem_ops
= &hisi_sfc_v3xx_mem_ops
;
240 version
= readl(host
->regbase
+ HISI_SFC_V3XX_VERSION
);
244 host
->max_cmd_dword
= 64;
247 host
->max_cmd_dword
= 16;
251 ret
= devm_spi_register_controller(dev
, ctlr
);
255 dev_info(&pdev
->dev
, "hw version 0x%x\n", version
);
260 spi_master_put(ctlr
);
264 #if IS_ENABLED(CONFIG_ACPI)
265 static const struct acpi_device_id hisi_sfc_v3xx_acpi_ids
[] = {
269 MODULE_DEVICE_TABLE(acpi
, hisi_sfc_v3xx_acpi_ids
);
272 static struct platform_driver hisi_sfc_v3xx_spi_driver
= {
274 .name
= "hisi-sfc-v3xx",
275 .acpi_match_table
= ACPI_PTR(hisi_sfc_v3xx_acpi_ids
),
277 .probe
= hisi_sfc_v3xx_probe
,
280 module_platform_driver(hisi_sfc_v3xx_spi_driver
);
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");