Lines Matching +full:wpcm450 +full:- +full:fiu

1 // SPDX-License-Identifier: GPL-2.0
10 #include <linux/spi/spi-mem.h>
53 /* The memory-mapped view of flash is 16 MiB long */
66 static void wpcm_fiu_set_opcode(struct wpcm_fiu_spi *fiu, u8 opcode) in wpcm_fiu_set_opcode() argument
68 writeb(opcode, fiu->regs + FIU_UMA_CODE); in wpcm_fiu_set_opcode()
71 static void wpcm_fiu_set_addr(struct wpcm_fiu_spi *fiu, u32 addr) in wpcm_fiu_set_addr() argument
73 writeb((addr >> 0) & 0xff, fiu->regs + FIU_UMA_AB0); in wpcm_fiu_set_addr()
74 writeb((addr >> 8) & 0xff, fiu->regs + FIU_UMA_AB1); in wpcm_fiu_set_addr()
75 writeb((addr >> 16) & 0xff, fiu->regs + FIU_UMA_AB2); in wpcm_fiu_set_addr()
78 static void wpcm_fiu_set_data(struct wpcm_fiu_spi *fiu, const u8 *data, unsigned int nbytes) in wpcm_fiu_set_data() argument
83 writeb(data[i], fiu->regs + FIU_UMA_DB0 + i); in wpcm_fiu_set_data()
86 static void wpcm_fiu_get_data(struct wpcm_fiu_spi *fiu, u8 *data, unsigned int nbytes) in wpcm_fiu_get_data() argument
91 data[i] = readb(fiu->regs + FIU_UMA_DB0 + i); in wpcm_fiu_get_data()
95 * Perform a UMA (User Mode Access) operation, i.e. a software-controlled SPI transfer.
97 static int wpcm_fiu_do_uma(struct wpcm_fiu_spi *fiu, unsigned int cs, in wpcm_fiu_do_uma() argument
109 writeb(cts, fiu->regs + FIU_UMA_CTS); in wpcm_fiu_do_uma()
112 if (!(readb(fiu->regs + FIU_UMA_CTS) & FIU_UMA_CTS_EXEC_DONE)) in wpcm_fiu_do_uma()
115 dev_info(fiu->dev, "UMA transfer has not finished in %d iterations\n", UMA_WAIT_ITERATIONS); in wpcm_fiu_do_uma()
116 return -EIO; in wpcm_fiu_do_uma()
119 static void wpcm_fiu_ects_assert(struct wpcm_fiu_spi *fiu, unsigned int cs) in wpcm_fiu_ects_assert() argument
121 u8 ects = readb(fiu->regs + FIU_UMA_ECTS); in wpcm_fiu_ects_assert()
124 writeb(ects, fiu->regs + FIU_UMA_ECTS); in wpcm_fiu_ects_assert()
127 static void wpcm_fiu_ects_deassert(struct wpcm_fiu_spi *fiu, unsigned int cs) in wpcm_fiu_ects_deassert() argument
129 u8 ects = readb(fiu->regs + FIU_UMA_ECTS); in wpcm_fiu_ects_deassert()
132 writeb(ects, fiu->regs + FIU_UMA_ECTS); in wpcm_fiu_ects_deassert()
143 if (op->cmd.opcode == 0x0b) in wpcm_fiu_normal_match()
146 return (op->addr.nbytes == 0 || op->addr.nbytes == 3) && in wpcm_fiu_normal_match()
147 op->dummy.nbytes == 0 && op->data.nbytes <= 4; in wpcm_fiu_normal_match()
152 struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(mem->spi->controller); in wpcm_fiu_normal_exec() local
155 wpcm_fiu_set_opcode(fiu, op->cmd.opcode); in wpcm_fiu_normal_exec()
156 wpcm_fiu_set_addr(fiu, op->addr.val); in wpcm_fiu_normal_exec()
157 if (op->data.dir == SPI_MEM_DATA_OUT) in wpcm_fiu_normal_exec()
158 wpcm_fiu_set_data(fiu, op->data.buf.out, op->data.nbytes); in wpcm_fiu_normal_exec()
160 ret = wpcm_fiu_do_uma(fiu, spi_get_chipselect(mem->spi, 0), op->addr.nbytes == 3, in wpcm_fiu_normal_exec()
161 op->data.dir == SPI_MEM_DATA_OUT, op->data.nbytes); in wpcm_fiu_normal_exec()
163 if (op->data.dir == SPI_MEM_DATA_IN) in wpcm_fiu_normal_exec()
164 wpcm_fiu_get_data(fiu, op->data.buf.in, op->data.nbytes); in wpcm_fiu_normal_exec()
171 return op->cmd.opcode == 0x0b && op->addr.nbytes == 3 && in wpcm_fiu_fast_read_match()
172 op->dummy.nbytes == 1 && in wpcm_fiu_fast_read_match()
173 op->data.nbytes >= 1 && op->data.nbytes <= 4 && in wpcm_fiu_fast_read_match()
174 op->data.dir == SPI_MEM_DATA_IN; in wpcm_fiu_fast_read_match()
179 return -EINVAL; in wpcm_fiu_fast_read_exec()
183 * 4-byte addressing.
186 * bytes: 13 aa bb cc dd -> 5a a5 f0 0f
187 * FIU's view: [ C A A A][ C D D D D]
188 * FIU mode: [ read/write][ read ]
192 return op->addr.nbytes == 4 && op->dummy.nbytes == 0 && op->data.nbytes <= 4; in wpcm_fiu_4ba_match()
197 struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(mem->spi->controller); in wpcm_fiu_4ba_exec() local
198 int cs = spi_get_chipselect(mem->spi, 0); in wpcm_fiu_4ba_exec()
200 wpcm_fiu_ects_assert(fiu, cs); in wpcm_fiu_4ba_exec()
202 wpcm_fiu_set_opcode(fiu, op->cmd.opcode); in wpcm_fiu_4ba_exec()
203 wpcm_fiu_set_addr(fiu, op->addr.val >> 8); in wpcm_fiu_4ba_exec()
204 wpcm_fiu_do_uma(fiu, cs, true, false, 0); in wpcm_fiu_4ba_exec()
206 wpcm_fiu_set_opcode(fiu, op->addr.val & 0xff); in wpcm_fiu_4ba_exec()
207 wpcm_fiu_set_addr(fiu, 0); in wpcm_fiu_4ba_exec()
208 if (op->data.dir == SPI_MEM_DATA_OUT) in wpcm_fiu_4ba_exec()
209 wpcm_fiu_set_data(fiu, op->data.buf.out, op->data.nbytes); in wpcm_fiu_4ba_exec()
210 wpcm_fiu_do_uma(fiu, cs, false, op->data.dir == SPI_MEM_DATA_OUT, op->data.nbytes); in wpcm_fiu_4ba_exec()
212 wpcm_fiu_ects_deassert(fiu, cs); in wpcm_fiu_4ba_exec()
214 if (op->data.dir == SPI_MEM_DATA_IN) in wpcm_fiu_4ba_exec()
215 wpcm_fiu_get_data(fiu, op->data.buf.in, op->data.nbytes); in wpcm_fiu_4ba_exec()
222 * be able to read 6 ID bytes and FIU can only read up to 4 at once.
235 return op->cmd.opcode == 0x9f && op->addr.nbytes == 0 && in wpcm_fiu_rdid_match()
236 op->dummy.nbytes == 0 && op->data.nbytes == 6 && in wpcm_fiu_rdid_match()
237 op->data.dir == SPI_MEM_DATA_IN; in wpcm_fiu_rdid_match()
242 struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(mem->spi->controller); in wpcm_fiu_rdid_exec() local
243 int cs = spi_get_chipselect(mem->spi, 0); in wpcm_fiu_rdid_exec()
246 wpcm_fiu_set_opcode(fiu, op->cmd.opcode); in wpcm_fiu_rdid_exec()
247 wpcm_fiu_set_addr(fiu, 0); in wpcm_fiu_rdid_exec()
248 wpcm_fiu_do_uma(fiu, cs, false, false, 3); in wpcm_fiu_rdid_exec()
249 wpcm_fiu_get_data(fiu, op->data.buf.in, 3); in wpcm_fiu_rdid_exec()
252 wpcm_fiu_set_opcode(fiu, op->cmd.opcode); in wpcm_fiu_rdid_exec()
253 wpcm_fiu_set_addr(fiu, 0); in wpcm_fiu_rdid_exec()
254 wpcm_fiu_do_uma(fiu, cs, true, false, 3); in wpcm_fiu_rdid_exec()
255 wpcm_fiu_get_data(fiu, op->data.buf.in + 3, 3); in wpcm_fiu_rdid_exec()
269 if (op->cmd.opcode == 0x0b) in wpcm_fiu_dummy_match()
272 return (op->addr.nbytes == 0 || op->addr.nbytes == 3) && in wpcm_fiu_dummy_match()
273 op->dummy.nbytes >= 1 && op->dummy.nbytes <= 5 && in wpcm_fiu_dummy_match()
274 op->data.nbytes <= 4; in wpcm_fiu_dummy_match()
279 struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(mem->spi->controller); in wpcm_fiu_dummy_exec() local
280 int cs = spi_get_chipselect(mem->spi, 0); in wpcm_fiu_dummy_exec()
282 wpcm_fiu_ects_assert(fiu, cs); in wpcm_fiu_dummy_exec()
285 wpcm_fiu_set_opcode(fiu, op->cmd.opcode); in wpcm_fiu_dummy_exec()
286 wpcm_fiu_set_addr(fiu, op->addr.val); in wpcm_fiu_dummy_exec()
287 wpcm_fiu_do_uma(fiu, cs, op->addr.nbytes != 0, true, op->dummy.nbytes - 1); in wpcm_fiu_dummy_exec()
290 wpcm_fiu_set_opcode(fiu, 0); in wpcm_fiu_dummy_exec()
291 wpcm_fiu_set_addr(fiu, 0); in wpcm_fiu_dummy_exec()
292 wpcm_fiu_do_uma(fiu, cs, false, false, op->data.nbytes); in wpcm_fiu_dummy_exec()
293 wpcm_fiu_get_data(fiu, op->data.buf.in, op->data.nbytes); in wpcm_fiu_dummy_exec()
295 wpcm_fiu_ects_deassert(fiu, cs); in wpcm_fiu_dummy_exec()
315 if (shape->match(op)) in wpcm_fiu_find_op_shape()
327 if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) in wpcm_fiu_supports_op()
330 if (op->cmd.buswidth > 1 || op->addr.buswidth > 1 || in wpcm_fiu_supports_op()
331 op->dummy.buswidth > 1 || op->data.buswidth > 1) in wpcm_fiu_supports_op()
341 static void wpcm_fiu_stall_host(struct wpcm_fiu_spi *fiu, bool stall) in wpcm_fiu_stall_host() argument
343 if (fiu->shm_regmap) { in wpcm_fiu_stall_host()
344 int res = regmap_update_bits(fiu->shm_regmap, SHM_FLASH_SIZE, in wpcm_fiu_stall_host()
348 dev_warn(fiu->dev, "Failed to (un)stall host memory accesses: %d\n", res); in wpcm_fiu_stall_host()
354 struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(mem->spi->controller); in wpcm_fiu_exec_op() local
357 wpcm_fiu_stall_host(fiu, true); in wpcm_fiu_exec_op()
360 return shape->exec(mem, op); in wpcm_fiu_exec_op()
362 wpcm_fiu_stall_host(fiu, false); in wpcm_fiu_exec_op()
364 return -EOPNOTSUPP; in wpcm_fiu_exec_op()
369 if (op->data.nbytes > 4) in wpcm_fiu_adjust_op_size()
370 op->data.nbytes = 4; in wpcm_fiu_adjust_op_size()
377 struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(desc->mem->spi->controller); in wpcm_fiu_dirmap_create() local
378 int cs = spi_get_chipselect(desc->mem->spi, 0); in wpcm_fiu_dirmap_create()
380 if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) in wpcm_fiu_dirmap_create()
381 return -EOPNOTSUPP; in wpcm_fiu_dirmap_create()
384 * Unfortunately, FIU only supports a 16 MiB direct mapping window (per in wpcm_fiu_dirmap_create()
389 if (desc->info.offset + desc->info.length > MAX_MEMORY_SIZE_PER_CS) in wpcm_fiu_dirmap_create()
390 return -EINVAL; in wpcm_fiu_dirmap_create()
393 if (cs * MAX_MEMORY_SIZE_PER_CS + desc->info.offset + desc->info.length > fiu->memory_size) in wpcm_fiu_dirmap_create()
394 return -EINVAL; in wpcm_fiu_dirmap_create()
401 struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(desc->mem->spi->controller); in wpcm_fiu_direct_read() local
402 int cs = spi_get_chipselect(desc->mem->spi, 0); in wpcm_fiu_direct_read()
405 return -ENOTSUPP; in wpcm_fiu_direct_read()
409 if (!fiu->memory || offs >= fiu->memory_size) in wpcm_fiu_direct_read()
410 return -ENOTSUPP; in wpcm_fiu_direct_read()
412 len = min_t(size_t, len, fiu->memory_size - offs); in wpcm_fiu_direct_read()
413 memcpy_fromio(buf, fiu->memory + offs, len); in wpcm_fiu_direct_read()
426 static void wpcm_fiu_hw_init(struct wpcm_fiu_spi *fiu) in wpcm_fiu_hw_init() argument
428 /* Configure memory-mapped flash access */ in wpcm_fiu_hw_init()
429 writeb(FIU_BURST_CFG_R16, fiu->regs + FIU_BURST_BFG); in wpcm_fiu_hw_init()
430 writeb(MAX_MEMORY_SIZE_TOTAL / (512 << 10), fiu->regs + FIU_CFG); in wpcm_fiu_hw_init()
431 writeb(MAX_MEMORY_SIZE_PER_CS / (512 << 10) | BIT(6), fiu->regs + FIU_SPI_FL_CFG); in wpcm_fiu_hw_init()
434 writeb(0x0f, fiu->regs + FIU_UMA_ECTS); in wpcm_fiu_hw_init()
439 struct device *dev = &pdev->dev; in wpcm_fiu_probe()
441 struct wpcm_fiu_spi *fiu; in wpcm_fiu_probe() local
444 ctrl = devm_spi_alloc_host(dev, sizeof(*fiu)); in wpcm_fiu_probe()
446 return -ENOMEM; in wpcm_fiu_probe()
448 fiu = spi_controller_get_devdata(ctrl); in wpcm_fiu_probe()
449 fiu->dev = dev; in wpcm_fiu_probe()
451 fiu->regs = devm_platform_ioremap_resource_byname(pdev, "control"); in wpcm_fiu_probe()
452 if (IS_ERR(fiu->regs)) in wpcm_fiu_probe()
453 return dev_err_probe(dev, PTR_ERR(fiu->regs), in wpcm_fiu_probe()
456 fiu->clk = devm_clk_get_enabled(dev, NULL); in wpcm_fiu_probe()
457 if (IS_ERR(fiu->clk)) in wpcm_fiu_probe()
458 return PTR_ERR(fiu->clk); in wpcm_fiu_probe()
461 fiu->memory = devm_ioremap_resource(dev, res); in wpcm_fiu_probe()
462 fiu->memory_size = min_t(size_t, resource_size(res), MAX_MEMORY_SIZE_TOTAL); in wpcm_fiu_probe()
463 if (IS_ERR(fiu->memory)) in wpcm_fiu_probe()
464 return dev_err_probe(dev, PTR_ERR(fiu->memory), in wpcm_fiu_probe()
467 fiu->shm_regmap = syscon_regmap_lookup_by_phandle_optional(dev->of_node, "nuvoton,shm"); in wpcm_fiu_probe()
469 wpcm_fiu_hw_init(fiu); in wpcm_fiu_probe()
471 ctrl->bus_num = -1; in wpcm_fiu_probe()
472 ctrl->mem_ops = &wpcm_fiu_mem_ops; in wpcm_fiu_probe()
473 ctrl->num_chipselect = 4; in wpcm_fiu_probe()
474 ctrl->dev.of_node = dev->of_node; in wpcm_fiu_probe()
477 * The FIU doesn't include a clock divider, the clock is entirely in wpcm_fiu_probe()
480 ctrl->min_speed_hz = clk_get_rate(fiu->clk); in wpcm_fiu_probe()
481 ctrl->max_speed_hz = clk_get_rate(fiu->clk); in wpcm_fiu_probe()
487 { .compatible = "nuvoton,wpcm450-fiu", },
494 .name = "wpcm450-fiu",
502 MODULE_DESCRIPTION("Nuvoton WPCM450 FIU SPI controller driver");