lkml.org 
[lkml]   [2020]   [May]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 07/10] spi: spi-dw-mchp: Add Sparx5 support
    Date
    This adds support for the Sparx5 SoC in the spi-dw-mchp SPI controller.

    Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
    Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
    ---
    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;
    +
    + /* CS0..3, only 16M reach */
    + if ((spi->chip_select < MAX_CS) &&
    + (op->addr.val + op->data.nbytes) < SZ_16M) {
    + 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);
    + 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
    \
     
     \ /
      Last update: 2020-05-13 16:02    [W:4.398 / U:0.036 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site