[U-Boot] spi: kirkwood_spi: implement workaround for FE-9144572

Message ID 20171013010521.12017-1-judge.packham@gmail.com
State Superseded
Delegated to: Prafulla Wadaskar
Headers show
Series
  • [U-Boot] spi: kirkwood_spi: implement workaround for FE-9144572
Related show

Commit Message

Chris Packham Oct. 13, 2017, 1:05 a.m.
Erratum NO. FE-9144572: The device SPI interface supports frequencies of
up to 50 MHz.  However, due to this erratum, when the device core clock
is 250 MHz and the SPI interfaces is configured for 50MHz SPI clock and
CPOL=CPHA=1 there might occur data corruption on reads from the SPI
device.

Implement the workaround by setting the TMISO_SAMPLE value to 0x2
in the timing1 register.

Signed-off-by: Chris Packham <judge.packham@gmail.com>
---

 arch/arm/include/asm/arch-mvebu/spi.h |  6 ++++
 drivers/spi/kirkwood_spi.c            | 62 +++++++++++++++++++++++++++++++++--
 2 files changed, 65 insertions(+), 3 deletions(-)

Comments

Chris Packham Oct. 13, 2017, 4:24 a.m. | #1
On Fri, Oct 13, 2017 at 2:05 PM, Chris Packham <judge.packham@gmail.com> wrote:
> Erratum NO. FE-9144572: The device SPI interface supports frequencies of
> up to 50 MHz.  However, due to this erratum, when the device core clock
> is 250 MHz and the SPI interfaces is configured for 50MHz SPI clock and
> CPOL=CPHA=1 there might occur data corruption on reads from the SPI
> device.
>
> Implement the workaround by setting the TMISO_SAMPLE value to 0x2
> in the timing1 register.
>
> Signed-off-by: Chris Packham <judge.packham@gmail.com>
> ---

I've based this as much as I can on the equivalent implementation in the
Linux kernel[1], but there are differences in the u-boot spi
infrastructure that means I can't be as specific when qualifying whether
the workaround is needed.

[1] - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/spi/spi-orion.c#n248

