dm: spi: soft_spi bug fix
Subsystems (4)
PATCH v3 1/4 dm: spi: soft_spi bug fix
When doing xfer, should use device->parent, but not device When doing bit xfer, should use "!!(tmpdout & 0x80)", but not "(tmpdout & 0x80)"
---
V3:
None
V2:
Follow Simon's comments, split bug fix into seperate patch.
drivers/spi/soft_spi.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-)
drivers/spi/soft_spi.c +12 -7
@@ -34,7 +34,8 @@ struct soft_spi_priv {static int soft_spi_scl (struct udevice *dev,int bit) { -struct soft_spi_platdata *plat = dev->platdata; +struct udevice *bus =dev_get_parent (dev); +struct soft_spi_platdata *plat =dev_get_platdata (bus);dm_gpio_set_value (&plat->sclk, bit);
@@ -43,7 +44,8 @@ static int soft_spi_scl(struct udevice *dev, int bit)static int soft_spi_sda (struct udevice *dev,int bit) { -struct soft_spi_platdata *plat = dev->platdata; +struct udevice *bus =dev_get_parent (dev); +struct soft_spi_platdata *plat =dev_get_platdata (bus);dm_gpio_set_value (&plat->mosi, bit);
@@ -52,7 +54,8 @@ static int soft_spi_sda(struct udevice *dev, int bit)static int soft_spi_cs_activate (struct udevice *dev) { -struct soft_spi_platdata *plat = dev->platdata; +struct udevice *bus =dev_get_parent (dev); +struct soft_spi_platdata *plat =dev_get_platdata (bus);dm_gpio_set_value (&plat->cs,0 );dm_gpio_set_value (&plat->sclk,0 );
@@ -63,7 +66,8 @@ static int soft_spi_cs_activate(struct udevice *dev)static int soft_spi_cs_deactivate (struct udevice *dev) { -struct soft_spi_platdata *plat = dev->platdata; +struct udevice *bus =dev_get_parent (dev); +struct soft_spi_platdata *plat =dev_get_platdata (bus);dm_gpio_set_value (&plat->cs,0 );
@@ -100,8 +104,9 @@ static int soft_spi_release_bus(struct udevice *dev)static int soft_spi_xfer (struct udevice *dev,unsigned int bitlen,const void *dout,void *din,unsigned long flags) { -struct soft_spi_priv *priv =dev_get_priv (dev); -struct soft_spi_platdata *plat = dev->platdata; +struct udevice *bus =dev_get_parent (dev); +struct soft_spi_priv *priv =dev_get_priv (bus); +struct soft_spi_platdata *plat =dev_get_platdata (bus);uchar tmpdin =0 ;uchar tmpdout =0 ;const u8 *txd = dout;
@@ -134,7 +139,7 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen,if (!cpha)soft_spi_scl (dev,0 ); -soft_spi_sda (dev, tmpdout &0x80 ); +soft_spi_sda (dev, !!(tmpdout &0x80 ));udelay (plat->spi_delay_us);if (cpha)soft_spi_scl (dev,0 );
Signature · 1 line
2.6.2
PATCH v3 2/4 dm: spi: soft_spi: switch to use linux compatible string
1. Support compatible string "spi-gpio" which is used by Linux Linux use different bindings, so use UBOOT_COMPAT and LINUX_COMPAT to differentiate them.
2. Introduce SPI_MASTER_NO_RX and SPI_MASTER_NO_TX to handle no rx or no tx case.
3. Tested on i.MX6 UltraLite board with 74LV595 spi-gpio chip.
---
V3:
None
V2:
Follow Simon's comments, drop uboot compatible string, convert to use Linux compatile string, and convert the universal board.
arch/arm/dts/exynos4210-universal_c210.dts | 10 ++++---- drivers/spi/soft_spi.c | 38 ++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 15 deletions(-)
arch/arm/dts/exynos4210-universal_c210.dts +5 -5
@@ -42,11 +42,11 @@ }; soft-spi { - compatible = "u-boot,soft-spi"; - cs-gpio = <&gpy4 3 0>; - sclk-gpio = <&gpy3 1 0>; - mosi-gpio = <&gpy3 3 0>; - miso-gpio = <&gpy3 0 0>; + compatible = "spi-gpio"; + cs-gpios = <&gpy4 3 0>; + gpio-sck = <&gpy3 1 0>; + gpio-mosi = <&gpy3 3 0>; + gpio-miso = <&gpy3 0 0>; spi-delay-us = <1>; #address-cells = <1>; #size-cells = <0>;
drivers/spi/soft_spi.c +28 -10
@@ -26,8 +26,12 @@ struct soft_spi_platdata {struct gpio_desc mosi;struct gpio_desc miso;int spi_delay_us; +int flags; }; +#define SPI_MASTER_NO_RX BIT(0) +#define SPI_MASTER_NO_TX BIT(1) +struct soft_spi_priv {unsigned int mode; };
@@ -139,14 +143,16 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen,if (!cpha)soft_spi_scl (dev,0 ); -soft_spi_sda (dev, !!(tmpdout &0x80 )); +if ((plat->flags &SPI_MASTER_NO_TX ) ==0 ) +soft_spi_sda (dev, !!(tmpdout &0x80 ));udelay (plat->spi_delay_us);if (cpha)soft_spi_scl (dev,0 );else soft_spi_scl (dev,1 ); tmpdin <<=1 ; - tmpdin |=dm_gpio_get_value (&plat->miso); +if ((plat->flags &SPI_MASTER_NO_RX ) ==0 ) + tmpdin |=dm_gpio_get_value (&plat->miso); tmpdout <<=1 ;udelay (plat->spi_delay_us);if (cpha)
@@ -208,24 +214,36 @@ static int soft_spi_probe(struct udevice *dev)struct spi_slave *slave =dev_get_parent_priv (dev);struct soft_spi_platdata *plat = dev->platdata;int cs_flags, clk_flags; +int ret; cs_flags = (slave->mode &SPI_CS_HIGH ) ?0 :GPIOD_ACTIVE_LOW ; clk_flags = (slave->mode &SPI_CPOL ) ?GPIOD_ACTIVE_LOW :0 ; -if (gpio_request_by_name (dev,"cs-gpio" ,0 , &plat->cs, + +if (gpio_request_by_name (dev,"cs-gpios" ,0 , &plat->cs,GPIOD_IS_OUT | cs_flags) || -gpio_request_by_name (dev,"sclk-gpio" ,0 , &plat->sclk, -GPIOD_IS_OUT | clk_flags) || -gpio_request_by_name (dev,"mosi-gpio" ,0 , &plat->mosi, -GPIOD_IS_OUT |GPIOD_IS_OUT_ACTIVE ) || -gpio_request_by_name (dev,"miso-gpio" ,0 , &plat->miso, -GPIOD_IS_IN )) +gpio_request_by_name (dev,"gpio-sck" ,0 , &plat->sclk, +GPIOD_IS_OUT | clk_flags)) +return -EINVAL ; + + ret =gpio_request_by_name (dev,"gpio-mosi" ,0 , &plat->mosi, +GPIOD_IS_OUT |GPIOD_IS_OUT_ACTIVE ); +if (ret) + plat->flags |=SPI_MASTER_NO_TX ; + + ret =gpio_request_by_name (dev,"gpio-miso" ,0 , &plat->miso, +GPIOD_IS_IN ); +if (ret) + plat->flags |=SPI_MASTER_NO_RX ; + +if ((plat->flags & (SPI_MASTER_NO_RX |SPI_MASTER_NO_TX )) == + (SPI_MASTER_NO_RX |SPI_MASTER_NO_TX ))return -EINVAL ;return 0 ; }static const struct udevice_id soft_spi_ids[] = { - { .compatible ="u-boot,soft-spi" }, + { .compatible ="spi-gpio" }, { } };
Signature · 1 line
2.6.2
PATCH v3 3/4 dm: spi: introduce dm api
In reply to Peng FanIntroduce dm_spi_claim_bus, dm_spi_release_bus and dm_spi_xfer
---
V3:
As Simon suggested, introduce new API. New patch.
drivers/spi/spi-uclass.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ include/spi.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+)
drivers/spi/spi-uclass.c +48 -0
@@ -45,6 +45,54 @@ static int spi_set_speed_mode(struct udevice *bus, int speed, int mode)return 0 ; } +int dm_spi_claim_bus (struct udevice *dev) +{ +struct udevice *bus = dev->parent; +struct dm_spi_ops *ops =spi_get_ops (bus); +struct dm_spi_bus *spi =dev_get_uclass_priv (bus); +struct spi_slave *slave =dev_get_parent_priv (dev); +int speed; +int ret; + + speed = slave->max_hz; +if (spi->max_hz) { +if (speed) + speed =min (speed, (int )spi->max_hz); +else + speed = spi->max_hz; + } +if (!speed) + speed =100000 ; +if (speed != slave->speed) { + ret =spi_set_speed_mode (bus, speed, slave->mode); +if (ret) +return ret; + slave->speed = speed; + } + +return ops->claim_bus ? ops->claim_bus (dev) :0 ; +} + +void dm_spi_release_bus (struct udevice *dev) +{ +struct udevice *bus = dev->parent; +struct dm_spi_ops *ops =spi_get_ops (bus); + +if (ops->release_bus) + ops->release_bus (dev); +} + +int dm_spi_xfer (struct udevice *dev,unsigned int bitlen, +const void *dout,void *din,unsigned long flags) +{ +struct udevice *bus = dev->parent; + +if (bus->uclass->uc_drv->id !=UCLASS_SPI ) +return -EOPNOTSUPP ; + +return spi_get_ops (bus)->xfer (dev, bitlen, dout, din, flags); +} +int spi_claim_bus (struct spi_slave *slave) {struct udevice *dev = slave->dev;
include/spi.h +52 -0
58 lines not on this page · raw message
Signature · 1 line
2.6.2
PATCH v3 3/4 dm: spi: introduce dm api
Hi Peng,
Quoted 9 lines · Peng Fan
Introduce
dm_spi_claim_bus,dm_spi_release_busanddm_spi_xferSigned-off-by Peng Fan <[email protected]>Cc Simon Glass <[email protected]>Cc Jagan Teki <[email protected]>---
V3:
As Simon suggested, introduce new API. New patch.
This looks great. But can you please adjust the legacy functions to call your new ones? E.g. spi_claim_bus() should call your function.
Quoted 5 lines
drivers/spi/spi-uclass.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ include/spi.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+)
[snip]
PATCH v3 4/4 dm: gpio: introduce 74x164 driver
In reply to Peng FanIntroduce driver to support "fairchild,74hc595" devices.
1. Take linux drivers/drivers/gpio/gpio-74x164.c as reference.
2. Following the naming used in Linux driver with gen_7x164 as the prefix.
3. Enable CONFIG_DM_74X164 to use this driver.
4. Follow Documentation/devicetree/bindings/gpio/gpio-74x164.txt to add device nodes
5. Tested on i.MX6 UltraLite with 74LV595 using gpio command and oscillograph.
---
V3:
Use dm_spi_claim_bus and dm_spi_xfer and dm_spi_release_bus Drop spi_slave from struct gen_74x164_priv.
V2:
Address Simon's comments:
Drop unused dev entry of gen_74x164_priv
Rename gen_74x164_info to gen_74x164_priv
Change u8 to uint
Change EINVAL to ENOSYS
Drop dm_gpio_set_value when probe, since GPIOD_IS_OUT_ACTIVE already active the gpio.
Drop complicated spi_get_bus_and_cs, use dev_get_parent_priv. To Kconfig help msg, I add 74lv165 and 75hc595, actually, the linux one does not contain manufacture info.
drivers/gpio/74x164_gpio.c | 193 +++++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/Kconfig | 8 ++ drivers/gpio/Makefile | 1 + 3 files changed, 202 insertions(+) create mode 100644 drivers/gpio/74x164_gpio.c
drivers/gpio/74x164_gpio.c new file +193 -0
@@ -0,0 +1,193 @@ +/* +* Take drivers/gpio/gpio-74x164.c as reference. +* +* 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver +* +* Copyright (C) 2016 Peng Fan <[email protected]> +* +* SPDX-License-Identifier: GPL-2.0+ +* +*/ + +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <fdtdec.h> +#include <malloc.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <dt-bindings/gpio/gpio.h> +#include <spi.h> + +DECLARE_GLOBAL_DATA_PTR ; + +/* +* struct gen_74x164_chip - Data for 74Hx164 +* +* @oe: OE pin +* @nregs: number of registers +* @buffer: buffer for chained chips +*/ +#define GEN_74X164_NUMBER_GPIOS 8 + +struct gen_74x164_priv { +struct gpio_desc oe; +u32 nregs; +/* +* Since the nregs are chained, every byte sent will make +* the previous byte shift to the next register in the +* chain. Thus, the first byte sent will end up in the last +* register at the end of the transfer. So, to have a logical +* numbering, store the bytes in reverse order. +*/ +u8 *buffer; +}; + +static int gen_74x164_write_conf (struct udevice *dev) +{ +struct gen_74x164_priv *priv =dev_get_priv (dev); +int ret; + + ret =dm_spi_claim_bus (dev); +if (ret) +return ret; + + ret =dm_spi_xfer (dev, priv->nregs *8 , priv->buffer,NULL , +SPI_XFER_BEGIN |SPI_XFER_END ); + +dm_spi_release_bus (dev); + +return ret; +} + +static int gen_74x164_get_value (struct udevice *dev,unsigned offset) +{ +struct gen_74x164_priv *priv =dev_get_priv (dev); +uint bank = priv->nregs -1 - offset /8 ; +uint pin = offset %8 ; + +return (priv->buffer[bank] >> pin) &0x1 ; +} + +static int gen_74x164_set_value (struct udevice *dev,unsigned offset, +int value) +{ +struct gen_74x164_priv *priv =dev_get_priv (dev); +uint bank = priv->nregs -1 - offset /8 ; +uint pin = offset %8 ; +int ret; + +if (value) + priv->buffer[bank] |=1 << pin; +else + priv->buffer[bank] &= ~(1 << pin); + + ret =gen_74x164_write_conf (dev); +if (ret) +return ret; + +return 0 ; +} + +static int gen_74x164_direction_input (struct udevice *dev,unsigned offset) +{ +return -ENOSYS ; +} + +static int gen_74x164_direction_output (struct udevice *dev,unsigned offset, +int value) +{ +return gen_74x164_set_value (dev, offset, value); +} + +static int gen_74x164_get_function (struct udevice *dev,unsigned offset) +{ +return GPIOF_OUTPUT ; +} + +static int gen_74x164_xlate (struct udevice *dev,struct gpio_desc *desc, +struct fdtdec_phandle_args *args) +{ + desc->offset = args->args[0 ]; + desc->flags = args->args[1 ] &GPIO_ACTIVE_LOW ?GPIOD_ACTIVE_LOW :0 ; + +return 0 ; +} + +static const struct dm_gpio_ops gen_74x164_ops = { + .direction_input = gen_74x164_direction_input, + .direction_output = gen_74x164_direction_output, + .get_value = gen_74x164_get_value, + .set_value = gen_74x164_set_value, + .get_function = gen_74x164_get_function, + .xlate = gen_74x164_xlate, +}; + +static int gen_74x164_probe (struct udevice *dev) +{ +struct gen_74x164_priv *priv =dev_get_priv (dev); +struct gpio_dev_priv *uc_priv =dev_get_uclass_priv (dev); +char *str, name[32 ]; +int ret; +const void *fdt = gd->fdt_blob; +int node = dev->of_offset; + +snprintf (name,sizeof (name),"%s_" , dev->name); + str =strdup (name); +if (!str) +return -ENOMEM ; + +/* +* See Linux kernel: +* Documentation/devicetree/bindings/gpio/gpio-74x164.txt +*/ + priv->nregs =fdtdec_get_int (fdt, node,"registers-number" ,1 ); + priv->buffer =calloc (priv->nregs,sizeof (u8)); +if (!priv->buffer) { + ret = -ENOMEM ; + goto free_str; + } + + ret =fdtdec_get_byte_array (fdt, node,"registers-default" , + priv->buffer, priv->nregs); +if (ret) +dev_dbg (dev,"No registers-default property\n" ); + + ret =gpio_request_by_name (dev,"oe-gpios" ,0 , &priv->oe, +GPIOD_IS_OUT |GPIOD_IS_OUT_ACTIVE ); +if (ret) { +dev_err (dev,"No oe-pins property\n" ); + goto free_buf; + } + + uc_priv->bank_name = str; + uc_priv->gpio_count = priv->nregs *8 ; + + ret =gen_74x164_write_conf (dev); +if (ret) + goto free_buf; + +dev_dbg (dev,"%s is ready\n" , dev->name); + +return 0 ; + +free_buf: +free (priv->buffer); +free_str: +free (str); +return ret; +} + +static const struct udevice_id gen_74x164_ids[] = { + { .compatible ="fairchild,74hc595" }, + { } +}; + +U_BOOT_DRIVER(74x164) = { + .name = "74x164", + .id = UCLASS_GPIO, + .ops = &gen_74x164_ops, + .probe = gen_74x164_probe, + .priv_auto_alloc_size = sizeof(struct gen_74x164_priv), + .of_match = gen_74x164_ids, +};
drivers/gpio/Kconfig +8 -0
@@ -143,6 +143,14 @@ config ZYNQ_GPIO help Supports GPIO access on Zynq SoC. +config DM_74X164 + bool "74x164 serial-in/parallel-out 8-bits shift register" + depends on DM_GPIO + help + Driver for 74x164 compatible serial-in/parallel-out 8-outputs + shift registers, such as 74lv165, 74hc595. + This driver can be used to provide access to more gpio outputs. + config DM_PCA953X bool "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports" depends on DM_GPIO
drivers/gpio/Makefile +1 -0
@@ -12,6 +12,7 @@ endif obj-$(CONFIG_DM_GPIO) += gpio-uclass.o obj-$(CONFIG_DM_PCA953X) += pca953x_gpio.o +obj-$(CONFIG_DM_74X164) += 74x164_gpio.o obj-$(CONFIG_AT91_GPIO) += at91_gpio.o obj-$(CONFIG_ATMEL_PIO4) += atmel_pio4.o
Signature · 1 line
2.6.2
PATCH v3 4/4 dm: gpio: introduce 74x164 driver
Quoted 22 lines · Peng Fan
Introduce driver to support "fairchild,74hc595" devices.
1. Take linuxdrivers/drivers/gpio/gpio-74x164.cas reference.
2. Following the naming used in Linux driver withgen_7x164as the prefix.
3. EnableCONFIG_DM_74X164to use this driver.
4. FollowDocumentation/devicetree/bindings/gpio/gpio-74x164.txtto add device nodes
5. Tested on i.MX6 UltraLite with 74LV595 using gpio command and oscillograph.Signed-off-by Peng Fan <[email protected]>Cc Simon Glass <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Chin Liang See <[email protected]>Cc Bhuvanchandra DV <[email protected]>Cc Daniel Schwierzeck <[email protected]>Cc Fabio Estevam <[email protected]>Cc Stefano Babic <[email protected]>---
V3:
Usedm_spi_claim_busanddm_spi_xferanddm_spi_release_busDropspi_slavefrom structgen_74x164_priv.
Quoted 11 lines
V2:
Address Simon's comments:
Drop unused dev entry ofgen_74x164_priv
Renamegen_74x164_infotogen_74x164_priv
Change u8 to uint
Change EINVAL to ENOSYS
Dropdm_gpio_set_valuewhen probe, sinceGPIOD_IS_OUT_ACTIVEalready active the gpio.
Drop complicatedspi_get_bus_and_cs, usedev_get_parent_priv. ToKconfighelp msg, I add 74lv165 and 75hc595, actually, the linux one does not contain manufacture info.
Very nice
PATCH v3 1/4 dm: spi: soft_spi bug fix
In reply to Peng FanQuoted 10 lines · Peng Fan
When doing xfer, should use
device->parent, but not device When doing bit xfer, should use "!!(tmpdout &0x80)", but not "(tmpdout &0x80)"Signed-off-by Peng Fan <[email protected]>Cc Simon Glass <[email protected]>Cc Jagan Teki <[email protected]>Reviewed-by Simon Glass <[email protected]>---
Quoted 8 lines
V3:
NoneV2:
Follow Simon's comments, split bug fix into seperate patch.drivers/spi/soft_spi.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-)