diff options
Diffstat (limited to 'drivers/spi/spi-rzv2h-rspi.c')
| -rw-r--r-- | drivers/spi/spi-rzv2h-rspi.c | 303 |
1 files changed, 262 insertions, 41 deletions
diff --git a/drivers/spi/spi-rzv2h-rspi.c b/drivers/spi/spi-rzv2h-rspi.c index dcc431ba60a9..1db7e4e5d64e 100644 --- a/drivers/spi/spi-rzv2h-rspi.c +++ b/drivers/spi/spi-rzv2h-rspi.c @@ -24,6 +24,7 @@ /* Registers */ #define RSPI_SPDR 0x00 #define RSPI_SPCR 0x08 +#define RSPI_SPPCR 0x0e #define RSPI_SSLP 0x10 #define RSPI_SPBR 0x11 #define RSPI_SPSCR 0x13 @@ -34,13 +35,18 @@ #define RSPI_SPFCR 0x6c /* Register SPCR */ +#define RSPI_SPCR_BPEN BIT(31) #define RSPI_SPCR_MSTR BIT(30) #define RSPI_SPCR_SPRIE BIT(17) #define RSPI_SPCR_SCKASE BIT(12) #define RSPI_SPCR_SPE BIT(0) +/* Register SPPCR */ +#define RSPI_SPPCR_SPLP2 BIT(1) + /* Register SPBR */ #define RSPI_SPBR_SPR_MIN 0 +#define RSPI_SPBR_SPR_PCLK_MIN 1 #define RSPI_SPBR_SPR_MAX 255 /* Register SPCMD */ @@ -58,7 +64,6 @@ /* Register SPDCR2 */ #define RSPI_SPDCR2_TTRG GENMASK(11, 8) #define RSPI_SPDCR2_RTRG GENMASK(3, 0) -#define RSPI_FIFO_SIZE 16 /* Register SPSR */ #define RSPI_SPSR_SPRF BIT(15) @@ -67,17 +72,41 @@ #define RSPI_SPSRC_CLEAR 0xfd80 #define RSPI_RESET_NUM 2 -#define RSPI_CLK_NUM 3 + +struct rzv2h_rspi_best_clock { + struct clk *clk; + unsigned long clk_rate; + unsigned long error; + u32 actual_hz; + u8 brdv; + u8 spr; +}; + +struct rzv2h_rspi_info { + void (*find_tclk_rate)(struct clk *clk, u32 hz, u8 spr_min, u8 spr_max, + struct rzv2h_rspi_best_clock *best_clk); + void (*find_pclk_rate)(struct clk *clk, u32 hz, u8 spr_low, u8 spr_high, + struct rzv2h_rspi_best_clock *best_clk); + const char *tclk_name; + unsigned int fifo_size; + unsigned int num_clks; +}; struct rzv2h_rspi_priv { struct reset_control_bulk_data resets[RSPI_RESET_NUM]; struct spi_controller *controller; + const struct rzv2h_rspi_info *info; void __iomem *base; struct clk *tclk; + struct clk *pclk; wait_queue_head_t wait; unsigned int bytes_per_word; + u32 last_speed_hz; u32 freq; u16 status; + u8 spr; + u8 brdv; + bool use_pclk; }; #define RZV2H_RSPI_TX(func, type) \ @@ -232,9 +261,112 @@ static inline u32 rzv2h_rspi_calc_bitrate(unsigned long tclk_rate, u8 spr, return DIV_ROUND_UP(tclk_rate, (2 * (spr + 1) * (1 << brdv))); } -static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz) +static void rzv2h_rspi_find_rate_variable(struct clk *clk, u32 hz, + u8 spr_min, u8 spr_max, + struct rzv2h_rspi_best_clock *best) +{ + long clk_rate, clk_min_rate, clk_max_rate; + int min_rate_spr, max_rate_spr; + unsigned long error; + u32 actual_hz; + u8 brdv; + int spr; + + /* + * On T2H / N2H, the source for the SPI clock is PCLKSPIn, which is a + * 1/32, 1/30, 1/25 or 1/24 divider of PLL4, which is 2400MHz, + * resulting in either 75MHz, 80MHz, 96MHz or 100MHz. + */ + clk_min_rate = clk_round_rate(clk, 0); + if (clk_min_rate < 0) + return; + + clk_max_rate = clk_round_rate(clk, ULONG_MAX); + if (clk_max_rate < 0) + return; + + /* + * From the manual: + * Bit rate = f(PCLKSPIn) / (2 * (n + 1) * 2^N) + * + * If we adapt it to the current context, we get the following: + * hz = rate / ((spr + 1) * (1 << (brdv + 1))) + * + * This can be written in multiple forms depending on what we want to + * determine. + * + * To find the rate, having hz, spr and brdv: + * rate = hz * (spr + 1) * (1 << (brdv + 1) + * + * To find the spr, having rate, hz, and spr: + * spr = rate / (hz * (1 << (brdv + 1)) - 1 + */ + + for (brdv = RSPI_SPCMD_BRDV_MIN; brdv <= RSPI_SPCMD_BRDV_MAX; brdv++) { + /* Calculate the divisor needed to find the SPR from a rate. */ + u32 rate_div = hz * (1 << (brdv + 1)); + + /* + * If the SPR for the minimum rate is greater than the maximum + * allowed value skip this BRDV. The divisor increases with each + * BRDV iteration, so the following BRDV might result in a + * minimum SPR that is in the valid range. + */ + min_rate_spr = DIV_ROUND_CLOSEST(clk_min_rate, rate_div) - 1; + if (min_rate_spr > spr_max) + continue; + + /* + * If the SPR for the maximum rate is less than the minimum + * allowed value, exit. The divisor only increases with each + * BRDV iteration, so the following BRDV cannot result in a + * maximum SPR that is in the valid range. + */ + max_rate_spr = DIV_ROUND_CLOSEST(clk_max_rate, rate_div) - 1; + if (max_rate_spr < spr_min) + break; + + if (min_rate_spr < spr_min) + min_rate_spr = spr_min; + + if (max_rate_spr > spr_max) + max_rate_spr = spr_max; + + for (spr = min_rate_spr; spr <= max_rate_spr; spr++) { + clk_rate = (spr + 1) * rate_div; + + clk_rate = clk_round_rate(clk, clk_rate); + if (clk_rate <= 0) + continue; + + actual_hz = rzv2h_rspi_calc_bitrate(clk_rate, spr, brdv); + error = abs((long)hz - (long)actual_hz); + + if (error >= best->error) + continue; + + *best = (struct rzv2h_rspi_best_clock) { + .clk = clk, + .clk_rate = clk_rate, + .error = error, + .actual_hz = actual_hz, + .brdv = brdv, + .spr = spr, + }; + + if (!error) + return; + } + } +} + +static void rzv2h_rspi_find_rate_fixed(struct clk *clk, u32 hz, + u8 spr_min, u8 spr_max, + struct rzv2h_rspi_best_clock *best) { - unsigned long tclk_rate; + unsigned long clk_rate; + unsigned long error; + u32 actual_hz; int spr; u8 brdv; @@ -247,21 +379,63 @@ static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz) * * n = SPR - is RSPI_SPBR.SPR (from 0 to 255) * * N = BRDV - is RSPI_SPCMD.BRDV (from 0 to 3) */ - tclk_rate = clk_get_rate(rspi->tclk); + clk_rate = clk_get_rate(clk); for (brdv = RSPI_SPCMD_BRDV_MIN; brdv <= RSPI_SPCMD_BRDV_MAX; brdv++) { - spr = DIV_ROUND_UP(tclk_rate, hz * (1 << (brdv + 1))); + spr = DIV_ROUND_UP(clk_rate, hz * (1 << (brdv + 1))); spr--; - if (spr >= RSPI_SPBR_SPR_MIN && spr <= RSPI_SPBR_SPR_MAX) + if (spr >= spr_min && spr <= spr_max) goto clock_found; } - return 0; + return; clock_found: - rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_BRDV, brdv); - writeb(spr, rspi->base + RSPI_SPBR); + actual_hz = rzv2h_rspi_calc_bitrate(clk_rate, spr, brdv); + error = abs((long)hz - (long)actual_hz); + + if (error >= best->error) + return; + + *best = (struct rzv2h_rspi_best_clock) { + .clk = clk, + .clk_rate = clk_rate, + .error = error, + .actual_hz = actual_hz, + .brdv = brdv, + .spr = spr, + }; +} + +static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz) +{ + struct rzv2h_rspi_best_clock best_clock = { + .error = ULONG_MAX, + }; + int ret; + + rspi->info->find_tclk_rate(rspi->tclk, hz, RSPI_SPBR_SPR_MIN, + RSPI_SPBR_SPR_MAX, &best_clock); + + /* + * T2H and N2H can also use PCLK as a source, which is 125MHz, but not + * when both SPR and BRDV are 0. + */ + if (best_clock.error && rspi->info->find_pclk_rate) + rspi->info->find_pclk_rate(rspi->pclk, hz, RSPI_SPBR_SPR_PCLK_MIN, + RSPI_SPBR_SPR_MAX, &best_clock); + + if (!best_clock.clk_rate) + return -EINVAL; - return rzv2h_rspi_calc_bitrate(tclk_rate, spr, brdv); + ret = clk_set_rate(best_clock.clk, best_clock.clk_rate); + if (ret) + return 0; + + rspi->use_pclk = best_clock.clk == rspi->pclk; + rspi->spr = best_clock.spr; + rspi->brdv = best_clock.brdv; + + return best_clock.actual_hz; } static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr, @@ -274,10 +448,34 @@ static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr, u8 bits_per_word; u32 conf32; u16 conf16; + u8 conf8; /* Make sure SPCR.SPE is 0 before amending the configuration */ rzv2h_rspi_spe_disable(rspi); + list_for_each_entry(xfer, &message->transfers, transfer_list) { + if (!xfer->speed_hz) + continue; + + speed_hz = min(xfer->speed_hz, speed_hz); + bits_per_word = xfer->bits_per_word; + } + + if (speed_hz == U32_MAX) + return -EINVAL; + + rspi->bytes_per_word = roundup_pow_of_two(BITS_TO_BYTES(bits_per_word)); + + if (speed_hz != rspi->last_speed_hz) { + rspi->freq = rzv2h_rspi_setup_clock(rspi, speed_hz); + if (!rspi->freq) + return -EINVAL; + + rspi->last_speed_hz = speed_hz; + } + + writeb(rspi->spr, rspi->base + RSPI_SPBR); + /* Configure the device to work in "host" mode */ conf32 = RSPI_SPCR_MSTR; @@ -287,15 +485,24 @@ static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr, /* SPI receive buffer full interrupt enable */ conf32 |= RSPI_SPCR_SPRIE; + /* Bypass synchronization circuit */ + conf32 |= FIELD_PREP(RSPI_SPCR_BPEN, rspi->use_pclk); + writel(conf32, rspi->base + RSPI_SPCR); /* Use SPCMD0 only */ writeb(0x0, rspi->base + RSPI_SPSCR); + /* Setup loopback */ + conf8 = FIELD_PREP(RSPI_SPPCR_SPLP2, !!(spi->mode & SPI_LOOP)); + writeb(conf8, rspi->base + RSPI_SPPCR); + /* Setup mode */ conf32 = FIELD_PREP(RSPI_SPCMD_CPOL, !!(spi->mode & SPI_CPOL)); conf32 |= FIELD_PREP(RSPI_SPCMD_CPHA, !!(spi->mode & SPI_CPHA)); conf32 |= FIELD_PREP(RSPI_SPCMD_LSBF, !!(spi->mode & SPI_LSB_FIRST)); + conf32 |= FIELD_PREP(RSPI_SPCMD_SPB, bits_per_word - 1); + conf32 |= FIELD_PREP(RSPI_SPCMD_BRDV, rspi->brdv); conf32 |= FIELD_PREP(RSPI_SPCMD_SSLKP, 1); conf32 |= FIELD_PREP(RSPI_SPCMD_SSLA, spi_get_chipselect(spi, 0)); writel(conf32, rspi->base + RSPI_SPCMD); @@ -305,30 +512,12 @@ static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr, writeb(0, rspi->base + RSPI_SSLP); /* Setup FIFO thresholds */ - conf16 = FIELD_PREP(RSPI_SPDCR2_TTRG, RSPI_FIFO_SIZE - 1); + conf16 = FIELD_PREP(RSPI_SPDCR2_TTRG, rspi->info->fifo_size - 1); conf16 |= FIELD_PREP(RSPI_SPDCR2_RTRG, 0); writew(conf16, rspi->base + RSPI_SPDCR2); rzv2h_rspi_clear_fifos(rspi); - list_for_each_entry(xfer, &message->transfers, transfer_list) { - if (!xfer->speed_hz) - continue; - - speed_hz = min(xfer->speed_hz, speed_hz); - bits_per_word = xfer->bits_per_word; - } - - if (speed_hz == U32_MAX) - return -EINVAL; - - rspi->bytes_per_word = roundup_pow_of_two(BITS_TO_BYTES(bits_per_word)); - rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_SPB, bits_per_word - 1); - - rspi->freq = rzv2h_rspi_setup_clock(rspi, speed_hz); - if (!rspi->freq) - return -EINVAL; - rzv2h_rspi_spe_enable(rspi); return 0; @@ -350,8 +539,8 @@ static int rzv2h_rspi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rzv2h_rspi_priv *rspi; struct clk_bulk_data *clks; - unsigned long tclk_rate; int irq_rx, ret, i; + long tclk_rate; controller = devm_spi_alloc_host(dev, sizeof(*rspi)); if (!controller) @@ -362,30 +551,32 @@ static int rzv2h_rspi_probe(struct platform_device *pdev) rspi->controller = controller; + rspi->info = device_get_match_data(dev); + rspi->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(rspi->base)) return PTR_ERR(rspi->base); ret = devm_clk_bulk_get_all_enabled(dev, &clks); - if (ret != RSPI_CLK_NUM) + if (ret != rspi->info->num_clks) return dev_err_probe(dev, ret >= 0 ? -EINVAL : ret, "cannot get clocks\n"); - for (i = 0; i < RSPI_CLK_NUM; i++) { - if (!strcmp(clks[i].id, "tclk")) { + for (i = 0; i < rspi->info->num_clks; i++) { + if (!strcmp(clks[i].id, rspi->info->tclk_name)) { rspi->tclk = clks[i].clk; - break; + } else if (rspi->info->find_pclk_rate && + !strcmp(clks[i].id, "pclk")) { + rspi->pclk = clks[i].clk; } } if (!rspi->tclk) return dev_err_probe(dev, -EINVAL, "Failed to get tclk\n"); - tclk_rate = clk_get_rate(rspi->tclk); - rspi->resets[0].id = "presetn"; rspi->resets[1].id = "tresetn"; - ret = devm_reset_control_bulk_get_exclusive(dev, RSPI_RESET_NUM, - rspi->resets); + ret = devm_reset_control_bulk_get_optional_exclusive(dev, RSPI_RESET_NUM, + rspi->resets); if (ret) return dev_err_probe(dev, ret, "cannot get resets\n"); @@ -407,15 +598,29 @@ static int rzv2h_rspi_probe(struct platform_device *pdev) } controller->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | - SPI_LSB_FIRST; + SPI_LSB_FIRST | SPI_LOOP; controller->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); controller->prepare_message = rzv2h_rspi_prepare_message; controller->unprepare_message = rzv2h_rspi_unprepare_message; controller->num_chipselect = 4; controller->transfer_one = rzv2h_rspi_transfer_one; + + tclk_rate = clk_round_rate(rspi->tclk, 0); + if (tclk_rate < 0) { + ret = tclk_rate; + goto quit_resets; + } + controller->min_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate, RSPI_SPBR_SPR_MAX, RSPI_SPCMD_BRDV_MAX); + + tclk_rate = clk_round_rate(rspi->tclk, ULONG_MAX); + if (tclk_rate < 0) { + ret = tclk_rate; + goto quit_resets; + } + controller->max_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate, RSPI_SPBR_SPR_MIN, RSPI_SPCMD_BRDV_MIN); @@ -445,8 +650,24 @@ static void rzv2h_rspi_remove(struct platform_device *pdev) reset_control_bulk_assert(RSPI_RESET_NUM, rspi->resets); } +static const struct rzv2h_rspi_info rzv2h_info = { + .find_tclk_rate = rzv2h_rspi_find_rate_fixed, + .tclk_name = "tclk", + .fifo_size = 16, + .num_clks = 3, +}; + +static const struct rzv2h_rspi_info rzt2h_info = { + .find_tclk_rate = rzv2h_rspi_find_rate_variable, + .find_pclk_rate = rzv2h_rspi_find_rate_fixed, + .tclk_name = "pclkspi", + .fifo_size = 4, + .num_clks = 2, +}; + static const struct of_device_id rzv2h_rspi_match[] = { - { .compatible = "renesas,r9a09g057-rspi" }, + { .compatible = "renesas,r9a09g057-rspi", &rzv2h_info }, + { .compatible = "renesas,r9a09g077-rspi", &rzt2h_info }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rzv2h_rspi_match); |
