diff options
author | leilk.liu <leilk.liu@mediatek.com> | 2021-02-07 06:09:53 +0300 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2021-02-08 14:41:23 +0300 |
commit | 9f6e7e8d432e39d4f3d5d3c80129aec7f383b2b4 (patch) | |
tree | 1a709119885558547bc131e02d007d80a344ccd7 /drivers/spi/spi-mt65xx.c | |
parent | 0486d9f91d373e7f47276f30898ee0cb12656a70 (diff) | |
download | linux-9f6e7e8d432e39d4f3d5d3c80129aec7f383b2b4.tar.xz |
spi: mediatek: add set_cs_timing support
this patch add set_cs_timing support for HW CS mode.
Signed-off-by: leilk.liu <leilk.liu@mediatek.com>
Link: https://lore.kernel.org/r/20210207030953.9297-4-leilk.liu@mediatek.com
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/spi/spi-mt65xx.c')
-rw-r--r-- | drivers/spi/spi-mt65xx.c | 72 |
1 files changed, 56 insertions, 16 deletions
diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 5d643051bf3d..976f73b9e299 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -287,7 +287,7 @@ static void mtk_spi_set_cs(struct spi_device *spi, bool enable) static void mtk_spi_prepare_transfer(struct spi_master *master, struct spi_transfer *xfer) { - u32 spi_clk_hz, div, sck_time, cs_time, reg_val; + u32 spi_clk_hz, div, sck_time, reg_val; struct mtk_spi *mdata = spi_master_get_devdata(master); spi_clk_hz = clk_get_rate(mdata->spi_clk); @@ -297,32 +297,25 @@ static void mtk_spi_prepare_transfer(struct spi_master *master, div = 1; sck_time = (div + 1) / 2; - cs_time = sck_time * 2; if (mdata->dev_comp->enhance_timing) { - reg_val = (((sck_time - 1) & 0xffff) + reg_val = readl(mdata->base + SPI_CFG2_REG); + reg_val &= ~(0xffff << SPI_CFG2_SCK_HIGH_OFFSET); + reg_val |= (((sck_time - 1) & 0xffff) << SPI_CFG2_SCK_HIGH_OFFSET); + reg_val &= ~(0xffff << SPI_CFG2_SCK_LOW_OFFSET); reg_val |= (((sck_time - 1) & 0xffff) << SPI_CFG2_SCK_LOW_OFFSET); writel(reg_val, mdata->base + SPI_CFG2_REG); - reg_val = (((cs_time - 1) & 0xffff) - << SPI_ADJUST_CFG0_CS_HOLD_OFFSET); - reg_val |= (((cs_time - 1) & 0xffff) - << SPI_ADJUST_CFG0_CS_SETUP_OFFSET); - writel(reg_val, mdata->base + SPI_CFG0_REG); } else { - reg_val = (((sck_time - 1) & 0xff) + reg_val = readl(mdata->base + SPI_CFG0_REG); + reg_val &= ~(0xff << SPI_CFG0_SCK_HIGH_OFFSET); + reg_val |= (((sck_time - 1) & 0xff) << SPI_CFG0_SCK_HIGH_OFFSET); + reg_val &= ~(0xff << SPI_CFG0_SCK_LOW_OFFSET); reg_val |= (((sck_time - 1) & 0xff) << SPI_CFG0_SCK_LOW_OFFSET); - reg_val |= (((cs_time - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET); - reg_val |= (((cs_time - 1) & 0xff) << SPI_CFG0_CS_SETUP_OFFSET); writel(reg_val, mdata->base + SPI_CFG0_REG); } - - reg_val = readl(mdata->base + SPI_CFG1_REG); - reg_val &= ~SPI_CFG1_CS_IDLE_MASK; - reg_val |= (((cs_time - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET); - writel(reg_val, mdata->base + SPI_CFG1_REG); } static void mtk_spi_setup_packet(struct spi_master *master) @@ -513,6 +506,52 @@ static bool mtk_spi_can_dma(struct spi_master *master, (unsigned long)xfer->rx_buf % 4 == 0); } +static int mtk_spi_set_hw_cs_timing(struct spi_device *spi, + struct spi_delay *setup, + struct spi_delay *hold, + struct spi_delay *inactive) +{ + struct mtk_spi *mdata = spi_master_get_devdata(spi->master); + u16 setup_dly, hold_dly, inactive_dly; + u32 reg_val; + + if ((setup && setup->unit != SPI_DELAY_UNIT_SCK) || + (hold && hold->unit != SPI_DELAY_UNIT_SCK) || + (inactive && inactive->unit != SPI_DELAY_UNIT_SCK)) { + dev_err(&spi->dev, + "Invalid delay unit, should be SPI_DELAY_UNIT_SCK\n"); + return -EINVAL; + } + + setup_dly = setup ? setup->value : 1; + hold_dly = hold ? hold->value : 1; + inactive_dly = inactive ? inactive->value : 1; + + reg_val = readl(mdata->base + SPI_CFG0_REG); + if (mdata->dev_comp->enhance_timing) { + reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_HOLD_OFFSET); + reg_val |= (((hold_dly - 1) & 0xffff) + << SPI_ADJUST_CFG0_CS_HOLD_OFFSET); + reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_SETUP_OFFSET); + reg_val |= (((setup_dly - 1) & 0xffff) + << SPI_ADJUST_CFG0_CS_SETUP_OFFSET); + } else { + reg_val &= ~(0xff << SPI_CFG0_CS_HOLD_OFFSET); + reg_val |= (((hold_dly - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET); + reg_val &= ~(0xff << SPI_CFG0_CS_SETUP_OFFSET); + reg_val |= (((setup_dly - 1) & 0xff) + << SPI_CFG0_CS_SETUP_OFFSET); + } + writel(reg_val, mdata->base + SPI_CFG0_REG); + + reg_val = readl(mdata->base + SPI_CFG1_REG); + reg_val &= ~SPI_CFG1_CS_IDLE_MASK; + reg_val |= (((inactive_dly - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET); + writel(reg_val, mdata->base + SPI_CFG1_REG); + + return 0; +} + static int mtk_spi_setup(struct spi_device *spi) { struct mtk_spi *mdata = spi_master_get_devdata(spi->master); @@ -644,6 +683,7 @@ static int mtk_spi_probe(struct platform_device *pdev) master->transfer_one = mtk_spi_transfer_one; master->can_dma = mtk_spi_can_dma; master->setup = mtk_spi_setup; + master->set_cs_timing = mtk_spi_set_hw_cs_timing; of_id = of_match_node(mtk_spi_of_match, pdev->dev.of_node); if (!of_id) { |