This adds support for the Sparx5 SoC in the spi-dw-mchp SPI controller.
Reviewed-by: Alexandre Belloni <[email protected]>
Signed-off-by: Lars Povlsen <[email protected]>
---
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
[Sorry about the slight delay on getting back on this]
On 14/05/20 11:25, Mark Brown wrote:
> Date: Thu, 14 May 2020 11:25:16 +0100
> From: Mark Brown <[email protected]>
> To: Lars Povlsen <[email protected]>
> Cc: SoC Team <[email protected]>, Microchip Linux Driver Support
> <[email protected]>, [email protected],
> [email protected], [email protected],
> [email protected], Alexandre Belloni
> <[email protected]>
> Subject: Re: [PATCH 07/10] spi: spi-dw-mchp: Add Sparx5 support
> User-Agent: Mutt/1.10.1 (2018-07-13)
>
> On Wed, May 13, 2020 at 04:00:28PM +0200, Lars Povlsen wrote:
>
> > +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);
>
> Please write normal conditional statements to improve legibility.
>
I will take your recommendation to heart.
> > +static void dw_spi_mchp_set_cs(struct spi_device *spi, bool nEnable)
> > +{
> > + bool enable = !nEnable; /* This keeps changing in the API... */
>
> No, it doesn't. The API has not changed for more than a decade.
>
I will remove the comment.
I think the comment was related to when we got bitten by the below
change, but alas.
commit ada9e3fcc175db4538f5b5e05abf5dedf626e550
Author: Charles Keepax <[email protected]>
Date: Wed Nov 27 15:39:36 2019 +0000
spi: dw: Correct handling of native chipselect
This patch reverts commit 6e0a32d6f376 ("spi: dw: Fix default polarity
of native chipselect").
> > + } 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);
>
> What's all this force_ena_off stuff about? The controller should not be
> making decisions about management of the chip select, this will break
> users.
>
Our controller is not using DMA, but the FIFO interface. And as the DW
controller drops CS when the FIFO runs empty, this will upset SPI
devices. The "ss_force" is something the HW designes put on top to
"override" the CS. We could of course use the GPIO's specifically to
overcome this - but the "boot" CS 0 is a builtin CS, with no
underlying GPIO.
Add to this that the HW dept decided to add *2* physical SPI busses to
the same controller. That we also need to switch between. And ensure
CS gets dropped correctly before changing tracks...
Long story, lot of grief...
> > + 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;
>
> Set use_gpio_descriptors and let the core manage the GPIO.
Good suggestion, just the ticket!
And thank you very much for your time & comments.
---Lars
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 <[email protected]>
> Signed-off-by: Lars Povlsen <[email protected]>
> ---
> 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
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel