diff options
Diffstat (limited to 'drivers/spi/spi-mtk-nor.c')
| -rw-r--r-- | drivers/spi/spi-mtk-nor.c | 174 |
1 files changed, 123 insertions, 51 deletions
diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index 5c93730615f8..5cc4632e13d7 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -13,7 +13,8 @@ #include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/spi/spi.h> #include <linux/spi/spi-mem.h> @@ -80,6 +81,9 @@ #define MTK_NOR_REG_DMA_FADR 0x71c #define MTK_NOR_REG_DMA_DADR 0x720 #define MTK_NOR_REG_DMA_END_DADR 0x724 +#define MTK_NOR_REG_CG_DIS 0x728 +#define MTK_NOR_SFC_SW_RST BIT(2) + #define MTK_NOR_REG_DMA_DADR_HB 0x738 #define MTK_NOR_REG_DMA_END_DADR_HB 0x73c @@ -95,6 +99,17 @@ #define CLK_TO_US(sp, clkcnt) DIV_ROUND_UP(clkcnt, sp->spi_freq / 1000000) +struct mtk_nor_caps { + u8 dma_bits; + + /* extra_dummy_bit is adding for the IP of new SoCs. + * Some new SoCs modify the timing of fetching registers' values + * and IDs of nor flash, they need a extra_dummy_bit which can add + * more clock cycles for fetching data. + */ + u8 extra_dummy_bit; +}; + struct mtk_nor { struct spi_controller *ctlr; struct device *dev; @@ -104,11 +119,13 @@ struct mtk_nor { struct clk *spi_clk; struct clk *ctlr_clk; struct clk *axi_clk; + struct clk *axi_s_clk; unsigned int spi_freq; bool wbuf_en; bool has_irq; bool high_dma; struct completion op_done; + const struct mtk_nor_caps *caps; }; static inline void mtk_nor_rmw(struct mtk_nor *sp, u32 reg, u32 set, u32 clr) @@ -134,6 +151,15 @@ static inline int mtk_nor_cmd_exec(struct mtk_nor *sp, u32 cmd, ulong clk) return ret; } +static void mtk_nor_reset(struct mtk_nor *sp) +{ + mtk_nor_rmw(sp, MTK_NOR_REG_CG_DIS, 0, MTK_NOR_SFC_SW_RST); + mb(); /* flush previous writes */ + mtk_nor_rmw(sp, MTK_NOR_REG_CG_DIS, MTK_NOR_SFC_SW_RST, 0); + mb(); /* flush previous writes */ + writel(MTK_NOR_ENABLE_SF_CMD, sp->base + MTK_NOR_REG_WP); +} + static void mtk_nor_set_addr(struct mtk_nor *sp, const struct spi_mem_op *op) { u32 addr = op->addr.val; @@ -249,7 +275,7 @@ static void mtk_nor_adj_prg_size(struct spi_mem_op *op) static int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) { - struct mtk_nor *sp = spi_controller_get_devdata(mem->spi->master); + struct mtk_nor *sp = spi_controller_get_devdata(mem->spi->controller); if (!op->data.nbytes) return 0; @@ -341,7 +367,7 @@ static int mtk_nor_dma_exec(struct mtk_nor *sp, u32 from, unsigned int length, dma_addr_t dma_addr) { int ret = 0; - ulong delay; + u32 delay, timeout; u32 reg; writel(from, sp->base + MTK_NOR_REG_DMA_FADR); @@ -363,15 +389,16 @@ static int mtk_nor_dma_exec(struct mtk_nor *sp, u32 from, unsigned int length, mtk_nor_rmw(sp, MTK_NOR_REG_DMA_CTL, MTK_NOR_DMA_START, 0); delay = CLK_TO_US(sp, (length + 5) * BITS_PER_BYTE); + timeout = (delay + 1) * 100; if (sp->has_irq) { if (!wait_for_completion_timeout(&sp->op_done, - (delay + 1) * 100)) + usecs_to_jiffies(max(timeout, 10000U)))) ret = -ETIMEDOUT; } else { ret = readl_poll_timeout(sp->base + MTK_NOR_REG_DMA_CTL, reg, !(reg & MTK_NOR_DMA_START), delay / 3, - (delay + 1) * 100); + timeout); } if (ret < 0) @@ -430,36 +457,28 @@ static int mtk_nor_read_pio(struct mtk_nor *sp, const struct spi_mem_op *op) return ret; } -static int mtk_nor_write_buffer_enable(struct mtk_nor *sp) +static int mtk_nor_setup_write_buffer(struct mtk_nor *sp, bool on) { int ret; u32 val; - if (sp->wbuf_en) + if (!(sp->wbuf_en ^ on)) return 0; val = readl(sp->base + MTK_NOR_REG_CFG2); - writel(val | MTK_NOR_WR_BUF_EN, sp->base + MTK_NOR_REG_CFG2); - ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CFG2, val, - val & MTK_NOR_WR_BUF_EN, 0, 10000); - if (!ret) - sp->wbuf_en = true; - return ret; -} - -static int mtk_nor_write_buffer_disable(struct mtk_nor *sp) -{ - int ret; - u32 val; + if (on) { + writel(val | MTK_NOR_WR_BUF_EN, sp->base + MTK_NOR_REG_CFG2); + ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CFG2, val, + val & MTK_NOR_WR_BUF_EN, 0, 10000); + } else { + writel(val & ~MTK_NOR_WR_BUF_EN, sp->base + MTK_NOR_REG_CFG2); + ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CFG2, val, + !(val & MTK_NOR_WR_BUF_EN), 0, 10000); + } - if (!sp->wbuf_en) - return 0; - val = readl(sp->base + MTK_NOR_REG_CFG2); - writel(val & ~MTK_NOR_WR_BUF_EN, sp->base + MTK_NOR_REG_CFG2); - ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CFG2, val, - !(val & MTK_NOR_WR_BUF_EN), 0, 10000); if (!ret) - sp->wbuf_en = false; + sp->wbuf_en = on; + return ret; } @@ -469,7 +488,7 @@ static int mtk_nor_pp_buffered(struct mtk_nor *sp, const struct spi_mem_op *op) u32 val; int ret, i; - ret = mtk_nor_write_buffer_enable(sp); + ret = mtk_nor_setup_write_buffer(sp, true); if (ret < 0) return ret; @@ -488,7 +507,7 @@ static int mtk_nor_pp_unbuffered(struct mtk_nor *sp, const u8 *buf = op->data.buf.out; int ret; - ret = mtk_nor_write_buffer_disable(sp); + ret = mtk_nor_setup_write_buffer(sp, false); if (ret < 0) return ret; writeb(buf[0], sp->base + MTK_NOR_REG_WDATA); @@ -554,7 +573,12 @@ static int mtk_nor_spi_mem_prg(struct mtk_nor *sp, const struct spi_mem_op *op) } // trigger op - writel(prg_len * BITS_PER_BYTE, sp->base + MTK_NOR_REG_PRG_CNT); + if (rx_len) + writel(prg_len * BITS_PER_BYTE + sp->caps->extra_dummy_bit, + sp->base + MTK_NOR_REG_PRG_CNT); + else + writel(prg_len * BITS_PER_BYTE, sp->base + MTK_NOR_REG_PRG_CNT); + ret = mtk_nor_cmd_exec(sp, MTK_NOR_CMD_PROGRAM, prg_len * BITS_PER_BYTE); if (ret) @@ -574,7 +598,7 @@ static int mtk_nor_spi_mem_prg(struct mtk_nor *sp, const struct spi_mem_op *op) static int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) { - struct mtk_nor *sp = spi_controller_get_devdata(mem->spi->master); + struct mtk_nor *sp = spi_controller_get_devdata(mem->spi->controller); int ret; if ((op->data.nbytes == 0) || @@ -590,7 +614,7 @@ static int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) } if ((op->data.dir == SPI_MEM_DATA_IN) && mtk_nor_match_read(op)) { - ret = mtk_nor_write_buffer_disable(sp); + ret = mtk_nor_setup_write_buffer(sp, false); if (ret < 0) return ret; mtk_nor_setup_bus(sp, op); @@ -598,7 +622,15 @@ static int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) mtk_nor_set_addr(sp, op); return mtk_nor_read_pio(sp, op); } else { - return mtk_nor_read_dma(sp, op); + ret = mtk_nor_read_dma(sp, op); + if (unlikely(ret)) { + /* Handle rare bus glitch */ + mtk_nor_reset(sp); + mtk_nor_setup_bus(sp, op); + return mtk_nor_read_dma(sp, op); + } + + return ret; } } @@ -607,7 +639,7 @@ static int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) static int mtk_nor_setup(struct spi_device *spi) { - struct mtk_nor *sp = spi_controller_get_devdata(spi->master); + struct mtk_nor *sp = spi_controller_get_devdata(spi->controller); if (spi->max_speed_hz && (spi->max_speed_hz < sp->spi_freq)) { dev_err(&spi->dev, "spi clock should be %u Hz.\n", @@ -619,10 +651,10 @@ static int mtk_nor_setup(struct spi_device *spi) return 0; } -static int mtk_nor_transfer_one_message(struct spi_controller *master, +static int mtk_nor_transfer_one_message(struct spi_controller *host, struct spi_message *m) { - struct mtk_nor *sp = spi_controller_get_devdata(master); + struct mtk_nor *sp = spi_controller_get_devdata(host); struct spi_transfer *t = NULL; unsigned long trx_len = 0; int stat = 0; @@ -664,7 +696,7 @@ static int mtk_nor_transfer_one_message(struct spi_controller *master, m->actual_length = trx_len; msg_done: m->status = stat; - spi_finalize_current_message(master); + spi_finalize_current_message(host); return 0; } @@ -674,6 +706,7 @@ static void mtk_nor_disable_clk(struct mtk_nor *sp) clk_disable_unprepare(sp->spi_clk); clk_disable_unprepare(sp->ctlr_clk); clk_disable_unprepare(sp->axi_clk); + clk_disable_unprepare(sp->axi_s_clk); } static int mtk_nor_enable_clk(struct mtk_nor *sp) @@ -697,6 +730,14 @@ static int mtk_nor_enable_clk(struct mtk_nor *sp) return ret; } + ret = clk_prepare_enable(sp->axi_s_clk); + if (ret) { + clk_disable_unprepare(sp->spi_clk); + clk_disable_unprepare(sp->ctlr_clk); + clk_disable_unprepare(sp->axi_clk); + return ret; + } + return 0; } @@ -743,9 +784,25 @@ static const struct spi_controller_mem_ops mtk_nor_mem_ops = { .exec_op = mtk_nor_exec_op }; +static const struct mtk_nor_caps mtk_nor_caps_mt8173 = { + .dma_bits = 32, + .extra_dummy_bit = 0, +}; + +static const struct mtk_nor_caps mtk_nor_caps_mt8186 = { + .dma_bits = 32, + .extra_dummy_bit = 1, +}; + +static const struct mtk_nor_caps mtk_nor_caps_mt8192 = { + .dma_bits = 36, + .extra_dummy_bit = 0, +}; + static const struct of_device_id mtk_nor_match[] = { - { .compatible = "mediatek,mt8192-nor", .data = (void *)36 }, - { .compatible = "mediatek,mt8173-nor", .data = (void *)32 }, + { .compatible = "mediatek,mt8173-nor", .data = &mtk_nor_caps_mt8173 }, + { .compatible = "mediatek,mt8186-nor", .data = &mtk_nor_caps_mt8186 }, + { .compatible = "mediatek,mt8192-nor", .data = &mtk_nor_caps_mt8192 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mtk_nor_match); @@ -754,10 +811,10 @@ static int mtk_nor_probe(struct platform_device *pdev) { struct spi_controller *ctlr; struct mtk_nor *sp; + struct mtk_nor_caps *caps; void __iomem *base; - struct clk *spi_clk, *ctlr_clk, *axi_clk; + struct clk *spi_clk, *ctlr_clk, *axi_clk, *axi_s_clk; int ret, irq; - unsigned long dma_bits; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) @@ -775,13 +832,19 @@ static int mtk_nor_probe(struct platform_device *pdev) if (IS_ERR(axi_clk)) return PTR_ERR(axi_clk); - dma_bits = (unsigned long)of_device_get_match_data(&pdev->dev); - if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(dma_bits))) { - dev_err(&pdev->dev, "failed to set dma mask(%lu)\n", dma_bits); - return -EINVAL; + axi_s_clk = devm_clk_get_optional(&pdev->dev, "axi_s"); + if (IS_ERR(axi_s_clk)) + return PTR_ERR(axi_s_clk); + + caps = (struct mtk_nor_caps *)of_device_get_match_data(&pdev->dev); + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(caps->dma_bits)); + if (ret) { + dev_err(&pdev->dev, "failed to set dma mask(%u)\n", caps->dma_bits); + return ret; } - ctlr = devm_spi_alloc_master(&pdev->dev, sizeof(*sp)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*sp)); if (!ctlr) { dev_err(&pdev->dev, "failed to allocate spi controller\n"); return -ENOMEM; @@ -808,7 +871,9 @@ static int mtk_nor_probe(struct platform_device *pdev) sp->spi_clk = spi_clk; sp->ctlr_clk = ctlr_clk; sp->axi_clk = axi_clk; - sp->high_dma = (dma_bits > 32); + sp->axi_s_clk = axi_s_clk; + sp->caps = caps; + sp->high_dma = caps->dma_bits > 32; sp->buffer = dmam_alloc_coherent(&pdev->dev, MTK_NOR_BOUNCE_BUF_SIZE + MTK_NOR_DMA_ALIGN, &sp->buffer_dma, GFP_KERNEL); @@ -853,7 +918,6 @@ static int mtk_nor_probe(struct platform_device *pdev) if (ret < 0) goto err_probe; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); dev_info(&pdev->dev, "spi frequency: %d Hz\n", sp->spi_freq); @@ -870,7 +934,7 @@ err_probe: return ret; } -static int mtk_nor_remove(struct platform_device *pdev) +static void mtk_nor_remove(struct platform_device *pdev) { struct spi_controller *ctlr = dev_get_drvdata(&pdev->dev); struct mtk_nor *sp = spi_controller_get_devdata(ctlr); @@ -880,8 +944,6 @@ static int mtk_nor_remove(struct platform_device *pdev) pm_runtime_dont_use_autosuspend(&pdev->dev); mtk_nor_disable_clk(sp); - - return 0; } static int __maybe_unused mtk_nor_runtime_suspend(struct device *dev) @@ -909,7 +971,17 @@ static int __maybe_unused mtk_nor_suspend(struct device *dev) static int __maybe_unused mtk_nor_resume(struct device *dev) { - return pm_runtime_force_resume(dev); + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct mtk_nor *sp = spi_controller_get_devdata(ctlr); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + mtk_nor_init(sp); + + return 0; } static const struct dev_pm_ops mtk_nor_pm_ops = { |
