Introduce driver model support for SPI, SPI flash, cros_ec
Subsystems (6)
cover Introduce driver model support for SPI, SPI flash, cros_ec
Up until now driver model has not been used for any type of bus. Buses have some unique properties and needs, so we cannot claim that driver model can cover all the common cases unless we have converted a bus over to driver model.
SPI is a reasonable choice for this next step. It has a fairly simple API and not too many dependencies. The main one is SPI flash so we may as well convert that also. Since the boards I test with have cros_ec I have also included that, for SPI only.
The technique used is make use of driver model's supported data structures to hold information currently kept by each subsystem in a private data structure. Since 'struct spi_slave' relates to the slave device on the bus it is stored in the 'parent' data with each child device of the bus. Since 'struct spi_flash' is a standard interface used for each SPI flash driver, it is stored in the SPI FLash uclass's private data for each device.
New defines are created to enable driver model for each subsystem. These are:
CONFIG_DM_SPI
CONFIG_DM_SPI_FLASH
CONFIG_DM_CROS_EC
This allows us to move some boards and drivers to driver model, while leaving others behind. A 'big bang' conversion of everything to driver model, event at a subsystem level, is never going to work.
On the other hand, we change the driver at the same time as the CONFIG option is enabled. Keeping both version of the driver around involves a flock of #ifdefs, the benefit of which is not apparent to me, since the old code is removed anyway.
There is some cost in changing the uclass interface after it is created, so if you have limited time, please spend it reviewing the uclass interfaces in spi.h and spi_flash.h. These need to be supported by each driver, so changing them later may involve changing multiple drivers.
To help with conversion of SPI drivers to driver model, documentation is provided which takes the happy camper through the process with an example.
As always, driver model patches are available at u-boot-dm.git branch 'working'.
Note: This series is not fully ready - e.g. some header files are missing comments. But I wanted to get it out for review early since some SPI work is ongoing which might depend on it.
Simon Glass (25):
sandbox: Convert SPI flash emulation to use sf_params
sandbox: config: Enable all SPI flash chips
sandbox: dts: Add a SPI device and cros_ec device
dm: spi: Move cmd device code into its own function
spi: Add brackets and tidy defines in spi.h
dm: spi: Add a uclass for SPI
dm: sandbox: Add a SPI emulation uclass
dm: Remove spi_init() from board_r.c when using driver model
dm: Add spi.h header to a few files
dm: spi: Adjust cmd_spi to work with driver model
dm: sandbox: spi: Move to driver model
dm: spi: Add documentation on how to convert over SPI drivers
dm: exynos: Convert SPI to driver model
sf: Add an empty entry to the parameter list
sf: Tidy up public and private header files
spi: Use error return value in sf_ops
dm: sf: Add a uclass for SPI flash
dm: Convert spi_flash_probe() and 'sf probe' to use driver model
dm: sf: sandbox: Convert SPI flash driver to driver model
dm: exynos: config: Use driver model for SPI flash
dm: spi: Add tests
dm: sf: Add tests for SPI flash
dm: cros_ec: Add support for driver model
dm: sandbox: cros_ec: Move sandbox cros_ec to driver module
dm: exynos: cros_ec: Move cros_ec_spi to driver model
arch/arm/dts/exynos5250-snow.dts | 8 + arch/arm/dts/exynos5420-peach-pit.dts | 1 + arch/sandbox/dts/sandbox.dts | 25 ++ arch/sandbox/include/asm/spi.h | 13 - arch/sandbox/include/asm/state.h | 2 +- board/buffalo/lsxl/lsxl.c | 3 +- board/samsung/common/board.c | 3 - common/board_r.c | 2 +- common/cmd_sf.c | 24 ++ common/cmd_spi.c | 73 +++- common/cros_ec.c | 30 ++ common/env_sf.c | 1 + common/exports.c | 4 +- doc/device-tree-bindings/mtd/spi/m25p80.txt | 29 ++ doc/driver-model/spi-howto.txt | 570 ++++++++++++++++++++++++++++ drivers/misc/cros_ec.c | 122 +++++- drivers/misc/cros_ec_sandbox.c | 99 ++++- drivers/misc/cros_ec_spi.c | 68 +++- drivers/mtd/spi/Makefile | 7 +- drivers/mtd/spi/ramtron.c | 1 + drivers/mtd/spi/sandbox.c | 438 +++++++++++++++------ drivers/mtd/spi/sf-uclass.c | 63 +++ drivers/mtd/spi/sf_internal.h | 67 +++- drivers/mtd/spi/sf_params.c | 2 + drivers/mtd/spi/sf_probe.c | 153 ++++++-- drivers/mtd/spi/spi_spl_load.c | 1 + drivers/spi/Makefile | 5 + drivers/spi/exynos_spi.c | 509 +++++++++---------------- drivers/spi/sandbox_spi.c | 198 ++++------ drivers/spi/spi-emul-uclass.c | 15 + drivers/spi/spi-uclass.c | 253 ++++++++++++ include/configs/exynos5-dt.h | 2 + include/configs/sandbox.h | 13 +- include/configs/smdk5420.h | 1 + include/cros_ec.h | 27 +- include/dm/uclass-id.h | 4 + include/spi.h | 194 +++++++++- include/spi_flash.h | 127 ++++--- test/dm/Makefile | 2 + test/dm/sf.c | 43 +++ test/dm/spi.c | 47 +++ test/dm/test.dts | 17 +- 42 files changed, 2504 insertions(+), 762 deletions(-) create mode 100644 doc/device-tree-bindings/mtd/spi/m25p80.txt create mode 100644 doc/driver-model/spi-howto.txt create mode 100644 drivers/mtd/spi/sf-uclass.c create mode 100644 drivers/spi/spi-emul-uclass.c create mode 100644 drivers/spi/spi-uclass.c create mode 100644 test/dm/sf.c create mode 100644 test/dm/spi.c
Signature · 1 line
2.0.0.526.g5318336
PATCH 1/25 sandbox: Convert SPI flash emulation to use sf_params
At present sandbox has its own table of supported SPI flash chips. Now that the SPI flash system is fully consolidated and has its own list, sandbox should use that.
This enables us to expand the number of chips that sandbox supports.
---
drivers/mtd/spi/sandbox.c | 114 ++++++++++++++++------------------------------ 1 file changed, 38 insertions(+), 76 deletions(-)
drivers/mtd/spi/sandbox.c +38 -76
@@ -51,46 +51,7 @@ static const char *sandbox_sf_state_name(enum sandbox_sf_state state)/* Assume all SPI flashes have 3 byte addresses since they do atm */ #define SF_ADDR_LEN 3 -struct sandbox_spi_flash_erase_commands { -u8 cmd; -u32 size; -}; -#define IDCODE_LEN 5 -#define MAX_ERASE_CMDS 3 -struct sandbox_spi_flash_data { -const char *name; -u8 idcode[IDCODE_LEN ]; -u32 size; -const struct sandbox_spi_flash_erase_commands - erase_cmds[MAX_ERASE_CMDS ]; -}; - -/* Structure describing all the flashes we know how to emulate */ -static const struct sandbox_spi_flash_data sandbox_sf_flashes[] = { - { -"M25P16" , {0x20 ,0x20 ,0x15 }, (2 <<20 ), - {/* erase commands */ - {0xd8 , (64 <<10 ), },/* sector */ - {0xc7 , (2 <<20 ), },/* bulk */ - }, - }, - { -"W25Q32" , {0xef ,0x40 ,0x16 }, (4 <<20 ), - {/* erase commands */ - {0x20 , (4 <<10 ), },/* 4KB */ - {0xd8 , (64 <<10 ), },/* sector */ - {0xc7 , (4 <<20 ), },/* bulk */ - }, - }, - { -"W25Q128" , {0xef ,0x40 ,0x18 }, (16 <<20 ), - {/* erase commands */ - {0x20 , (4 <<10 ), },/* 4KB */ - {0xd8 , (64 <<10 ), },/* sector */ - {0xc7 , (16 <<20 ), },/* bulk */ - }, - }, -}; +#define IDCODE_LEN 3/* Used to quickly bulk erase backing store */ static u8 sandbox_sf_0xff[0x1000 ];
@@ -109,7 +70,8 @@ struct sandbox_spi_flash {*/ enum sandbox_sf_state state;uint cmd; -const void *cmd_data; +/* Erase size of current erase command */ +uint erase_size;/* Current position in the flash; used when reading/writing/etc... */ uint off;/* How many address bytes we've consumed */
@@ -117,7 +79,7 @@ struct sandbox_spi_flash {/* The current flash status (see STAT_XXX defines above) */ u16 status;/* Data describing the flash we're emulating */ -const struct sandbox_spi_flash_data *data; +const struct spi_flash_params *data;/* The file on disk to serv up data from */ int fd; };
@@ -127,8 +89,8 @@ static int sandbox_sf_setup(void **priv, const char *spec)/* spec = idcode:file */ struct sandbox_spi_flash *sbsf;const char *file; -size_t i, len, idname_len; -const struct sandbox_spi_flash_data *data; +size_t len, idname_len; +const struct spi_flash_params *data; file =strchr (spec,':' );if (!file) {
@@ -138,15 +100,14 @@ static int sandbox_sf_setup(void **priv, const char *spec) idname_len = file - spec; ++file; -for (i =0 ; i <ARRAY_SIZE (sandbox_sf_flashes); ++i) { - data = &sandbox_sf_flashes[i]; +for (data = spi_flash_params_table; data->name; data++) { len =strlen (data->name);if (idname_len != len)continue ;if (!memcmp (spec, data->name, len))break ; } -if (i ==ARRAY_SIZE (sandbox_sf_flashes)) { +if (!data->name) {printf ("sandbox_sf: unknown flash '%*s'\n" , (int )idname_len, spec); goto error;
@@ -223,7 +184,6 @@ static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx, sbsf->pad_addr_bytes =1 ;case CMD_READ_ARRAY_SLOW :case CMD_PAGE_PROGRAM : - state_addr: sbsf->state =SF_ADDR ;break ;case CMD_WRITE_DISABLE :
@@ -241,24 +201,25 @@ static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx, sbsf->status |=STAT_WEL ;break ;default : { -size_t i; - -/* handle erase commands first */ -for (i =0 ; i <MAX_ERASE_CMDS ; ++i) { -const struct sandbox_spi_flash_erase_commands * - erase_cmd = &sbsf->data->erase_cmds[i]; - -if (erase_cmd->cmd ==0x00 ) -continue ; -if (sbsf->cmd != erase_cmd->cmd) -continue ; - - sbsf->cmd_data = erase_cmd; - goto state_addr; +int flags = sbsf->data->flags; + +/* we only support erase here */ +if (sbsf->cmd ==CMD_ERASE_CHIP ) { + sbsf->erase_size = sbsf->data->sector_size * + sbsf->data->nr_sectors; + }else if (sbsf->cmd ==CMD_ERASE_4K && (flags &SECT_4K )) { + sbsf->erase_size =4 <<10 ; + }else if (sbsf->cmd ==CMD_ERASE_32K && (flags &SECT_32K )) { + sbsf->erase_size =32 <<10 ; + }else if (sbsf->cmd ==CMD_ERASE_64K && + !(flags & (SECT_4K |SECT_32K ))) { + sbsf->erase_size =64 <<10 ; + }else { +debug (" cmd unknown: %#x\n" , sbsf->cmd); +return 1 ; } - -debug (" cmd unknown: %#x\n" , sbsf->cmd); -return 1 ; + sbsf->state =SF_ADDR ; +break ; } }
@@ -309,11 +270,14 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx,u8 id;debug (" id: off:%u tx:" , sbsf->off); -if (sbsf->off <IDCODE_LEN ) - id = sbsf->data->idcode[sbsf->off]; -else +if (sbsf->off <IDCODE_LEN ) { +/* Extract correct byte from ID 0x00aabbcc */ + id = sbsf->data->jedec >> + (8 * (IDCODE_LEN -1 - sbsf->off)); + }else { id =0 ; -debug ("%02x\n" , id); + } +debug ("%d %02x\n" , sbsf->off, id); tx[pos++] = id; ++sbsf->off;break ;
@@ -406,24 +370,22 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx,break ;case SF_ERASE : case_sf_erase: { -const struct sandbox_spi_flash_erase_commands * - erase_cmd = sbsf->cmd_data; -if (!(sbsf->status &STAT_WEL )) {puts ("sandbox_sf: write enable not set before erase\n" ); goto done; }/* verify address is aligned */ -if (sbsf->off & (erase_cmd->size -1 )) { +if (sbsf->off & (sbsf->erase_size -1 )) {debug (" sector erase: cmd:%#x needs align:%#x, but we got %#x\n" , - erase_cmd->cmd, erase_cmd->size, + sbsf->cmd, sbsf->erase_size, sbsf->off); sbsf->status &= ~STAT_WEL ; goto done; } -debug (" sector erase addr: %u\n" , sbsf->off); +debug (" sector erase addr: %u, size: %u\n" , sbsf->off, + sbsf->erase_size); cnt = bytes - pos;sandbox_spi_tristate (&tx[pos], cnt);
@@ -433,7 +395,7 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx,* TODO(vapier at gentoo.org): latch WIP in status, and * delay before clearing it ? */ - ret =sandbox_erase_part (sbsf, erase_cmd->size); + ret =sandbox_erase_part (sbsf, sbsf->erase_size); sbsf->status &= ~STAT_WEL ;if (ret) {debug ("sandbox_sf: Erase failed\n" );
Signature · 1 line
2.0.0.526.g5318336
PATCH 1/25 sandbox: Convert SPI flash emulation to use sf_params
Quoted 11 lines · Simon Glass
At present sandbox has its own table of supported SPI flash chips. Now that the SPI flash system is fully consolidated and has its own list, sandbox should use that.
This enables us to expand the number of chips that sandbox supports.
Signed-off-by Simon Glass <[email protected]>---
drivers/mtd/spi/sandbox.c | 114 ++++++++++++++++------------------------------ 1 file changed, 38 insertions(+), 76 deletions(-)
Quoted 210 lines
drivers/mtd/spi/sandbox.c +38
-77@@ -51,46 +51,7 @@ static const char *sandbox_sf_state_name(enum sandbox_sf_state state)/* Assume all SPI flashes have 3 byte addresses since they do atm */ #define SF_ADDR_LEN 3 -struct sandbox_spi_flash_erase_commands { -u8 cmd; -u32 size; -}; -#define IDCODE_LEN 5 -#define MAX_ERASE_CMDS 3 -struct sandbox_spi_flash_data { -const char *name; -u8 idcode[IDCODE_LEN ]; -u32 size; -const struct sandbox_spi_flash_erase_commands - erase_cmds[MAX_ERASE_CMDS ]; -}; - -/* Structure describing all the flashes we know how to emulate */ -static const struct sandbox_spi_flash_data sandbox_sf_flashes[] = { - { -"M25P16" , {0x20 ,0x20 ,0x15 }, (2 <<20 ), - {/* erase commands */ - {0xd8 , (64 <<10 ), },/* sector */ - {0xc7 , (2 <<20 ), },/* bulk */ - }, - }, - { -"W25Q32" , {0xef ,0x40 ,0x16 }, (4 <<20 ), - {/* erase commands */ - {0x20 , (4 <<10 ), },/* 4KB */ - {0xd8 , (64 <<10 ), },/* sector */ - {0xc7 , (4 <<20 ), },/* bulk */ - }, - }, - { -"W25Q128" , {0xef ,0x40 ,0x18 }, (16 <<20 ), - {/* erase commands */ - {0x20 , (4 <<10 ), },/* 4KB */ - {0xd8 , (64 <<10 ), },/* sector */ - {0xc7 , (16 <<20 ), },/* bulk */ - }, - }, -}; +#define IDCODE_LEN 3/* Used to quickly bulk erase backing store */ static u8 sandbox_sf_0xff[0x1000 ];@@ -109,7 +70,8 @@ struct sandbox_spi_flash {*/ enum sandbox_sf_state state;uint cmd; -const void *cmd_data; +/* Erase size of current erase command */ +uint erase_size;/* Current position in the flash; used when reading/writing/etc... */ uint off;/* How many address bytes we've consumed */ @@ -117,7 +79,7 @@ struct sandbox_spi_flash {/* The current flash status (see STAT_XXX defines above) */ u16 status;/* Data describing the flash we're emulating */ -const struct sandbox_spi_flash_data *data; +const struct spi_flash_params *data;/* The file on disk to serv up data from */ int fd; };@@ -127,8 +89,8 @@ static int sandbox_sf_setup(void **priv, const char *spec)/* spec = idcode:file */ struct sandbox_spi_flash *sbsf;const char *file; -size_t i, len, idname_len; -const struct sandbox_spi_flash_data *data; +size_t len, idname_len; +const struct spi_flash_params *data; file =strchr (spec,':' );if (!file) {@@ -138,15 +100,14 @@ static int sandbox_sf_setup(void **priv, const char *spec) idname_len = file - spec; ++file; -for (i =0 ; i <ARRAY_SIZE (sandbox_sf_flashes); ++i) { - data = &sandbox_sf_flashes[i]; +for (data = spi_flash_params_table; data->name; data++) { len =strlen (data->name);if (idname_len != len)continue ;if (!memcmp (spec, data->name, len))break ; } -if (i ==ARRAY_SIZE (sandbox_sf_flashes)) { +if (!data->name) {printf ("sandbox_sf: unknown flash '%*s'\n" , (int )idname_len, spec); goto error;@@ -223,7 +184,6 @@ static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx, sbsf->pad_addr_bytes =1 ;case CMD_READ_ARRAY_SLOW :case CMD_PAGE_PROGRAM : - state_addr: sbsf->state =SF_ADDR ;break ;case CMD_WRITE_DISABLE :@@ -241,24 +201,25 @@ static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx, sbsf->status |=STAT_WEL ;break ;default : { -size_t i; - -/* handle erase commands first */ -for (i =0 ; i <MAX_ERASE_CMDS ; ++i) { -const struct sandbox_spi_flash_erase_commands * - erase_cmd = &sbsf->data->erase_cmds[i]; - -if (erase_cmd->cmd ==0x00 ) -continue ; -if (sbsf->cmd != erase_cmd->cmd) -continue ; - - sbsf->cmd_data = erase_cmd; - goto state_addr; +int flags = sbsf->data->flags; + +/* we only support erase here */ +if (sbsf->cmd ==CMD_ERASE_CHIP ) { + sbsf->erase_size = sbsf->data->sector_size * + sbsf->data->nr_sectors; + }else if (sbsf->cmd ==CMD_ERASE_4K && (flags &SECT_4K )) { + sbsf->erase_size =4 <<10 ; + }else if (sbsf->cmd ==CMD_ERASE_32K && (flags &SECT_32K )) { + sbsf->erase_size =32 <<10 ; + }else if (sbsf->cmd ==CMD_ERASE_64K && + !(flags & (SECT_4K |SECT_32K ))) { + sbsf->erase_size =64 <<10 ; + }else { +debug (" cmd unknown: %#x\n" , sbsf->cmd); +return 1 ; } - -debug (" cmd unknown: %#x\n" , sbsf->cmd); -return 1 ; + sbsf->state =SF_ADDR ; +break ; } }@@ -309,11 +270,14 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx,u8 id;debug (" id: off:%u tx:" , sbsf->off); -if (sbsf->off <IDCODE_LEN ) - id = sbsf->data->idcode[sbsf->off]; -else +if (sbsf->off <IDCODE_LEN ) { +/* Extract correct byte from ID 0x00aabbcc */ + id = sbsf->data->jedec >> + (8 * (IDCODE_LEN -1 - sbsf->off)); + }else { id =0 ; -debug ("%02x\n" , id); + } +debug ("%d %02x\n" , sbsf->off, id); tx[pos++] = id; ++sbsf->off;break ;@@ -406,24 +370,22 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx,break ;case SF_ERASE : case_sf_erase: { -const struct sandbox_spi_flash_erase_commands * - erase_cmd = sbsf->cmd_data; -if (!(sbsf->status &STAT_WEL )) {puts ("sandbox_sf: write enable not set before erase\n" ); goto done; }/* verify address is aligned */ -if (sbsf->off & (erase_cmd->size -1 )) { +if (sbsf->off & (sbsf->erase_size -1 )) {debug (" sector erase: cmd:%#x needs align:%#x, but we got %#x\n" , - erase_cmd->cmd, erase_cmd->size, + sbsf->cmd, sbsf->erase_size, sbsf->off); sbsf->status &= ~STAT_WEL ; goto done; } -debug (" sector erase addr: %u\n" , sbsf->off); +debug (" sector erase addr: %u, size: %u\n" , sbsf->off, + sbsf->erase_size); cnt = bytes - pos;sandbox_spi_tristate (&tx[pos], cnt);@@ -433,7 +395,7 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx,* TODO(vapier at gentoo.org): latch WIP in status, and * delay before clearing it ? */ - ret =sandbox_erase_part (sbsf, erase_cmd->size); + ret =sandbox_erase_part (sbsf, sbsf->erase_size); sbsf->status &= ~STAT_WEL ;if (ret) {debug ("sandbox_sf: Erase failed\n" ); --
Quoted 2 lines
2.0.0.526.g5318336
thanks!
Signature · 1 line
Jagan.
PATCH 2/25 sandbox: config: Enable all SPI flash chips
In reply to Simon GlassSandbox may as well support everything. This increases the amount of code that is built/tested by sandbox, and also provides access to all the supported SPI flash devices.
---
include/configs/sandbox.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
include/configs/sandbox.h +7 -1
@@ -94,7 +94,7 @@#define CONFIG_ENV_SIZE 8192#define CONFIG_ENV_IS_NOWHERE -/* SPI */ +/* SPI - enable all SPI flash types for testing purposes */ #define CONFIG_SANDBOX_SPI #define CONFIG_CMD_SF #define CONFIG_CMD_SF_TEST
@@ -102,7 +102,13 @@#define CONFIG_SPI_FLASH #define CONFIG_OF_SPI #define CONFIG_OF_SPI_FLASH +#define CONFIG_SPI_FLASH_ATMEL +#define CONFIG_SPI_FLASH_EON +#define CONFIG_SPI_FLASH_GIGADEVICE +#define CONFIG_SPI_FLASH_MACRONIX #define CONFIG_SPI_FLASH_SANDBOX +#define CONFIG_SPI_FLASH_SPANSION +#define CONFIG_SPI_FLASH_SST #define CONFIG_SPI_FLASH_STMICRO #define CONFIG_SPI_FLASH_WINBOND
Signature · 1 line
2.0.0.526.g5318336
PATCH 2/25 sandbox: config: Enable all SPI flash chips
Quoted 9 lines · Simon Glass
Sandbox may as well support everything. This increases the amount of code that is built/tested by sandbox, and also provides access to all the supported SPI flash devices.
Signed-off-by Simon Glass <[email protected]>---
include/configs/sandbox.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
Quoted 29 lines
include/configs/sandbox.h +7
-2@@ -94,7 +94,7 @@#define CONFIG_ENV_SIZE 8192#define CONFIG_ENV_IS_NOWHERE -/* SPI */ +/* SPI - enable all SPI flash types for testing purposes */ #define CONFIG_SANDBOX_SPI #define CONFIG_CMD_SF #define CONFIG_CMD_SF_TEST @@ -102,7 +102,13 @@#define CONFIG_SPI_FLASH #define CONFIG_OF_SPI #define CONFIG_OF_SPI_FLASH +#define CONFIG_SPI_FLASH_ATMEL +#define CONFIG_SPI_FLASH_EON +#define CONFIG_SPI_FLASH_GIGADEVICE +#define CONFIG_SPI_FLASH_MACRONIX #define CONFIG_SPI_FLASH_SANDBOX +#define CONFIG_SPI_FLASH_SPANSION +#define CONFIG_SPI_FLASH_SST #define CONFIG_SPI_FLASH_STMICRO #define CONFIG_SPI_FLASH_WINBOND --
Quoted 2 lines
2.0.0.526.g5318336
thanks!
Signature · 1 line
Jagan.
PATCH 3/25 sandbox: dts: Add a SPI device and cros_ec device
In reply to Simon GlassAdd a SPI device which can be used for testing SPI flash features in sandbox.
Also add a cros_ec device since with driver model the Chrome OS EC emulation will not otherwise be available.
---
arch/sandbox/dts/sandbox.dts | 25 +++++++++++++++++++++++++ doc/device-tree-bindings/mtd/spi/m25p80.txt | 29 +++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 doc/device-tree-bindings/mtd/spi/m25p80.txt
arch/sandbox/dts/sandbox.dts +25 -0
@@ -1,6 +1,9 @@ /dts-v1/; / { + #address-cells = <1>; + #size-cells = <0>; + aliases { console = "/serial"; };
@@ -131,5 +134,27 @@ num-gpios = <20>; }; + spi at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + compatible = "sandbox,spi"; + cs-gpios = <0>, <&gpio_a 0>; + flash at 0 { + reg = <0>; + compatible = "spansion,m25p16", "sandbox,spi-flash"; + spi-max-frequency = <40000000>; + sandbox,filename = "spi.bin"; + }; + }; + + cros-ec at 0 { + compatible = "google,cros-ec"; + #address-cells = <1>; + #size-cells = <1>; + firmware_storage_spi: flash at 0 { + reg = <0 0x400000>; + }; + }; };
doc/device-tree-bindings/mtd/spi/m25p80.txt new file +29 -0
@@ -0,0 +1,29 @@ +* MTD SPI driver for ST M25Pxx (and similar) serial flash chips + +Required properties: +- #address-cells, #size-cells : Must be present if the device has sub-nodes + representing partitions. +- compatible : Should be the manufacturer and the name of the chip. Bear in mind + the DT binding is not Linux-only, but in case of Linux, see the + "m25p_ids" table in drivers/mtd/devices/m25p80.c for the list of + supported chips. +- reg : Chip-Select number +- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at + +Optional properties: +- m25p,fast-read : Use the "fast read" opcode to read data from the chip instead + of the usual "read" opcode. This opcode is not supported by + all chips and support for it can not be detected at runtime. + Refer to your chips' datasheet to check if this is supported + by your chip. + +Example: + + flash: m25p80 at 0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,m25p80"; + reg = <0>; + spi-max-frequency = <40000000>; + m25p,fast-read; + };
Signature · 1 line
2.0.0.526.g5318336
PATCH 3/25 sandbox: dts: Add a SPI device and cros_ec device
Hi Simon,
Quoted 92 lines · Simon Glass
Add a SPI device which can be used for testing SPI flash features in sandbox.
Also add a
cros_ecdevice since with driver model the Chrome OS EC emulation will not otherwise be available.Signed-off-by Simon Glass <[email protected]>---
arch/sandbox/dts/sandbox.dts | 25 +++++++++++++++++++++++++ doc/device-tree-bindings/mtd/spi/m25p80.txt | 29 +++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 doc/device-tree-bindings/mtd/spi/m25p80.txtarch/sandbox/dts/sandbox.dts +25
-0@@ -1,6 +1,9 @@ /dts-v1/; / { + #address-cells = <1>; + #size-cells = <0>; + aliases { console = "/serial"; };@@ -131,5 +134,27 @@ num-gpios = <20>; }; + spi at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + compatible = "sandbox,spi"; + cs-gpios = <0>, <&gpio_a 0>; + flash at 0 { + reg = <0>; + compatible = "spansion,m25p16", "sandbox,spi-flash"; + spi-max-frequency = <40000000>; + sandbox,filename = "spi.bin"; + }; + }; + + cros-ec at 0 { + compatible = "google,cros-ec"; + #address-cells = <1>; + #size-cells = <1>; + firmware_storage_spi: flash at 0 { + reg = <0 0x400000>; + }; + }; };doc/device-tree-bindings/mtd/spi/m25p80.txt new file +29
-1@@ -0,0 +1,29 @@ +* MTD SPI driver for ST M25Pxx (and similar) serial flash chips + +Required properties: +- #address-cells, #size-cells : Must be present if the device has sub-nodes + representing partitions. +- compatible : Should be the manufacturer and the name of the chip. Bear in mind + the DT binding is not Linux-only, but in case of Linux, see the + "m25p_ids" table in drivers/mtd/devices/m25p80.c for the list of + supported chips. +- reg : Chip-Select number +- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at + +Optional properties: +- m25p,fast-read : Use the "fast read" opcode to read data from the chip instead + of the usual "read" opcode. This opcode is not supported by + all chips and support for it can not be detected at runtime. + Refer to your chips' datasheet to check if this is supported + by your chip. + +Example: + + flash: m25p80 at 0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,m25p80"; + reg = <0>; + spi-max-frequency = <40000000>; + m25p,fast-read; + }; --
Quoted 2 lines
2.0.0.526.g5318336
Please add doc-binding stuff more specific to your usage and w/o having any Linux stuff/terminologies as of now.
thanks!
Signature · 1 line
Jagan.
PATCH 4/25 dm: spi: Move cmd device code into its own function
In reply to Simon GlassIn preparation for changing the error handling in this code for driver model, move it into its own function.
---
common/cmd_spi.c | 53 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 21 deletions(-)
common/cmd_spi.c +32 -21
@@ -11,6 +11,7 @@#include <common.h> #include <command.h> +#include <errno.h> #include <spi.h> /*-----------------------------------------------------------------------
@@ -38,6 +39,35 @@ static int bitlen;static uchar dout[MAX_SPI_BYTES ];static uchar din[MAX_SPI_BYTES ]; +static int do_spi_xfer (int bus,int cs) +{ +struct spi_slave *slave; +int rcode =0 ; + + slave =spi_setup_slave (bus, cs,1000000 , mode); +if (!slave) { +printf ("Invalid device %d:%d\n" , bus, cs); +return -EINVAL ; + } + +spi_claim_bus (slave); +if (spi_xfer (slave, bitlen, dout, din, +SPI_XFER_BEGIN |SPI_XFER_END ) !=0 ) { +printf ("Error during SPI transaction\n" ); + rcode = -EIO ; + }else { +int j; + +for (j =0 ; j < ((bitlen +7 ) /8 ); j++) +printf ("%02X" , din[j]); +printf ("\n" ); + } +spi_release_bus (slave); +spi_free_slave (slave); + +return rcode; +} +/* * SPI read/write *
@@ -51,11 +81,9 @@ static uchar din[MAX_SPI_BYTES];int do_spi (cmd_tbl_t *cmdtp,int flag,int argc,char *const argv[]) { -struct spi_slave *slave;char *cp =0 ;uchar tmp;int j; -int rcode =0 ;/* * We use the last specified parameters, unless new ones are
@@ -103,27 +131,10 @@ int do_spi (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])return 1 ; } - slave =spi_setup_slave (bus, cs,1000000 , mode); -if (!slave) { -printf ("Invalid device %d:%d\n" , bus, cs); +if (do_spi_xfer (bus, cs))return 1 ; - } - -spi_claim_bus (slave); -if (spi_xfer (slave, bitlen, dout, din, -SPI_XFER_BEGIN |SPI_XFER_END ) !=0 ) { -printf ("Error during SPI transaction\n" ); - rcode =1 ; - }else { -for (j =0 ; j < ((bitlen +7 ) /8 ); j++) { -printf ("%02X" , din[j]); - } -printf ("\n" ); - } -spi_release_bus (slave); -spi_free_slave (slave); -return rcode; +return 0 ; }/***************************************************/
Signature · 1 line
2.0.0.526.g5318336
PATCH 4/25 dm: spi: Move cmd device code into its own function
Quoted 9 lines · Simon Glass
In preparation for changing the error handling in this code for driver model, move it into its own function.
Signed-off-by Simon Glass <[email protected]>---
common/cmd_spi.c | 53 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 21 deletions(-)
Quoted 91 lines
common/cmd_spi.c +32
-22@@ -11,6 +11,7 @@#include <common.h> #include <command.h> +#include <errno.h> #include <spi.h> /*----------------------------------------------------------------------- @@ -38,6 +39,35 @@ static int bitlen;static uchar dout[MAX_SPI_BYTES ];static uchar din[MAX_SPI_BYTES ]; +static int do_spi_xfer (int bus,int cs) +{ +struct spi_slave *slave; +int rcode =0 ; + + slave =spi_setup_slave (bus, cs,1000000 , mode); +if (!slave) { +printf ("Invalid device %d:%d\n" , bus, cs); +return -EINVAL ; + } + +spi_claim_bus (slave); +if (spi_xfer (slave, bitlen, dout, din, +SPI_XFER_BEGIN |SPI_XFER_END ) !=0 ) { +printf ("Error during SPI transaction\n" ); + rcode = -EIO ; + }else { +int j; + +for (j =0 ; j < ((bitlen +7 ) /8 ); j++) +printf ("%02X" , din[j]); +printf ("\n" ); + } +spi_release_bus (slave); +spi_free_slave (slave); + +return rcode; +} +/* * SPI read/write * @@ -51,11 +81,9 @@ static uchar din[MAX_SPI_BYTES];int do_spi (cmd_tbl_t *cmdtp,int flag,int argc,char *const argv[]) { -struct spi_slave *slave;char *cp =0 ;uchar tmp;int j; -int rcode =0 ;/* * We use the last specified parameters, unless new ones are @@ -103,27 +131,10 @@ int do_spi (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])return 1 ; } - slave =spi_setup_slave (bus, cs,1000000 , mode); -if (!slave) { -printf ("Invalid device %d:%d\n" , bus, cs); +if (do_spi_xfer (bus, cs))return 1 ; - } - -spi_claim_bus (slave); -if (spi_xfer (slave, bitlen, dout, din, -SPI_XFER_BEGIN |SPI_XFER_END ) !=0 ) { -printf ("Error during SPI transaction\n" ); - rcode =1 ; - }else { -for (j =0 ; j < ((bitlen +7 ) /8 ); j++) { -printf ("%02X" , din[j]); - } -printf ("\n" ); - } -spi_release_bus (slave); -spi_free_slave (slave); -return rcode; +return 0 ; }/***************************************************/ --
Quoted 2 lines
2.0.0.526.g5318336
thanks!
Signature · 1 line
Jagan.
PATCH 4/25 dm: spi: Move cmd device code into its own function
Hi Jagan,
Quoted 4 lines · Jagan Teki
Quoted 3 lines · Simon Glass
In preparation for changing the error handling in this code for driver model, move it into its own function.
Quoted 8 lines
Quoted 6 lines
Signed-off-by Simon Glass <[email protected]>---
common/cmd_spi.c | 53 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 21 deletions(-)Reviewed-by Jagannadha Sutradharudu Teki <[email protected]>
Thanks. I'd quite like to pull these reviewed patches through the -dm tree as it is easier for me to keep things consistent. Is that OK with you?
Regards,
Simon
PATCH 5/25 spi: Add brackets and tidy defines in spi.h
In reply to Simon GlassSome of the #defines in spi.h are not bracketed. To avoid future mistakes add brackets. Also add an explanatory comment for SPI_CONN_DUAL_...
---
include/spi.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)
include/spi.h +12 -12
@@ -30,24 +30,24 @@#define SPI_XFER_MMAP 0x08/* Memory Mapped start */ #define SPI_XFER_MMAP_END 0x10/* Memory Mapped End */ #define SPI_XFER_ONCE (SPI_XFER_BEGIN | SPI_XFER_END) -#define SPI_XFER_U_PAGE (1 << 5) +#define SPI_XFER_U_PAGE (1 << 5)/* SPI TX operation modes */ -#define SPI_OPM_TX_QPP 1 << 0 +#define SPI_OPM_TX_QPP (1 << 0)/* SPI RX operation modes */ -#define SPI_OPM_RX_AS 1 << 0 -#define SPI_OPM_RX_DOUT 1 << 1 -#define SPI_OPM_RX_DIO 1 << 2 -#define SPI_OPM_RX_QOF 1 << 3 -#define SPI_OPM_RX_QIOF 1 << 4 -#define SPI_OPM_RX_EXTN SPI_OPM_RX_AS | SPI_OPM_RX_DOUT | \ +#define SPI_OPM_RX_AS (1 << 0) +#define SPI_OPM_RX_DOUT (1 << 1) +#define SPI_OPM_RX_DIO (1 << 2) +#define SPI_OPM_RX_QOF (1 << 3) +#define SPI_OPM_RX_QIOF (1 << 4) +#define SPI_OPM_RX_EXTN (SPI_OPM_RX_AS | SPI_OPM_RX_DOUT | \ SPI_OPM_RX_DIO | SPI_OPM_RX_QOF | \ - SPI_OPM_RX_QIOF + SPI_OPM_RX_QIOF) -/* SPI bus connection options */ -#define SPI_CONN_DUAL_SHARED 1 << 0 -#define SPI_CONN_DUAL_SEPARATED 1 << 1 +/* SPI bus connection options - see enum spi_dual_flash */ +#define SPI_CONN_DUAL_SHARED (1 << 0) +#define SPI_CONN_DUAL_SEPARATED (1 << 1)/* Header byte that marks the start of the message */ #define SPI_PREAMBLE_END_BYTE 0xec
Signature · 1 line
2.0.0.526.g5318336
PATCH 5/25 spi: Add brackets and tidy defines in spi.h
Thanks for the patch.
Quoted 51 lines · Simon Glass
Some of the #defines in
spi.hare not bracketed. To avoid future mistakes add brackets. Also add an explanatory comment forSPI_CONN_DUAL_...Signed-off-by Simon Glass <[email protected]>---
include/spi.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)include/spi.h +12
-13@@ -30,24 +30,24 @@#define SPI_XFER_MMAP 0x08/* Memory Mapped start */ #define SPI_XFER_MMAP_END 0x10/* Memory Mapped End */ #define SPI_XFER_ONCE (SPI_XFER_BEGIN | SPI_XFER_END) -#define SPI_XFER_U_PAGE (1 << 5) +#define SPI_XFER_U_PAGE (1 << 5)/* SPI TX operation modes */ -#define SPI_OPM_TX_QPP 1 << 0 +#define SPI_OPM_TX_QPP (1 << 0)/* SPI RX operation modes */ -#define SPI_OPM_RX_AS 1 << 0 -#define SPI_OPM_RX_DOUT 1 << 1 -#define SPI_OPM_RX_DIO 1 << 2 -#define SPI_OPM_RX_QOF 1 << 3 -#define SPI_OPM_RX_QIOF 1 << 4 -#define SPI_OPM_RX_EXTN SPI_OPM_RX_AS | SPI_OPM_RX_DOUT | \ +#define SPI_OPM_RX_AS (1 << 0) +#define SPI_OPM_RX_DOUT (1 << 1) +#define SPI_OPM_RX_DIO (1 << 2) +#define SPI_OPM_RX_QOF (1 << 3) +#define SPI_OPM_RX_QIOF (1 << 4) +#define SPI_OPM_RX_EXTN (SPI_OPM_RX_AS | SPI_OPM_RX_DOUT | \ SPI_OPM_RX_DIO | SPI_OPM_RX_QOF | \ - SPI_OPM_RX_QIOF + SPI_OPM_RX_QIOF) -/* SPI bus connection options */ -#define SPI_CONN_DUAL_SHARED 1 << 0 -#define SPI_CONN_DUAL_SEPARATED 1 << 1 +/* SPI bus connection options - see enum spi_dual_flash */ +#define SPI_CONN_DUAL_SHARED (1 << 0) +#define SPI_CONN_DUAL_SEPARATED (1 << 1)/* Header byte that marks the start of the message */ #define SPI_PREAMBLE_END_BYTE 0xec --
Quoted 2 lines
2.0.0.526.g5318336
thanks!
Signature · 1 line
Jagan.
PATCH 6/25 dm: spi: Add a uclass for SPI
In reply to Simon GlassAdd a uclass which provides access to SPI buses and includes operations required by SPI.
For a time driver model will need to co-exist with the legacy SPI interface so some parts of the header file are changed depending on which is in use. The exports are adjusted also since some functions are not available with driver model.
Boards must define CONFIG_DM_SPI to use driver model for SPI.
---
common/exports.c | 4 +- drivers/spi/Makefile | 4 + drivers/spi/spi-uclass.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/spi.h | 140 ++++++++++++++++++++++++++ 5 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 drivers/spi/spi-uclass.c
common/exports.c +3 -1
@@ -27,10 +27,12 @@ unsigned long get_version(void)# define i2c_write dummy# define i2c_read dummy#endif -#ifndef CONFIG_CMD_SPI +#if !defined(CONFIG_CMD_SPI ) || defined(CONFIG_DM_SPI )# define spi_init dummy# define spi_setup_slave dummy# define spi_free_slave dummy +#endif +#ifndef CONFIG_CMD_SPI # define spi_claim_bus dummy# define spi_release_bus dummy# define spi_xfer dummy
drivers/spi/Makefile +4 -0
@@ -6,7 +6,11 @@ # # There are many options which enable SPI, so make this library available +ifdef CONFIG_DM_SPI +obj-y += spi-uclass.o +else obj-y += spi.o +endif obj-$(CONFIG_EP93XX_SPI) += ep93xx_spi.o obj-$(CONFIG_ALTERA_SPI) += altera_spi.o
drivers/spi/spi-uclass.c new file +253 -0
253 lines not on this page · raw message
include/dm/uclass-id.h +1 -0
@@ -22,6 +22,7 @@ enum uclass_id {/* U-Boot uclasses start here */ UCLASS_GPIO ,/* Bank of general-purpose I/O pins */ UCLASS_SERIAL ,/* Serial UART */ +UCLASS_SPI ,/* SPI bus */ UCLASS_COUNT , UCLASS_INVALID = -1,
include/spi.h +140 -0
175 lines not on this page · raw message
Signature · 1 line
2.0.0.526.g5318336
PATCH 6/25 dm: spi: Add a uclass for SPI
Hi
Quoted 14 lines · On Monday 14 of July 2014 18:56:13 Simon Glass wrote:
Add a uclass which provides access to SPI buses and includes operations required by SPI.
For a time driver model will need to co-exist with the legacy SPI interface so some parts of the header file are changed depending on which is in use. The exports are adjusted also since some functions are not available with driver model.
Boards must define
CONFIG_DM_SPIto use driver model for SPI.Signed-off-by Simon Glass <[email protected]>---
...
drivers/spi/spi-uclass.c line 86
Quoted 5 lines
drivers/spi/spi-uclass.c new file +5
-0+int spi_xfer (struct spi_slave *slave,unsigned int bitlen, +const void *dout,void *din,unsigned long flags) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent;
is this the best interface here?
I think it would be cleaner if bus drivers had interfaces which follow a certain template, such asbus_ops(struct udevice *bus, struct udevice *child, ...)
struct spi_slave would be a prime candidate to have in child->parentdata (which should only be accessed by the parent IIUC)
regards
Pavel Herrmann
PATCH 6/25 dm: spi: Add a uclass for SPI
Hi Pavel,
Quoted 17 lines · Pavel Herrmann
Hi
Quoted 13 lines · On Monday 14 of July 2014 18:56:13 Simon Glass wrote:
Add a uclass which provides access to SPI buses and includes operations required by SPI.
For a time driver model will need to co-exist with the legacy SPI interface so some parts of the header file are changed depending on which is in use. The exports are adjusted also since some functions are not available with driver model.
Boards must define
CONFIG_DM_SPIto use driver model for SPI.Signed-off-by Simon Glass <[email protected]>---
Quoted 11 lines
drivers/spi/spi-uclass.c line 86
Quoted 6 lines
...
drivers/spi/spi-uclass.c new file +5
-0+int spi_xfer (struct spi_slave *slave,unsigned int bitlen, +const void *dout,void *din,unsigned long flags) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent;is this the best interface here?
I think it would be cleaner if bus drivers had interfaces which follow a certain template, such asbus_ops(struct udevice *bus, struct udevice *child, ...)
Thanks for your comments.
Well I thought about that long and hard. Clearly in a pure driver-model work we would pass a udevice, not a spi_slave.
Quoted 3 lines
struct
spi_slavewould be a prime candidate to have inchild->parentdata(which should only be accessed by the parent IIUC)
Yes, it is. In other words, 'struct spi_slave' is the child's parent data. The only reason that spi_xfer() is passed a spi_slave rather than a udevice is to maintain compatibility with the existing SPI system. I tried various other approachs, such as '#define spi_slave udevice' and the like. At some point we can change it, but it is really painful to have two completely different APIs co-existing in U-Boot.
This way, driver model becomes a fairly soft transition, the amount of rewriting needed is reduced, and I think it is much more likely that people will use it. As an example, one of the reasons that the generic board change has been relatively painless is that people can just define CONFIG_SYS_GENERIC_BOARD in their board header file, and in most cases it just works. If we require people to rewrite things it will take longer, etc. There is already enough rewriting required in individual drivers to keep people busy. It will be a relatively easy change to do in the future if we want to.
Re passing both the bus and the device, really, what's the point? The only valid bus to pass to the function is dev->parent. If you pass anything else it is an error. It is redundant and therefore just introduces the possibility of error.
Regards,
Simon
PATCH 6/25 dm: spi: Add a uclass for SPI
Hi
Quoted 50 lines · On Wednesday 16 of July 2014 23:39:44 Simon Glass wrote:
Hi Pavel,
Quoted 19 lines · Pavel Herrmann
Hi
Quoted 16 lines · On Monday 14 of July 2014 18:56:13 Simon Glass wrote:
Add a uclass which provides access to SPI buses and includes operations required by SPI.
For a time driver model will need to co-exist with the legacy SPI interface
so some parts of the header file are changed depending on which is in use.
The exports are adjusted also since some functions are not available with
driver model.Boards must define
CONFIG_DM_SPIto use driver model for SPI.Signed-off-by Simon Glass <[email protected]>---
Quoted 11 lines
drivers/spi/spi-uclass.c line 86
Quoted 6 lines
...
drivers/spi/spi-uclass.c new file +5
-0+int spi_xfer (struct spi_slave *slave,unsigned int bitlen, +const void *dout,void *din,unsigned long flags) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent;is this the best interface here?
I think it would be cleaner if bus drivers had interfaces which follow a certain template, such asbus_ops(struct udevice *bus, struct udevice *child, ...)Thanks for your comments.
Well I thought about that long and hard. Clearly in a pure driver-model work we would pass a udevice, not a
spi_slave.Quoted 2 lines
struct
spi_slavewould be a prime candidate to have inchild->parentdata(which should only be accessed by the parent IIUC)Yes, it is. In other words, 'struct
spi_slave' is the child's parent data. The only reason thatspi_xfer()is passed aspi_slaverather than a udevice is to maintain compatibility with the existing SPI system. I tried various other approachs, such as '#definespi_slaveudevice' and the like. At some point we can change it, but it is really painful to have two completely different APIs co-existing in U-Boot.
Quoted 9 lines
This way, driver model becomes a fairly soft transition, the amount of rewriting needed is reduced, and I think it is much more likely that people will use it. As an example, one of the reasons that the generic board change has been relatively painless is that people can just define
CONFIG_SYS_GENERIC_BOARDin their board header file, and in most cases it just works. If we require people to rewrite things it will take longer, etc. There is already enough rewriting required in individual drivers to keep people busy. It will be a relatively easy change to do in the future if we want to.
OK, that reason I understand.
However, what you are doing now is limiting what parentdata a SPI bus controller can use.
This means that each bus driver has its parentdata defined by what uclass it belongs to. Are you sure this won't be a limitation? I mean that you could end up with uclasses that have the same calls but a slightly different parentdata.
Yes, you could have a shared parentdata from the uclass (that makes sense for all controllers, because of the way the bus works), and a controller-specific parentdata as an extension (read "void *private" at the end of the parentdata structure)
Quoted 4 lines
Re passing both the bus and the device, really, what's the point? The only valid bus to pass to the function is
dev->parent. If you pass anything else it is an error. It is redundant and therefore just introduces the possibility of error.
Well, yes, sort of.
Consider a bus with transparent bridges, like USB or PCI.
If you were to represent these bridges as udevices, you would end up with something in between the actual devices and the bus controller, that forwards requests with no or minimal change.
in case of USB specifically (IIRC), hubs are visible during initialization, but when the device is up it has its own descriptor that is used to
in case of PCI, the device address actually contains the bus number, but the device itself doesn't really care about it, and is only used to select which bus the command goes to.
consider the following scenario:
------ ---------- --------- --------- ------------ |root| --- |ehci_hcd| --- |usb_hub| --- |usb_hub| --- |usb_device| ------ ---------- --------- --------- ------------
If you were to flatten the bus, the udevice tree would not really correspond to how the bus looks like in reality, and it might give you some hurdles with initialization.
note that in these cases you cannot pass a child udevice to the bus controller as a part of the upcall, since it is not necessarily its child. this is in contradiction to what I wrote previously, simply because I didn't think hard enough to find these counter examples.
regards
Pavel Herrmann
PATCH 6/25 dm: spi: Add a uclass for SPI
Hi Pavel,
Quoted 63 lines · Pavel Herrmann
Hi
Quoted 50 lines · On Wednesday 16 of July 2014 23:39:44 Simon Glass wrote:
Hi Pavel,
Quoted 19 lines · Pavel Herrmann
Hi
Quoted 16 lines · On Monday 14 of July 2014 18:56:13 Simon Glass wrote:
Add a uclass which provides access to SPI buses and includes operations required by SPI.
For a time driver model will need to co-exist with the legacy SPI interface
so some parts of the header file are changed depending on which is in use.
The exports are adjusted also since some functions are not available with
driver model.Boards must define
CONFIG_DM_SPIto use driver model for SPI.Signed-off-by Simon Glass <[email protected]>---
Quoted 11 lines
drivers/spi/spi-uclass.c line 86
Quoted 6 lines
...
drivers/spi/spi-uclass.c new file +5
-0+int spi_xfer (struct spi_slave *slave,unsigned int bitlen, +const void *dout,void *din,unsigned long flags) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent;is this the best interface here?
I think it would be cleaner if bus drivers had interfaces which follow a certain template, such asbus_ops(struct udevice *bus, struct udevice *child, ...)Thanks for your comments.
Well I thought about that long and hard. Clearly in a pure driver-model work we would pass a udevice, not a
spi_slave.Quoted 2 lines
struct
spi_slavewould be a prime candidate to have inchild->parentdata(which should only be accessed by the parent IIUC)Yes, it is. In other words, 'struct
spi_slave' is the child's parent data. The only reason thatspi_xfer()is passed aspi_slaverather than a udevice is to maintain compatibility with the existing SPI system. I tried various other approachs, such as '#definespi_slaveudevice' and the like. At some point we can change it, but it is really painful to have two completely different APIs co-existing in U-Boot.Quoted 9 lines
This way, driver model becomes a fairly soft transition, the amount of rewriting needed is reduced, and I think it is much more likely that people will use it. As an example, one of the reasons that the generic board change has been relatively painless is that people can just define
CONFIG_SYS_GENERIC_BOARDin their board header file, and in most cases it just works. If we require people to rewrite things it will take longer, etc. There is already enough rewriting required in individual drivers to keep people busy. It will be a relatively easy change to do in the future if we want to.
Quoted 7 lines
OK, that reason I understand.
However, what you are doing now is limiting what parentdata a SPI bus controller can use.
This means that each bus driver has its parentdata defined by what uclass it belongs to. Are you sure this won't be a limitation? I mean that you could end up with uclasses that have the same calls but a slightly different parentdata.
No it is defined by the driver of the bus. Since it is possible to have (for example) 3 different USB drivers in a system, each can have its own idea of what child data it wants. I definitely agree that setting the parentdata by the child's uclass, or even the bus's uclass would be limiting. In the case of SPI I have elected to use struct spi_slave for reasons I explained earlier.
Quoted 29 lines
Yes, you could have a shared parentdata from the uclass (that makes sense for all controllers, because of the way the bus works), and a controller-specific parentdata as an extension (read "void *private" at the end of the parentdata structure)
Quoted 4 lines
Re passing both the bus and the device, really, what's the point? The only valid bus to pass to the function is
dev->parent. If you pass anything else it is an error. It is redundant and therefore just introduces the possibility of error.Well, yes, sort of.
Consider a bus with transparent bridges, like USB or PCI.
If you were to represent these bridges as udevices, you would end up with something in between the actual devices and the bus controller, that forwards requests with no or minimal change.
in case of USB specifically (IIRC), hubs are visible during initialization, but when the device is up it has its own descriptor that is used to
in case of PCI, the device address actually contains the bus number, but the device itself doesn't really care about it, and is only used to select which bus the command goes to.consider the following scenario:
------ ---------- --------- --------- ------------ |root| --- |ehci_hcd| --- |usb_hub| --- |usb_hub| --- |usb_device| ------ ---------- --------- --------- ------------
Quoted 8 lines
If you were to flatten the bus, the udevice tree would not really correspond to how the bus looks like in reality, and it might give you some hurdles with initialization.
note that in these cases you cannot pass a child udevice to the bus controller as a part of the upcall, since it is not necessarily its child. this is in contradiction to what I wrote previously, simply because I didn't think hard enough to find these counter examples.
I think you are referring to setting up a bus such that it becomes transparent. But even when it is, I'm not sure that driver model needs to rearrange its data structures to suit. Why would you flatten the bus? If we keep the data structures the same (strict parent-child relationships) then we always have a parent for the child, rather than trying to be too clever. Still, the child can obtain direct bus access through some mechanism delegated by the parent if we like. In that case there is even less reason to access the parent udevice IMO.
I think I'm missing something from your example, so please let me know.
Regards,
Simon
PATCH 6/25 dm: spi: Add a uclass for SPI
Hi
Quoted 77 lines · On Thursday 17 of July 2014 09:26:47 Simon Glass wrote:
Hi Pavel,
Quoted 66 lines · Pavel Herrmann
Hi
Quoted 53 lines · On Wednesday 16 of July 2014 23:39:44 Simon Glass wrote:
Hi Pavel,
Quoted 20 lines · Pavel Herrmann
Hi
Quoted 17 lines · On Monday 14 of July 2014 18:56:13 Simon Glass wrote:
Add a uclass which provides access to SPI buses and includes operations
required by SPI.For a time driver model will need to co-exist with the legacy SPI interface
so some parts of the header file are changed depending on which is in use.
The exports are adjusted also since some functions are not available with
driver model.Boards must define
CONFIG_DM_SPIto use driver model for SPI.Signed-off-by Simon Glass <[email protected]>---
Quoted 12 lines
drivers/spi/spi-uclass.c line 86
Quoted 6 lines
...
drivers/spi/spi-uclass.c new file +5
-0+int spi_xfer (struct spi_slave *slave,unsigned int bitlen, +const void *dout,void *din,unsigned long flags) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent;is this the best interface here?
I think it would be cleaner if bus drivers had interfaces which follow a
certain template, such asbus_ops(struct udevice *bus, struct udevice *child, ...)Thanks for your comments.
Well I thought about that long and hard. Clearly in a pure driver-model work we would pass a udevice, not a
spi_slave.Quoted 3 lines
struct
spi_slavewould be a prime candidate to have inchild->parentdata
(which should only be accessed by the parent IIUC)Yes, it is. In other words, 'struct
spi_slave' is the child's parent data. The only reason thatspi_xfer()is passed aspi_slaverather than a udevice is to maintain compatibility with the existing SPI system. I tried various other approachs, such as '#definespi_slaveudevice' and the like. At some point we can change it, but it is really painful to have two completely different APIs co-existing in U-Boot.Quoted 9 lines
This way, driver model becomes a fairly soft transition, the amount of rewriting needed is reduced, and I think it is much more likely that people will use it. As an example, one of the reasons that the generic board change has been relatively painless is that people can just define
CONFIG_SYS_GENERIC_BOARDin their board header file, and in most cases it just works. If we require people to rewrite things it will take longer, etc. There is already enough rewriting required in individual drivers to keep people busy. It will be a relatively easy change to do in the future if we want to.Quoted 8 lines
OK, that reason I understand.
However, what you are doing now is limiting what parentdata a SPI bus controller can use.
This means that each bus driver has its parentdata defined by what uclass it belongs to. Are you sure this won't be a limitation? I mean that you could end up with uclasses that have the same calls but a slightly different parentdata.
Quoted 6 lines
No it is defined by the driver of the bus. Since it is possible to have (for example) 3 different USB drivers in a system, each can have its own idea of what child data it wants. I definitely agree that setting the parentdata by the child's uclass, or even the bus's uclass would be limiting. In the case of SPI I have elected to use struct
spi_slavefor reasons I explained earlier.
OK, so you would have some bus uclasses that pass typecasted parentdata, because it is the same for all bus drivers, and some that pass the childs udevice*, because the parentdata type is not known beforehands? what happens if suddenly you need to add a bus controller that needs a little more per- child data than the existing ones?
wouldn't it be better to unify this somehow?
Quoted 49 lines
Quoted 31 lines
Yes, you could have a shared parentdata from the uclass (that makes sense for all controllers, because of the way the bus works), and a controller-specific parentdata as an extension (read "void *private" at the end of the parentdata structure)
Quoted 4 lines
Re passing both the bus and the device, really, what's the point? The only valid bus to pass to the function is
dev->parent. If you pass anything else it is an error. It is redundant and therefore just introduces the possibility of error.Well, yes, sort of.
Consider a bus with transparent bridges, like USB or PCI.
If you were to represent these bridges as udevices, you would end up with something in between the actual devices and the bus controller, that forwards requests with no or minimal change.
in case of USB specifically (IIRC), hubs are visible during initialization, but when the device is up it has its own descriptor that is used to
in case of PCI, the device address actually contains the bus number, but the device itself doesn't really care about it, and is only used to select which bus the command goes to.consider the following scenario:
------ ---------- --------- --------- ------------|root| --- |ehci_hcd| --- |usb_hub| --- |usb_hub| --- |usb_device|
------ ---------- --------- --------- ------------
Quoted 8 lines
If you were to flatten the bus, the udevice tree would not really correspond to how the bus looks like in reality, and it might give you some hurdles with initialization.
note that in these cases you cannot pass a child udevice to the bus controller as a part of the upcall, since it is not necessarily its child. this is in contradiction to what I wrote previously, simply because I didn't think hard enough to find these counter examples.
I think you are referring to setting up a bus such that it becomes transparent. But even when it is, I'm not sure that driver model needs to rearrange its data structures to suit. Why would you flatten the bus? If we keep the data structures the same (strict parent-child relationships) then we always have a parent for the child, rather than trying to be too clever. Still, the child can obtain direct bus access through some mechanism delegated by the parent if we like. In that case there is even less reason to access the parent udevice IMO.
Quoted 1 line
I think I'm missing something from your example, so please let me know.
ok, lets try some code examples
lets say a usb_hub is implemented as a USB bus that itself sits in a USB bus. lets say USB bus uclass has a function usb_bulk_msg()
usb_hub's implementation of such function would look like this:
usb_bulk_msg(udevice *hub, usb_descriptor *child, ...)
{
return usb_bulk_msg(hub->parent, child, ...);
}your USB device would then callusb_bulk_msg(my_dev->parent, my_desc,...)
this would work recursively through all hubs, and the end result you would like is to call usb_bulk_msg() of the host bus controller, without changing any of the parameters. (except the first, which is used for recursion)
However, if you got rid of the first parameter, you would need a different variable to control your recursion, or you would need the device descriptor to hold pointer to the udevice of the host controller, which would effectively flatten the bus.
this is the case where I believe the best way to implement is to have a shared parentdata based on parent uclass (the usb_descriptor in this case), with a driver-specific extension, in case the bus driver need something extra over what the "standard" is. the shared parentdata would then be used as a device descriptor/address/whatever-you-call-that in the bus calls.
One would also need to relax the semantics a bit, so that host bus controller can fill the parentdata for a udevice that is not its direct child.
I'm not saying your code is wrong or anything, I just think you should have a look at a more complex bus like USB or PCI, and design the "bus uclass interface template" in a way that would support such a bus. Otherwise you might end up either reworking your simpler busses later, or having inconsistent bus uclass interfaces (which we did when we tried this the first time).
I understand that API compatibility is an issue, and I agree that it is fine to cut some corners at this point, but we need to keep in mind to fix them once all the drivers have been converted.
regards
Pavel
PATCH 6/25 dm: spi: Add a uclass for SPI
Quoted 176 lines · On Thursday 17 of July 2014 20:01:29 Pavel Herrmann wrote:
Hi
Quoted 82 lines · On Thursday 17 of July 2014 09:26:47 Simon Glass wrote:
Hi Pavel,
Quoted 71 lines · Pavel Herrmann
Hi
Quoted 56 lines · On Wednesday 16 of July 2014 23:39:44 Simon Glass wrote:
Hi Pavel,
Quoted 22 lines · Pavel Herrmann
Hi
Quoted 19 lines · On Monday 14 of July 2014 18:56:13 Simon Glass wrote:
Add a uclass which provides access to SPI buses and includes operations
required by SPI.For a time driver model will need to co-exist with the legacy SPI interface
so some parts of the header file are changed depending on which is in
use.
The exports are adjusted also since some functions are not available
with
driver model.Boards must define
CONFIG_DM_SPIto use driver model for SPI.Signed-off-by Simon Glass <[email protected]>---
Quoted 13 lines
drivers/spi/spi-uclass.c line 86
Quoted 6 lines
...
drivers/spi/spi-uclass.c new file +5
-0+int spi_xfer (struct spi_slave *slave,unsigned int bitlen, +const void *dout,void *din,unsigned long flags) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent;is this the best interface here?
I think it would be cleaner if bus drivers had interfaces which follow
a
certain template, such asbus_ops(struct udevice *bus, struct udevice *child, ...)Thanks for your comments.
Well I thought about that long and hard. Clearly in a pure driver-model work we would pass a udevice, not a
spi_slave.Quoted 3 lines
struct
spi_slavewould be a prime candidate to have inchild->parentdata
(which should only be accessed by the parent IIUC)Yes, it is. In other words, 'struct
spi_slave' is the child's parent data. The only reason thatspi_xfer()is passed aspi_slaverather than a udevice is to maintain compatibility with the existing SPI system. I tried various other approachs, such as '#definespi_slaveudevice' and the like. At some point we can change it, but it is really painful to have two completely different APIs co-existing in U-Boot.Quoted 9 lines
This way, driver model becomes a fairly soft transition, the amount of rewriting needed is reduced, and I think it is much more likely that people will use it. As an example, one of the reasons that the generic board change has been relatively painless is that people can just define
CONFIG_SYS_GENERIC_BOARDin their board header file, and in most cases it just works. If we require people to rewrite things it will take longer, etc. There is already enough rewriting required in individual drivers to keep people busy. It will be a relatively easy change to do in the future if we want to.OK, that reason I understand.
Quoted 7 lines
However, what you are doing now is limiting what parentdata a SPI bus controller can use.
This means that each bus driver has its parentdata defined by what uclass
it belongs to. Are you sure this won't be a limitation? I mean that you could end up with uclasses that have the same calls but a slightly different parentdata.Quoted 6 lines
No it is defined by the driver of the bus. Since it is possible to have (for example) 3 different USB drivers in a system, each can have its own idea of what child data it wants. I definitely agree that setting the parentdata by the child's uclass, or even the bus's uclass would be limiting. In the case of SPI I have elected to use struct
spi_slavefor reasons I explained earlier.OK, so you would have some bus uclasses that pass typecasted parentdata, because it is the same for all bus drivers, and some that pass the childs udevice*, because the parentdata type is not known beforehands? what happens if suddenly you need to add a bus controller that needs a little more per- child data than the existing ones?
wouldn't it be better to unify this somehow?
Quoted 51 lines
Quoted 33 lines
Yes, you could have a shared parentdata from the uclass (that makes sense
for all controllers, because of the way the bus works), and a controller-specific parentdata as an extension (read "void *private" at the end of the parentdata structure)Quoted 4 lines
Re passing both the bus and the device, really, what's the point? The only valid bus to pass to the function is
dev->parent. If you pass anything else it is an error. It is redundant and therefore just introduces the possibility of error.Well, yes, sort of.
Consider a bus with transparent bridges, like USB or PCI.
If you were to represent these bridges as udevices, you would end up with
something in between the actual devices and the bus controller, that forwards requests with no or minimal change.
in case of USB specifically (IIRC), hubs are visible during initialization, but when the device is up it has its own descriptor that is used to
in case of PCI, the device address actually contains the bus number, but the device itself doesn't really care about it, and is only used to select which bus the command goes to.consider the following scenario:
------ ---------- --------- --------- ------------|root| --- |ehci_hcd| --- |usb_hub| --- |usb_hub| --- |usb_device|
------ ---------- --------- --------- ------------
Quoted 8 lines
If you were to flatten the bus, the udevice tree would not really correspond to how the bus looks like in reality, and it might give you some hurdles with initialization.
note that in these cases you cannot pass a child udevice to the bus controller as a part of the upcall, since it is not necessarily its child. this is in contradiction to what I wrote previously, simply because I didn't think hard enough to find these counter examples.
I think you are referring to setting up a bus such that it becomes transparent. But even when it is, I'm not sure that driver model needs to rearrange its data structures to suit. Why would you flatten the bus? If we keep the data structures the same (strict parent-child relationships) then we always have a parent for the child, rather than trying to be too clever. Still, the child can obtain direct bus access through some mechanism delegated by the parent if we like. In that case there is even less reason to access the parent udevice IMO.
Quoted 1 line
I think I'm missing something from your example, so please let me know.
ok, lets try some code examples
lets say a
usb_hubis implemented as a USB bus that itself sits in a USB bus. lets say USB bus uclass has a functionusb_bulk_msg()usb_hub's implementation of such function would look like this:
usb_bulk_msg(udevice *hub, usb_descriptor *child, ...){ return usb_bulk_msg(hub->parent, child, ...); }your USB device would then call
usb_bulk_msg(my_dev->parent, my_desc,...)this would work recursively through all hubs, and the end result you would like is to call
usb_bulk_msg()of the host bus controller, without changing any of the parameters. (except the first, which is used for recursion)However, if you got rid of the first parameter, you would need a different variable to control your recursion, or you would need the device descriptor to hold pointer to the udevice of the host controller, which would effectively flatten the bus.
Quoted 5 lines
this is the case where I believe the best way to implement is to have a shared parentdata based on parent uclass (the
usb_descriptorin this case), with a driver-specific extension, in case the bus driver need something extra over what the "standard" is. the shared parentdata would then be used as a device descriptor/address/whatever-you-call-that in the bus calls.
I would like to add that passing the childs udevice* should work equally as well, if you relax the bus == child->parent assumption.
Quoted 18 lines
One would also need to relax the semantics a bit, so that host bus controller can fill the parentdata for a udevice that is not its direct child.
I'm not saying your code is wrong or anything, I just think you should have a look at a more complex bus like USB or PCI, and design the "bus uclass interface template" in a way that would support such a bus. Otherwise you might end up either reworking your simpler busses later, or having inconsistent bus uclass interfaces (which we did when we tried this the first time).
I understand that API compatibility is an issue, and I agree that it is fine to cut some corners at this point, but we need to keep in mind to fix them once all the drivers have been converted.
regards
Pavel
PATCH 6/25 dm: spi: Add a uclass for SPI
Hi Pavel,
Quoted 177 lines · Pavel Herrmann
Quoted 176 lines · On Thursday 17 of July 2014 20:01:29 Pavel Herrmann wrote:
Hi
Quoted 82 lines · On Thursday 17 of July 2014 09:26:47 Simon Glass wrote:
Hi Pavel,
Quoted 71 lines · Pavel Herrmann
Hi
Quoted 56 lines · On Wednesday 16 of July 2014 23:39:44 Simon Glass wrote:
Hi Pavel,
Quoted 22 lines · Pavel Herrmann
Hi
Quoted 19 lines · On Monday 14 of July 2014 18:56:13 Simon Glass wrote:
Add a uclass which provides access to SPI buses and includes operations
required by SPI.For a time driver model will need to co-exist with the legacy SPI interface
so some parts of the header file are changed depending on which is in
use.
The exports are adjusted also since some functions are not available
with
driver model.Boards must define
CONFIG_DM_SPIto use driver model for SPI.Signed-off-by Simon Glass <[email protected]>---
Quoted 13 lines
Review
Quoted 6 lines
...
(quoted diff) +5
-0+int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *dev = slave->dev; + struct udevice *bus = dev->parent;is this the best interface here?
I think it would be cleaner if bus drivers had interfaces which follow
a
certain template, such asbus_ops(struct udevice *bus, struct udevice *child, ...)Thanks for your comments.
Well I thought about that long and hard. Clearly in a pure driver-model work we would pass a udevice, not a
spi_slave.Quoted 3 lines
struct
spi_slavewould be a prime candidate to have inchild->parentdata
(which should only be accessed by the parent IIUC)Yes, it is. In other words, 'struct
spi_slave' is the child's parent data. The only reason thatspi_xfer()is passed aspi_slaverather than a udevice is to maintain compatibility with the existing SPI system. I tried various other approachs, such as '#definespi_slaveudevice' and the like. At some point we can change it, but it is really painful to have two completely different APIs co-existing in U-Boot.Quoted 9 lines
This way, driver model becomes a fairly soft transition, the amount of rewriting needed is reduced, and I think it is much more likely that people will use it. As an example, one of the reasons that the generic board change has been relatively painless is that people can just define
CONFIG_SYS_GENERIC_BOARDin their board header file, and in most cases it just works. If we require people to rewrite things it will take longer, etc. There is already enough rewriting required in individual drivers to keep people busy. It will be a relatively easy change to do in the future if we want to.OK, that reason I understand.
Quoted 7 lines
However, what you are doing now is limiting what parentdata a SPI bus controller can use.
This means that each bus driver has its parentdata defined by what uclass
it belongs to. Are you sure this won't be a limitation? I mean that you could end up with uclasses that have the same calls but a slightly different parentdata.Quoted 6 lines
No it is defined by the driver of the bus. Since it is possible to have (for example) 3 different USB drivers in a system, each can have its own idea of what child data it wants. I definitely agree that setting the parentdata by the child's uclass, or even the bus's uclass would be limiting. In the case of SPI I have elected to use struct
spi_slavefor reasons I explained earlier.OK, so you would have some bus uclasses that pass typecasted parentdata, because it is the same for all bus drivers, and some that pass the childs udevice*, because the parentdata type is not known beforehands? what happens if suddenly you need to add a bus controller that needs a little more per- child data than the existing ones?
wouldn't it be better to unify this somehow?
Quoted 51 lines
Quoted 33 lines
Yes, you could have a shared parentdata from the uclass (that makes sense
for all controllers, because of the way the bus works), and a controller-specific parentdata as an extension (read "void *private" at the end of the parentdata structure)Quoted 4 lines
Re passing both the bus and the device, really, what's the point? The only valid bus to pass to the function is
dev->parent. If you pass anything else it is an error. It is redundant and therefore just introduces the possibility of error.Well, yes, sort of.
Consider a bus with transparent bridges, like USB or PCI.
If you were to represent these bridges as udevices, you would end up with
something in between the actual devices and the bus controller, that forwards requests with no or minimal change.
in case of USB specifically (IIRC), hubs are visible during initialization, but when the device is up it has its own descriptor that is used to
in case of PCI, the device address actually contains the bus number, but the device itself doesn't really care about it, and is only used to select which bus the command goes to.consider the following scenario:
------ ---------- --------- --------- ------------|root| --- |ehci_hcd| --- |usb_hub| --- |usb_hub| --- |usb_device|
------ ---------- --------- --------- ------------
Quoted 8 lines
If you were to flatten the bus, the udevice tree would not really correspond to how the bus looks like in reality, and it might give you some hurdles with initialization.
note that in these cases you cannot pass a child udevice to the bus controller as a part of the upcall, since it is not necessarily its child. this is in contradiction to what I wrote previously, simply because I didn't think hard enough to find these counter examples.
I think you are referring to setting up a bus such that it becomes transparent. But even when it is, I'm not sure that driver model needs to rearrange its data structures to suit. Why would you flatten the bus? If we keep the data structures the same (strict parent-child relationships) then we always have a parent for the child, rather than trying to be too clever. Still, the child can obtain direct bus access through some mechanism delegated by the parent if we like. In that case there is even less reason to access the parent udevice IMO.
Quoted 1 line
I think I'm missing something from your example, so please let me know.
ok, lets try some code examples
lets say a
usb_hubis implemented as a USB bus that itself sits in a USB bus. lets say USB bus uclass has a functionusb_bulk_msg()usb_hub's implementation of such function would look like this:
usb_bulk_msg(udevice *hub, usb_descriptor *child, ...){ return usb_bulk_msg(hub->parent, child, ...); }your USB device would then call
usb_bulk_msg(my_dev->parent, my_desc,...)this would work recursively through all hubs, and the end result you would like is to call
usb_bulk_msg()of the host bus controller, without changing any of the parameters. (except the first, which is used for recursion)However, if you got rid of the first parameter, you would need a different variable to control your recursion, or you would need the device descriptor to hold pointer to the udevice of the host controller, which would effectively flatten the bus.
Quoted 8 lines
Quoted 5 lines
this is the case where I believe the best way to implement is to have a shared parentdata based on parent uclass (the
usb_descriptorin this case), with a driver-specific extension, in case the bus driver need something extra over what the "standard" is. the shared parentdata would then be used as a device descriptor/address/whatever-you-call-that in the bus calls.I would like to add that passing the childs udevice* should work equally as well, if you relax the bus ==
child->parentassumption.
OK. But at least in the case of USB I think we should be able to display the bus as a tree - hubs included. If we start re-stitching children to different parents when a bus is active I worry that things are going to get complicated.
Regards,
Simon
PATCH 6/25 dm: spi: Add a uclass for SPI
In reply to Pavel HerrmannHi Pavel,
Quoted 87 lines · Pavel Herrmann
Hi
Quoted 77 lines · On Thursday 17 of July 2014 09:26:47 Simon Glass wrote:
Hi Pavel,
Quoted 66 lines · Pavel Herrmann
Hi
Quoted 53 lines · On Wednesday 16 of July 2014 23:39:44 Simon Glass wrote:
Hi Pavel,
Quoted 20 lines · Pavel Herrmann
Hi
Quoted 17 lines · On Monday 14 of July 2014 18:56:13 Simon Glass wrote:
Add a uclass which provides access to SPI buses and includes operations
required by SPI.For a time driver model will need to co-exist with the legacy SPI interface
so some parts of the header file are changed depending on which is in use.
The exports are adjusted also since some functions are not available with
driver model.Boards must define
CONFIG_DM_SPIto use driver model for SPI.Signed-off-by Simon Glass <[email protected]>---
Quoted 12 lines
drivers/spi/spi-uclass.c line 86
Quoted 6 lines
...
drivers/spi/spi-uclass.c new file +5
-0+int spi_xfer (struct spi_slave *slave,unsigned int bitlen, +const void *dout,void *din,unsigned long flags) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent;is this the best interface here?
I think it would be cleaner if bus drivers had interfaces which follow a
certain template, such asbus_ops(struct udevice *bus, struct udevice *child, ...)Thanks for your comments.
Well I thought about that long and hard. Clearly in a pure driver-model work we would pass a udevice, not a
spi_slave.Quoted 3 lines
struct
spi_slavewould be a prime candidate to have inchild->parentdata
(which should only be accessed by the parent IIUC)Yes, it is. In other words, 'struct
spi_slave' is the child's parent data. The only reason thatspi_xfer()is passed aspi_slaverather than a udevice is to maintain compatibility with the existing SPI system. I tried various other approachs, such as '#definespi_slaveudevice' and the like. At some point we can change it, but it is really painful to have two completely different APIs co-existing in U-Boot.Quoted 9 lines
This way, driver model becomes a fairly soft transition, the amount of rewriting needed is reduced, and I think it is much more likely that people will use it. As an example, one of the reasons that the generic board change has been relatively painless is that people can just define
CONFIG_SYS_GENERIC_BOARDin their board header file, and in most cases it just works. If we require people to rewrite things it will take longer, etc. There is already enough rewriting required in individual drivers to keep people busy. It will be a relatively easy change to do in the future if we want to.Quoted 8 lines
OK, that reason I understand.
However, what you are doing now is limiting what parentdata a SPI bus controller can use.
This means that each bus driver has its parentdata defined by what uclass it belongs to. Are you sure this won't be a limitation? I mean that you could end up with uclasses that have the same calls but a slightly different parentdata.Quoted 6 lines
No it is defined by the driver of the bus. Since it is possible to have (for example) 3 different USB drivers in a system, each can have its own idea of what child data it wants. I definitely agree that setting the parentdata by the child's uclass, or even the bus's uclass would be limiting. In the case of SPI I have elected to use struct
spi_slavefor reasons I explained earlier.
Quoted 7 lines
OK, so you would have some bus uclasses that pass typecasted parentdata, because it is the same for all bus drivers, and some that pass the childs udevice*, because the parentdata type is not known beforehands? what happens if suddenly you need to add a bus controller that needs a little more per- child data than the existing ones?
wouldn't it be better to unify this somehow?
As mentioned spi_slave is something of a special case to retain some sort of code compatibility. We use the same structure with an without driver model.
Possibly we should unify this. I did think of allowing a child to support both parent-defined data and parent-uclass-defined data. It is an obvious step in some ways. However, I am concerned that the complexity of all of this is getting out of hand. My approach so far with driver model has been to build things as they are needed. I agree that this requires a certain about of refactoring effort, but I am much more worried about creating a framework that looks lovely but no one understands :-)
Quoted 91 lines
Quoted 50 lines
Quoted 32 lines
Yes, you could have a shared parentdata from the uclass (that makes sense for all controllers, because of the way the bus works), and a controller-specific parentdata as an extension (read "void *private" at the end of the parentdata structure)
Quoted 4 lines
Re passing both the bus and the device, really, what's the point? The only valid bus to pass to the function is
dev->parent. If you pass anything else it is an error. It is redundant and therefore just introduces the possibility of error.Well, yes, sort of.
Consider a bus with transparent bridges, like USB or PCI.
If you were to represent these bridges as udevices, you would end up with something in between the actual devices and the bus controller, that forwards requests with no or minimal change.
in case of USB specifically (IIRC), hubs are visible during initialization, but when the device is up it has its own descriptor that is used to
in case of PCI, the device address actually contains the bus number, but the device itself doesn't really care about it, and is only used to select which bus the command goes to.consider the following scenario:
------ ---------- --------- --------- ------------|root| --- |ehci_hcd| --- |usb_hub| --- |usb_hub| --- |usb_device|
------ ---------- --------- --------- ------------
Quoted 8 lines
If you were to flatten the bus, the udevice tree would not really correspond to how the bus looks like in reality, and it might give you some hurdles with initialization.
note that in these cases you cannot pass a child udevice to the bus controller as a part of the upcall, since it is not necessarily its child. this is in contradiction to what I wrote previously, simply because I didn't think hard enough to find these counter examples.
I think you are referring to setting up a bus such that it becomes transparent. But even when it is, I'm not sure that driver model needs to rearrange its data structures to suit. Why would you flatten the bus? If we keep the data structures the same (strict parent-child relationships) then we always have a parent for the child, rather than trying to be too clever. Still, the child can obtain direct bus access through some mechanism delegated by the parent if we like. In that case there is even less reason to access the parent udevice IMO.
Quoted 1 line
I think I'm missing something from your example, so please let me know.
ok, lets try some code examples
lets say a
usb_hubis implemented as a USB bus that itself sits in a USB bus. lets say USB bus uclass has a functionusb_bulk_msg()
usb_hub's implementation of such function would look like this:
usb_bulk_msg(udevice *hub, usb_descriptor *child, ...){ return usb_bulk_msg(hub->parent, child, ...); }your USB device would then call
usb_bulk_msg(my_dev->parent, my_desc,...)this would work recursively through all hubs, and the end result you would like is to call
usb_bulk_msg()of the host bus controller, without changing any of the parameters. (except the first, which is used for recursion)However, if you got rid of the first parameter, you would need a different variable to control your recursion, or you would need the device descriptor to hold pointer to the udevice of the host controller, which would effectively flatten the bus.
this is the case where I believe the best way to implement is to have a shared parentdata based on parent uclass (the
usb_descriptorin this case), with a driver-specific extension, in case the bus driver need something extra over what the "standard" is. the shared parentdata would then be used as a device descriptor/address/whatever-you-call-that in the bus calls.One would also need to relax the semantics a bit, so that host bus controller can fill the parentdata for a udevice that is not its direct child.
I'm not saying your code is wrong or anything, I just think you should have a look at a more complex bus like USB or PCI, and design the "bus uclass interface template" in a way that would support such a bus. Otherwise you might end up either reworking your simpler busses later, or having inconsistent bus uclass interfaces (which we did when we tried this the first time).
Quoted 3 lines
I understand that API compatibility is an issue, and I agree that it is fine to cut some corners at this point, but we need to keep in mind to fix them once all the drivers have been converted.
Right, so you almost have me convinced that we should add a separate parent parameter. However I would really like to see how USB hubs are implemented before we do this. We are actually talking about a different uclass, and there is no requirement that different uclasses follow similar semantics. I agree that this is desirable trait and I'm not really worried about the inefficiency of passing a redundant parameter in the case of SPI since the cost is pretty small.
So how about we see how USB hubs are implemented in practice, and keep this in mind? For that reason it would be useful to get USB going quickly.
Regards,
Simon
PATCH 6/25 dm: spi: Add a uclass for SPI
In reply to Simon GlassHi Simon,
some nitpicking
Quoted 20 lines · Simon Glass
Add a uclass which provides access to SPI buses and includes operations required by SPI.
For a time driver model will need to co-exist with the legacy SPI interface so some parts of the header file are changed depending on which is in use. The exports are adjusted also since some functions are not available with driver model.
Boards must define
CONFIG_DM_SPIto use driver model for SPI.Signed-off-by Simon Glass <[email protected]>---
common/exports.c | 4 +- drivers/spi/Makefile | 4 + drivers/spi/spi-uclass.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/spi.h | 140 ++++++++++++++++++++++++++ 5 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 drivers/spi/spi-uclass.c
...
Quoted 19 lines
drivers/spi/spi-uclass.c new file +16
-0@@ -0,0 +1,253 @@ +/* +* Copyright (c) 2014 Google, Inc +* +* SPDX-License-Identifier: GPL-2.0+ +*/ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <spi.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <dm/root.h> +#include <dm/lists.h> +
drivers/spi/spi-uclass.c line 26
Quoted 10 lines
drivers/spi/spi-uclass.c new file +10
-0+DECLARE_GLOBAL_DATA_PTR ; + +static int spi_set_speed_mode (struct udevice *bus,int speed,int mode) +{ +struct dm_spi_ops *ops; +int ret; + + ops =spi_get_ops (bus); +if (ops->set_speed) + ret = (*ops->set_speed)(bus, speed);
ret = ops->set_speed(bus, speed);
drivers/spi/spi-uclass.c line 34
Quoted 8 lines
drivers/spi/spi-uclass.c new file +8
-0+else + ret = -EINVAL ; +if (ret) { +printf ("Cannot set speed (err=%d)\n" , ret); +return ret; + } + + ops =spi_get_ops (bus);
redundant assignment
drivers/spi/spi-uclass.c line 36
Quoted 2 lines
drivers/spi/spi-uclass.c new file +2
-0+if (ops->set_mode) + ret = (*ops->set_mode)(bus, mode);
ret = ops->set_mode(bus, mode);
Quoted 33 lines
drivers/spi/spi-uclass.c new file +33
-0+else + ret = -EINVAL ; +if (ret) { +printf ("Cannot set mode (err=%d)\n" , ret); +return ret; + } + +return 0 ; +} + +int spi_claim_bus (struct spi_slave *slave) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent; +struct dm_spi_ops *ops =spi_get_ops (bus); +struct dm_spi_bus *spi = bus->uclass_priv; +int speed; +int ret; + + speed = slave->max_hz; +if (spi->max_hz) { +if (speed) + speed =min (speed, spi->max_hz); +else + speed = spi->max_hz; + } +if (!speed) + speed =100000 ; + ret =spi_set_speed_mode (bus, speed, slave->mode); +if (ret) +return ret; + +return ops->claim_bus ? ops->claim_bus (bus) :0 ;
drivers/spi/spi-uclass.c line 79
Quoted 10 lines
drivers/spi/spi-uclass.c new file +10
-0+} + +void spi_release_bus (struct spi_slave *slave) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent; +struct dm_spi_ops *ops =spi_get_ops (bus); + +if (ops->release_bus) +spi_get_ops (bus)->release_bus (bus);
ops->release_bus(bus);
Quoted 174 lines
drivers/spi/spi-uclass.c new file +174
-0+} + +int spi_xfer (struct spi_slave *slave,unsigned int bitlen, +const void *dout,void *din,unsigned long flags) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent; + +if (bus->uclass->uc_drv->id !=UCLASS_SPI ) +return -EOPNOTSUPP ; + +return spi_get_ops (bus)->xfer (bus, dev, bitlen, dout, din, flags); +} + +int spi_post_bind (struct udevice *dev) +{ +/* Scan the bus for devices */ +return dm_scan_fdt_node (dev, gd->fdt_blob, dev->of_offset, false); +} + +int spi_post_probe (struct udevice *dev) +{ +struct dm_spi_bus *spi = dev->uclass_priv; + + spi->max_hz =fdtdec_get_int (gd->fdt_blob, dev->of_offset, +"spi-max-frequency" ,0 ); + +return 0 ; +} + +int spi_bind_device (struct udevice *bus,int cs,const char *drv_name, +const char *dev_name,struct udevice **slavep) +{ +struct driver *drv; +int ret; + + drv =lists_driver_lookup_name (drv_name); +if (!drv) { +puts ("Cannot find spi_flash_std driver\n" ); +return -ENOENT ; + } + ret =device_bind (bus, drv, dev_name,NULL ,-1 , slavep); +if (ret) { +printf ("Cannot create device named '%s' (err=%d)\n" , + dev_name, ret); +return ret; + } + (*slavep)->req_seq = cs; + +return 0 ; +} + +int spi_find_bus_and_cs (int busnum,int cs,struct udevice **busp, +struct udevice **devp) +{ +struct udevice *bus, *dev; +int ret; + + ret =uclass_find_device_by_seq (UCLASS_SPI , busnum, false, &bus); +if (ret) +return ret; + ret =device_find_child_by_seq (bus, cs, false, &dev); +if (ret) +return ret; + *busp = bus; + *devp = dev; + +return ret; +} + +int spi_get_bus_and_cs (int busnum,int cs,int speed,int mode, +const char *drv_name,const char *dev_name, +struct udevice **devp,struct spi_slave **slavep) +{ +struct udevice *bus, *dev; +int ret; + + ret =uclass_get_device_by_seq (UCLASS_SPI , busnum, &bus); +if (ret) { +printf ("Invalid bus %d (err=%d)\n" , busnum, ret); +return ret; + } + ret =device_get_child_by_seq (bus, cs, &dev); + +/** +* If there is no such device, create one automatically. This means +* that we don't need a device tree node or platform data for the +* SPI flash chip - we will bind to the correct driver. +*/ +if (ret == -ENODEV && drv_name) { + ret =spi_bind_device (bus, cs, drv_name, dev_name, &dev); +if (ret) +return ret; + } +if (ret) { +printf ("Invalid chip select %d:%d (err=%d)\n" , busnum, cs, + ret); +return ret; + } + + ret =spi_set_speed_mode (bus, speed, mode); +if (ret) +return ret; + + *devp = bus; + *slavep =dev_get_parentdata (dev); + +return 0 ; +} + +/* Compatibility function - to be removed */ +struct spi_slave *spi_setup_slave_fdt (const void *blob,int node, +int bus_node) +{ +struct udevice *bus, *dev; +int ret; + + ret =uclass_get_device_by_of_offset (UCLASS_SPI , bus_node, &bus); +if (ret) +return NULL ; + ret =device_get_child_by_of_offset (bus, node, &dev); +if (ret) +return NULL ; +return dev_get_parentdata (dev); +} + +/* Compatibility function - to be removed */ +struct spi_slave *spi_setup_slave (unsigned int busnum,unsigned int cs, +unsigned int speed,unsigned int mode) +{ +struct spi_slave *slave; +struct udevice *dev; +int ret; + + ret =spi_get_bus_and_cs (busnum, cs, speed, mode,NULL ,0 , &dev, + &slave); +if (ret) +return NULL ; + +return slave; +} + +void spi_free_slave (struct spi_slave *slave) +{ +device_remove (slave->dev); + slave->dev =NULL ; +} + +int spi_ofdata_to_platdata (const void *blob,int node, +struct spi_slave *spi) +{ +int mode =0 ; + + spi->max_hz =fdtdec_get_int (blob, node,"spi-max-frequency" ,0 ); +if (fdtdec_get_bool (blob, node,"spi-cpol" )) + mode |=SPI_CPOL ; +if (fdtdec_get_bool (blob, node,"spi-cpha" )) + mode |=SPI_CPHA ; +if (fdtdec_get_bool (blob, node,"spi-cs-high" )) + mode |=SPI_CS_HIGH ; +if (fdtdec_get_bool (blob, node,"spi-half-duplex" )) + mode |=SPI_PREAMBLE ; + spi->mode = mode; + +return 0 ; +} + +UCLASS_DRIVER (spi) = { + .id =UCLASS_SPI , + .name ="spi" , + .post_bind = spi_post_bind, + .post_probe = spi_post_probe, + .per_device_auto_alloc_size =sizeof (struct dm_spi_bus ), +};
Signature · 1 line
- Daniel
PATCH 6/25 dm: spi: Add a uclass for SPI
In reply to Simon GlassQuoted 143 lines · Simon Glass
Add a uclass which provides access to SPI buses and includes operations required by SPI.
For a time driver model will need to co-exist with the legacy SPI interface so some parts of the header file are changed depending on which is in use. The exports are adjusted also since some functions are not available with driver model.
Boards must define
CONFIG_DM_SPIto use driver model for SPI.Signed-off-by Simon Glass <[email protected]>---
common/exports.c | 4 +- drivers/spi/Makefile | 4 + drivers/spi/spi-uclass.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/spi.h | 140 ++++++++++++++++++++++++++ 5 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 drivers/spi/spi-uclass.ccommon/exports.c +3
-1@@ -27,10 +27,12 @@ unsigned long get_version(void)# define i2c_write dummy# define i2c_read dummy#endif -#ifndef CONFIG_CMD_SPI +#if !defined(CONFIG_CMD_SPI ) || defined(CONFIG_DM_SPI )# define spi_init dummy# define spi_setup_slave dummy# define spi_free_slave dummy +#endif +#ifndef CONFIG_CMD_SPI # define spi_claim_bus dummy# define spi_release_bus dummy# define spi_xfer dummydrivers/spi/Makefile +4
-0@@ -6,7 +6,11 @@ # # There are many options which enable SPI, so make this library available +ifdef CONFIG_DM_SPI +obj-y += spi-uclass.o +else obj-y += spi.o +endif obj-$(CONFIG_EP93XX_SPI) += ep93xx_spi.o obj-$(CONFIG_ALTERA_SPI) += altera_spi.odrivers/spi/spi-uclass.c new file +82
-0@@ -0,0 +1,253 @@ +/* +* Copyright (c) 2014 Google, Inc +* +* SPDX-License-Identifier: GPL-2.0+ +*/ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <spi.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <dm/root.h> +#include <dm/lists.h> + +DECLARE_GLOBAL_DATA_PTR ; + +static int spi_set_speed_mode (struct udevice *bus,int speed,int mode) +{ +struct dm_spi_ops *ops; +int ret; + + ops =spi_get_ops (bus); +if (ops->set_speed) + ret = (*ops->set_speed)(bus, speed); +else + ret = -EINVAL ; +if (ret) { +printf ("Cannot set speed (err=%d)\n" , ret); +return ret; + } + + ops =spi_get_ops (bus); +if (ops->set_mode) + ret = (*ops->set_mode)(bus, mode); +else + ret = -EINVAL ; +if (ret) { +printf ("Cannot set mode (err=%d)\n" , ret); +return ret; + } + +return 0 ; +} + +int spi_claim_bus (struct spi_slave *slave) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent; +struct dm_spi_ops *ops =spi_get_ops (bus); +struct dm_spi_bus *spi = bus->uclass_priv; +int speed; +int ret; + + speed = slave->max_hz; +if (spi->max_hz) { +if (speed) + speed =min (speed, spi->max_hz); +else + speed = spi->max_hz; + } +if (!speed) + speed =100000 ; + ret =spi_set_speed_mode (bus, speed, slave->mode); +if (ret) +return ret; + +return ops->claim_bus ? ops->claim_bus (bus) :0 ; +} + +void spi_release_bus (struct spi_slave *slave) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent; +struct dm_spi_ops *ops =spi_get_ops (bus); + +if (ops->release_bus) +spi_get_ops (bus)->release_bus (bus); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
drivers/spi/spi-uclass.c line 92
Quoted 10 lines
drivers/spi/spi-uclass.c new file +10
-0+ const void *dout, void *din, unsigned long flags) +{ + struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent; + +if (bus->uclass->uc_drv->id !=UCLASS_SPI ) +return -EOPNOTSUPP ; + +return spi_get_ops (bus)->xfer (bus, dev, bitlen, dout, din, flags); +}
Cleared all these calls, as individual drivers/spi* will setup their ops and fills
the priv data. So this uclass will call accordingly - correct? add if I'm missing anything.
Quoted 6 lines
drivers/spi/spi-uclass.c new file +6
-0+ +int spi_post_bind (struct udevice *dev) +{ +/* Scan the bus for devices */ +return dm_scan_fdt_node (dev, gd->fdt_blob, dev->of_offset, false); +}
drivers/spi/spi-uclass.c line 108
Quoted 10 lines
drivers/spi/spi-uclass.c new file +10
-0+ +int spi_post_probe (struct udevice *dev) +{ +struct dm_spi_bus *spi = dev->uclass_priv; + + spi->max_hz =fdtdec_get_int (gd->fdt_blob, dev->of_offset, +"spi-max-frequency" ,0 ); + +return 0 ; +}
Anyway each spi driver will fills the data from dtb - like spi-max-frequency, why the spi
core (uclass) will do the same.
Can't we reuse the spi_slave, why dm_spi_bus?
Quoted 70 lines
drivers/spi/spi-uclass.c new file +70
-0+ +int spi_bind_device (struct udevice *bus,int cs,const char *drv_name, +const char *dev_name,struct udevice **slavep) +{ +struct driver *drv; +int ret; + + drv =lists_driver_lookup_name (drv_name); +if (!drv) { +puts ("Cannot find spi_flash_std driver\n" ); +return -ENOENT ; + } + ret =device_bind (bus, drv, dev_name,NULL ,-1 , slavep); +if (ret) { +printf ("Cannot create device named '%s' (err=%d)\n" , + dev_name, ret); +return ret; + } + (*slavep)->req_seq = cs; + +return 0 ; +} + +int spi_find_bus_and_cs (int busnum,int cs,struct udevice **busp, +struct udevice **devp) +{ +struct udevice *bus, *dev; +int ret; + + ret =uclass_find_device_by_seq (UCLASS_SPI , busnum, false, &bus); +if (ret) +return ret; + ret =device_find_child_by_seq (bus, cs, false, &dev); +if (ret) +return ret; + *busp = bus; + *devp = dev; + +return ret; +} + +int spi_get_bus_and_cs (int busnum,int cs,int speed,int mode, +const char *drv_name,const char *dev_name, +struct udevice **devp,struct spi_slave **slavep) +{ +struct udevice *bus, *dev; +int ret; + + ret =uclass_get_device_by_seq (UCLASS_SPI , busnum, &bus); +if (ret) { +printf ("Invalid bus %d (err=%d)\n" , busnum, ret); +return ret; + } + ret =device_get_child_by_seq (bus, cs, &dev); + +/** +* If there is no such device, create one automatically. This means +* that we don't need a device tree node or platform data for the +* SPI flash chip - we will bind to the correct driver. +*/ +if (ret == -ENODEV && drv_name) { + ret =spi_bind_device (bus, cs, drv_name, dev_name, &dev); +if (ret) +return ret; + } +if (ret) { +printf ("Invalid chip select %d:%d (err=%d)\n" , busnum, cs, + ret); +return ret; + }
drivers/spi/spi-uclass.c line 188
Quoted 10 lines
drivers/spi/spi-uclass.c new file +10
-0+ + ret =spi_set_speed_mode (bus, speed, mode); +if (ret) +return ret; + + *devp = bus; + *slavep =dev_get_parentdata (dev); + +return 0 ; +}
I guess this is one of the replacement for spi_setup_slave() in spi drivers, so how bus and cs member will populate to drivers.
Usually bus and cs members from spi_slave will populate to drivers through "sf probe" so spi_cs_activate will set the respective chip-select and along withreg_base in driver select through bus value.
spi_setup_slave() {
...zslave->base = get_zynq_spi_base(bus);
...
}
How these will handling with dm?
Quoted 82 lines
drivers/spi/spi-uclass.c new file +65
-0+ +/* Compatibility function - to be removed */ +struct spi_slave *spi_setup_slave_fdt (const void *blob,int node, +int bus_node) +{ +struct udevice *bus, *dev; +int ret; + + ret =uclass_get_device_by_of_offset (UCLASS_SPI , bus_node, &bus); +if (ret) +return NULL ; + ret =device_get_child_by_of_offset (bus, node, &dev); +if (ret) +return NULL ; +return dev_get_parentdata (dev); +} + +/* Compatibility function - to be removed */ +struct spi_slave *spi_setup_slave (unsigned int busnum,unsigned int cs, +unsigned int speed,unsigned int mode) +{ +struct spi_slave *slave; +struct udevice *dev; +int ret; + + ret =spi_get_bus_and_cs (busnum, cs, speed, mode,NULL ,0 , &dev, + &slave); +if (ret) +return NULL ; + +return slave; +} + +void spi_free_slave (struct spi_slave *slave) +{ +device_remove (slave->dev); + slave->dev =NULL ; +} + +int spi_ofdata_to_platdata (const void *blob,int node, +struct spi_slave *spi) +{ +int mode =0 ; + + spi->max_hz =fdtdec_get_int (blob, node,"spi-max-frequency" ,0 ); +if (fdtdec_get_bool (blob, node,"spi-cpol" )) + mode |=SPI_CPOL ; +if (fdtdec_get_bool (blob, node,"spi-cpha" )) + mode |=SPI_CPHA ; +if (fdtdec_get_bool (blob, node,"spi-cs-high" )) + mode |=SPI_CS_HIGH ; +if (fdtdec_get_bool (blob, node,"spi-half-duplex" )) + mode |=SPI_PREAMBLE ; + spi->mode = mode; + +return 0 ; +} + +UCLASS_DRIVER (spi) = { + .id =UCLASS_SPI , + .name ="spi" , + .post_bind = spi_post_bind, + .post_probe = spi_post_probe, + .per_device_auto_alloc_size =sizeof (struct dm_spi_bus ), +};include/dm/uclass-id.h +1
-0@@ -22,6 +22,7 @@ enum uclass_id {/* U-Boot uclasses start here */ UCLASS_GPIO ,/* Bank of general-purpose I/O pins */ UCLASS_SERIAL ,/* Serial UART */ +UCLASS_SPI ,/* SPI bus */ UCLASS_COUNT , UCLASS_INVALID = -1,include/spi.h +0
-0
include/spi.h line 63
Quoted 10 lines
include/spi.h +7
-0@@ -54,11 +54,19 @@#define SPI_DEFAULT_WORDLEN 8 +#ifdef CONFIG_DM_SPI +struct dm_spi_bus { +uint max_hz; +}; + +#endif /* CONFIG_DM_SPI */ +
No idea why this should require, max_hz is already a part of spi_slave and handling of these like getting from dtb and process and fill on priv data should be part of individual drivers and filling spi_slave member should be part of uclass - based on my understanding.
Any comments?
include/spi.h line 69
Quoted 6 lines
include/spi.h +1
-0/** * struct spi_slave - Representation of a SPI slave * * Drivers are expected to extend this with controller-specific data. * +* @dev: SPI slave device
Missing comments for max_hz and mode
Quoted 164 lines
include/spi.h +132
-1* @bus: ID of the bus that the slave is attached to. * @cs: ID of the chip select connected to the slave. * @op_mode_rx: SPI RX operation mode. @@ -71,8 +79,14 @@* @flags: Indication of SPI flags. */ struct spi_slave { +#ifdef CONFIG_DM_SPI +struct udevice *dev;/* struct spi_slave is dev->parentdata */ +uint max_hz; +uint mode; +#else unsigned int bus;unsigned int cs; +#endif u8 op_mode_rx;u8 op_mode_tx;unsigned int wordlen;@@ -220,6 +234,8 @@ int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen);int spi_xfer (struct spi_slave *slave,unsigned int bitlen,const void *dout,void *din,unsigned long flags); +#ifndef CONFIG_DM_SPI +/** * Determine if a SPI chipselect is valid. * This function is provided by the board if the low-level SPI driver @@ -255,6 +271,7 @@ void spi_cs_deactivate(struct spi_slave *slave);* @hz: The transfer speed */ void spi_set_speed (struct spi_slave *slave,uint hz); +#endif /** * Write 8 bits, then read 8 bits. @@ -305,4 +322,127 @@ struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node,struct spi_slave *spi_base_setup_slave_fdt (const void *blob,int busnum,int node); +#ifdef CONFIG_DM_SPI + +/** +* struct struct dm_spi_ops - Driver model SPI operations +* +* The uclass interface is implemented by all SPI devices which use +* driver model. +*/ +struct dm_spi_ops { +/** +* Claim the bus and prepare it for communication. +* +* The device privided is the slave device. It's parent controller +* will be used to provide the communication. +* +* This must be called before doing any transfers with a SPI slave. It +* will enable and initialize any SPI hardware as necessary, and make +* sure that the SCK line is in the correct idle state. It is not +* allowed to claim the same bus for several slaves without releasing +* the bus in between. +* +* @device: The SPI slave +* +* Returns: 0 if the bus was claimed successfully, or a negative value +* if it wasn't. +*/ +int (*claim_bus)(struct udevice *device); + +/** +* Release the SPI bus +* +* This must be called once for every call to spi_claim_bus() after +* all transfers have finished. It may disable any SPI hardware as +* appropriate. +* +* @device: The SPI slave +*/ +int (*release_bus)(struct udevice *device); + +/** +* Set the word length for SPI transactions +* +* Set the word length (number of bits per word) for SPI transactions. +* +* @device: The SPI slave +* @wordlen: The number of bits in a word +* +* Returns: 0 on success, -ve on failure. +*/ +int (*set_wordlen)(struct udevice *deiuce,unsigned int wordlen); + +/** +* SPI transfer +* +* This writes "bitlen" bits out the SPI MOSI port and simultaneously +* clocks "bitlen" bits in the SPI MISO port. That's just the way SPI +* works. +* +* The source of the outgoing bits is the "dout" parameter and the +* destination of the input bits is the "din" parameter. Note that +* "dout" and "din" can point to the same memory location, in which +* case the input data overwrites the output data (since both are +* buffered by temporary variables, this is OK). +* +* spi_xfer() interface: +* @device: The SPI bus which will be sending/receiving the data. +* @slave: The slave device to communicate with +* @bitlen: How many bits to write and read. +* @dout: Pointer to a string of bits to send out. The bits are +* held in a byte array and are sent MSB first. +* @din: Pointer to a string of bits that will be filled in. +* @flags: A bitwise combination of SPI_XFER_* flags. +* +* Returns: 0 on success, not -1 on failure +*/ +int (*xfer)(struct udevice *device,struct udevice *slave, +unsigned int bitlen,const void *dout,void *din, +unsigned long flags); + +/** +* Set transfer speed. +* This sets a new speed to be applied for next spi_xfer(). +* @slave: The SPI slave +* @hz: The transfer speed +* @return 0 if OK, -ve on error +*/ +int (*set_speed)(struct udevice *device,uint hz); + +/** +* Set the SPI mode/flags +* +* It is unclear if we want to set speed and mode together instead +* of separately. +* +* @slave: The SPI slave +* @mode: Requested SPI mode (SPI_... flags) +* @return 0 if OK, -ve on error +*/ +int (*set_mode)(struct udevice *device,uint mode); +}; + +int spi_find_bus_and_cs (int busnum,int cs,struct udevice **busp, +struct udevice **devp); + +int spi_get_bus_and_cs (int busnum,int cs,int speed,int mode, +const char *drv_name,const char *dev_name, +struct udevice **devp,struct spi_slave **slavep); + +int spi_bind_device (struct udevice *bus,int cs,const char *drv_name, +const char *dev_name,struct udevice **slavep); + +int spi_ofdata_to_platdata (const void *blob,int node, +struct spi_slave *spi); + +struct sandbox_state ; +int sandbox_spi_get_emul (struct sandbox_state *state, +struct udevice *bus,struct udevice *slave, +struct udevice **emulp); + +/* Access the serial operations for a device */ +#define spi_get_ops (dev) ((struct dm_spi_ops *)(dev)->driver->ops) +#endif /* CONFIG_DM_SPI */ +#endif /* _SPI_H_ */ --
Quoted 2 lines
2.0.0.526.g5318336
thanks!
Signature · 1 line
Jagan.
PATCH 6/25 dm: spi: Add a uclass for SPI
Hi Jagan,
Quoted 151 lines · Jagan Teki
Quoted 150 lines · Simon Glass
Add a uclass which provides access to SPI buses and includes operations required by SPI.
For a time driver model will need to co-exist with the legacy SPI interface so some parts of the header file are changed depending on which is in use. The exports are adjusted also since some functions are not available with driver model.
Boards must define
CONFIG_DM_SPIto use driver model for SPI.Signed-off-by Simon Glass <[email protected]>---
common/exports.c | 4 +- drivers/spi/Makefile | 4 + drivers/spi/spi-uclass.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/spi.h | 140 ++++++++++++++++++++++++++ 5 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 drivers/spi/spi-uclass.ccommon/exports.c +3
-1@@ -27,10 +27,12 @@ unsigned long get_version(void)# define i2c_write dummy# define i2c_read dummy#endif -#ifndef CONFIG_CMD_SPI +#if !defined(CONFIG_CMD_SPI ) || defined(CONFIG_DM_SPI )# define spi_init dummy# define spi_setup_slave dummy# define spi_free_slave dummy +#endif +#ifndef CONFIG_CMD_SPI # define spi_claim_bus dummy# define spi_release_bus dummy# define spi_xfer dummydrivers/spi/Makefile +4
-0@@ -6,7 +6,11 @@ # # There are many options which enable SPI, so make this library available +ifdef CONFIG_DM_SPI +obj-y += spi-uclass.o +else obj-y += spi.o +endif obj-$(CONFIG_EP93XX_SPI) += ep93xx_spi.o obj-$(CONFIG_ALTERA_SPI) += altera_spi.odrivers/spi/spi-uclass.c new file +89
-0@@ -0,0 +1,253 @@ +/* +* Copyright (c) 2014 Google, Inc +* +* SPDX-License-Identifier: GPL-2.0+ +*/ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <spi.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <dm/root.h> +#include <dm/lists.h> + +DECLARE_GLOBAL_DATA_PTR ; + +static int spi_set_speed_mode (struct udevice *bus,int speed,int mode) +{ +struct dm_spi_ops *ops; +int ret; + + ops =spi_get_ops (bus); +if (ops->set_speed) + ret = (*ops->set_speed)(bus, speed); +else + ret = -EINVAL ; +if (ret) { +printf ("Cannot set speed (err=%d)\n" , ret); +return ret; + } + + ops =spi_get_ops (bus); +if (ops->set_mode) + ret = (*ops->set_mode)(bus, mode); +else + ret = -EINVAL ; +if (ret) { +printf ("Cannot set mode (err=%d)\n" , ret); +return ret; + } + +return 0 ; +} + +int spi_claim_bus (struct spi_slave *slave) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent; +struct dm_spi_ops *ops =spi_get_ops (bus); +struct dm_spi_bus *spi = bus->uclass_priv; +int speed; +int ret; + + speed = slave->max_hz; +if (spi->max_hz) { +if (speed) + speed =min (speed, spi->max_hz); +else + speed = spi->max_hz; + } +if (!speed) + speed =100000 ; + ret =spi_set_speed_mode (bus, speed, slave->mode); +if (ret) +return ret; + +return ops->claim_bus ? ops->claim_bus (bus) :0 ; +} + +void spi_release_bus (struct spi_slave *slave) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent; +struct dm_spi_ops *ops =spi_get_ops (bus); + +if (ops->release_bus) +spi_get_ops (bus)->release_bus (bus); +} + +int spi_xfer (struct spi_slave *slave,unsigned int bitlen, +const void *dout,void *din,unsigned long flags) +{ +struct udevice *dev = slave->dev; +struct udevice *bus = dev->parent; + +if (bus->uclass->uc_drv->id !=UCLASS_SPI ) +return -EOPNOTSUPP ;
Quoted 8 lines
drivers/spi/spi-uclass.c line 92
Quoted 3 lines
drivers/spi/spi-uclass.c new file +3
-0+ +return spi_get_ops (bus)->xfer (bus, dev, bitlen, dout, din, flags); +}Cleared all these calls, as individual
drivers/spi*will setup their ops and fills
the priv data. So this uclass will call accordingly - correct? add if I'm missing anything.
Yes so far as I understand you, that is correct.
Quoted 13 lines
Quoted 13 lines
drivers/spi/spi-uclass.c new file +12
-0+ +int spi_post_bind (struct udevice *dev) +{ +/* Scan the bus for devices */ +return dm_scan_fdt_node (dev, gd->fdt_blob, dev->of_offset, false); +} + +int spi_post_probe (struct udevice *dev) +{ +struct dm_spi_bus *spi = dev->uclass_priv; + + spi->max_hz = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
Quoted 8 lines
drivers/spi/spi-uclass.c line 108
Quoted 4 lines
drivers/spi/spi-uclass.c new file +4
-0+ "spi-max-frequency", 0); + +return 0 ; +}Anyway each spi driver will fills the data from dtb - like spi-max-frequency, why the spi
core (uclass) will do the same.
Because the overall SPI peripheral has a maximum speed too. We should allow it to run at a nominal speed, but slow down for particular peripherals if needed.
Quoted 92 lines
Can't we reuse the
spi_slave, whydm_spi_bus?Quoted 70 lines
drivers/spi/spi-uclass.c new file +70
-0+ +int spi_bind_device (struct udevice *bus,int cs,const char *drv_name, +const char *dev_name,struct udevice **slavep) +{ +struct driver *drv; +int ret; + + drv =lists_driver_lookup_name (drv_name); +if (!drv) { +puts ("Cannot find spi_flash_std driver\n" ); +return -ENOENT ; + } + ret =device_bind (bus, drv, dev_name,NULL ,-1 , slavep); +if (ret) { +printf ("Cannot create device named '%s' (err=%d)\n" , + dev_name, ret); +return ret; + } + (*slavep)->req_seq = cs; + +return 0 ; +} + +int spi_find_bus_and_cs (int busnum,int cs,struct udevice **busp, +struct udevice **devp) +{ +struct udevice *bus, *dev; +int ret; + + ret =uclass_find_device_by_seq (UCLASS_SPI , busnum, false, &bus); +if (ret) +return ret; + ret =device_find_child_by_seq (bus, cs, false, &dev); +if (ret) +return ret; + *busp = bus; + *devp = dev; + +return ret; +} + +int spi_get_bus_and_cs (int busnum,int cs,int speed,int mode, +const char *drv_name,const char *dev_name, +struct udevice **devp,struct spi_slave **slavep) +{ +struct udevice *bus, *dev; +int ret; + + ret =uclass_get_device_by_seq (UCLASS_SPI , busnum, &bus); +if (ret) { +printf ("Invalid bus %d (err=%d)\n" , busnum, ret); +return ret; + } + ret =device_get_child_by_seq (bus, cs, &dev); + +/** +* If there is no such device, create one automatically. This means +* that we don't need a device tree node or platform data for the +* SPI flash chip - we will bind to the correct driver. +*/ +if (ret == -ENODEV && drv_name) { + ret =spi_bind_device (bus, cs, drv_name, dev_name, &dev); +if (ret) +return ret; + } +if (ret) { +printf ("Invalid chip select %d:%d (err=%d)\n" , busnum, cs, + ret); +return ret; + }drivers/spi/spi-uclass.c line 188
Quoted 10 lines
drivers/spi/spi-uclass.c new file +10
-0+ + ret =spi_set_speed_mode (bus, speed, mode); +if (ret) +return ret; + + *devp = bus; + *slavep =dev_get_parentdata (dev); + +return 0 ; +}I guess this is one of the replacement for
spi_setup_slave()in spi drivers, so how bus and cs member will populate to drivers.Usually bus and cs members from
spi_slavewill populate to drivers through "sf probe" sospi_cs_activatewill set the respective chip-select and along withreg_basein driver select through bus value.
Quoted 7 lines
spi_setup_slave(){
...zslave->base=get_zynq_spi_base(bus);
...
}How these will handling with dm?
The base should be in the device tree, or in platform data. The bus number should not be visible to the driver.
Quoted 99 lines
Quoted 83 lines
drivers/spi/spi-uclass.c new file +65
-0+ +/* Compatibility function - to be removed */ +struct spi_slave *spi_setup_slave_fdt (const void *blob,int node, +int bus_node) +{ +struct udevice *bus, *dev; +int ret; + + ret =uclass_get_device_by_of_offset (UCLASS_SPI , bus_node, &bus); +if (ret) +return NULL ; + ret =device_get_child_by_of_offset (bus, node, &dev); +if (ret) +return NULL ; +return dev_get_parentdata (dev); +} + +/* Compatibility function - to be removed */ +struct spi_slave *spi_setup_slave (unsigned int busnum,unsigned int cs, +unsigned int speed,unsigned int mode) +{ +struct spi_slave *slave; +struct udevice *dev; +int ret; + + ret =spi_get_bus_and_cs (busnum, cs, speed, mode,NULL ,0 , &dev, + &slave); +if (ret) +return NULL ; + +return slave; +} + +void spi_free_slave (struct spi_slave *slave) +{ +device_remove (slave->dev); + slave->dev =NULL ; +} + +int spi_ofdata_to_platdata (const void *blob,int node, +struct spi_slave *spi) +{ +int mode =0 ; + + spi->max_hz =fdtdec_get_int (blob, node,"spi-max-frequency" ,0 ); +if (fdtdec_get_bool (blob, node,"spi-cpol" )) + mode |=SPI_CPOL ; +if (fdtdec_get_bool (blob, node,"spi-cpha" )) + mode |=SPI_CPHA ; +if (fdtdec_get_bool (blob, node,"spi-cs-high" )) + mode |=SPI_CS_HIGH ; +if (fdtdec_get_bool (blob, node,"spi-half-duplex" )) + mode |=SPI_PREAMBLE ; + spi->mode = mode; + +return 0 ; +} + +UCLASS_DRIVER (spi) = { + .id =UCLASS_SPI , + .name ="spi" , + .post_bind = spi_post_bind, + .post_probe = spi_post_probe, + .per_device_auto_alloc_size =sizeof (struct dm_spi_bus ), +};include/dm/uclass-id.h +1
-0@@ -22,6 +22,7 @@ enum uclass_id {/* U-Boot uclasses start here */ UCLASS_GPIO ,/* Bank of general-purpose I/O pins */ UCLASS_SERIAL ,/* Serial UART */ +UCLASS_SPI ,/* SPI bus */ UCLASS_COUNT , UCLASS_INVALID = -1,include/spi.h +0
-0include/spi.h line 63
Quoted 10 lines
include/spi.h +7
-0@@ -54,11 +54,19 @@#define SPI_DEFAULT_WORDLEN 8 +#ifdef CONFIG_DM_SPI +struct dm_spi_bus { +uint max_hz; +}; + +#endif /* CONFIG_DM_SPI */ +No idea why this should require,
max_hzis already a part ofspi_slaveand handling of these like getting from dtb and process and fill on priv data should be part of individual drivers and fillingspi_slavemember should be part of uclass - based on my understanding.
Quoted 1 line
Any comments?
See above - the bus has its own maximum speed in many cases.
Quoted 173 lines
include/spi.h line 69
Quoted 8 lines
include/spi.h +1
-0/** * struct spi_slave - Representation of a SPI slave * * Drivers are expected to extend this with controller-specific data. * +* @dev: SPI slave device Missing comments for
max_hzand modeQuoted 162 lines
include/spi.h +132
-0* @bus: ID of the bus that the slave is attached to. * @cs: ID of the chip select connected to the slave. * @op_mode_rx: SPI RX operation mode. @@ -71,8 +79,14 @@* @flags: Indication of SPI flags. */ struct spi_slave { +#ifdef CONFIG_DM_SPI +struct udevice *dev;/* struct spi_slave is dev->parentdata */ +uint max_hz; +uint mode; +#else unsigned int bus;unsigned int cs; +#endif u8 op_mode_rx;u8 op_mode_tx;unsigned int wordlen;@@ -220,6 +234,8 @@ int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen);int spi_xfer (struct spi_slave *slave,unsigned int bitlen,const void *dout,void *din,unsigned long flags); +#ifndef CONFIG_DM_SPI +/** * Determine if a SPI chipselect is valid. * This function is provided by the board if the low-level SPI driver @@ -255,6 +271,7 @@ void spi_cs_deactivate(struct spi_slave *slave);* @hz: The transfer speed */ void spi_set_speed (struct spi_slave *slave,uint hz); +#endif /** * Write 8 bits, then read 8 bits. @@ -305,4 +322,127 @@ struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node,struct spi_slave *spi_base_setup_slave_fdt (const void *blob,int busnum,int node); +#ifdef CONFIG_DM_SPI + +/** +* struct struct dm_spi_ops - Driver model SPI operations +* +* The uclass interface is implemented by all SPI devices which use +* driver model. +*/ +struct dm_spi_ops { +/** +* Claim the bus and prepare it for communication. +* +* The device privided is the slave device. It's parent controller +* will be used to provide the communication. +* +* This must be called before doing any transfers with a SPI slave. It +* will enable and initialize any SPI hardware as necessary, and make +* sure that the SCK line is in the correct idle state. It is not +* allowed to claim the same bus for several slaves without releasing +* the bus in between. +* +* @device: The SPI slave +* +* Returns: 0 if the bus was claimed successfully, or a negative value +* if it wasn't. +*/ +int (*claim_bus)(struct udevice *device); + +/** +* Release the SPI bus +* +* This must be called once for every call to spi_claim_bus() after +* all transfers have finished. It may disable any SPI hardware as +* appropriate. +* +* @device: The SPI slave +*/ +int (*release_bus)(struct udevice *device); + +/** +* Set the word length for SPI transactions +* +* Set the word length (number of bits per word) for SPI transactions. +* +* @device: The SPI slave +* @wordlen: The number of bits in a word +* +* Returns: 0 on success, -ve on failure. +*/ +int (*set_wordlen)(struct udevice *deiuce,unsigned int wordlen); + +/** +* SPI transfer +* +* This writes "bitlen" bits out the SPI MOSI port and simultaneously +* clocks "bitlen" bits in the SPI MISO port. That's just the way SPI +* works. +* +* The source of the outgoing bits is the "dout" parameter and the +* destination of the input bits is the "din" parameter. Note that +* "dout" and "din" can point to the same memory location, in which +* case the input data overwrites the output data (since both are +* buffered by temporary variables, this is OK). +* +* spi_xfer() interface: +* @device: The SPI bus which will be sending/receiving the data. +* @slave: The slave device to communicate with +* @bitlen: How many bits to write and read. +* @dout: Pointer to a string of bits to send out. The bits are +* held in a byte array and are sent MSB first. +* @din: Pointer to a string of bits that will be filled in. +* @flags: A bitwise combination of SPI_XFER_* flags. +* +* Returns: 0 on success, not -1 on failure +*/ +int (*xfer)(struct udevice *device,struct udevice *slave, +unsigned int bitlen,const void *dout,void *din, +unsigned long flags); + +/** +* Set transfer speed. +* This sets a new speed to be applied for next spi_xfer(). +* @slave: The SPI slave +* @hz: The transfer speed +* @return 0 if OK, -ve on error +*/ +int (*set_speed)(struct udevice *device,uint hz); + +/** +* Set the SPI mode/flags +* +* It is unclear if we want to set speed and mode together instead +* of separately. +* +* @slave: The SPI slave +* @mode: Requested SPI mode (SPI_... flags) +* @return 0 if OK, -ve on error +*/ +int (*set_mode)(struct udevice *device,uint mode); +}; + +int spi_find_bus_and_cs (int busnum,int cs,struct udevice **busp, +struct udevice **devp); + +int spi_get_bus_and_cs (int busnum,int cs,int speed,int mode, +const char *drv_name,const char *dev_name, +struct udevice **devp,struct spi_slave **slavep); + +int spi_bind_device (struct udevice *bus,int cs,const char *drv_name, +const char *dev_name,struct udevice **slavep); + +int spi_ofdata_to_platdata (const void *blob,int node, +struct spi_slave *spi); + +struct sandbox_state ; +int sandbox_spi_get_emul (struct sandbox_state *state, +struct udevice *bus,struct udevice *slave, +struct udevice **emulp); + +/* Access the serial operations for a device */ +#define spi_get_ops (dev) ((struct dm_spi_ops *)(dev)->driver->ops) +#endif /* CONFIG_DM_SPI */ +
Quoted 8 lines
Quoted 4 lines
include/spi.h +0
-1#endif /* _SPI_H_ */ --2.0.0.526.g5318336
thanks!
--
Jagan.
Regards,
Simon
PATCH 7/25 dm: sandbox: Add a SPI emulation uclass
In reply to Simon GlassU-Boot includes a SPI emulation driver already but it is not explicit, and is hidden in the SPI flash code.
Conceptually with sandbox's SPI implementation we have a layer which creates SPI bus transitions and a layer which interprets them, currently only for SPI flash. The latter is actually an emulation, and it should be possible to add more than one emulation - not just SPI flash.
Add a SPI emulation uclass so that other emulations can be plugged in to support different types of emulated devices on difference buses/chip selects.
---
drivers/spi/Makefile | 1 + drivers/spi/spi-emul-uclass.c | 15 +++++++++++++++ include/dm/uclass-id.h | 1 + include/spi.h | 30 ++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 drivers/spi/spi-emul-uclass.c
drivers/spi/Makefile +1 -0
@@ -8,6 +8,7 @@ # There are many options which enable SPI, so make this library available ifdef CONFIG_DM_SPI obj-y += spi-uclass.o +obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o else obj-y += spi.o endif
drivers/spi/spi-emul-uclass.c new file +15 -0
@@ -0,0 +1,15 @@ +/* +* Copyright (c) 2014 Google, Inc +* +* SPDX-License-Identifier: GPL-2.0+ +*/ + +#include <common.h> +#include <dm.h> +#include <spi.h> +#include <spi_flash.h> + +UCLASS_DRIVER (spi_emul) = { + .id =UCLASS_SPI_EMUL , + .name ="spi_emul" , +};
include/dm/uclass-id.h +1 -0
@@ -18,6 +18,7 @@ enum uclass_id {UCLASS_TEST ,UCLASS_TEST_FDT ,UCLASS_TEST_BUS , +UCLASS_SPI_EMUL ,/* sandbox SPI device emulator */ /* U-Boot uclasses start here */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */
include/spi.h +30 -0
@@ -423,6 +423,35 @@ struct dm_spi_ops {int (*set_mode)(struct udevice *device,uint mode); }; +struct dm_spi_emul_ops { +/** +* SPI transfer +* +* This writes "bitlen" bits out the SPI MOSI port and simultaneously +* clocks "bitlen" bits in the SPI MISO port. That's just the way SPI +* works. Here the device is a slave. +* +* The source of the outgoing bits is the "dout" parameter and the +* destination of the input bits is the "din" parameter. Note that +* "dout" and "din" can point to the same memory location, in which +* case the input data overwrites the output data (since both are +* buffered by temporary variables, this is OK). +* +* spi_xfer() interface: +* @slave: The SPI slave which will be sending/receiving the data. +* @bitlen: How many bits to write and read. +* @dout: Pointer to a string of bits sent to the device. The +* bits are held in a byte array and are sent MSB first. +* @din: Pointer to a string of bits that will be sent back to +* the master. +* @flags: A bitwise combination of SPI_XFER_* flags. +* +* Returns: 0 on success, not -1 on failure +*/ +int (*xfer)(struct udevice *device,unsigned int bitlen, +const void *dout,void *din,unsigned long flags); +}; +int spi_find_bus_and_cs (int busnum,int cs,struct udevice **busp,struct udevice **devp);
@@ -443,6 +472,7 @@ int sandbox_spi_get_emul(struct sandbox_state *state,/* Access the serial operations for a device */ #define spi_get_ops (dev) ((struct dm_spi_ops *)(dev)->driver->ops) +#define spi_emul_get_ops (dev) ((struct dm_spi_emul_ops *)(dev)->driver->ops)#endif /* CONFIG_DM_SPI */ #endif /* _SPI_H_ */
Signature · 1 line
2.0.0.526.g5318336
PATCH 8/25 dm: Remove spi_init() from board_r.c when using driver model
In reply to Simon GlassDriver model does its own init, so we don't need this.
There is also a call in board_f.c but it is only enabled by CONFIG_HARD_SPI. It is easy enough to disable that option when converting boards which use it to driver model.
---
common/board_r.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
common/board_r.c +1 -1
@@ -354,7 +354,7 @@ static int initr_flash(void) }#endif -#ifdef CONFIG_PPC +#if defined(CONFIG_PPC ) && !defined(CONFIG_DM_SPI )static int initr_spi (void ) {/* PPC does this here */
Signature · 1 line
2.0.0.526.g5318336
PATCH 9/25 dm: Add spi.h header to a few files
In reply to Simon GlassSome files are using SPI functions but not explitly including the SPI header file. Fix this, since driver model needs it.
---
common/env_sf.c | 1 + drivers/mtd/spi/sf_params.c | 1 + 2 files changed, 2 insertions(+)
common/env_sf.c +1 -0
@@ -12,6 +12,7 @@#include <common.h> #include <environment.h> #include <malloc.h> +#include <spi.h> #include <spi_flash.h> #include <search.h> #include <errno.h>
drivers/mtd/spi/sf_params.c +1 -0
@@ -7,6 +7,7 @@*/ #include <common.h> +#include <spi.h> #include <spi_flash.h> #include "sf_internal.h"
Signature · 1 line
2.0.0.526.g5318336
PATCH 10/25 dm: spi: Adjust cmd_spi to work with driver model
In reply to Simon GlassDriver model uses a different way to find the SPI bus and slave from the numbered devices given on the command line. Adjust the code to suit.
---
common/cmd_spi.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-)
common/cmd_spi.c +27 -7
@@ -11,6 +11,7 @@#include <common.h> #include <command.h> +#include <dm.h> #include <errno.h> #include <spi.h>
@@ -42,19 +43,35 @@ static uchar din[MAX_SPI_BYTES];static int do_spi_xfer (int bus,int cs) {struct spi_slave *slave; -int rcode =0 ; +int ret =0 ; +#ifdef CONFIG_DM_SPI +struct udevice *dev; + + ret =spi_get_bus_and_cs (bus, cs,1000000 , mode,NULL ,0 , &dev, + &slave); +if (ret) +return ret; +#else slave =spi_setup_slave (bus, cs,1000000 , mode);if (!slave) {printf ("Invalid device %d:%d\n" , bus, cs);return -EINVAL ; } +#endif -spi_claim_bus (slave); -if (spi_xfer (slave, bitlen, dout, din, -SPI_XFER_BEGIN |SPI_XFER_END ) !=0 ) { -printf ("Error during SPI transaction\n" ); - rcode = -EIO ; + ret =spi_claim_bus (slave); +if (ret) + goto done; + ret =spi_xfer (slave, bitlen, dout, din, +SPI_XFER_BEGIN |SPI_XFER_END ); +#ifndef CONFIG_DM_SPI +/* We don't get an error code in this case */ +if (ret) + ret = -EIO ; +#endif +if (ret) { +printf ("Error %d during SPI transaction\n" , ret); }else {int j;
@@ -62,10 +79,13 @@ static int do_spi_xfer(int bus, int cs)printf ("%02X" , din[j]);printf ("\n" ); } +done:spi_release_bus (slave); +#ifndef CONFIG_DM_SPI spi_free_slave (slave); +#endif -return rcode; +return ret; }/*
Signature · 1 line
2.0.0.526.g5318336
PATCH 11/25 dm: sandbox: spi: Move to driver model
In reply to Simon GlassAdjust the sandbox SPI driver to support driver model and move sandbox over to driver model for SPI.
---
arch/sandbox/include/asm/state.h | 1 + drivers/spi/sandbox_spi.c | 198 ++++++++++++++------------------------- include/configs/sandbox.h | 3 +- 3 files changed, 74 insertions(+), 128 deletions(-)
arch/sandbox/include/asm/state.h +1 -0
@@ -43,6 +43,7 @@ enum state_terminal_raw {struct sandbox_spi_info {const char *spec;const struct sandbox_spi_emu_ops *ops; +struct udevice *emul; };/* The complete state of the test system */
drivers/spi/sandbox_spi.c +72 -126
252 lines not on this page · raw message
include/configs/sandbox.h +1 -2
@@ -100,8 +100,7 @@#define CONFIG_CMD_SF_TEST #define CONFIG_CMD_SPI #define CONFIG_SPI_FLASH -#define CONFIG_OF_SPI -#define CONFIG_OF_SPI_FLASH +#define CONFIG_DM_SPI #define CONFIG_SPI_FLASH_ATMEL #define CONFIG_SPI_FLASH_EON #define CONFIG_SPI_FLASH_GIGADEVICE
Signature · 1 line
2.0.0.526.g5318336
PATCH 12/25 dm: spi: Add documentation on how to convert over SPI drivers
In reply to Simon GlassThis README is intended to help maintainers move their SPI drivers over to driver model. It works through the required steps with an example.
---
doc/driver-model/spi-howto.txt | 570 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 570 insertions(+) create mode 100644 doc/driver-model/spi-howto.txt
doc/driver-model/spi-howto.txt new file +570 -0
@@ -0,0 +1,570 @@ +How to port a SPI driver to driver model +======================================== + +Here is a rough step-by-step guide. It is based around converting the +exynos SPI driver to driver model (DM) and the example code is based +around U-Boot v2014.04 (commit dda0dbf). + +It is quite long since it includes actual code examples. + +Before driver model, SPI drivers have their own private structure which +contains 'struct spi_slave'. With driver model, 'struct spi_slave' still +exists, but now it is 'per-child data' for the SPI bus. Each child of the +SPI bus is a SPI slave. The information that was stored in the +driver-specific slave structure can now be put in private data for the +SPI bus. + +For example, struct tegra_spi_slave looks like this: + +struct tegra_spi_slave { + struct spi_slave slave; + struct tegra_spi_ctrl *ctrl; +}; + +In this case 'slave' will be in per-child data, and 'ctrl' will be in the +SPI bus' private data. + + +0. How long does this take? + +Around 2.5 hours, including some allowance for figuring out the driver +model bits. + + +1. Enable driver mode for SPI and SPI flash + +Add these to your board config: + +#define CONFIG_DM_SPI +#define CONFIG_DM_SPI_FLASH + + +2. Add the skeleton + +Put this code at the bottom of your existing driver file: + +struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + return NULL; +} + +struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node, + int spi_node) +{ + return NULL; +} + +static int exynos_spi_ofdata_to_platdata(struct udevice *dev) +{ + return -ENODEV; +} + +static int exynos_spi_probe(struct udevice *dev) +{ + return -ENODEV; +} + +static int exynos_spi_remove(struct udevice *dev) +{ + return -ENODEV; +} + +static int exynos_spi_claim_bus(struct udevice *dev) +{ + + return -ENODEV; +} + +static int exynos_spi_release_bus(struct udevice *dev) +{ + + return -ENODEV; +} + +static int exynos_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + + return -ENODEV; +} + +static int exynos_spi_set_speed(struct udevice *dev, uint speed) +{ + return -ENODEV; +} + +static int exynos_spi_set_mode(struct udevice *dev, uint mode) +{ + return -ENODEV; +} + +static const struct dm_spi_ops exynos_spi_ops = { + .claim_bus = exynos_spi_claim_bus, + .release_bus = exynos_spi_release_bus, + .xfer = exynos_spi_xfer, + .set_speed = exynos_spi_set_speed, + .set_mode = exynos_spi_set_mode, +}; + +static const struct udevice_id exynos_spi_ids[] = { + { .compatible = "samsung,exynos-spi" }, + { } +}; + +U_BOOT_DRIVER(exynos_spi) = { + .name = "exynos_spi", + .id = UCLASS_SPI, + .of_match = exynos_spi_ids, + .ops = &exynos_spi_ops, + .ofdata_to_platdata = exynos_spi_ofdata_to_platdata, + .probe = exynos_spi_probe, + .remove = exynos_spi_remove +}; + + +3. Replace 'exynos' in the above code with your driver name + + +4. #ifdef out all of the code in your driver except for the above + +This will allow you to get it building, which means you can work +incrementally. Since all the methods return an error initially, there is +less chance that you will accidentally leave something in. + +Also, even though your conversion is basically a rewrite, it might help +reviewers if you leave functions in the same place in the file, +particularly for large drivers. + + +5. Add some includes + +Add these includes to your driver: + +#include <dm.h> +#include <errno.h> + + +6. Build + +At this point you should be able to build U-Boot for your board with the +empty SPI driver. You still have empty methods in yur driver, but we will +write these one by one. + +If you have spi_init() functions or the like that are called from your +board then the build will fail. Remove these calls and make a note of the +init that needs to be done. + + +7. Set up your platform data structure + +This will hold the information your driver needs to operate, like its +hardware address or maximum frequency. + +You may already have a struct like this, or you may need to create one +from some of the #defines or global variables in the driver. + +Note that this information is not the run-time information. It should not +include state that changes. It should be fixed throughout the life of +U-Boot. Run-time information comes later. + +Here is what was in the exynos spi driver: + +struct spi_bus { + enum periph_id periph_id; + s32 frequency; /* Default clock frequency, -1 for none */ + struct exynos_spi *regs; + int inited; /* 1 if this bus is ready for use */ + int node; + uint deactivate_delay_us; /* Delay to wait after deactivate */ +}; + +Of these, inited is handled by DM and node is the device tree node, which +DM tells you. The name is not quite right. So in this case we would use: + +struct exynos_spi_platdata { + enum periph_id periph_id; + s32 frequency; /* Default clock frequency, -1 for none */ + struct exynos_spi *regs; + uint deactivate_delay_us; /* Delay to wait after deactivate */ +}; + + +8a. Write ofdata_to_platdata() [for device tree only] + +This method will convert information in the device tree node into a C +structure in your driver (called platform data). If you are not using +device tree, go to 8b. + +DM will automatically allocate the struct for us when we are using device +tree, but we need to tell it the size: + +U_BOOT_DRIVER(spi_exynos) = { +... + .platdata_auto_alloc_size = sizeof(struct exynos_spi_platdata), + + +Here is a sample function. It gets a pointer to the platform data and +fills in the fields from device tree. + +static int exynos_spi_ofdata_to_platdata(struct udevice *dev) +{ + struct exynos_spi_platdata *plat = dev->platdata; + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + + plat->regs = (struct exynos_spi *)fdtdec_get_addr(blob, node, "reg"); + plat->periph_id = pinmux_decode_periph_id(blob, node); + + if (plat->periph_id == PERIPH_ID_NONE) { + debug("%s: Invalid peripheral ID %d\n", __func__, + plat->periph_id); + return -FDT_ERR_NOTFOUND; + } + + /* Use 500KHz as a suitable default */ + plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", + 500000); + plat->deactivate_delay_us = fdtdec_get_int(blob, node, + "spi-deactivate-delay", 0); + + return 0; +} + + +8b. Add the platform data [non-device-tree only] + +Specify this data in a U_BOOT_DEVICE() declaration in your board file: + +struct exynos_spi_platdata platdata_spi0 = { + .periph_id = ... + .frequency = ... + .regs = ... + .deactivate_delay_us = ... +}; + +U_BOOT_DEVICE(board_spi0) = { + .name = "exynos_spi", + .platdata = &platdata_spi0, +}; + +You will unfortunately need to put the struct into a header file in this +case so that your board file can use it. + + +9. Add the device private data + +Most devices have some private data which they use to keep track of things +while active. This is the run-time information and needs to be stored in +a structure. There is probably a structure in the driver that includes a +'struct spi_slave', so you can use that. + +struct exynos_spi_slave { + struct spi_slave slave; + struct exynos_spi *regs; + unsigned int freq; /* Default frequency */ + unsigned int mode; + enum periph_id periph_id; /* Peripheral ID for this device */ + unsigned int fifo_size; + int skip_preamble; + struct spi_bus *bus; /* Pointer to our SPI bus info */ + ulong last_transaction_us; /* Time of last transaction end */ +}; + + +We should rename this to make its purpose more obvious, and get rid of +the slave structure, so we have: + +struct exynos_spi_priv { + struct exynos_spi *regs; + unsigned int freq; /* Default frequency */ + unsigned int mode; + enum periph_id periph_id; /* Peripheral ID for this device */ + unsigned int fifo_size; + int skip_preamble; + ulong last_transaction_us; /* Time of last transaction end */ +}; + + +DM can auto-allocate this also: + +U_BOOT_DRIVER(spi_exynos) = { +... + .priv_auto_alloc_size = sizeof(struct exynos_spi_priv), + + +Note that this is created before the probe() method is called, and destroyed +after the remove() method is called. It will be zeroed when the probe() +method is called. + + +10. Add the probe() and remove() methods + +Note: It's a good idea to build repeatedly as you are working, to avoid a +huge amount of work getting things compiling at the end. + +The probe() method is supposed to set up the hardware. U-Boot used to use +spi_setup_slave() to do this. So take a look at this function and see +what you can copy out to set things up. + + +static int exynos_spi_probe(struct udevice *dev) +{ + struct exynos_spi_platdata *pdata = dev_get_platdata(dev); + struct exynos_spi_priv *priv = dev_get_priv(dev); + + if (pdata->periph_id == PERIPH_ID_SPI1 || + pdata->periph_id == PERIPH_ID_SPI2) + priv->fifo_size = 64; + else + priv->fifo_size = 256; + + priv->skip_preamble = 0; + priv->last_transaction_us = timer_get_us(); + priv->freq = pdata->frequency; + priv->periph_id = pdata->periph_id; + + return 0; +} + +This implementation doesn't actually touch the hardware, which is somewhat +unusual for a driver. In this case we will do that when the device is +claimed by something that wants to use the SPI bus. + +For remove() we could shut down the clocks, but in this case there is +nothing to do. DM frees any memory that it allocated, so we can just +remove exynos_spi_remove() and its reference in U_BOOT_DRIVER. + + +11. Implement set_speed() + +This should set up clocks so that the SPI bus is running at the right +speed. With the old API spi_claim_bus() would normally do this and several +of the following functions, so let's look at that function: + +int spi_claim_bus(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + struct exynos_spi *regs = spi_slave->regs; + u32 reg = 0; + int ret; + + ret = set_spi_clk(spi_slave->periph_id, + spi_slave->freq); + if (ret < 0) { + debug("%s: Failed to setup spi clock\n", __func__); + return ret; + } + + exynos_pinmux_config(spi_slave->periph_id, PINMUX_FLAG_NONE); + + spi_flush_fifo(slave); + + reg = readl(®s->ch_cfg); + reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); + + if (spi_slave->mode & SPI_CPHA) + reg |= SPI_CH_CPHA_B; + + if (spi_slave->mode & SPI_CPOL) + reg |= SPI_CH_CPOL_L; + + writel(reg, ®s->ch_cfg); + writel(SPI_FB_DELAY_180, ®s->fb_clk); + + return 0; +} + + +It sets up the speed, mode, pinmux, feedback delay and clears the FIFOs. +With DM these will happen in separate methods. + + +Here is an example for the speed part: + +static int exynos_spi_set_speed(struct udevice *dev, uint speed) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + int ret; + + ret = set_spi_clk(priv->periph_id, speed); + if (ret) + return ret; + priv->freq = speed; + + return 0; +} + + +12. Implement set_mode() + +This should adjust the SPI mode (polarity, etc.). Again this code probably +comes from the old spi_claim_bus(). Here is an example: + + +static int exynos_spi_set_mode(struct udevice *dev, uint mode) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + uint32_t reg; + + reg = readl(&priv->regs->ch_cfg); + reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); + + if (mode & SPI_CPHA) + reg |= SPI_CH_CPHA_B; + + if (mode & SPI_CPOL) + reg |= SPI_CH_CPOL_L; + + writel(reg, &priv->regs->ch_cfg); + + return 0; +} + + +13. Implement claim_bus() + +This is where a client wants to make use of the bus, so claims it first. +At this point we need to make sure every is set up ready for data transfer. + +Here again we look at the old claim function and see some code that is +needed. It is anything unrelated to speed and mode: + +static int exynos_spi_claim_bus(struct udevice *dev) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + + exynos_pinmux_config(priv->periph_id, PINMUX_FLAG_NONE); + spi_flush_fifo(priv->regs); + + writel(SPI_FB_DELAY_180, &priv->regs->fb_clk); + + return 0; +} + +The spi_flush_fifo() function is in the removed part of the code, so we +need to expose it again (perhaps with an #endif before it and '#if 0' +after it). It only needs accesss to priv->regs which is why we have +passed that in: + +/** + * Flush spi tx, rx fifos and reset the SPI controller + * + * @param regs Pointer to SPI registers + */ +static void spi_flush_fifo(struct exynos_spi *regs) +{ + clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + setbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON); +} + + +14. Implement release_bus() + +This releases the bus - in our example the old code in spi_release_bus() +is a call to spi_flush_fifo, so we add: + +static int exynos_spi_release_bus(struct udevice *dev) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + + spi_flush_fifo(priv->regs); + + return 0; +} + + +15. Implement xfer() + +This is the final method that we need to create, and it is where all the +work happens. The method parameters are the same as the old spi_xfer() with +the addition of a 'struct udevice' so conversion is pretty easy. Start +by copying the contents of spi_xfer() to your new xfer() mthod and proceed +from there. + +If (flags & SPI_XFER_BEGIN) is non-zero then xfer() normally calls an +activate function, something like this: + +void spi_cs_activate(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + + /* If it's too soon to do another transaction, wait */ + if (spi_slave->bus->deactivate_delay_us && + spi_slave->last_transaction_us) { + ulong delay_us; /* The delay completed so far */ + delay_us = timer_get_us() - spi_slave->last_transaction_us; + if (delay_us < spi_slave->bus->deactivate_delay_us) + udelay(spi_slave->bus->deactivate_delay_us - delay_us); + } + + clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Activate CS, bus %d\n", spi_slave->slave.bus); + spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE; +} + +The new version looks like this: + +static void spi_cs_activate(struct udevice *dev) +{ + struct exynos_spi_platdata *pdata = dev_get_platdata(dev); + struct exynos_spi_priv *priv = dev_get_priv(dev); + + /* If it's too soon to do another transaction, wait */ + if (pdata->deactivate_delay_us && + priv->last_transaction_us) { + ulong delay_us; /* The delay completed so far */ + delay_us = timer_get_us() - priv->last_transaction_us; + if (delay_us < pdata->deactivate_delay_us) + udelay(pdata->deactivate_delay_us - delay_us); + } + + clrbits_le32(&priv->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Activate CS, bus '%s'\n", dev->name); + priv->skip_preamble = priv->mode & SPI_PREAMBLE; +} + +All we have really done here is change the pointers and print the device name +instead of the bus number. Other local static functions can be treated in +the same way. + + +16. Set up the per-child data and child_pre_probe() method + +To minimise the pain and complexity of the SPI subsystem while the driver +model change-over is in place, struct spi_slave is used to reference a +SPI bus slave, even though that slave is actually a struct udevice. In fact +struct spi_slave is the device's child data. We need to make sure this is +set up. It is possible to allocate more space that struct spi_slave needs, +but this is the minimum. + +U_BOOT_DRIVER(exynos_spi) = { +... + .per_child_auto_alloc_size = sizeof(struct spi_slave), + .child_pre_probe = exynos_spi_child_pre_probe, +} + +int exynos_spi_child_pre_probe(struct udevice *dev) +{ + struct spi_slave *slave = dev_get_parentdata(dev); + + return spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, slave); +} + + +Here our child_pre_probe() method merely calls a standard method to convert +device tree data to that used in the slave. + + +17. Test it + +Now that you have the code written and it compiles, try testing it using +the 'sf test' command. You may need to enable CONFIG_CMD_SF_TEST for your +board. + + +18. Prepare patches and send them to the mailing lists + +You can use 'tools/patman/patman' to prepare, check and send patches for +your work. See the README for details.
Signature · 1 line
2.0.0.526.g5318336
PATCH 12/25 dm: spi: Add documentation on how to convert over SPI drivers
Quoted 21 lines · Simon Glass
This README is intended to help maintainers move their SPI drivers over to driver model. It works through the required steps with an example.
Signed-off-by Simon Glass <[email protected]>---
doc/driver-model/spi-howto.txt | 570 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 570 insertions(+) create mode 100644 doc/driver-model/spi-howto.txtdoc/driver-model/spi-howto.txt new file +5
-0@@ -0,0 +1,570 @@ +How to port a SPI driver to driver model +======================================== + +Here is a rough step-by-step guide. It is based around converting the +exynos SPI driver to driver model (DM) and the example code is based
doc/driver-model/spi-howto.txt line 15
Quoted 10 lines
doc/driver-model/spi-howto.txt new file +10
-0+around U-Boot v2014.04 (commit dda0dbf). + +It is quite long since it includes actual code examples. + +Before driver model, SPI drivers have their own private structure which +contains 'struct spi_slave'. With driver model, 'struct spi_slave' still +exists, but now it is 'per-child data' for the SPI bus. Each child of the +SPI bus is a SPI slave. The information that was stored in the +driver-specific slave structure can now be put in private data for the +SPI bus.
Do you think spi_slave still require, I guess It's needed as some slave members to cs, bus are used to control the driver.
But any specific reason for removing spi_slave from exynos_spi.c?
Quoted 97 lines
doc/driver-model/spi-howto.txt new file +97
-0+ +For example, struct tegra_spi_slave looks like this: + +struct tegra_spi_slave { + struct spi_slave slave; + struct tegra_spi_ctrl *ctrl; +}; + +In this case 'slave' will be in per-child data, and 'ctrl' will be in the +SPI bus' private data. + + +0. How long does this take? + +Around 2.5 hours, including some allowance for figuring out the driver +model bits. + + +1. Enable driver mode for SPI and SPI flash + +Add these to your board config: + +#define CONFIG_DM_SPI +#define CONFIG_DM_SPI_FLASH + + +2. Add the skeleton + +Put this code at the bottom of your existing driver file: + +struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + return NULL; +} + +struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node, + int spi_node) +{ + return NULL; +} + +static int exynos_spi_ofdata_to_platdata(struct udevice *dev) +{ + return -ENODEV; +} + +static int exynos_spi_probe(struct udevice *dev) +{ + return -ENODEV; +} + +static int exynos_spi_remove(struct udevice *dev) +{ + return -ENODEV; +} + +static int exynos_spi_claim_bus(struct udevice *dev) +{ + + return -ENODEV; +} + +static int exynos_spi_release_bus(struct udevice *dev) +{ + + return -ENODEV; +} + +static int exynos_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + + return -ENODEV; +} + +static int exynos_spi_set_speed(struct udevice *dev, uint speed) +{ + return -ENODEV; +} + +static int exynos_spi_set_mode(struct udevice *dev, uint mode) +{ + return -ENODEV; +} + +static const struct dm_spi_ops exynos_spi_ops = { + .claim_bus = exynos_spi_claim_bus, + .release_bus = exynos_spi_release_bus, + .xfer = exynos_spi_xfer, + .set_speed = exynos_spi_set_speed, + .set_mode = exynos_spi_set_mode, +}; + +static const struct udevice_id exynos_spi_ids[] = { + { .compatible = "samsung,exynos-spi" }, + { }
doc/driver-model/spi-howto.txt line 122
Quoted 10 lines
doc/driver-model/spi-howto.txt new file +10
-0+}; + +U_BOOT_DRIVER(exynos_spi) = { + .name = "exynos_spi", + .id = UCLASS_SPI, + .of_match = exynos_spi_ids, + .ops = &exynos_spi_ops, + .ofdata_to_platdata = exynos_spi_ofdata_to_platdata, + .probe = exynos_spi_probe, + .remove = exynos_spi_remove
comma missing - remove is necessary?
Quoted 19 lines
doc/driver-model/spi-howto.txt new file +19
-0+}; + + +3. Replace 'exynos' in the above code with your driver name + + +4. #ifdef out all of the code in your driver except for the above + +This will allow you to get it building, which means you can work +incrementally. Since all the methods return an error initially, there is +less chance that you will accidentally leave something in. + +Also, even though your conversion is basically a rewrite, it might help +reviewers if you leave functions in the same place in the file, +particularly for large drivers. + + +5. Add some includes +
doc/driver-model/spi-howto.txt line 151
Quoted 10 lines
doc/driver-model/spi-howto.txt new file +10
-0+Add these includes to your driver: + +#include <dm.h> +#include <errno.h> + + +6. Build + +At this point you should be able to build U-Boot for your board with the +empty SPI driver. You still have empty methods in yur driver, but we will
typo your instead of yur
Quoted 322 lines
doc/driver-model/spi-howto.txt new file +322
-0+write these one by one. + +If you have spi_init() functions or the like that are called from your +board then the build will fail. Remove these calls and make a note of the +init that needs to be done. + + +7. Set up your platform data structure + +This will hold the information your driver needs to operate, like its +hardware address or maximum frequency. + +You may already have a struct like this, or you may need to create one +from some of the #defines or global variables in the driver. + +Note that this information is not the run-time information. It should not +include state that changes. It should be fixed throughout the life of +U-Boot. Run-time information comes later. + +Here is what was in the exynos spi driver: + +struct spi_bus { + enum periph_id periph_id; + s32 frequency; /* Default clock frequency, -1 for none */ + struct exynos_spi *regs; + int inited; /* 1 if this bus is ready for use */ + int node; + uint deactivate_delay_us; /* Delay to wait after deactivate */ +}; + +Of these, inited is handled by DM and node is the device tree node, which +DM tells you. The name is not quite right. So in this case we would use: + +struct exynos_spi_platdata { + enum periph_id periph_id; + s32 frequency; /* Default clock frequency, -1 for none */ + struct exynos_spi *regs; + uint deactivate_delay_us; /* Delay to wait after deactivate */ +}; + + +8a. Write ofdata_to_platdata() [for device tree only] + +This method will convert information in the device tree node into a C +structure in your driver (called platform data). If you are not using +device tree, go to 8b. + +DM will automatically allocate the struct for us when we are using device +tree, but we need to tell it the size: + +U_BOOT_DRIVER(spi_exynos) = { +... + .platdata_auto_alloc_size = sizeof(struct exynos_spi_platdata), + + +Here is a sample function. It gets a pointer to the platform data and +fills in the fields from device tree. + +static int exynos_spi_ofdata_to_platdata(struct udevice *dev) +{ + struct exynos_spi_platdata *plat = dev->platdata; + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + + plat->regs = (struct exynos_spi *)fdtdec_get_addr(blob, node, "reg"); + plat->periph_id = pinmux_decode_periph_id(blob, node); + + if (plat->periph_id == PERIPH_ID_NONE) { + debug("%s: Invalid peripheral ID %d\n", __func__, + plat->periph_id); + return -FDT_ERR_NOTFOUND; + } + + /* Use 500KHz as a suitable default */ + plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", + 500000); + plat->deactivate_delay_us = fdtdec_get_int(blob, node, + "spi-deactivate-delay", 0); + + return 0; +} + + +8b. Add the platform data [non-device-tree only] + +Specify this data in a U_BOOT_DEVICE() declaration in your board file: + +struct exynos_spi_platdata platdata_spi0 = { + .periph_id = ... + .frequency = ... + .regs = ... + .deactivate_delay_us = ... +}; + +U_BOOT_DEVICE(board_spi0) = { + .name = "exynos_spi", + .platdata = &platdata_spi0, +}; + +You will unfortunately need to put the struct into a header file in this +case so that your board file can use it. + + +9. Add the device private data + +Most devices have some private data which they use to keep track of things +while active. This is the run-time information and needs to be stored in +a structure. There is probably a structure in the driver that includes a +'struct spi_slave', so you can use that. + +struct exynos_spi_slave { + struct spi_slave slave; + struct exynos_spi *regs; + unsigned int freq; /* Default frequency */ + unsigned int mode; + enum periph_id periph_id; /* Peripheral ID for this device */ + unsigned int fifo_size; + int skip_preamble; + struct spi_bus *bus; /* Pointer to our SPI bus info */ + ulong last_transaction_us; /* Time of last transaction end */ +}; + + +We should rename this to make its purpose more obvious, and get rid of +the slave structure, so we have: + +struct exynos_spi_priv { + struct exynos_spi *regs; + unsigned int freq; /* Default frequency */ + unsigned int mode; + enum periph_id periph_id; /* Peripheral ID for this device */ + unsigned int fifo_size; + int skip_preamble; + ulong last_transaction_us; /* Time of last transaction end */ +}; + + +DM can auto-allocate this also: + +U_BOOT_DRIVER(spi_exynos) = { +... + .priv_auto_alloc_size = sizeof(struct exynos_spi_priv), + + +Note that this is created before the probe() method is called, and destroyed +after the remove() method is called. It will be zeroed when the probe() +method is called. + + +10. Add the probe() and remove() methods + +Note: It's a good idea to build repeatedly as you are working, to avoid a +huge amount of work getting things compiling at the end. + +The probe() method is supposed to set up the hardware. U-Boot used to use +spi_setup_slave() to do this. So take a look at this function and see +what you can copy out to set things up. + + +static int exynos_spi_probe(struct udevice *dev) +{ + struct exynos_spi_platdata *pdata = dev_get_platdata(dev); + struct exynos_spi_priv *priv = dev_get_priv(dev); + + if (pdata->periph_id == PERIPH_ID_SPI1 || + pdata->periph_id == PERIPH_ID_SPI2) + priv->fifo_size = 64; + else + priv->fifo_size = 256; + + priv->skip_preamble = 0; + priv->last_transaction_us = timer_get_us(); + priv->freq = pdata->frequency; + priv->periph_id = pdata->periph_id; + + return 0; +} + +This implementation doesn't actually touch the hardware, which is somewhat +unusual for a driver. In this case we will do that when the device is +claimed by something that wants to use the SPI bus. + +For remove() we could shut down the clocks, but in this case there is +nothing to do. DM frees any memory that it allocated, so we can just +remove exynos_spi_remove() and its reference in U_BOOT_DRIVER. + + +11. Implement set_speed() + +This should set up clocks so that the SPI bus is running at the right +speed. With the old API spi_claim_bus() would normally do this and several +of the following functions, so let's look at that function: + +int spi_claim_bus(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + struct exynos_spi *regs = spi_slave->regs; + u32 reg = 0; + int ret; + + ret = set_spi_clk(spi_slave->periph_id, + spi_slave->freq); + if (ret < 0) { + debug("%s: Failed to setup spi clock\n", __func__); + return ret; + } + + exynos_pinmux_config(spi_slave->periph_id, PINMUX_FLAG_NONE); + + spi_flush_fifo(slave); + + reg = readl(®s->ch_cfg); + reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); + + if (spi_slave->mode & SPI_CPHA) + reg |= SPI_CH_CPHA_B; + + if (spi_slave->mode & SPI_CPOL) + reg |= SPI_CH_CPOL_L; + + writel(reg, ®s->ch_cfg); + writel(SPI_FB_DELAY_180, ®s->fb_clk); + + return 0; +} + + +It sets up the speed, mode, pinmux, feedback delay and clears the FIFOs. +With DM these will happen in separate methods. + + +Here is an example for the speed part: + +static int exynos_spi_set_speed(struct udevice *dev, uint speed) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + int ret; + + ret = set_spi_clk(priv->periph_id, speed); + if (ret) + return ret; + priv->freq = speed; + + return 0; +} + + +12. Implement set_mode() + +This should adjust the SPI mode (polarity, etc.). Again this code probably +comes from the old spi_claim_bus(). Here is an example: + + +static int exynos_spi_set_mode(struct udevice *dev, uint mode) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + uint32_t reg; + + reg = readl(&priv->regs->ch_cfg); + reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); + + if (mode & SPI_CPHA) + reg |= SPI_CH_CPHA_B; + + if (mode & SPI_CPOL) + reg |= SPI_CH_CPOL_L; + + writel(reg, &priv->regs->ch_cfg); + + return 0; +} + + +13. Implement claim_bus() + +This is where a client wants to make use of the bus, so claims it first. +At this point we need to make sure every is set up ready for data transfer. + +Here again we look at the old claim function and see some code that is +needed. It is anything unrelated to speed and mode: + +static int exynos_spi_claim_bus(struct udevice *dev) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + + exynos_pinmux_config(priv->periph_id, PINMUX_FLAG_NONE); + spi_flush_fifo(priv->regs); + + writel(SPI_FB_DELAY_180, &priv->regs->fb_clk); + + return 0; +} + +The spi_flush_fifo() function is in the removed part of the code, so we +need to expose it again (perhaps with an #endif before it and '#if 0' +after it). It only needs accesss to priv->regs which is why we have +passed that in: + +/** + * Flush spi tx, rx fifos and reset the SPI controller + * + * @param regs Pointer to SPI registers + */ +static void spi_flush_fifo(struct exynos_spi *regs) +{ + clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + setbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON); +} + + +14. Implement release_bus() + +This releases the bus - in our example the old code in spi_release_bus() +is a call to spi_flush_fifo, so we add: + +static int exynos_spi_release_bus(struct udevice *dev) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + + spi_flush_fifo(priv->regs); +
doc/driver-model/spi-howto.txt line 483
Quoted 10 lines
doc/driver-model/spi-howto.txt new file +10
-0+ return 0; +} + + +15. Implement xfer() + +This is the final method that we need to create, and it is where all the +work happens. The method parameters are the same as the old spi_xfer() with +the addition of a 'struct udevice' so conversion is pretty easy. Start +by copying the contents of spi_xfer() to your new xfer() mthod and proceed
typo - method
Quoted 42 lines
doc/driver-model/spi-howto.txt new file +42
-0+from there. + +If (flags & SPI_XFER_BEGIN) is non-zero then xfer() normally calls an +activate function, something like this: + +void spi_cs_activate(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + + /* If it's too soon to do another transaction, wait */ + if (spi_slave->bus->deactivate_delay_us && + spi_slave->last_transaction_us) { + ulong delay_us; /* The delay completed so far */ + delay_us = timer_get_us() - spi_slave->last_transaction_us; + if (delay_us < spi_slave->bus->deactivate_delay_us) + udelay(spi_slave->bus->deactivate_delay_us - delay_us); + } + + clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Activate CS, bus %d\n", spi_slave->slave.bus); + spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE; +} + +The new version looks like this: + +static void spi_cs_activate(struct udevice *dev) +{ + struct exynos_spi_platdata *pdata = dev_get_platdata(dev); + struct exynos_spi_priv *priv = dev_get_priv(dev); + + /* If it's too soon to do another transaction, wait */ + if (pdata->deactivate_delay_us && + priv->last_transaction_us) { + ulong delay_us; /* The delay completed so far */ + delay_us = timer_get_us() - priv->last_transaction_us; + if (delay_us < pdata->deactivate_delay_us) + udelay(pdata->deactivate_delay_us - delay_us); + } + + clrbits_le32(&priv->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Activate CS, bus '%s'\n", dev->name); + priv->skip_preamble = priv->mode & SPI_PREAMBLE;
doc/driver-model/spi-howto.txt line 535
Quoted 10 lines
doc/driver-model/spi-howto.txt new file +10
-0+} + +All we have really done here is change the pointers and print the device name +instead of the bus number. Other local static functions can be treated in +the same way. + + +16. Set up the per-child data and child_pre_probe() method + +To minimise the pain and complexity of the SPI subsystem while the driver
typo - minimize
Quoted 8 lines
doc/driver-model/spi-howto.txt new file +8
-0+model change-over is in place, struct spi_slave is used to reference a +SPI bus slave, even though that slave is actually a struct udevice. In fact +struct spi_slave is the device's child data. We need to make sure this is +set up. It is possible to allocate more space that struct spi_slave needs, +but this is the minimum. + +U_BOOT_DRIVER(exynos_spi) = { +...
doc/driver-model/spi-howto.txt line 553
Quoted 10 lines
doc/driver-model/spi-howto.txt new file +10
-0+ .per_child_auto_alloc_size = sizeof(struct spi_slave), + .child_pre_probe = exynos_spi_child_pre_probe, +} + +int exynos_spi_child_pre_probe(struct udevice *dev) +{ + struct spi_slave *slave = dev_get_parentdata(dev); + + return spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, slave); +}
So this routine will fill spi_slave members - is it? Why cs and bus are not part of DM?
Quoted 18 lines
doc/driver-model/spi-howto.txt new file +17
-1+ + +Here our child_pre_probe() method merely calls a standard method to convert +device tree data to that used in the slave. + + +17. Test it + +Now that you have the code written and it compiles, try testing it using +the 'sf test' command. You may need to enable CONFIG_CMD_SF_TEST for your +board. + + +18. Prepare patches and send them to the mailing lists + +You can use 'tools/patman/patman' to prepare, check and send patches for +your work. See the README for details. --
Quoted 2 lines
2.0.0.526.g5318336
thanks!
Signature · 1 line
Jagan.
PATCH 12/25 dm: spi: Add documentation on how to convert over SPI drivers
Hi Jagan,
Quoted 27 lines · Jagan Teki
Quoted 26 lines · Simon Glass
This README is intended to help maintainers move their SPI drivers over to driver model. It works through the required steps with an example.
Signed-off-by Simon Glass <[email protected]>---
doc/driver-model/spi-howto.txt | 570 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 570 insertions(+) create mode 100644 doc/driver-model/spi-howto.txtdoc/driver-model/spi-howto.txt new file +10
-0@@ -0,0 +1,570 @@ +How to port a SPI driver to driver model +======================================== + +Here is a rough step-by-step guide. It is based around converting the +exynos SPI driver to driver model (DM) and the example code is based +around U-Boot v2014.04 (commit dda0dbf). + +It is quite long since it includes actual code examples. + +Before driver model, SPI drivers have their own private structure which
Quoted 8 lines
doc/driver-model/spi-howto.txt line 15
Quoted 5 lines
doc/driver-model/spi-howto.txt new file +5
-0+contains 'struct spi_slave'. With driver model, 'struct spi_slave' still +exists, but now it is 'per-child data' for the SPI bus. Each child of the +SPI bus is a SPI slave. The information that was stored in the +driver-specific slave structure can now be put in private data for the +SPI bus.Do you think
spi_slavestill require, I guess It's needed as some slave members to cs, bus are used to control the driver.
The CS and bus are purely command-line conveniences with DM. When it gets down to the driver the bus is determined by the SPI peripheral it is connected to, and the CS is the control that it adjusts to enable the chip select. The numbering of buses and chip selects is not relevant down at the driver level anymore.
Quoted 2 lines
But any specific reason for removing
spi_slavefromexynos_spi.c?
The driver has its own private data, there is no longer a need to store the SPI subsystem's data there also. That is what the per-child data is for.
Quoted 524 lines
Quoted 98 lines
doc/driver-model/spi-howto.txt new file +97
-0+ +For example, struct tegra_spi_slave looks like this: + +struct tegra_spi_slave { + struct spi_slave slave; + struct tegra_spi_ctrl *ctrl; +}; + +In this case 'slave' will be in per-child data, and 'ctrl' will be in the +SPI bus' private data. + + +0. How long does this take? + +Around 2.5 hours, including some allowance for figuring out the driver +model bits. + + +1. Enable driver mode for SPI and SPI flash + +Add these to your board config: + +#define CONFIG_DM_SPI +#define CONFIG_DM_SPI_FLASH + + +2. Add the skeleton + +Put this code at the bottom of your existing driver file: + +struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + return NULL; +} + +struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node, + int spi_node) +{ + return NULL; +} + +static int exynos_spi_ofdata_to_platdata(struct udevice *dev) +{ + return -ENODEV; +} + +static int exynos_spi_probe(struct udevice *dev) +{ + return -ENODEV; +} + +static int exynos_spi_remove(struct udevice *dev) +{ + return -ENODEV; +} + +static int exynos_spi_claim_bus(struct udevice *dev) +{ + + return -ENODEV; +} + +static int exynos_spi_release_bus(struct udevice *dev) +{ + + return -ENODEV; +} + +static int exynos_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + + return -ENODEV; +} + +static int exynos_spi_set_speed(struct udevice *dev, uint speed) +{ + return -ENODEV; +} + +static int exynos_spi_set_mode(struct udevice *dev, uint mode) +{ + return -ENODEV; +} + +static const struct dm_spi_ops exynos_spi_ops = { + .claim_bus = exynos_spi_claim_bus, + .release_bus = exynos_spi_release_bus, + .xfer = exynos_spi_xfer, + .set_speed = exynos_spi_set_speed, + .set_mode = exynos_spi_set_mode, +}; + +static const struct udevice_id exynos_spi_ids[] = { + { .compatible = "samsung,exynos-spi" }, + { }doc/driver-model/spi-howto.txt line 122
Quoted 10 lines
doc/driver-model/spi-howto.txt new file +10
-0+}; + +U_BOOT_DRIVER(exynos_spi) = { + .name = "exynos_spi", + .id = UCLASS_SPI, + .of_match = exynos_spi_ids, + .ops = &exynos_spi_ops, + .ofdata_to_platdata = exynos_spi_ofdata_to_platdata, + .probe = exynos_spi_probe, + .remove = exynos_spi_removecomma missing - remove is necessary?
Quoted 19 lines
doc/driver-model/spi-howto.txt new file +19
-0+}; + + +3. Replace 'exynos' in the above code with your driver name + + +4. #ifdef out all of the code in your driver except for the above + +This will allow you to get it building, which means you can work +incrementally. Since all the methods return an error initially, there is +less chance that you will accidentally leave something in. + +Also, even though your conversion is basically a rewrite, it might help +reviewers if you leave functions in the same place in the file, +particularly for large drivers. + + +5. Add some includes +doc/driver-model/spi-howto.txt line 151
Quoted 10 lines
doc/driver-model/spi-howto.txt new file +10
-0+Add these includes to your driver: + +#include <dm.h> +#include <errno.h> + + +6. Build + +At this point you should be able to build U-Boot for your board with the +empty SPI driver. You still have empty methods in yur driver, but we willtypo your instead of yur
Quoted 322 lines
doc/driver-model/spi-howto.txt new file +322
-0+write these one by one. + +If you have spi_init() functions or the like that are called from your +board then the build will fail. Remove these calls and make a note of the +init that needs to be done. + + +7. Set up your platform data structure + +This will hold the information your driver needs to operate, like its +hardware address or maximum frequency. + +You may already have a struct like this, or you may need to create one +from some of the #defines or global variables in the driver. + +Note that this information is not the run-time information. It should not +include state that changes. It should be fixed throughout the life of +U-Boot. Run-time information comes later. + +Here is what was in the exynos spi driver: + +struct spi_bus { + enum periph_id periph_id; + s32 frequency; /* Default clock frequency, -1 for none */ + struct exynos_spi *regs; + int inited; /* 1 if this bus is ready for use */ + int node; + uint deactivate_delay_us; /* Delay to wait after deactivate */ +}; + +Of these, inited is handled by DM and node is the device tree node, which +DM tells you. The name is not quite right. So in this case we would use: + +struct exynos_spi_platdata { + enum periph_id periph_id; + s32 frequency; /* Default clock frequency, -1 for none */ + struct exynos_spi *regs; + uint deactivate_delay_us; /* Delay to wait after deactivate */ +}; + + +8a. Write ofdata_to_platdata() [for device tree only] + +This method will convert information in the device tree node into a C +structure in your driver (called platform data). If you are not using +device tree, go to 8b. + +DM will automatically allocate the struct for us when we are using device +tree, but we need to tell it the size: + +U_BOOT_DRIVER(spi_exynos) = { +... + .platdata_auto_alloc_size = sizeof(struct exynos_spi_platdata), + + +Here is a sample function. It gets a pointer to the platform data and +fills in the fields from device tree. + +static int exynos_spi_ofdata_to_platdata(struct udevice *dev) +{ + struct exynos_spi_platdata *plat = dev->platdata; + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + + plat->regs = (struct exynos_spi *)fdtdec_get_addr(blob, node, "reg"); + plat->periph_id = pinmux_decode_periph_id(blob, node); + + if (plat->periph_id == PERIPH_ID_NONE) { + debug("%s: Invalid peripheral ID %d\n", __func__, + plat->periph_id); + return -FDT_ERR_NOTFOUND; + } + + /* Use 500KHz as a suitable default */ + plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", + 500000); + plat->deactivate_delay_us = fdtdec_get_int(blob, node, + "spi-deactivate-delay", 0); + + return 0; +} + + +8b. Add the platform data [non-device-tree only] + +Specify this data in a U_BOOT_DEVICE() declaration in your board file: + +struct exynos_spi_platdata platdata_spi0 = { + .periph_id = ... + .frequency = ... + .regs = ... + .deactivate_delay_us = ... +}; + +U_BOOT_DEVICE(board_spi0) = { + .name = "exynos_spi", + .platdata = &platdata_spi0, +}; + +You will unfortunately need to put the struct into a header file in this +case so that your board file can use it. + + +9. Add the device private data + +Most devices have some private data which they use to keep track of things +while active. This is the run-time information and needs to be stored in +a structure. There is probably a structure in the driver that includes a +'struct spi_slave', so you can use that. + +struct exynos_spi_slave { + struct spi_slave slave; + struct exynos_spi *regs; + unsigned int freq; /* Default frequency */ + unsigned int mode; + enum periph_id periph_id; /* Peripheral ID for this device */ + unsigned int fifo_size; + int skip_preamble; + struct spi_bus *bus; /* Pointer to our SPI bus info */ + ulong last_transaction_us; /* Time of last transaction end */ +}; + + +We should rename this to make its purpose more obvious, and get rid of +the slave structure, so we have: + +struct exynos_spi_priv { + struct exynos_spi *regs; + unsigned int freq; /* Default frequency */ + unsigned int mode; + enum periph_id periph_id; /* Peripheral ID for this device */ + unsigned int fifo_size; + int skip_preamble; + ulong last_transaction_us; /* Time of last transaction end */ +}; + + +DM can auto-allocate this also: + +U_BOOT_DRIVER(spi_exynos) = { +... + .priv_auto_alloc_size = sizeof(struct exynos_spi_priv), + + +Note that this is created before the probe() method is called, and destroyed +after the remove() method is called. It will be zeroed when the probe() +method is called. + + +10. Add the probe() and remove() methods + +Note: It's a good idea to build repeatedly as you are working, to avoid a +huge amount of work getting things compiling at the end. + +The probe() method is supposed to set up the hardware. U-Boot used to use +spi_setup_slave() to do this. So take a look at this function and see +what you can copy out to set things up. + + +static int exynos_spi_probe(struct udevice *dev) +{ + struct exynos_spi_platdata *pdata = dev_get_platdata(dev); + struct exynos_spi_priv *priv = dev_get_priv(dev); + + if (pdata->periph_id == PERIPH_ID_SPI1 || + pdata->periph_id == PERIPH_ID_SPI2) + priv->fifo_size = 64; + else + priv->fifo_size = 256; + + priv->skip_preamble = 0; + priv->last_transaction_us = timer_get_us(); + priv->freq = pdata->frequency; + priv->periph_id = pdata->periph_id; + + return 0; +} + +This implementation doesn't actually touch the hardware, which is somewhat +unusual for a driver. In this case we will do that when the device is +claimed by something that wants to use the SPI bus. + +For remove() we could shut down the clocks, but in this case there is +nothing to do. DM frees any memory that it allocated, so we can just +remove exynos_spi_remove() and its reference in U_BOOT_DRIVER. + + +11. Implement set_speed() + +This should set up clocks so that the SPI bus is running at the right +speed. With the old API spi_claim_bus() would normally do this and several +of the following functions, so let's look at that function: + +int spi_claim_bus(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + struct exynos_spi *regs = spi_slave->regs; + u32 reg = 0; + int ret; + + ret = set_spi_clk(spi_slave->periph_id, + spi_slave->freq); + if (ret < 0) { + debug("%s: Failed to setup spi clock\n", __func__); + return ret; + } + + exynos_pinmux_config(spi_slave->periph_id, PINMUX_FLAG_NONE); + + spi_flush_fifo(slave); + + reg = readl(®s->ch_cfg); + reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); + + if (spi_slave->mode & SPI_CPHA) + reg |= SPI_CH_CPHA_B; + + if (spi_slave->mode & SPI_CPOL) + reg |= SPI_CH_CPOL_L; + + writel(reg, ®s->ch_cfg); + writel(SPI_FB_DELAY_180, ®s->fb_clk); + + return 0; +} + + +It sets up the speed, mode, pinmux, feedback delay and clears the FIFOs. +With DM these will happen in separate methods. + + +Here is an example for the speed part: + +static int exynos_spi_set_speed(struct udevice *dev, uint speed) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + int ret; + + ret = set_spi_clk(priv->periph_id, speed); + if (ret) + return ret; + priv->freq = speed; + + return 0; +} + + +12. Implement set_mode() + +This should adjust the SPI mode (polarity, etc.). Again this code probably +comes from the old spi_claim_bus(). Here is an example: + + +static int exynos_spi_set_mode(struct udevice *dev, uint mode) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + uint32_t reg; + + reg = readl(&priv->regs->ch_cfg); + reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); + + if (mode & SPI_CPHA) + reg |= SPI_CH_CPHA_B; + + if (mode & SPI_CPOL) + reg |= SPI_CH_CPOL_L; + + writel(reg, &priv->regs->ch_cfg); + + return 0; +} + + +13. Implement claim_bus() + +This is where a client wants to make use of the bus, so claims it first. +At this point we need to make sure every is set up ready for data transfer. + +Here again we look at the old claim function and see some code that is +needed. It is anything unrelated to speed and mode: + +static int exynos_spi_claim_bus(struct udevice *dev) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + + exynos_pinmux_config(priv->periph_id, PINMUX_FLAG_NONE); + spi_flush_fifo(priv->regs); + + writel(SPI_FB_DELAY_180, &priv->regs->fb_clk); + + return 0; +} + +The spi_flush_fifo() function is in the removed part of the code, so we +need to expose it again (perhaps with an #endif before it and '#if 0' +after it). It only needs accesss to priv->regs which is why we have +passed that in: + +/** + * Flush spi tx, rx fifos and reset the SPI controller + * + * @param regs Pointer to SPI registers + */ +static void spi_flush_fifo(struct exynos_spi *regs) +{ + clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + setbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON); +} + + +14. Implement release_bus() + +This releases the bus - in our example the old code in spi_release_bus() +is a call to spi_flush_fifo, so we add: + +static int exynos_spi_release_bus(struct udevice *dev) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + + spi_flush_fifo(priv->regs); +doc/driver-model/spi-howto.txt line 483
Quoted 10 lines
doc/driver-model/spi-howto.txt new file +10
-0+ return 0; +} + + +15. Implement xfer() + +This is the final method that we need to create, and it is where all the +work happens. The method parameters are the same as the old spi_xfer() with +the addition of a 'struct udevice' so conversion is pretty easy. Start +by copying the contents of spi_xfer() to your new xfer() mthod and proceedtypo - method
Quoted 46 lines
doc/driver-model/spi-howto.txt new file +46
-0+from there. + +If (flags & SPI_XFER_BEGIN) is non-zero then xfer() normally calls an +activate function, something like this: + +void spi_cs_activate(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + + /* If it's too soon to do another transaction, wait */ + if (spi_slave->bus->deactivate_delay_us && + spi_slave->last_transaction_us) { + ulong delay_us; /* The delay completed so far */ + delay_us = timer_get_us() - spi_slave->last_transaction_us; + if (delay_us < spi_slave->bus->deactivate_delay_us) + udelay(spi_slave->bus->deactivate_delay_us - delay_us); + } + + clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Activate CS, bus %d\n", spi_slave->slave.bus); + spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE; +} + +The new version looks like this: + +static void spi_cs_activate(struct udevice *dev) +{ + struct exynos_spi_platdata *pdata = dev_get_platdata(dev); + struct exynos_spi_priv *priv = dev_get_priv(dev); + + /* If it's too soon to do another transaction, wait */ + if (pdata->deactivate_delay_us && + priv->last_transaction_us) { + ulong delay_us; /* The delay completed so far */ + delay_us = timer_get_us() - priv->last_transaction_us; + if (delay_us < pdata->deactivate_delay_us) + udelay(pdata->deactivate_delay_us - delay_us); + } + + clrbits_le32(&priv->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Activate CS, bus '%s'\n", dev->name); + priv->skip_preamble = priv->mode & SPI_PREAMBLE; +} + +All we have really done here is change the pointers and print the device name +instead of the bus number. Other local static functions can be treated in
Quoted 8 lines
doc/driver-model/spi-howto.txt line 535
Quoted 6 lines
doc/driver-model/spi-howto.txt new file +6
-0+the same way. + + +16. Set up the per-child data and child_pre_probe() method + +To minimise the pain and complexity of the SPI subsystem while the drivertypo - minimize
English spelling (this is U-Boot).
Quoted 14 lines
Quoted 14 lines
doc/driver-model/spi-howto.txt new file +13
-0+model change-over is in place, struct spi_slave is used to reference a +SPI bus slave, even though that slave is actually a struct udevice. In fact +struct spi_slave is the device's child data. We need to make sure this is +set up. It is possible to allocate more space that struct spi_slave needs, +but this is the minimum. + +U_BOOT_DRIVER(exynos_spi) = { +... + .per_child_auto_alloc_size = sizeof(struct spi_slave), + .child_pre_probe = exynos_spi_child_pre_probe, +} + +int exynos_spi_child_pre_probe(struct udevice *dev)
Quoted 8 lines
doc/driver-model/spi-howto.txt line 553
Quoted 5 lines
doc/driver-model/spi-howto.txt new file +5
-0+{ + struct spi_slave *slave = dev_get_parentdata(dev); + + return spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, slave); +}So this routine will fill
spi_slavemembers - is it? Why cs and bus are not part of DM?
See above for my comments, but let me know if unsure.
Quoted 17 lines
Quoted 17 lines
doc/driver-model/spi-howto.txt new file +16
-0+ + +Here our child_pre_probe() method merely calls a standard method to convert +device tree data to that used in the slave. + + +17. Test it + +Now that you have the code written and it compiles, try testing it using +the 'sf test' command. You may need to enable CONFIG_CMD_SF_TEST for your +board. + + +18. Prepare patches and send them to the mailing lists + +You can use 'tools/patman/patman' to prepare, check and send patches for
Quoted 8 lines
Quoted 4 lines
doc/driver-model/spi-howto.txt new file +1
-1+your work. See the README for details. --2.0.0.526.g5318336
thanks!
--
Jagan.
Regards,
Simon
PATCH 12/25 dm: spi: Add documentation on how to convert over SPI drivers
Hi Simon,
Quoted 39 lines · Simon Glass
Hi Jagan,
Quoted 27 lines · Jagan Teki
Quoted 26 lines · Simon Glass
This README is intended to help maintainers move their SPI drivers over to driver model. It works through the required steps with an example.
Signed-off-by Simon Glass <[email protected]>---
doc/driver-model/spi-howto.txt | 570 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 570 insertions(+) create mode 100644 doc/driver-model/spi-howto.txtdoc/driver-model/spi-howto.txt new file +10
-0@@ -0,0 +1,570 @@ +How to port a SPI driver to driver model +======================================== + +Here is a rough step-by-step guide. It is based around converting the +exynos SPI driver to driver model (DM) and the example code is based +around U-Boot v2014.04 (commit dda0dbf). + +It is quite long since it includes actual code examples. + +Before driver model, SPI drivers have their own private structure whichQuoted 8 lines
doc/driver-model/spi-howto.txt line 15
Quoted 5 lines
doc/driver-model/spi-howto.txt new file +5
-0+contains 'struct spi_slave'. With driver model, 'struct spi_slave' still +exists, but now it is 'per-child data' for the SPI bus. Each child of the +SPI bus is a SPI slave. The information that was stored in the +driver-specific slave structure can now be put in private data for the +SPI bus.Do you think
spi_slavestill require, I guess It's needed as some slave members to cs, bus are used to control the driver.
Quoted 5 lines
The CS and bus are purely command-line conveniences with DM. When it gets down to the driver the bus is determined by the SPI peripheral it is connected to, and the CS is the control that it adjusts to enable the chip select. The numbering of buses and chip selects is not relevant down at the driver level anymore.
Yes - I understand but let me explain my comments.
u-boot> sf probe bus:cs speed mode
Unlike other buses/controllers on u-boot we're dynamically probing the slave on bus
using bus and cs for example, we have a zynq_spi handling spi0, spi1 (bus's) and each
one has 3 chip-selects. So
u-boot> sf probe 1:1
for probing bus1 (spi1) and cs 1
u-boot> sf probe 0:1
for probing bus0 (spi0) and cs 1
The respective reg base (based on bus) and register chip-select (based on cs) zynq_spi.c
will handle these in driver it self.
speed and mode settings, I'm fine with dm, where it will configure using
int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode,
const char *drv_name, const char *dev_name, struct udevice **devp, struct spi_slave **slavep)
{
.....
ret = spi_set_speed_mode(bus, speed, mode);
......
}
But how can spi-uclass.c will identifies how many bus's along with cs lines does specified controller driver support.
This what usually we're doing in driver as int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
/* 2 bus with 3 chipselect */
return bus < 2 && cs < 3;
}Please let me know if you have any questions, this is what usually doing most of drivers.
Quoted 580 lines
Quoted 3 lines
But any specific reason for removing
spi_slavefromexynos_spi.c?The driver has its own private data, there is no longer a need to store the SPI subsystem's data there also. That is what the per-child data is for.
Quoted 524 lines
Quoted 98 lines
doc/driver-model/spi-howto.txt new file +97
-0+ +For example, struct tegra_spi_slave looks like this: + +struct tegra_spi_slave { + struct spi_slave slave; + struct tegra_spi_ctrl *ctrl; +}; + +In this case 'slave' will be in per-child data, and 'ctrl' will be in the +SPI bus' private data. + + +0. How long does this take? + +Around 2.5 hours, including some allowance for figuring out the driver +model bits. + + +1. Enable driver mode for SPI and SPI flash + +Add these to your board config: + +#define CONFIG_DM_SPI +#define CONFIG_DM_SPI_FLASH + + +2. Add the skeleton + +Put this code at the bottom of your existing driver file: + +struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + return NULL; +} + +struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node, + int spi_node) +{ + return NULL; +} + +static int exynos_spi_ofdata_to_platdata(struct udevice *dev) +{ + return -ENODEV; +} + +static int exynos_spi_probe(struct udevice *dev) +{ + return -ENODEV; +} + +static int exynos_spi_remove(struct udevice *dev) +{ + return -ENODEV; +} + +static int exynos_spi_claim_bus(struct udevice *dev) +{ + + return -ENODEV; +} + +static int exynos_spi_release_bus(struct udevice *dev) +{ + + return -ENODEV; +} + +static int exynos_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + + return -ENODEV; +} + +static int exynos_spi_set_speed(struct udevice *dev, uint speed) +{ + return -ENODEV; +} + +static int exynos_spi_set_mode(struct udevice *dev, uint mode) +{ + return -ENODEV; +} + +static const struct dm_spi_ops exynos_spi_ops = { + .claim_bus = exynos_spi_claim_bus, + .release_bus = exynos_spi_release_bus, + .xfer = exynos_spi_xfer, + .set_speed = exynos_spi_set_speed, + .set_mode = exynos_spi_set_mode, +}; + +static const struct udevice_id exynos_spi_ids[] = { + { .compatible = "samsung,exynos-spi" }, + { }doc/driver-model/spi-howto.txt line 122
Quoted 10 lines
doc/driver-model/spi-howto.txt new file +10
-0+}; + +U_BOOT_DRIVER(exynos_spi) = { + .name = "exynos_spi", + .id = UCLASS_SPI, + .of_match = exynos_spi_ids, + .ops = &exynos_spi_ops, + .ofdata_to_platdata = exynos_spi_ofdata_to_platdata, + .probe = exynos_spi_probe, + .remove = exynos_spi_removecomma missing - remove is necessary?
Quoted 19 lines
doc/driver-model/spi-howto.txt new file +19
-0+}; + + +3. Replace 'exynos' in the above code with your driver name + + +4. #ifdef out all of the code in your driver except for the above + +This will allow you to get it building, which means you can work +incrementally. Since all the methods return an error initially, there is +less chance that you will accidentally leave something in. + +Also, even though your conversion is basically a rewrite, it might help +reviewers if you leave functions in the same place in the file, +particularly for large drivers. + + +5. Add some includes +doc/driver-model/spi-howto.txt line 151
Quoted 10 lines
doc/driver-model/spi-howto.txt new file +10
-0+Add these includes to your driver: + +#include <dm.h> +#include <errno.h> + + +6. Build + +At this point you should be able to build U-Boot for your board with the +empty SPI driver. You still have empty methods in yur driver, but we willtypo your instead of yur
Quoted 322 lines
doc/driver-model/spi-howto.txt new file +322
-0+write these one by one. + +If you have spi_init() functions or the like that are called from your +board then the build will fail. Remove these calls and make a note of the +init that needs to be done. + + +7. Set up your platform data structure + +This will hold the information your driver needs to operate, like its +hardware address or maximum frequency. + +You may already have a struct like this, or you may need to create one +from some of the #defines or global variables in the driver. + +Note that this information is not the run-time information. It should not +include state that changes. It should be fixed throughout the life of +U-Boot. Run-time information comes later. + +Here is what was in the exynos spi driver: + +struct spi_bus { + enum periph_id periph_id; + s32 frequency; /* Default clock frequency, -1 for none */ + struct exynos_spi *regs; + int inited; /* 1 if this bus is ready for use */ + int node; + uint deactivate_delay_us; /* Delay to wait after deactivate */ +}; + +Of these, inited is handled by DM and node is the device tree node, which +DM tells you. The name is not quite right. So in this case we would use: + +struct exynos_spi_platdata { + enum periph_id periph_id; + s32 frequency; /* Default clock frequency, -1 for none */ + struct exynos_spi *regs; + uint deactivate_delay_us; /* Delay to wait after deactivate */ +}; + + +8a. Write ofdata_to_platdata() [for device tree only] + +This method will convert information in the device tree node into a C +structure in your driver (called platform data). If you are not using +device tree, go to 8b. + +DM will automatically allocate the struct for us when we are using device +tree, but we need to tell it the size: + +U_BOOT_DRIVER(spi_exynos) = { +... + .platdata_auto_alloc_size = sizeof(struct exynos_spi_platdata), + + +Here is a sample function. It gets a pointer to the platform data and +fills in the fields from device tree. + +static int exynos_spi_ofdata_to_platdata(struct udevice *dev) +{ + struct exynos_spi_platdata *plat = dev->platdata; + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + + plat->regs = (struct exynos_spi *)fdtdec_get_addr(blob, node, "reg"); + plat->periph_id = pinmux_decode_periph_id(blob, node); + + if (plat->periph_id == PERIPH_ID_NONE) { + debug("%s: Invalid peripheral ID %d\n", __func__, + plat->periph_id); + return -FDT_ERR_NOTFOUND; + } + + /* Use 500KHz as a suitable default */ + plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", + 500000); + plat->deactivate_delay_us = fdtdec_get_int(blob, node, + "spi-deactivate-delay", 0); + + return 0; +} + + +8b. Add the platform data [non-device-tree only] + +Specify this data in a U_BOOT_DEVICE() declaration in your board file: + +struct exynos_spi_platdata platdata_spi0 = { + .periph_id = ... + .frequency = ... + .regs = ... + .deactivate_delay_us = ... +}; + +U_BOOT_DEVICE(board_spi0) = { + .name = "exynos_spi", + .platdata = &platdata_spi0, +}; + +You will unfortunately need to put the struct into a header file in this +case so that your board file can use it. + + +9. Add the device private data + +Most devices have some private data which they use to keep track of things +while active. This is the run-time information and needs to be stored in +a structure. There is probably a structure in the driver that includes a +'struct spi_slave', so you can use that. + +struct exynos_spi_slave { + struct spi_slave slave; + struct exynos_spi *regs; + unsigned int freq; /* Default frequency */ + unsigned int mode; + enum periph_id periph_id; /* Peripheral ID for this device */ + unsigned int fifo_size; + int skip_preamble; + struct spi_bus *bus; /* Pointer to our SPI bus info */ + ulong last_transaction_us; /* Time of last transaction end */ +}; + + +We should rename this to make its purpose more obvious, and get rid of +the slave structure, so we have: + +struct exynos_spi_priv { + struct exynos_spi *regs; + unsigned int freq; /* Default frequency */ + unsigned int mode; + enum periph_id periph_id; /* Peripheral ID for this device */ + unsigned int fifo_size; + int skip_preamble; + ulong last_transaction_us; /* Time of last transaction end */ +}; + + +DM can auto-allocate this also: + +U_BOOT_DRIVER(spi_exynos) = { +... + .priv_auto_alloc_size = sizeof(struct exynos_spi_priv), + + +Note that this is created before the probe() method is called, and destroyed +after the remove() method is called. It will be zeroed when the probe() +method is called. + + +10. Add the probe() and remove() methods + +Note: It's a good idea to build repeatedly as you are working, to avoid a +huge amount of work getting things compiling at the end. + +The probe() method is supposed to set up the hardware. U-Boot used to use +spi_setup_slave() to do this. So take a look at this function and see +what you can copy out to set things up. + + +static int exynos_spi_probe(struct udevice *dev) +{ + struct exynos_spi_platdata *pdata = dev_get_platdata(dev); + struct exynos_spi_priv *priv = dev_get_priv(dev); + + if (pdata->periph_id == PERIPH_ID_SPI1 || + pdata->periph_id == PERIPH_ID_SPI2) + priv->fifo_size = 64; + else + priv->fifo_size = 256; + + priv->skip_preamble = 0; + priv->last_transaction_us = timer_get_us(); + priv->freq = pdata->frequency; + priv->periph_id = pdata->periph_id; + + return 0; +} + +This implementation doesn't actually touch the hardware, which is somewhat +unusual for a driver. In this case we will do that when the device is +claimed by something that wants to use the SPI bus. + +For remove() we could shut down the clocks, but in this case there is +nothing to do. DM frees any memory that it allocated, so we can just +remove exynos_spi_remove() and its reference in U_BOOT_DRIVER. + + +11. Implement set_speed() + +This should set up clocks so that the SPI bus is running at the right +speed. With the old API spi_claim_bus() would normally do this and several +of the following functions, so let's look at that function: + +int spi_claim_bus(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + struct exynos_spi *regs = spi_slave->regs; + u32 reg = 0; + int ret; + + ret = set_spi_clk(spi_slave->periph_id, + spi_slave->freq); + if (ret < 0) { + debug("%s: Failed to setup spi clock\n", __func__); + return ret; + } + + exynos_pinmux_config(spi_slave->periph_id, PINMUX_FLAG_NONE); + + spi_flush_fifo(slave); + + reg = readl(®s->ch_cfg); + reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); + + if (spi_slave->mode & SPI_CPHA) + reg |= SPI_CH_CPHA_B; + + if (spi_slave->mode & SPI_CPOL) + reg |= SPI_CH_CPOL_L; + + writel(reg, ®s->ch_cfg); + writel(SPI_FB_DELAY_180, ®s->fb_clk); + + return 0; +} + + +It sets up the speed, mode, pinmux, feedback delay and clears the FIFOs. +With DM these will happen in separate methods. + + +Here is an example for the speed part: + +static int exynos_spi_set_speed(struct udevice *dev, uint speed) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + int ret; + + ret = set_spi_clk(priv->periph_id, speed); + if (ret) + return ret; + priv->freq = speed; + + return 0; +} + + +12. Implement set_mode() + +This should adjust the SPI mode (polarity, etc.). Again this code probably +comes from the old spi_claim_bus(). Here is an example: + + +static int exynos_spi_set_mode(struct udevice *dev, uint mode) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + uint32_t reg; + + reg = readl(&priv->regs->ch_cfg); + reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); + + if (mode & SPI_CPHA) + reg |= SPI_CH_CPHA_B; + + if (mode & SPI_CPOL) + reg |= SPI_CH_CPOL_L; + + writel(reg, &priv->regs->ch_cfg); + + return 0; +} + + +13. Implement claim_bus() + +This is where a client wants to make use of the bus, so claims it first. +At this point we need to make sure every is set up ready for data transfer. + +Here again we look at the old claim function and see some code that is +needed. It is anything unrelated to speed and mode: + +static int exynos_spi_claim_bus(struct udevice *dev) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + + exynos_pinmux_config(priv->periph_id, PINMUX_FLAG_NONE); + spi_flush_fifo(priv->regs); + + writel(SPI_FB_DELAY_180, &priv->regs->fb_clk); + + return 0; +} + +The spi_flush_fifo() function is in the removed part of the code, so we +need to expose it again (perhaps with an #endif before it and '#if 0' +after it). It only needs accesss to priv->regs which is why we have +passed that in: + +/** + * Flush spi tx, rx fifos and reset the SPI controller + * + * @param regs Pointer to SPI registers + */ +static void spi_flush_fifo(struct exynos_spi *regs) +{ + clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + setbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON); +} + + +14. Implement release_bus() + +This releases the bus - in our example the old code in spi_release_bus() +is a call to spi_flush_fifo, so we add: + +static int exynos_spi_release_bus(struct udevice *dev) +{ + struct exynos_spi_priv *priv = dev_get_priv(dev); + + spi_flush_fifo(priv->regs); +doc/driver-model/spi-howto.txt line 483
Quoted 10 lines
doc/driver-model/spi-howto.txt new file +10
-0+ return 0; +} + + +15. Implement xfer() + +This is the final method that we need to create, and it is where all the +work happens. The method parameters are the same as the old spi_xfer() with +the addition of a 'struct udevice' so conversion is pretty easy. Start +by copying the contents of spi_xfer() to your new xfer() mthod and proceedtypo - method
Quoted 46 lines
doc/driver-model/spi-howto.txt new file +46
-0+from there. + +If (flags & SPI_XFER_BEGIN) is non-zero then xfer() normally calls an +activate function, something like this: + +void spi_cs_activate(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + + /* If it's too soon to do another transaction, wait */ + if (spi_slave->bus->deactivate_delay_us && + spi_slave->last_transaction_us) { + ulong delay_us; /* The delay completed so far */ + delay_us = timer_get_us() - spi_slave->last_transaction_us; + if (delay_us < spi_slave->bus->deactivate_delay_us) + udelay(spi_slave->bus->deactivate_delay_us - delay_us); + } + + clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Activate CS, bus %d\n", spi_slave->slave.bus); + spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE; +} + +The new version looks like this: + +static void spi_cs_activate(struct udevice *dev) +{ + struct exynos_spi_platdata *pdata = dev_get_platdata(dev); + struct exynos_spi_priv *priv = dev_get_priv(dev); + + /* If it's too soon to do another transaction, wait */ + if (pdata->deactivate_delay_us && + priv->last_transaction_us) { + ulong delay_us; /* The delay completed so far */ + delay_us = timer_get_us() - priv->last_transaction_us; + if (delay_us < pdata->deactivate_delay_us) + udelay(pdata->deactivate_delay_us - delay_us); + } + + clrbits_le32(&priv->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Activate CS, bus '%s'\n", dev->name); + priv->skip_preamble = priv->mode & SPI_PREAMBLE; +} + +All we have really done here is change the pointers and print the device name +instead of the bus number. Other local static functions can be treated inQuoted 8 lines
doc/driver-model/spi-howto.txt line 535
Quoted 6 lines
doc/driver-model/spi-howto.txt new file +6
-0+the same way. + + +16. Set up the per-child data and child_pre_probe() method + +To minimise the pain and complexity of the SPI subsystem while the drivertypo - minimize
English spelling (this is U-Boot).
Quoted 14 lines
Quoted 14 lines
doc/driver-model/spi-howto.txt new file +13
-0+model change-over is in place, struct spi_slave is used to reference a +SPI bus slave, even though that slave is actually a struct udevice. In fact +struct spi_slave is the device's child data. We need to make sure this is +set up. It is possible to allocate more space that struct spi_slave needs, +but this is the minimum. + +U_BOOT_DRIVER(exynos_spi) = { +... + .per_child_auto_alloc_size = sizeof(struct spi_slave), + .child_pre_probe = exynos_spi_child_pre_probe, +} + +int exynos_spi_child_pre_probe(struct udevice *dev)Quoted 8 lines
doc/driver-model/spi-howto.txt line 553
Quoted 5 lines
doc/driver-model/spi-howto.txt new file +5
-0+{ + struct spi_slave *slave = dev_get_parentdata(dev); + + return spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, slave); +}So this routine will fill
spi_slavemembers - is it? Why cs and bus are not part of DM?See above for my comments, but let me know if unsure.
Quoted 12 lines
Quoted 12 lines
doc/driver-model/spi-howto.txt new file +11
-0+ + +Here our child_pre_probe() method merely calls a standard method to convert +device tree data to that used in the slave. + + +17. Test it + +Now that you have the code written and it compiles, try testing it using +the 'sf test' command. You may need to enable CONFIG_CMD_SF_TEST for your +board.
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
doc/driver-model/spi-howto.txt new file +6
-1+ + +18. Prepare patches and send them to the mailing lists + +You can use 'tools/patman/patman' to prepare, check and send patches for +your work. See the README for details. --2.0.0.526.g5318336
thanks!
Signature · 1 line
Jagan.
PATCH 12/25 dm: spi: Add documentation on how to convert over SPI drivers
Hi Jagan,
Quoted 79 lines · Jagan Teki
Hi Simon,
Quoted 39 lines · Simon Glass
Hi Jagan,
Quoted 27 lines · Jagan Teki
Quoted 26 lines · Simon Glass
This README is intended to help maintainers move their SPI drivers over to driver model. It works through the required steps with an example.
Signed-off-by Simon Glass <[email protected]>---
doc/driver-model/spi-howto.txt | 570 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 570 insertions(+) create mode 100644 doc/driver-model/spi-howto.txtdoc/driver-model/spi-howto.txt new file +10
-0@@ -0,0 +1,570 @@ +How to port a SPI driver to driver model +======================================== + +Here is a rough step-by-step guide. It is based around converting the +exynos SPI driver to driver model (DM) and the example code is based +around U-Boot v2014.04 (commit dda0dbf). + +It is quite long since it includes actual code examples. + +Before driver model, SPI drivers have their own private structure whichQuoted 8 lines
doc/driver-model/spi-howto.txt line 15
Quoted 5 lines
doc/driver-model/spi-howto.txt new file +5
-0+contains 'struct spi_slave'. With driver model, 'struct spi_slave' still +exists, but now it is 'per-child data' for the SPI bus. Each child of the +SPI bus is a SPI slave. The information that was stored in the +driver-specific slave structure can now be put in private data for the +SPI bus.Do you think
spi_slavestill require, I guess It's needed as some slave members to cs, bus are used to control the driver.Quoted 5 lines
The CS and bus are purely command-line conveniences with DM. When it gets down to the driver the bus is determined by the SPI peripheral it is connected to, and the CS is the control that it adjusts to enable the chip select. The numbering of buses and chip selects is not relevant down at the driver level anymore.
Yes - I understand but let me explain my comments.
u-boot> sf probe bus:cs speed mode
Unlike other buses/controllers on u-boot we're dynamically probing the slave on bus
using bus and cs for example, we have azynq_spihandling spi0, spi1 (bus's) and each
one has 3 chip-selects. Sou-boot> sf probe 1:1
for probing bus1 (spi1) and cs 1u-boot> sf probe 0:1
for probing bus0 (spi0) and cs 1The respective reg base (based on bus) and register chip-select (based on cs)
zynq_spi.c
will handle these in driver it self.speed and mode settings, I'm fine with dm, where it will configure using
int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode,
const char*drv_name, const char*dev_name, struct udevice **devp, structspi_slave**slavep)
{
.....
ret =spi_set_speed_mode(bus, speed, mode);
......
}
Quoted 2 lines
But how can
spi-uclass.cwill identifies how many bus's along with cs lines does specified controller driver support.
This is done with platform data or device tree. In other words we explicitly list a driver for each chip select.
However I did anticipate problems with this approach, particularly while so few drivers are converted. Also we need to support things like the 'sspi' command and 'sf probe'. So I added spi_bind_device() which is automatically called if no SPI device is found for a bus/chip select.
Quoted 10 lines
This what usually we're doing in driver as int
spi_cs_is_valid(unsigned int bus, unsigned int cs){ /* 2 bus with 3 chipselect */ return bus < 2 && cs < 3; }Please let me know if you have any questions, this is what usually doing most of drivers.
Yes, I don't really like that - it is a fine way of doing things before driver model, but now it is not that useful. We are trying to bind drivers to chip selects, not just figure out which ones are valid.
One thing I have not figured out yet (mostly since I have no driver that needs it) is how to activate multiple SPI chip selects using GPIOs. It shouldn't be too hard, just need a board that supports it.
[snip]
Regards,
Simon
PATCH 13/25 dm: exynos: Convert SPI to driver model
In reply to Simon GlassMove the exynos SPI driver over to driver model. This removes quite a bit of boilerplate from the driver, although it adds some for driver model.
A few device tree additions are needed to make the SPI flash available.
---
arch/arm/dts/exynos5250-snow.dts | 8 + arch/arm/dts/exynos5420-peach-pit.dts | 1 + board/samsung/common/board.c | 3 - drivers/spi/exynos_spi.c | 509 ++++++++++++---------------------- include/configs/exynos5-dt.h | 1 + 5 files changed, 186 insertions(+), 336 deletions(-)
arch/arm/dts/exynos5250-snow.dts +8 -0
@@ -53,6 +53,14 @@ }; }; + spi at 12d30000 { + spi-max-frequency = <50000000>; + firmware_storage_spi: flash at 0 { + compatible = "spi-flash"; + reg = <0>; + }; + }; + spi at 131b0000 { spi-max-frequency = <1000000>; spi-deactivate-delay = <100>;
arch/arm/dts/exynos5420-peach-pit.dts +1 -0
@@ -135,6 +135,7 @@ spi at 12d30000 { /* spi1 */ spi-max-frequency = <50000000>; firmware_storage_spi: flash at 0 { + compatible = "spi-flash"; reg = <0>; /*
board/samsung/common/board.c +0 -3
@@ -86,9 +86,6 @@ int board_init(void)boot_temp_check ();#endif -#ifdef CONFIG_EXYNOS_SPI -spi_init (); -#endif return exynos_init (); }
drivers/spi/exynos_spi.c +176 -333
648 lines not on this page · raw message
include/configs/exynos5-dt.h +1 -0
@@ -20,6 +20,7 @@#define CONFIG_CMD_DM #define CONFIG_DM_GPIO #define CONFIG_DM_SERIAL +#define CONFIG_DM_SPI #define CONFIG_SYS_GENERIC_BOARD #define CONFIG_ARCH_CPU_INIT
Signature · 1 line
2.0.0.526.g5318336
PATCH 14/25 sf: Add an empty entry to the parameter list
In reply to Simon GlassThe list is supposed to be terminated with a NULL name, but is not. If a board probes a chip which does not appear in the table, U-Boot will crash (at least on sandbox).
---
drivers/mtd/spi/sf_params.c | 1 + 1 file changed, 1 insertion(+)
drivers/mtd/spi/sf_params.c +1 -0
@@ -117,6 +117,7 @@ const struct spi_flash_params spi_flash_params_table[] = { {"W25Q64DW", 0xef6017, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K}, {"W25Q128FW", 0xef6018, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP | SECT_4K}, #endif + {}, /* Empty entry to terminate the list */ /* * Note: * Below paired flash devices has similar spi_flash params.
Signature · 1 line
2.0.0.526.g5318336
PATCH 15/25 sf: Tidy up public and private header files
In reply to Simon GlassSince spi_flash.h is supposed to be the public API for SPI flash, move private things to sf_internal.h. Also tidy up a few comment nits.
---
board/buffalo/lsxl/lsxl.c | 3 +- common/cmd_sf.c | 1 + drivers/mtd/spi/ramtron.c | 1 + drivers/mtd/spi/sf_internal.h | 67 +++++++++++++++++++++++++++++++++++++----- drivers/mtd/spi/spi_spl_load.c | 1 + include/spi_flash.h | 57 ++++------------------------------- 6 files changed, 70 insertions(+), 60 deletions(-)
board/buffalo/lsxl/lsxl.c +2 -1
@@ -13,11 +13,12 @@#include <malloc.h> #include <netdev.h> #include <miiphy.h> +#include <spi.h> +#include <spi_flash.h> #include <asm/arch/kirkwood.h> #include <asm/arch/cpu.h> #include <asm/arch/mpp.h> #include <asm/arch/gpio.h> -#include <spi_flash.h> #include "lsxl.h"
common/cmd_sf.c +1 -0
@@ -9,6 +9,7 @@#include <common.h> #include <div64.h> #include <malloc.h> +#include <spi.h> #include <spi_flash.h> #include <asm/io.h>
drivers/mtd/spi/ramtron.c +1 -0
@@ -35,6 +35,7 @@#include <common.h> #include <malloc.h> +#include <spi.h> #include <spi_flash.h> #include "sf_internal.h"
drivers/mtd/spi/sf_internal.h +60 -7
102 lines not on this page · raw message
drivers/mtd/spi/spi_spl_load.c +1 -0
@@ -10,6 +10,7 @@*/ #include <common.h> +#include <spi.h> #include <spi_flash.h> #include <spl.h>
include/spi_flash.h +5 -52
81 lines not on this page · raw message
Signature · 1 line
2.0.0.526.g5318336
PATCH 16/25 spi: Use error return value in sf_ops
In reply to Simon GlassAdjust spi_flash_probe_slave() to return an error value instead of a pointer so we get the correct error return.
Have the caller allocate memory for spi_flash to simplify error handling, and also so that driver model can use its existing allocated memory.
Add a spi.h include in the sf_params file.
---
drivers/mtd/spi/sf_probe.c | 90 +++++++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 33 deletions(-)
drivers/mtd/spi/sf_probe.c +57 -33
@@ -9,6 +9,7 @@*/ #include <common.h> +#include <errno.h> #include <fdtdec.h> #include <malloc.h> #include <spi.h>
@@ -95,15 +96,15 @@ static int spi_flash_set_qeb(struct spi_flash *flash, u8 idcode0) } } -static struct spi_flash *spi_flash_validate_params (struct spi_slave *spi, -u8 *idcode) +static int spi_flash_validate_params (struct spi_slave *spi,u8 *idcode, +struct spi_flash *flash) {const struct spi_flash_params *params; -struct spi_flash *flash;u8 cmd;u16 jedec = idcode[1 ] <<8 | idcode[2 ];u16 ext_jedec = idcode[3 ] <<8 | idcode[4 ]; +/* Validate params from spi_flash_params table */ params = spi_flash_params_table;for (; params->name !=NULL ; params++) {if ((params->jedec >>16 ) == idcode[0 ]) {
@@ -120,13 +121,7 @@ static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi,printf ("SF: Unsupported flash IDs: " );printf ("manuf %02x, jedec %04x, ext_jedec %04x\n" , idcode[0 ], jedec, ext_jedec); -return NULL ; - } - - flash =calloc (1 ,sizeof (*flash)); -if (!flash) { -debug ("SF: Failed to allocate spi_flash\n" ); -return NULL ; +return -EPROTONOSUPPORT ; }/* Assign spi data */
@@ -227,15 +222,18 @@ static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi,#ifdef CONFIG_SPI_FLASH_BAR u8 curr_bank =0 ;if (flash->size >SPI_FLASH_16MB_BOUN ) { +int ret; + flash->bank_read_cmd = (idcode[0 ] ==0x01 ) ?CMD_BANKADDR_BRRD :CMD_EXTNADDR_RDEAR ; flash->bank_write_cmd = (idcode[0 ] ==0x01 ) ?CMD_BANKADDR_BRWR :CMD_EXTNADDR_WREAR ; -if (spi_flash_read_common (flash, &flash->bank_read_cmd,1 , - &curr_bank,1 )) { + ret =spi_flash_read_common (flash, &flash->bank_read_cmd,1 , + &curr_bank,1 ); +if (ret) {debug ("SF: fail to read bank addr register\n" ); -return NULL ; +return ret; } flash->bank_curr = curr_bank; }else {
@@ -250,7 +248,7 @@ static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi,spi_flash_cmd_write_status (flash,0 );#endif -return flash; +return 0 ; }#ifdef CONFIG_OF_CONTROL
@@ -281,23 +279,29 @@ int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash) }#endif /* CONFIG_OF_CONTROL */ -static struct spi_flash *spi_flash_probe_slave (struct spi_slave *spi) +/** +* spi_flash_probe_slave() - Probe for a SPI flash device on a bus +* +* @spi: Bus to probe +* @flashp: Pointer to place to put flash info, which may be NULL if the +* space should be allocated +*/ +int spi_flash_probe_slave (struct spi_slave *spi,struct spi_flash *flash) { -struct spi_flash *flash =NULL ;u8 idcode[5 ];int ret;/* Setup spi_slave */ if (!spi) {printf ("SF: Failed to set up slave\n" ); -return NULL ; +return -ENODEV ; }/* Claim spi bus */ ret =spi_claim_bus (spi);if (ret) {debug ("SF: Failed to claim SPI bus: %d\n" , ret); - goto err_claim_bus; +return ret; }/* Read the ID codes */
@@ -312,10 +316,10 @@ static struct spi_flash *spi_flash_probe_slave(struct spi_slave *spi)print_buffer (0 , idcode,1 ,sizeof (idcode),0 );#endif -/* Validate params from spi_flash_params table */ - flash =spi_flash_validate_params (spi, idcode); -if (!flash) +if (spi_flash_validate_params (spi, idcode, flash)) { + ret = -EINVAL ; goto err_read_id; + }/* Set the quad enable bit - only for quad commands */ if ((flash->read_cmd == CMD_READ_QUAD_OUTPUT_FAST) ||
@@ -323,13 +327,15 @@ static struct spi_flash *spi_flash_probe_slave(struct spi_slave *spi) (flash->write_cmd == CMD_QUAD_PAGE_PROGRAM)) {if (spi_flash_set_qeb (flash, idcode[0 ])) {debug ("SF: Fail to set QEB for %02x\n" , idcode[0 ]); -return NULL ; + ret = -EINVAL ; + goto err_read_id; } }#ifdef CONFIG_OF_CONTROL if (spi_flash_decode_fdt (gd->fdt_blob, flash)) {debug ("SF: FDT decode error\n" ); + ret = -EINVAL ; goto err_read_id; }#endif
@@ -355,32 +361,50 @@ static struct spi_flash *spi_flash_probe_slave(struct spi_slave *spi)/* Release spi bus */ spi_release_bus (spi); -return flash; +return 0 ; err_read_id:spi_release_bus (spi); -err_claim_bus: -spi_free_slave (spi); -return NULL ; +return ret; +} + +static struct spi_flash *spi_flash_probe_tail (struct spi_slave *bus) +{ +struct spi_flash *flash; + +/* Allocate space if needed (not used by sf-uclass */ + flash =calloc (1 ,sizeof (*flash)); +if (!flash) { +debug ("SF: Failed to allocate spi_flash\n" ); +return NULL ; + } + +if (spi_flash_probe_slave (bus, flash)) { +spi_free_slave (bus); +free (flash); +return NULL ; + } + +return flash; } -struct spi_flash *spi_flash_probe (unsigned int bus,unsigned int cs, +struct spi_flash *spi_flash_probe (unsigned int busnum,unsigned int cs,unsigned int max_hz,unsigned int spi_mode) { -struct spi_slave *spi; +struct spi_slave *bus; - spi =spi_setup_slave (bus, cs, max_hz, spi_mode); -return spi_flash_probe_slave (spi); + bus =spi_setup_slave (busnum, cs, max_hz, spi_mode); +return spi_flash_probe_tail (bus); }#ifdef CONFIG_OF_SPI_FLASH struct spi_flash *spi_flash_probe_fdt (const void *blob,int slave_node,int spi_node) { -struct spi_slave *spi; +struct spi_slave *bus; - spi =spi_setup_slave_fdt (blob, slave_node, spi_node); -return spi_flash_probe_slave (spi); + bus =spi_setup_slave_fdt (blob, slave_node, spi_node); +return spi_flash_probe_tail (bus); }#endif
Signature · 1 line
2.0.0.526.g5318336
PATCH 17/25 dm: sf: Add a uclass for SPI flash
In reply to Simon GlassAdd a driver model uclass for SPI flash which supports the common operations (read, write, erase). Since we must keep support for the non-dm interface, some modification of the spi_flash header is required.
CONFIG_DM_SPI_FLASH is used to enable driver model for SPI flash.
---
drivers/mtd/spi/Makefile | 7 ++++- drivers/mtd/spi/sf-uclass.c | 63 ++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/spi_flash.h | 70 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/spi/sf-uclass.c
drivers/mtd/spi/Makefile +6 -1
@@ -5,13 +5,18 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_DM_SPI_FLASH) += sf-uclass.o + ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_SPI_LOAD) += spi_spl_load.o obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o endif +#ifndef CONFIG_DM_SPI +obj-$(CONFIG_SPI_FLASH) += sf_probe.o +#endif obj-$(CONFIG_CMD_SF) += sf.o -obj-$(CONFIG_SPI_FLASH) += sf_params.o sf_probe.o sf_ops.o +obj-$(CONFIG_SPI_FLASH) += sf_ops.o sf_params.o obj-$(CONFIG_SPI_FRAM_RAMTRON) += ramtron.o obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o obj-$(CONFIG_SPI_M95XXX) += eeprom_m95xxx.o
drivers/mtd/spi/sf-uclass.c new file +63 -0
63 lines not on this page · raw message
include/dm/uclass-id.h +1 -0
@@ -24,6 +24,7 @@ enum uclass_id {UCLASS_GPIO ,/* Bank of general-purpose I/O pins */ UCLASS_SERIAL ,/* Serial UART */ UCLASS_SPI ,/* SPI bus */ +UCLASS_SPI_FLASH ,/* SPI flash */ UCLASS_COUNT , UCLASS_INVALID = -1,
include/spi_flash.h +70 -0
101 lines not on this page · raw message
Signature · 1 line
2.0.0.526.g5318336
PATCH 18/25 dm: Convert spi_flash_probe() and 'sf probe' to use driver model
In reply to Simon GlassWe want the SPI flash probing feature to operate as a standard driver. Add a driver for the basic probing feature used by most boards. This will be activated by device_probe() as with any other driver.
The 'sf probe' command currently keeps track of the SPI slave that it last used. This doesn't work with driver model, since some other driver or system may have probed the device and have access to it too. On the other hand, if we try to probe a device twice the second probe is a nop with driver model.
Fix this by searching for the matching device, removing it, and then probing it again. This should work as expected regardless of other device activity.
---
common/cmd_sf.c | 23 ++++++++++++++++ drivers/mtd/spi/sf_probe.c | 65 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 2 deletions(-)
common/cmd_sf.c +23 -0
@@ -8,11 +8,13 @@#include <common.h> #include <div64.h> +#include <dm.h> #include <malloc.h> #include <spi.h> #include <spi_flash.h> #include <asm/io.h> +#include <dm/device-internal.h> #ifndef CONFIG_SF_DEFAULT_SPEED # define CONFIG_SF_DEFAULT_SPEED 1000000
@@ -94,7 +96,12 @@ static int do_spi_flash_probe(int argc, char * const argv[])unsigned int speed =CONFIG_SF_DEFAULT_SPEED ;unsigned int mode =CONFIG_SF_DEFAULT_MODE ;char *endp; +#ifdef CONFIG_DM_SPI_FLASH +struct udevice *new, *bus_dev; +int ret; +#else struct spi_flash *new; +#endif if (argc >=2 ) { cs =simple_strtoul (argv[1 ], &endp,0 );
@@ -122,6 +129,21 @@ static int do_spi_flash_probe(int argc, char * const argv[])return -1 ; } +#ifdef CONFIG_DM_SPI_FLASH +/* Remove the old device, otherwise probe will just be a nop */ + ret =spi_find_bus_and_cs (bus, cs, &bus_dev, &new); +if (!ret) +device_remove (new); + flash =NULL ; + ret =spi_flash_probe_bus_cs (bus, cs, speed, mode, &new); +if (ret) { +printf ("Failed to initialize SPI flash at %u:%u (error %d)\n" , + bus, cs, ret); +return 1 ; + } + + flash = new->uclass_priv; +#else new =spi_flash_probe (bus, cs, speed, mode);if (!new) {printf ("Failed to initialize SPI flash at %u:%u\n" , bus, cs);
@@ -131,6 +153,7 @@ static int do_spi_flash_probe(int argc, char * const argv[])if (flash)spi_flash_free (flash); flash = new; +#endif return 0 ; }
drivers/mtd/spi/sf_probe.c +63 -2
92 lines not on this page · raw message
Signature · 1 line
2.0.0.526.g5318336
PATCH 19/25 dm: sf: sandbox: Convert SPI flash driver to driver model
In reply to Simon GlassConvert sandbox's spi flash emulation driver to use driver model.
---
arch/sandbox/include/asm/spi.h | 13 -- arch/sandbox/include/asm/state.h | 1 - drivers/mtd/spi/sandbox.c | 326 +++++++++++++++++++++++++++++++++------ include/configs/sandbox.h | 1 + 4 files changed, 279 insertions(+), 62 deletions(-)
arch/sandbox/include/asm/spi.h +0 -13
@@ -33,19 +33,6 @@ struct sandbox_spi_emu_ops { };/* -* There are times when the data lines are allowed to tristate. What -* is actually sensed on the line depends on the hardware. It could -* always be 0xFF/0x00 (if there are pull ups/downs), or things could -* float and so we'd get garbage back. This func encapsulates that -* scenario so we can worry about the details here. -*/ -static inline void sandbox_spi_tristate (u8 *buf,uint len) -{ -/* XXX: make this into a user config option ? */ -memset (buf,0xff , len); -} - -/* * Extract the bus/cs from the spi spec and return the start of the spi * client spec. If the bus/cs are invalid for the current config, then * it returns NULL.
arch/sandbox/include/asm/state.h +0 -1
@@ -42,7 +42,6 @@ enum state_terminal_raw {struct sandbox_spi_info {const char *spec; -const struct sandbox_spi_emu_ops *ops;struct udevice *emul; };
drivers/mtd/spi/sandbox.c +278 -48
497 lines not on this page · raw message
include/configs/sandbox.h +1 -0
@@ -101,6 +101,7 @@#define CONFIG_CMD_SPI #define CONFIG_SPI_FLASH #define CONFIG_DM_SPI +#define CONFIG_DM_SPI_FLASH #define CONFIG_SPI_FLASH_ATMEL #define CONFIG_SPI_FLASH_EON #define CONFIG_SPI_FLASH_GIGADEVICE
Signature · 1 line
2.0.0.526.g5318336
PATCH 20/25 dm: exynos: config: Use driver model for SPI flash
In reply to Simon GlassUse driver model for exynos5 board SPI flash.
---
include/configs/exynos5-dt.h | 1 + 1 file changed, 1 insertion(+)
include/configs/exynos5-dt.h +1 -0
@@ -21,6 +21,7 @@#define CONFIG_DM_GPIO #define CONFIG_DM_SERIAL #define CONFIG_DM_SPI +#define CONFIG_DM_SPI_FLASH #define CONFIG_SYS_GENERIC_BOARD #define CONFIG_ARCH_CPU_INIT
Signature · 1 line
2.0.0.526.g5318336
PATCH 21/25 dm: spi: Add tests
In reply to Simon GlassThese tests use SPI flash (and the sandbox emulation) to operate.
---
test/dm/Makefile | 1 + test/dm/spi.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 test/dm/spi.c
test/dm/Makefile +1 -0
@@ -18,4 +18,5 @@ obj-$(CONFIG_DM_TEST) += core.o obj-$(CONFIG_DM_TEST) += ut.o ifneq ($(CONFIG_SANDBOX),) obj-$(CONFIG_DM_GPIO) += gpio.o +obj-$(CONFIG_DM_SPI) += spi.o endif
test/dm/spi.c new file +47 -0
47 lines not on this page · raw message
Signature · 1 line
2.0.0.526.g5318336
PATCH 22/25 dm: sf: Add tests for SPI flash
In reply to Simon GlassAdd a simple test for SPI that uses SPI flash. It operates by creating a SPI flash file, and using the 'sf test' command to test that all operations work correctly.
---
test/dm/Makefile | 1 + test/dm/sf.c | 43 +++++++++++++++++++++++++++++++++++++++++++ test/dm/test.dts | 17 ++++++++++++++++- 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 test/dm/sf.c
test/dm/Makefile +1 -0
@@ -19,4 +19,5 @@ obj-$(CONFIG_DM_TEST) += ut.o ifneq ($(CONFIG_SANDBOX),) obj-$(CONFIG_DM_GPIO) += gpio.o obj-$(CONFIG_DM_SPI) += spi.o +obj-$(CONFIG_DM_SPI_FLASH) += sf.o endif
test/dm/sf.c new file +43 -0
43 lines not on this page · raw message
test/dm/test.dts +16 -1
@@ -82,7 +82,7 @@ compatible = "google,another-fdt-test"; }; - base-gpios { + gpio_a: base-gpios { compatible = "sandbox,gpio"; gpio-bank-name = "a"; num-gpios = <20>;
@@ -93,4 +93,19 @@ gpio-bank-name = "b"; num-gpios = <10>; }; + + spi at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + compatible = "sandbox,spi"; + cs-gpios = <0>, <&gpio_a 0>; + spi.bin at 0 { + reg = <0>; + compatible = "spansion,m25p16", "spi-flash"; + spi-max-frequency = <40000000>; + sandbox,filename = "spi.bin"; + }; + }; + };
Signature · 1 line
2.0.0.526.g5318336
PATCH 23/25 dm: cros_ec: Add support for driver model
In reply to Simon GlassAdd support for driver model if enabled. This involves minimal changes to the code, mostly just plumbing around the edges.
---
common/cros_ec.c | 30 ++++++++++ drivers/misc/cros_ec.c | 122 +++++++++++++++++++++++++++++++++++++---- drivers/misc/cros_ec_sandbox.c | 9 ++- include/cros_ec.h | 27 ++++++++- include/dm/uclass-id.h | 1 + 5 files changed, 174 insertions(+), 15 deletions(-)
common/cros_ec.c +30 -0
@@ -10,25 +10,44 @@#include <common.h> #include <cros_ec.h> +#include <dm.h> +#include <errno.h> +DECLARE_GLOBAL_DATA_PTR ; +#ifndef CONFIG_DM_CROS_EC struct local_info {struct cros_ec_dev *cros_ec_dev;/* Pointer to cros_ec device */ int cros_ec_err;/* Error for cros_ec, 0 if ok */ };static struct local_info local; +#endif struct cros_ec_dev *board_get_cros_ec_dev (void ) { +#ifdef CONFIG_DM_CROS_EC +struct udevice *dev; +int ret; + + ret =uclass_get_device (UCLASS_CROS_EC ,0 , &dev); +if (ret) { +debug ("%s: Error %d\n" , __func__, ret); +return NULL ; + } +return dev->uclass_priv; +#else return local.cros_ec_dev; +#endif }static int board_init_cros_ec_devices (const void *blob) { +#ifndef CONFIG_DM_CROS_EC local.cros_ec_err =cros_ec_init (blob, &local.cros_ec_dev);if (local.cros_ec_err)return -1 ;/* Will report in board_late_init() */ +#endif return 0 ; }
@@ -40,5 +59,16 @@ int cros_ec_board_init(void)int cros_ec_get_error (void ) { +#ifdef CONFIG_DM_CROS_EC +struct udevice *dev; +int ret; + + ret =uclass_get_device (UCLASS_CROS_EC ,0 , &dev); +if (ret && ret != -ENODEV ) +return ret; + +return 0 ; +#else return local.cros_ec_err; +#endif }
drivers/misc/cros_ec.c +110 -12
248 lines not on this page · raw message
drivers/misc/cros_ec_sandbox.c +7 -2
@@ -525,8 +525,13 @@ int cros_ec_sandbox_init(struct cros_ec_dev *dev, const void *blob)int node;int err; - state = &s_state; - err =cros_ec_decode_ec_flash (blob, &ec->ec_config); + node =fdtdec_next_compatible (blob,0 ,COMPAT_GOOGLE_CROS_EC ); +if (node <0 ) { +debug ("Failed to find chrome-ec node'\n" ); +return -1 ; + } + + err =cros_ec_decode_ec_flash (blob, node, &ec->ec_config);if (err)return err;
include/cros_ec.h +26 -1
@@ -14,6 +14,7 @@#include <fdtdec.h> #include <cros_ec_message.h> +#ifndef CONFIG_DM_CROS_EC /* Which interface is the device on? */ enum cros_ec_interface_t { CROS_EC_IF_NONE,
@@ -22,9 +23,13 @@ enum cros_ec_interface_t {CROS_EC_IF_LPC ,/* Intel Low Pin Count interface */ CROS_EC_IF_SANDBOX, }; +#endif /* Our configuration information */ struct cros_ec_dev { +#ifdef CONFIG_DM_CROS_EC +struct udevice *dev;/* Transport device */ +#else enum cros_ec_interface_t interface;struct spi_slave *spi;/* Our SPI slave, if using SPI */ int node;/* Our node */
@@ -33,6 +38,7 @@ struct cros_ec_dev {unsigned int addr;/* Device address (for I2C) */ unsigned int bus_num;/* Bus number (for I2C) */ unsigned int max_frequency;/* Maximum interface frequency */ +#endif struct fdt_gpio_state ec_int;/* GPIO used as EC interrupt line */ int protocol_version;/* Protocol version to use */ int optimise_flash_write;/* Don't write erased flash blocks */
@@ -233,6 +239,22 @@ int cros_ec_flash_update_rw(struct cros_ec_dev *dev,*/ struct cros_ec_dev *board_get_cros_ec_dev (void ); +#ifdef CONFIG_DM_CROS_EC + +struct dm_cros_ec_ops { +int (*check_version)(struct udevice *dev); +int (*command)(struct udevice *dev,uint8_t cmd,int cmd_version, +const uint8_t *dout,int dout_len, +uint8_t **dinp,int din_len); +int (*packet)(struct udevice *dev,int out_bytes,int in_bytes); +}; + +#define dm_cros_ec_get_ops (dev) \ + ((struct dm_cros_ec_ops *)(dev)->driver->ops) + +int cros_ec_register (struct udevice *dev); + +#else /* !CONFIG_DM_CROS_EC */ /* Internal interfaces */ int cros_ec_i2c_init (struct cros_ec_dev *dev,const void *blob);
@@ -336,6 +358,7 @@ int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version,int cros_ec_spi_packet (struct cros_ec_dev *dev,int out_bytes,int in_bytes);int cros_ec_sandbox_packet (struct cros_ec_dev *dev,int out_bytes,int in_bytes); +#endif /** * Dump a block of data for a command.
@@ -489,9 +512,11 @@ int cros_ec_get_error(void);* Returns information from the FDT about the Chrome EC flash * * @param blob FDT blob to use +* @param node Node offset to read from * @param config Structure to use to return information */ -int cros_ec_decode_ec_flash (const void *blob,struct fdt_cros_ec *config); +int cros_ec_decode_ec_flash (const void *blob,int node, +struct fdt_cros_ec *config);/** * Check the current keyboard state, in case recovery mode is requested.
include/dm/uclass-id.h +1 -0
@@ -25,6 +25,7 @@ enum uclass_id {UCLASS_SERIAL ,/* Serial UART */ UCLASS_SPI ,/* SPI bus */ UCLASS_SPI_FLASH ,/* SPI flash */ +UCLASS_CROS_EC ,/* Chrome OS EC */ UCLASS_COUNT , UCLASS_INVALID = -1,
Signature · 1 line
2.0.0.526.g5318336
PATCH 24/25 dm: sandbox: cros_ec: Move sandbox cros_ec to driver module
In reply to Simon GlassAdjust the sandbox cros_ec emulation driver to work with driver model, and switch over to driver model for sandbox cros_ec.
---
drivers/misc/cros_ec_sandbox.c | 90 +++++++++++++++++++++++++++++++++++++++--- include/configs/sandbox.h | 1 + 2 files changed, 86 insertions(+), 5 deletions(-)
drivers/misc/cros_ec_sandbox.c +85 -5
@@ -8,6 +8,7 @@#include <common.h> #include <cros_ec.h> +#include <dm.h> #include <ec_commands.h> #include <errno.h> #include <hash.h>
@@ -85,7 +86,7 @@ struct ec_state {struct ec_keymatrix_entry *matrix;/* the key matrix info */ uint8_t keyscan[KEYBOARD_COLS ];bool recovery_req; -} s_state, *state; +} s_state, *g_state;/** * cros_ec_read_state() - read the sandbox EC state from the state file
@@ -138,7 +139,7 @@ static int cros_ec_read_state(const void *blob, int node)*/ static int cros_ec_write_state (void *blob,int node) { -struct ec_state *ec = &s_state; +struct ec_state *ec = g_state;/* We are guaranteed enough space to write basic properties */ fdt_setprop_u32 (blob, node,"current-image" , ec->current_image);
@@ -369,7 +370,7 @@ static int process_cmd(struct ec_state *ec,struct fmap_entry *entry;int ret, size; - entry = &state->ec_config.region[EC_FLASH_REGION_RW ]; + entry = &ec->ec_config.region[EC_FLASH_REGION_RW ];switch (req->cmd) {case EC_VBOOT_HASH_RECALC :
@@ -426,7 +427,7 @@ static int process_cmd(struct ec_state *ec,case EC_FLASH_REGION_RO :case EC_FLASH_REGION_RW :case EC_FLASH_REGION_WP_RO : - entry = &state->ec_config.region[req->region]; + entry = &ec->ec_config.region[req->region]; resp->offset = entry->offset; resp->size = entry->length; len =sizeof (*resp);
@@ -466,16 +467,24 @@ static int process_cmd(struct ec_state *ec,return len; } +#ifdef CONFIG_DM_CROS_EC +int cros_ec_sandbox_packet (struct udevice *udev,int out_bytes,int in_bytes) +{ +struct cros_ec_dev *dev = udev->uclass_priv; +struct ec_state *ec =dev_get_priv (dev->dev); +#else int cros_ec_sandbox_packet (struct cros_ec_dev *dev,int out_bytes,int in_bytes) { +struct ec_state *ec = &s_state; +#endif struct ec_host_request *req_hdr = (struct ec_host_request *)dev->dout;const void *req_data = req_hdr +1 ;struct ec_host_response *resp_hdr = (struct ec_host_response *)dev->din;void *resp_data = resp_hdr +1 ;int len; - len =process_cmd (&s_state, req_hdr, req_data, resp_hdr, resp_data); + len =process_cmd (ec, req_hdr, req_data, resp_hdr, resp_data);if (len <0 )return len;
@@ -498,7 +507,11 @@ int cros_ec_sandbox_decode_fdt(struct cros_ec_dev *dev, const void *blob)void cros_ec_check_keyboard (struct cros_ec_dev *dev) { +#ifdef CONFIG_DM_CROS_EC +struct ec_state *ec =dev_get_priv (dev->dev); +#else struct ec_state *ec = &s_state; +#endif ulong start;printf ("Press keys for EC to detect on reset (ESC=recovery)..." );
@@ -512,6 +525,52 @@ void cros_ec_check_keyboard(struct cros_ec_dev *dev) } } +#ifdef CONFIG_DM_CROS_EC +int cros_ec_probe (struct udevice *dev) +{ +struct ec_state *ec = dev->priv; +struct cros_ec_dev *cdev = dev->uclass_priv; +const void *blob = gd->fdt_blob; +int node; +int err; + +memcpy (ec, &s_state,sizeof (*ec)); + err =cros_ec_decode_ec_flash (blob, dev->of_offset, &ec->ec_config); +if (err) +return err; + + node =fdtdec_next_compatible (blob,0 ,COMPAT_GOOGLE_CROS_EC_KEYB ); +if (node <0 ) { +debug ("%s: No cros_ec keyboard found\n" , __func__); + }else if (keyscan_read_fdt_matrix (ec, blob, node)) { +debug ("%s: Could not read key matrix\n" , __func__); +return -1 ; + } + +/* If we loaded EC data, check that the length matches */ +if (ec->flash_data && + ec->flash_data_len != ec->ec_config.flash.length) { +printf ("EC data length is %x, expected %x, discarding data\n" , + ec->flash_data_len, ec->ec_config.flash.length); +os_free (ec->flash_data); + ec->flash_data =NULL ; + } + +/* Otherwise allocate the memory */ +if (!ec->flash_data) { + ec->flash_data_len = ec->ec_config.flash.length; + ec->flash_data =os_malloc (ec->flash_data_len); +if (!ec->flash_data) +return -ENOMEM ; + } + + cdev->dev = dev; + g_state = ec; +return cros_ec_register (dev); +} + +#else +/** * Initialize sandbox EC emulation. *
@@ -562,3 +621,24 @@ int cros_ec_sandbox_init(struct cros_ec_dev *dev, const void *blob) return 0; } +#endif + +#ifdef CONFIG_DM_CROS_EC +struct dm_cros_ec_ops cros_ec_ops = { + .packet = cros_ec_sandbox_packet, +}; + +static const struct udevice_id cros_ec_ids[] = { + { .compatible ="google,cros-ec" }, + { } +}; + +U_BOOT_DRIVER (cros_ec_sandbox) = { + .name ="cros_ec" , + .id =UCLASS_CROS_EC , + .of_match = cros_ec_ids, + .probe = cros_ec_probe, + .priv_auto_alloc_size =sizeof (struct ec_state ), + .ops = &cros_ec_ops, +}; +#endif
include/configs/sandbox.h +1 -0
@@ -32,6 +32,7 @@#define CONFIG_DM_GPIO #define CONFIG_DM_TEST #define CONFIG_DM_SERIAL +#define CONFIG_DM_CROS_EC #define CONFIG_SYS_STDIO_DEREGISTER
Signature · 1 line
2.0.0.526.g5318336
PATCH 25/25 dm: exynos: cros_ec: Move cros_ec_spi to driver model
In reply to Simon GlassAdjust this driver to use driver model and move smdk5420 boards over to use it.
---
drivers/misc/cros_ec_spi.c | 68 ++++++++++++++++++++++++++++++++++++++++------ include/configs/smdk5420.h | 1 + 2 files changed, 60 insertions(+), 9 deletions(-)
drivers/misc/cros_ec_spi.c +59 -9
@@ -15,23 +15,34 @@#include <common.h> #include <cros_ec.h> +#include <dm.h> +#include <errno.h> #include <spi.h> +DECLARE_GLOBAL_DATA_PTR ; + +#ifdef CONFIG_DM_CROS_EC +int cros_ec_spi_packet (struct udevice *udev,int out_bytes,int in_bytes) +{ +struct cros_ec_dev *dev = udev->uclass_priv; +#else int cros_ec_spi_packet (struct cros_ec_dev *dev,int out_bytes,int in_bytes) { +#endif +struct spi_slave *slave =dev_get_parentdata (dev->dev);int rv;/* Do the transfer */ -if (spi_claim_bus (dev->spi)) { +if (spi_claim_bus (slave)) {debug ("%s: Cannot claim SPI bus\n" , __func__);return -1 ; } - rv =spi_xfer (dev->spi,max (out_bytes, in_bytes) *8 , + rv =spi_xfer (slave,max (out_bytes, in_bytes) *8 , dev->dout, dev->din,SPI_XFER_BEGIN |SPI_XFER_END ); -spi_release_bus (dev->spi); +spi_release_bus (slave);if (rv) {debug ("%s: Cannot complete SPI transfer\n" , __func__);
@@ -56,10 +67,19 @@ int cros_ec_spi_packet(struct cros_ec_dev *dev, int out_bytes, int in_bytes)* @param din_len Maximum size of response in bytes * @return number of bytes in response, or -1 on error */ +#ifdef CONFIG_DM_CROS_EC +int cros_ec_spi_command (struct udevice *udev,uint8_t cmd,int cmd_version, +const uint8_t *dout,int dout_len, +uint8_t **dinp,int din_len) +{ +struct cros_ec_dev *dev = udev->uclass_priv; +#else int cros_ec_spi_command (struct cros_ec_dev *dev,uint8_t cmd,int cmd_version,const uint8_t *dout,int dout_len,uint8_t **dinp,int din_len) { +#endif +struct spi_slave *slave =dev_get_parentdata (dev->dev);int in_bytes = din_len +4 ;/* status, length, checksum, trailer */ uint8_t *out;uint8_t *p;
@@ -92,7 +112,7 @@ int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version,*/ memset (dev->din,'\0' , in_bytes); -if (spi_claim_bus (dev->spi)) { +if (spi_claim_bus (slave)) {debug ("%s: Cannot claim SPI bus\n" , __func__);return -1 ; }
@@ -113,10 +133,10 @@ int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, p = dev->din +sizeof (int64_t ) -2 ; len = dout_len +4 ;cros_ec_dump_data ("out" , cmd, out, len); - rv =spi_xfer (dev->spi,max (len, in_bytes) *8 , out, p, + rv =spi_xfer (slave,max (len, in_bytes) *8 , out, p,SPI_XFER_BEGIN |SPI_XFER_END ); -spi_release_bus (dev->spi); +spi_release_bus (slave);if (rv) {debug ("%s: Cannot complete SPI transfer\n" , __func__);
@@ -146,6 +166,7 @@ int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version,return len; } +#ifndef CONFIG_DM_CROS_EC int cros_ec_spi_decode_fdt (struct cros_ec_dev *dev,const void *blob) {/* Decode interface-specific FDT params */
@@ -165,11 +186,40 @@ int cros_ec_spi_decode_fdt(struct cros_ec_dev *dev, const void *blob)*/ int cros_ec_spi_init (struct cros_ec_dev *dev,const void *blob) { - dev->spi =spi_setup_slave_fdt (blob, dev->node, dev->parent_node); -if (!dev->spi) { +int ret; + + ret =spi_setup_slave_fdt (blob, dev->node, dev->parent_node, + &slave); +if (ret) {debug ("%s: Could not setup SPI slave\n" , __func__); -return -1 ; +return ret; }return 0 ; } +#endif + +#ifdef CONFIG_DM_CROS_EC +int cros_ec_probe (struct udevice *dev) +{ +return cros_ec_register (dev); +} + +struct dm_cros_ec_ops cros_ec_ops = { + .packet = cros_ec_spi_packet, + .command = cros_ec_spi_command, +}; + +static const struct udevice_id cros_ec_ids[] = { + { .compatible ="google,cros-ec" }, + { } +}; + +U_BOOT_DRIVER (cros_ec_spi) = { + .name ="cros_ec" , + .id =UCLASS_CROS_EC , + .of_match = cros_ec_ids, + .probe = cros_ec_probe, + .ops = &cros_ec_ops, +}; +#endif
include/configs/smdk5420.h +1 -0
@@ -26,5 +26,6 @@#define CONFIG_POWER_TPS65090_EC #define CONFIG_CROS_EC_SPI /* Support CROS_EC over SPI */ +#define CONFIG_DM_CROS_EC #endif /* __CONFIG_SMDK5420_H */
Signature · 1 line
2.0.0.526.g5318336
cover Introduce driver model support for SPI, SPI flash, cros_ec
In reply to Simon GlassHi,
Quoted 42 lines · Simon Glass
Up until now driver model has not been used for any type of bus. Buses have some unique properties and needs, so we cannot claim that driver model can cover all the common cases unless we have converted a bus over to driver model.
SPI is a reasonable choice for this next step. It has a fairly simple API and not too many dependencies. The main one is SPI flash so we may as well convert that also. Since the boards I test with have
cros_ecI have also included that, for SPI only.The technique used is make use of driver model's supported data structures to hold information currently kept by each subsystem in a private data structure. Since 'struct
spi_slave' relates to the slave device on the bus it is stored in the 'parent' data with each child device of the bus. Since 'structspi_flash' is a standard interface used for each SPI flash driver, it is stored in the SPI FLash uclass's private data for each device.New defines are created to enable driver model for each subsystem. These are:
CONFIG_DM_SPI
CONFIG_DM_SPI_FLASH
CONFIG_DM_CROS_ECThis allows us to move some boards and drivers to driver model, while leaving others behind. A 'big bang' conversion of everything to driver model, event at a subsystem level, is never going to work.
On the other hand, we change the driver at the same time as the CONFIG option is enabled. Keeping both version of the driver around involves a flock of #ifdefs, the benefit of which is not apparent to me, since the old code is removed anyway.
There is some cost in changing the uclass interface after it is created, so if you have limited time, please spend it reviewing the uclass interfaces in
spi.handspi_flash.h. These need to be supported by each driver, so changing them later may involve changing multiple drivers.To help with conversion of SPI drivers to driver model, documentation is provided which takes the happy camper through the process with an example.
Quoted 6 lines
As always, driver model patches are available at u-boot-dm.git branch 'working'.
Note: This series is not fully ready - e.g. some header files are missing comments. But I wanted to get it out for review early since some SPI work is ongoing which might depend on it.
Are there any comments on this series please? I'd like to apply these to dm/master early next week, excluding the exynos ones.
Regards,
Simon
cover Introduce driver model support for SPI, SPI flash, cros_ec
Quoted 48 lines · Simon Glass
Hi,
Quoted 45 lines · Simon Glass
Up until now driver model has not been used for any type of bus. Buses have some unique properties and needs, so we cannot claim that driver model can cover all the common cases unless we have converted a bus over to driver model.
SPI is a reasonable choice for this next step. It has a fairly simple API and not too many dependencies. The main one is SPI flash so we may as well convert that also. Since the boards I test with have
cros_ecI have also included that, for SPI only.The technique used is make use of driver model's supported data structures to hold information currently kept by each subsystem in a private data structure. Since 'struct
spi_slave' relates to the slave device on the bus it is stored in the 'parent' data with each child device of the bus. Since 'structspi_flash' is a standard interface used for each SPI flash driver, it is stored in the SPI FLash uclass's private data for each device.New defines are created to enable driver model for each subsystem. These are:
CONFIG_DM_SPI
CONFIG_DM_SPI_FLASH
CONFIG_DM_CROS_ECThis allows us to move some boards and drivers to driver model, while leaving others behind. A 'big bang' conversion of everything to driver model, event at a subsystem level, is never going to work.
On the other hand, we change the driver at the same time as the CONFIG option is enabled. Keeping both version of the driver around involves a flock of #ifdefs, the benefit of which is not apparent to me, since the old code is removed anyway.
There is some cost in changing the uclass interface after it is created, so if you have limited time, please spend it reviewing the uclass interfaces in
spi.handspi_flash.h. These need to be supported by each driver, so changing them later may involve changing multiple drivers.To help with conversion of SPI drivers to driver model, documentation is provided which takes the happy camper through the process with an example.
As always, driver model patches are available at u-boot-dm.git branch 'working'.
Quoted 6 lines
Quoted 3 lines
Note: This series is not fully ready - e.g. some header files are missing comments. But I wanted to get it out for review early since some SPI work is ongoing which might depend on it.
Are there any comments on this series please? I'd like to apply these to dm/master early next week, excluding the exynos ones.
I will be busy in next full week, give me 2 more weeks. Probably I will give my comments on next to next weekend.
thanks!
Signature · 1 line
Jagan.
cover Introduce driver model support for SPI, SPI flash, cros_ec
Hi Jagan,
Quoted 53 lines · Jagan Teki
Quoted 52 lines · Simon Glass
Hi,
Quoted 48 lines · Simon Glass
Up until now driver model has not been used for any type of bus. Buses have some unique properties and needs, so we cannot claim that driver model can cover all the common cases unless we have converted a bus over to driver model.
SPI is a reasonable choice for this next step. It has a fairly simple API and not too many dependencies. The main one is SPI flash so we may as well convert that also. Since the boards I test with have
cros_ecI have also included that, for SPI only.The technique used is make use of driver model's supported data structures to hold information currently kept by each subsystem in a private data structure. Since 'struct
spi_slave' relates to the slave device on the bus it is stored in the 'parent' data with each child device of the bus. Since 'structspi_flash' is a standard interface used for each SPI flash driver, it is stored in the SPI FLash uclass's private data for each device.New defines are created to enable driver model for each subsystem. These are:
CONFIG_DM_SPI
CONFIG_DM_SPI_FLASH
CONFIG_DM_CROS_ECThis allows us to move some boards and drivers to driver model, while leaving others behind. A 'big bang' conversion of everything to driver model, event at a subsystem level, is never going to work.
On the other hand, we change the driver at the same time as the CONFIG option is enabled. Keeping both version of the driver around involves a flock of #ifdefs, the benefit of which is not apparent to me, since the old code is removed anyway.
There is some cost in changing the uclass interface after it is created, so if you have limited time, please spend it reviewing the uclass interfaces in
spi.handspi_flash.h. These need to be supported by each driver, so changing them later may involve changing multiple drivers.To help with conversion of SPI drivers to driver model, documentation is provided which takes the happy camper through the process with an example.
As always, driver model patches are available at u-boot-dm.git branch 'working'.
Note: This series is not fully ready - e.g. some header files are missing comments. But I wanted to get it out for review early since some SPI work is ongoing which might depend on it.
Quoted 5 lines
Quoted 2 lines
Are there any comments on this series please? I'd like to apply these to dm/master early next week, excluding the exynos ones.
I will be busy in next full week, give me 2 more weeks. Probably I will give my comments on next to next weekend.
I'll see what I can do. It has been out for review for 4 weeks :-)
Regards,
Simon
cover Introduce driver model support for SPI, SPI flash, cros_ec
Hi Jagan,
Quoted 56 lines · Simon Glass
Hi Jagan,
Quoted 53 lines · Jagan Teki
Quoted 52 lines · Simon Glass
Hi,
Quoted 48 lines · Simon Glass
Up until now driver model has not been used for any type of bus. Buses have some unique properties and needs, so we cannot claim that driver model can cover all the common cases unless we have converted a bus over to driver model.
SPI is a reasonable choice for this next step. It has a fairly simple API and not too many dependencies. The main one is SPI flash so we may as well convert that also. Since the boards I test with have
cros_ecI have also included that, for SPI only.The technique used is make use of driver model's supported data structures to hold information currently kept by each subsystem in a private data structure. Since 'struct
spi_slave' relates to the slave device on the bus it is stored in the 'parent' data with each child device of the bus. Since 'structspi_flash' is a standard interface used for each SPI flash driver, it is stored in the SPI FLash uclass's private data for each device.New defines are created to enable driver model for each subsystem. These are:
CONFIG_DM_SPI
CONFIG_DM_SPI_FLASH
CONFIG_DM_CROS_ECThis allows us to move some boards and drivers to driver model, while leaving others behind. A 'big bang' conversion of everything to driver model, event at a subsystem level, is never going to work.
On the other hand, we change the driver at the same time as the CONFIG option is enabled. Keeping both version of the driver around involves a flock of #ifdefs, the benefit of which is not apparent to me, since the old code is removed anyway.
There is some cost in changing the uclass interface after it is created, so if you have limited time, please spend it reviewing the uclass interfaces in
spi.handspi_flash.h. These need to be supported by each driver, so changing them later may involve changing multiple drivers.To help with conversion of SPI drivers to driver model, documentation is provided which takes the happy camper through the process with an example.
As always, driver model patches are available at u-boot-dm.git branch 'working'.
Note: This series is not fully ready - e.g. some header files are missing comments. But I wanted to get it out for review early since some SPI work is ongoing which might depend on it.
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
Are there any comments on this series please? I'd like to apply these to dm/master early next week, excluding the exynos ones.
I will be busy in next full week, give me 2 more weeks. Probably I will give my comments on next to next weekend.
I'll see what I can do. It has been out for review for 4 weeks :-)
I'll send an updated series with your comments address. I'm planning to apply the non-exynos parts of SPI this week. Please let me know if you have any objections. I'd like to allow sufficient test time before the release.
Regards,
Simon