diff mbox

spi-imx: add support for single burst mode (8,16,32)

Message ID 574D5C2B.1000400@gtsys.com.hk (mailing list archive)
State New, archived
Headers show

Commit Message

Chris Ruehl May 31, 2016, 9:40 a.m. UTC
Hi,

as imx6 using multiple burst to send data to spi slaves and drop the chipselect 
between the words by default my sensor NXP MPL115A1 wasn't working.
The hint comes from a discussion in the Freescale forum from 2013 where Jeff 
Coffman posted his solution for a 3.x kernel.
I'd pick-up the idea behind and develop something which works "so far" with
4.6 and linux-next

Up front - I'm not happy using the xfer->cs_change to get set the single burst
I more likely want add a new xfer bit which allow to dedicated set a single burst.
To replace todays:
         xfer[0].cs_change = 0;
with a
	xfer[0].singleburst = 1;

An other issue with is not yet solved; when I have a odd number of bytes (8 bpw) 
in a transfer, its result in 3 bytes eaten on the start and 0x00 added on the 
tail -

Single file patch - something to view.

Patch v0: add support for imx6 single burst mode.

  		cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs);
@@ -861,6 +1090,8 @@ static int spi_imx_setupxfer(struct spi_device *spi,
  	config.speed_hz  = t ? t->speed_hz : spi->max_speed_hz;
  	config.mode = spi->mode;
  	config.cs = spi->chip_select;
+	config.len = t->len;
+	config.single_burst_mode = ~(t->cs_change);

  	if (!config.speed_hz)
  		config.speed_hz = spi->max_speed_hz;





Regards
Chris

Comments

Fabio Estevam May 31, 2016, 10:43 a.m. UTC | #1
[Adding Sascha and Anton in Cc]

On Tue, May 31, 2016 at 6:40 AM, Chris Ruehl <chris.ruehl@gtsys.com.hk> wrote:
> Hi,
>
> as imx6 using multiple burst to send data to spi slaves and drop the
> chipselect between the words by default my sensor NXP MPL115A1 wasn't
> working.
> The hint comes from a discussion in the Freescale forum from 2013 where Jeff
> Coffman posted his solution for a 3.x kernel.
> I'd pick-up the idea behind and develop something which works "so far" with
> 4.6 and linux-next
>
> Up front - I'm not happy using the xfer->cs_change to get set the single
> burst
> I more likely want add a new xfer bit which allow to dedicated set a single
> burst.
> To replace todays:
>         xfer[0].cs_change = 0;
> with a
>         xfer[0].singleburst = 1;
>
> An other issue with is not yet solved; when I have a odd number of bytes (8
> bpw) in a transfer, its result in 3 bytes eaten on the start and 0x00 added
> on the tail -
>
> Single file patch - something to view.
>
> Patch v0: add support for imx6 single burst mode.
>
> diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
> index 50769078..3440d0e 100644
> --- a/drivers/spi/spi-imx.c
> +++ b/drivers/spi/spi-imx.c
> @@ -60,6 +60,8 @@ struct spi_imx_config {
>         unsigned int speed_hz;
>         unsigned int bpw;
>         unsigned int mode;
> +       unsigned int len;
> +       unsigned int single_burst_mode:1;
>         u8 cs;
>  };
>
> @@ -99,11 +101,13 @@ struct spi_imx_data {
>         unsigned int bytes_per_word;
>
>         unsigned int count;
> +       unsigned int rxcount;
>         void (*tx)(struct spi_imx_data *);
>         void (*rx)(struct spi_imx_data *);
>         void *rx_buf;
>         const void *tx_buf;
>         unsigned int txfifo; /* number of words pushed in tx FIFO */
> +       u8 bits_per_word;
>
>         /* DMA */
>         bool usedma;
> @@ -168,6 +172,191 @@ MXC_SPI_BUF_TX(u16)
>  MXC_SPI_BUF_RX(u32)
>  MXC_SPI_BUF_TX(u32)
>
> +static void spi_imx_buf_rx_sb(struct spi_imx_data *spi_imx)
> +{
> +    unsigned int val = readl(spi_imx->base + MXC_CSPIRXDATA);
> +    dev_dbg(spi_imx->dev, "%s: rxcount: %u val:0x%08x\n",
> +           __func__, spi_imx->rxcount , val);
> +    if (spi_imx->rxcount>=4) {
> +           if (spi_imx->rx_buf) {
> +                   if (spi_imx->bits_per_word==32) {
> +                           spi_imx_buf_rx_u32(spi_imx);
> +                   } else if (spi_imx->bits_per_word==16) {
> +                           *(u16 *)spi_imx->rx_buf = (val>>16);
> +                           spi_imx->rx_buf += sizeof(u16);
> +                           *(u16 *)spi_imx->rx_buf = val;
> +                           spi_imx->rx_buf += sizeof(u16);
> +                   } else {
> +                           *(u8 *)spi_imx->rx_buf = (val>>24);
> +                           spi_imx->rx_buf += sizeof(u8);
> +                           *(u8 *)spi_imx->rx_buf = (val>>16);
> +                           spi_imx->rx_buf += sizeof(u8);
> +                           *(u8 *)spi_imx->rx_buf = (val>>8);
> +                           spi_imx->rx_buf += sizeof(u8);
> +                           *(u8 *)spi_imx->rx_buf = val;
> +                           spi_imx->rx_buf += sizeof(u8);
> +                   }
> +           }
> +           spi_imx->rxcount-=4;
> +    }
> +    else if (spi_imx->rxcount==3) {
> +           if (spi_imx->rx_buf) {
> +                   if (spi_imx->bits_per_word==32) {
> +                           *(u8 *)spi_imx->rx_buf = val>>24;
> +                           spi_imx->rx_buf += sizeof(u8);
> +                           *(u8 *)spi_imx->rx_buf = val>>16;
> +                           spi_imx->rx_buf += sizeof(u8);
> +                           *(u8 *)spi_imx->rx_buf = val>>8;
> +                           spi_imx->rx_buf += sizeof(u8);
> +                   } else if (spi_imx->bits_per_word==16) {
> +                           *(u16 *)spi_imx->rx_buf = (val>>16);
> +                           spi_imx->rx_buf += sizeof(u16);
> +                           *(u8 *)spi_imx->rx_buf = val>>8;
> +                           spi_imx->rx_buf += sizeof(u8);
> +                   } else {
> +                           *(u8 *)spi_imx->rx_buf = (val>>16);
> +                           spi_imx->rx_buf += sizeof(u8);
> +                           *(u8 *)spi_imx->rx_buf = (val>>8);
> +                           spi_imx->rx_buf += sizeof(u8);
> +                           *(u8 *)spi_imx->rx_buf = val;
> +                           spi_imx->rx_buf += sizeof(u8);
> +                   }
> +           }
> +           spi_imx->rxcount-=3;
> +    }
> +    else if (spi_imx->rxcount==2) {
> +           if (spi_imx->rx_buf) {
> +                   if (spi_imx->bits_per_word==32) {
> +                           *(u8 *)spi_imx->rx_buf = val>>24;
> +                           spi_imx->rx_buf += sizeof(u8);
> +                           *(u8 *)spi_imx->rx_buf = val>>16;
> +                           spi_imx->rx_buf += sizeof(u8);
> +                   } else if (spi_imx->bits_per_word==16) {
> +                           spi_imx_buf_rx_u16(spi_imx);
> +                   } else {
> +                           *(u8 *)spi_imx->rx_buf = (val>>8);
> +                           spi_imx->rx_buf += sizeof(u8);
> +                           *(u8 *)spi_imx->rx_buf = val;
> +                           spi_imx->rx_buf += sizeof(u8);
> +                   }
> +           }
> +           spi_imx->rxcount-=2;
> +    }
> +    else if (spi_imx->rxcount==1) {
> +           if (spi_imx->rx_buf) {
> +                   if (spi_imx->bits_per_word==32) {
> +                           *(u8 *)spi_imx->rx_buf = val>>24;
> +                   } else if (spi_imx->bits_per_word==16) {
> +                           *(u8 *)spi_imx->rx_buf = val>>8;
> +                   } else {
> +                           spi_imx_buf_rx_u8(spi_imx);
> +                   }
> +           }
> +           spi_imx->rxcount-=1;
> +    }
> +}
> +
> +static void spi_imx_buf_tx_sb(struct spi_imx_data *spi_imx)
> +{
> +       unsigned int val = 0;
> +       dev_dbg(spi_imx->dev, "%s: txcount: %u ptr:0x%08x\n",
> +               __func__, spi_imx->count ,*(u32 *)spi_imx->tx_buf);
> +       if (spi_imx->count>=4) {
> +               if (spi_imx->bits_per_word==32) {
> +                       spi_imx_buf_tx_u32(spi_imx);
> +               } else {
> +                       if (spi_imx->tx_buf) {
> +                               if (spi_imx->bits_per_word==16) {
> +                                       val = *(u16 *)spi_imx->tx_buf<<16;
> +                                       spi_imx->tx_buf += sizeof(u16);
> +                                       val |= *(u16 *)spi_imx->tx_buf;
> +                                       spi_imx->tx_buf += sizeof(u16);
> +                               } else {
> +                                       val = *(u8 *)spi_imx->tx_buf<<24;
> +                                       spi_imx->tx_buf += sizeof(u8);
> +                                       val |= *(u8 *)spi_imx->tx_buf<<16;
> +                                       spi_imx->tx_buf += sizeof(u8);
> +                                       val |= *(u8 *)spi_imx->tx_buf<<8;
> +                                       spi_imx->tx_buf += sizeof(u8);
> +                                       val |= *(u8 *)spi_imx->tx_buf;
> +                                       spi_imx->tx_buf += sizeof(u8);
> +                               }
> +                               writel(val, spi_imx->base + MXC_CSPITXDATA);
> +                       }
> +                       spi_imx->count -= 4;
> +               }
> +       }
> +       else if (spi_imx->count==3) {
> +               if (spi_imx->tx_buf) {
> +                       if (spi_imx->bits_per_word==32) {
> +                               val = *(u8 *)spi_imx->tx_buf<<24;
> +                               spi_imx->tx_buf += sizeof(u8);
> +                               val |= *(u8 *)spi_imx->tx_buf<<16;
> +                               spi_imx->tx_buf += sizeof(u8);
> +                               val |= *(u8 *)spi_imx->tx_buf<<8;
> +                               spi_imx->tx_buf += sizeof(u8);
> +
> +                       } else if (spi_imx->bits_per_word==16) {
> +                               val = *(u8 *)spi_imx->tx_buf<<24;
> +                               spi_imx->tx_buf += sizeof(u8);
> +                               val |= *(u8 *)spi_imx->tx_buf<<16;
> +                               spi_imx->tx_buf += sizeof(u8);
> +                               val |= *(u8 *)spi_imx->tx_buf;
> +                               spi_imx->tx_buf += sizeof(u8);
> +
> +                       } else {
> +                               val = *(u8 *)spi_imx->tx_buf<<16;
> +                               spi_imx->tx_buf += sizeof(u8);
> +                               val |= *(u8 *)spi_imx->tx_buf<<8;
> +                               spi_imx->tx_buf += sizeof(u8);
> +                               val |= *(u8 *)spi_imx->tx_buf;
> +                               spi_imx->tx_buf += sizeof(u8);
> +                       }
> +                       writel(val, spi_imx->base + MXC_CSPITXDATA);
> +               }
> +               spi_imx->count -= 3;
> +       }
> +       else if (spi_imx->count==2) {
> +               if (spi_imx->bits_per_word==16) {
> +                       spi_imx_buf_tx_u16(spi_imx);
> +               } else {
> +                       if (spi_imx->tx_buf) {
> +                               if (spi_imx->bits_per_word==32) {
> +                                       val = *(u8 *)spi_imx->tx_buf<<24;
> +                                       spi_imx->tx_buf += sizeof(u8);
> +                                       val |= *(u8 *)spi_imx->tx_buf<<16;
> +                                       spi_imx->tx_buf += sizeof(u8);
> +                               } else {
> +                                       val = *(u8 *)spi_imx->tx_buf<<8;
> +                                       spi_imx->tx_buf += sizeof(u8);
> +                                       val |= *(u8 *)spi_imx->tx_buf;
> +                                       spi_imx->tx_buf += sizeof(u8);
> +                               }
> +                               writel(val, spi_imx->base + MXC_CSPITXDATA);
> +                       }
> +                       spi_imx->count -= 2;
> +               }
> +       }
> +       else if (spi_imx->count==1) {
> +               if (spi_imx->bits_per_word==8){
> +                       spi_imx_buf_tx_u8(spi_imx);
> +               } else {
> +                       if (spi_imx->tx_buf) {
> +                               if (spi_imx->bits_per_word==32) {
> +                                       val = *(u8 *)spi_imx->tx_buf<<24;
> +                                       spi_imx->tx_buf += sizeof(u8);
> +                               } else if (spi_imx->bits_per_word==16) {
> +                                       val = *(u8 *)spi_imx->tx_buf<<8;
> +                                       spi_imx->tx_buf += sizeof(u8);
> +                               }
> +                               writel(val, spi_imx->base + MXC_CSPITXDATA);
> +                       }
> +                       spi_imx->count -= 1;
> +               }
> +       }
> +}
> +
> +
>  /* First entry is reserved, second entry is valid only if SDHC_SPIEN is set
>   * (which is currently not the case in this driver)
>   */
> @@ -357,9 +546,49 @@ static int __maybe_unused mx51_ecspi_config(struct
> spi_imx_data *spi_imx,
>         /* set chip select to use */
>         ctrl |= MX51_ECSPI_CTRL_CS(config->cs);
>
> -       ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET;
> +       /* set single/multible burst parameters */
> +       if (config->single_burst_mode>0) {
> +               reg = 0;
> +               spi_imx->rx = spi_imx_buf_rx_sb;
> +               spi_imx->tx = spi_imx_buf_tx_sb;
> +               spi_imx->rxcount = config->len;
> +               spi_imx->bits_per_word = config->bpw;
> +
> +               /* calculate the Burst Length,
> +                  refer to 21.7.3 Control Register (ECSPIx_CONREG)
> +                  for details.
> +                */
> +               switch (config->len%4)
> +               {
> +               case 1:
> +                       ctrl |= 7 << MX51_ECSPI_CTRL_BL_OFFSET;
> +                       reg = (config->len-1) / 4;
> +                       break;
> +               case 2:
> +                       ctrl |= 15 << MX51_ECSPI_CTRL_BL_OFFSET;
> +                       reg = (config->len-2) / 4;
> +                       break;
> +               case 3:
> +                       ctrl |= 23 << MX51_ECSPI_CTRL_BL_OFFSET;
> +                       reg = (config->len-3) / 4;
> +                       break;
> +               case 0:
> +                       ctrl |= 31 << MX51_ECSPI_CTRL_BL_OFFSET;
> +                       reg = (config->len-4) / 4;
> +                       break;
> +               }
> +
> +               if (reg>0)
> +                       ctrl |= reg << (MX51_ECSPI_CTRL_BL_OFFSET + 5);
>
> -       cfg |= MX51_ECSPI_CONFIG_SBBCTRL(config->cs);
> +               cfg &= ~(MX51_ECSPI_CONFIG_SBBCTRL(config->cs));
> +
> +               dev_dbg(spi_imx->dev, "Single Burst reg:0x%08x cfg:0x%08x
> ctrl:0x%08x\n"
> +                       , reg, cfg, ctrl);
> +       } else {
> +               ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET;
> +               cfg |= MX51_ECSPI_CONFIG_SBBCTRL(config->cs);
> +       }
>
>         if (config->mode & SPI_CPHA)
>                 cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs);
> @@ -861,6 +1090,8 @@ static int spi_imx_setupxfer(struct spi_device *spi,
>         config.speed_hz  = t ? t->speed_hz : spi->max_speed_hz;
>         config.mode = spi->mode;
>         config.cs = spi->chip_select;
> +       config.len = t->len;
> +       config.single_burst_mode = ~(t->cs_change);
>
>         if (!config.speed_hz)
>                 config.speed_hz = spi->max_speed_hz;
>
>
>
>
>
> Regards
> Chris
>
> --
> GTSYS Limited RFID Technology
> 9/F, Unit E, R07, Kwai Shing Industrial Building Phase 2,
> 42-46 Tai Lin Pai Road, Kwai Chung, N.T., Hong Kong
> Tel (852) 9079 9521
>
> Disclaimer: http://www.gtsys.com.hk/email/classified.html
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sascha Hauer May 31, 2016, 11:06 a.m. UTC | #2
On Tue, May 31, 2016 at 07:43:37AM -0300, Fabio Estevam wrote:
> [Adding Sascha and Anton in Cc]
> 
> On Tue, May 31, 2016 at 6:40 AM, Chris Ruehl <chris.ruehl@gtsys.com.hk> wrote:
> > Hi,
> >
> > as imx6 using multiple burst to send data to spi slaves and drop the
> > chipselect between the words by default my sensor NXP MPL115A1 wasn't
> > working.

I generally recommend to use GPIO Chip selects. The SPI controller has
its own ideas when to assert the chipselects which is hard to match (if
possible at all) with what Linux SPI expects. See the cs-gpios device
tree property.

Sascha
Vladimir Zapolskiy May 31, 2016, 11:20 a.m. UTC | #3
On 31.05.2016 12:40, Chris Ruehl wrote:
> Hi,
> 
> as imx6 using multiple burst to send data to spi slaves and drop the chipselect 
> between the words by default my sensor NXP MPL115A1 wasn't working.
> The hint comes from a discussion in the Freescale forum from 2013 where Jeff 
> Coffman posted his solution for a 3.x kernel.
> I'd pick-up the idea behind and develop something which works "so far" with
> 4.6 and linux-next
> 
> Up front - I'm not happy using the xfer->cs_change to get set the single burst
> I more likely want add a new xfer bit which allow to dedicated set a single burst.
> To replace todays:
>          xfer[0].cs_change = 0;
> with a
> 	xfer[0].singleburst = 1;
> 
> An other issue with is not yet solved; when I have a odd number of bytes (8 bpw) 
> in a transfer, its result in 3 bytes eaten on the start and 0x00 added on the 
> tail -

Do you use PIO or DMA transfer mode? The problem description resembles
a known issue within SDMA firmware (referenced as TKT238285 or ERR008517)
and you should not be able to face it, if SPI controller operates in PIO
mode.

--
With best wishes,
Vladimir
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mark Brown May 31, 2016, 1:08 p.m. UTC | #4
On Tue, May 31, 2016 at 05:40:59PM +0800, Chris Ruehl wrote:
> Hi,
> 
> as imx6 using multiple burst to send data to spi slaves and drop the
> chipselect between the words by default my sensor NXP MPL115A1 wasn't
> working.

Please submit patches in the format covered in SubmittingPatches, I
can't take anything without a Signed-off-by.
Chris Ruehl June 1, 2016, 1:48 a.m. UTC | #5
On Tuesday, May 31, 2016 07:20 PM, Vladimir Zapolskiy wrote:
> On 31.05.2016 12:40, Chris Ruehl wrote:
>> Hi,
>>
>> as imx6 using multiple burst to send data to spi slaves and drop the chipselect
>> between the words by default my sensor NXP MPL115A1 wasn't working.
>> The hint comes from a discussion in the Freescale forum from 2013 where Jeff
>> Coffman posted his solution for a 3.x kernel.
>> I'd pick-up the idea behind and develop something which works "so far" with
>> 4.6 and linux-next
>>
>> Up front - I'm not happy using the xfer->cs_change to get set the single burst
>> I more likely want add a new xfer bit which allow to dedicated set a single burst.
>> To replace todays:
>>           xfer[0].cs_change = 0;
>> with a
>> 	xfer[0].singleburst = 1;
>>
>> An other issue with is not yet solved; when I have a odd number of bytes (8 bpw)
>> in a transfer, its result in 3 bytes eaten on the start and 0x00 added on the
>> tail -
>
> Do you use PIO or DMA transfer mode? The problem description resembles
> a known issue within SDMA firmware (referenced as TKT238285 or ERR008517)
> and you should not be able to face it, if SPI controller operates in PIO
> mode.

Board is a imx6d and its runs in DMA mode. I have the MPL115A working properly
when I keep an eye on even numbers of bytes in a transfer (which is always the 
case in a half duplex mode <cmd><00>..)

But any how thanks for this interesting info!


Chris
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Chris Ruehl June 1, 2016, 1:54 a.m. UTC | #6
On Tuesday, May 31, 2016 07:06 PM, Sascha Hauer wrote:
> On Tue, May 31, 2016 at 07:43:37AM -0300, Fabio Estevam wrote:
>> [Adding Sascha and Anton in Cc]
>>
>> On Tue, May 31, 2016 at 6:40 AM, Chris Ruehl <chris.ruehl@gtsys.com.hk> wrote:
>>> Hi,
>>>
>>> as imx6 using multiple burst to send data to spi slaves and drop the
>>> chipselect between the words by default my sensor NXP MPL115A1 wasn't
>>> working.
>
> I generally recommend to use GPIO Chip selects. The SPI controller has
> its own ideas when to assert the chipselects which is hard to match (if
> possible at all) with what Linux SPI expects. See the cs-gpios device
> tree property.
>
> Sascha

Sascha that's a good approach to fight this problem, if you have the
choice. Sadly my hardware design is finished and I can only switch to
a gpio by run wires on the finished PCB.. and that's not what I want.

With the single burst mode patch, the logic analyser shows a very nice and
and clean and accurate chipselect clock MOSI and MISO.

Chris
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Chris Ruehl June 1, 2016, 1:58 a.m. UTC | #7
Mark,

On Tuesday, May 31, 2016 09:08 PM, Mark Brown wrote:
> On Tue, May 31, 2016 at 05:40:59PM +0800, Chris Ruehl wrote:
>> Hi,
>>
>> as imx6 using multiple burst to send data to spi slaves and drop the
>> chipselect between the words by default my sensor NXP MPL115A1 wasn't
>> working.
>
> Please submit patches in the format covered in SubmittingPatches, I
> can't take anything without a Signed-off-by.
>

Thanks,
  I will do submit an 'offical" patch with the input's from the
ongoing discussion. I'm on a change enable the single-burst-mode let
the cs_change alone.
I set a mask in the bit_per_word to enable it, which keeps changes in
the spi-imx.c only.

Chris



--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sascha Hauer June 1, 2016, 6:30 a.m. UTC | #8
On Wed, Jun 01, 2016 at 09:54:39AM +0800, Chris Ruehl wrote:
> On Tuesday, May 31, 2016 07:06 PM, Sascha Hauer wrote:
> >On Tue, May 31, 2016 at 07:43:37AM -0300, Fabio Estevam wrote:
> >>[Adding Sascha and Anton in Cc]
> >>
> >>On Tue, May 31, 2016 at 6:40 AM, Chris Ruehl <chris.ruehl@gtsys.com.hk> wrote:
> >>>Hi,
> >>>
> >>>as imx6 using multiple burst to send data to spi slaves and drop the
> >>>chipselect between the words by default my sensor NXP MPL115A1 wasn't
> >>>working.
> >
> >I generally recommend to use GPIO Chip selects. The SPI controller has
> >its own ideas when to assert the chipselects which is hard to match (if
> >possible at all) with what Linux SPI expects. See the cs-gpios device
> >tree property.
> >
> >Sascha
> 
> Sascha that's a good approach to fight this problem, if you have the
> choice. Sadly my hardware design is finished and I can only switch to
> a gpio by run wires on the finished PCB.. and that's not what I want.

I'm not suggesting to change your hardware. Every chipselect pin can be
configured as GPIO aswell, i.e. exchange
MX6QDL_PAD_DISP0_DAT3__ECSPI3_SS0 with MX6QDL_PAD_DISP0_DAT3__GPIO4_IO24
and be done with it.

Sascha
Chris Ruehl June 1, 2016, 6:39 a.m. UTC | #9
On Wednesday, June 01, 2016 02:30 PM, Sascha Hauer wrote:
> On Wed, Jun 01, 2016 at 09:54:39AM +0800, Chris Ruehl wrote:
>> On Tuesday, May 31, 2016 07:06 PM, Sascha Hauer wrote:
>>> On Tue, May 31, 2016 at 07:43:37AM -0300, Fabio Estevam wrote:
>>>> [Adding Sascha and Anton in Cc]
>>>>
>>>> On Tue, May 31, 2016 at 6:40 AM, Chris Ruehl <chris.ruehl@gtsys.com.hk> wrote:
>>>>> Hi,
>>>>>
>>>>> as imx6 using multiple burst to send data to spi slaves and drop the
>>>>> chipselect between the words by default my sensor NXP MPL115A1 wasn't
>>>>> working.
>>>
>>> I generally recommend to use GPIO Chip selects. The SPI controller has
>>> its own ideas when to assert the chipselects which is hard to match (if
>>> possible at all) with what Linux SPI expects. See the cs-gpios device
>>> tree property.
>>>
>>> Sascha
>>
>> Sascha that's a good approach to fight this problem, if you have the
>> choice. Sadly my hardware design is finished and I can only switch to
>> a gpio by run wires on the finished PCB.. and that's not what I want.
>
> I'm not suggesting to change your hardware. Every chipselect pin can be
> configured as GPIO aswell, i.e. exchange
> MX6QDL_PAD_DISP0_DAT3__ECSPI3_SS0 with MX6QDL_PAD_DISP0_DAT3__GPIO4_IO24
> and be done with it.
>
> Sascha
>

Hi Sascha,

yes, :) agree.

But my native chip select with single burst is doing the job flawless
now.

Chris
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index 50769078..3440d0e 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -60,6 +60,8 @@  struct spi_imx_config {
  	unsigned int speed_hz;
  	unsigned int bpw;
  	unsigned int mode;
+	unsigned int len;
+	unsigned int single_burst_mode:1;
  	u8 cs;
  };

@@ -99,11 +101,13 @@  struct spi_imx_data {
  	unsigned int bytes_per_word;

  	unsigned int count;
+	unsigned int rxcount;
  	void (*tx)(struct spi_imx_data *);
  	void (*rx)(struct spi_imx_data *);
  	void *rx_buf;
  	const void *tx_buf;
  	unsigned int txfifo; /* number of words pushed in tx FIFO */
+	u8 bits_per_word;

  	/* DMA */
  	bool usedma;
@@ -168,6 +172,191 @@  MXC_SPI_BUF_TX(u16)
  MXC_SPI_BUF_RX(u32)
  MXC_SPI_BUF_TX(u32)

+static void spi_imx_buf_rx_sb(struct spi_imx_data *spi_imx)
+{
+    unsigned int val = readl(spi_imx->base + MXC_CSPIRXDATA);
+    dev_dbg(spi_imx->dev, "%s: rxcount: %u val:0x%08x\n",
+	    __func__, spi_imx->rxcount , val);
+    if (spi_imx->rxcount>=4) {
+	    if (spi_imx->rx_buf) {
+		    if (spi_imx->bits_per_word==32) {
+			    spi_imx_buf_rx_u32(spi_imx);
+		    } else if (spi_imx->bits_per_word==16) {
+			    *(u16 *)spi_imx->rx_buf = (val>>16);
+			    spi_imx->rx_buf += sizeof(u16);
+			    *(u16 *)spi_imx->rx_buf = val;
+			    spi_imx->rx_buf += sizeof(u16);
+		    } else {
+			    *(u8 *)spi_imx->rx_buf = (val>>24);
+			    spi_imx->rx_buf += sizeof(u8);
+			    *(u8 *)spi_imx->rx_buf = (val>>16);
+			    spi_imx->rx_buf += sizeof(u8);
+			    *(u8 *)spi_imx->rx_buf = (val>>8);
+			    spi_imx->rx_buf += sizeof(u8);
+			    *(u8 *)spi_imx->rx_buf = val;
+			    spi_imx->rx_buf += sizeof(u8);
+		    }
+	    }
+	    spi_imx->rxcount-=4;
+    }
+    else if (spi_imx->rxcount==3) {
+	    if (spi_imx->rx_buf) {
+		    if (spi_imx->bits_per_word==32) {
+			    *(u8 *)spi_imx->rx_buf = val>>24;
+			    spi_imx->rx_buf += sizeof(u8);
+			    *(u8 *)spi_imx->rx_buf = val>>16;
+			    spi_imx->rx_buf += sizeof(u8);
+			    *(u8 *)spi_imx->rx_buf = val>>8;
+			    spi_imx->rx_buf += sizeof(u8);
+		    } else if (spi_imx->bits_per_word==16) {
+			    *(u16 *)spi_imx->rx_buf = (val>>16);
+			    spi_imx->rx_buf += sizeof(u16);
+			    *(u8 *)spi_imx->rx_buf = val>>8;
+			    spi_imx->rx_buf += sizeof(u8);
+		    } else {
+			    *(u8 *)spi_imx->rx_buf = (val>>16);
+			    spi_imx->rx_buf += sizeof(u8);
+			    *(u8 *)spi_imx->rx_buf = (val>>8);
+			    spi_imx->rx_buf += sizeof(u8);
+			    *(u8 *)spi_imx->rx_buf = val;
+			    spi_imx->rx_buf += sizeof(u8);
+		    }
+	    }
+	    spi_imx->rxcount-=3;
+    }
+    else if (spi_imx->rxcount==2) {
+	    if (spi_imx->rx_buf) {
+		    if (spi_imx->bits_per_word==32) {
+			    *(u8 *)spi_imx->rx_buf = val>>24;
+			    spi_imx->rx_buf += sizeof(u8);
+			    *(u8 *)spi_imx->rx_buf = val>>16;
+			    spi_imx->rx_buf += sizeof(u8);
+		    } else if (spi_imx->bits_per_word==16) {
+			    spi_imx_buf_rx_u16(spi_imx);
+		    } else {
+			    *(u8 *)spi_imx->rx_buf = (val>>8);
+			    spi_imx->rx_buf += sizeof(u8);
+			    *(u8 *)spi_imx->rx_buf = val;
+			    spi_imx->rx_buf += sizeof(u8);
+		    }
+	    }
+	    spi_imx->rxcount-=2;
+    }
+    else if (spi_imx->rxcount==1) {
+	    if (spi_imx->rx_buf) {
+		    if (spi_imx->bits_per_word==32) {
+			    *(u8 *)spi_imx->rx_buf = val>>24;
+		    } else if (spi_imx->bits_per_word==16) {
+			    *(u8 *)spi_imx->rx_buf = val>>8;
+		    } else {
+			    spi_imx_buf_rx_u8(spi_imx);
+		    }
+	    }
+	    spi_imx->rxcount-=1;
+    }
+}
+
+static void spi_imx_buf_tx_sb(struct spi_imx_data *spi_imx)
+{
+	unsigned int val = 0;
+	dev_dbg(spi_imx->dev, "%s: txcount: %u ptr:0x%08x\n",
+		__func__, spi_imx->count ,*(u32 *)spi_imx->tx_buf);
+	if (spi_imx->count>=4) {
+		if (spi_imx->bits_per_word==32) {
+			spi_imx_buf_tx_u32(spi_imx);
+		} else {
+			if (spi_imx->tx_buf) {
+				if (spi_imx->bits_per_word==16) {
+					val = *(u16 *)spi_imx->tx_buf<<16;
+					spi_imx->tx_buf += sizeof(u16);
+					val |= *(u16 *)spi_imx->tx_buf;
+					spi_imx->tx_buf += sizeof(u16);
+				} else {
+					val = *(u8 *)spi_imx->tx_buf<<24;
+					spi_imx->tx_buf += sizeof(u8);
+					val |= *(u8 *)spi_imx->tx_buf<<16;
+					spi_imx->tx_buf += sizeof(u8);
+					val |= *(u8 *)spi_imx->tx_buf<<8;
+					spi_imx->tx_buf += sizeof(u8);
+					val |= *(u8 *)spi_imx->tx_buf;
+					spi_imx->tx_buf += sizeof(u8);
+				}
+				writel(val, spi_imx->base + MXC_CSPITXDATA);
+			}
+			spi_imx->count -= 4;
+		}
+	}
+	else if (spi_imx->count==3) {
+		if (spi_imx->tx_buf) {
+			if (spi_imx->bits_per_word==32) {
+				val = *(u8 *)spi_imx->tx_buf<<24;
+				spi_imx->tx_buf += sizeof(u8);
+				val |= *(u8 *)spi_imx->tx_buf<<16;
+				spi_imx->tx_buf += sizeof(u8);
+				val |= *(u8 *)spi_imx->tx_buf<<8;
+				spi_imx->tx_buf += sizeof(u8);
+
+			} else if (spi_imx->bits_per_word==16) {
+				val = *(u8 *)spi_imx->tx_buf<<24;
+				spi_imx->tx_buf += sizeof(u8);
+				val |= *(u8 *)spi_imx->tx_buf<<16;
+				spi_imx->tx_buf += sizeof(u8);
+				val |= *(u8 *)spi_imx->tx_buf;
+				spi_imx->tx_buf += sizeof(u8);
+
+			} else {
+				val = *(u8 *)spi_imx->tx_buf<<16;
+				spi_imx->tx_buf += sizeof(u8);
+				val |= *(u8 *)spi_imx->tx_buf<<8;
+				spi_imx->tx_buf += sizeof(u8);
+				val |= *(u8 *)spi_imx->tx_buf;
+				spi_imx->tx_buf += sizeof(u8);
+			}
+			writel(val, spi_imx->base + MXC_CSPITXDATA);
+		}
+		spi_imx->count -= 3;
+	}
+	else if (spi_imx->count==2) {
+		if (spi_imx->bits_per_word==16) {
+			spi_imx_buf_tx_u16(spi_imx);
+		} else {
+			if (spi_imx->tx_buf) {
+				if (spi_imx->bits_per_word==32) {
+					val = *(u8 *)spi_imx->tx_buf<<24;
+					spi_imx->tx_buf += sizeof(u8);
+					val |= *(u8 *)spi_imx->tx_buf<<16;
+					spi_imx->tx_buf += sizeof(u8);
+				} else {
+					val = *(u8 *)spi_imx->tx_buf<<8;
+					spi_imx->tx_buf += sizeof(u8);
+					val |= *(u8 *)spi_imx->tx_buf;
+					spi_imx->tx_buf += sizeof(u8);
+				}
+				writel(val, spi_imx->base + MXC_CSPITXDATA);
+			}
+			spi_imx->count -= 2;
+		}
+	}
+	else if (spi_imx->count==1) {
+		if (spi_imx->bits_per_word==8){
+			spi_imx_buf_tx_u8(spi_imx);
+		} else {
+			if (spi_imx->tx_buf) {
+				if (spi_imx->bits_per_word==32) {
+					val = *(u8 *)spi_imx->tx_buf<<24;
+					spi_imx->tx_buf += sizeof(u8);
+				} else if (spi_imx->bits_per_word==16) {
+					val = *(u8 *)spi_imx->tx_buf<<8;
+					spi_imx->tx_buf += sizeof(u8);
+				}
+				writel(val, spi_imx->base + MXC_CSPITXDATA);
+			}
+			spi_imx->count -= 1;
+		}
+	}
+}
+
+
  /* First entry is reserved, second entry is valid only if SDHC_SPIEN is set
   * (which is currently not the case in this driver)
   */
@@ -357,9 +546,49 @@  static int __maybe_unused mx51_ecspi_config(struct 
spi_imx_data *spi_imx,
  	/* set chip select to use */
  	ctrl |= MX51_ECSPI_CTRL_CS(config->cs);

-	ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET;
+	/* set single/multible burst parameters */
+	if (config->single_burst_mode>0) {
+		reg = 0;
+		spi_imx->rx = spi_imx_buf_rx_sb;
+		spi_imx->tx = spi_imx_buf_tx_sb;
+		spi_imx->rxcount = config->len;
+		spi_imx->bits_per_word = config->bpw;
+
+		/* calculate the Burst Length,
+		   refer to 21.7.3 Control Register (ECSPIx_CONREG)
+		   for details.
+		 */
+		switch (config->len%4)
+		{
+		case 1:
+			ctrl |= 7 << MX51_ECSPI_CTRL_BL_OFFSET;
+			reg = (config->len-1) / 4;
+			break;
+		case 2:
+			ctrl |= 15 << MX51_ECSPI_CTRL_BL_OFFSET;
+			reg = (config->len-2) / 4;
+			break;
+		case 3:
+			ctrl |= 23 << MX51_ECSPI_CTRL_BL_OFFSET;
+			reg = (config->len-3) / 4;
+			break;
+		case 0:
+			ctrl |= 31 << MX51_ECSPI_CTRL_BL_OFFSET;
+			reg = (config->len-4) / 4;
+			break;
+		}
+
+		if (reg>0)
+			ctrl |= reg << (MX51_ECSPI_CTRL_BL_OFFSET + 5);

-	cfg |= MX51_ECSPI_CONFIG_SBBCTRL(config->cs);
+		cfg &= ~(MX51_ECSPI_CONFIG_SBBCTRL(config->cs));
+
+		dev_dbg(spi_imx->dev, "Single Burst reg:0x%08x cfg:0x%08x ctrl:0x%08x\n"
+			, reg, cfg, ctrl);
+	} else {
+		ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET;
+		cfg |= MX51_ECSPI_CONFIG_SBBCTRL(config->cs);
+	}

  	if (config->mode & SPI_CPHA)