mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-07-09 15:25:22 +02:00
spi: gpio: Implement LSB First bitbang support
Add support for slave DT property spi-lsb-first, i.e., SPI_LSB_FIRST mode. Duplicate the inline helpers bitbang_txrx_be_cpha{0,1} as LE versions. Conditionally call them from all the spi-gpio txrx_word callbacks. Some alternatives to this implementation approach were discussed back then [0], but eventually it was considered reasonable. [0] https://lore.kernel.org/linux-arm-kernel/20191212033952.5967-8-afaerber@suse.de/ Signed-off-by: Andreas Färber <afaerber@suse.de> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com> Tested-by: Christian Hewitt <christianshewitt@gmail.com> Link: https://lore.kernel.org/r/feac3377-4ad1-77d8-9a18-3588d80fb909@gmail.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
5741150c80
commit
1847e3046c
|
@ -41,6 +41,8 @@
|
||||||
* chips need ... there may be several reasons you'd need to tweak timings
|
* chips need ... there may be several reasons you'd need to tweak timings
|
||||||
* in these routines, not just to make it faster or slower to match a
|
* in these routines, not just to make it faster or slower to match a
|
||||||
* particular CPU clock rate.
|
* particular CPU clock rate.
|
||||||
|
*
|
||||||
|
* ToDo: Maybe the bitrev macros can be used to improve the code?
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline u32
|
static inline u32
|
||||||
|
@ -106,3 +108,67 @@ bitbang_txrx_be_cpha1(struct spi_device *spi,
|
||||||
}
|
}
|
||||||
return word;
|
return word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u32
|
||||||
|
bitbang_txrx_le_cpha0(struct spi_device *spi,
|
||||||
|
unsigned int nsecs, unsigned int cpol, unsigned int flags,
|
||||||
|
u32 word, u8 bits)
|
||||||
|
{
|
||||||
|
/* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
|
||||||
|
|
||||||
|
u32 oldbit = !(word & 1);
|
||||||
|
/* clock starts at inactive polarity */
|
||||||
|
for (; likely(bits); bits--) {
|
||||||
|
|
||||||
|
/* setup LSB (to slave) on trailing edge */
|
||||||
|
if ((flags & SPI_MASTER_NO_TX) == 0) {
|
||||||
|
if ((word & 1) != oldbit) {
|
||||||
|
setmosi(spi, word & 1);
|
||||||
|
oldbit = word & 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spidelay(nsecs); /* T(setup) */
|
||||||
|
|
||||||
|
setsck(spi, !cpol);
|
||||||
|
spidelay(nsecs);
|
||||||
|
|
||||||
|
/* sample LSB (from slave) on leading edge */
|
||||||
|
word >>= 1;
|
||||||
|
if ((flags & SPI_MASTER_NO_RX) == 0)
|
||||||
|
word |= getmiso(spi) << (bits - 1);
|
||||||
|
setsck(spi, cpol);
|
||||||
|
}
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32
|
||||||
|
bitbang_txrx_le_cpha1(struct spi_device *spi,
|
||||||
|
unsigned int nsecs, unsigned int cpol, unsigned int flags,
|
||||||
|
u32 word, u8 bits)
|
||||||
|
{
|
||||||
|
/* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */
|
||||||
|
|
||||||
|
u32 oldbit = !(word & 1);
|
||||||
|
/* clock starts at inactive polarity */
|
||||||
|
for (; likely(bits); bits--) {
|
||||||
|
|
||||||
|
/* setup LSB (to slave) on leading edge */
|
||||||
|
setsck(spi, !cpol);
|
||||||
|
if ((flags & SPI_MASTER_NO_TX) == 0) {
|
||||||
|
if ((word & 1) != oldbit) {
|
||||||
|
setmosi(spi, word & 1);
|
||||||
|
oldbit = word & 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spidelay(nsecs); /* T(setup) */
|
||||||
|
|
||||||
|
setsck(spi, cpol);
|
||||||
|
spidelay(nsecs);
|
||||||
|
|
||||||
|
/* sample LSB (from slave) on trailing edge */
|
||||||
|
word >>= 1;
|
||||||
|
if ((flags & SPI_MASTER_NO_RX) == 0)
|
||||||
|
word |= getmiso(spi) << (bits - 1);
|
||||||
|
}
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
|
@ -135,24 +135,36 @@ static inline int getmiso(const struct spi_device *spi)
|
||||||
static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi,
|
static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi,
|
||||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||||
{
|
{
|
||||||
|
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||||
|
return bitbang_txrx_le_cpha0(spi, nsecs, 0, flags, word, bits);
|
||||||
|
else
|
||||||
return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits);
|
return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi,
|
static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi,
|
||||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||||
{
|
{
|
||||||
|
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||||
|
return bitbang_txrx_le_cpha1(spi, nsecs, 0, flags, word, bits);
|
||||||
|
else
|
||||||
return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits);
|
return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi,
|
static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi,
|
||||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||||
{
|
{
|
||||||
|
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||||
|
return bitbang_txrx_le_cpha0(spi, nsecs, 1, flags, word, bits);
|
||||||
|
else
|
||||||
return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits);
|
return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi,
|
static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi,
|
||||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||||
{
|
{
|
||||||
|
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||||
|
return bitbang_txrx_le_cpha1(spi, nsecs, 1, flags, word, bits);
|
||||||
|
else
|
||||||
return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits);
|
return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +182,9 @@ static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi,
|
||||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||||
{
|
{
|
||||||
flags = spi->master->flags;
|
flags = spi->master->flags;
|
||||||
|
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||||
|
return bitbang_txrx_le_cpha0(spi, nsecs, 0, flags, word, bits);
|
||||||
|
else
|
||||||
return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits);
|
return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,6 +192,9 @@ static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi,
|
||||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||||
{
|
{
|
||||||
flags = spi->master->flags;
|
flags = spi->master->flags;
|
||||||
|
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||||
|
return bitbang_txrx_le_cpha1(spi, nsecs, 0, flags, word, bits);
|
||||||
|
else
|
||||||
return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits);
|
return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,6 +202,9 @@ static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi,
|
||||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||||
{
|
{
|
||||||
flags = spi->master->flags;
|
flags = spi->master->flags;
|
||||||
|
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||||
|
return bitbang_txrx_le_cpha0(spi, nsecs, 1, flags, word, bits);
|
||||||
|
else
|
||||||
return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits);
|
return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,6 +212,9 @@ static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi,
|
||||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||||
{
|
{
|
||||||
flags = spi->master->flags;
|
flags = spi->master->flags;
|
||||||
|
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||||
|
return bitbang_txrx_le_cpha1(spi, nsecs, 1, flags, word, bits);
|
||||||
|
else
|
||||||
return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits);
|
return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,7 +402,7 @@ static int spi_gpio_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
|
||||||
master->mode_bits = SPI_3WIRE | SPI_3WIRE_HIZ | SPI_CPHA | SPI_CPOL |
|
master->mode_bits = SPI_3WIRE | SPI_3WIRE_HIZ | SPI_CPHA | SPI_CPOL |
|
||||||
SPI_CS_HIGH;
|
SPI_CS_HIGH | SPI_LSB_FIRST;
|
||||||
if (!spi_gpio->mosi) {
|
if (!spi_gpio->mosi) {
|
||||||
/* HW configuration without MOSI pin
|
/* HW configuration without MOSI pin
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue
Block a user