Commit 8e886ac8 authored by Maksim Kiselev's avatar Maksim Kiselev Committed by Mark Brown

spi: sun6i: add quirk for in-controller clock divider

Previously SPI controllers in Allwinner SoCs has a clock divider inside.
However now the clock divider is removed and to set the transfer clock
rate it's only needed to set the SPI module clock to the target value
and configure a proper work mode.

According to the datasheet there are three work modes:

| SPI Sample Mode         | SDM(bit13) | SDC(bit11) | Run Clock |
|-------------------------|------------|------------|-----------|
| normal sample           |      1     |      0     | <= 24 MHz |
| delay half cycle sample |      0     |      0     | <= 40 MHz |
| delay one cycle sample  |      0     |      1     | >= 80 MHz |

Add a quirk for this kind of SPI controllers.

Co-developed-by: Icenowy Zheng <icenowy@aosc.io
Signed-off-by: Maksim Kiselev <bigunclemax@gmail.com
Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com
Reviewed-by: Andre Przywara <andre.przywara@arm.com
Link: https://lore.kernel.org/r/20230510081121.3463710-4-bigunclemax@gmail.com
Signed-off-by: Mark Brown <broonie@kernel.org
parent b00c0d89
......@@ -42,7 +42,9 @@
#define SUN6I_TFR_CTL_CS_MANUAL BIT(6)
#define SUN6I_TFR_CTL_CS_LEVEL BIT(7)
#define SUN6I_TFR_CTL_DHB BIT(8)
#define SUN6I_TFR_CTL_SDC BIT(11)
#define SUN6I_TFR_CTL_FBS BIT(12)
#define SUN6I_TFR_CTL_SDM BIT(13)
#define SUN6I_TFR_CTL_XCH BIT(31)
#define SUN6I_INT_CTL_REG 0x10
......@@ -87,6 +89,7 @@
struct sun6i_spi_cfg {
unsigned long fifo_depth;
bool has_clk_ctl;
};
struct sun6i_spi {
......@@ -260,7 +263,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
struct spi_transfer *tfr)
{
struct sun6i_spi *sspi = spi_master_get_devdata(master);
unsigned int mclk_rate, div, div_cdr1, div_cdr2, timeout;
unsigned int div, div_cdr1, div_cdr2, timeout;
unsigned int start, end, tx_time;
unsigned int trig_level;
unsigned int tx_len = 0, rx_len = 0;
......@@ -350,8 +353,10 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
if (sspi->cfg->has_clk_ctl) {
unsigned int mclk_rate = clk_get_rate(sspi->mclk);
/* 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);
mclk_rate = clk_get_rate(sspi->mclk);
......@@ -383,6 +388,30 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
}
sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
} else {
clk_set_rate(sspi->mclk, tfr->speed_hz);
tfr->effective_speed_hz = clk_get_rate(sspi->mclk);
/*
* Configure work mode.
*
* There are three work modes depending on the controller clock
* frequency:
* - normal sample mode : CLK <= 24MHz SDM=1 SDC=0
* - delay half-cycle sample mode : CLK <= 40MHz SDM=0 SDC=0
* - delay one-cycle sample mode : CLK >= 80MHz SDM=0 SDC=1
*/
reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
reg &= ~(SUN6I_TFR_CTL_SDM | SUN6I_TFR_CTL_SDC);
if (tfr->effective_speed_hz <= 24000000)
reg |= SUN6I_TFR_CTL_SDM;
else if (tfr->effective_speed_hz >= 80000000)
reg |= SUN6I_TFR_CTL_SDC;
sun6i_spi_write(sspi, SUN6I_TFR_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;
......@@ -701,10 +730,12 @@ static void sun6i_spi_remove(struct platform_device *pdev)
static const struct sun6i_spi_cfg sun6i_a31_spi_cfg = {
.fifo_depth = SUN6I_FIFO_DEPTH,
.has_clk_ctl = true,
};
static const struct sun6i_spi_cfg sun8i_h3_spi_cfg = {
.fifo_depth = SUN8I_FIFO_DEPTH,
.has_clk_ctl = true,
};
static const struct of_device_id sun6i_spi_match[] = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment