On Wed, May 13, 2020 at 04:00:28PM +0200, Lars Povlsen wrote: > This adds support for the Sparx5 SoC in the spi-dw-mchp SPI controller. > > Reviewed-by: Alexandre Belloni <alexandre.belloni@xxxxxxxxxxx> > Signed-off-by: Lars Povlsen <lars.povlsen@xxxxxxxxxxxxx> > --- > drivers/spi/spi-dw-mchp.c | 211 ++++++++++++++++++++++++++++++++++---- > 1 file changed, 189 insertions(+), 22 deletions(-) > > diff --git a/drivers/spi/spi-dw-mchp.c b/drivers/spi/spi-dw-mchp.c > index 0828a7616d9ab..3abdd44a550ea 100644 > --- a/drivers/spi/spi-dw-mchp.c > +++ b/drivers/spi/spi-dw-mchp.c > @@ -28,21 +28,22 @@ > > #define MAX_CS 4 > > -#define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24 > -#define OCELOT_IF_SI_OWNER_OFFSET 4 > -#define JAGUAR2_IF_SI_OWNER_OFFSET 6 > #define MSCC_IF_SI_OWNER_MASK GENMASK(1, 0) > #define MSCC_IF_SI_OWNER_SISL 0 > #define MSCC_IF_SI_OWNER_SIBM 1 > #define MSCC_IF_SI_OWNER_SIMC 2 > > #define MSCC_SPI_MST_SW_MODE 0x14 > -#define MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE BIT(13) > -#define MSCC_SPI_MST_SW_MODE_SW_SPI_CS(x) (x << 5) > > struct dw_spi_mchp_props { > const char *syscon_name; > - u32 si_owner_bit; > + u32 general_ctrl_off; > + u32 si_owner_bit, si_owner2_bit; > + u32 pinctrl_bit_off; > + u32 cs_bit_off; > + u32 ss_force_ena_off; > + u32 ss_force_val_off; > + u32 bootmaster_cs; > }; > > struct dw_spi_mchp { > @@ -53,44 +54,176 @@ struct dw_spi_mchp { > void __iomem *spi_mst; > const struct dw_spi_mchp_props *props; > u32 gen_owner; > + u32 if2mask; > }; > > static const struct dw_spi_mchp_props dw_spi_mchp_props_ocelot = { > .syscon_name = "mscc,ocelot-cpu-syscon", > + .general_ctrl_off = 0x24, > .si_owner_bit = 4, > + .pinctrl_bit_off = 13, > + .cs_bit_off = 5, > + .bootmaster_cs = 0, > }; > > static const struct dw_spi_mchp_props dw_spi_mchp_props_jaguar2 = { > .syscon_name = "mscc,ocelot-cpu-syscon", > + .general_ctrl_off = 0x24, > .si_owner_bit = 6, > + .pinctrl_bit_off = 13, > + .cs_bit_off = 5, > + .bootmaster_cs = 0, > +}; > + > +static const struct dw_spi_mchp_props dw_spi_mchp_props_sparx5 = { > + .syscon_name = "microchip,sparx5-cpu-syscon", > + .general_ctrl_off = 0x88, > + .si_owner_bit = 6, > + .si_owner2_bit = 4, > + .ss_force_ena_off = 0xa4, > + .ss_force_val_off = 0xa8, > + .bootmaster_cs = 0, > }; > > /* > - * The Designware SPI controller (referred to as master in the documentation) > - * automatically deasserts chip select when the tx fifo is empty. The chip > - * selects then needs to be either driven as GPIOs or, for the first 4 using the > - * the SPI boot controller registers. the final chip select is an OR gate > - * between the Designware SPI controller and the SPI boot controller. > + * Set the owner of the SPI interface > */ > -static void dw_spi_mchp_set_cs(struct spi_device *spi, bool enable) > +static void dw_spi_mchp_set_owner(struct dw_spi_mchp *dwsmchp, > + const struct dw_spi_mchp_props *props, > + u8 owner, u8 owner2) > +{ > + u32 val, msk; > + > + val = (owner << props->si_owner_bit); > + msk = (MSCC_IF_SI_OWNER_MASK << props->si_owner_bit); > + if (props->si_owner2_bit) { > + val |= owner2 << props->si_owner2_bit; > + msk |= (MSCC_IF_SI_OWNER_MASK << props->si_owner2_bit); > + } > + if (dwsmchp->gen_owner != val) { > + regmap_update_bits(dwsmchp->syscon, props->general_ctrl_off, > + msk, val); > + dwsmchp->gen_owner = val; > + } > +} > + > +static void dw_spi_mchp_set_cs_owner(struct dw_spi_mchp *dwsmchp, > + const struct dw_spi_mchp_props *props, > + u8 cs, u8 owner) > { > + u8 dummy = (owner == MSCC_IF_SI_OWNER_SIBM ? > + MSCC_IF_SI_OWNER_SIMC : MSCC_IF_SI_OWNER_SIBM); > + if (props->si_owner2_bit && (dwsmchp->if2mask & BIT(cs))) { > + /* SPI2 */ > + dw_spi_mchp_set_owner(dwsmchp, props, dummy, owner); > + } else { > + /* SPI1 */ > + dw_spi_mchp_set_owner(dwsmchp, props, owner, dummy); > + } > +} > + > +/* > + * The Designware SPI controller (referred to as master in the > + * documentation) automatically deasserts chip select when the tx fifo > + * is empty. The chip selects then needs to be either driven as GPIOs > + * or, for the first 4 using the the SPI boot controller > + * registers. the final chip select is an OR gate between the > + * Designware SPI controller and the SPI boot controller. nselect is > + * an active low signal > + */ > +static void dw_spi_mchp_set_cs(struct spi_device *spi, bool nEnable) > +{ > + bool enable = !nEnable; /* This keeps changing in the API... */ > struct dw_spi *dws = spi_master_get_devdata(spi->master); > struct dw_spi_mchp *dwsmchp = container_of(dws, struct dw_spi_mchp, > dws); > - u32 cs = spi->chip_select; > + const struct dw_spi_mchp_props *props = dwsmchp->props; > + u8 cs = spi->chip_select; > > - if (cs < 4) { > - u32 sw_mode = MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE; > + if (enable) > + dw_spi_mchp_set_cs_owner(dwsmchp, props, cs, > + MSCC_IF_SI_OWNER_SIMC); > > - if (!enable) > - sw_mode |= MSCC_SPI_MST_SW_MODE_SW_SPI_CS(BIT(cs)); > + if (dwsmchp->spi_mst && (cs < MAX_CS)) { > + u32 sw_mode; > > + if (enable) > + sw_mode = BIT(props->pinctrl_bit_off) | > + (BIT(cs) << props->cs_bit_off); > + else > + sw_mode = 0; > writel(sw_mode, dwsmchp->spi_mst + MSCC_SPI_MST_SW_MODE); > + } else if (props->ss_force_ena_off) { > + if (enable) { > + /* Ensure CS toggles, so start off all disabled */ > + regmap_write(dwsmchp->syscon, props->ss_force_val_off, > + ~0); > + /* CS override drive enable */ > + regmap_write(dwsmchp->syscon, props->ss_force_ena_off, > + 1); > + /* Allow settle */ > + udelay(1); > + /* Now set CSx enabled */ > + regmap_write(dwsmchp->syscon, props->ss_force_val_off, > + ~BIT(cs)); > + } else { > + /* CS value */ > + regmap_write(dwsmchp->syscon, props->ss_force_val_off, > + ~0); > + /* CS override drive disable */ > + regmap_write(dwsmchp->syscon, props->ss_force_ena_off, > + 0); > + } > } > > - dw_spi_set_cs(spi, enable); > + dw_spi_set_cs(spi, nEnable); > +} > + > +static int dw_mchp_bootmaster_exec_mem_op(struct spi_mem *mem, > + const struct spi_mem_op *op) > +{ > + struct spi_device *spi = mem->spi; > + int ret = -ENOTSUPP; > + > + /* Only reads, addrsize 1..4 */ > + if (!op->data.nbytes || !op->addr.nbytes || op->addr.nbytes > 4 || > + op->data.dir != SPI_MEM_DATA_IN) > + return ret; > + > + /* Only handle (normal+fast) 3/4 bytes read */ > + if (op->cmd.opcode != SPINOR_OP_READ && > + op->cmd.opcode != SPINOR_OP_READ_FAST && > + op->cmd.opcode != SPINOR_OP_READ_4B && > + op->cmd.opcode != SPINOR_OP_READ_FAST_4B) > + return ret; Hm, this part most like belongs to supports_op() callback. > + > + /* CS0..3, only 16M reach */ > + if ((spi->chip_select < MAX_CS) && > + (op->addr.val + op->data.nbytes) < SZ_16M) { The driver shouldn't return a failure if more than available data requested. Just return the length the driver managed to read. > + struct dw_spi *dws = spi_master_get_devdata(spi->master); > + struct dw_spi_mchp *dwsmchp = container_of(dws, > + struct dw_spi_mchp, > + dws); > + const struct dw_spi_mchp_props *props = dwsmchp->props; > + u8 __iomem *src = dwsmchp->read_map + > + (spi->chip_select * SZ_16M) + op->addr.val; > + > + if (props->bootmaster_cs != spi->chip_select) > + return ret; > + > + /* Make boot master owner of SI interface */ > + dw_spi_mchp_set_cs_owner(dwsmchp, props, spi->chip_select, > + MSCC_IF_SI_OWNER_SIBM); > + memcpy(op->data.buf.in, src, op->data.nbytes); So after all it's just memcpy from the directly mapped SPI-flash memory, right? Then it's not mem_op, but I supposed it should be implemented by means of the dirmap_{create,read,destroy}. -Sergey > + ret = op->data.nbytes; > + } > + return ret; > } > > +static const struct spi_controller_mem_ops dw_mchp_bootmaster_mem_ops = { > + .exec_op = dw_mchp_bootmaster_exec_mem_op, > +}; > + > static int dw_spi_mchp_init(struct platform_device *pdev, > struct dw_spi *dws, > struct dw_spi_mchp *dwsmchp, > @@ -107,6 +240,18 @@ static int dw_spi_mchp_init(struct platform_device *pdev, > } > } > > + /* See if we have a direct read window */ > + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); > + if (res && resource_size(res) >= (SZ_16M*MAX_CS)) { > + void __iomem *ptr = devm_ioremap_resource(&pdev->dev, res); > + > + if (!IS_ERR(ptr)) { > + dwsmchp->read_map = ptr; > + dws->mem_ops = &dw_mchp_bootmaster_mem_ops; > + dev_info(&pdev->dev, "Enabling fast memory operations\n"); > + } > + } > + > dwsmchp->syscon = > syscon_regmap_lookup_by_compatible(props->syscon_name); > if (IS_ERR(dwsmchp->syscon)) { > @@ -119,10 +264,9 @@ static int dw_spi_mchp_init(struct platform_device *pdev, > if (dwsmchp->spi_mst) > writel(0, dwsmchp->spi_mst + MSCC_SPI_MST_SW_MODE); > > - /* Select the owner of the SI interface */ > - regmap_update_bits(dwsmchp->syscon, MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL, > - MSCC_IF_SI_OWNER_MASK << props->si_owner_bit, > - MSCC_IF_SI_OWNER_SIMC << props->si_owner_bit); > + /* SPI2 mapping bitmask */ > + device_property_read_u32(&pdev->dev, "interface-mapping-mask", > + &dwsmchp->if2mask); > > dwsmchp->dws.set_cs = dw_spi_mchp_set_cs; > > @@ -180,6 +324,27 @@ static int dw_spi_mchp_probe(struct platform_device *pdev) > dws->rx_sample_dly = DIV_ROUND_UP(rx_sample_dly, > (dws->max_freq / 1000000)); > > + if (pdev->dev.of_node) { > + int i; > + > + for (i = 0; i < dws->num_cs; i++) { > + int cs_gpio = of_get_named_gpio(pdev->dev.of_node, > + "cs-gpios", i); > + > + if (cs_gpio == -EPROBE_DEFER) { > + ret = cs_gpio; > + goto out; > + } > + > + if (gpio_is_valid(cs_gpio)) { > + ret = devm_gpio_request(&pdev->dev, cs_gpio, > + dev_name(&pdev->dev)); > + if (ret) > + goto out; > + } > + } > + } > + > props = device_get_match_data(&pdev->dev); > if (props) > ret = dw_spi_mchp_init(pdev, dws, dwsmchp, props); > @@ -213,6 +378,8 @@ static int dw_spi_mchp_remove(struct platform_device *pdev) > static const struct of_device_id dw_spi_mchp_of_match[] = { > { .compatible = "mscc,ocelot-spi", .data = &dw_spi_mchp_props_ocelot}, > { .compatible = "mscc,jaguar2-spi", .data = &dw_spi_mchp_props_jaguar2}, > + { .compatible = "microchip,sparx5-spi", > + .data = &dw_spi_mchp_props_sparx5}, > { /* end of table */} > }; > MODULE_DEVICE_TABLE(of, dw_spi_mchp_of_match); > -- > 2.26.2 > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel@xxxxxxxxxxxxxxxxxxx > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel