[PATCH v1 1/1] spi: spi-mt65xx: add cs timing support
Leilk.Liu
leilk.liu at mediatek.com
Tue Jan 5 02:57:20 EST 2021
On Thu, 2020-12-31 at 17:57 +0800, Mason.Zhang at mediatek.com wrote:
> From: mtk22786 <Mason.Zhang at mediatek.com>
>
> spi user can set cs_setup, cs_hold, cs_inactive by call function:
> spi_set_cs_timing. If user have not call this, spi master will use
> default cs timing.
>
> Change-Id: I0189bab9a066d384e1f265846dac46816bf4561c
> Signed-off-by: Mason.Zhang
> ---
> drivers/spi/spi-mt65xx.c | 74 ++++++++++++++++++++++++++++++++++------
> 1 file changed, 63 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
> index 5d643051bf3d..23d94eb9a465 100644
> --- a/drivers/spi/spi-mt65xx.c
> +++ b/drivers/spi/spi-mt65xx.c
> @@ -284,11 +284,38 @@ static void mtk_spi_set_cs(struct spi_device *spi, bool enable)
> }
> }
>
> +static int mtk_set_cs_timing(struct spi_device *spi,
> + struct spi_delay *setup,
> + struct spi_delay *hold,
> + struct spi_delay *inactive)
> +{
> + u32 len = sizeof(struct spi_delay);
> +
> + if (setup)
> + memcpy(&spi->controller->cs_setup, setup, len);
> + else
> + memset(&spi->controller->cs_setup, 0, len);
> +
> + if (hold)
> + memcpy(&spi->controller->cs_hold, hold, len);
> + else
> + memset(&spi->controller->cs_hold, 0, len);
> +
> + if (inactive)
> + memcpy(&spi->controller->cs_inactive, inactive, len);
> + else
> + memset(&spi->controller->cs_inactive, 0, len);
> +
> + return 0;
> +}
> +
> static void mtk_spi_prepare_transfer(struct spi_master *master,
> - struct spi_transfer *xfer)
> + struct spi_transfer *xfer, struct spi_device *spi)
> {
> - u32 spi_clk_hz, div, sck_time, cs_time, reg_val;
> + u32 spi_clk_hz, div, sck_time, cs_time, reg_val, delay = 0;
> struct mtk_spi *mdata = spi_master_get_devdata(master);
> + u32 cs_setuptime, cs_holdtime, cs_idletime = 0;
> + u32 clk_period_ns = 0;
>
> spi_clk_hz = clk_get_rate(mdata->spi_clk);
> if (xfer->speed_hz < spi_clk_hz / 2)
> @@ -297,31 +324,55 @@ static void mtk_spi_prepare_transfer(struct spi_master *master,
> div = 1;
>
> sck_time = (div + 1) / 2;
> + /* set default cs timing */
> cs_time = sck_time * 2;
>
> + clk_period_ns = 1000000000 / xfer->speed_hz;
> +
> + delay = spi_delay_to_ns(&master->cs_setup, xfer);
> + cs_setuptime = delay / clk_period_ns;
> +
> + delay = spi_delay_to_ns(&master->cs_hold, xfer);
> + cs_holdtime = delay / clk_period_ns;
> +
> + delay = spi_delay_to_ns(&master->cs_inactive, xfer);
> + cs_idletime = delay / clk_period_ns;
> +
> + if (cs_setuptime < cs_time)
> + cs_setuptime = cs_time;
> +
> + if (cs_holdtime < cs_time)
> + cs_holdtime = cs_time;
> +
> + if (cs_idletime < cs_time)
> + cs_idletime = cs_time;
> +
> if (mdata->dev_comp->enhance_timing) {
> reg_val = (((sck_time - 1) & 0xffff)
> << SPI_CFG2_SCK_HIGH_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)
> + reg_val = (((cs_holdtime - 1) & 0xffff)
> << SPI_ADJUST_CFG0_CS_HOLD_OFFSET);
> - reg_val |= (((cs_time - 1) & 0xffff)
> + reg_val |= (((cs_setuptime - 1) & 0xffff)
> << SPI_ADJUST_CFG0_CS_SETUP_OFFSET);
> writel(reg_val, mdata->base + SPI_CFG0_REG);
> } else {
> reg_val = (((sck_time - 1) & 0xff)
> - << SPI_CFG0_SCK_HIGH_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);
> + << SPI_CFG0_SCK_HIGH_OFFSET);
> + reg_val |= (((sck_time - 1) & 0xff)
> + << SPI_CFG0_SCK_LOW_OFFSET);
> + reg_val |= (((cs_holdtime - 1) & 0xff)
> + << SPI_CFG0_CS_HOLD_OFFSET);
> + reg_val |= (((cs_setuptime - 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);
> + reg_val |= (((cs_idletime - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET);
> writel(reg_val, mdata->base + SPI_CFG1_REG);
> }
>
> @@ -430,7 +481,7 @@ static int mtk_spi_fifo_transfer(struct spi_master *master,
> mdata->cur_transfer = xfer;
> mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, xfer->len);
> mdata->num_xfered = 0;
> - mtk_spi_prepare_transfer(master, xfer);
> + mtk_spi_prepare_transfer(master, xfer, spi);
> mtk_spi_setup_packet(master);
>
> cnt = xfer->len / 4;
> @@ -462,7 +513,7 @@ static int mtk_spi_dma_transfer(struct spi_master *master,
> mdata->cur_transfer = xfer;
> mdata->num_xfered = 0;
>
> - mtk_spi_prepare_transfer(master, xfer);
> + mtk_spi_prepare_transfer(master, xfer, spi);
>
> cmd = readl(mdata->base + SPI_CMD_REG);
> if (xfer->tx_buf)
> @@ -644,6 +695,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_set_cs_timing;
while add set_cs_timing callback, this driver can't support multiple
devices on one bus(use "cs-gpios" property) with gpio cs timing.
>
> of_id = of_match_node(mtk_spi_of_match, pdev->dev.of_node);
> if (!of_id) {
More information about the linux-arm-kernel
mailing list