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