Commit | Line | Data |
---|---|---|
ace55c41 TM |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2019 Nuvoton Technology corporation. | |
3 | ||
0ec544ce | 4 | #include <linux/bits.h> |
ace55c41 TM |
5 | #include <linux/init.h> |
6 | #include <linux/kernel.h> | |
7 | #include <linux/device.h> | |
8 | #include <linux/module.h> | |
9 | #include <linux/ioport.h> | |
10 | #include <linux/clk.h> | |
11 | #include <linux/platform_device.h> | |
12 | #include <linux/io.h> | |
13 | #include <linux/vmalloc.h> | |
14 | #include <linux/regmap.h> | |
749396cb | 15 | #include <linux/of.h> |
ace55c41 TM |
16 | #include <linux/spi/spi-mem.h> |
17 | #include <linux/mfd/syscon.h> | |
18 | ||
19 | /* NPCM7xx GCR module */ | |
20 | #define NPCM7XX_INTCR3_OFFSET 0x9C | |
21 | #define NPCM7XX_INTCR3_FIU_FIX BIT(6) | |
22 | ||
23 | /* Flash Interface Unit (FIU) Registers */ | |
24 | #define NPCM_FIU_DRD_CFG 0x00 | |
25 | #define NPCM_FIU_DWR_CFG 0x04 | |
26 | #define NPCM_FIU_UMA_CFG 0x08 | |
27 | #define NPCM_FIU_UMA_CTS 0x0C | |
28 | #define NPCM_FIU_UMA_CMD 0x10 | |
29 | #define NPCM_FIU_UMA_ADDR 0x14 | |
30 | #define NPCM_FIU_PRT_CFG 0x18 | |
31 | #define NPCM_FIU_UMA_DW0 0x20 | |
32 | #define NPCM_FIU_UMA_DW1 0x24 | |
33 | #define NPCM_FIU_UMA_DW2 0x28 | |
34 | #define NPCM_FIU_UMA_DW3 0x2C | |
35 | #define NPCM_FIU_UMA_DR0 0x30 | |
36 | #define NPCM_FIU_UMA_DR1 0x34 | |
37 | #define NPCM_FIU_UMA_DR2 0x38 | |
38 | #define NPCM_FIU_UMA_DR3 0x3C | |
650b014f | 39 | #define NPCM_FIU_CFG 0x78 |
ace55c41 TM |
40 | #define NPCM_FIU_MAX_REG_LIMIT 0x80 |
41 | ||
42 | /* FIU Direct Read Configuration Register */ | |
43 | #define NPCM_FIU_DRD_CFG_LCK BIT(31) | |
44 | #define NPCM_FIU_DRD_CFG_R_BURST GENMASK(25, 24) | |
45 | #define NPCM_FIU_DRD_CFG_ADDSIZ GENMASK(17, 16) | |
46 | #define NPCM_FIU_DRD_CFG_DBW GENMASK(13, 12) | |
47 | #define NPCM_FIU_DRD_CFG_ACCTYPE GENMASK(9, 8) | |
48 | #define NPCM_FIU_DRD_CFG_RDCMD GENMASK(7, 0) | |
49 | #define NPCM_FIU_DRD_ADDSIZ_SHIFT 16 | |
50 | #define NPCM_FIU_DRD_DBW_SHIFT 12 | |
51 | #define NPCM_FIU_DRD_ACCTYPE_SHIFT 8 | |
52 | ||
53 | /* FIU Direct Write Configuration Register */ | |
54 | #define NPCM_FIU_DWR_CFG_LCK BIT(31) | |
55 | #define NPCM_FIU_DWR_CFG_W_BURST GENMASK(25, 24) | |
56 | #define NPCM_FIU_DWR_CFG_ADDSIZ GENMASK(17, 16) | |
57 | #define NPCM_FIU_DWR_CFG_ABPCK GENMASK(11, 10) | |
58 | #define NPCM_FIU_DWR_CFG_DBPCK GENMASK(9, 8) | |
59 | #define NPCM_FIU_DWR_CFG_WRCMD GENMASK(7, 0) | |
60 | #define NPCM_FIU_DWR_ADDSIZ_SHIFT 16 | |
61 | #define NPCM_FIU_DWR_ABPCK_SHIFT 10 | |
62 | #define NPCM_FIU_DWR_DBPCK_SHIFT 8 | |
63 | ||
64 | /* FIU UMA Configuration Register */ | |
65 | #define NPCM_FIU_UMA_CFG_LCK BIT(31) | |
66 | #define NPCM_FIU_UMA_CFG_CMMLCK BIT(30) | |
67 | #define NPCM_FIU_UMA_CFG_RDATSIZ GENMASK(28, 24) | |
68 | #define NPCM_FIU_UMA_CFG_DBSIZ GENMASK(23, 21) | |
69 | #define NPCM_FIU_UMA_CFG_WDATSIZ GENMASK(20, 16) | |
70 | #define NPCM_FIU_UMA_CFG_ADDSIZ GENMASK(13, 11) | |
71 | #define NPCM_FIU_UMA_CFG_CMDSIZ BIT(10) | |
72 | #define NPCM_FIU_UMA_CFG_RDBPCK GENMASK(9, 8) | |
73 | #define NPCM_FIU_UMA_CFG_DBPCK GENMASK(7, 6) | |
74 | #define NPCM_FIU_UMA_CFG_WDBPCK GENMASK(5, 4) | |
75 | #define NPCM_FIU_UMA_CFG_ADBPCK GENMASK(3, 2) | |
76 | #define NPCM_FIU_UMA_CFG_CMBPCK GENMASK(1, 0) | |
77 | #define NPCM_FIU_UMA_CFG_ADBPCK_SHIFT 2 | |
78 | #define NPCM_FIU_UMA_CFG_WDBPCK_SHIFT 4 | |
79 | #define NPCM_FIU_UMA_CFG_DBPCK_SHIFT 6 | |
80 | #define NPCM_FIU_UMA_CFG_RDBPCK_SHIFT 8 | |
81 | #define NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT 11 | |
82 | #define NPCM_FIU_UMA_CFG_WDATSIZ_SHIFT 16 | |
83 | #define NPCM_FIU_UMA_CFG_DBSIZ_SHIFT 21 | |
84 | #define NPCM_FIU_UMA_CFG_RDATSIZ_SHIFT 24 | |
85 | ||
86 | /* FIU UMA Control and Status Register */ | |
87 | #define NPCM_FIU_UMA_CTS_RDYIE BIT(25) | |
88 | #define NPCM_FIU_UMA_CTS_RDYST BIT(24) | |
89 | #define NPCM_FIU_UMA_CTS_SW_CS BIT(16) | |
90 | #define NPCM_FIU_UMA_CTS_DEV_NUM GENMASK(9, 8) | |
91 | #define NPCM_FIU_UMA_CTS_EXEC_DONE BIT(0) | |
92 | #define NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT 8 | |
93 | ||
94 | /* FIU UMA Command Register */ | |
95 | #define NPCM_FIU_UMA_CMD_DUM3 GENMASK(31, 24) | |
96 | #define NPCM_FIU_UMA_CMD_DUM2 GENMASK(23, 16) | |
97 | #define NPCM_FIU_UMA_CMD_DUM1 GENMASK(15, 8) | |
98 | #define NPCM_FIU_UMA_CMD_CMD GENMASK(7, 0) | |
99 | ||
100 | /* FIU UMA Address Register */ | |
101 | #define NPCM_FIU_UMA_ADDR_UMA_ADDR GENMASK(31, 0) | |
102 | #define NPCM_FIU_UMA_ADDR_AB3 GENMASK(31, 24) | |
103 | #define NPCM_FIU_UMA_ADDR_AB2 GENMASK(23, 16) | |
104 | #define NPCM_FIU_UMA_ADDR_AB1 GENMASK(15, 8) | |
105 | #define NPCM_FIU_UMA_ADDR_AB0 GENMASK(7, 0) | |
106 | ||
107 | /* FIU UMA Write Data Bytes 0-3 Register */ | |
108 | #define NPCM_FIU_UMA_DW0_WB3 GENMASK(31, 24) | |
109 | #define NPCM_FIU_UMA_DW0_WB2 GENMASK(23, 16) | |
110 | #define NPCM_FIU_UMA_DW0_WB1 GENMASK(15, 8) | |
111 | #define NPCM_FIU_UMA_DW0_WB0 GENMASK(7, 0) | |
112 | ||
113 | /* FIU UMA Write Data Bytes 4-7 Register */ | |
114 | #define NPCM_FIU_UMA_DW1_WB7 GENMASK(31, 24) | |
115 | #define NPCM_FIU_UMA_DW1_WB6 GENMASK(23, 16) | |
116 | #define NPCM_FIU_UMA_DW1_WB5 GENMASK(15, 8) | |
117 | #define NPCM_FIU_UMA_DW1_WB4 GENMASK(7, 0) | |
118 | ||
119 | /* FIU UMA Write Data Bytes 8-11 Register */ | |
120 | #define NPCM_FIU_UMA_DW2_WB11 GENMASK(31, 24) | |
121 | #define NPCM_FIU_UMA_DW2_WB10 GENMASK(23, 16) | |
122 | #define NPCM_FIU_UMA_DW2_WB9 GENMASK(15, 8) | |
123 | #define NPCM_FIU_UMA_DW2_WB8 GENMASK(7, 0) | |
124 | ||
125 | /* FIU UMA Write Data Bytes 12-15 Register */ | |
126 | #define NPCM_FIU_UMA_DW3_WB15 GENMASK(31, 24) | |
127 | #define NPCM_FIU_UMA_DW3_WB14 GENMASK(23, 16) | |
128 | #define NPCM_FIU_UMA_DW3_WB13 GENMASK(15, 8) | |
129 | #define NPCM_FIU_UMA_DW3_WB12 GENMASK(7, 0) | |
130 | ||
131 | /* FIU UMA Read Data Bytes 0-3 Register */ | |
132 | #define NPCM_FIU_UMA_DR0_RB3 GENMASK(31, 24) | |
133 | #define NPCM_FIU_UMA_DR0_RB2 GENMASK(23, 16) | |
134 | #define NPCM_FIU_UMA_DR0_RB1 GENMASK(15, 8) | |
135 | #define NPCM_FIU_UMA_DR0_RB0 GENMASK(7, 0) | |
136 | ||
137 | /* FIU UMA Read Data Bytes 4-7 Register */ | |
138 | #define NPCM_FIU_UMA_DR1_RB15 GENMASK(31, 24) | |
139 | #define NPCM_FIU_UMA_DR1_RB14 GENMASK(23, 16) | |
140 | #define NPCM_FIU_UMA_DR1_RB13 GENMASK(15, 8) | |
141 | #define NPCM_FIU_UMA_DR1_RB12 GENMASK(7, 0) | |
142 | ||
143 | /* FIU UMA Read Data Bytes 8-11 Register */ | |
144 | #define NPCM_FIU_UMA_DR2_RB15 GENMASK(31, 24) | |
145 | #define NPCM_FIU_UMA_DR2_RB14 GENMASK(23, 16) | |
146 | #define NPCM_FIU_UMA_DR2_RB13 GENMASK(15, 8) | |
147 | #define NPCM_FIU_UMA_DR2_RB12 GENMASK(7, 0) | |
148 | ||
149 | /* FIU UMA Read Data Bytes 12-15 Register */ | |
150 | #define NPCM_FIU_UMA_DR3_RB15 GENMASK(31, 24) | |
151 | #define NPCM_FIU_UMA_DR3_RB14 GENMASK(23, 16) | |
152 | #define NPCM_FIU_UMA_DR3_RB13 GENMASK(15, 8) | |
153 | #define NPCM_FIU_UMA_DR3_RB12 GENMASK(7, 0) | |
154 | ||
650b014f TM |
155 | /* FIU Configuration Register */ |
156 | #define NPCM_FIU_CFG_FIU_FIX BIT(31) | |
157 | ||
ace55c41 TM |
158 | /* FIU Read Mode */ |
159 | enum { | |
160 | DRD_SINGLE_WIRE_MODE = 0, | |
161 | DRD_DUAL_IO_MODE = 1, | |
162 | DRD_QUAD_IO_MODE = 2, | |
163 | DRD_SPI_X_MODE = 3, | |
164 | }; | |
165 | ||
166 | enum { | |
167 | DWR_ABPCK_BIT_PER_CLK = 0, | |
168 | DWR_ABPCK_2_BIT_PER_CLK = 1, | |
169 | DWR_ABPCK_4_BIT_PER_CLK = 2, | |
170 | }; | |
171 | ||
172 | enum { | |
173 | DWR_DBPCK_BIT_PER_CLK = 0, | |
174 | DWR_DBPCK_2_BIT_PER_CLK = 1, | |
175 | DWR_DBPCK_4_BIT_PER_CLK = 2, | |
176 | }; | |
177 | ||
178 | #define NPCM_FIU_DRD_16_BYTE_BURST 0x3000000 | |
179 | #define NPCM_FIU_DWR_16_BYTE_BURST 0x3000000 | |
180 | ||
181 | #define MAP_SIZE_128MB 0x8000000 | |
182 | #define MAP_SIZE_16MB 0x1000000 | |
183 | #define MAP_SIZE_8MB 0x800000 | |
184 | ||
ace55c41 TM |
185 | #define FIU_DRD_MAX_DUMMY_NUMBER 3 |
186 | #define NPCM_MAX_CHIP_NUM 4 | |
187 | #define CHUNK_SIZE 16 | |
188 | #define UMA_MICRO_SEC_TIMEOUT 150 | |
189 | ||
190 | enum { | |
191 | FIU0 = 0, | |
192 | FIU3, | |
193 | FIUX, | |
650b014f | 194 | FIU1, |
ace55c41 TM |
195 | }; |
196 | ||
197 | struct npcm_fiu_info { | |
198 | char *name; | |
199 | u32 fiu_id; | |
200 | u32 max_map_size; | |
201 | u32 max_cs; | |
202 | }; | |
203 | ||
204 | struct fiu_data { | |
205 | const struct npcm_fiu_info *npcm_fiu_data_info; | |
206 | int fiu_max; | |
207 | }; | |
208 | ||
b15e3bc7 | 209 | static const struct npcm_fiu_info npcm7xx_fiu_info[] = { |
ace55c41 TM |
210 | {.name = "FIU0", .fiu_id = FIU0, |
211 | .max_map_size = MAP_SIZE_128MB, .max_cs = 2}, | |
212 | {.name = "FIU3", .fiu_id = FIU3, | |
213 | .max_map_size = MAP_SIZE_128MB, .max_cs = 4}, | |
214 | {.name = "FIUX", .fiu_id = FIUX, | |
215 | .max_map_size = MAP_SIZE_16MB, .max_cs = 2} }; | |
216 | ||
b15e3bc7 JN |
217 | static const struct fiu_data npcm7xx_fiu_data = { |
218 | .npcm_fiu_data_info = npcm7xx_fiu_info, | |
ace55c41 TM |
219 | .fiu_max = 3, |
220 | }; | |
221 | ||
650b014f TM |
222 | static const struct npcm_fiu_info npxm8xx_fiu_info[] = { |
223 | {.name = "FIU0", .fiu_id = FIU0, | |
224 | .max_map_size = MAP_SIZE_128MB, .max_cs = 2}, | |
225 | {.name = "FIU3", .fiu_id = FIU3, | |
226 | .max_map_size = MAP_SIZE_128MB, .max_cs = 4}, | |
227 | {.name = "FIUX", .fiu_id = FIUX, | |
228 | .max_map_size = MAP_SIZE_16MB, .max_cs = 2}, | |
229 | {.name = "FIU1", .fiu_id = FIU1, | |
230 | .max_map_size = MAP_SIZE_16MB, .max_cs = 4} }; | |
231 | ||
232 | static const struct fiu_data npxm8xx_fiu_data = { | |
233 | .npcm_fiu_data_info = npxm8xx_fiu_info, | |
234 | .fiu_max = 4, | |
235 | }; | |
236 | ||
ace55c41 TM |
237 | struct npcm_fiu_spi; |
238 | ||
239 | struct npcm_fiu_chip { | |
240 | void __iomem *flash_region_mapped_ptr; | |
241 | struct npcm_fiu_spi *fiu; | |
242 | unsigned long clkrate; | |
243 | u32 chipselect; | |
244 | }; | |
245 | ||
246 | struct npcm_fiu_spi { | |
247 | struct npcm_fiu_chip chip[NPCM_MAX_CHIP_NUM]; | |
248 | const struct npcm_fiu_info *info; | |
249 | struct spi_mem_op drd_op; | |
250 | struct resource *res_mem; | |
251 | struct regmap *regmap; | |
252 | unsigned long clkrate; | |
253 | struct device *dev; | |
254 | struct clk *clk; | |
255 | bool spix_mode; | |
256 | }; | |
257 | ||
258 | static const struct regmap_config npcm_mtd_regmap_config = { | |
259 | .reg_bits = 32, | |
260 | .val_bits = 32, | |
261 | .reg_stride = 4, | |
262 | .max_register = NPCM_FIU_MAX_REG_LIMIT, | |
263 | }; | |
264 | ||
265 | static void npcm_fiu_set_drd(struct npcm_fiu_spi *fiu, | |
266 | const struct spi_mem_op *op) | |
267 | { | |
268 | regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, | |
269 | NPCM_FIU_DRD_CFG_ACCTYPE, | |
270 | ilog2(op->addr.buswidth) << | |
271 | NPCM_FIU_DRD_ACCTYPE_SHIFT); | |
272 | fiu->drd_op.addr.buswidth = op->addr.buswidth; | |
273 | regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, | |
274 | NPCM_FIU_DRD_CFG_DBW, | |
7c3193f7 | 275 | op->dummy.nbytes << NPCM_FIU_DRD_DBW_SHIFT); |
ace55c41 TM |
276 | fiu->drd_op.dummy.nbytes = op->dummy.nbytes; |
277 | regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, | |
278 | NPCM_FIU_DRD_CFG_RDCMD, op->cmd.opcode); | |
279 | fiu->drd_op.cmd.opcode = op->cmd.opcode; | |
280 | regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, | |
281 | NPCM_FIU_DRD_CFG_ADDSIZ, | |
282 | (op->addr.nbytes - 3) << NPCM_FIU_DRD_ADDSIZ_SHIFT); | |
283 | fiu->drd_op.addr.nbytes = op->addr.nbytes; | |
284 | } | |
285 | ||
286 | static ssize_t npcm_fiu_direct_read(struct spi_mem_dirmap_desc *desc, | |
287 | u64 offs, size_t len, void *buf) | |
288 | { | |
289 | struct npcm_fiu_spi *fiu = | |
1f6c80a7 | 290 | spi_controller_get_devdata(desc->mem->spi->controller); |
9e264f3f | 291 | struct npcm_fiu_chip *chip = &fiu->chip[spi_get_chipselect(desc->mem->spi, 0)]; |
ace55c41 TM |
292 | void __iomem *src = (void __iomem *)(chip->flash_region_mapped_ptr + |
293 | offs); | |
294 | u8 *buf_rx = buf; | |
295 | u32 i; | |
296 | ||
297 | if (fiu->spix_mode) { | |
298 | for (i = 0 ; i < len ; i++) | |
299 | *(buf_rx + i) = ioread8(src + i); | |
300 | } else { | |
301 | if (desc->info.op_tmpl.addr.buswidth != fiu->drd_op.addr.buswidth || | |
302 | desc->info.op_tmpl.dummy.nbytes != fiu->drd_op.dummy.nbytes || | |
303 | desc->info.op_tmpl.cmd.opcode != fiu->drd_op.cmd.opcode || | |
304 | desc->info.op_tmpl.addr.nbytes != fiu->drd_op.addr.nbytes) | |
305 | npcm_fiu_set_drd(fiu, &desc->info.op_tmpl); | |
306 | ||
307 | memcpy_fromio(buf_rx, src, len); | |
308 | } | |
309 | ||
310 | return len; | |
311 | } | |
312 | ||
313 | static ssize_t npcm_fiu_direct_write(struct spi_mem_dirmap_desc *desc, | |
314 | u64 offs, size_t len, const void *buf) | |
315 | { | |
316 | struct npcm_fiu_spi *fiu = | |
1f6c80a7 | 317 | spi_controller_get_devdata(desc->mem->spi->controller); |
9e264f3f | 318 | struct npcm_fiu_chip *chip = &fiu->chip[spi_get_chipselect(desc->mem->spi, 0)]; |
ace55c41 TM |
319 | void __iomem *dst = (void __iomem *)(chip->flash_region_mapped_ptr + |
320 | offs); | |
321 | const u8 *buf_tx = buf; | |
322 | u32 i; | |
323 | ||
324 | if (fiu->spix_mode) | |
325 | for (i = 0 ; i < len ; i++) | |
326 | iowrite8(*(buf_tx + i), dst + i); | |
327 | else | |
328 | memcpy_toio(dst, buf_tx, len); | |
329 | ||
330 | return len; | |
331 | } | |
332 | ||
333 | static int npcm_fiu_uma_read(struct spi_mem *mem, | |
334 | const struct spi_mem_op *op, u32 addr, | |
335 | bool is_address_size, u8 *data, u32 data_size) | |
336 | { | |
337 | struct npcm_fiu_spi *fiu = | |
1f6c80a7 | 338 | spi_controller_get_devdata(mem->spi->controller); |
ace55c41 TM |
339 | u32 uma_cfg = BIT(10); |
340 | u32 data_reg[4]; | |
341 | int ret; | |
342 | u32 val; | |
343 | u32 i; | |
344 | ||
345 | regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS, | |
346 | NPCM_FIU_UMA_CTS_DEV_NUM, | |
9e264f3f | 347 | (spi_get_chipselect(mem->spi, 0) << |
ace55c41 TM |
348 | NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT)); |
349 | regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CMD, | |
350 | NPCM_FIU_UMA_CMD_CMD, op->cmd.opcode); | |
351 | ||
352 | if (is_address_size) { | |
353 | uma_cfg |= ilog2(op->cmd.buswidth); | |
354 | uma_cfg |= ilog2(op->addr.buswidth) | |
355 | << NPCM_FIU_UMA_CFG_ADBPCK_SHIFT; | |
356 | uma_cfg |= ilog2(op->dummy.buswidth) | |
357 | << NPCM_FIU_UMA_CFG_DBPCK_SHIFT; | |
358 | uma_cfg |= ilog2(op->data.buswidth) | |
359 | << NPCM_FIU_UMA_CFG_RDBPCK_SHIFT; | |
360 | uma_cfg |= op->dummy.nbytes << NPCM_FIU_UMA_CFG_DBSIZ_SHIFT; | |
361 | uma_cfg |= op->addr.nbytes << NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT; | |
362 | regmap_write(fiu->regmap, NPCM_FIU_UMA_ADDR, addr); | |
363 | } else { | |
364 | regmap_write(fiu->regmap, NPCM_FIU_UMA_ADDR, 0x0); | |
365 | } | |
366 | ||
367 | uma_cfg |= data_size << NPCM_FIU_UMA_CFG_RDATSIZ_SHIFT; | |
368 | regmap_write(fiu->regmap, NPCM_FIU_UMA_CFG, uma_cfg); | |
369 | regmap_write_bits(fiu->regmap, NPCM_FIU_UMA_CTS, | |
370 | NPCM_FIU_UMA_CTS_EXEC_DONE, | |
371 | NPCM_FIU_UMA_CTS_EXEC_DONE); | |
372 | ret = regmap_read_poll_timeout(fiu->regmap, NPCM_FIU_UMA_CTS, val, | |
373 | (!(val & NPCM_FIU_UMA_CTS_EXEC_DONE)), 0, | |
374 | UMA_MICRO_SEC_TIMEOUT); | |
375 | if (ret) | |
376 | return ret; | |
377 | ||
378 | if (data_size) { | |
379 | for (i = 0; i < DIV_ROUND_UP(data_size, 4); i++) | |
380 | regmap_read(fiu->regmap, NPCM_FIU_UMA_DR0 + (i * 4), | |
381 | &data_reg[i]); | |
382 | memcpy(data, data_reg, data_size); | |
383 | } | |
384 | ||
385 | return 0; | |
386 | } | |
387 | ||
388 | static int npcm_fiu_uma_write(struct spi_mem *mem, | |
389 | const struct spi_mem_op *op, u8 cmd, | |
390 | bool is_address_size, u8 *data, u32 data_size) | |
391 | { | |
392 | struct npcm_fiu_spi *fiu = | |
1f6c80a7 | 393 | spi_controller_get_devdata(mem->spi->controller); |
ace55c41 TM |
394 | u32 uma_cfg = BIT(10); |
395 | u32 data_reg[4] = {0}; | |
396 | u32 val; | |
397 | u32 i; | |
398 | ||
399 | regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS, | |
400 | NPCM_FIU_UMA_CTS_DEV_NUM, | |
9e264f3f | 401 | (spi_get_chipselect(mem->spi, 0) << |
ace55c41 TM |
402 | NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT)); |
403 | ||
404 | regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CMD, | |
405 | NPCM_FIU_UMA_CMD_CMD, cmd); | |
406 | ||
407 | if (data_size) { | |
408 | memcpy(data_reg, data, data_size); | |
409 | for (i = 0; i < DIV_ROUND_UP(data_size, 4); i++) | |
410 | regmap_write(fiu->regmap, NPCM_FIU_UMA_DW0 + (i * 4), | |
411 | data_reg[i]); | |
412 | } | |
413 | ||
414 | if (is_address_size) { | |
415 | uma_cfg |= ilog2(op->cmd.buswidth); | |
416 | uma_cfg |= ilog2(op->addr.buswidth) << | |
417 | NPCM_FIU_UMA_CFG_ADBPCK_SHIFT; | |
418 | uma_cfg |= ilog2(op->data.buswidth) << | |
419 | NPCM_FIU_UMA_CFG_WDBPCK_SHIFT; | |
420 | uma_cfg |= op->addr.nbytes << NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT; | |
421 | regmap_write(fiu->regmap, NPCM_FIU_UMA_ADDR, op->addr.val); | |
422 | } else { | |
423 | regmap_write(fiu->regmap, NPCM_FIU_UMA_ADDR, 0x0); | |
424 | } | |
425 | ||
426 | uma_cfg |= (data_size << NPCM_FIU_UMA_CFG_WDATSIZ_SHIFT); | |
427 | regmap_write(fiu->regmap, NPCM_FIU_UMA_CFG, uma_cfg); | |
428 | ||
429 | regmap_write_bits(fiu->regmap, NPCM_FIU_UMA_CTS, | |
430 | NPCM_FIU_UMA_CTS_EXEC_DONE, | |
431 | NPCM_FIU_UMA_CTS_EXEC_DONE); | |
432 | ||
433 | return regmap_read_poll_timeout(fiu->regmap, NPCM_FIU_UMA_CTS, val, | |
434 | (!(val & NPCM_FIU_UMA_CTS_EXEC_DONE)), 0, | |
435 | UMA_MICRO_SEC_TIMEOUT); | |
436 | } | |
437 | ||
438 | static int npcm_fiu_manualwrite(struct spi_mem *mem, | |
439 | const struct spi_mem_op *op) | |
440 | { | |
441 | struct npcm_fiu_spi *fiu = | |
1f6c80a7 | 442 | spi_controller_get_devdata(mem->spi->controller); |
ace55c41 TM |
443 | u8 *data = (u8 *)op->data.buf.out; |
444 | u32 num_data_chunks; | |
445 | u32 remain_data; | |
446 | u32 idx = 0; | |
447 | int ret; | |
448 | ||
449 | num_data_chunks = op->data.nbytes / CHUNK_SIZE; | |
450 | remain_data = op->data.nbytes % CHUNK_SIZE; | |
451 | ||
452 | regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS, | |
453 | NPCM_FIU_UMA_CTS_DEV_NUM, | |
9e264f3f | 454 | (spi_get_chipselect(mem->spi, 0) << |
ace55c41 TM |
455 | NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT)); |
456 | regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS, | |
457 | NPCM_FIU_UMA_CTS_SW_CS, 0); | |
458 | ||
459 | ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, true, NULL, 0); | |
460 | if (ret) | |
461 | return ret; | |
462 | ||
463 | /* Starting the data writing loop in multiples of 8 */ | |
464 | for (idx = 0; idx < num_data_chunks; ++idx) { | |
465 | ret = npcm_fiu_uma_write(mem, op, data[0], false, | |
466 | &data[1], CHUNK_SIZE - 1); | |
467 | if (ret) | |
468 | return ret; | |
469 | ||
470 | data += CHUNK_SIZE; | |
471 | } | |
472 | ||
473 | /* Handling chunk remains */ | |
474 | if (remain_data > 0) { | |
475 | ret = npcm_fiu_uma_write(mem, op, data[0], false, | |
476 | &data[1], remain_data - 1); | |
477 | if (ret) | |
478 | return ret; | |
479 | } | |
480 | ||
481 | regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS, | |
482 | NPCM_FIU_UMA_CTS_SW_CS, NPCM_FIU_UMA_CTS_SW_CS); | |
483 | ||
484 | return 0; | |
485 | } | |
486 | ||
487 | static int npcm_fiu_read(struct spi_mem *mem, const struct spi_mem_op *op) | |
488 | { | |
489 | u8 *data = op->data.buf.in; | |
490 | int i, readlen, currlen; | |
ace55c41 TM |
491 | u8 *buf_ptr; |
492 | u32 addr; | |
493 | int ret; | |
494 | ||
495 | i = 0; | |
496 | currlen = op->data.nbytes; | |
497 | ||
498 | do { | |
499 | addr = ((u32)op->addr.val + i); | |
500 | if (currlen < 16) | |
501 | readlen = currlen; | |
502 | else | |
503 | readlen = 16; | |
504 | ||
505 | buf_ptr = data + i; | |
506 | ret = npcm_fiu_uma_read(mem, op, addr, true, buf_ptr, | |
507 | readlen); | |
508 | if (ret) | |
509 | return ret; | |
510 | ||
511 | i += readlen; | |
512 | currlen -= 16; | |
513 | } while (currlen > 0); | |
514 | ||
ace55c41 TM |
515 | return 0; |
516 | } | |
517 | ||
518 | static void npcm_fiux_set_direct_wr(struct npcm_fiu_spi *fiu) | |
519 | { | |
520 | regmap_write(fiu->regmap, NPCM_FIU_DWR_CFG, | |
521 | NPCM_FIU_DWR_16_BYTE_BURST); | |
522 | regmap_update_bits(fiu->regmap, NPCM_FIU_DWR_CFG, | |
523 | NPCM_FIU_DWR_CFG_ABPCK, | |
524 | DWR_ABPCK_4_BIT_PER_CLK << NPCM_FIU_DWR_ABPCK_SHIFT); | |
525 | regmap_update_bits(fiu->regmap, NPCM_FIU_DWR_CFG, | |
526 | NPCM_FIU_DWR_CFG_DBPCK, | |
527 | DWR_DBPCK_4_BIT_PER_CLK << NPCM_FIU_DWR_DBPCK_SHIFT); | |
528 | } | |
529 | ||
530 | static void npcm_fiux_set_direct_rd(struct npcm_fiu_spi *fiu) | |
531 | { | |
532 | u32 rx_dummy = 0; | |
533 | ||
534 | regmap_write(fiu->regmap, NPCM_FIU_DRD_CFG, | |
535 | NPCM_FIU_DRD_16_BYTE_BURST); | |
536 | regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, | |
537 | NPCM_FIU_DRD_CFG_ACCTYPE, | |
538 | DRD_SPI_X_MODE << NPCM_FIU_DRD_ACCTYPE_SHIFT); | |
539 | regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, | |
540 | NPCM_FIU_DRD_CFG_DBW, | |
541 | rx_dummy << NPCM_FIU_DRD_DBW_SHIFT); | |
542 | } | |
543 | ||
544 | static int npcm_fiu_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) | |
545 | { | |
546 | struct npcm_fiu_spi *fiu = | |
1f6c80a7 | 547 | spi_controller_get_devdata(mem->spi->controller); |
9e264f3f | 548 | struct npcm_fiu_chip *chip = &fiu->chip[spi_get_chipselect(mem->spi, 0)]; |
ace55c41 TM |
549 | int ret = 0; |
550 | u8 *buf; | |
551 | ||
552 | dev_dbg(fiu->dev, "cmd:%#x mode:%d.%d.%d.%d addr:%#llx len:%#x\n", | |
553 | op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, | |
554 | op->dummy.buswidth, op->data.buswidth, op->addr.val, | |
555 | op->data.nbytes); | |
556 | ||
557 | if (fiu->spix_mode || op->addr.nbytes > 4) | |
558 | return -ENOTSUPP; | |
559 | ||
560 | if (fiu->clkrate != chip->clkrate) { | |
561 | ret = clk_set_rate(fiu->clk, chip->clkrate); | |
562 | if (ret < 0) | |
0d6fccc1 CIK |
563 | dev_warn(fiu->dev, "Failed setting %lu frequency, stay at %lu frequency\n", |
564 | chip->clkrate, fiu->clkrate); | |
ace55c41 TM |
565 | else |
566 | fiu->clkrate = chip->clkrate; | |
567 | } | |
568 | ||
569 | if (op->data.dir == SPI_MEM_DATA_IN) { | |
570 | if (!op->addr.nbytes) { | |
571 | buf = op->data.buf.in; | |
572 | ret = npcm_fiu_uma_read(mem, op, op->addr.val, false, | |
573 | buf, op->data.nbytes); | |
574 | } else { | |
575 | ret = npcm_fiu_read(mem, op); | |
576 | } | |
577 | } else { | |
578 | if (!op->addr.nbytes && !op->data.nbytes) | |
579 | ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, false, | |
580 | NULL, 0); | |
581 | if (op->addr.nbytes && !op->data.nbytes) { | |
582 | int i; | |
583 | u8 buf_addr[4]; | |
584 | u32 addr = op->addr.val; | |
585 | ||
586 | for (i = op->addr.nbytes - 1; i >= 0; i--) { | |
587 | buf_addr[i] = addr & 0xff; | |
588 | addr >>= 8; | |
589 | } | |
590 | ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, false, | |
591 | buf_addr, op->addr.nbytes); | |
592 | } | |
593 | if (!op->addr.nbytes && op->data.nbytes) | |
594 | ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, false, | |
595 | (u8 *)op->data.buf.out, | |
596 | op->data.nbytes); | |
597 | if (op->addr.nbytes && op->data.nbytes) | |
598 | ret = npcm_fiu_manualwrite(mem, op); | |
599 | } | |
600 | ||
601 | return ret; | |
602 | } | |
603 | ||
604 | static int npcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc) | |
605 | { | |
606 | struct npcm_fiu_spi *fiu = | |
1f6c80a7 | 607 | spi_controller_get_devdata(desc->mem->spi->controller); |
9e264f3f | 608 | struct npcm_fiu_chip *chip = &fiu->chip[spi_get_chipselect(desc->mem->spi, 0)]; |
ace55c41 TM |
609 | struct regmap *gcr_regmap; |
610 | ||
611 | if (!fiu->res_mem) { | |
612 | dev_warn(fiu->dev, "Reserved memory not defined, direct read disabled\n"); | |
613 | desc->nodirmap = true; | |
614 | return 0; | |
615 | } | |
616 | ||
617 | if (!fiu->spix_mode && | |
618 | desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) { | |
619 | desc->nodirmap = true; | |
620 | return 0; | |
621 | } | |
622 | ||
623 | if (!chip->flash_region_mapped_ptr) { | |
624 | chip->flash_region_mapped_ptr = | |
4bdc0d67 | 625 | devm_ioremap(fiu->dev, (fiu->res_mem->start + |
ace55c41 | 626 | (fiu->info->max_map_size * |
9e264f3f | 627 | spi_get_chipselect(desc->mem->spi, 0))), |
ace55c41 TM |
628 | (u32)desc->info.length); |
629 | if (!chip->flash_region_mapped_ptr) { | |
630 | dev_warn(fiu->dev, "Error mapping memory region, direct read disabled\n"); | |
631 | desc->nodirmap = true; | |
632 | return 0; | |
633 | } | |
634 | } | |
635 | ||
636 | if (of_device_is_compatible(fiu->dev->of_node, "nuvoton,npcm750-fiu")) { | |
637 | gcr_regmap = | |
638 | syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr"); | |
639 | if (IS_ERR(gcr_regmap)) { | |
640 | dev_warn(fiu->dev, "Didn't find nuvoton,npcm750-gcr, direct read disabled\n"); | |
641 | desc->nodirmap = true; | |
642 | return 0; | |
643 | } | |
644 | regmap_update_bits(gcr_regmap, NPCM7XX_INTCR3_OFFSET, | |
645 | NPCM7XX_INTCR3_FIU_FIX, | |
646 | NPCM7XX_INTCR3_FIU_FIX); | |
650b014f TM |
647 | } else { |
648 | regmap_update_bits(fiu->regmap, NPCM_FIU_CFG, | |
649 | NPCM_FIU_CFG_FIU_FIX, | |
650 | NPCM_FIU_CFG_FIU_FIX); | |
ace55c41 TM |
651 | } |
652 | ||
653 | if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) { | |
654 | if (!fiu->spix_mode) | |
655 | npcm_fiu_set_drd(fiu, &desc->info.op_tmpl); | |
656 | else | |
657 | npcm_fiux_set_direct_rd(fiu); | |
658 | ||
659 | } else { | |
660 | npcm_fiux_set_direct_wr(fiu); | |
661 | } | |
662 | ||
663 | return 0; | |
664 | } | |
665 | ||
666 | static int npcm_fiu_setup(struct spi_device *spi) | |
667 | { | |
1f6c80a7 | 668 | struct spi_controller *ctrl = spi->controller; |
ace55c41 TM |
669 | struct npcm_fiu_spi *fiu = spi_controller_get_devdata(ctrl); |
670 | struct npcm_fiu_chip *chip; | |
671 | ||
9e264f3f | 672 | chip = &fiu->chip[spi_get_chipselect(spi, 0)]; |
ace55c41 | 673 | chip->fiu = fiu; |
9e264f3f | 674 | chip->chipselect = spi_get_chipselect(spi, 0); |
ace55c41 TM |
675 | chip->clkrate = spi->max_speed_hz; |
676 | ||
677 | fiu->clkrate = clk_get_rate(fiu->clk); | |
678 | ||
679 | return 0; | |
680 | } | |
681 | ||
682 | static const struct spi_controller_mem_ops npcm_fiu_mem_ops = { | |
683 | .exec_op = npcm_fiu_exec_op, | |
684 | .dirmap_create = npcm_fiu_dirmap_create, | |
685 | .dirmap_read = npcm_fiu_direct_read, | |
686 | .dirmap_write = npcm_fiu_direct_write, | |
687 | }; | |
688 | ||
689 | static const struct of_device_id npcm_fiu_dt_ids[] = { | |
b15e3bc7 | 690 | { .compatible = "nuvoton,npcm750-fiu", .data = &npcm7xx_fiu_data }, |
650b014f | 691 | { .compatible = "nuvoton,npcm845-fiu", .data = &npxm8xx_fiu_data }, |
ace55c41 TM |
692 | { /* sentinel */ } |
693 | }; | |
694 | ||
695 | static int npcm_fiu_probe(struct platform_device *pdev) | |
696 | { | |
697 | const struct fiu_data *fiu_data_match; | |
ace55c41 TM |
698 | struct device *dev = &pdev->dev; |
699 | struct spi_controller *ctrl; | |
700 | struct npcm_fiu_spi *fiu; | |
701 | void __iomem *regbase; | |
234266a5 | 702 | int id, ret; |
ace55c41 | 703 | |
1f6c80a7 | 704 | ctrl = devm_spi_alloc_host(dev, sizeof(*fiu)); |
ace55c41 TM |
705 | if (!ctrl) |
706 | return -ENOMEM; | |
707 | ||
708 | fiu = spi_controller_get_devdata(ctrl); | |
709 | ||
dc8fea13 MCCZ |
710 | fiu_data_match = of_device_get_match_data(dev); |
711 | if (!fiu_data_match) { | |
ace55c41 TM |
712 | dev_err(dev, "No compatible OF match\n"); |
713 | return -ENODEV; | |
714 | } | |
715 | ||
ace55c41 TM |
716 | id = of_alias_get_id(dev->of_node, "fiu"); |
717 | if (id < 0 || id >= fiu_data_match->fiu_max) { | |
718 | dev_err(dev, "Invalid platform device id: %d\n", id); | |
719 | return -EINVAL; | |
720 | } | |
721 | ||
722 | fiu->info = &fiu_data_match->npcm_fiu_data_info[id]; | |
723 | ||
724 | platform_set_drvdata(pdev, fiu); | |
725 | fiu->dev = dev; | |
726 | ||
1793d366 | 727 | regbase = devm_platform_ioremap_resource_byname(pdev, "control"); |
ace55c41 TM |
728 | if (IS_ERR(regbase)) |
729 | return PTR_ERR(regbase); | |
730 | ||
731 | fiu->regmap = devm_regmap_init_mmio(dev, regbase, | |
732 | &npcm_mtd_regmap_config); | |
733 | if (IS_ERR(fiu->regmap)) { | |
734 | dev_err(dev, "Failed to create regmap\n"); | |
735 | return PTR_ERR(fiu->regmap); | |
736 | } | |
737 | ||
738 | fiu->res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
739 | "memory"); | |
740 | fiu->clk = devm_clk_get(dev, NULL); | |
741 | if (IS_ERR(fiu->clk)) | |
742 | return PTR_ERR(fiu->clk); | |
743 | ||
744 | fiu->spix_mode = of_property_read_bool(dev->of_node, | |
745 | "nuvoton,spix-mode"); | |
746 | ||
747 | platform_set_drvdata(pdev, fiu); | |
748 | clk_prepare_enable(fiu->clk); | |
749 | ||
750 | ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | |
751 | | SPI_TX_DUAL | SPI_TX_QUAD; | |
752 | ctrl->setup = npcm_fiu_setup; | |
753 | ctrl->bus_num = -1; | |
754 | ctrl->mem_ops = &npcm_fiu_mem_ops; | |
755 | ctrl->num_chipselect = fiu->info->max_cs; | |
756 | ctrl->dev.of_node = dev->of_node; | |
757 | ||
1f6c80a7 | 758 | ret = devm_spi_register_controller(dev, ctrl); |
234266a5 LW |
759 | if (ret) |
760 | clk_disable_unprepare(fiu->clk); | |
761 | ||
762 | return ret; | |
ace55c41 TM |
763 | } |
764 | ||
5e4830ad | 765 | static void npcm_fiu_remove(struct platform_device *pdev) |
ace55c41 TM |
766 | { |
767 | struct npcm_fiu_spi *fiu = platform_get_drvdata(pdev); | |
768 | ||
769 | clk_disable_unprepare(fiu->clk); | |
ace55c41 TM |
770 | } |
771 | ||
772 | MODULE_DEVICE_TABLE(of, npcm_fiu_dt_ids); | |
773 | ||
774 | static struct platform_driver npcm_fiu_driver = { | |
775 | .driver = { | |
776 | .name = "NPCM-FIU", | |
777 | .bus = &platform_bus_type, | |
778 | .of_match_table = npcm_fiu_dt_ids, | |
779 | }, | |
780 | .probe = npcm_fiu_probe, | |
5e4830ad | 781 | .remove_new = npcm_fiu_remove, |
ace55c41 TM |
782 | }; |
783 | module_platform_driver(npcm_fiu_driver); | |
784 | ||
785 | MODULE_DESCRIPTION("Nuvoton FLASH Interface Unit SPI Controller Driver"); | |
786 | MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>"); | |
787 | MODULE_LICENSE("GPL v2"); |