From ec8dfb455da3822451129257ab21e2f0d03a6ae3 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Fri, 16 Jul 2021 21:46:31 -0500 Subject: [PATCH 076/117] spi: spi-sun6i: Add Allwinner R329 support Signed-off-by: Samuel Holland --- drivers/spi/spi-sun6i.c | 78 ++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 29 deletions(-) --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -30,6 +30,7 @@ #define SUN6I_GBL_CTL_REG 0x04 #define SUN6I_GBL_CTL_BUS_ENABLE BIT(0) #define SUN6I_GBL_CTL_MASTER BIT(1) +#define SUN6I_GBL_CTL_SAMPLE_MODE BIT(2) #define SUN6I_GBL_CTL_TP BIT(7) #define SUN6I_GBL_CTL_RST BIT(31) @@ -87,6 +88,8 @@ struct sun6i_spi_quirks { unsigned long fifo_depth; + bool has_divider : 1; + bool has_new_sample_mode : 1; }; struct sun6i_spi { @@ -362,38 +365,44 @@ static int sun6i_spi_transfer_one(struct sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); /* Ensure that we have a parent clock fast enough */ - mclk_rate = clk_get_rate(sspi->mclk); - if (mclk_rate < (2 * tfr->speed_hz)) { - clk_set_rate(sspi->mclk, 2 * tfr->speed_hz); + if (sspi->quirks->has_divider) { mclk_rate = clk_get_rate(sspi->mclk); - } + if (mclk_rate < (2 * tfr->speed_hz)) { + clk_set_rate(sspi->mclk, 2 * tfr->speed_hz); + mclk_rate = clk_get_rate(sspi->mclk); + } - /* - * Setup clock divider. - * - * We have two choices there. Either we can use the clock - * divide rate 1, which is calculated thanks to this formula: - * SPI_CLK = MOD_CLK / (2 ^ cdr) - * Or we can use CDR2, which is calculated with the formula: - * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) - * Wether we use the former or the latter is set through the - * DRS bit. - * - * First try CDR2, and if we can't reach the expected - * frequency, fall back to CDR1. - */ - div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz); - div_cdr2 = DIV_ROUND_UP(div_cdr1, 2); - if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) { - reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS; - tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2); + /* + * Setup clock divider. + * + * We have two choices there. Either we can use the clock + * divide rate 1, which is calculated thanks to this formula: + * SPI_CLK = MOD_CLK / (2 ^ cdr) + * Or we can use CDR2, which is calculated with the formula: + * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) + * Wether we use the former or the latter is set through the + * DRS bit. + * + * First try CDR2, and if we can't reach the expected + * frequency, fall back to CDR1. + */ + div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz); + div_cdr2 = DIV_ROUND_UP(div_cdr1, 2); + if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) { + reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS; + tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2); + } else { + div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1)); + reg = SUN6I_CLK_CTL_CDR1(div); + tfr->effective_speed_hz = mclk_rate / (1 << div); + } + sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg); } else { - div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1)); - reg = SUN6I_CLK_CTL_CDR1(div); - tfr->effective_speed_hz = mclk_rate / (1 << div); + clk_set_rate(sspi->mclk, tfr->speed_hz); + mclk_rate = clk_get_rate(sspi->mclk); + tfr->effective_speed_hz = mclk_rate; } - sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg); /* Finally enable the bus - doing so before might raise SCK to HIGH */ reg = sun6i_spi_read(sspi, SUN6I_GBL_CTL_REG); reg |= SUN6I_GBL_CTL_BUS_ENABLE; @@ -518,6 +527,7 @@ static int sun6i_spi_runtime_resume(stru struct spi_master *master = dev_get_drvdata(dev); struct sun6i_spi *sspi = spi_master_get_devdata(master); int ret; + u32 reg; ret = clk_prepare_enable(sspi->hclk); if (ret) { @@ -537,8 +547,10 @@ static int sun6i_spi_runtime_resume(stru goto err2; } - sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG, - SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP); + reg = SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP; + if (sspi->quirks->has_new_sample_mode) + reg |= SUN6I_GBL_CTL_SAMPLE_MODE; + sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG, reg); return 0; @@ -729,15 +741,23 @@ static int sun6i_spi_remove(struct platf static const struct sun6i_spi_quirks sun6i_a31_spi_quirks = { .fifo_depth = SUN6I_FIFO_DEPTH, + .has_divider = true, }; static const struct sun6i_spi_quirks sun8i_h3_spi_quirks = { .fifo_depth = SUN8I_FIFO_DEPTH, + .has_divider = true, +}; + +static const struct sun6i_spi_quirks sun50i_r329_spi_quirks = { + .fifo_depth = SUN8I_FIFO_DEPTH, + .has_new_sample_mode = true, }; static const struct of_device_id sun6i_spi_match[] = { { .compatible = "allwinner,sun6i-a31-spi", .data = &sun6i_a31_spi_quirks }, { .compatible = "allwinner,sun8i-h3-spi", .data = &sun8i_h3_spi_quirks }, + { .compatible = "allwinner,sun50i-r329-spi", .data = &sun50i_r329_spi_quirks }, {} }; MODULE_DEVICE_TABLE(of, sun6i_spi_match);