>
>  arch/arm/include/asm/arch-mvebu/spi.h |  6 ++++
>  drivers/spi/kirkwood_spi.c            | 62 +++++++++++++++++++++++++++++++++--
>  2 files changed, 65 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm/include/asm/arch-mvebu/spi.h b/arch/arm/include/asm/arch-mvebu/spi.h
> index 3545aed17347..1de510ea6da9 100644
> --- a/arch/arm/include/asm/arch-mvebu/spi.h
> +++ b/arch/arm/include/asm/arch-mvebu/spi.h
> @@ -57,6 +57,12 @@ struct kwspi_registers {
>  #define KWSPI_TXLSBF           (1 << 13)
>  #define KWSPI_RXLSBF           (1 << 14)
>
> +/* Timing Parameters 1 Register */
> +#define KW_SPI_TMISO_SAMPLE_OFFSET     6
> +#define KW_SPI_TMISO_SAMPLE_MASK       (0x3 << KW_SPI_TMISO_SAMPLE_OFFSET)
> +#define KW_SPI_TMISO_SAMPLE_1          (1 << KW_SPI_TMISO_SAMPLE_OFFSET)
> +#define KW_SPI_TMISO_SAMPLE_2          (2 << KW_SPI_TMISO_SAMPLE_OFFSET)
> +
>  #define KWSPI_IRQUNMASK                1 /* unmask SPI interrupt */
>  #define KWSPI_IRQMASK          0 /* mask SPI interrupt */
>  #define KWSPI_SMEMRDIRQ                1 /* SerMem data xfer ready irq */
> diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c
> index 0c6bd295cde9..7992556d49eb 100644
> --- a/drivers/spi/kirkwood_spi.c
> +++ b/drivers/spi/kirkwood_spi.c
> @@ -243,6 +243,16 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
>
>  /* Here now the DM part */
>
> +enum mvebu_spi_type {
> +       ORION_SPI,
> +       ARMADA_SPI,
> +};
> +
> +struct mvebu_spi_dev {
> +       enum mvebu_spi_type     typ;
> +       bool                    is_errata_50mhz_ac;
> +};
> +
>  struct mvebu_spi_platdata {
>         struct kwspi_registers *spireg;
>  };
> @@ -269,10 +279,30 @@ static int mvebu_spi_set_speed(struct udevice *bus, uint hz)
>         return 0;
>  }
>
> +static void
> +mvebu_spi_50mhz_ac_timing_erratum(struct udevice *bus, uint mode)
> +{
> +       struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
> +       struct kwspi_registers *reg = plat->spireg;
> +       u32 data = readl(&reg->timing1);
> +
> +       data &= ~KW_SPI_TMISO_SAMPLE_MASK;
> +
> +       if (CONFIG_SYS_TCLK == 250000000 &&
> +           mode & SPI_CPOL &&
> +           mode & SPI_CPHA)
> +               data |= KW_SPI_TMISO_SAMPLE_2;
> +       else
> +               data |= KW_SPI_TMISO_SAMPLE_1;
> +
> +       writel(data, &reg->timing1);
> +}
> +
>  static int mvebu_spi_set_mode(struct udevice *bus, uint mode)
>  {
>         struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
>         struct kwspi_registers *reg = plat->spireg;
> +       const struct mvebu_spi_dev *drvdata;
>         u32 data = readl(&reg->cfg);
>
>         data &= ~(KWSPI_CPHA | KWSPI_CPOL | KWSPI_RXLSBF | KWSPI_TXLSBF);
> @@ -286,6 +316,10 @@ static int mvebu_spi_set_mode(struct udevice *bus, uint mode)
>
>         writel(data, &reg->cfg);
>
> +       drvdata = (struct mvebu_spi_dev *)dev_get_driver_data(bus);
> +       if (drvdata->is_errata_50mhz_ac)
> +               mvebu_spi_50mhz_ac_timing_erratum(bus, mode);
> +
>         return 0;
>  }
>
> @@ -343,10 +377,32 @@ static const struct dm_spi_ops mvebu_spi_ops = {
>          */
>  };
>
> +static const struct mvebu_spi_dev armada_xp_spi_dev_data = {
> +       .typ = ARMADA_SPI,
> +};
> +
> +static const struct mvebu_spi_dev armada_375_spi_dev_data = {
> +       .typ = ARMADA_SPI,
> +};
> +
> +static const struct mvebu_spi_dev armada_380_spi_dev_data = {
> +       .typ = ARMADA_SPI,
> +       .is_errata_50mhz_ac = true,
> +};
> +
>  static const struct udevice_id mvebu_spi_ids[] = {
> -       { .compatible = "marvell,armada-375-spi" },
> -       { .compatible = "marvell,armada-380-spi" },
> -       { .compatible = "marvell,armada-xp-spi" },
> +       {
> +               .compatible = "marvell,armada-375-spi",
> +               .data = (ulong)&armada_375_spi_dev_data
> +       },
> +       {
> +               .compatible = "marvell,armada-380-spi",
> +               .data = (ulong)&armada_380_spi_dev_data
> +       },
> +       {
> +               .compatible = "marvell,armada-xp-spi",
> +               .data = (ulong)&armada_xp_spi_dev_data
> +       },
>         { }
>  };
>
> --
> 2.14.2
>
Stefan Roese Oct. 15, 2017, 7:53 a.m. | #2
On 13.10.2017 03:05, Chris Packham wrote:
> Erratum NO. FE-9144572: The device SPI interface supports frequencies of
> up to 50 MHz.  However, due to this erratum, when the device core clock
> is 250 MHz and the SPI interfaces is configured for 50MHz SPI clock and
> CPOL=CPHA=1 there might occur data corruption on reads from the SPI
> device.
> 
> Implement the workaround by setting the TMISO_SAMPLE value to 0x2
> in the timing1 register.
> 
> Signed-off-by: Chris Packham <judge.packham@gmail.com>
> ---
> 
>   arch/arm/include/asm/arch-mvebu/spi.h |  6 ++++
>   drivers/spi/kirkwood_spi.c            | 62 +++++++++++++++++++++++++++++++++--
>   2 files changed, 65 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm/include/asm/arch-mvebu/spi.h b/arch/arm/include/asm/arch-mvebu/spi.h
> index 3545aed17347..1de510ea6da9 100644
> --- a/arch/arm/include/asm/arch-mvebu/spi.h
> +++ b/arch/arm/include/asm/arch-mvebu/spi.h
> @@ -57,6 +57,12 @@ struct kwspi_registers {
>   #define KWSPI_TXLSBF		(1 << 13)
>   #define KWSPI_RXLSBF		(1 << 14)
>   
> +/* Timing Parameters 1 Register */
> +#define KW_SPI_TMISO_SAMPLE_OFFSET	6
> +#define KW_SPI_TMISO_SAMPLE_MASK	(0x3 << KW_SPI_TMISO_SAMPLE_OFFSET)
> +#define KW_SPI_TMISO_SAMPLE_1		(1 << KW_SPI_TMISO_SAMPLE_OFFSET)
> +#define KW_SPI_TMISO_SAMPLE_2		(2 << KW_SPI_TMISO_SAMPLE_OFFSET)
> +
>   #define KWSPI_IRQUNMASK		1 /* unmask SPI interrupt */
>   #define KWSPI_IRQMASK		0 /* mask SPI interrupt */
>   #define KWSPI_SMEMRDIRQ		1 /* SerMem data xfer ready irq */
> diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c
> index 0c6bd295cde9..7992556d49eb 100644
> --- a/drivers/spi/kirkwood_spi.c
> +++ b/drivers/spi/kirkwood_spi.c
> @@ -243,6 +243,16 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
>   
>   /* Here now the DM part */
>   
> +enum mvebu_spi_type {
> +	ORION_SPI,
> +	ARMADA_SPI,
> +};
> +
> +struct mvebu_spi_dev {
> +	enum mvebu_spi_type	typ;
> +	bool			is_errata_50mhz_ac;
> +};
> +
>   struct mvebu_spi_platdata {
>   	struct kwspi_registers *spireg;
>   };
> @@ -269,10 +279,30 @@ static int mvebu_spi_set_speed(struct udevice *bus, uint hz)
>   	return 0;
>   }
>   
> +static void
> +mvebu_spi_50mhz_ac_timing_erratum(struct udevice *bus, uint mode)
> +{
> +	struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
> +	struct kwspi_registers *reg = plat->spireg;
> +	u32 data = readl(&reg->timing1);
> +
> +	data &= ~KW_SPI_TMISO_SAMPLE_MASK;
> +
> +	if (CONFIG_SYS_TCLK == 250000000 &&
> +	    mode & SPI_CPOL &&
> +	    mode & SPI_CPHA)
> +		data |= KW_SPI_TMISO_SAMPLE_2;
> +	else
> +		data |= KW_SPI_TMISO_SAMPLE_1;
> +
> +	writel(data, &reg->timing1);
> +}
> +
>   static int mvebu_spi_set_mode(struct udevice *bus, uint mode)
>   {
>   	struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
>   	struct kwspi_registers *reg = plat->spireg;
> +	const struct mvebu_spi_dev *drvdata;
>   	u32 data = readl(&reg->cfg);
>   
>   	data &= ~(KWSPI_CPHA | KWSPI_CPOL | KWSPI_RXLSBF | KWSPI_TXLSBF);
> @@ -286,6 +316,10 @@ static int mvebu_spi_set_mode(struct udevice *bus, uint mode)
>   
>   	writel(data, &reg->cfg);
>   
> +	drvdata = (struct mvebu_spi_dev *)dev_get_driver_data(bus);
> +	if (drvdata->is_errata_50mhz_ac)
> +		mvebu_spi_50mhz_ac_timing_erratum(bus, mode);
> +
>   	return 0;
>   }
>   
> @@ -343,10 +377,32 @@ static const struct dm_spi_ops mvebu_spi_ops = {
>   	 */
>   };
>   
> +static const struct mvebu_spi_dev armada_xp_spi_dev_data = {
> +	.typ = ARMADA_SPI,
> +};
> +
> +static const struct mvebu_spi_dev armada_375_spi_dev_data = {
> +	.typ = ARMADA_SPI,
> +};
> +
> +static const struct mvebu_spi_dev armada_380_spi_dev_data = {
> +	.typ = ARMADA_SPI,
> +	.is_errata_50mhz_ac = true,
> +};
> +
>   static const struct udevice_id mvebu_spi_ids[] = {
> -	{ .compatible = "marvell,armada-375-spi" },
> -	{ .compatible = "marvell,armada-380-spi" },
> -	{ .compatible = "marvell,armada-xp-spi" },
> +	{
> +		.compatible = "marvell,armada-375-spi",
> +		.data = (ulong)&armada_375_spi_dev_data
> +	},
> +	{
> +		.compatible = "marvell,armada-380-spi",
> +		.data = (ulong)&armada_380_spi_dev_data
> +	},
> +	{
> +		.compatible = "marvell,armada-xp-spi",
> +		.data = (ulong)&armada_xp_spi_dev_data
> +	},
>   	{ }
>   };
>   
> 

