Lines Matching +full:sg2044 +full:- +full:spifmc +full:- +full:nor

1 // SPDX-License-Identifier: GPL-2.0-only
3 * SG2044 SPI NOR controller driver
14 #include <linux/spi/spi-mem.h>
101 static int sg2044_spifmc_wait_int(struct sg2044_spifmc *spifmc, u8 int_type) in sg2044_spifmc_wait_int() argument
105 return readl_poll_timeout(spifmc->io_base + SPIFMC_INT_STS, stat, in sg2044_spifmc_wait_int()
109 static int sg2044_spifmc_wait_xfer_size(struct sg2044_spifmc *spifmc, in sg2044_spifmc_wait_xfer_size() argument
114 return readl_poll_timeout(spifmc->io_base + SPIFMC_FIFO_PT, stat, in sg2044_spifmc_wait_xfer_size()
118 static u32 sg2044_spifmc_init_reg(struct sg2044_spifmc *spifmc) in sg2044_spifmc_init_reg() argument
122 reg = readl(spifmc->io_base + SPIFMC_TRAN_CSR); in sg2044_spifmc_init_reg()
132 writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR); in sg2044_spifmc_init_reg()
137 static ssize_t sg2044_spifmc_read_64k(struct sg2044_spifmc *spifmc, in sg2044_spifmc_read_64k() argument
146 reg = sg2044_spifmc_init_reg(spifmc); in sg2044_spifmc_read_64k()
147 reg |= (op->addr.nbytes + op->dummy.nbytes) << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT; in sg2044_spifmc_read_64k()
148 reg |= spifmc->chip_info->rd_fifo_int_trigger_level; in sg2044_spifmc_read_64k()
152 writel(0, spifmc->io_base + SPIFMC_FIFO_PT); in sg2044_spifmc_read_64k()
153 writeb(op->cmd.opcode, spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_read_64k()
155 for (i = op->addr.nbytes - 1; i >= 0; i--) in sg2044_spifmc_read_64k()
156 writeb((from >> i * 8) & 0xff, spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_read_64k()
158 for (i = 0; i < op->dummy.nbytes; i++) in sg2044_spifmc_read_64k()
159 writeb(0xff, spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_read_64k()
161 writel(len, spifmc->io_base + SPIFMC_TRAN_NUM); in sg2044_spifmc_read_64k()
162 writel(0, spifmc->io_base + SPIFMC_INT_STS); in sg2044_spifmc_read_64k()
164 writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR); in sg2044_spifmc_read_64k()
166 ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_RD_FIFO); in sg2044_spifmc_read_64k()
172 xfer_size = min_t(size_t, SPIFMC_MAX_FIFO_DEPTH, len - offset); in sg2044_spifmc_read_64k()
174 ret = sg2044_spifmc_wait_xfer_size(spifmc, xfer_size); in sg2044_spifmc_read_64k()
179 buf[i + offset] = readb(spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_read_64k()
184 ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_TRAN_DONE); in sg2044_spifmc_read_64k()
188 writel(0, spifmc->io_base + SPIFMC_FIFO_PT); in sg2044_spifmc_read_64k()
193 static ssize_t sg2044_spifmc_read(struct sg2044_spifmc *spifmc, in sg2044_spifmc_read() argument
198 loff_t from = op->addr.val; in sg2044_spifmc_read()
199 size_t len = op->data.nbytes; in sg2044_spifmc_read()
201 u8 *din = op->data.buf.in; in sg2044_spifmc_read()
205 xfer_size = min_t(size_t, SPIFMC_MAX_READ_SIZE, len - offset); in sg2044_spifmc_read()
207 ret = sg2044_spifmc_read_64k(spifmc, op, from, xfer_size, din); in sg2044_spifmc_read()
219 static ssize_t sg2044_spifmc_write(struct sg2044_spifmc *spifmc, in sg2044_spifmc_write() argument
223 const u8 *dout = op->data.buf.out; in sg2044_spifmc_write()
228 reg = sg2044_spifmc_init_reg(spifmc); in sg2044_spifmc_write()
229 reg |= (op->addr.nbytes + op->dummy.nbytes) << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT; in sg2044_spifmc_write()
234 writel(0, spifmc->io_base + SPIFMC_FIFO_PT); in sg2044_spifmc_write()
235 writeb(op->cmd.opcode, spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_write()
237 for (i = op->addr.nbytes - 1; i >= 0; i--) in sg2044_spifmc_write()
238 writeb((op->addr.val >> i * 8) & 0xff, spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_write()
240 for (i = 0; i < op->dummy.nbytes; i++) in sg2044_spifmc_write()
241 writeb(0xff, spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_write()
243 writel(0, spifmc->io_base + SPIFMC_INT_STS); in sg2044_spifmc_write()
244 writel(op->data.nbytes, spifmc->io_base + SPIFMC_TRAN_NUM); in sg2044_spifmc_write()
246 writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR); in sg2044_spifmc_write()
248 ret = sg2044_spifmc_wait_xfer_size(spifmc, 0); in sg2044_spifmc_write()
252 writel(0, spifmc->io_base + SPIFMC_FIFO_PT); in sg2044_spifmc_write()
255 while (offset < op->data.nbytes) { in sg2044_spifmc_write()
256 xfer_size = min_t(size_t, SPIFMC_MAX_FIFO_DEPTH, op->data.nbytes - offset); in sg2044_spifmc_write()
258 ret = sg2044_spifmc_wait_xfer_size(spifmc, 0); in sg2044_spifmc_write()
263 writeb(dout[i + offset], spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_write()
268 ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_TRAN_DONE); in sg2044_spifmc_write()
272 writel(0, spifmc->io_base + SPIFMC_FIFO_PT); in sg2044_spifmc_write()
277 static ssize_t sg2044_spifmc_tran_cmd(struct sg2044_spifmc *spifmc, in sg2044_spifmc_tran_cmd() argument
283 reg = sg2044_spifmc_init_reg(spifmc); in sg2044_spifmc_tran_cmd()
284 reg |= (op->addr.nbytes + op->dummy.nbytes) << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT; in sg2044_spifmc_tran_cmd()
288 writel(0, spifmc->io_base + SPIFMC_FIFO_PT); in sg2044_spifmc_tran_cmd()
289 writeb(op->cmd.opcode, spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_tran_cmd()
291 for (i = op->addr.nbytes - 1; i >= 0; i--) in sg2044_spifmc_tran_cmd()
292 writeb((op->addr.val >> i * 8) & 0xff, spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_tran_cmd()
294 for (i = 0; i < op->dummy.nbytes; i++) in sg2044_spifmc_tran_cmd()
295 writeb(0xff, spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_tran_cmd()
297 writel(0, spifmc->io_base + SPIFMC_INT_STS); in sg2044_spifmc_tran_cmd()
299 writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR); in sg2044_spifmc_tran_cmd()
301 ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_TRAN_DONE); in sg2044_spifmc_tran_cmd()
305 writel(0, spifmc->io_base + SPIFMC_FIFO_PT); in sg2044_spifmc_tran_cmd()
310 static void sg2044_spifmc_trans(struct sg2044_spifmc *spifmc, in sg2044_spifmc_trans() argument
313 if (op->data.dir == SPI_MEM_DATA_IN) in sg2044_spifmc_trans()
314 sg2044_spifmc_read(spifmc, op); in sg2044_spifmc_trans()
315 else if (op->data.dir == SPI_MEM_DATA_OUT) in sg2044_spifmc_trans()
316 sg2044_spifmc_write(spifmc, op); in sg2044_spifmc_trans()
318 sg2044_spifmc_tran_cmd(spifmc, op); in sg2044_spifmc_trans()
321 static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc, in sg2044_spifmc_trans_reg() argument
326 size_t len = op->data.nbytes; in sg2044_spifmc_trans_reg()
330 if (op->data.dir == SPI_MEM_DATA_IN) in sg2044_spifmc_trans_reg()
331 din = op->data.buf.in; in sg2044_spifmc_trans_reg()
333 dout = op->data.buf.out; in sg2044_spifmc_trans_reg()
335 reg = sg2044_spifmc_init_reg(spifmc); in sg2044_spifmc_trans_reg()
344 if (spifmc->chip_info->has_opt_reg) in sg2044_spifmc_trans_reg()
345 writel(SPIFMC_OPT_DISABLE_FIFO_FLUSH, spifmc->io_base + SPIFMC_OPT); in sg2044_spifmc_trans_reg()
352 if (op->cmd.opcode == 0x01) { in sg2044_spifmc_trans_reg()
355 writel(len, spifmc->io_base + SPIFMC_TRAN_NUM); in sg2044_spifmc_trans_reg()
359 writel(0, spifmc->io_base + SPIFMC_FIFO_PT); in sg2044_spifmc_trans_reg()
360 writeb(op->cmd.opcode, spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_trans_reg()
364 writeb(0xff, spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_trans_reg()
366 writeb(dout[i], spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_trans_reg()
369 writel(0, spifmc->io_base + SPIFMC_INT_STS); in sg2044_spifmc_trans_reg()
370 writel(len, spifmc->io_base + SPIFMC_TRAN_NUM); in sg2044_spifmc_trans_reg()
372 writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR); in sg2044_spifmc_trans_reg()
374 ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_TRAN_DONE); in sg2044_spifmc_trans_reg()
379 while (len--) in sg2044_spifmc_trans_reg()
380 *din++ = readb(spifmc->io_base + SPIFMC_FIFO_PORT); in sg2044_spifmc_trans_reg()
383 writel(0, spifmc->io_base + SPIFMC_FIFO_PT); in sg2044_spifmc_trans_reg()
391 struct sg2044_spifmc *spifmc; in sg2044_spifmc_exec_op() local
393 spifmc = spi_controller_get_devdata(mem->spi->controller); in sg2044_spifmc_exec_op()
395 mutex_lock(&spifmc->lock); in sg2044_spifmc_exec_op()
397 if (op->addr.nbytes == 0) in sg2044_spifmc_exec_op()
398 sg2044_spifmc_trans_reg(spifmc, op); in sg2044_spifmc_exec_op()
400 sg2044_spifmc_trans(spifmc, op); in sg2044_spifmc_exec_op()
402 mutex_unlock(&spifmc->lock); in sg2044_spifmc_exec_op()
411 static void sg2044_spifmc_init(struct sg2044_spifmc *spifmc) in sg2044_spifmc_init() argument
416 writel(0, spifmc->io_base + SPIFMC_DMMR); in sg2044_spifmc_init()
418 reg = readl(spifmc->io_base + SPIFMC_CTRL); in sg2044_spifmc_init()
422 writel(reg, spifmc->io_base + SPIFMC_CTRL); in sg2044_spifmc_init()
424 writel(0, spifmc->io_base + SPIFMC_CE_CTRL); in sg2044_spifmc_init()
426 tran_csr = readl(spifmc->io_base + SPIFMC_TRAN_CSR); in sg2044_spifmc_init()
430 writel(tran_csr, spifmc->io_base + SPIFMC_TRAN_CSR); in sg2044_spifmc_init()
435 struct device *dev = &pdev->dev; in sg2044_spifmc_probe()
437 struct sg2044_spifmc *spifmc; in sg2044_spifmc_probe() local
440 ctrl = devm_spi_alloc_host(&pdev->dev, sizeof(*spifmc)); in sg2044_spifmc_probe()
442 return -ENOMEM; in sg2044_spifmc_probe()
444 spifmc = spi_controller_get_devdata(ctrl); in sg2044_spifmc_probe()
446 spifmc->clk = devm_clk_get_enabled(&pdev->dev, NULL); in sg2044_spifmc_probe()
447 if (IS_ERR(spifmc->clk)) in sg2044_spifmc_probe()
448 return dev_err_probe(dev, PTR_ERR(spifmc->clk), "Cannot get and enable AHB clock\n"); in sg2044_spifmc_probe()
450 spifmc->dev = &pdev->dev; in sg2044_spifmc_probe()
451 spifmc->ctrl = ctrl; in sg2044_spifmc_probe()
453 spifmc->io_base = devm_platform_ioremap_resource(pdev, 0); in sg2044_spifmc_probe()
454 if (IS_ERR(spifmc->io_base)) in sg2044_spifmc_probe()
455 return PTR_ERR(spifmc->io_base); in sg2044_spifmc_probe()
457 ctrl->num_chipselect = 1; in sg2044_spifmc_probe()
458 ctrl->dev.of_node = pdev->dev.of_node; in sg2044_spifmc_probe()
459 ctrl->bits_per_word_mask = SPI_BPW_MASK(8); in sg2044_spifmc_probe()
460 ctrl->auto_runtime_pm = false; in sg2044_spifmc_probe()
461 ctrl->mem_ops = &sg2044_spifmc_mem_ops; in sg2044_spifmc_probe()
462 ctrl->mode_bits = SPI_RX_DUAL | SPI_TX_DUAL | SPI_RX_QUAD | SPI_TX_QUAD; in sg2044_spifmc_probe()
464 ret = devm_mutex_init(dev, &spifmc->lock); in sg2044_spifmc_probe()
467 spifmc->chip_info = device_get_match_data(&pdev->dev); in sg2044_spifmc_probe()
468 if (!spifmc->chip_info) { in sg2044_spifmc_probe()
469 dev_err(&pdev->dev, "Failed to get specific chip info\n"); in sg2044_spifmc_probe()
470 return -EINVAL; in sg2044_spifmc_probe()
473 sg2044_spifmc_init(spifmc); in sg2044_spifmc_probe()
474 sg2044_spifmc_init_reg(spifmc); in sg2044_spifmc_probe()
476 ret = devm_spi_register_controller(&pdev->dev, ctrl); in sg2044_spifmc_probe()
494 { .compatible = "sophgo,sg2044-spifmc-nor", .data = &sg2044_chip_info },
495 { .compatible = "sophgo,sg2042-spifmc-nor", .data = &sg2042_chip_info },
502 .name = "sg2044,spifmc-nor",
509 MODULE_DESCRIPTION("SG2044 SPI NOR controller driver");