]>
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> | |
15 | #include <linux/of_device.h> | |
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 | |
39 | #define NPCM_FIU_MAX_REG_LIMIT 0x80 | |
40 | ||
41 | /* FIU Direct Read Configuration Register */ | |
42 | #define NPCM_FIU_DRD_CFG_LCK BIT(31) | |
43 | #define NPCM_FIU_DRD_CFG_R_BURST GENMASK(25, 24) | |
44 | #define NPCM_FIU_DRD_CFG_ADDSIZ GENMASK(17, 16) | |
45 | #define NPCM_FIU_DRD_CFG_DBW GENMASK(13, 12) | |
46 | #define NPCM_FIU_DRD_CFG_ACCTYPE GENMASK(9, 8) | |
47 | #define NPCM_FIU_DRD_CFG_RDCMD GENMASK(7, 0) | |
48 | #define NPCM_FIU_DRD_ADDSIZ_SHIFT 16 | |
49 | #define NPCM_FIU_DRD_DBW_SHIFT 12 | |
50 | #define NPCM_FIU_DRD_ACCTYPE_SHIFT 8 | |
51 | ||
52 | /* FIU Direct Write Configuration Register */ | |
53 | #define NPCM_FIU_DWR_CFG_LCK BIT(31) | |
54 | #define NPCM_FIU_DWR_CFG_W_BURST GENMASK(25, 24) | |
55 | #define NPCM_FIU_DWR_CFG_ADDSIZ GENMASK(17, 16) | |
56 | #define NPCM_FIU_DWR_CFG_ABPCK GENMASK(11, 10) | |
57 | #define NPCM_FIU_DWR_CFG_DBPCK GENMASK(9, 8) | |
58 | #define NPCM_FIU_DWR_CFG_WRCMD GENMASK(7, 0) | |
59 | #define NPCM_FIU_DWR_ADDSIZ_SHIFT 16 | |
60 | #define NPCM_FIU_DWR_ABPCK_SHIFT 10 | |
61 | #define NPCM_FIU_DWR_DBPCK_SHIFT 8 | |
62 | ||
63 | /* FIU UMA Configuration Register */ | |
64 | #define NPCM_FIU_UMA_CFG_LCK BIT(31) | |
65 | #define NPCM_FIU_UMA_CFG_CMMLCK BIT(30) | |
66 | #define NPCM_FIU_UMA_CFG_RDATSIZ GENMASK(28, 24) | |
67 | #define NPCM_FIU_UMA_CFG_DBSIZ GENMASK(23, 21) | |
68 | #define NPCM_FIU_UMA_CFG_WDATSIZ GENMASK(20, 16) | |
69 | #define NPCM_FIU_UMA_CFG_ADDSIZ GENMASK(13, 11) | |
70 | #define NPCM_FIU_UMA_CFG_CMDSIZ BIT(10) | |
71 | #define NPCM_FIU_UMA_CFG_RDBPCK GENMASK(9, 8) | |
72 | #define NPCM_FIU_UMA_CFG_DBPCK GENMASK(7, 6) | |
73 | #define NPCM_FIU_UMA_CFG_WDBPCK GENMASK(5, 4) | |
74 | #define NPCM_FIU_UMA_CFG_ADBPCK GENMASK(3, 2) | |
75 | #define NPCM_FIU_UMA_CFG_CMBPCK GENMASK(1, 0) | |
76 | #define NPCM_FIU_UMA_CFG_ADBPCK_SHIFT 2 | |
77 | #define NPCM_FIU_UMA_CFG_WDBPCK_SHIFT 4 | |
78 | #define NPCM_FIU_UMA_CFG_DBPCK_SHIFT 6 | |
79 | #define NPCM_FIU_UMA_CFG_RDBPCK_SHIFT 8 | |
80 | #define NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT 11 | |
81 | #define NPCM_FIU_UMA_CFG_WDATSIZ_SHIFT 16 | |
82 | #define NPCM_FIU_UMA_CFG_DBSIZ_SHIFT 21 | |
83 | #define NPCM_FIU_UMA_CFG_RDATSIZ_SHIFT 24 | |
84 | ||
85 | /* FIU UMA Control and Status Register */ | |
86 | #define NPCM_FIU_UMA_CTS_RDYIE BIT(25) | |
87 | #define NPCM_FIU_UMA_CTS_RDYST BIT(24) | |
88 | #define NPCM_FIU_UMA_CTS_SW_CS BIT(16) | |
89 | #define NPCM_FIU_UMA_CTS_DEV_NUM GENMASK(9, 8) | |
90 | #define NPCM_FIU_UMA_CTS_EXEC_DONE BIT(0) | |
91 | #define NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT 8 | |
92 | ||
93 | /* FIU UMA Command Register */ | |
94 | #define NPCM_FIU_UMA_CMD_DUM3 GENMASK(31, 24) | |
95 | #define NPCM_FIU_UMA_CMD_DUM2 GENMASK(23, 16) | |
96 | #define NPCM_FIU_UMA_CMD_DUM1 GENMASK(15, 8) | |
97 | #define NPCM_FIU_UMA_CMD_CMD GENMASK(7, 0) | |
98 | ||
99 | /* FIU UMA Address Register */ | |
100 | #define NPCM_FIU_UMA_ADDR_UMA_ADDR GENMASK(31, 0) | |
101 | #define NPCM_FIU_UMA_ADDR_AB3 GENMASK(31, 24) | |
102 | #define NPCM_FIU_UMA_ADDR_AB2 GENMASK(23, 16) | |
103 | #define NPCM_FIU_UMA_ADDR_AB1 GENMASK(15, 8) | |
104 | #define NPCM_FIU_UMA_ADDR_AB0 GENMASK(7, 0) | |
105 | ||
106 | /* FIU UMA Write Data Bytes 0-3 Register */ | |
107 | #define NPCM_FIU_UMA_DW0_WB3 GENMASK(31, 24) | |
108 | #define NPCM_FIU_UMA_DW0_WB2 GENMASK(23, 16) | |
109 | #define NPCM_FIU_UMA_DW0_WB1 GENMASK(15, 8) | |
110 | #define NPCM_FIU_UMA_DW0_WB0 GENMASK(7, 0) | |
111 | ||
112 | /* FIU UMA Write Data Bytes 4-7 Register */ | |
113 | #define NPCM_FIU_UMA_DW1_WB7 GENMASK(31, 24) | |
114 | #define NPCM_FIU_UMA_DW1_WB6 GENMASK(23, 16) | |
115 | #define NPCM_FIU_UMA_DW1_WB5 GENMASK(15, 8) | |
116 | #define NPCM_FIU_UMA_DW1_WB4 GENMASK(7, 0) | |
117 | ||
118 | /* FIU UMA Write Data Bytes 8-11 Register */ | |
119 | #define NPCM_FIU_UMA_DW2_WB11 GENMASK(31, 24) | |
120 | #define NPCM_FIU_UMA_DW2_WB10 GENMASK(23, 16) | |
121 | #define NPCM_FIU_UMA_DW2_WB9 GENMASK(15, 8) | |
122 | #define NPCM_FIU_UMA_DW2_WB8 GENMASK(7, 0) | |
123 | ||
124 | /* FIU UMA Write Data Bytes 12-15 Register */ | |
125 | #define NPCM_FIU_UMA_DW3_WB15 GENMASK(31, 24) | |
126 | #define NPCM_FIU_UMA_DW3_WB14 GENMASK(23, 16) | |
127 | #define NPCM_FIU_UMA_DW3_WB13 GENMASK(15, 8) | |
128 | #define NPCM_FIU_UMA_DW3_WB12 GENMASK(7, 0) | |
129 | ||
130 | /* FIU UMA Read Data Bytes 0-3 Register */ | |
131 | #define NPCM_FIU_UMA_DR0_RB3 GENMASK(31, 24) | |
132 | #define NPCM_FIU_UMA_DR0_RB2 GENMASK(23, 16) | |
133 | #define NPCM_FIU_UMA_DR0_RB1 GENMASK(15, 8) | |
134 | #define NPCM_FIU_UMA_DR0_RB0 GENMASK(7, 0) | |
135 | ||
136 | /* FIU UMA Read Data Bytes 4-7 Register */ | |
137 | #define NPCM_FIU_UMA_DR1_RB15 GENMASK(31, 24) | |
138 | #define NPCM_FIU_UMA_DR1_RB14 GENMASK(23, 16) | |
139 | #define NPCM_FIU_UMA_DR1_RB13 GENMASK(15, 8) | |
140 | #define NPCM_FIU_UMA_DR1_RB12 GENMASK(7, 0) | |
141 | ||
142 | /* FIU UMA Read Data Bytes 8-11 Register */ | |
143 | #define NPCM_FIU_UMA_DR2_RB15 GENMASK(31, 24) | |
144 | #define NPCM_FIU_UMA_DR2_RB14 GENMASK(23, 16) | |
145 | #define NPCM_FIU_UMA_DR2_RB13 GENMASK(15, 8) | |
146 | #define NPCM_FIU_UMA_DR2_RB12 GENMASK(7, 0) | |
147 | ||
148 | /* FIU UMA Read Data Bytes 12-15 Register */ | |
149 | #define NPCM_FIU_UMA_DR3_RB15 GENMASK(31, 24) | |
150 | #define NPCM_FIU_UMA_DR3_RB14 GENMASK(23, 16) | |
151 | #define NPCM_FIU_UMA_DR3_RB13 GENMASK(15, 8) | |
152 | #define NPCM_FIU_UMA_DR3_RB12 GENMASK(7, 0) | |
153 | ||
154 | /* FIU Read Mode */ | |
155 | enum { | |
156 | DRD_SINGLE_WIRE_MODE = 0, | |
157 | DRD_DUAL_IO_MODE = 1, | |
158 | DRD_QUAD_IO_MODE = 2, | |
159 | DRD_SPI_X_MODE = 3, | |
160 | }; | |
161 | ||
162 | enum { | |
163 | DWR_ABPCK_BIT_PER_CLK = 0, | |
164 | DWR_ABPCK_2_BIT_PER_CLK = 1, | |
165 | DWR_ABPCK_4_BIT_PER_CLK = 2, | |
166 | }; | |
167 | ||
168 | enum { | |
169 | DWR_DBPCK_BIT_PER_CLK = 0, | |
170 | DWR_DBPCK_2_BIT_PER_CLK = 1, | |
171 | DWR_DBPCK_4_BIT_PER_CLK = 2, | |
172 | }; | |
173 | ||
174 | #define NPCM_FIU_DRD_16_BYTE_BURST 0x3000000 | |
175 | #define NPCM_FIU_DWR_16_BYTE_BURST 0x3000000 | |
176 | ||
177 | #define MAP_SIZE_128MB 0x8000000 | |
178 | #define MAP_SIZE_16MB 0x1000000 | |
179 | #define MAP_SIZE_8MB 0x800000 | |
180 | ||
ace55c41 TM |
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, | |
0ec544ce AS |
255 | ((op->dummy.nbytes * ilog2(op->addr.buswidth)) / BITS_PER_BYTE) |
256 | << NPCM_FIU_DRD_DBW_SHIFT); | |
ace55c41 TM |
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; | |
ace55c41 TM |
472 | u8 *buf_ptr; |
473 | u32 addr; | |
474 | int ret; | |
475 | ||
476 | i = 0; | |
477 | currlen = op->data.nbytes; | |
478 | ||
479 | do { | |
480 | addr = ((u32)op->addr.val + i); | |
481 | if (currlen < 16) | |
482 | readlen = currlen; | |
483 | else | |
484 | readlen = 16; | |
485 | ||
486 | buf_ptr = data + i; | |
487 | ret = npcm_fiu_uma_read(mem, op, addr, true, buf_ptr, | |
488 | readlen); | |
489 | if (ret) | |
490 | return ret; | |
491 | ||
492 | i += readlen; | |
493 | currlen -= 16; | |
494 | } while (currlen > 0); | |
495 | ||
ace55c41 TM |
496 | return 0; |
497 | } | |
498 | ||
499 | static void npcm_fiux_set_direct_wr(struct npcm_fiu_spi *fiu) | |
500 | { | |
501 | regmap_write(fiu->regmap, NPCM_FIU_DWR_CFG, | |
502 | NPCM_FIU_DWR_16_BYTE_BURST); | |
503 | regmap_update_bits(fiu->regmap, NPCM_FIU_DWR_CFG, | |
504 | NPCM_FIU_DWR_CFG_ABPCK, | |
505 | DWR_ABPCK_4_BIT_PER_CLK << NPCM_FIU_DWR_ABPCK_SHIFT); | |
506 | regmap_update_bits(fiu->regmap, NPCM_FIU_DWR_CFG, | |
507 | NPCM_FIU_DWR_CFG_DBPCK, | |
508 | DWR_DBPCK_4_BIT_PER_CLK << NPCM_FIU_DWR_DBPCK_SHIFT); | |
509 | } | |
510 | ||
511 | static void npcm_fiux_set_direct_rd(struct npcm_fiu_spi *fiu) | |
512 | { | |
513 | u32 rx_dummy = 0; | |
514 | ||
515 | regmap_write(fiu->regmap, NPCM_FIU_DRD_CFG, | |
516 | NPCM_FIU_DRD_16_BYTE_BURST); | |
517 | regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, | |
518 | NPCM_FIU_DRD_CFG_ACCTYPE, | |
519 | DRD_SPI_X_MODE << NPCM_FIU_DRD_ACCTYPE_SHIFT); | |
520 | regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, | |
521 | NPCM_FIU_DRD_CFG_DBW, | |
522 | rx_dummy << NPCM_FIU_DRD_DBW_SHIFT); | |
523 | } | |
524 | ||
525 | static int npcm_fiu_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) | |
526 | { | |
527 | struct npcm_fiu_spi *fiu = | |
528 | spi_controller_get_devdata(mem->spi->master); | |
529 | struct npcm_fiu_chip *chip = &fiu->chip[mem->spi->chip_select]; | |
530 | int ret = 0; | |
531 | u8 *buf; | |
532 | ||
533 | dev_dbg(fiu->dev, "cmd:%#x mode:%d.%d.%d.%d addr:%#llx len:%#x\n", | |
534 | op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, | |
535 | op->dummy.buswidth, op->data.buswidth, op->addr.val, | |
536 | op->data.nbytes); | |
537 | ||
538 | if (fiu->spix_mode || op->addr.nbytes > 4) | |
539 | return -ENOTSUPP; | |
540 | ||
541 | if (fiu->clkrate != chip->clkrate) { | |
542 | ret = clk_set_rate(fiu->clk, chip->clkrate); | |
543 | if (ret < 0) | |
0d6fccc1 CIK |
544 | dev_warn(fiu->dev, "Failed setting %lu frequency, stay at %lu frequency\n", |
545 | chip->clkrate, fiu->clkrate); | |
ace55c41 TM |
546 | else |
547 | fiu->clkrate = chip->clkrate; | |
548 | } | |
549 | ||
550 | if (op->data.dir == SPI_MEM_DATA_IN) { | |
551 | if (!op->addr.nbytes) { | |
552 | buf = op->data.buf.in; | |
553 | ret = npcm_fiu_uma_read(mem, op, op->addr.val, false, | |
554 | buf, op->data.nbytes); | |
555 | } else { | |
556 | ret = npcm_fiu_read(mem, op); | |
557 | } | |
558 | } else { | |
559 | if (!op->addr.nbytes && !op->data.nbytes) | |
560 | ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, false, | |
561 | NULL, 0); | |
562 | if (op->addr.nbytes && !op->data.nbytes) { | |
563 | int i; | |
564 | u8 buf_addr[4]; | |
565 | u32 addr = op->addr.val; | |
566 | ||
567 | for (i = op->addr.nbytes - 1; i >= 0; i--) { | |
568 | buf_addr[i] = addr & 0xff; | |
569 | addr >>= 8; | |
570 | } | |
571 | ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, false, | |
572 | buf_addr, op->addr.nbytes); | |
573 | } | |
574 | if (!op->addr.nbytes && op->data.nbytes) | |
575 | ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, false, | |
576 | (u8 *)op->data.buf.out, | |
577 | op->data.nbytes); | |
578 | if (op->addr.nbytes && op->data.nbytes) | |
579 | ret = npcm_fiu_manualwrite(mem, op); | |
580 | } | |
581 | ||
582 | return ret; | |
583 | } | |
584 | ||
585 | static int npcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc) | |
586 | { | |
587 | struct npcm_fiu_spi *fiu = | |
588 | spi_controller_get_devdata(desc->mem->spi->master); | |
589 | struct npcm_fiu_chip *chip = &fiu->chip[desc->mem->spi->chip_select]; | |
590 | struct regmap *gcr_regmap; | |
591 | ||
592 | if (!fiu->res_mem) { | |
593 | dev_warn(fiu->dev, "Reserved memory not defined, direct read disabled\n"); | |
594 | desc->nodirmap = true; | |
595 | return 0; | |
596 | } | |
597 | ||
598 | if (!fiu->spix_mode && | |
599 | desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) { | |
600 | desc->nodirmap = true; | |
601 | return 0; | |
602 | } | |
603 | ||
604 | if (!chip->flash_region_mapped_ptr) { | |
605 | chip->flash_region_mapped_ptr = | |
4bdc0d67 | 606 | devm_ioremap(fiu->dev, (fiu->res_mem->start + |
ace55c41 TM |
607 | (fiu->info->max_map_size * |
608 | desc->mem->spi->chip_select)), | |
609 | (u32)desc->info.length); | |
610 | if (!chip->flash_region_mapped_ptr) { | |
611 | dev_warn(fiu->dev, "Error mapping memory region, direct read disabled\n"); | |
612 | desc->nodirmap = true; | |
613 | return 0; | |
614 | } | |
615 | } | |
616 | ||
617 | if (of_device_is_compatible(fiu->dev->of_node, "nuvoton,npcm750-fiu")) { | |
618 | gcr_regmap = | |
619 | syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr"); | |
620 | if (IS_ERR(gcr_regmap)) { | |
621 | dev_warn(fiu->dev, "Didn't find nuvoton,npcm750-gcr, direct read disabled\n"); | |
622 | desc->nodirmap = true; | |
623 | return 0; | |
624 | } | |
625 | regmap_update_bits(gcr_regmap, NPCM7XX_INTCR3_OFFSET, | |
626 | NPCM7XX_INTCR3_FIU_FIX, | |
627 | NPCM7XX_INTCR3_FIU_FIX); | |
628 | } | |
629 | ||
630 | if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) { | |
631 | if (!fiu->spix_mode) | |
632 | npcm_fiu_set_drd(fiu, &desc->info.op_tmpl); | |
633 | else | |
634 | npcm_fiux_set_direct_rd(fiu); | |
635 | ||
636 | } else { | |
637 | npcm_fiux_set_direct_wr(fiu); | |
638 | } | |
639 | ||
640 | return 0; | |
641 | } | |
642 | ||
643 | static int npcm_fiu_setup(struct spi_device *spi) | |
644 | { | |
645 | struct spi_controller *ctrl = spi->master; | |
646 | struct npcm_fiu_spi *fiu = spi_controller_get_devdata(ctrl); | |
647 | struct npcm_fiu_chip *chip; | |
648 | ||
649 | chip = &fiu->chip[spi->chip_select]; | |
650 | chip->fiu = fiu; | |
651 | chip->chipselect = spi->chip_select; | |
652 | chip->clkrate = spi->max_speed_hz; | |
653 | ||
654 | fiu->clkrate = clk_get_rate(fiu->clk); | |
655 | ||
656 | return 0; | |
657 | } | |
658 | ||
659 | static const struct spi_controller_mem_ops npcm_fiu_mem_ops = { | |
660 | .exec_op = npcm_fiu_exec_op, | |
661 | .dirmap_create = npcm_fiu_dirmap_create, | |
662 | .dirmap_read = npcm_fiu_direct_read, | |
663 | .dirmap_write = npcm_fiu_direct_write, | |
664 | }; | |
665 | ||
666 | static const struct of_device_id npcm_fiu_dt_ids[] = { | |
667 | { .compatible = "nuvoton,npcm750-fiu", .data = &npxm7xx_fiu_data }, | |
668 | { /* sentinel */ } | |
669 | }; | |
670 | ||
671 | static int npcm_fiu_probe(struct platform_device *pdev) | |
672 | { | |
673 | const struct fiu_data *fiu_data_match; | |
674 | const struct of_device_id *match; | |
675 | struct device *dev = &pdev->dev; | |
676 | struct spi_controller *ctrl; | |
677 | struct npcm_fiu_spi *fiu; | |
678 | void __iomem *regbase; | |
679 | struct resource *res; | |
234266a5 | 680 | int id, ret; |
ace55c41 | 681 | |
04a9cd51 | 682 | ctrl = devm_spi_alloc_master(dev, sizeof(*fiu)); |
ace55c41 TM |
683 | if (!ctrl) |
684 | return -ENOMEM; | |
685 | ||
686 | fiu = spi_controller_get_devdata(ctrl); | |
687 | ||
688 | match = of_match_device(npcm_fiu_dt_ids, dev); | |
689 | if (!match || !match->data) { | |
690 | dev_err(dev, "No compatible OF match\n"); | |
691 | return -ENODEV; | |
692 | } | |
693 | ||
694 | fiu_data_match = match->data; | |
695 | id = of_alias_get_id(dev->of_node, "fiu"); | |
696 | if (id < 0 || id >= fiu_data_match->fiu_max) { | |
697 | dev_err(dev, "Invalid platform device id: %d\n", id); | |
698 | return -EINVAL; | |
699 | } | |
700 | ||
701 | fiu->info = &fiu_data_match->npcm_fiu_data_info[id]; | |
702 | ||
703 | platform_set_drvdata(pdev, fiu); | |
704 | fiu->dev = dev; | |
705 | ||
706 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control"); | |
707 | regbase = devm_ioremap_resource(dev, res); | |
708 | if (IS_ERR(regbase)) | |
709 | return PTR_ERR(regbase); | |
710 | ||
711 | fiu->regmap = devm_regmap_init_mmio(dev, regbase, | |
712 | &npcm_mtd_regmap_config); | |
713 | if (IS_ERR(fiu->regmap)) { | |
714 | dev_err(dev, "Failed to create regmap\n"); | |
715 | return PTR_ERR(fiu->regmap); | |
716 | } | |
717 | ||
718 | fiu->res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
719 | "memory"); | |
720 | fiu->clk = devm_clk_get(dev, NULL); | |
721 | if (IS_ERR(fiu->clk)) | |
722 | return PTR_ERR(fiu->clk); | |
723 | ||
724 | fiu->spix_mode = of_property_read_bool(dev->of_node, | |
725 | "nuvoton,spix-mode"); | |
726 | ||
727 | platform_set_drvdata(pdev, fiu); | |
728 | clk_prepare_enable(fiu->clk); | |
729 | ||
730 | ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | |
731 | | SPI_TX_DUAL | SPI_TX_QUAD; | |
732 | ctrl->setup = npcm_fiu_setup; | |
733 | ctrl->bus_num = -1; | |
734 | ctrl->mem_ops = &npcm_fiu_mem_ops; | |
735 | ctrl->num_chipselect = fiu->info->max_cs; | |
736 | ctrl->dev.of_node = dev->of_node; | |
737 | ||
234266a5 LW |
738 | ret = devm_spi_register_master(dev, ctrl); |
739 | if (ret) | |
740 | clk_disable_unprepare(fiu->clk); | |
741 | ||
742 | return ret; | |
ace55c41 TM |
743 | } |
744 | ||
745 | static int npcm_fiu_remove(struct platform_device *pdev) | |
746 | { | |
747 | struct npcm_fiu_spi *fiu = platform_get_drvdata(pdev); | |
748 | ||
749 | clk_disable_unprepare(fiu->clk); | |
750 | return 0; | |
751 | } | |
752 | ||
753 | MODULE_DEVICE_TABLE(of, npcm_fiu_dt_ids); | |
754 | ||
755 | static struct platform_driver npcm_fiu_driver = { | |
756 | .driver = { | |
757 | .name = "NPCM-FIU", | |
758 | .bus = &platform_bus_type, | |
759 | .of_match_table = npcm_fiu_dt_ids, | |
760 | }, | |
761 | .probe = npcm_fiu_probe, | |
762 | .remove = npcm_fiu_remove, | |
763 | }; | |
764 | module_platform_driver(npcm_fiu_driver); | |
765 | ||
766 | MODULE_DESCRIPTION("Nuvoton FLASH Interface Unit SPI Controller Driver"); | |
767 | MODULE_AUTHOR("Tomer Maimon <[email protected]>"); | |
768 | MODULE_LICENSE("GPL v2"); |