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; | |
2ec8b010 WKI |
356 | if (op->dummy.nbytes) |
357 | uma_cfg |= ilog2(op->dummy.buswidth) | |
358 | << NPCM_FIU_UMA_CFG_DBPCK_SHIFT; | |
ace55c41 TM |
359 | uma_cfg |= ilog2(op->data.buswidth) |
360 | << NPCM_FIU_UMA_CFG_RDBPCK_SHIFT; | |
361 | uma_cfg |= op->dummy.nbytes << NPCM_FIU_UMA_CFG_DBSIZ_SHIFT; | |
362 | uma_cfg |= op->addr.nbytes << NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT; | |
363 | regmap_write(fiu->regmap, NPCM_FIU_UMA_ADDR, addr); | |
364 | } else { | |
365 | regmap_write(fiu->regmap, NPCM_FIU_UMA_ADDR, 0x0); | |
366 | } | |
367 | ||
368 | uma_cfg |= data_size << NPCM_FIU_UMA_CFG_RDATSIZ_SHIFT; | |
369 | regmap_write(fiu->regmap, NPCM_FIU_UMA_CFG, uma_cfg); | |
370 | regmap_write_bits(fiu->regmap, NPCM_FIU_UMA_CTS, | |
371 | NPCM_FIU_UMA_CTS_EXEC_DONE, | |
372 | NPCM_FIU_UMA_CTS_EXEC_DONE); | |
373 | ret = regmap_read_poll_timeout(fiu->regmap, NPCM_FIU_UMA_CTS, val, | |
374 | (!(val & NPCM_FIU_UMA_CTS_EXEC_DONE)), 0, | |
375 | UMA_MICRO_SEC_TIMEOUT); | |
376 | if (ret) | |
377 | return ret; | |
378 | ||
379 | if (data_size) { | |
380 | for (i = 0; i < DIV_ROUND_UP(data_size, 4); i++) | |
381 | regmap_read(fiu->regmap, NPCM_FIU_UMA_DR0 + (i * 4), | |
382 | &data_reg[i]); | |
383 | memcpy(data, data_reg, data_size); | |
384 | } | |
385 | ||
386 | return 0; | |
387 | } | |
388 | ||
389 | static int npcm_fiu_uma_write(struct spi_mem *mem, | |
390 | const struct spi_mem_op *op, u8 cmd, | |
391 | bool is_address_size, u8 *data, u32 data_size) | |
392 | { | |
393 | struct npcm_fiu_spi *fiu = | |
1f6c80a7 | 394 | spi_controller_get_devdata(mem->spi->controller); |
ace55c41 TM |
395 | u32 uma_cfg = BIT(10); |
396 | u32 data_reg[4] = {0}; | |
397 | u32 val; | |
398 | u32 i; | |
399 | ||
400 | regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS, | |
401 | NPCM_FIU_UMA_CTS_DEV_NUM, | |
9e264f3f | 402 | (spi_get_chipselect(mem->spi, 0) << |
ace55c41 TM |
403 | NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT)); |
404 | ||
405 | regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CMD, | |
406 | NPCM_FIU_UMA_CMD_CMD, cmd); | |
407 | ||
408 | if (data_size) { | |
409 | memcpy(data_reg, data, data_size); | |
410 | for (i = 0; i < DIV_ROUND_UP(data_size, 4); i++) | |
411 | regmap_write(fiu->regmap, NPCM_FIU_UMA_DW0 + (i * 4), | |
412 | data_reg[i]); | |
413 | } | |
414 | ||
415 | if (is_address_size) { | |
416 | uma_cfg |= ilog2(op->cmd.buswidth); | |
417 | uma_cfg |= ilog2(op->addr.buswidth) << | |
418 | NPCM_FIU_UMA_CFG_ADBPCK_SHIFT; | |
419 | uma_cfg |= ilog2(op->data.buswidth) << | |
420 | NPCM_FIU_UMA_CFG_WDBPCK_SHIFT; | |
421 | uma_cfg |= op->addr.nbytes << NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT; | |
422 | regmap_write(fiu->regmap, NPCM_FIU_UMA_ADDR, op->addr.val); | |
423 | } else { | |
424 | regmap_write(fiu->regmap, NPCM_FIU_UMA_ADDR, 0x0); | |
425 | } | |
426 | ||
427 | uma_cfg |= (data_size << NPCM_FIU_UMA_CFG_WDATSIZ_SHIFT); | |
428 | regmap_write(fiu->regmap, NPCM_FIU_UMA_CFG, uma_cfg); | |
429 | ||
430 | regmap_write_bits(fiu->regmap, NPCM_FIU_UMA_CTS, | |
431 | NPCM_FIU_UMA_CTS_EXEC_DONE, | |
432 | NPCM_FIU_UMA_CTS_EXEC_DONE); | |
433 | ||
434 | return regmap_read_poll_timeout(fiu->regmap, NPCM_FIU_UMA_CTS, val, | |
435 | (!(val & NPCM_FIU_UMA_CTS_EXEC_DONE)), 0, | |
436 | UMA_MICRO_SEC_TIMEOUT); | |
437 | } | |
438 | ||
439 | static int npcm_fiu_manualwrite(struct spi_mem *mem, | |
440 | const struct spi_mem_op *op) | |
441 | { | |
442 | struct npcm_fiu_spi *fiu = | |
1f6c80a7 | 443 | spi_controller_get_devdata(mem->spi->controller); |
ace55c41 TM |
444 | u8 *data = (u8 *)op->data.buf.out; |
445 | u32 num_data_chunks; | |
446 | u32 remain_data; | |
447 | u32 idx = 0; | |
448 | int ret; | |
449 | ||
450 | num_data_chunks = op->data.nbytes / CHUNK_SIZE; | |
451 | remain_data = op->data.nbytes % CHUNK_SIZE; | |
452 | ||
453 | regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS, | |
454 | NPCM_FIU_UMA_CTS_DEV_NUM, | |
9e264f3f | 455 | (spi_get_chipselect(mem->spi, 0) << |
ace55c41 TM |
456 | NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT)); |
457 | regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS, | |
458 | NPCM_FIU_UMA_CTS_SW_CS, 0); | |
459 | ||
460 | ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, true, NULL, 0); | |
461 | if (ret) | |
462 | return ret; | |
463 | ||
464 | /* Starting the data writing loop in multiples of 8 */ | |
465 | for (idx = 0; idx < num_data_chunks; ++idx) { | |
466 | ret = npcm_fiu_uma_write(mem, op, data[0], false, | |
467 | &data[1], CHUNK_SIZE - 1); | |
468 | if (ret) | |
469 | return ret; | |
470 | ||
471 | data += CHUNK_SIZE; | |
472 | } | |
473 | ||
474 | /* Handling chunk remains */ | |
475 | if (remain_data > 0) { | |
476 | ret = npcm_fiu_uma_write(mem, op, data[0], false, | |
477 | &data[1], remain_data - 1); | |
478 | if (ret) | |
479 | return ret; | |
480 | } | |
481 | ||
482 | regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS, | |
483 | NPCM_FIU_UMA_CTS_SW_CS, NPCM_FIU_UMA_CTS_SW_CS); | |
484 | ||
485 | return 0; | |
486 | } | |
487 | ||
488 | static int npcm_fiu_read(struct spi_mem *mem, const struct spi_mem_op *op) | |
489 | { | |
490 | u8 *data = op->data.buf.in; | |
491 | int i, readlen, currlen; | |
ace55c41 TM |
492 | u8 *buf_ptr; |
493 | u32 addr; | |
494 | int ret; | |
495 | ||
496 | i = 0; | |
497 | currlen = op->data.nbytes; | |
498 | ||
499 | do { | |
500 | addr = ((u32)op->addr.val + i); | |
501 | if (currlen < 16) | |
502 | readlen = currlen; | |
503 | else | |
504 | readlen = 16; | |
505 | ||
506 | buf_ptr = data + i; | |
507 | ret = npcm_fiu_uma_read(mem, op, addr, true, buf_ptr, | |
508 | readlen); | |
509 | if (ret) | |
510 | return ret; | |
511 | ||
512 | i += readlen; | |
513 | currlen -= 16; | |
514 | } while (currlen > 0); | |
515 | ||
ace55c41 TM |
516 | return 0; |
517 | } | |
518 | ||
519 | static void npcm_fiux_set_direct_wr(struct npcm_fiu_spi *fiu) | |
520 | { | |
521 | regmap_write(fiu->regmap, NPCM_FIU_DWR_CFG, | |
522 | NPCM_FIU_DWR_16_BYTE_BURST); | |
523 | regmap_update_bits(fiu->regmap, NPCM_FIU_DWR_CFG, | |
524 | NPCM_FIU_DWR_CFG_ABPCK, | |
525 | DWR_ABPCK_4_BIT_PER_CLK << NPCM_FIU_DWR_ABPCK_SHIFT); | |
526 | regmap_update_bits(fiu->regmap, NPCM_FIU_DWR_CFG, | |
527 | NPCM_FIU_DWR_CFG_DBPCK, | |
528 | DWR_DBPCK_4_BIT_PER_CLK << NPCM_FIU_DWR_DBPCK_SHIFT); | |
529 | } | |
530 | ||
531 | static void npcm_fiux_set_direct_rd(struct npcm_fiu_spi *fiu) | |
532 | { | |
533 | u32 rx_dummy = 0; | |
534 | ||
535 | regmap_write(fiu->regmap, NPCM_FIU_DRD_CFG, | |
536 | NPCM_FIU_DRD_16_BYTE_BURST); | |
537 | regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, | |
538 | NPCM_FIU_DRD_CFG_ACCTYPE, | |
539 | DRD_SPI_X_MODE << NPCM_FIU_DRD_ACCTYPE_SHIFT); | |
540 | regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, | |
541 | NPCM_FIU_DRD_CFG_DBW, | |
542 | rx_dummy << NPCM_FIU_DRD_DBW_SHIFT); | |
543 | } | |
544 | ||
545 | static int npcm_fiu_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) | |
546 | { | |
547 | struct npcm_fiu_spi *fiu = | |
1f6c80a7 | 548 | spi_controller_get_devdata(mem->spi->controller); |
9e264f3f | 549 | struct npcm_fiu_chip *chip = &fiu->chip[spi_get_chipselect(mem->spi, 0)]; |
ace55c41 TM |
550 | int ret = 0; |
551 | u8 *buf; | |
552 | ||
553 | dev_dbg(fiu->dev, "cmd:%#x mode:%d.%d.%d.%d addr:%#llx len:%#x\n", | |
554 | op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, | |
555 | op->dummy.buswidth, op->data.buswidth, op->addr.val, | |
556 | op->data.nbytes); | |
557 | ||
558 | if (fiu->spix_mode || op->addr.nbytes > 4) | |
cff49d58 | 559 | return -EOPNOTSUPP; |
ace55c41 TM |
560 | |
561 | if (fiu->clkrate != chip->clkrate) { | |
562 | ret = clk_set_rate(fiu->clk, chip->clkrate); | |
563 | if (ret < 0) | |
0d6fccc1 CIK |
564 | dev_warn(fiu->dev, "Failed setting %lu frequency, stay at %lu frequency\n", |
565 | chip->clkrate, fiu->clkrate); | |
ace55c41 TM |
566 | else |
567 | fiu->clkrate = chip->clkrate; | |
568 | } | |
569 | ||
570 | if (op->data.dir == SPI_MEM_DATA_IN) { | |
571 | if (!op->addr.nbytes) { | |
572 | buf = op->data.buf.in; | |
573 | ret = npcm_fiu_uma_read(mem, op, op->addr.val, false, | |
574 | buf, op->data.nbytes); | |
575 | } else { | |
576 | ret = npcm_fiu_read(mem, op); | |
577 | } | |
578 | } else { | |
579 | if (!op->addr.nbytes && !op->data.nbytes) | |
580 | ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, false, | |
581 | NULL, 0); | |
582 | if (op->addr.nbytes && !op->data.nbytes) { | |
583 | int i; | |
584 | u8 buf_addr[4]; | |
585 | u32 addr = op->addr.val; | |
586 | ||
587 | for (i = op->addr.nbytes - 1; i >= 0; i--) { | |
588 | buf_addr[i] = addr & 0xff; | |
589 | addr >>= 8; | |
590 | } | |
591 | ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, false, | |
592 | buf_addr, op->addr.nbytes); | |
593 | } | |
594 | if (!op->addr.nbytes && op->data.nbytes) | |
595 | ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, false, | |
596 | (u8 *)op->data.buf.out, | |
597 | op->data.nbytes); | |
598 | if (op->addr.nbytes && op->data.nbytes) | |
599 | ret = npcm_fiu_manualwrite(mem, op); | |
600 | } | |
601 | ||
602 | return ret; | |
603 | } | |
604 | ||
605 | static int npcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc) | |
606 | { | |
607 | struct npcm_fiu_spi *fiu = | |
1f6c80a7 | 608 | spi_controller_get_devdata(desc->mem->spi->controller); |
9e264f3f | 609 | struct npcm_fiu_chip *chip = &fiu->chip[spi_get_chipselect(desc->mem->spi, 0)]; |
ace55c41 TM |
610 | struct regmap *gcr_regmap; |
611 | ||
612 | if (!fiu->res_mem) { | |
613 | dev_warn(fiu->dev, "Reserved memory not defined, direct read disabled\n"); | |
614 | desc->nodirmap = true; | |
615 | return 0; | |
616 | } | |
617 | ||
618 | if (!fiu->spix_mode && | |
619 | desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) { | |
620 | desc->nodirmap = true; | |
621 | return 0; | |
622 | } | |
623 | ||
624 | if (!chip->flash_region_mapped_ptr) { | |
625 | chip->flash_region_mapped_ptr = | |
4bdc0d67 | 626 | devm_ioremap(fiu->dev, (fiu->res_mem->start + |
ace55c41 | 627 | (fiu->info->max_map_size * |
9e264f3f | 628 | spi_get_chipselect(desc->mem->spi, 0))), |
ace55c41 TM |
629 | (u32)desc->info.length); |
630 | if (!chip->flash_region_mapped_ptr) { | |
631 | dev_warn(fiu->dev, "Error mapping memory region, direct read disabled\n"); | |
632 | desc->nodirmap = true; | |
633 | return 0; | |
634 | } | |
635 | } | |
636 | ||
637 | if (of_device_is_compatible(fiu->dev->of_node, "nuvoton,npcm750-fiu")) { | |
638 | gcr_regmap = | |
639 | syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr"); | |
640 | if (IS_ERR(gcr_regmap)) { | |
641 | dev_warn(fiu->dev, "Didn't find nuvoton,npcm750-gcr, direct read disabled\n"); | |
642 | desc->nodirmap = true; | |
643 | return 0; | |
644 | } | |
645 | regmap_update_bits(gcr_regmap, NPCM7XX_INTCR3_OFFSET, | |
646 | NPCM7XX_INTCR3_FIU_FIX, | |
647 | NPCM7XX_INTCR3_FIU_FIX); | |
650b014f TM |
648 | } else { |
649 | regmap_update_bits(fiu->regmap, NPCM_FIU_CFG, | |
650 | NPCM_FIU_CFG_FIU_FIX, | |
651 | NPCM_FIU_CFG_FIU_FIX); | |
ace55c41 TM |
652 | } |
653 | ||
654 | if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) { | |
655 | if (!fiu->spix_mode) | |
656 | npcm_fiu_set_drd(fiu, &desc->info.op_tmpl); | |
657 | else | |
658 | npcm_fiux_set_direct_rd(fiu); | |
659 | ||
660 | } else { | |
661 | npcm_fiux_set_direct_wr(fiu); | |
662 | } | |
663 | ||
664 | return 0; | |
665 | } | |
666 | ||
667 | static int npcm_fiu_setup(struct spi_device *spi) | |
668 | { | |
1f6c80a7 | 669 | struct spi_controller *ctrl = spi->controller; |
ace55c41 TM |
670 | struct npcm_fiu_spi *fiu = spi_controller_get_devdata(ctrl); |
671 | struct npcm_fiu_chip *chip; | |
672 | ||
9e264f3f | 673 | chip = &fiu->chip[spi_get_chipselect(spi, 0)]; |
ace55c41 | 674 | chip->fiu = fiu; |
9e264f3f | 675 | chip->chipselect = spi_get_chipselect(spi, 0); |
ace55c41 TM |
676 | chip->clkrate = spi->max_speed_hz; |
677 | ||
678 | fiu->clkrate = clk_get_rate(fiu->clk); | |
679 | ||
680 | return 0; | |
681 | } | |
682 | ||
683 | static const struct spi_controller_mem_ops npcm_fiu_mem_ops = { | |
684 | .exec_op = npcm_fiu_exec_op, | |
685 | .dirmap_create = npcm_fiu_dirmap_create, | |
686 | .dirmap_read = npcm_fiu_direct_read, | |
687 | .dirmap_write = npcm_fiu_direct_write, | |
688 | }; | |
689 | ||
690 | static const struct of_device_id npcm_fiu_dt_ids[] = { | |
b15e3bc7 | 691 | { .compatible = "nuvoton,npcm750-fiu", .data = &npcm7xx_fiu_data }, |
650b014f | 692 | { .compatible = "nuvoton,npcm845-fiu", .data = &npxm8xx_fiu_data }, |
ace55c41 TM |
693 | { /* sentinel */ } |
694 | }; | |
695 | ||
696 | static int npcm_fiu_probe(struct platform_device *pdev) | |
697 | { | |
698 | const struct fiu_data *fiu_data_match; | |
ace55c41 TM |
699 | struct device *dev = &pdev->dev; |
700 | struct spi_controller *ctrl; | |
701 | struct npcm_fiu_spi *fiu; | |
702 | void __iomem *regbase; | |
82c4fadb | 703 | int id; |
ace55c41 | 704 | |
1f6c80a7 | 705 | ctrl = devm_spi_alloc_host(dev, sizeof(*fiu)); |
ace55c41 TM |
706 | if (!ctrl) |
707 | return -ENOMEM; | |
708 | ||
709 | fiu = spi_controller_get_devdata(ctrl); | |
710 | ||
dc8fea13 MCCZ |
711 | fiu_data_match = of_device_get_match_data(dev); |
712 | if (!fiu_data_match) { | |
ace55c41 TM |
713 | dev_err(dev, "No compatible OF match\n"); |
714 | return -ENODEV; | |
715 | } | |
716 | ||
ace55c41 TM |
717 | id = of_alias_get_id(dev->of_node, "fiu"); |
718 | if (id < 0 || id >= fiu_data_match->fiu_max) { | |
719 | dev_err(dev, "Invalid platform device id: %d\n", id); | |
720 | return -EINVAL; | |
721 | } | |
722 | ||
723 | fiu->info = &fiu_data_match->npcm_fiu_data_info[id]; | |
724 | ||
725 | platform_set_drvdata(pdev, fiu); | |
726 | fiu->dev = dev; | |
727 | ||
1793d366 | 728 | regbase = devm_platform_ioremap_resource_byname(pdev, "control"); |
ace55c41 TM |
729 | if (IS_ERR(regbase)) |
730 | return PTR_ERR(regbase); | |
731 | ||
732 | fiu->regmap = devm_regmap_init_mmio(dev, regbase, | |
733 | &npcm_mtd_regmap_config); | |
734 | if (IS_ERR(fiu->regmap)) { | |
735 | dev_err(dev, "Failed to create regmap\n"); | |
736 | return PTR_ERR(fiu->regmap); | |
737 | } | |
738 | ||
739 | fiu->res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
740 | "memory"); | |
82c4fadb | 741 | fiu->clk = devm_clk_get_enabled(dev, NULL); |
ace55c41 TM |
742 | if (IS_ERR(fiu->clk)) |
743 | return PTR_ERR(fiu->clk); | |
744 | ||
745 | fiu->spix_mode = of_property_read_bool(dev->of_node, | |
746 | "nuvoton,spix-mode"); | |
747 | ||
748 | platform_set_drvdata(pdev, fiu); | |
ace55c41 TM |
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 | ||
82c4fadb | 758 | return devm_spi_register_controller(dev, ctrl); |
ace55c41 TM |
759 | } |
760 | ||
5e4830ad | 761 | static void npcm_fiu_remove(struct platform_device *pdev) |
ace55c41 | 762 | { |
ace55c41 TM |
763 | } |
764 | ||
765 | MODULE_DEVICE_TABLE(of, npcm_fiu_dt_ids); | |
766 | ||
767 | static struct platform_driver npcm_fiu_driver = { | |
768 | .driver = { | |
769 | .name = "NPCM-FIU", | |
770 | .bus = &platform_bus_type, | |
771 | .of_match_table = npcm_fiu_dt_ids, | |
772 | }, | |
773 | .probe = npcm_fiu_probe, | |
5e4830ad | 774 | .remove_new = npcm_fiu_remove, |
ace55c41 TM |
775 | }; |
776 | module_platform_driver(npcm_fiu_driver); | |
777 | ||
778 | MODULE_DESCRIPTION("Nuvoton FLASH Interface Unit SPI Controller Driver"); | |
779 | MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>"); | |
780 | MODULE_LICENSE("GPL v2"); |