From 3669e0e759118fed3d371e2427b4b47d3969bfd0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:29 -0700 Subject: dm: gpio: Add better functions to request GPIOs At present U-Boot sort-of supports the standard way of reading GPIOs from device tree nodes, but the support is incomplete, a bit clunky and only works for GPIO bindings where #gpio-cells is 2. Add new functions to request GPIOs, taking full account of the device tree binding. These permit requesting a GPIO with a simple call like: gpio_request_by_name(dev, "cd-gpios", 0, &desc, GPIOD_IS_IN); This will request the GPIO, looking at the device's node which might be this, for example: cd-gpios = <&gpio TEGRA_GPIO(B, 3) GPIO_ACTIVE_LOW>; The GPIO will be set to input mode in this case and polarity will be honoured by the GPIO calls. It is also possible to request and free a list of GPIOs. Signed-off-by: Simon Glass --- test/dm/gpio.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- test/dm/test.dts | 11 ++++++++- 2 files changed, 78 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/dm/gpio.c b/test/dm/gpio.c index 94bd0d99dc0..b29daf1af4e 100644 --- a/test/dm/gpio.c +++ b/test/dm/gpio.c @@ -174,5 +174,72 @@ static int dm_test_gpio_leak(struct dm_test_state *dms) return 0; } - DM_TEST(dm_test_gpio_leak, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test that we can find GPIOs using phandles */ +static int dm_test_gpio_phandles(struct dm_test_state *dms) +{ + struct gpio_desc desc, desc_list[8], desc_list2[8]; + struct udevice *dev, *gpio_a, *gpio_b; + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + + ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio_a)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 2, &gpio_b)); + ut_asserteq_str("base-gpios", gpio_a->name); + ut_asserteq(true, !!device_active(gpio_a)); + ut_asserteq_ptr(gpio_a, desc.dev); + ut_asserteq(4, desc.offset); + /* GPIOF_INPUT is the sandbox GPIO driver default */ + ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_a, 4, NULL)); + ut_assertok(dm_gpio_free(dev, &desc)); + + ut_asserteq(-ENOENT, gpio_request_by_name(dev, "test-gpios", 3, &desc, + 0)); + ut_asserteq_ptr(NULL, desc.dev); + ut_asserteq(desc.offset, 0); + ut_asserteq(-ENOENT, gpio_request_by_name(dev, "test-gpios", 5, &desc, + 0)); + + /* Last GPIO is ignord as it comes after <0> */ + ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc_list, + ARRAY_SIZE(desc_list), 0)); + ut_asserteq(-EBUSY, gpio_request_list_by_name(dev, "test-gpios", + desc_list2, + ARRAY_SIZE(desc_list2), + 0)); + ut_assertok(gpio_free_list(dev, desc_list, 3)); + ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc_list, + ARRAY_SIZE(desc_list), + GPIOD_IS_OUT | + GPIOD_IS_OUT_ACTIVE)); + ut_asserteq_ptr(gpio_a, desc_list[0].dev); + ut_asserteq(1, desc_list[0].offset); + ut_asserteq_ptr(gpio_a, desc_list[1].dev); + ut_asserteq(4, desc_list[1].offset); + ut_asserteq_ptr(gpio_b, desc_list[2].dev); + ut_asserteq(5, desc_list[2].offset); + ut_asserteq(1, dm_gpio_get_value(desc_list)); + ut_assertok(gpio_free_list(dev, desc_list, 3)); + + ut_asserteq(6, gpio_request_list_by_name(dev, "test2-gpios", desc_list, + ARRAY_SIZE(desc_list), 0)); + /* This was set to output previously, so still will be */ + ut_asserteq(GPIOF_OUTPUT, gpio_get_function(gpio_a, 1, NULL)); + + /* Active low should invert the input value */ + ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_b, 6, NULL)); + ut_asserteq(1, dm_gpio_get_value(&desc_list[2])); + + ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_b, 7, NULL)); + ut_asserteq(GPIOF_OUTPUT, gpio_get_function(gpio_b, 8, NULL)); + ut_asserteq(0, dm_gpio_get_value(&desc_list[4])); + ut_asserteq(GPIOF_OUTPUT, gpio_get_function(gpio_b, 9, NULL)); + ut_asserteq(1, dm_gpio_get_value(&desc_list[5])); + + + return 0; +} +DM_TEST(dm_test_gpio_phandles, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/test.dts b/test/dm/test.dts index fb0272a59cd..33f2c00747d 100644 --- a/test/dm/test.dts +++ b/test/dm/test.dts @@ -22,6 +22,11 @@ ping-expect = <0>; ping-add = <0>; u-boot,dm-pre-reloc; + test-gpios = <&gpio_a 1>, <&gpio_a 4>, <&gpio_b 5 0 3 2 1>, + <0>, <&gpio_a 12>; + test2-gpios = <&gpio_a 1>, <&gpio_a 4>, <&gpio_b 6 1 3 2 1>, + <&gpio_b 7 2 3 2 1>, <&gpio_b 8 4 3 2 1>, + <&gpio_b 9 0xc 3 2 1>; }; junk { @@ -83,12 +88,16 @@ gpio_a: base-gpios { compatible = "sandbox,gpio"; + gpio-controller; + #gpio-cells = <1>; gpio-bank-name = "a"; num-gpios = <20>; }; - extra-gpios { + gpio_b: extra-gpios { compatible = "sandbox,gpio"; + gpio-controller; + #gpio-cells = <5>; gpio-bank-name = "b"; num-gpios = <10>; }; -- cgit v1.3.1 From f9a4c2da72d04e13b05deecb800f232d2948eb85 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 12 Jan 2015 18:02:07 -0700 Subject: dm: i2c: Rename driver model I2C functions to permit compatibility Add a dm_ prefix to driver model I2C functions so that we can keep the old ones around. This is a little unfortunate, but on reflection it is too difficult to change the API. We can undo this rename when most boards and drivers are converted to use driver model for I2C. Signed-off-by: Simon Glass --- board/avionic-design/common/tamonten-ng.c | 2 +- board/nvidia/cardhu/cardhu.c | 8 ++--- board/nvidia/dalmore/dalmore.c | 6 ++-- board/nvidia/whistler/whistler.c | 10 +++--- board/toradex/apalis_t30/apalis_t30.c | 6 ++-- common/cmd_i2c.c | 28 ++++++++------- common/exports.c | 3 +- drivers/i2c/i2c-uclass.c | 9 ++--- drivers/power/as3722.c | 4 +-- drivers/power/tps6586x.c | 4 +-- include/i2c.h | 19 +++++----- test/dm/i2c.c | 60 +++++++++++++++---------------- 12 files changed, 82 insertions(+), 77 deletions(-) (limited to 'test') diff --git a/board/avionic-design/common/tamonten-ng.c b/board/avionic-design/common/tamonten-ng.c index 86a0844273d..bca91831e0e 100644 --- a/board/avionic-design/common/tamonten-ng.c +++ b/board/avionic-design/common/tamonten-ng.c @@ -60,7 +60,7 @@ void pmu_write(uchar reg, uchar data) debug("%s: Cannot find PMIC I2C chip\n", __func__); return; } - i2c_write(dev, reg, &data, 1); + dm_i2c_write(dev, reg, &data, 1); } /* diff --git a/board/nvidia/cardhu/cardhu.c b/board/nvidia/cardhu/cardhu.c index 95c4ff25092..fc31d29140b 100644 --- a/board/nvidia/cardhu/cardhu.c +++ b/board/nvidia/cardhu/cardhu.c @@ -57,7 +57,7 @@ void board_sdmmc_voltage_init(void) reg = 0x32; for (i = 0; i < MAX_I2C_RETRY; ++i) { - if (i2c_write(dev, reg, data_buffer, 1)) + if (dm_i2c_write(dev, reg, data_buffer, 1)) udelay(100); } @@ -66,7 +66,7 @@ void board_sdmmc_voltage_init(void) reg = 0x67; for (i = 0; i < MAX_I2C_RETRY; ++i) { - if (i2c_write(dev, reg, data_buffer, 1)) + if (dm_i2c_write(dev, reg, data_buffer, 1)) udelay(100); } } @@ -104,7 +104,7 @@ int tegra_pcie_board_init(void) data[0] = 0x15; addr = 0x30; - err = i2c_write(dev, addr, data, 1); + err = dm_i2c_write(dev, addr, data, 1); if (err) { debug("failed to set VDD supply\n"); return err; @@ -121,7 +121,7 @@ int tegra_pcie_board_init(void) data[0] = 0x15; addr = 0x31; - err = i2c_write(dev, addr, data, 1); + err = dm_i2c_write(dev, addr, data, 1); if (err) { debug("failed to set AVDD supply\n"); return err; diff --git a/board/nvidia/dalmore/dalmore.c b/board/nvidia/dalmore/dalmore.c index 2a737468ddc..c0991c57a97 100644 --- a/board/nvidia/dalmore/dalmore.c +++ b/board/nvidia/dalmore/dalmore.c @@ -65,7 +65,7 @@ void board_sdmmc_voltage_init(void) data_buffer[0] = 0x31; reg = 0x61; - ret = i2c_write(dev, reg, data_buffer, 1); + ret = dm_i2c_write(dev, reg, data_buffer, 1); if (ret) printf("%s: PMU i2c_write %02X<-%02X returned %d\n", __func__, reg, data_buffer[0], ret); @@ -74,7 +74,7 @@ void board_sdmmc_voltage_init(void) data_buffer[0] = 0x01; reg = 0x60; - ret = i2c_write(dev, reg, data_buffer, 1); + ret = dm_i2c_write(dev, reg, data_buffer, 1); if (ret) printf("%s: PMU i2c_write %02X<-%02X returned %d\n", __func__, reg, data_buffer[0], ret); @@ -88,7 +88,7 @@ void board_sdmmc_voltage_init(void) debug("%s: Cannot find charger I2C chip\n", __func__); return; } - ret = i2c_write(dev, reg, data_buffer, 1); + ret = dm_i2c_write(dev, reg, data_buffer, 1); if (ret) printf("%s: BAT i2c_write %02X<-%02X returned %d\n", __func__, reg, data_buffer[0], ret); diff --git a/board/nvidia/whistler/whistler.c b/board/nvidia/whistler/whistler.c index 3114b20be02..ad6ea091df7 100644 --- a/board/nvidia/whistler/whistler.c +++ b/board/nvidia/whistler/whistler.c @@ -33,15 +33,15 @@ void pin_mux_mmc(void) return; } val = 0x29; - ret = i2c_write(dev, 0x46, &val, 1); + ret = dm_i2c_write(dev, 0x46, &val, 1); if (ret) printf("i2c_write 0 0x3c 0x46 failed: %d\n", ret); val = 0x00; - ret = i2c_write(dev, 0x45, &val, 1); + ret = dm_i2c_write(dev, 0x45, &val, 1); if (ret) printf("i2c_write 0 0x3c 0x45 failed: %d\n", ret); val = 0x1f; - ret = i2c_write(dev, 0x44, &val, 1); + ret = dm_i2c_write(dev, 0x44, &val, 1); if (ret) printf("i2c_write 0 0x3c 0x44 failed: %d\n", ret); @@ -70,11 +70,11 @@ void pin_mux_usb(void) return; } val = 0x03; - ret = i2c_write(dev, 2, &val, 1); + ret = dm_i2c_write(dev, 2, &val, 1); if (ret) printf("i2c_write 0 0x20 2 failed: %d\n", ret); val = 0xfc; - ret = i2c_write(dev, 6, &val, 1); + ret = dm_i2c_write(dev, 6, &val, 1); if (ret) printf("i2c_write 0 0x20 6 failed: %d\n", ret); } diff --git a/board/toradex/apalis_t30/apalis_t30.c b/board/toradex/apalis_t30/apalis_t30.c index 5d2c024e890..1c4b4c1cd48 100644 --- a/board/toradex/apalis_t30/apalis_t30.c +++ b/board/toradex/apalis_t30/apalis_t30.c @@ -51,7 +51,7 @@ int tegra_pcie_board_init(void) data[0] = 0x27; addr = 0x25; - err = i2c_write(dev, addr, data, 1); + err = dm_i2c_write(dev, addr, data, 1); if (err) { debug("failed to set VDD supply\n"); return err; @@ -61,7 +61,7 @@ int tegra_pcie_board_init(void) data[0] = 0x0D; addr = 0x24; - err = i2c_write(dev, addr, data, 1); + err = dm_i2c_write(dev, addr, data, 1); if (err) { debug("failed to enable VDD supply\n"); return err; @@ -71,7 +71,7 @@ int tegra_pcie_board_init(void) data[0] = 0x0D; addr = 0x35; - err = i2c_write(dev, addr, data, 1); + err = dm_i2c_write(dev, addr, data, 1); if (err) { debug("failed to set AVDD supply\n"); return err; diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c index f65ab616b01..1e500fbf2cc 100644 --- a/common/cmd_i2c.c +++ b/common/cmd_i2c.c @@ -133,7 +133,7 @@ static uchar i2c_no_probes[] = CONFIG_SYS_I2C_NOPROBES; #ifdef CONFIG_DM_I2C static struct udevice *i2c_cur_bus; -static int i2c_set_bus_num(unsigned int busnum) +static int cmd_i2c_set_bus_num(unsigned int busnum) { struct udevice *bus; int ret; @@ -323,7 +323,7 @@ static int do_i2c_read ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv if (!ret && alen != -1) ret = i2c_set_chip_offset_len(dev, alen); if (!ret) - ret = i2c_read(dev, devaddr, memaddr, length); + ret = dm_i2c_read(dev, devaddr, memaddr, length); #else ret = i2c_read(chip, devaddr, alen, memaddr, length); #endif @@ -381,7 +381,7 @@ static int do_i2c_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[ while (length-- > 0) { #ifdef CONFIG_DM_I2C - ret = i2c_write(dev, devaddr++, memaddr++, 1); + ret = dm_i2c_write(dev, devaddr++, memaddr++, 1); #else ret = i2c_write(chip, devaddr++, alen, memaddr++, 1); #endif @@ -513,7 +513,7 @@ static int do_i2c_md ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes; #ifdef CONFIG_DM_I2C - ret = i2c_read(dev, addr, linebuf, linebytes); + ret = dm_i2c_read(dev, addr, linebuf, linebytes); #else ret = i2c_read(chip, addr, alen, linebuf, linebytes); #endif @@ -611,7 +611,7 @@ static int do_i2c_mw ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] while (count-- > 0) { #ifdef CONFIG_DM_I2C - ret = i2c_write(dev, addr++, &byte, 1); + ret = dm_i2c_write(dev, addr++, &byte, 1); #else ret = i2c_write(chip, addr++, alen, &byte, 1); #endif @@ -698,7 +698,7 @@ static int do_i2c_crc (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] err = 0; while (count-- > 0) { #ifdef CONFIG_DM_I2C - ret = i2c_read(dev, addr, &byte, 1); + ret = dm_i2c_read(dev, addr, &byte, 1); #else ret = i2c_read(chip, addr, alen, &byte, 1); #endif @@ -793,7 +793,7 @@ mod_i2c_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const arg do { printf("%08lx:", addr); #ifdef CONFIG_DM_I2C - ret = i2c_read(dev, addr, (uchar *)&data, size); + ret = dm_i2c_read(dev, addr, (uchar *)&data, size); #else ret = i2c_read(chip, addr, alen, (uchar *)&data, size); #endif @@ -841,8 +841,8 @@ mod_i2c_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const arg */ bootretry_reset_cmd_timeout(); #ifdef CONFIG_DM_I2C - ret = i2c_write(dev, addr, (uchar *)&data, - size); + ret = dm_i2c_write(dev, addr, (uchar *)&data, + size); #else ret = i2c_write(chip, addr, alen, (uchar *)&data, size); @@ -917,7 +917,7 @@ static int do_i2c_probe (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv continue; #endif #ifdef CONFIG_DM_I2C - ret = i2c_probe(bus, j, 0, &dev); + ret = dm_i2c_probe(bus, j, 0, &dev); #else ret = i2c_probe(j); #endif @@ -1010,7 +1010,7 @@ static int do_i2c_loop(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] */ while (1) { #ifdef CONFIG_DM_I2C - ret = i2c_read(dev, addr, bytes, length); + ret = dm_i2c_read(dev, addr, bytes, length); #else ret = i2c_read(chip, addr, alen, bytes, length); #endif @@ -1579,7 +1579,7 @@ int do_edid(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) #ifdef CONFIG_DM_I2C ret = i2c_get_cur_bus_chip(chip, &dev); if (!ret) - ret = i2c_read(dev, 0, (uchar *)&edid, sizeof(edid)); + ret = dm_i2c_read(dev, 0, (uchar *)&edid, sizeof(edid)); #else ret = i2c_read(chip, 0, 1, (uchar *)&edid, sizeof(edid)); #endif @@ -1696,7 +1696,11 @@ static int do_i2c_bus_num(cmd_tbl_t *cmdtp, int flag, int argc, } #endif printf("Setting bus to %d\n", bus_no); +#ifdef CONFIG_DM_I2C + ret = cmd_i2c_set_bus_num(bus_no); +#else ret = i2c_set_bus_num(bus_no); +#endif if (ret) printf("Failure changing bus number (%d)\n", ret); } diff --git a/common/exports.c b/common/exports.c index 88fcfc8cb6f..459e18cb097 100644 --- a/common/exports.c +++ b/common/exports.c @@ -23,7 +23,8 @@ unsigned long get_version(void) # define install_hdlr irq_install_handler # define free_hdlr irq_free_handler #endif -#ifndef CONFIG_CMD_I2C +#if !defined(CONFIG_CMD_I2C) || \ + (defined(CONFIG_DM_I2C) && !defined(CONFIG_DM_I2C_COMPAT)) # define i2c_write dummy # define i2c_read dummy #endif diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 005bf8662f2..25f2c18cf4f 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -100,7 +100,7 @@ static int i2c_write_bytewise(struct udevice *dev, uint offset, return 0; } -int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len) +int dm_i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len) { struct dm_i2c_chip *chip = dev_get_parentdata(dev); struct udevice *bus = dev_get_parent(dev); @@ -130,7 +130,8 @@ int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len) return ops->xfer(bus, msg, msg_count); } -int i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, int len) +int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, + int len) { struct dm_i2c_chip *chip = dev_get_parentdata(dev); struct udevice *bus = dev_get_parent(dev); @@ -303,8 +304,8 @@ int i2c_get_chip_for_busnum(int busnum, int chip_addr, struct udevice **devp) return 0; } -int i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, - struct udevice **devp) +int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, + struct udevice **devp) { int ret; diff --git a/drivers/power/as3722.c b/drivers/power/as3722.c index 4c6de79cd60..3aafdc96704 100644 --- a/drivers/power/as3722.c +++ b/drivers/power/as3722.c @@ -31,7 +31,7 @@ static int as3722_read(struct udevice *pmic, u8 reg, u8 *value) { int err; - err = i2c_read(pmic, reg, value, 1); + err = dm_i2c_read(pmic, reg, value, 1); if (err < 0) return err; @@ -42,7 +42,7 @@ static int as3722_write(struct udevice *pmic, u8 reg, u8 value) { int err; - err = i2c_write(pmic, reg, &value, 1); + err = dm_i2c_write(pmic, reg, &value, 1); if (err < 0) return err; diff --git a/drivers/power/tps6586x.c b/drivers/power/tps6586x.c index 29bab4cc00d..865098386d9 100644 --- a/drivers/power/tps6586x.c +++ b/drivers/power/tps6586x.c @@ -37,7 +37,7 @@ static int tps6586x_read(int reg) int retval = -1; for (i = 0; i < MAX_I2C_RETRY; ++i) { - if (!i2c_read(tps6586x_dev, reg, &data, 1)) { + if (!dm_i2c_read(tps6586x_dev, reg, &data, 1)) { retval = (int)data; goto exit; } @@ -60,7 +60,7 @@ static int tps6586x_write(int reg, uchar *data, uint len) int retval = -1; for (i = 0; i < MAX_I2C_RETRY; ++i) { - if (!i2c_write(tps6586x_dev, reg, data, len)) { + if (!dm_i2c_write(tps6586x_dev, reg, data, len)) { retval = 0; goto exit; } diff --git a/include/i2c.h b/include/i2c.h index 9c6a60cf9ae..30d53c8a9df 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -75,7 +75,7 @@ struct dm_i2c_bus { }; /** - * i2c_read() - read bytes from an I2C chip + * dm_i2c_read() - read bytes from an I2C chip * * To obtain an I2C device (called a 'chip') given the I2C bus address you * can use i2c_get_chip(). To obtain a bus by bus number use @@ -91,13 +91,12 @@ struct dm_i2c_bus { * * @return 0 on success, -ve on failure */ -int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, - int len); +int dm_i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len); /** - * i2c_write() - write bytes to an I2C chip + * dm_i2c_write() - write bytes to an I2C chip * - * See notes for i2c_read() above. + * See notes for dm_i2c_read() above. * * @dev: Chip to write to * @offset: Offset within chip to start writing @@ -106,11 +105,11 @@ int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, * * @return 0 on success, -ve on failure */ -int i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, - int len); +int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, + int len); /** - * i2c_probe() - probe a particular chip address + * dm_i2c_probe() - probe a particular chip address * * This can be useful to check for the existence of a chip on the bus. * It is typically implemented by writing the chip address to the bus @@ -122,8 +121,8 @@ int i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, * @devp: Returns the device found, or NULL if none * @return 0 if a chip was found at that address, -ve if not */ -int i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, - struct udevice **devp); +int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, + struct udevice **devp); /** * i2c_set_bus_speed() - set the speed of a bus diff --git a/test/dm/i2c.c b/test/dm/i2c.c index a53e28dbe5d..4823b0c6483 100644 --- a/test/dm/i2c.c +++ b/test/dm/i2c.c @@ -35,8 +35,8 @@ static int dm_test_i2c_find(struct dm_test_state *dms) * remove the emulation and the slave device. */ ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); - ut_assertok(i2c_probe(bus, chip, 0, &dev)); - ut_asserteq(-ENODEV, i2c_probe(bus, no_chip, 0, &dev)); + ut_assertok(dm_i2c_probe(bus, chip, 0, &dev)); + ut_asserteq(-ENODEV, dm_i2c_probe(bus, no_chip, 0, &dev)); ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_I2C, 1, &bus)); return 0; @@ -50,10 +50,10 @@ static int dm_test_i2c_read_write(struct dm_test_state *dms) ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); ut_assertok(i2c_get_chip(bus, chip, &dev)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0\0\0\0", sizeof(buf))); - ut_assertok(i2c_write(dev, 2, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0AB\0", sizeof(buf))); return 0; @@ -68,11 +68,11 @@ static int dm_test_i2c_speed(struct dm_test_state *dms) ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); ut_assertok(i2c_get_chip(bus, chip, &dev)); ut_assertok(i2c_set_bus_speed(bus, 100000)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(i2c_set_bus_speed(bus, 400000)); ut_asserteq(400000, i2c_get_bus_speed(bus)); - ut_assertok(i2c_read(dev, 0, buf, 5)); - ut_asserteq(-EINVAL, i2c_write(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq(-EINVAL, dm_i2c_write(dev, 0, buf, 5)); return 0; } @@ -86,7 +86,7 @@ static int dm_test_i2c_offset_len(struct dm_test_state *dms) ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); ut_assertok(i2c_get_chip(bus, chip, &dev)); ut_assertok(i2c_set_chip_offset_len(dev, 1)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); /* This is not supported by the uclass */ ut_asserteq(-EINVAL, i2c_set_chip_offset_len(dev, 5)); @@ -100,7 +100,7 @@ static int dm_test_i2c_probe_empty(struct dm_test_state *dms) struct udevice *bus, *dev; ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); - ut_assertok(i2c_probe(bus, SANDBOX_I2C_TEST_ADDR, 0, &dev)); + ut_assertok(dm_i2c_probe(bus, SANDBOX_I2C_TEST_ADDR, 0, &dev)); return 0; } @@ -114,7 +114,7 @@ static int dm_test_i2c_bytewise(struct dm_test_state *dms) ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); ut_assertok(i2c_get_chip(bus, chip, &dev)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0\0\0\0", sizeof(buf))); /* Tell the EEPROM to only read/write one register at a time */ @@ -123,34 +123,34 @@ static int dm_test_i2c_bytewise(struct dm_test_state *dms) sandbox_i2c_eeprom_set_test_mode(eeprom, SIE_TEST_MODE_SINGLE_BYTE); /* Now we only get the first byte - the rest will be 0xff */ - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\xff\xff\xff\xff", sizeof(buf))); /* If we do a separate transaction for each byte, it works */ ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0\0\0\0", sizeof(buf))); /* This will only write A */ ut_assertok(i2c_set_chip_flags(dev, 0)); - ut_assertok(i2c_write(dev, 2, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\xff\xff\xff\xff", sizeof(buf))); /* Check that the B was ignored */ ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0A\0\0\0", sizeof(buf))); /* Now write it again with the new flags, it should work */ ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_WR_ADDRESS)); - ut_assertok(i2c_write(dev, 2, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\xff\xff\xff\xff", sizeof(buf))); ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_WR_ADDRESS | DM_I2C_CHIP_RD_ADDRESS)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0AB\0\0", sizeof(buf))); /* Restore defaults */ @@ -170,42 +170,42 @@ static int dm_test_i2c_offset(struct dm_test_state *dms) ut_assertok(i2c_get_chip_for_busnum(busnum, chip, &dev)); /* Do a transfer so we can find the emulator */ - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(uclass_first_device(UCLASS_I2C_EMUL, &eeprom)); /* Offset length 0 */ sandbox_i2c_eeprom_set_offset_len(eeprom, 0); ut_assertok(i2c_set_chip_offset_len(dev, 0)); - ut_assertok(i2c_write(dev, 10 /* ignored */, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_write(dev, 10 /* ignored */, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "AB\0\0\0\0", sizeof(buf))); /* Offset length 1 */ sandbox_i2c_eeprom_set_offset_len(eeprom, 1); ut_assertok(i2c_set_chip_offset_len(dev, 1)); - ut_assertok(i2c_write(dev, 2, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "ABAB\0", sizeof(buf))); /* Offset length 2 */ sandbox_i2c_eeprom_set_offset_len(eeprom, 2); ut_assertok(i2c_set_chip_offset_len(dev, 2)); - ut_assertok(i2c_write(dev, 0x210, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0x210, buf, 5)); + ut_assertok(dm_i2c_write(dev, 0x210, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x210, buf, 5)); ut_assertok(memcmp(buf, "AB\0\0\0", sizeof(buf))); /* Offset length 3 */ sandbox_i2c_eeprom_set_offset_len(eeprom, 2); ut_assertok(i2c_set_chip_offset_len(dev, 2)); - ut_assertok(i2c_write(dev, 0x410, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0x410, buf, 5)); + ut_assertok(dm_i2c_write(dev, 0x410, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x410, buf, 5)); ut_assertok(memcmp(buf, "AB\0\0\0", sizeof(buf))); /* Offset length 4 */ sandbox_i2c_eeprom_set_offset_len(eeprom, 2); ut_assertok(i2c_set_chip_offset_len(dev, 2)); - ut_assertok(i2c_write(dev, 0x420, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0x420, buf, 5)); + ut_assertok(dm_i2c_write(dev, 0x420, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x420, buf, 5)); ut_assertok(memcmp(buf, "AB\0\0\0", sizeof(buf))); /* Restore defaults */ -- cgit v1.3.1 From 25ab4b0303f2df5e6b94ed92e37875a7c98f4de3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:26:55 -0700 Subject: dm: i2c: Provide an offset length parameter where needed Rather than assuming that the chip offset length is 1, allow it to be provided. This allows chips that don't use the default offset length to be used (at present they are only supported by the command line 'i2c' command which sets the offset length explicitly). Signed-off-by: Simon Glass Acked-by: Heiko Schocher --- arch/arm/cpu/tegra20-common/pmu.c | 2 +- board/avionic-design/common/tamonten-ng.c | 2 +- board/nvidia/cardhu/cardhu.c | 4 ++-- board/nvidia/dalmore/dalmore.c | 4 ++-- board/nvidia/whistler/whistler.c | 4 ++-- board/toradex/apalis_t30/apalis_t30.c | 2 +- common/cmd_i2c.c | 2 +- drivers/i2c/i2c-uclass.c | 16 +++++++++------- drivers/i2c/sandbox_i2c.c | 2 +- drivers/power/as3722.c | 2 +- include/i2c.h | 8 ++++++-- test/dm/i2c.c | 10 +++++----- 12 files changed, 32 insertions(+), 26 deletions(-) (limited to 'test') diff --git a/arch/arm/cpu/tegra20-common/pmu.c b/arch/arm/cpu/tegra20-common/pmu.c index 36a76a24d97..a774246a277 100644 --- a/arch/arm/cpu/tegra20-common/pmu.c +++ b/arch/arm/cpu/tegra20-common/pmu.c @@ -52,7 +52,7 @@ int pmu_set_nominal(void) debug("%s: Cannot find DVC I2C bus\n", __func__); return ret; } - ret = i2c_get_chip(bus, PMI_I2C_ADDRESS, &dev); + ret = i2c_get_chip(bus, PMI_I2C_ADDRESS, 1, &dev); if (ret) { debug("%s: Cannot find DVC I2C chip\n", __func__); return ret; diff --git a/board/avionic-design/common/tamonten-ng.c b/board/avionic-design/common/tamonten-ng.c index bca91831e0e..17046271126 100644 --- a/board/avionic-design/common/tamonten-ng.c +++ b/board/avionic-design/common/tamonten-ng.c @@ -55,7 +55,7 @@ void pmu_write(uchar reg, uchar data) struct udevice *dev; int ret; - ret = i2c_get_chip_for_busnum(4, PMU_I2C_ADDRESS, &dev); + ret = i2c_get_chip_for_busnum(4, PMU_I2C_ADDRESS, 1, &dev); if (ret) { debug("%s: Cannot find PMIC I2C chip\n", __func__); return; diff --git a/board/nvidia/cardhu/cardhu.c b/board/nvidia/cardhu/cardhu.c index fc31d29140b..1540526a613 100644 --- a/board/nvidia/cardhu/cardhu.c +++ b/board/nvidia/cardhu/cardhu.c @@ -46,7 +46,7 @@ void board_sdmmc_voltage_init(void) int ret; int i; - ret = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, &dev); + ret = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, 1, &dev); if (ret) { debug("%s: Cannot find PMIC I2C chip\n", __func__); return; @@ -94,7 +94,7 @@ int tegra_pcie_board_init(void) u8 addr, data[1]; int err; - err = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, &dev); + err = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, 1, &dev); if (err) { debug("failed to find PMU bus\n"); return err; diff --git a/board/nvidia/dalmore/dalmore.c b/board/nvidia/dalmore/dalmore.c index c0991c57a97..d7c1a695ff8 100644 --- a/board/nvidia/dalmore/dalmore.c +++ b/board/nvidia/dalmore/dalmore.c @@ -55,7 +55,7 @@ void board_sdmmc_voltage_init(void) uchar reg, data_buffer[1]; int ret; - ret = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, &dev); + ret = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, 1, &dev); if (ret) { debug("%s: Cannot find PMIC I2C chip\n", __func__); return; @@ -83,7 +83,7 @@ void board_sdmmc_voltage_init(void) data_buffer[0] = 0x03; reg = 0x14; - ret = i2c_get_chip_for_busnum(0, BAT_I2C_ADDRESS, &dev); + ret = i2c_get_chip_for_busnum(0, BAT_I2C_ADDRESS, 1, &dev); if (ret) { debug("%s: Cannot find charger I2C chip\n", __func__); return; diff --git a/board/nvidia/whistler/whistler.c b/board/nvidia/whistler/whistler.c index ad6ea091df7..3476f1159fe 100644 --- a/board/nvidia/whistler/whistler.c +++ b/board/nvidia/whistler/whistler.c @@ -27,7 +27,7 @@ void pin_mux_mmc(void) int ret; /* Turn on MAX8907B LDO12 to 2.8V for J40 power */ - ret = i2c_get_chip_for_busnum(0, 0x3c, &dev); + ret = i2c_get_chip_for_busnum(0, 0x3c, 1, &dev); if (ret) { printf("%s: Cannot find MAX8907B I2C chip\n", __func__); return; @@ -64,7 +64,7 @@ void pin_mux_usb(void) */ /* Turn on TAC6416's GPIO 0+1 for USB1/3's VBUS */ - ret = i2c_get_chip_for_busnum(0, 0x20, &dev); + ret = i2c_get_chip_for_busnum(0, 0x20, 1, &dev); if (ret) { printf("%s: Cannot find TAC6416 I2C chip\n", __func__); return; diff --git a/board/toradex/apalis_t30/apalis_t30.c b/board/toradex/apalis_t30/apalis_t30.c index 1c4b4c1cd48..624421496a4 100644 --- a/board/toradex/apalis_t30/apalis_t30.c +++ b/board/toradex/apalis_t30/apalis_t30.c @@ -42,7 +42,7 @@ int tegra_pcie_board_init(void) u8 addr, data[1]; int err; - err = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, &dev); + err = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, 1, &dev); if (err) { debug("%s: Cannot find PMIC I2C chip\n", __func__); return err; diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c index 1e500fbf2cc..7c3ad00fdf0 100644 --- a/common/cmd_i2c.c +++ b/common/cmd_i2c.c @@ -168,7 +168,7 @@ static int i2c_get_cur_bus_chip(uint chip_addr, struct udevice **devp) if (ret) return ret; - return i2c_get_chip(bus, chip_addr, devp); + return i2c_get_chip(bus, chip_addr, 1, devp); } #endif diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 25f2c18cf4f..24e5ec68f2b 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -220,7 +220,7 @@ static int i2c_probe_chip(struct udevice *bus, uint chip_addr, return ops->xfer(bus, msg, 1); } -static int i2c_bind_driver(struct udevice *bus, uint chip_addr, +static int i2c_bind_driver(struct udevice *bus, uint chip_addr, uint offset_len, struct udevice **devp) { struct dm_i2c_chip chip; @@ -238,7 +238,7 @@ static int i2c_bind_driver(struct udevice *bus, uint chip_addr, /* Tell the device what we know about it */ memset(&chip, '\0', sizeof(chip)); chip.chip_addr = chip_addr; - chip.offset_len = 1; /* we assume */ + chip.offset_len = offset_len; ret = device_probe_child(dev, &chip); debug("%s: device_probe_child: ret=%d\n", __func__, ret); if (ret) @@ -254,7 +254,8 @@ err_bind: return ret; } -int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp) +int i2c_get_chip(struct udevice *bus, uint chip_addr, uint offset_len, + struct udevice **devp) { struct udevice *dev; @@ -281,10 +282,11 @@ int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp) } } debug("not found\n"); - return i2c_bind_driver(bus, chip_addr, devp); + return i2c_bind_driver(bus, chip_addr, offset_len, devp); } -int i2c_get_chip_for_busnum(int busnum, int chip_addr, struct udevice **devp) +int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len, + struct udevice **devp) { struct udevice *bus; int ret; @@ -294,7 +296,7 @@ int i2c_get_chip_for_busnum(int busnum, int chip_addr, struct udevice **devp) debug("Cannot find I2C bus %d\n", busnum); return ret; } - ret = i2c_get_chip(bus, chip_addr, devp); + ret = i2c_get_chip(bus, chip_addr, offset_len, devp); if (ret) { debug("Cannot find I2C chip %02x on bus %d\n", chip_addr, busnum); @@ -319,7 +321,7 @@ int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, return ret; /* The chip was found, see if we have a driver, and probe it */ - ret = i2c_get_chip(bus, chip_addr, devp); + ret = i2c_get_chip(bus, chip_addr, 1, devp); debug("%s: i2c_get_chip: ret=%d\n", __func__, ret); return ret; diff --git a/drivers/i2c/sandbox_i2c.c b/drivers/i2c/sandbox_i2c.c index f0e9f51a1f2..e2f6c3b9bb5 100644 --- a/drivers/i2c/sandbox_i2c.c +++ b/drivers/i2c/sandbox_i2c.c @@ -60,7 +60,7 @@ static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, if (msg->addr == SANDBOX_I2C_TEST_ADDR) return 0; - ret = i2c_get_chip(bus, msg->addr, &dev); + ret = i2c_get_chip(bus, msg->addr, 1, &dev); if (ret) return ret; diff --git a/drivers/power/as3722.c b/drivers/power/as3722.c index 3aafdc96704..a60bb5f83fb 100644 --- a/drivers/power/as3722.c +++ b/drivers/power/as3722.c @@ -242,7 +242,7 @@ int as3722_init(struct udevice **devp) const unsigned int address = 0x40; int err; - err = i2c_get_chip_for_busnum(bus, address, &pmic); + err = i2c_get_chip_for_busnum(bus, address, 1, &pmic); if (err) return err; err = as3722_read_id(pmic, &id, &revision); diff --git a/include/i2c.h b/include/i2c.h index 47529f4b283..76090b7a93a 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -388,10 +388,12 @@ struct dm_i2c_ops { * * @bus: Bus to examine * @chip_addr: Chip address for the new device + * @offset_len: Length of a register offset in bytes (normally 1) * @devp: Returns pointer to new device if found or -ENODEV if not * found */ -int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp); +int i2c_get_chip(struct udevice *bus, uint chip_addr, uint offset_len, + struct udevice **devp); /** * i2c_get_chip() - get a device to use to access a chip on a bus number @@ -401,10 +403,12 @@ int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp); * * @busnum: Bus number to examine * @chip_addr: Chip address for the new device + * @offset_len: Length of a register offset in bytes (normally 1) * @devp: Returns pointer to new device if found or -ENODEV if not * found */ -int i2c_get_chip_for_busnum(int busnum, int chip_addr, struct udevice **devp); +int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len, + struct udevice **devp); /** * i2c_chip_ofdata_to_platdata() - Decode standard I2C platform data diff --git a/test/dm/i2c.c b/test/dm/i2c.c index 4823b0c6483..ef88372d563 100644 --- a/test/dm/i2c.c +++ b/test/dm/i2c.c @@ -49,7 +49,7 @@ static int dm_test_i2c_read_write(struct dm_test_state *dms) uint8_t buf[5]; ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); - ut_assertok(i2c_get_chip(bus, chip, &dev)); + ut_assertok(i2c_get_chip(bus, chip, 1, &dev)); ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0\0\0\0", sizeof(buf))); ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); @@ -66,7 +66,7 @@ static int dm_test_i2c_speed(struct dm_test_state *dms) uint8_t buf[5]; ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); - ut_assertok(i2c_get_chip(bus, chip, &dev)); + ut_assertok(i2c_get_chip(bus, chip, 1, &dev)); ut_assertok(i2c_set_bus_speed(bus, 100000)); ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(i2c_set_bus_speed(bus, 400000)); @@ -84,7 +84,7 @@ static int dm_test_i2c_offset_len(struct dm_test_state *dms) uint8_t buf[5]; ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); - ut_assertok(i2c_get_chip(bus, chip, &dev)); + ut_assertok(i2c_get_chip(bus, chip, 1, &dev)); ut_assertok(i2c_set_chip_offset_len(dev, 1)); ut_assertok(dm_i2c_read(dev, 0, buf, 5)); @@ -113,7 +113,7 @@ static int dm_test_i2c_bytewise(struct dm_test_state *dms) uint8_t buf[5]; ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); - ut_assertok(i2c_get_chip(bus, chip, &dev)); + ut_assertok(i2c_get_chip(bus, chip, 1, &dev)); ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0\0\0\0", sizeof(buf))); @@ -167,7 +167,7 @@ static int dm_test_i2c_offset(struct dm_test_state *dms) struct udevice *dev; uint8_t buf[5]; - ut_assertok(i2c_get_chip_for_busnum(busnum, chip, &dev)); + ut_assertok(i2c_get_chip_for_busnum(busnum, chip, 1, &dev)); /* Do a transfer so we can find the emulator */ ut_assertok(dm_i2c_read(dev, 0, buf, 5)); -- cgit v1.3.1 From 22cc069160dc77972b70d45c0d2558e20e9ee766 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:26:56 -0700 Subject: dm: Don't run tests if U-Boot cannot be built There is no point in running the tests if U-Boot cannot be built. Abort in this case. Signed-off-by: Simon Glass --- test/dm/test-dm.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/dm/test-dm.sh b/test/dm/test-dm.sh index bb99677ece9..8ebc39297cb 100755 --- a/test/dm/test-dm.sh +++ b/test/dm/test-dm.sh @@ -1,9 +1,14 @@ #!/bin/sh +die() { + echo $1 + exit 1 +} + NUM_CPUS=$(cat /proc/cpuinfo |grep -c processor) dtc -I dts -O dtb test/dm/test.dts -o test/dm/test.dtb -make O=sandbox sandbox_config -make O=sandbox -s -j${NUM_CPUS} +make O=sandbox sandbox_config || die "Cannot configure U-Boot" +make O=sandbox -s -j${NUM_CPUS} || die "Cannot build U-Boot" dd if=/dev/zero of=spi.bin bs=1M count=2 ./sandbox/u-boot -d test/dm/test.dtb -c "dm test" rm spi.bin -- cgit v1.3.1 From f8a85449ef3e0963add728815771ccc09aa99875 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:00 -0700 Subject: dm: core: Allocate platform data when binding a device When using allocated platform data, allocate it when we bind the device. This makes it possible to fill in this information before the device is probed. This fits with the platform data model (when not using device tree), since platform data exists at bind-time. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/device-remove.c | 8 ++++---- drivers/core/device.c | 22 +++++++++++++--------- test/dm/test-fdt.c | 4 ++-- 3 files changed, 19 insertions(+), 15 deletions(-) (limited to 'test') diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 8fc6b710842..2c8257752b6 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -88,6 +88,10 @@ int device_unbind(struct udevice *dev) if (ret) return ret; + if (dev->flags & DM_FLAG_ALLOC_PDATA) { + free(dev->platdata); + dev->platdata = NULL; + } ret = uclass_unbind_device(dev); if (ret) return ret; @@ -111,10 +115,6 @@ void device_free(struct udevice *dev) free(dev->priv); dev->priv = NULL; } - if (dev->flags & DM_FLAG_ALLOC_PDATA) { - free(dev->platdata); - dev->platdata = NULL; - } size = dev->uclass->uc_drv->per_device_auto_alloc_size; if (size) { free(dev->uclass_priv); diff --git a/drivers/core/device.c b/drivers/core/device.c index eca8edac26a..366cffed89d 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -72,8 +72,14 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, #else dev->req_seq = -1; #endif - if (!dev->platdata && drv->platdata_auto_alloc_size) + if (!dev->platdata && drv->platdata_auto_alloc_size) { dev->flags |= DM_FLAG_ALLOC_PDATA; + dev->platdata = calloc(1, drv->platdata_auto_alloc_size); + if (!dev->platdata) { + ret = -ENOMEM; + goto fail_alloc1; + } + } /* put dev into parent's successor list */ if (parent) @@ -103,6 +109,11 @@ fail_bind: fail_uclass_bind: if (parent) list_del(&dev->sibling_node); + if (dev->flags & DM_FLAG_ALLOC_PDATA) { + free(dev->platdata); + dev->platdata = NULL; + } +fail_alloc1: free(dev); return ret; @@ -139,7 +150,7 @@ int device_probe_child(struct udevice *dev, void *parent_priv) drv = dev->driver; assert(drv); - /* Allocate private data and platdata if requested */ + /* Allocate private data if requested */ if (drv->priv_auto_alloc_size) { dev->priv = calloc(1, drv->priv_auto_alloc_size); if (!dev->priv) { @@ -148,13 +159,6 @@ int device_probe_child(struct udevice *dev, void *parent_priv) } } /* Allocate private data if requested */ - if (dev->flags & DM_FLAG_ALLOC_PDATA) { - dev->platdata = calloc(1, drv->platdata_auto_alloc_size); - if (!dev->platdata) { - ret = -ENOMEM; - goto fail; - } - } size = dev->uclass->uc_drv->per_device_auto_alloc_size; if (size) { dev->uclass_priv = calloc(1, size); diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index cd2c38995e9..dc4ebf9adaf 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -143,12 +143,12 @@ static int dm_test_fdt(struct dm_test_state *dms) /* These are num_devices compatible root-level device tree nodes */ ut_asserteq(num_devices, list_count_items(&uc->dev_head)); - /* Each should have no platdata / priv */ + /* Each should have platform data but no private data */ for (i = 0; i < num_devices; i++) { ret = uclass_find_device(UCLASS_TEST_FDT, i, &dev); ut_assert(!ret); ut_assert(!dev_get_priv(dev)); - ut_assert(!dev->platdata); + ut_assert(dev->platdata); } ut_assertok(dm_check_devices(dms, num_devices)); -- cgit v1.3.1 From cdc133bde9dd221319b9cf7fd99fcb4da8ba195c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:01 -0700 Subject: dm: core: Allow parents to have platform data for their children For buses it is common for parents to need to know the address of the child on the bus, the bus speed to use for that child, and other information. This can be provided in platform data attached to each child. Add driver model support for this, including auto-allocation which can be requested using a new property to specify the size of the data. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/device-remove.c | 4 +++ drivers/core/device.c | 30 ++++++++++++++-- include/dm/device.h | 19 +++++++++++ test/dm/bus.c | 81 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 2c8257752b6..56c358a0ec2 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -92,6 +92,10 @@ int device_unbind(struct udevice *dev) free(dev->platdata); dev->platdata = NULL; } + if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) { + free(dev->parent_platdata); + dev->parent_platdata = NULL; + } ret = uclass_unbind_device(dev); if (ret) return ret; diff --git a/drivers/core/device.c b/drivers/core/device.c index 366cffed89d..ee97cc84fff 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -80,6 +80,18 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, goto fail_alloc1; } } + if (parent) { + int size = parent->driver->per_child_platdata_auto_alloc_size; + + if (size) { + dev->flags |= DM_FLAG_ALLOC_PARENT_PDATA; + dev->parent_platdata = calloc(1, size); + if (!dev->parent_platdata) { + ret = -ENOMEM; + goto fail_alloc2; + } + } + } /* put dev into parent's successor list */ if (parent) @@ -107,8 +119,12 @@ fail_bind: dev->name); } fail_uclass_bind: - if (parent) - list_del(&dev->sibling_node); + list_del(&dev->sibling_node); + if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) { + free(dev->parent_platdata); + dev->parent_platdata = NULL; + } +fail_alloc2: if (dev->flags & DM_FLAG_ALLOC_PDATA) { free(dev->platdata); dev->platdata = NULL; @@ -247,6 +263,16 @@ void *dev_get_platdata(struct udevice *dev) return dev->platdata; } +void *dev_get_parent_platdata(struct udevice *dev) +{ + if (!dev) { + dm_warn("%s: null device", __func__); + return NULL; + } + + return dev->parent_platdata; +} + void *dev_get_priv(struct udevice *dev) { if (!dev) { diff --git a/include/dm/device.h b/include/dm/device.h index 13598a15b68..096d84b7b6f 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -26,6 +26,9 @@ struct driver_info; /* DM should init this device prior to relocation */ #define DM_FLAG_PRE_RELOC (1 << 2) +/* DM is responsible for allocating and freeing parent_platdata */ +#define DM_FLAG_ALLOC_PARENT_PDATA (1 << 3) + /** * struct udevice - An instance of a driver * @@ -46,6 +49,7 @@ struct driver_info; * @driver: The driver used by this device * @name: Name of device, typically the FDT node name * @platdata: Configuration data for this device + * @parent_platdata: The parent bus's configuration data for this device * @of_offset: Device tree node offset for this device (- for none) * @of_id: Pointer to the udevice_id structure which created the device * @parent: Parent of this device, or NULL for the top level device @@ -65,6 +69,7 @@ struct udevice { struct driver *driver; const char *name; void *platdata; + void *parent_platdata; int of_offset; const struct udevice_id *of_id; struct udevice *parent; @@ -146,6 +151,9 @@ struct udevice_id { * device_probe_child() pass it in. So far the use case for allocating it * is SPI, but I found that unsatisfactory. Since it is here I will leave it * until things are clearer. + * @per_child_platdata_auto_alloc_size: A bus likes to store information about + * its children. If non-zero this is the size of this data, to be allocated + * in the child's parent_platdata pointer. * @ops: Driver-specific operations. This is typically a list of function * pointers defined by the driver, to implement driver functions required by * the uclass. @@ -165,6 +173,7 @@ struct driver { int priv_auto_alloc_size; int platdata_auto_alloc_size; int per_child_auto_alloc_size; + int per_child_platdata_auto_alloc_size; const void *ops; /* driver-specific operations */ uint32_t flags; }; @@ -183,6 +192,16 @@ struct driver { */ void *dev_get_platdata(struct udevice *dev); +/** + * dev_get_parent_platdata() - Get the parent platform data for a device + * + * This checks that dev is not NULL, but no other checks for now + * + * @dev Device to check + * @return parent's platform data, or NULL if none + */ +void *dev_get_parent_platdata(struct udevice *dev); + /** * dev_get_parentdata() - Get the parent data for a device * diff --git a/test/dm/bus.c b/test/dm/bus.c index abbaccff509..63c8a9f7385 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -9,11 +9,16 @@ #include #include #include +#include #include #include DECLARE_GLOBAL_DATA_PTR; +struct dm_test_parent_platdata { + int count; +}; + enum { FLAG_CHILD_PROBED = 10, FLAG_CHILD_REMOVED = -7, @@ -62,6 +67,8 @@ U_BOOT_DRIVER(testbus_drv) = { .priv_auto_alloc_size = sizeof(struct dm_test_priv), .platdata_auto_alloc_size = sizeof(struct dm_test_pdata), .per_child_auto_alloc_size = sizeof(struct dm_test_parent_data), + .per_child_platdata_auto_alloc_size = + sizeof(struct dm_test_parent_platdata), .child_pre_probe = testbus_child_pre_probe, .child_post_remove = testbus_child_post_remove, }; @@ -271,3 +278,77 @@ static int dm_test_bus_parent_ops(struct dm_test_state *dms) return 0; } DM_TEST(dm_test_bus_parent_ops, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test that the bus can store platform data about each child */ +static int dm_test_bus_parent_platdata(struct dm_test_state *dms) +{ + struct dm_test_parent_platdata *plat; + struct udevice *bus, *dev; + int child_count; + + /* Check that the bus has no children */ + ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus)); + device_find_first_child(bus, &dev); + ut_asserteq_ptr(NULL, dev); + + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + + for (device_find_first_child(bus, &dev), child_count = 0; + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_platdata(dev); + ut_assert(plat != NULL); + + /* + * Check that it is not affected by the device being + * probed/removed + */ + plat->count++; + ut_asserteq(1, plat->count); + device_probe(dev); + device_remove(dev); + + ut_asserteq_ptr(plat, dev_get_parent_platdata(dev)); + ut_asserteq(1, plat->count); + ut_assertok(device_probe(dev)); + child_count++; + } + ut_asserteq(3, child_count); + + /* Removing the bus should also have no effect (it is still bound) */ + device_remove(bus); + for (device_find_first_child(bus, &dev), child_count = 0; + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_platdata(dev); + ut_assert(plat != NULL); + ut_asserteq(1, plat->count); + child_count++; + } + ut_asserteq(3, child_count); + + /* Unbind all the children */ + do { + device_find_first_child(bus, &dev); + if (dev) + device_unbind(dev); + } while (dev); + + /* Now the child platdata should be removed and re-added */ + device_probe(bus); + for (device_find_first_child(bus, &dev), child_count = 0; + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_platdata(dev); + ut_assert(plat != NULL); + ut_asserteq(0, plat->count); + child_count++; + } + ut_asserteq(3, child_count); + + return 0; +} +DM_TEST(dm_test_bus_parent_platdata, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From ba8da9dc43ac8ae3351345df12dc7f9d1cd07ae0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:02 -0700 Subject: dm: core: Allow uclasses to specify platdata for a device's children In many cases the child platform data for a device's children is defined by the uclass rather than the individual devices. For example, a SPI bus needs to know the chip select and speed for each of its children. It makes sense to allow this information to be defined the SPI uclass rather than each individual driver. If the device provides a size value for its child platdata, then use it. Failng that, fall back to that provided by the uclass. Reviewed-by: Masahiro Yamada Signed-off-by: Simon Glass --- drivers/core/device.c | 4 ++++ include/dm/uclass.h | 5 +++++ test/dm/bus.c | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/drivers/core/device.c b/drivers/core/device.c index ee97cc84fff..2f33b0e6a3b 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -83,6 +83,10 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, if (parent) { int size = parent->driver->per_child_platdata_auto_alloc_size; + if (!size) { + size = parent->uclass->uc_drv-> + per_child_platdata_auto_alloc_size; + } if (size) { dev->flags |= DM_FLAG_ALLOC_PARENT_PDATA; dev->parent_platdata = calloc(1, size); diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 2577ae6489b..7d92d34bec0 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -60,6 +60,10 @@ struct udevice; * @per_device_auto_alloc_size: Each device can hold private data owned * by the uclass. If required this will be automatically allocated if this * value is non-zero. + * @per_child_platdata_auto_alloc_size: A bus likes to store information about + * its children. If non-zero this is the size of this data, to be allocated + * in the child device's parent_platdata pointer. This value is only used as + * a falback if this member is 0 in the driver. * @ops: Uclass operations, providing the consistent interface to devices * within the uclass. */ @@ -74,6 +78,7 @@ struct uclass_driver { int (*destroy)(struct uclass *class); int priv_auto_alloc_size; int per_device_auto_alloc_size; + int per_child_platdata_auto_alloc_size; const void *ops; }; diff --git a/test/dm/bus.c b/test/dm/bus.c index 63c8a9f7385..26b82930375 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -279,8 +279,7 @@ static int dm_test_bus_parent_ops(struct dm_test_state *dms) } DM_TEST(dm_test_bus_parent_ops, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -/* Test that the bus can store platform data about each child */ -static int dm_test_bus_parent_platdata(struct dm_test_state *dms) +static int test_bus_parent_platdata(struct dm_test_state *dms) { struct dm_test_parent_platdata *plat; struct udevice *bus, *dev; @@ -351,4 +350,33 @@ static int dm_test_bus_parent_platdata(struct dm_test_state *dms) return 0; } + +/* Test that the bus can store platform data about each child */ +static int dm_test_bus_parent_platdata(struct dm_test_state *dms) +{ + return test_bus_parent_platdata(dms); +} DM_TEST(dm_test_bus_parent_platdata, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* As above but the size is controlled by the uclass */ +static int dm_test_bus_parent_platdata_uclass(struct dm_test_state *dms) +{ + struct udevice *bus; + int size; + int ret; + + /* Set the driver size to 0 so that the uclass size is used */ + ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus)); + size = bus->driver->per_child_platdata_auto_alloc_size; + bus->uclass->uc_drv->per_child_platdata_auto_alloc_size = size; + bus->driver->per_child_platdata_auto_alloc_size = 0; + ret = test_bus_parent_platdata(dms); + if (ret) + return ret; + bus->uclass->uc_drv->per_child_platdata_auto_alloc_size = 0; + bus->driver->per_child_platdata_auto_alloc_size = size; + + return 0; +} +DM_TEST(dm_test_bus_parent_platdata_uclass, + DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From 0118ce79577f9b0881f99a6e4f8a79cd5014cb87 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:03 -0700 Subject: dm: core: Add a post_bind method for parents Allow parent drivers to be called when a new child is bound to them. This allows a bus to set up information it needs for that child. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/device.c | 12 ++++++++++++ include/dm/device.h | 2 ++ test/dm/bus.c | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) (limited to 'test') diff --git a/drivers/core/device.c b/drivers/core/device.c index 2f33b0e6a3b..365676b9121 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -111,12 +111,24 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, if (ret) goto fail_bind; } + if (parent && parent->driver->child_post_bind) { + ret = parent->driver->child_post_bind(dev); + if (ret) + goto fail_child_post_bind; + } + if (parent) dm_dbg("Bound device %s to %s\n", dev->name, parent->name); *devp = dev; return 0; +fail_child_post_bind: + if (drv->unbind && drv->unbind(dev)) { + dm_warn("unbind() method failed on dev '%s' on error path\n", + dev->name); + } + fail_bind: if (uclass_unbind_device(dev)) { dm_warn("Failed to unbind dev '%s' on error path\n", diff --git a/include/dm/device.h b/include/dm/device.h index 096d84b7b6f..50f1b4f5087 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -132,6 +132,7 @@ struct udevice_id { * @remove: Called to remove a device, i.e. de-activate it * @unbind: Called to unbind a device from its driver * @ofdata_to_platdata: Called before probe to decode device tree data + * @child_post_bind: Called after a new child has been bound * @child_pre_probe: Called before a child device is probed. The device has * memory allocated but it has not yet been probed. * @child_post_remove: Called after a child device is removed. The device @@ -168,6 +169,7 @@ struct driver { int (*remove)(struct udevice *dev); int (*unbind)(struct udevice *dev); int (*ofdata_to_platdata)(struct udevice *dev); + int (*child_post_bind)(struct udevice *dev); int (*child_pre_probe)(struct udevice *dev); int (*child_post_remove)(struct udevice *dev); int priv_auto_alloc_size; diff --git a/test/dm/bus.c b/test/dm/bus.c index 26b82930375..e18a6f72499 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -17,6 +17,7 @@ DECLARE_GLOBAL_DATA_PTR; struct dm_test_parent_platdata { int count; + int bind_flag; }; enum { @@ -31,6 +32,16 @@ static int testbus_drv_probe(struct udevice *dev) return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); } +static int testbus_child_post_bind(struct udevice *dev) +{ + struct dm_test_parent_platdata *plat; + + plat = dev_get_parent_platdata(dev); + plat->bind_flag = 1; + + return 0; +} + static int testbus_child_pre_probe(struct udevice *dev) { struct dm_test_parent_data *parent_data = dev_get_parentdata(dev); @@ -64,6 +75,7 @@ U_BOOT_DRIVER(testbus_drv) = { .of_match = testbus_ids, .id = UCLASS_TEST_BUS, .probe = testbus_drv_probe, + .child_post_bind = testbus_child_post_bind, .priv_auto_alloc_size = sizeof(struct dm_test_priv), .platdata_auto_alloc_size = sizeof(struct dm_test_pdata), .per_child_auto_alloc_size = sizeof(struct dm_test_parent_data), @@ -380,3 +392,26 @@ static int dm_test_bus_parent_platdata_uclass(struct dm_test_state *dms) } DM_TEST(dm_test_bus_parent_platdata_uclass, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test that the child post_bind method is called */ +static int dm_test_bus_child_post_bind(struct dm_test_state *dms) +{ + struct dm_test_parent_platdata *plat; + struct udevice *bus, *dev; + int child_count; + + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + for (device_find_first_child(bus, &dev), child_count = 0; + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_platdata(dev); + ut_assert(plat != NULL); + ut_asserteq(1, plat->bind_flag); + child_count++; + } + ut_asserteq(3, child_count); + + return 0; +} +DM_TEST(dm_test_bus_child_post_bind, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From b367053102e597eb21b0a5e86c63e8d10f368cb0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:04 -0700 Subject: dm: core: Add a function to get a device's uclass ID This is useful to check which uclass a device is in. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/device.c | 5 +++++ include/dm/device.h | 8 ++++++++ test/dm/core.c | 11 +++++++++++ 3 files changed, 24 insertions(+) (limited to 'test') diff --git a/drivers/core/device.c b/drivers/core/device.c index 365676b9121..2606d187177 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -438,3 +438,8 @@ ulong dev_get_of_data(struct udevice *dev) { return dev->of_id->data; } + +enum uclass_id device_get_uclass_id(struct udevice *dev) +{ + return dev->uclass->uc_drv->id; +} diff --git a/include/dm/device.h b/include/dm/device.h index 50f1b4f5087..81afa8c6281 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -245,6 +245,14 @@ struct udevice *dev_get_parent(struct udevice *child); */ ulong dev_get_of_data(struct udevice *dev); +/* + * device_get_uclass_id() - return the uclass ID of a device + * + * @dev: Device to check + * @return uclass ID for the device + */ +enum uclass_id device_get_uclass_id(struct udevice *dev); + /** * device_get_child() - Get the child of a device by index * diff --git a/test/dm/core.c b/test/dm/core.c index ff5c2a749c5..eccda0974da 100644 --- a/test/dm/core.c +++ b/test/dm/core.c @@ -598,3 +598,14 @@ static int dm_test_uclass_before_ready(struct dm_test_state *dms) } DM_TEST(dm_test_uclass_before_ready, 0); + +static int dm_test_device_get_uclass_id(struct dm_test_state *dms) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_TEST, 0, &dev)); + ut_asserteq(UCLASS_TEST, device_get_uclass_id(dev)); + + return 0; +} +DM_TEST(dm_test_device_get_uclass_id, DM_TESTF_SCAN_PDATA); -- cgit v1.3.1 From 9cc36a2b89ebe5148d69d521745c1e1d26365c3a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:05 -0700 Subject: dm: core: Add a flag to control sequence numbering At present we try to use the 'reg' property and device tree aliases to give devices a sequence number. The 'reg' property is often actually a memory address, so the sequence numbers thus-obtained are not useful. It would be better if the devices were just sequentially numbered in that case. In fact neither I2C nor SPI use this feature, so drop it. Some devices need us to look up an alias to number them within the uclass. Add a flag to control this, so it is not done unless it is needed. Adjust the tests to test this new behaviour. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- doc/driver-model/README.txt | 51 +++++++++++------------------------------- drivers/core/device.c | 28 +++++++++++------------ drivers/i2c/i2c-uclass.c | 1 + drivers/serial/serial-uclass.c | 1 + drivers/spi/spi-uclass.c | 1 + include/dm/uclass.h | 5 +++++ test/dm/bus.c | 3 ++- test/dm/test-fdt.c | 9 ++++---- test/dm/test.dts | 16 +++++++++++++ 9 files changed, 57 insertions(+), 58 deletions(-) (limited to 'test') diff --git a/doc/driver-model/README.txt b/doc/driver-model/README.txt index 40415698cf4..0c1292bf125 100644 --- a/doc/driver-model/README.txt +++ b/doc/driver-model/README.txt @@ -388,12 +388,12 @@ Device Sequence Numbers U-Boot numbers devices from 0 in many situations, such as in the command line for I2C and SPI buses, and the device names for serial ports (serial0, serial1, ...). Driver model supports this numbering and permits devices -to be locating by their 'sequence'. This numbering unique identifies a +to be locating by their 'sequence'. This numbering uniquely identifies a device in its uclass, so no two devices within a particular uclass can have the same sequence number. Sequence numbers start from 0 but gaps are permitted. For example, a board -may have I2C buses 0, 1, 4, 5 but no 2 or 3. The choice of how devices are +may have I2C buses 1, 4, 5 but no 0, 2 or 3. The choice of how devices are numbered is up to a particular board, and may be set by the SoC in some cases. While it might be tempting to automatically renumber the devices where there are gaps in the sequence, this can lead to confusion and is @@ -403,7 +403,7 @@ Each device can request a sequence number. If none is required then the device will be automatically allocated the next available sequence number. To specify the sequence number in the device tree an alias is typically -used. +used. Make sure that the uclass has the DM_UC_FLAG_SEQ_ALIAS flag set. aliases { serial2 = "/serial@22230000"; @@ -413,43 +413,18 @@ This indicates that in the uclass called "serial", the named node ("/serial@22230000") will be given sequence number 2. Any command or driver which requests serial device 2 will obtain this device. -Some devices represent buses where the devices on the bus are numbered or -addressed. For example, SPI typically numbers its slaves from 0, and I2C -uses a 7-bit address. In these cases the 'reg' property of the subnode is -used, for example: +More commonly you can use node references, which expand to the full path: -{ - aliases { - spi2 = "/spi@22300000"; - }; - - spi@22300000 { - #address-cells = <1>; - #size-cells = <1>; - spi-flash@0 { - reg = <0>; - ... - } - eeprom@1 { - reg = <1>; - }; - }; - -In this case we have a SPI bus with two slaves at 0 and 1. The SPI bus -itself is numbered 2. So we might access the SPI flash with: - - sf probe 2:0 - -and the eeprom with - - sspi 2:1 32 ef - -These commands simply need to look up the 2nd device in the SPI uclass to -find the right SPI bus. Then, they look at the children of that bus for the -right sequence number (0 or 1 in this case). +aliases { + serial2 = &serial_2; +}; +... +serial_2: serial@22230000 { +... +}; -Typically the alias method is used for top-level nodes and the 'reg' method -is used only for buses. +The alias resolves to the same string in this case, but this version is +easier to read. Device sequence numbers are resolved when a device is probed. Before then the sequence number is only a request which may or may not be honoured, diff --git a/drivers/core/device.c b/drivers/core/device.c index 2606d187177..f78b78a299e 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -53,24 +53,22 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, dev->driver = drv; dev->uclass = uc; - /* - * For some devices, such as a SPI or I2C bus, the 'reg' property - * is a reasonable indicator of the sequence number. But if there is - * an alias, we use that in preference. In any case, this is just - * a 'requested' sequence, and will be resolved (and ->seq updated) - * when the device is probed. - */ dev->seq = -1; + dev->req_seq = -1; #ifdef CONFIG_OF_CONTROL - dev->req_seq = fdtdec_get_int(gd->fdt_blob, of_offset, "reg", -1); - if (!IS_ERR_VALUE(dev->req_seq)) - dev->req_seq &= INT_MAX; - if (uc->uc_drv->name && of_offset != -1) { - fdtdec_get_alias_seq(gd->fdt_blob, uc->uc_drv->name, of_offset, - &dev->req_seq); + /* + * Some devices, such as a SPI bus, I2C bus and serial ports are + * numbered using aliases. + * + * This is just a 'requested' sequence, and will be + * resolved (and ->seq updated) when the device is probed. + */ + if (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS) { + if (uc->uc_drv->name && of_offset != -1) { + fdtdec_get_alias_seq(gd->fdt_blob, uc->uc_drv->name, + of_offset, &dev->req_seq); + } } -#else - dev->req_seq = -1; #endif if (!dev->platdata && drv->platdata_auto_alloc_size) { dev->flags |= DM_FLAG_ALLOC_PDATA; diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 24e5ec68f2b..94b49dfe52b 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -453,6 +453,7 @@ int i2c_post_bind(struct udevice *dev) UCLASS_DRIVER(i2c) = { .id = UCLASS_I2C, .name = "i2c", + .flags = DM_UC_FLAG_SEQ_ALIAS, .per_device_auto_alloc_size = sizeof(struct dm_i2c_bus), .post_bind = i2c_post_bind, .post_probe = i2c_post_probe, diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index d1b5777cecd..9131a8f93d9 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -297,6 +297,7 @@ static int serial_pre_remove(struct udevice *dev) UCLASS_DRIVER(serial) = { .id = UCLASS_SERIAL, .name = "serial", + .flags = DM_UC_FLAG_SEQ_ALIAS, .post_probe = serial_post_probe, .pre_remove = serial_pre_remove, .per_device_auto_alloc_size = sizeof(struct serial_dev_priv), diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 7a57bceb260..35756ad784e 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -344,6 +344,7 @@ int spi_ofdata_to_platdata(const void *blob, int node, UCLASS_DRIVER(spi) = { .id = UCLASS_SPI, .name = "spi", + .flags = DM_UC_FLAG_SEQ_ALIAS, .post_bind = spi_post_bind, .post_probe = spi_post_probe, .per_device_auto_alloc_size = sizeof(struct dm_spi_bus), diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 7d92d34bec0..9000b22c38d 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -40,6 +40,9 @@ struct uclass { struct udevice; +/* Members of this uclass sequence themselves with aliases */ +#define DM_UC_FLAG_SEQ_ALIAS (1 << 0) + /** * struct uclass_driver - Driver for the uclass * @@ -66,6 +69,7 @@ struct udevice; * a falback if this member is 0 in the driver. * @ops: Uclass operations, providing the consistent interface to devices * within the uclass. + * @flags: Flags for this uclass (DM_UC_...) */ struct uclass_driver { const char *name; @@ -80,6 +84,7 @@ struct uclass_driver { int per_device_auto_alloc_size; int per_child_platdata_auto_alloc_size; const void *ops; + uint32_t flags; }; /* Declare a new uclass_driver */ diff --git a/test/dm/bus.c b/test/dm/bus.c index e18a6f72499..972c4497908 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -88,12 +88,13 @@ U_BOOT_DRIVER(testbus_drv) = { UCLASS_DRIVER(testbus) = { .name = "testbus", .id = UCLASS_TEST_BUS, + .flags = DM_UC_FLAG_SEQ_ALIAS, }; /* Test that we can probe for children */ static int dm_test_bus_children(struct dm_test_state *dms) { - int num_devices = 4; + int num_devices = 6; struct udevice *bus; struct uclass *uc; diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index dc4ebf9adaf..dfcb3af61bb 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -89,6 +89,7 @@ int testfdt_ping(struct udevice *dev, int pingval, int *pingret) UCLASS_DRIVER(testfdt) = { .name = "testfdt", .id = UCLASS_TEST_FDT, + .flags = DM_UC_FLAG_SEQ_ALIAS, }; int dm_check_devices(struct dm_test_state *dms, int num_devices) @@ -128,7 +129,7 @@ int dm_check_devices(struct dm_test_state *dms, int num_devices) /* Test that FDT-based binding works correctly */ static int dm_test_fdt(struct dm_test_state *dms) { - const int num_devices = 4; + const int num_devices = 6; struct udevice *dev; struct uclass *uc; int ret; @@ -184,7 +185,7 @@ static int dm_test_fdt_uclass_seq(struct dm_test_state *dms) ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_FDT, 3, true, &dev)); ut_asserteq_str("b-test", dev->name); - ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_FDT, 0, true, &dev)); + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_FDT, 8, true, &dev)); ut_asserteq_str("a-test", dev->name); ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 5, @@ -220,11 +221,11 @@ static int dm_test_fdt_uclass_seq(struct dm_test_state *dms) ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_TEST_FDT, 1, &dev)); ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); - ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 4, &dev)); /* But now that it is probed, we can find it */ ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 1, &dev)); - ut_asserteq_str("a-test", dev->name); + ut_asserteq_str("f-test", dev->name); return 0; } diff --git a/test/dm/test.dts b/test/dm/test.dts index 33f2c00747d..84024a44a3f 100644 --- a/test/dm/test.dts +++ b/test/dm/test.dts @@ -8,7 +8,15 @@ aliases { console = &uart0; + i2c0 = "/i2c@0"; + spi0 = "/spi@0"; testfdt6 = "/e-test"; + testbus3 = "/some-bus"; + testfdt0 = "/some-bus/c-test@0"; + testfdt1 = "/some-bus/c-test@1"; + testfdt3 = "/b-test"; + testfdt5 = "/some-bus/c-test@5"; + testfdt8 = "/a-test"; }; uart0: serial { @@ -86,6 +94,14 @@ compatible = "google,another-fdt-test"; }; + f-test { + compatible = "denx,u-boot-fdt-test"; + }; + + g-test { + compatible = "denx,u-boot-fdt-test"; + }; + gpio_a: base-gpios { compatible = "sandbox,gpio"; gpio-controller; -- cgit v1.3.1 From dac8db2ce66944828e441cccf25703b262a256cd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:06 -0700 Subject: dm: core: Allow uclasses to specify private data for a device's children In many cases the per-child private data for a device's children is defined by the uclass rather than the individual driver. For example, a SPI bus needs to store information about each of its children, but all SPI drivers store the same information. It makes sense to allow the uclass to define this data. If the driver provides a size value for its per-child private data, then use it. Failng that, fall back to that provided by the uclass. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/device-remove.c | 4 ++++ drivers/core/device.c | 4 ++++ include/dm/uclass.h | 4 ++++ test/dm/bus.c | 31 +++++++++++++++++++++++++++++-- 4 files changed, 41 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 56c358a0ec2..3a5f48df7a2 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -126,6 +126,10 @@ void device_free(struct udevice *dev) } if (dev->parent) { size = dev->parent->driver->per_child_auto_alloc_size; + if (!size) { + size = dev->parent->uclass->uc_drv-> + per_child_auto_alloc_size; + } if (size) { free(dev->parent_priv); dev->parent_priv = NULL; diff --git a/drivers/core/device.c b/drivers/core/device.c index f78b78a299e..78bc460d35f 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -201,6 +201,10 @@ int device_probe_child(struct udevice *dev, void *parent_priv) /* Ensure all parents are probed */ if (dev->parent) { size = dev->parent->driver->per_child_auto_alloc_size; + if (!size) { + size = dev->parent->uclass->uc_drv-> + per_child_auto_alloc_size; + } if (size) { dev->parent_priv = calloc(1, size); if (!dev->parent_priv) { diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 9000b22c38d..ac6c85072c9 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -63,6 +63,9 @@ struct udevice; * @per_device_auto_alloc_size: Each device can hold private data owned * by the uclass. If required this will be automatically allocated if this * value is non-zero. + * @per_child_auto_alloc_size: Each child device (of a parent in this + * uclass) can hold parent data for the device/uclass. This value is only + * used as a falback if this member is 0 in the driver. * @per_child_platdata_auto_alloc_size: A bus likes to store information about * its children. If non-zero this is the size of this data, to be allocated * in the child device's parent_platdata pointer. This value is only used as @@ -82,6 +85,7 @@ struct uclass_driver { int (*destroy)(struct uclass *class); int priv_auto_alloc_size; int per_device_auto_alloc_size; + int per_child_auto_alloc_size; int per_child_platdata_auto_alloc_size; const void *ops; uint32_t flags; diff --git a/test/dm/bus.c b/test/dm/bus.c index 972c4497908..e9096970628 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -192,7 +192,7 @@ DM_TEST(dm_test_bus_children_iterators, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); /* Test that the bus can store data about each child */ -static int dm_test_bus_parent_data(struct dm_test_state *dms) +static int test_bus_parent_data(struct dm_test_state *dms) { struct dm_test_parent_data *parent_data; struct udevice *bus, *dev; @@ -251,9 +251,36 @@ static int dm_test_bus_parent_data(struct dm_test_state *dms) return 0; } - +/* Test that the bus can store data about each child */ +static int dm_test_bus_parent_data(struct dm_test_state *dms) +{ + return test_bus_parent_data(dms); +} DM_TEST(dm_test_bus_parent_data, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); +/* As above but the size is controlled by the uclass */ +static int dm_test_bus_parent_data_uclass(struct dm_test_state *dms) +{ + struct udevice *bus; + int size; + int ret; + + /* Set the driver size to 0 so that the uclass size is used */ + ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus)); + size = bus->driver->per_child_auto_alloc_size; + bus->uclass->uc_drv->per_child_auto_alloc_size = size; + bus->driver->per_child_auto_alloc_size = 0; + ret = test_bus_parent_data(dms); + if (ret) + return ret; + bus->uclass->uc_drv->per_child_auto_alloc_size = 0; + bus->driver->per_child_auto_alloc_size = size; + + return 0; +} +DM_TEST(dm_test_bus_parent_data_uclass, + DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + /* Test that the bus ops are called when a child is probed/removed */ static int dm_test_bus_parent_ops(struct dm_test_state *dms) { -- cgit v1.3.1 From 081f2fcbd9a95ba10677065359791f8fea3f8c58 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:08 -0700 Subject: dm: core: Allow the uclass to set up a device's child after binding For buses, after a child is bound, allow the uclass to perform some processing. This can be used to figure out the address of the child (e.g. the chip select for SPI slaves) so that it is ready to be probed. This avoids bus drivers having to repeat the same process, which really should be done by the uclass, since it is common. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/uclass.c | 21 ++++++++++++++++----- include/dm/uclass.h | 2 ++ test/dm/bus.c | 26 ++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 901b06ed2ba..a5445518f57 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -319,18 +319,29 @@ int uclass_bind_device(struct udevice *dev) int ret; uc = dev->uclass; - list_add_tail(&dev->uclass_node, &uc->dev_head); + if (dev->parent) { + struct uclass_driver *uc_drv = dev->parent->uclass->uc_drv; + + if (uc_drv->child_post_bind) { + ret = uc_drv->child_post_bind(dev); + if (ret) + goto err; + } + } if (uc->uc_drv->post_bind) { ret = uc->uc_drv->post_bind(dev); - if (ret) { - list_del(&dev->uclass_node); - return ret; - } + if (ret) + goto err; } return 0; +err: + /* There is no need to undo the parent's post_bind call */ + list_del(&dev->uclass_node); + + return ret; } int uclass_unbind_device(struct udevice *dev) diff --git a/include/dm/uclass.h b/include/dm/uclass.h index ac6c85072c9..5c5b8f42083 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -55,6 +55,7 @@ struct udevice; * @pre_unbind: Called before a device is unbound from this uclass * @post_probe: Called after a new device is probed * @pre_remove: Called before a device is removed + * @child_post_bind: Called after a child is bound to a device in this uclass * @init: Called to set up the uclass * @destroy: Called to destroy the uclass * @priv_auto_alloc_size: If non-zero this is the size of the private data @@ -81,6 +82,7 @@ struct uclass_driver { int (*pre_unbind)(struct udevice *dev); int (*post_probe)(struct udevice *dev); int (*pre_remove)(struct udevice *dev); + int (*child_post_bind)(struct udevice *dev); int (*init)(struct uclass *class); int (*destroy)(struct uclass *class); int priv_auto_alloc_size; diff --git a/test/dm/bus.c b/test/dm/bus.c index e9096970628..c123ed79310 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -18,6 +18,7 @@ DECLARE_GLOBAL_DATA_PTR; struct dm_test_parent_platdata { int count; int bind_flag; + int uclass_bind_flag; }; enum { @@ -38,6 +39,7 @@ static int testbus_child_post_bind(struct udevice *dev) plat = dev_get_parent_platdata(dev); plat->bind_flag = 1; + plat->uclass_bind_flag = 2; return 0; } @@ -443,3 +445,27 @@ static int dm_test_bus_child_post_bind(struct dm_test_state *dms) return 0; } DM_TEST(dm_test_bus_child_post_bind, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test that the child post_bind method is called */ +static int dm_test_bus_child_post_bind_uclass(struct dm_test_state *dms) +{ + struct dm_test_parent_platdata *plat; + struct udevice *bus, *dev; + int child_count; + + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + for (device_find_first_child(bus, &dev), child_count = 0; + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_platdata(dev); + ut_assert(plat != NULL); + ut_asserteq(2, plat->uclass_bind_flag); + child_count++; + } + ut_asserteq(3, child_count); + + return 0; +} +DM_TEST(dm_test_bus_child_post_bind_uclass, + DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From 83c7e434c9dd3ca81f8b763e23c1881b973bcf2f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:10 -0700 Subject: dm: core: Allow uclass to set up a device's child before it is probed Some buses need to set up their devices before they can be used. This setup may well be common to all buses in a particular uclass. Support a common pre-probe method for the uclass, called before any bus devices are probed. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/device.c | 4 ++++ drivers/core/uclass.c | 13 +++++++++++++ include/dm/test.h | 3 +++ include/dm/uclass-internal.h | 11 +++++++++++ include/dm/uclass.h | 1 + test/dm/bus.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ test/dm/test-fdt.c | 7 +++++++ 7 files changed, 85 insertions(+) (limited to 'test') diff --git a/drivers/core/device.c b/drivers/core/device.c index 78bc460d35f..b73d3b8961d 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -227,6 +227,10 @@ int device_probe_child(struct udevice *dev, void *parent_priv) } dev->seq = seq; + ret = uclass_pre_probe_child(dev); + if (ret) + goto fail; + if (dev->parent && dev->parent->driver->child_pre_probe) { ret = dev->parent->driver->child_pre_probe(dev); if (ret) diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index a5445518f57..289a5d2d53d 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -391,6 +391,19 @@ int uclass_resolve_seq(struct udevice *dev) return seq; } +int uclass_pre_probe_child(struct udevice *dev) +{ + struct uclass_driver *uc_drv; + + if (!dev->parent) + return 0; + uc_drv = dev->parent->uclass->uc_drv; + if (uc_drv->child_pre_probe) + return uc_drv->child_pre_probe(dev); + + return 0; +} + int uclass_post_probe_device(struct udevice *dev) { struct uclass_driver *uc_drv = dev->uclass->uc_drv; diff --git a/include/dm/test.h b/include/dm/test.h index f08c05da814..707c69e07f0 100644 --- a/include/dm/test.h +++ b/include/dm/test.h @@ -67,6 +67,8 @@ enum { struct dm_test_priv { int ping_total; int op_count[DM_TEST_OP_COUNT]; + int uclass_flag; + int uclass_total; }; /** @@ -88,6 +90,7 @@ struct dm_test_uclass_priv { * * @sum: Test value used to check parent data works correctly * @flag: Used to track calling of parent operations + * @uclass_flag: Used to track calling of parent operations by uclass */ struct dm_test_parent_data { int sum; diff --git a/include/dm/uclass-internal.h b/include/dm/uclass-internal.h index f718f37affb..f2f254a8259 100644 --- a/include/dm/uclass-internal.h +++ b/include/dm/uclass-internal.h @@ -43,6 +43,17 @@ int uclass_bind_device(struct udevice *dev); */ int uclass_unbind_device(struct udevice *dev); +/** + * uclass_pre_probe_child() - Deal with a child that is about to be probed + * + * Perform any pre-processing that is needed by the uclass before it can be + * probed. + * + * @dev: Pointer to the device + * #return 0 on success, -ve on error + */ +int uclass_pre_probe_child(struct udevice *dev); + /** * uclass_post_probe_device() - Deal with a device that has just been probed * diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 5c5b8f42083..d6c40c60dda 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -83,6 +83,7 @@ struct uclass_driver { int (*post_probe)(struct udevice *dev); int (*pre_remove)(struct udevice *dev); int (*child_post_bind)(struct udevice *dev); + int (*child_pre_probe)(struct udevice *dev); int (*init)(struct uclass *class); int (*destroy)(struct uclass *class); int priv_auto_alloc_size; diff --git a/test/dm/bus.c b/test/dm/bus.c index c123ed79310..faffe6a385b 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -53,6 +53,15 @@ static int testbus_child_pre_probe(struct udevice *dev) return 0; } +static int testbus_child_pre_probe_uclass(struct udevice *dev) +{ + struct dm_test_priv *priv = dev_get_priv(dev); + + priv->uclass_flag++; + + return 0; +} + static int testbus_child_post_remove(struct udevice *dev) { struct dm_test_parent_data *parent_data = dev_get_parentdata(dev); @@ -91,6 +100,7 @@ UCLASS_DRIVER(testbus) = { .name = "testbus", .id = UCLASS_TEST_BUS, .flags = DM_UC_FLAG_SEQ_ALIAS, + .child_pre_probe = testbus_child_pre_probe_uclass, }; /* Test that we can probe for children */ @@ -469,3 +479,39 @@ static int dm_test_bus_child_post_bind_uclass(struct dm_test_state *dms) } DM_TEST(dm_test_bus_child_post_bind_uclass, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* + * Test that the bus' uclass' child_pre_probe() is called before the + * device's probe() method + */ +static int dm_test_bus_child_pre_probe_uclass(struct dm_test_state *dms) +{ + struct udevice *bus, *dev; + int child_count; + + /* + * See testfdt_drv_probe() which effectively checks that the uclass + * flag is set before that method is called + */ + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + for (device_find_first_child(bus, &dev), child_count = 0; + dev; + device_find_next_child(&dev)) { + struct dm_test_priv *priv = dev_get_priv(dev); + + /* Check that things happened in the right order */ + ut_asserteq_ptr(NULL, priv); + ut_assertok(device_probe(dev)); + + priv = dev_get_priv(dev); + ut_assert(priv != NULL); + ut_asserteq(1, priv->uclass_flag); + ut_asserteq(1, priv->uclass_total); + child_count++; + } + ut_asserteq(3, child_count); + + return 0; +} +DM_TEST(dm_test_bus_child_pre_probe_uclass, + DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index dfcb3af61bb..b8ee9599a22 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -51,6 +51,13 @@ static int testfdt_drv_probe(struct udevice *dev) priv->ping_total += DM_TEST_START_TOTAL; + /* + * If this device is on a bus, the uclass_flag will be set before + * calling this function. This is used by + * dm_test_bus_child_pre_probe_uclass(). + */ + priv->uclass_total += priv->uclass_flag; + return 0; } -- cgit v1.3.1 From d0cff03e187cc1de3d6b477b92c376aae27c95e8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:12 -0700 Subject: dm: spi: Move slave details to child platdata At present we go through various contortions to store the SPI slave's chip select in its private data. This only exists when the slave is active so must be set up when it is probed. Until the device is probed we don't actually know what chip select it will appear on. However, now that we can support per-child platform data, we can use that instead. This allows us to set up the chip select when the child is bound, and avoid the messy contortions. Unfortunately this is a fairly large change and it seems to be difficult to break it down further. Signed-off-by: Simon Glass --- drivers/misc/cros_ec_spi.c | 19 ----------- drivers/mtd/spi/sandbox.c | 5 +++ drivers/mtd/spi/sf_probe.c | 3 +- drivers/spi/soft_spi.c | 9 ----- drivers/spi/spi-uclass.c | 83 +++++++++++++++++++++++++++------------------- include/spi.h | 42 +++++++++++++++++------ test/dm/spi.c | 6 ++-- 7 files changed, 91 insertions(+), 76 deletions(-) (limited to 'test') diff --git a/drivers/misc/cros_ec_spi.c b/drivers/misc/cros_ec_spi.c index e6dba298b1e..25a5a0468e6 100644 --- a/drivers/misc/cros_ec_spi.c +++ b/drivers/misc/cros_ec_spi.c @@ -202,25 +202,6 @@ int cros_ec_spi_init(struct cros_ec_dev *dev, const void *blob) #ifdef CONFIG_DM_CROS_EC int cros_ec_probe(struct udevice *dev) { - struct spi_slave *slave = dev_get_parentdata(dev); - int ret; - - /* - * TODO(sjg@chromium.org) - * - * This is really horrible at present. It is an artifact of removing - * the child_pre_probe() method for SPI. Everything here could go in - * an automatic function, except that spi_get_bus_and_cs() wants to - * set it up manually and call device_probe_child(). - * - * The solution may be to re-enable the child_pre_probe() method for - * SPI and have it do nothing if the child is already passed in via - * device_probe_child(). - */ - slave->dev = dev; - ret = spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, slave); - if (ret) - return ret; return cros_ec_register(dev); } diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c index 106dda9ccc5..d576d31243a 100644 --- a/drivers/mtd/spi/sandbox.c +++ b/drivers/mtd/spi/sandbox.c @@ -590,6 +590,11 @@ int sandbox_sf_bind_emul(struct sandbox_state *state, int busnum, int cs, void sandbox_sf_unbind_emul(struct sandbox_state *state, int busnum, int cs) { + struct udevice *dev; + + dev = state->spi[busnum][cs].emul; + device_remove(dev); + device_unbind(dev); state->spi[busnum][cs].emul = NULL; } diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index ce9987fd1a8..41037238590 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -481,11 +481,12 @@ int spi_flash_std_erase(struct udevice *dev, u32 offset, size_t len) int spi_flash_std_probe(struct udevice *dev) { struct spi_slave *slave = dev_get_parentdata(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); struct spi_flash *flash; flash = dev->uclass_priv; flash->dev = dev; - debug("%s: slave=%p, cs=%d\n", __func__, slave, slave->cs); + debug("%s: slave=%p, cs=%d\n", __func__, slave, plat->cs); return spi_flash_probe_slave(slave, flash); } diff --git a/drivers/spi/soft_spi.c b/drivers/spi/soft_spi.c index 9f7d80ee4da..6ae45f5377a 100644 --- a/drivers/spi/soft_spi.c +++ b/drivers/spi/soft_spi.c @@ -179,14 +179,6 @@ static int soft_spi_set_mode(struct udevice *dev, unsigned int mode) return 0; } -static int soft_spi_child_pre_probe(struct udevice *dev) -{ - struct spi_slave *slave = dev_get_parentdata(dev); - - slave->dev = dev; - return spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, slave); -} - static const struct dm_spi_ops soft_spi_ops = { .claim_bus = soft_spi_claim_bus, .release_bus = soft_spi_release_bus, @@ -241,5 +233,4 @@ U_BOOT_DRIVER(soft_spi) = { .platdata_auto_alloc_size = sizeof(struct soft_spi_platdata), .priv_auto_alloc_size = sizeof(struct soft_spi_priv), .probe = soft_spi_probe, - .child_pre_probe = soft_spi_child_pre_probe, }; diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 2c134eb39f7..63a6217cc62 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -98,11 +98,21 @@ int spi_post_bind(struct udevice *dev) return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); } -int spi_post_probe(struct udevice *dev) +int spi_child_post_bind(struct udevice *dev) { - struct dm_spi_bus *spi = dev->uclass_priv; + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); - spi->max_hz = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + if (dev->of_offset == -1) + return 0; + + return spi_slave_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, plat); +} + +int spi_post_probe(struct udevice *bus) +{ + struct dm_spi_bus *spi = bus->uclass_priv; + + spi->max_hz = fdtdec_get_int(gd->fdt_blob, bus->of_offset, "spi-max-frequency", 0); return 0; @@ -110,18 +120,29 @@ int spi_post_probe(struct udevice *dev) int spi_child_pre_probe(struct udevice *dev) { + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); struct spi_slave *slave = dev_get_parentdata(dev); + /* + * This is needed because we pass struct spi_slave around the place + * instead slave->dev (a struct udevice). So we have to have some + * way to access the slave udevice given struct spi_slave. Once we + * change the SPI API to use udevice instead of spi_slave, we can + * drop this. + */ slave->dev = dev; + slave->max_hz = plat->max_hz; + slave->mode = plat->mode; + return 0; } int spi_chip_select(struct udevice *dev) { - struct spi_slave *slave = dev_get_parentdata(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); - return slave ? slave->cs : -ENOENT; + return plat ? plat->cs : -ENOENT; } int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp) @@ -130,17 +151,11 @@ int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp) for (device_find_first_child(bus, &dev); dev; device_find_next_child(&dev)) { - struct spi_slave store; - struct spi_slave *slave = dev_get_parentdata(dev); + struct dm_spi_slave_platdata *plat; - if (!slave) { - slave = &store; - spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, - slave); - } - debug("%s: slave=%p, cs=%d\n", __func__, slave, - slave ? slave->cs : -1); - if (slave && slave->cs == cs) { + plat = dev_get_parent_platdata(dev); + debug("%s: plat=%p, cs=%d\n", __func__, plat, plat->cs); + if (plat->cs == cs) { *devp = dev; return 0; } @@ -224,7 +239,6 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, struct udevice **busp, struct spi_slave **devp) { struct udevice *bus, *dev; - struct spi_slave *slave; bool created = false; int ret; @@ -241,11 +255,17 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, * SPI flash chip - we will bind to the correct driver. */ if (ret == -ENODEV && drv_name) { + struct dm_spi_slave_platdata *plat; + debug("%s: Binding new device '%s', busnum=%d, cs=%d, driver=%s\n", __func__, dev_name, busnum, cs, drv_name); ret = device_bind_driver(bus, drv_name, dev_name, &dev); if (ret) return ret; + plat = dev_get_parent_platdata(dev); + plat->cs = cs; + plat->max_hz = speed; + plat->mode = mode; created = true; } else if (ret) { printf("Invalid chip select %d:%d (err=%d)\n", busnum, cs, @@ -254,23 +274,13 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, } if (!device_active(dev)) { - slave = (struct spi_slave *)calloc(1, - sizeof(struct spi_slave)); - if (!slave) { - ret = -ENOMEM; - goto err; - } + struct spi_slave *slave; - ret = spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, - slave); + ret = device_probe(dev); if (ret) goto err; - slave->cs = cs; + slave = dev_get_parentdata(dev); slave->dev = dev; - ret = device_probe_child(dev, slave); - free(slave); - if (ret) - goto err; } ret = spi_set_speed_mode(bus, speed, mode); @@ -284,6 +294,8 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, return 0; err: + debug("%s: Error path, credted=%d, device '%s'\n", __func__, + created, dev->name); if (created) { device_remove(dev); device_unbind(dev); @@ -330,13 +342,13 @@ void spi_free_slave(struct spi_slave *slave) slave->dev = NULL; } -int spi_ofdata_to_platdata(const void *blob, int node, - struct spi_slave *spi) +int spi_slave_ofdata_to_platdata(const void *blob, int node, + struct dm_spi_slave_platdata *plat) { int mode = 0; - spi->cs = fdtdec_get_int(blob, node, "reg", -1); - spi->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", 0); + plat->cs = fdtdec_get_int(blob, node, "reg", -1); + plat->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")) @@ -345,7 +357,7 @@ int spi_ofdata_to_platdata(const void *blob, int node, mode |= SPI_CS_HIGH; if (fdtdec_get_bool(blob, node, "spi-half-duplex")) mode |= SPI_PREAMBLE; - spi->mode = mode; + plat->mode = mode; return 0; } @@ -359,6 +371,9 @@ UCLASS_DRIVER(spi) = { .child_pre_probe = spi_child_pre_probe, .per_device_auto_alloc_size = sizeof(struct dm_spi_bus), .per_child_auto_alloc_size = sizeof(struct spi_slave), + .per_child_platdata_auto_alloc_size = + sizeof(struct dm_spi_slave_platdata), + .child_post_bind = spi_child_post_bind, }; UCLASS_DRIVER(spi_generic) = { diff --git a/include/spi.h b/include/spi.h index ec17bd0bcc8..c58e4535596 100644 --- a/include/spi.h +++ b/include/spi.h @@ -56,20 +56,42 @@ #define SPI_DEFAULT_WORDLEN 8 #ifdef CONFIG_DM_SPI +/* TODO(sjg@chromium.org): Remove this and use max_hz from struct spi_slave */ struct dm_spi_bus { uint max_hz; }; +/** + * struct dm_spi_platdata - platform data for all SPI slaves + * + * This describes a SPI slave, a child device of the SPI bus. To obtain this + * struct from a spi_slave, use dev_get_parent_platdata(dev) or + * dev_get_parent_platdata(slave->dev). + * + * This data is immuatable. Each time the device is probed, @max_hz and @mode + * will be copied to struct spi_slave. + * + * @cs: Chip select number (0..n-1) + * @max_hz: Maximum bus speed that this slave can tolerate + * @mode: SPI mode to use for this device (see SPI mode flags) + */ +struct dm_spi_slave_platdata { + unsigned int cs; + uint max_hz; + uint mode; +}; + #endif /* CONFIG_DM_SPI */ /** * struct spi_slave - Representation of a SPI slave * * For driver model this is the per-child data used by the SPI bus. It can - * be accessed using dev_get_parentdata() on the slave device. Each SPI - * driver should define this child data in its U_BOOT_DRIVER() definition: - * - * .per_child_auto_alloc_size = sizeof(struct spi_slave), + * be accessed using dev_get_parentdata() on the slave device. The SPI uclass + * sets uip per_child_auto_alloc_size to sizeof(struct spi_slave), and the + * driver should not override it. Two platform data fields (max_hz and mode) + * are copied into this structure to provide an initial value. This allows + * them to be changed, since we should never change platform data in drivers. * * If not using driver model, drivers are expected to extend this with * controller-specific data. @@ -97,8 +119,8 @@ struct spi_slave { uint mode; #else unsigned int bus; -#endif unsigned int cs; +#endif u8 op_mode_rx; u8 op_mode_tx; unsigned int wordlen; @@ -545,16 +567,16 @@ int spi_chip_select(struct udevice *slave); int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp); /** - * spi_ofdata_to_platdata() - decode standard SPI platform data + * spi_slave_ofdata_to_platdata() - decode standard SPI platform data * - * This decodes the speed and mode from a device tree node and puts it into - * the spi_slave structure. + * This decodes the speed and mode for a slave from a device tree node * * @blob: Device tree blob * @node: Node offset to read from - * @spi: Place to put the decoded information + * @plat: Place to put the decoded information */ -int spi_ofdata_to_platdata(const void *blob, int node, struct spi_slave *spi); +int spi_slave_ofdata_to_platdata(const void *blob, int node, + struct dm_spi_slave_platdata *plat); /** * spi_cs_info() - Check information on a chip select diff --git a/test/dm/spi.c b/test/dm/spi.c index 61b5b2548c4..c7ee65207b0 100644 --- a/test/dm/spi.c +++ b/test/dm/spi.c @@ -36,7 +36,6 @@ static int dm_test_spi_find(struct dm_test_state *dms) ut_asserteq(0, uclass_get_device_by_seq(UCLASS_SPI, busnum, &bus)); ut_assertok(spi_cs_info(bus, cs, &info)); of_offset = info.dev->of_offset; - sandbox_sf_unbind_emul(state_get_current(), busnum, cs); device_remove(info.dev); device_unbind(info.dev); @@ -45,7 +44,7 @@ static int dm_test_spi_find(struct dm_test_state *dms) * reports that CS 0 is present */ ut_assertok(spi_cs_info(bus, cs, &info)); - ut_asserteq_ptr(info.dev, NULL); + ut_asserteq_ptr(NULL, info.dev); /* This finds nothing because we removed the device */ ut_asserteq(-ENODEV, spi_find_bus_and_cs(busnum, cs, &bus, &dev)); @@ -62,8 +61,9 @@ static int dm_test_spi_find(struct dm_test_state *dms) ut_asserteq(-ENOENT, spi_get_bus_and_cs(busnum, cs, speed, mode, "spi_flash_std", "name", &bus, &slave)); + sandbox_sf_unbind_emul(state_get_current(), busnum, cs); ut_assertok(spi_cs_info(bus, cs, &info)); - ut_asserteq_ptr(info.dev, NULL); + ut_asserteq_ptr(NULL, info.dev); /* Add the emulation and try again */ ut_assertok(sandbox_sf_bind_emul(state, busnum, cs, bus, of_offset, -- cgit v1.3.1