Reviewed-by: Stefan Roese <sr@denx.de>

Thanks,
Stefan
Jagan Teki Oct. 16, 2017, 5:04 p.m. | #3
On Fri, Oct 13, 2017 at 6:35 AM, Chris Packham <judge.packham@gmail.com> wrote:
> Erratum NO. FE-9144572: The device SPI interface supports frequencies of
> up to 50 MHz.  However, due to this erratum, when the device core clock
> is 250 MHz and the SPI interfaces is configured for 50MHz SPI clock and
> CPOL=CPHA=1 there might occur data corruption on reads from the SPI
> device.
>
> Implement the workaround by setting the TMISO_SAMPLE value to 0x2
> in the timing1 register.
>
> Signed-off-by: Chris Packham <judge.packham@gmail.com>
> ---
>
>  arch/arm/include/asm/arch-mvebu/spi.h |  6 ++++
>  drivers/spi/kirkwood_spi.c            | 62 +++++++++++++++++++++++++++++++++--
>  2 files changed, 65 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm/include/asm/arch-mvebu/spi.h b/arch/arm/include/asm/arch-mvebu/spi.h
> index 3545aed17347..1de510ea6da9 100644
> --- a/arch/arm/include/asm/arch-mvebu/spi.h
> +++ b/arch/arm/include/asm/arch-mvebu/spi.h
> @@ -57,6 +57,12 @@ struct kwspi_registers {
>  #define KWSPI_TXLSBF           (1 << 13)
>  #define KWSPI_RXLSBF           (1 << 14)
>
> +/* Timing Parameters 1 Register */
> +#define KW_SPI_TMISO_SAMPLE_OFFSET     6
> +#define KW_SPI_TMISO_SAMPLE_MASK       (0x3 << KW_SPI_TMISO_SAMPLE_OFFSET)
> +#define KW_SPI_TMISO_SAMPLE_1          (1 << KW_SPI_TMISO_SAMPLE_OFFSET)
> +#define KW_SPI_TMISO_SAMPLE_2          (2 << KW_SPI_TMISO_SAMPLE_OFFSET)
> +
>  #define KWSPI_IRQUNMASK                1 /* unmask SPI interrupt */
>  #define KWSPI_IRQMASK          0 /* mask SPI interrupt */
>  #define KWSPI_SMEMRDIRQ                1 /* SerMem data xfer ready irq */
> diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c
> index 0c6bd295cde9..7992556d49eb 100644
> --- a/drivers/spi/kirkwood_spi.c
> +++ b/drivers/spi/kirkwood_spi.c
> @@ -243,6 +243,16 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
>
>  /* Here now the DM part */
>
> +enum mvebu_spi_type {
> +       ORION_SPI,

This is never used, since there is a separate driver in orion - any
plan to merge them as single driver? otherwise it's no need.

> +       ARMADA_SPI,
> +};
> +
> +struct mvebu_spi_dev {
> +       enum mvebu_spi_type     typ;
> +       bool                    is_errata_50mhz_ac;
> +};
> +
>  struct mvebu_spi_platdata {
>         struct kwspi_registers *spireg;
>  };
> @@ -269,10 +279,30 @@ static int mvebu_spi_set_speed(struct udevice *bus, uint hz)
>         return 0;
>  }
>
> +static void
> +mvebu_spi_50mhz_ac_timing_erratum(struct udevice *bus, uint mode)
> +{
> +       struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
> +       struct kwspi_registers *reg = plat->spireg;
> +       u32 data = readl(&reg->timing1);
> +

Better to have description of errata.

thanks!
Chris Packham Oct. 16, 2017, 8:57 p.m. | #4
On Tue, Oct 17, 2017 at 6:04 AM, Jagan Teki <jagannadh.teki@gmail.com> wrote:
> On Fri, Oct 13, 2017 at 6:35 AM, Chris Packham <judge.packham@gmail.com> wrote:
>> Erratum NO. FE-9144572: The device SPI interface supports frequencies of
>> up to 50 MHz.  However, due to this erratum, when the device core clock
>> is 250 MHz and the SPI interfaces is configured for 50MHz SPI clock and
>> CPOL=CPHA=1 there might occur data corruption on reads from the SPI
>> device.
>>
>> Implement the workaround by setting the TMISO_SAMPLE value to 0x2
>> in the timing1 register.
>>
>> Signed-off-by: Chris Packham <judge.packham@gmail.com>
>> ---
>>
>>  arch/arm/include/asm/arch-mvebu/spi.h |  6 ++++
>>  drivers/spi/kirkwood_spi.c            | 62 +++++++++++++++++++++++++++++++++--
>>  2 files changed, 65 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/arm/include/asm/arch-mvebu/spi.h b/arch/arm/include/asm/arch-mvebu/spi.h
>> index 3545aed17347..1de510ea6da9 100644
>> --- a/arch/arm/include/asm/arch-mvebu/spi.h
>> +++ b/arch/arm/include/asm/arch-mvebu/spi.h
>> @@ -57,6 +57,12 @@ struct kwspi_registers {
>>  #define KWSPI_TXLSBF           (1 << 13)
>>  #define KWSPI_RXLSBF           (1 << 14)
>>
>> +/* Timing Parameters 1 Register */
>> +#define KW_SPI_TMISO_SAMPLE_OFFSET     6
>> +#define KW_SPI_TMISO_SAMPLE_MASK       (0x3 << KW_SPI_TMISO_SAMPLE_OFFSET)
>> +#define KW_SPI_TMISO_SAMPLE_1          (1 << KW_SPI_TMISO_SAMPLE_OFFSET)
>> +#define KW_SPI_TMISO_SAMPLE_2          (2 << KW_SPI_TMISO_SAMPLE_OFFSET)
>> +
>>  #define KWSPI_IRQUNMASK                1 /* unmask SPI interrupt */
>>  #define KWSPI_IRQMASK          0 /* mask SPI interrupt */
>>  #define KWSPI_SMEMRDIRQ                1 /* SerMem data xfer ready irq */
>> diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c
>> index 0c6bd295cde9..7992556d49eb 100644
>> --- a/drivers/spi/kirkwood_spi.c
>> +++ b/drivers/spi/kirkwood_spi.c
>> @@ -243,6 +243,16 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
>>
>>  /* Here now the DM part */
>>
>> +enum mvebu_spi_type {
>> +       ORION_SPI,
>
> This is never used, since there is a separate driver in orion - any
> plan to merge them as single driver? otherwise it's no need.
>

I actually do have access to some Kirkwood hardware. But a distinct
lack of round tuits to port it to master/DM/etc.

For now I'll drop the type. It can be added back if/when a user emerges.

>> +       ARMADA_SPI,
>> +};
>> +
>> +struct mvebu_spi_dev {
>> +       enum mvebu_spi_type     typ;
>> +       bool                    is_errata_50mhz_ac;
>> +};
>> +
>>  struct mvebu_spi_platdata {
>>         struct kwspi_registers *spireg;
>>  };
>> @@ -269,10 +279,30 @@ static int mvebu_spi_set_speed(struct udevice *bus, uint hz)
>>         return 0;
>>  }
>>
>> +static void
>> +mvebu_spi_50mhz_ac_timing_erratum(struct udevice *bus, uint mode)
>> +{
>> +       struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
>> +       struct kwspi_registers *reg = plat->spireg;
>> +       u32 data = readl(&reg->timing1);
>> +
>
> Better to have description of errata.
>

Will add in v2.

> thanks!
> --
> Jagan Teki
> Free Software Engineer | www.openedev.com
> U-Boot, Linux | Upstream Maintainer
> Hyderabad, India.

Patch

diff --git a/arch/arm/include/asm/arch-mvebu/spi.h b/arch/arm/include/asm/arch-mvebu/spi.h
index 3545aed17347..1de510ea6da9 100644
--- a/arch/arm/include/asm/arch-mvebu/spi.h
+++ b/arch/arm/include/asm/arch-mvebu/spi.h
@@ -57,6 +57,12 @@  struct kwspi_registers {
 #define KWSPI_TXLSBF		(1 << 13)
 #define KWSPI_RXLSBF		(1 << 14)
 
+/* Timing Parameters 1 Register */
+#define KW_SPI_TMISO_SAMPLE_OFFSET	6
+#define KW_SPI_TMISO_SAMPLE_MASK	(0x3 << KW_SPI_TMISO_SAMPLE_OFFSET)
+#define KW_SPI_TMISO_SAMPLE_1		(1 << KW_SPI_TMISO_SAMPLE_OFFSET)
+#define KW_SPI_TMISO_SAMPLE_2		(2 << KW_SPI_TMISO_SAMPLE_OFFSET)
+
 #define KWSPI_IRQUNMASK		1 /* unmask SPI interrupt */
 #define KWSPI_IRQMASK		0 /* mask SPI interrupt */
 #define KWSPI_SMEMRDIRQ		1 /* SerMem data xfer ready irq */
diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c
index 0c6bd295cde9..7992556d49eb 100644
--- a/drivers/spi/kirkwood_spi.c
+++ b/drivers/spi/kirkwood_spi.c
@@ -243,6 +243,16 @@  int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
 
 /* Here now the DM part */
 
+enum mvebu_spi_type {
+	ORION_SPI,
+	ARMADA_SPI,
+};
+
+struct mvebu_spi_dev {
+	enum mvebu_spi_type	typ;
+	bool			is_errata_50mhz_ac;
+};
+
 struct mvebu_spi_platdata {
 	struct kwspi_registers *spireg;
 };
@@ -269,10 +279,30 @@  static int mvebu_spi_set_speed(struct udevice *bus, uint hz)
 	return 0;
 }
 
+static void
+mvebu_spi_50mhz_ac_timing_erratum(struct udevice *bus, uint mode)
+{
+	struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
+	struct kwspi_registers *reg = plat->spireg;
+	u32 data = readl(&reg->timing1);
+
+	data &= ~KW_SPI_TMISO_SAMPLE_MASK;
+
+	if (CONFIG_SYS_TCLK == 250000000 &&
+	    mode & SPI_CPOL &&
+	    mode & SPI_CPHA)
+		data |= KW_SPI_TMISO_SAMPLE_2;
+	else
+		data |= KW_SPI_TMISO_SAMPLE_1;
+
+	writel(data, &reg->timing1);
+}
+
 static int mvebu_spi_set_mode(struct udevice *bus, uint mode)
 {
 	struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
 	struct kwspi_registers *reg = plat->spireg;
+	const struct mvebu_spi_dev *drvdata;
 	u32 data = readl(&reg->cfg);
 
 	data &= ~(KWSPI_CPHA | KWSPI_CPOL | KWSPI_RXLSBF | KWSPI_TXLSBF);
@@ -286,6 +316,10 @@  static int mvebu_spi_set_mode(struct udevice *bus, uint mode)
 
 	writel(data, &reg->cfg);
 
+	drvdata = (struct mvebu_spi_dev *)dev_get_driver_data(bus);
+	if (drvdata->is_errata_50mhz_ac)
+		mvebu_spi_50mhz_ac_timing_erratum(bus, mode);
+
 	return 0;
 }
 
@@ -343,10 +377,32 @@  static const struct dm_spi_ops mvebu_spi_ops = {
 	 */
 };
 
+static const struct mvebu_spi_dev armada_xp_spi_dev_data = {
+	.typ = ARMADA_SPI,
+};
+
+static const struct mvebu_spi_dev armada_375_spi_dev_data = {
+	.typ = ARMADA_SPI,
+};
+
+static const struct mvebu_spi_dev armada_380_spi_dev_data = {
+	.typ = ARMADA_SPI,
+	.is_errata_50mhz_ac = true,
+};
+
 static const struct udevice_id mvebu_spi_ids[] = {
-	{ .compatible = "marvell,armada-375-spi" },
-	{ .compatible = "marvell,armada-380-spi" },
-	{ .compatible = "marvell,armada-xp-spi" },
+	{
+		.compatible = "marvell,armada-375-spi",
+		.data = (ulong)&armada_375_spi_dev_data
+	},
+	{
+		.compatible = "marvell,armada-380-spi",
+		.data = (ulong)&armada_380_spi_dev_data
+	},
+	{
+		.compatible = "marvell,armada-xp-spi",
+		.data = (ulong)&armada_xp_spi_dev_data
+	},
 	{ }
 };