[U-Boot] [PATCH RESEND 0/9] sunxi: chip: Enable the DIP auto-detection

The NextThing's C.H.I.P. can have expansion boards called DIPs. Those DIPs are connected through the external headers, and comes with an identification mechanism based on 1-Wire EEPROMs.
That auto-detection works great, because 1-Wire allows the enumeration, and the EEPROMs are guaranteed to have different addresses, which allows us to stack as many DIPs as possible, without any constraints.
Since those DIPs can be close to anything, ranging from a simple PWM-based buzzer to a full featured device such as the PocketCHIP (which comes with a touchscreen, a keyboard, a battery, etc), some adjustments might be needed in U-Boot to be able to present something that just works to the user.
In particular, we need to: - Apply an overlay if the device is something that should be dealt with early in the boot process (display, storage device) - Adjust the U-Boot environment if the DIP has a display to change the default video output - Adjust the kernel command line in previous case for Linux to have the same default
In order to achieve that, we introduced some logic optionally hooked into the sunxi board, two new uclasses for 1-Wire and EEPROMs, and a bunch of new environment variables: - dip-auto-video to control the automatic video set up (default to yes) - dip_overlay_cmd that is a script to load the overlay $dip_overlay_name to $dip_addr_r, from whatever place it was stored in, and later apply it. - kernelarg_video to host the default kernel output in the kernel command line
I think the biggest drawback at the moment is that we maintain a list of DIPs and the actions needed directly into the C code, which will make it quite hard to customise for end users and tedious to maintain in the long term. I couldn't really get my head around a better solution, so feel free to suggest alternative approaches.
Let me know what you think, Maxime
Maxime Ripard (9): fdtdec: Fix alias retrieval sunxi: chip: Add 1-wire node w1: Add 1-Wire uclass w1: Add 1-Wire gpio driver EEPROM: Add an EEPROM uclass eeprom: Add DS2431 support video: Allow board hook before video init sunxi: Add CHIP's DIP support config: chip: Enable DIP support
arch/arm/dts/sun5i-r8-chip.dts | 15 ++- board/sunxi/Kconfig | 9 +- board/sunxi/Makefile | 1 +- board/sunxi/board.c | 6 +- board/sunxi/dip.c | 227 ++++++++++++++++++++++++++++++- configs/CHIP_defconfig | 5 +- drivers/Kconfig | 4 +- drivers/Makefile | 2 +- drivers/eeprom/Kconfig | 23 +++- drivers/eeprom/Makefile | 3 +- drivers/eeprom/ds2431.c | 38 +++++- drivers/eeprom/eeprom-uclass.c | 57 +++++++- drivers/video/cfb_console.c | 9 +- drivers/w1/Kconfig | 23 +++- drivers/w1/Makefile | 3 +- drivers/w1/w1-gpio.c | 160 +++++++++++++++++++++- drivers/w1/w1-uclass.c | 259 ++++++++++++++++++++++++++++++++++- include/dm/uclass-id.h | 2 +- include/eeprom.h | 21 +++- include/w1.h | 47 ++++++- lib/fdtdec.c | 3 +- 21 files changed, 916 insertions(+), 1 deletion(-) create mode 100644 board/sunxi/dip.c create mode 100644 drivers/eeprom/Kconfig create mode 100644 drivers/eeprom/Makefile create mode 100644 drivers/eeprom/ds2431.c create mode 100644 drivers/eeprom/eeprom-uclass.c create mode 100644 drivers/w1/Kconfig create mode 100644 drivers/w1/Makefile create mode 100644 drivers/w1/w1-gpio.c create mode 100644 drivers/w1/w1-uclass.c create mode 100644 include/eeprom.h create mode 100644 include/w1.h
base-commit: d8bdfc80da39211d95f10d24e79f2e867305f71b

When the subsystem in u-boot ends up with a number, the alias parsing mechanism was off, being confused between the actual number, and the alias number.
Fix the code to start parsing the alias at the end of our subsystem name.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- lib/fdtdec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 4e619c49a2ff..fc31e3576e5c 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -538,7 +538,8 @@ int fdtdec_get_alias_seq(const void *blob, const char *base, int offset, slash = strrchr(prop, '/'); if (strcmp(slash + 1, find_name)) continue; - val = trailing_strtol(name); + + val = simple_strtol(name + base_len, NULL, 10); if (val != -1) { *seqp = val; debug("Found seq %d\n", *seqp);

The CHIP uses on PD2 (a pin incorrectly called LCD_D2) a bitbanged 1-Wire bus to discover the adapters plug into it.
Add that bus to the device tree.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- arch/arm/dts/sun5i-r8-chip.dts | 15 +++++++++++++++ 1 file changed, 15 insertions(+), 0 deletions(-)
diff --git a/arch/arm/dts/sun5i-r8-chip.dts b/arch/arm/dts/sun5i-r8-chip.dts index 7a8cc2727960..fdba47c2bc33 100644 --- a/arch/arm/dts/sun5i-r8-chip.dts +++ b/arch/arm/dts/sun5i-r8-chip.dts @@ -59,6 +59,7 @@ i2c2 = &i2c2; serial0 = &uart1; serial1 = &uart3; + w10 = &w1; };
chosen { @@ -76,6 +77,13 @@ gpio = <&pio 2 19 GPIO_ACTIVE_HIGH>; /* PC19 */ enable-active-high; }; + + w1: onewire { + compatible = "w1-gpio"; + gpios = <&pio 3 2 GPIO_ACTIVE_HIGH>; /* PD2 */ + pinctrl-names = "default"; + pinctrl-0 = <&chip_w1_pin>; + }; };
&be0 { @@ -175,6 +183,13 @@ allwinner,drive = <SUN4I_PINCTRL_10_MA>; allwinner,pull = <SUN4I_PINCTRL_NO_PULL>; }; + + chip_w1_pin: chip_w1_pin@0 { + allwinner,pins = "PD2"; + allwinner,function = "gpio_in"; + allwinner,drive = <SUN4I_PINCTRL_10_MA>; + allwinner,pull = <SUN4I_PINCTRL_PULL_UP>; + }; };
®_dcdc2 {

We might want to use 1-Wire devices connected on boards such as EEPROMs in U-Boot.
Provide a framework to be able to do that.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- drivers/Kconfig | 2 +- drivers/Makefile | 1 +- drivers/w1/Kconfig | 17 +++- drivers/w1/Makefile | 2 +- drivers/w1/w1-uclass.c | 259 ++++++++++++++++++++++++++++++++++++++++++- include/dm/uclass-id.h | 1 +- include/w1.h | 47 ++++++++- 7 files changed, 329 insertions(+), 0 deletions(-) create mode 100644 drivers/w1/Kconfig create mode 100644 drivers/w1/Makefile create mode 100644 drivers/w1/w1-uclass.c create mode 100644 include/w1.h
diff --git a/drivers/Kconfig b/drivers/Kconfig index e8c9e0a32626..74194b0d6f7f 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -84,6 +84,8 @@ source "drivers/usb/Kconfig"
source "drivers/video/Kconfig"
+source "drivers/w1/Kconfig" + source "drivers/watchdog/Kconfig"
config PHYS_TO_BUS diff --git a/drivers/Makefile b/drivers/Makefile index 761d0b3e85b4..543c43bb0d23 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -91,6 +91,7 @@ obj-y += input/ obj-y += soc/ obj-$(CONFIG_REMOTEPROC) += remoteproc/ obj-y += thermal/ +obj-y += w1/
obj-$(CONFIG_MACH_PIC32) += ddr/microchip/ endif diff --git a/drivers/w1/Kconfig b/drivers/w1/Kconfig new file mode 100644 index 000000000000..0c056b4c06a9 --- /dev/null +++ b/drivers/w1/Kconfig @@ -0,0 +1,17 @@ +# +# W1 subsystem configuration +# + +menu "1-Wire support" + +config W1 + bool "Enable 1-Wire controllers support" + depends on DM + help + Support for the Dallas 1-Wire bus. + +if W1 + +endif + +endmenu diff --git a/drivers/w1/Makefile b/drivers/w1/Makefile new file mode 100644 index 000000000000..26820fa209e1 --- /dev/null +++ b/drivers/w1/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_W1) += w1-uclass.o + diff --git a/drivers/w1/w1-uclass.c b/drivers/w1/w1-uclass.c new file mode 100644 index 000000000000..f9224746806c --- /dev/null +++ b/drivers/w1/w1-uclass.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2015 Free Electrons + * Copyright (c) 2015 NextThing Co. + * + * Maxime Ripard maxime.ripard@free-electrons.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <w1.h> + +#include <dm/device-internal.h> + +#define W1_MATCH_ROM 0x55 +#define W1_SKIP_ROM 0xcc +#define W1_SEARCH 0xf0 + +struct w1_bus { + u64 search_id; +}; + +struct w1_device { + u64 id; +}; + +static int w1_new_device(struct udevice *bus, u64 id) +{ + struct w1_driver_entry *start, *entry; + int n_ents, ret = -ENODEV; + u8 family = id & 0xff; + + debug("%s: Detected new device 0x%llx (family 0x%x)\n", + bus->name, id, family); + + start = ll_entry_start(struct w1_driver_entry, w1_driver_entry); + n_ents = ll_entry_count(struct w1_driver_entry, w1_driver_entry); + + for (entry = start; entry != start + n_ents; entry++) { + const struct driver *drv; + struct w1_device *w1; + struct udevice *dev; + + if (entry->family != family) + continue; + + drv = entry->driver; + ret = device_bind(bus, drv, drv->name, NULL, -1, + &dev); + if (ret) + goto error; + + debug("%s: Match found: %s\n", __func__, drv->name); + + w1 = dev_get_parent_platdata(dev); + w1->id = id; + + return 0; + } + +error: + debug("%s: No matches found: error %d\n", __func__, ret); + return ret; +} + +static int w1_enumerate(struct udevice *bus) +{ + const struct w1_ops *ops = device_get_ops(bus); + struct w1_bus *w1 = dev_get_uclass_priv(bus); + u64 last_rn, rn = w1->search_id, tmp64; + bool last_device = false; + int search_bit, desc_bit = 64; + int last_zero = -1; + u8 triplet_ret = 0; + int i; + + if (!ops->reset || !ops->write_byte || !ops->triplet) + return -ENOSYS; + + while ( !last_device ) { + last_rn = rn; + rn = 0; + + /* + * Reset bus and all 1-wire device state machines + * so they can respond to our requests. + * + * Return 0 - device(s) present, 1 - no devices present. + */ + if (ops->reset(bus)) { + debug("%s: No devices present on the wire.\n", + __func__); + break; + } + + /* Start the search */ + ops->write_byte(bus, W1_SEARCH); + for (i = 0; i < 64; ++i) { + /* Determine the direction/search bit */ + if (i == desc_bit) + search_bit = 1; /* took the 0 path last time, so take the 1 path */ + else if (i > desc_bit) + search_bit = 0; /* take the 0 path on the next branch */ + else + search_bit = ((last_rn >> i) & 0x1); + + /* Read two bits and write one bit */ + triplet_ret = ops->triplet(bus, search_bit); + + /* quit if no device responded */ + if ( (triplet_ret & 0x03) == 0x03 ) + break; + + /* If both directions were valid, and we took the 0 path... */ + if (triplet_ret == 0) + last_zero = i; + + /* extract the direction taken & update the device number */ + tmp64 = (triplet_ret >> 2); + rn |= (tmp64 << i); + } + + if ( (triplet_ret & 0x03) != 0x03 ) { + if ((desc_bit == last_zero) || (last_zero < 0)) { + last_device = 1; + w1->search_id = 0; + } else { + w1->search_id = rn; + } + desc_bit = last_zero; + + w1_new_device(bus, rn); + } + } + + return 0; +} + +int w1_get_bus(int busnum, struct udevice **busp) +{ + int ret; + + ret = uclass_get_device_by_seq(UCLASS_W1, busnum, busp); + if (ret) { + debug("Cannot find w1 bus %d\n", busnum); + return ret; + } + + return 0; +} + +u8 w1_get_device_family(struct udevice *dev) +{ + struct w1_device *w1 = dev_get_parent_platdata(dev); + + return w1->id & 0xff; +} + +int w1_reset_select(struct udevice *dev) +{ + struct w1_device *w1 = dev_get_parent_platdata(dev); + struct udevice *bus = dev_get_parent(dev); + const struct w1_ops *ops = device_get_ops(bus); + int i; + + if (!ops->reset || !ops->write_byte) + return -ENOSYS; + + ops->reset(bus); + + ops->write_byte(bus, W1_MATCH_ROM); + + for (i = 0; i < sizeof(w1->id); i++) + ops->write_byte(bus, (w1->id >> (i * 8)) & 0xff); + + return 0; +} + +int w1_read_byte(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + const struct w1_ops *ops = device_get_ops(bus); + + if (!ops->read_byte) + return -ENOSYS; + + return ops->read_byte(bus); +} + +int w1_read_buf(struct udevice *dev, u8 *buf, unsigned count) +{ + int i; + + for (i = 0; i < count; i++) { + int ret = w1_read_byte(dev); + if (ret < 0) + return ret; + + buf[i] = ret & 0xff; + } + + return 0; +} + +int w1_write_byte(struct udevice *dev, u8 byte) +{ + struct udevice *bus = dev_get_parent(dev); + const struct w1_ops *ops = device_get_ops(bus); + + if (!ops->write_byte) + return -ENOSYS; + + ops->write_byte(bus, byte); + + return 0; +} + +static int w1_post_probe(struct udevice *bus) +{ + w1_enumerate(bus); + + return 0; +} + +UCLASS_DRIVER(w1) = { + .name = "w1", + .id = UCLASS_W1, + .flags = DM_UC_FLAG_SEQ_ALIAS, + .per_device_auto_alloc_size = sizeof(struct w1_bus), + .per_child_platdata_auto_alloc_size = sizeof(struct w1_device), + .post_probe = w1_post_probe, +}; + +int w1_init(void) +{ + struct udevice *bus; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_W1, &uc); + if (ret) + return ret; + + uclass_foreach_dev(bus, uc) { + ret = device_probe(bus); + if (ret == -ENODEV) { /* No such device. */ + printf("W1 controller not available.\n"); + continue; + } + + if (ret) { /* Other error. */ + printf("W1 controller probe failed, error %d\n", ret); + continue; + } + } + + return 0; +} diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index eb78c4dac485..b88adcbe802f 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -82,6 +82,7 @@ enum uclass_id { UCLASS_VIDEO, /* Video or LCD device */ UCLASS_VIDEO_BRIDGE, /* Video bridge, e.g. DisplayPort to LVDS */ UCLASS_VIDEO_CONSOLE, /* Text console driver for video device */ + UCLASS_W1, /* Dallas 1-Wire bus */
UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/w1.h b/include/w1.h new file mode 100644 index 000000000000..d03509bedbff --- /dev/null +++ b/include/w1.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015 Free Electrons + * Copyright (c) 2015 NextThing Co + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __W1_H +#define __W1_H + +#include <dm.h> + +#define W1_FAMILY_DS2431 0x2d + +/** + * struct w1_driver_entry - Matches a driver to its w1 family + * @driver: Driver to use + * @family: W1 family handled by this driver + */ +struct w1_driver_entry { + struct driver *driver; + u8 family; +}; + +#define U_BOOT_W1_DEVICE(__name, __family) \ + ll_entry_declare(struct w1_driver_entry, __name, w1_driver_entry) = { \ + .driver = llsym(struct driver, __name, driver), \ + .family = __family, \ + } + +struct w1_ops { + u8 (*read_byte)(struct udevice *dev); + bool (*reset)(struct udevice *dev); + u8 (*triplet)(struct udevice *dev, bool bdir); + void (*write_byte)(struct udevice *dev, u8 byte); +}; + +int w1_get_bus(int busnum, struct udevice **busp); +u8 w1_get_device_family(struct udevice *dev); + +int w1_read_buf(struct udevice *dev, u8 *buf, unsigned count); +int w1_read_byte(struct udevice *dev); +int w1_reset_select(struct udevice *dev); +int w1_write_buf(struct udevice *dev, u8 *buf, unsigned count); +int w1_write_byte(struct udevice *dev, u8 byte); + +#endif

Add a bus driver for bitbanging a 1-Wire bus over a GPIO.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- drivers/w1/Kconfig | 6 ++- drivers/w1/Makefile | 1 +- drivers/w1/w1-gpio.c | 160 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 167 insertions(+), 0 deletions(-) create mode 100644 drivers/w1/w1-gpio.c
diff --git a/drivers/w1/Kconfig b/drivers/w1/Kconfig index 0c056b4c06a9..ccc3ae15db86 100644 --- a/drivers/w1/Kconfig +++ b/drivers/w1/Kconfig @@ -12,6 +12,12 @@ config W1
if W1
+config W1_GPIO + bool "Enable 1-Wire GPIO bitbanging" + depends on DM_GPIO + help + Emulate a 1-Wire bus using a GPIO. + endif
endmenu diff --git a/drivers/w1/Makefile b/drivers/w1/Makefile index 26820fa209e1..7fd8697f8419 100644 --- a/drivers/w1/Makefile +++ b/drivers/w1/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_W1) += w1-uclass.o
+obj-$(CONFIG_W1_GPIO) += w1-gpio.o diff --git a/drivers/w1/w1-gpio.c b/drivers/w1/w1-gpio.c new file mode 100644 index 000000000000..091849162533 --- /dev/null +++ b/drivers/w1/w1-gpio.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2015 Free Electrons + * Copyright (c) 2015 NextThing Co + * + * Maxime Ripard maxime.ripard@free-electrons.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <w1.h> + +#include <asm/gpio.h> + + +#define W1_TIMING_A 6 +#define W1_TIMING_B 64 +#define W1_TIMING_C 60 +#define W1_TIMING_D 10 +#define W1_TIMING_E 9 +#define W1_TIMING_F 55 +#define W1_TIMING_G 0 +#define W1_TIMING_H 480 +#define W1_TIMING_I 70 +#define W1_TIMING_J 410 + +struct w1_gpio_pdata { + struct gpio_desc gpio; + u64 search_id; +}; + +static bool w1_gpio_read_bit(struct udevice *dev) +{ + struct w1_gpio_pdata *pdata = dev_get_platdata(dev); + int val; + + dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_OUT); + udelay(W1_TIMING_A); + + dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_IN); + udelay(W1_TIMING_E); + + val = dm_gpio_get_value(&pdata->gpio); + udelay(W1_TIMING_F); + + return val; +} + +static u8 w1_gpio_read_byte(struct udevice *dev) +{ + int i; + u8 ret = 0; + + for (i = 0; i < 8; ++i) + ret |= (w1_gpio_read_bit(dev) ? 1 : 0) << i; + + return ret; +} + +static void w1_gpio_write_bit(struct udevice *dev, bool bit) +{ + struct w1_gpio_pdata *pdata = dev_get_platdata(dev); + + dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_OUT); + + bit ? udelay(W1_TIMING_A) : udelay(W1_TIMING_C); + + dm_gpio_set_value(&pdata->gpio, 1); + + bit ? udelay(W1_TIMING_B) : udelay(W1_TIMING_D); +} + +static void w1_gpio_write_byte(struct udevice *dev, u8 byte) +{ + int i; + + for (i = 0; i < 8; ++i) + w1_gpio_write_bit(dev, (byte >> i) & 0x1); +} + +static bool w1_gpio_reset(struct udevice *dev) +{ + struct w1_gpio_pdata *pdata = dev_get_platdata(dev); + int val; + + dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + udelay(W1_TIMING_G); + + dm_gpio_set_value(&pdata->gpio, 0); + udelay(W1_TIMING_H); + + dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_IN); + udelay(W1_TIMING_I); + + val = dm_gpio_get_value(&pdata->gpio); + udelay(W1_TIMING_J); + + return val; +} + +static u8 w1_gpio_triplet(struct udevice *dev, bool bdir) +{ + u8 id_bit = w1_gpio_read_bit(dev); + u8 comp_bit = w1_gpio_read_bit(dev); + u8 retval; + + if (id_bit && comp_bit) + return 0x03; /* error */ + + if (!id_bit && !comp_bit) { + /* Both bits are valid, take the direction given */ + retval = bdir ? 0x04 : 0; + } else { + /* Only one bit is valid, take that direction */ + bdir = id_bit; + retval = id_bit ? 0x05 : 0x02; + } + + w1_gpio_write_bit(dev, bdir); + return retval; +} + + +static const struct w1_ops w1_gpio_ops = { + .read_byte = w1_gpio_read_byte, + .reset = w1_gpio_reset, + .triplet = w1_gpio_triplet, + .write_byte = w1_gpio_write_byte, +}; + +static int w1_gpio_ofdata_to_platdata(struct udevice *dev) +{ + struct w1_gpio_pdata *pdata = dev_get_platdata(dev); + int ret; + + ret = gpio_request_by_name(dev, "gpios", 0, &pdata->gpio, 0); + if (ret < 0) + goto error; + + return 0; + +error: + printf("Error claiming GPIO %d\n", ret); + return ret; +}; + +static const struct udevice_id w1_gpio_id[] = { + { "w1-gpio", 0 }, + { }, +}; + +U_BOOT_DRIVER(w1_gpio_drv) = { + .id = UCLASS_W1, + .name = "w1_gpio_drv", + .of_match = w1_gpio_id, + .ofdata_to_platdata = w1_gpio_ofdata_to_platdata, + .ops = &w1_gpio_ops, + .platdata_auto_alloc_size = sizeof(struct w1_gpio_pdata), +};

On 8.11.2016 11:19, Maxime Ripard wrote:
Add a bus driver for bitbanging a 1-Wire bus over a GPIO.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
drivers/w1/Kconfig | 6 ++- drivers/w1/Makefile | 1 +- drivers/w1/w1-gpio.c | 160 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 167 insertions(+), 0 deletions(-) create mode 100644 drivers/w1/w1-gpio.c
diff --git a/drivers/w1/Kconfig b/drivers/w1/Kconfig index 0c056b4c06a9..ccc3ae15db86 100644 --- a/drivers/w1/Kconfig +++ b/drivers/w1/Kconfig @@ -12,6 +12,12 @@ config W1
if W1
+config W1_GPIO
- bool "Enable 1-Wire GPIO bitbanging"
- depends on DM_GPIO
- help
Emulate a 1-Wire bus using a GPIO.
endif
endmenu diff --git a/drivers/w1/Makefile b/drivers/w1/Makefile index 26820fa209e1..7fd8697f8419 100644 --- a/drivers/w1/Makefile +++ b/drivers/w1/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_W1) += w1-uclass.o
+obj-$(CONFIG_W1_GPIO) += w1-gpio.o diff --git a/drivers/w1/w1-gpio.c b/drivers/w1/w1-gpio.c new file mode 100644 index 000000000000..091849162533 --- /dev/null +++ b/drivers/w1/w1-gpio.c @@ -0,0 +1,160 @@ +/*
- Copyright (c) 2015 Free Electrons
- Copyright (c) 2015 NextThing Co
- Maxime Ripard maxime.ripard@free-electrons.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <dm.h> +#include <w1.h>
+#include <asm/gpio.h>
+#define W1_TIMING_A 6 +#define W1_TIMING_B 64 +#define W1_TIMING_C 60 +#define W1_TIMING_D 10 +#define W1_TIMING_E 9 +#define W1_TIMING_F 55 +#define W1_TIMING_G 0 +#define W1_TIMING_H 480 +#define W1_TIMING_I 70 +#define W1_TIMING_J 410
+struct w1_gpio_pdata {
- struct gpio_desc gpio;
- u64 search_id;
+};
+static bool w1_gpio_read_bit(struct udevice *dev) +{
- struct w1_gpio_pdata *pdata = dev_get_platdata(dev);
- int val;
- dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_OUT);
- udelay(W1_TIMING_A);
- dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_IN);
- udelay(W1_TIMING_E);
- val = dm_gpio_get_value(&pdata->gpio);
- udelay(W1_TIMING_F);
- return val;
+}
+static u8 w1_gpio_read_byte(struct udevice *dev) +{
- int i;
- u8 ret = 0;
- for (i = 0; i < 8; ++i)
ret |= (w1_gpio_read_bit(dev) ? 1 : 0) << i;
- return ret;
+}
+static void w1_gpio_write_bit(struct udevice *dev, bool bit) +{
- struct w1_gpio_pdata *pdata = dev_get_platdata(dev);
- dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_OUT);
- bit ? udelay(W1_TIMING_A) : udelay(W1_TIMING_C);
- dm_gpio_set_value(&pdata->gpio, 1);
- bit ? udelay(W1_TIMING_B) : udelay(W1_TIMING_D);
+}
+static void w1_gpio_write_byte(struct udevice *dev, u8 byte) +{
- int i;
- for (i = 0; i < 8; ++i)
w1_gpio_write_bit(dev, (byte >> i) & 0x1);
+}
+static bool w1_gpio_reset(struct udevice *dev) +{
- struct w1_gpio_pdata *pdata = dev_get_platdata(dev);
- int val;
- dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
- udelay(W1_TIMING_G);
- dm_gpio_set_value(&pdata->gpio, 0);
- udelay(W1_TIMING_H);
- dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_IN);
- udelay(W1_TIMING_I);
- val = dm_gpio_get_value(&pdata->gpio);
- udelay(W1_TIMING_J);
- return val;
+}
+static u8 w1_gpio_triplet(struct udevice *dev, bool bdir) +{
- u8 id_bit = w1_gpio_read_bit(dev);
- u8 comp_bit = w1_gpio_read_bit(dev);
- u8 retval;
- if (id_bit && comp_bit)
return 0x03; /* error */
- if (!id_bit && !comp_bit) {
/* Both bits are valid, take the direction given */
retval = bdir ? 0x04 : 0;
- } else {
/* Only one bit is valid, take that direction */
bdir = id_bit;
retval = id_bit ? 0x05 : 0x02;
- }
- w1_gpio_write_bit(dev, bdir);
- return retval;
+}
+static const struct w1_ops w1_gpio_ops = {
- .read_byte = w1_gpio_read_byte,
- .reset = w1_gpio_reset,
- .triplet = w1_gpio_triplet,
- .write_byte = w1_gpio_write_byte,
+};
+static int w1_gpio_ofdata_to_platdata(struct udevice *dev) +{
- struct w1_gpio_pdata *pdata = dev_get_platdata(dev);
- int ret;
- ret = gpio_request_by_name(dev, "gpios", 0, &pdata->gpio, 0);
- if (ret < 0)
goto error;
- return 0;
+error:
- printf("Error claiming GPIO %d\n", ret);
- return ret;
+};
+static const struct udevice_id w1_gpio_id[] = {
- { "w1-gpio", 0 },
- { },
+};
+U_BOOT_DRIVER(w1_gpio_drv) = {
- .id = UCLASS_W1,
- .name = "w1_gpio_drv",
- .of_match = w1_gpio_id,
- .ofdata_to_platdata = w1_gpio_ofdata_to_platdata,
- .ops = &w1_gpio_ops,
- .platdata_auto_alloc_size = sizeof(struct w1_gpio_pdata),
+};
Has someone test this on different HW?
As I went through the series will be good to have cmd/w1.c to be able to test that devices.
Thanks, Michal

On Thu, Nov 10, 2016 at 02:35:46PM +0100, Michal Simek wrote:
On 8.11.2016 11:19, Maxime Ripard wrote:
Add a bus driver for bitbanging a 1-Wire bus over a GPIO.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
drivers/w1/Kconfig | 6 ++- drivers/w1/Makefile | 1 +- drivers/w1/w1-gpio.c | 160 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 167 insertions(+), 0 deletions(-) create mode 100644 drivers/w1/w1-gpio.c
diff --git a/drivers/w1/Kconfig b/drivers/w1/Kconfig index 0c056b4c06a9..ccc3ae15db86 100644 --- a/drivers/w1/Kconfig +++ b/drivers/w1/Kconfig @@ -12,6 +12,12 @@ config W1
if W1
+config W1_GPIO
- bool "Enable 1-Wire GPIO bitbanging"
- depends on DM_GPIO
- help
Emulate a 1-Wire bus using a GPIO.
endif
endmenu diff --git a/drivers/w1/Makefile b/drivers/w1/Makefile index 26820fa209e1..7fd8697f8419 100644 --- a/drivers/w1/Makefile +++ b/drivers/w1/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_W1) += w1-uclass.o
+obj-$(CONFIG_W1_GPIO) += w1-gpio.o diff --git a/drivers/w1/w1-gpio.c b/drivers/w1/w1-gpio.c new file mode 100644 index 000000000000..091849162533 --- /dev/null +++ b/drivers/w1/w1-gpio.c @@ -0,0 +1,160 @@ +/*
- Copyright (c) 2015 Free Electrons
- Copyright (c) 2015 NextThing Co
- Maxime Ripard maxime.ripard@free-electrons.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <dm.h> +#include <w1.h>
+#include <asm/gpio.h>
+#define W1_TIMING_A 6 +#define W1_TIMING_B 64 +#define W1_TIMING_C 60 +#define W1_TIMING_D 10 +#define W1_TIMING_E 9 +#define W1_TIMING_F 55 +#define W1_TIMING_G 0 +#define W1_TIMING_H 480 +#define W1_TIMING_I 70 +#define W1_TIMING_J 410
+struct w1_gpio_pdata {
- struct gpio_desc gpio;
- u64 search_id;
+};
+static bool w1_gpio_read_bit(struct udevice *dev) +{
- struct w1_gpio_pdata *pdata = dev_get_platdata(dev);
- int val;
- dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_OUT);
- udelay(W1_TIMING_A);
- dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_IN);
- udelay(W1_TIMING_E);
- val = dm_gpio_get_value(&pdata->gpio);
- udelay(W1_TIMING_F);
- return val;
+}
+static u8 w1_gpio_read_byte(struct udevice *dev) +{
- int i;
- u8 ret = 0;
- for (i = 0; i < 8; ++i)
ret |= (w1_gpio_read_bit(dev) ? 1 : 0) << i;
- return ret;
+}
+static void w1_gpio_write_bit(struct udevice *dev, bool bit) +{
- struct w1_gpio_pdata *pdata = dev_get_platdata(dev);
- dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_OUT);
- bit ? udelay(W1_TIMING_A) : udelay(W1_TIMING_C);
- dm_gpio_set_value(&pdata->gpio, 1);
- bit ? udelay(W1_TIMING_B) : udelay(W1_TIMING_D);
+}
+static void w1_gpio_write_byte(struct udevice *dev, u8 byte) +{
- int i;
- for (i = 0; i < 8; ++i)
w1_gpio_write_bit(dev, (byte >> i) & 0x1);
+}
+static bool w1_gpio_reset(struct udevice *dev) +{
- struct w1_gpio_pdata *pdata = dev_get_platdata(dev);
- int val;
- dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
- udelay(W1_TIMING_G);
- dm_gpio_set_value(&pdata->gpio, 0);
- udelay(W1_TIMING_H);
- dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_IN);
- udelay(W1_TIMING_I);
- val = dm_gpio_get_value(&pdata->gpio);
- udelay(W1_TIMING_J);
- return val;
+}
+static u8 w1_gpio_triplet(struct udevice *dev, bool bdir) +{
- u8 id_bit = w1_gpio_read_bit(dev);
- u8 comp_bit = w1_gpio_read_bit(dev);
- u8 retval;
- if (id_bit && comp_bit)
return 0x03; /* error */
- if (!id_bit && !comp_bit) {
/* Both bits are valid, take the direction given */
retval = bdir ? 0x04 : 0;
- } else {
/* Only one bit is valid, take that direction */
bdir = id_bit;
retval = id_bit ? 0x05 : 0x02;
- }
- w1_gpio_write_bit(dev, bdir);
- return retval;
+}
+static const struct w1_ops w1_gpio_ops = {
- .read_byte = w1_gpio_read_byte,
- .reset = w1_gpio_reset,
- .triplet = w1_gpio_triplet,
- .write_byte = w1_gpio_write_byte,
+};
+static int w1_gpio_ofdata_to_platdata(struct udevice *dev) +{
- struct w1_gpio_pdata *pdata = dev_get_platdata(dev);
- int ret;
- ret = gpio_request_by_name(dev, "gpios", 0, &pdata->gpio, 0);
- if (ret < 0)
goto error;
- return 0;
+error:
- printf("Error claiming GPIO %d\n", ret);
- return ret;
+};
+static const struct udevice_id w1_gpio_id[] = {
- { "w1-gpio", 0 },
- { },
+};
+U_BOOT_DRIVER(w1_gpio_drv) = {
- .id = UCLASS_W1,
- .name = "w1_gpio_drv",
- .of_match = w1_gpio_id,
- .ofdata_to_platdata = w1_gpio_ofdata_to_platdata,
- .ops = &w1_gpio_ops,
- .platdata_auto_alloc_size = sizeof(struct w1_gpio_pdata),
+};
Has someone test this on different HW?
I didn't, but it looks pretty generic.
The only thing you have to make sure is that you can respect the rather tight timing constraints of the bus. That rules out a GPIO on an I2C expander for example.
As I went through the series will be good to have cmd/w1.c to be able to test that devices.
Ack.
Maxime

We might want to access data stored onto EEPROMs. Create a framework to provide a consistent API.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- drivers/Kconfig | 2 +- drivers/Makefile | 1 +- drivers/eeprom/Kconfig | 17 ++++++++++- drivers/eeprom/Makefile | 2 +- drivers/eeprom/eeprom-uclass.c | 57 +++++++++++++++++++++++++++++++++++- include/dm/uclass-id.h | 1 +- include/eeprom.h | 21 +++++++++++++- 7 files changed, 101 insertions(+), 0 deletions(-) create mode 100644 drivers/eeprom/Kconfig create mode 100644 drivers/eeprom/Makefile create mode 100644 drivers/eeprom/eeprom-uclass.c create mode 100644 include/eeprom.h
diff --git a/drivers/Kconfig b/drivers/Kconfig index 74194b0d6f7f..e518752eae1a 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -20,6 +20,8 @@ source "drivers/dfu/Kconfig"
source "drivers/dma/Kconfig"
+source "drivers/eeprom/Kconfig" + source "drivers/fpga/Kconfig"
source "drivers/gpio/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 543c43bb0d23..c8f285f66bb3 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -64,6 +64,7 @@ obj-y += block/ obj-$(CONFIG_BOOTCOUNT_LIMIT) += bootcount/ obj-$(CONFIG_CPU) += cpu/ obj-y += crypto/ +obj-y += eeprom/ obj-$(CONFIG_FPGA) += fpga/ obj-y += hwmon/ obj-y += misc/ diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig new file mode 100644 index 000000000000..8dc597a8d894 --- /dev/null +++ b/drivers/eeprom/Kconfig @@ -0,0 +1,17 @@ +# +# EEPROM subsystem configuration +# + +menu "EEPROM support" + +config EEPROM + bool "Enable EEPROM support" + depends on DM + help + Support for the EEPROMs + +if EEPROM + +endif + +endmenu diff --git a/drivers/eeprom/Makefile b/drivers/eeprom/Makefile new file mode 100644 index 000000000000..147dba5ec4b8 --- /dev/null +++ b/drivers/eeprom/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_EEPROM) += eeprom-uclass.o + diff --git a/drivers/eeprom/eeprom-uclass.c b/drivers/eeprom/eeprom-uclass.c new file mode 100644 index 000000000000..020b0087d22c --- /dev/null +++ b/drivers/eeprom/eeprom-uclass.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2015 Free Electrons + * Copyright (c) 2015 NextThing Co. + * + * Maxime Ripard maxime.ripard@free-electrons.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <eeprom.h> + +#include <dm/device-internal.h> + +int eeprom_read_buf(struct udevice *dev, unsigned offset, + u8 *buf, unsigned count) +{ + const struct eeprom_ops *ops = device_get_ops(dev); + + if (!ops->read_buf) + return -ENOSYS; + + return ops->read_buf(dev, offset, buf, count); +} + + +UCLASS_DRIVER(eeprom) = { + .name = "eeprom", + .id = UCLASS_EEPROM, +}; + +int eeprom_dm_init(void) +{ + struct udevice *bus; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_EEPROM, &uc); + if (ret) + return ret; + + uclass_foreach_dev(bus, uc) { + ret = device_probe(bus); + if (ret == -ENODEV) { /* No such device. */ + printf("EEPROM not available.\n"); + continue; + } + + if (ret) { /* Other error. */ + printf("EEPROM probe failed, error %d\n", ret); + continue; + } + } + + return 0; +} diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index b88adcbe802f..909a32404259 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -83,6 +83,7 @@ enum uclass_id { UCLASS_VIDEO_BRIDGE, /* Video bridge, e.g. DisplayPort to LVDS */ UCLASS_VIDEO_CONSOLE, /* Text console driver for video device */ UCLASS_W1, /* Dallas 1-Wire bus */ + UCLASS_EEPROM, /* EEPROM */
UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/eeprom.h b/include/eeprom.h new file mode 100644 index 000000000000..648c8606c35a --- /dev/null +++ b/include/eeprom.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2015 Free Electrons + * Copyright (c) 2015 NextThing Co + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __EEPROM_H +#define __EEPROM_H + +#include <dm.h> + +struct eeprom_ops { + int (*read_buf)(struct udevice *dev, unsigned offset, + u8 *buf, unsigned count); +}; + +int eeprom_read_buf(struct udevice *dev, unsigned offset, + u8 *buf, unsigned count); + +#endif

Hi Maxime,
On 8 November 2016 at 03:19, Maxime Ripard maxime.ripard@free-electrons.com wrote:
We might want to access data stored onto EEPROMs. Create a framework to provide a consistent API.
We have UCLASS_I2C_EEPROM. Can we unify these? If not, please add a sandbox driver and test.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
drivers/Kconfig | 2 +- drivers/Makefile | 1 +- drivers/eeprom/Kconfig | 17 ++++++++++- drivers/eeprom/Makefile | 2 +- drivers/eeprom/eeprom-uclass.c | 57 +++++++++++++++++++++++++++++++++++- include/dm/uclass-id.h | 1 +- include/eeprom.h | 21 +++++++++++++- 7 files changed, 101 insertions(+), 0 deletions(-) create mode 100644 drivers/eeprom/Kconfig create mode 100644 drivers/eeprom/Makefile create mode 100644 drivers/eeprom/eeprom-uclass.c create mode 100644 include/eeprom.h
diff --git a/drivers/Kconfig b/drivers/Kconfig index 74194b0d6f7f..e518752eae1a 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -20,6 +20,8 @@ source "drivers/dfu/Kconfig"
source "drivers/dma/Kconfig"
+source "drivers/eeprom/Kconfig"
source "drivers/fpga/Kconfig"
source "drivers/gpio/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 543c43bb0d23..c8f285f66bb3 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -64,6 +64,7 @@ obj-y += block/ obj-$(CONFIG_BOOTCOUNT_LIMIT) += bootcount/ obj-$(CONFIG_CPU) += cpu/ obj-y += crypto/ +obj-y += eeprom/ obj-$(CONFIG_FPGA) += fpga/ obj-y += hwmon/ obj-y += misc/ diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig new file mode 100644 index 000000000000..8dc597a8d894 --- /dev/null +++ b/drivers/eeprom/Kconfig @@ -0,0 +1,17 @@ +# +# EEPROM subsystem configuration +#
+menu "EEPROM support"
+config EEPROM
bool "Enable EEPROM support"
depends on DM
help
Support for the EEPROMs
Please expand this a bit.
+if EEPROM
+endif
+endmenu diff --git a/drivers/eeprom/Makefile b/drivers/eeprom/Makefile new file mode 100644 index 000000000000..147dba5ec4b8 --- /dev/null +++ b/drivers/eeprom/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_EEPROM) += eeprom-uclass.o
diff --git a/drivers/eeprom/eeprom-uclass.c b/drivers/eeprom/eeprom-uclass.c new file mode 100644 index 000000000000..020b0087d22c --- /dev/null +++ b/drivers/eeprom/eeprom-uclass.c @@ -0,0 +1,57 @@ +/*
- Copyright (c) 2015 Free Electrons
- Copyright (c) 2015 NextThing Co.
- Maxime Ripard maxime.ripard@free-electrons.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <dm.h> +#include <eeprom.h>
+#include <dm/device-internal.h>
+int eeprom_read_buf(struct udevice *dev, unsigned offset,
u8 *buf, unsigned count)
+{
const struct eeprom_ops *ops = device_get_ops(dev);
if (!ops->read_buf)
return -ENOSYS;
return ops->read_buf(dev, offset, buf, count);
+}
+UCLASS_DRIVER(eeprom) = {
.name = "eeprom",
.id = UCLASS_EEPROM,
+};
+int eeprom_dm_init(void) +{
struct udevice *bus;
struct uclass *uc;
int ret;
ret = uclass_get(UCLASS_EEPROM, &uc);
if (ret)
return ret;
uclass_foreach_dev(bus, uc) {
ret = device_probe(bus);
if (ret == -ENODEV) { /* No such device. */
printf("EEPROM not available.\n");
debug()?
continue;
}
if (ret) { /* Other error. */
printf("EEPROM probe failed, error %d\n", ret);
debug()?
continue;
}
}
return 0;
+} diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index b88adcbe802f..909a32404259 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -83,6 +83,7 @@ enum uclass_id { UCLASS_VIDEO_BRIDGE, /* Video bridge, e.g. DisplayPort to LVDS */ UCLASS_VIDEO_CONSOLE, /* Text console driver for video device */ UCLASS_W1, /* Dallas 1-Wire bus */
UCLASS_EEPROM, /* EEPROM */
Please put this in alpha order.
UCLASS_COUNT, UCLASS_INVALID = -1,
diff --git a/include/eeprom.h b/include/eeprom.h new file mode 100644 index 000000000000..648c8606c35a --- /dev/null +++ b/include/eeprom.h @@ -0,0 +1,21 @@ +/*
- Copyright (c) 2015 Free Electrons
- Copyright (c) 2015 NextThing Co
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __EEPROM_H +#define __EEPROM_H
+#include <dm.h>
You can use a 'struct udevice *' forward decl instead.
+struct eeprom_ops {
Please add comments
int (*read_buf)(struct udevice *dev, unsigned offset,
u8 *buf, unsigned count);
+};
+int eeprom_read_buf(struct udevice *dev, unsigned offset,
u8 *buf, unsigned count);
+#endif
git-series 0.8.11
Regards, Simon

Hi Maxime,
On Fri, Nov 11, 2016 at 8:17 AM, Simon Glass sjg@chromium.org wrote:
Hi Maxime,
On 8 November 2016 at 03:19, Maxime Ripard maxime.ripard@free-electrons.com wrote:
We might want to access data stored onto EEPROMs. Create a framework to provide a consistent API.
We have UCLASS_I2C_EEPROM. Can we unify these? If not, please add a sandbox driver and test.
I've been working on something very similar (the API looks the same obviously, since the ops are pretty trivial, modulo function names) In my opinion it should be working as follows:
UCLASS_EEPROM ... I2C_EEPROM ....SPI_EEPROM (AT25)
I also have some code to make the CMD_EEPROM stuff work, I'll submit a follow up patch, once we agreed on how the UCLASS_EEPROM looks like.
Cheers,
Moritz

Hi Moritz,
On Fri, Nov 11, 2016 at 11:13:39AM -0800, Moritz Fischer wrote:
Hi Maxime,
On Fri, Nov 11, 2016 at 8:17 AM, Simon Glass sjg@chromium.org wrote:
Hi Maxime,
On 8 November 2016 at 03:19, Maxime Ripard maxime.ripard@free-electrons.com wrote:
We might want to access data stored onto EEPROMs. Create a framework to provide a consistent API.
We have UCLASS_I2C_EEPROM. Can we unify these? If not, please add a sandbox driver and test.
I've been working on something very similar (the API looks the same obviously, since the ops are pretty trivial, modulo function names) In my opinion it should be working as follows:
UCLASS_EEPROM ... I2C_EEPROM ....SPI_EEPROM (AT25)
I agree. Different board variations might be using different buses to store the same data, and it's all EEPROM anyway.
Maxime

Add a driver for the Maxim DS2431 1-Wire EEPROM
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- drivers/eeprom/Kconfig | 6 ++++++ drivers/eeprom/Makefile | 1 + drivers/eeprom/ds2431.c | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 0 deletions(-) create mode 100644 drivers/eeprom/ds2431.c
diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig index 8dc597a8d894..98bbd67ba579 100644 --- a/drivers/eeprom/Kconfig +++ b/drivers/eeprom/Kconfig @@ -12,6 +12,12 @@ config EEPROM
if EEPROM
+config EEPROM_DS2431 + bool "Enable Maxim DS2431 EEPROM support" + depends on W1 + help + Maxim DS2431 1-Wire EEPROM support + endif
endmenu diff --git a/drivers/eeprom/Makefile b/drivers/eeprom/Makefile index 147dba5ec4b8..93dae0bf5d6d 100644 --- a/drivers/eeprom/Makefile +++ b/drivers/eeprom/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_EEPROM) += eeprom-uclass.o
+obj-$(CONFIG_EEPROM_DS2431) += ds2431.o diff --git a/drivers/eeprom/ds2431.c b/drivers/eeprom/ds2431.c new file mode 100644 index 000000000000..84c1a126c339 --- /dev/null +++ b/drivers/eeprom/ds2431.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015 Free Electrons + * Copyright (c) 2015 NextThing Co + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/err.h> +#include <dm.h> +#include <eeprom.h> +#include <w1.h> + +#define W1_F2D_READ_EEPROM 0xf0 + +static int ds2431_read_buf(struct udevice *dev, unsigned offset, + u8 *buf, unsigned count) +{ + w1_reset_select(dev); + + w1_write_byte(dev, W1_F2D_READ_EEPROM); + w1_write_byte(dev, offset & 0xff); + w1_write_byte(dev, offset >> 8); + + return w1_read_buf(dev, buf, count); +} + +static const struct eeprom_ops ds2431_ops = { + .read_buf = ds2431_read_buf, +}; + +U_BOOT_DRIVER(ds2431) = { + .name = "ds2431", + .id = UCLASS_EEPROM, + .ops = &ds2431_ops, +}; + +U_BOOT_W1_DEVICE(ds2431, W1_FAMILY_DS2431);

Hi Maxime,
On Tue, Nov 8, 2016 at 2:19 AM, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Add a driver for the Maxim DS2431 1-Wire EEPROM
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
drivers/eeprom/Kconfig | 6 ++++++ drivers/eeprom/Makefile | 1 + drivers/eeprom/ds2431.c | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 0 deletions(-) create mode 100644 drivers/eeprom/ds2431.c
diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig index 8dc597a8d894..98bbd67ba579 100644 --- a/drivers/eeprom/Kconfig +++ b/drivers/eeprom/Kconfig @@ -12,6 +12,12 @@ config EEPROM
if EEPROM
+config EEPROM_DS2431
bool "Enable Maxim DS2431 EEPROM support"
depends on W1
help
Maxim DS2431 1-Wire EEPROM support
endif
endmenu diff --git a/drivers/eeprom/Makefile b/drivers/eeprom/Makefile index 147dba5ec4b8..93dae0bf5d6d 100644 --- a/drivers/eeprom/Makefile +++ b/drivers/eeprom/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_EEPROM) += eeprom-uclass.o
+obj-$(CONFIG_EEPROM_DS2431) += ds2431.o diff --git a/drivers/eeprom/ds2431.c b/drivers/eeprom/ds2431.c new file mode 100644 index 000000000000..84c1a126c339 --- /dev/null +++ b/drivers/eeprom/ds2431.c @@ -0,0 +1,38 @@ +/*
- Copyright (c) 2015 Free Electrons
- Copyright (c) 2015 NextThing Co
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <linux/err.h> +#include <dm.h> +#include <eeprom.h> +#include <w1.h>
+#define W1_F2D_READ_EEPROM 0xf0
+static int ds2431_read_buf(struct udevice *dev, unsigned offset,
u8 *buf, unsigned count)
+{
w1_reset_select(dev);
w1_write_byte(dev, W1_F2D_READ_EEPROM);
w1_write_byte(dev, offset & 0xff);
w1_write_byte(dev, offset >> 8);
return w1_read_buf(dev, buf, count);
+}
+static const struct eeprom_ops ds2431_ops = {
.read_buf = ds2431_read_buf,
+};
+U_BOOT_DRIVER(ds2431) = {
.name = "ds2431",
.id = UCLASS_EEPROM,
.ops = &ds2431_ops,
Do you want to add a .flags = DM_UC_FLAG_SEQ_ALIAS here?
+};
+U_BOOT_W1_DEVICE(ds2431, W1_FAMILY_DS2431);
git-series 0.8.11 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot
Cheers,
Moritz

Hi,
On Fri, Nov 11, 2016 at 11:16:39AM -0800, Moritz Fischer wrote:
+U_BOOT_DRIVER(ds2431) = {
.name = "ds2431",
.id = UCLASS_EEPROM,
.ops = &ds2431_ops,
Do you want to add a .flags = DM_UC_FLAG_SEQ_ALIAS here?
I don't know. I was kind of wondering why U-Boot relies on aliases so much, especially when the Linux DT maintainers are saying that aliases should be avoided entirely, and we'll won't be able to upstream those changes.
Thanks! Maxime

On Mon, Nov 14, 2016 at 02:42:59PM +0100, Maxime Ripard wrote:
Hi,
On Fri, Nov 11, 2016 at 11:16:39AM -0800, Moritz Fischer wrote:
+U_BOOT_DRIVER(ds2431) = {
.name = "ds2431",
.id = UCLASS_EEPROM,
.ops = &ds2431_ops,
Do you want to add a .flags = DM_UC_FLAG_SEQ_ALIAS here?
I don't know. I was kind of wondering why U-Boot relies on aliases so much, especially when the Linux DT maintainers are saying that aliases should be avoided entirely, and we'll won't be able to upstream those changes.
Bah. Do you have a pointer to some discussion about this handy? Thanks!

On Mon, Nov 14, 2016 at 10:14:57AM -0500, Tom Rini wrote:
On Mon, Nov 14, 2016 at 02:42:59PM +0100, Maxime Ripard wrote:
Hi,
On Fri, Nov 11, 2016 at 11:16:39AM -0800, Moritz Fischer wrote:
+U_BOOT_DRIVER(ds2431) = {
.name = "ds2431",
.id = UCLASS_EEPROM,
.ops = &ds2431_ops,
Do you want to add a .flags = DM_UC_FLAG_SEQ_ALIAS here?
I don't know. I was kind of wondering why U-Boot relies on aliases so much, especially when the Linux DT maintainers are saying that aliases should be avoided entirely, and we'll won't be able to upstream those changes.
Bah. Do you have a pointer to some discussion about this handy?
Rob said this multiple times, but here is an example: http://lkml.iu.edu/hypermail/linux/kernel/1609.1/00653.html
Maxime

Hi Maxime,
On 14 November 2016 at 13:12, Maxime Ripard maxime.ripard@free-electrons.com wrote:
On Mon, Nov 14, 2016 at 10:14:57AM -0500, Tom Rini wrote:
On Mon, Nov 14, 2016 at 02:42:59PM +0100, Maxime Ripard wrote:
Hi,
On Fri, Nov 11, 2016 at 11:16:39AM -0800, Moritz Fischer wrote:
+U_BOOT_DRIVER(ds2431) = {
.name = "ds2431",
.id = UCLASS_EEPROM,
.ops = &ds2431_ops,
Do you want to add a .flags = DM_UC_FLAG_SEQ_ALIAS here?
I don't know. I was kind of wondering why U-Boot relies on aliases so much, especially when the Linux DT maintainers are saying that aliases should be avoided entirely, and we'll won't be able to upstream those changes.
Bah. Do you have a pointer to some discussion about this handy?
Rob said this multiple times, but here is an example: http://lkml.iu.edu/hypermail/linux/kernel/1609.1/00653.html
Well that's not really an explanation!
- Simon

On Mon, Nov 14, 2016 at 01:44:42PM -0700, Simon Glass wrote:
Hi Maxime,
On 14 November 2016 at 13:12, Maxime Ripard maxime.ripard@free-electrons.com wrote:
On Mon, Nov 14, 2016 at 10:14:57AM -0500, Tom Rini wrote:
On Mon, Nov 14, 2016 at 02:42:59PM +0100, Maxime Ripard wrote:
Hi,
On Fri, Nov 11, 2016 at 11:16:39AM -0800, Moritz Fischer wrote:
+U_BOOT_DRIVER(ds2431) = {
.name = "ds2431",
.id = UCLASS_EEPROM,
.ops = &ds2431_ops,
Do you want to add a .flags = DM_UC_FLAG_SEQ_ALIAS here?
I don't know. I was kind of wondering why U-Boot relies on aliases so much, especially when the Linux DT maintainers are saying that aliases should be avoided entirely, and we'll won't be able to upstream those changes.
Bah. Do you have a pointer to some discussion about this handy?
Rob said this multiple times, but here is an example: http://lkml.iu.edu/hypermail/linux/kernel/1609.1/00653.html
Well that's not really an explanation!
Don't shoot the messenger :)
I think I found some other discussion where they discuss it at length: https://patchwork.kernel.org/patch/9133903/
The interesting part is when Rob Herring is involved.
Maxime

Hi Maxime,
On 14 November 2016 at 14:09, Maxime Ripard maxime.ripard@free-electrons.com wrote:
On Mon, Nov 14, 2016 at 01:44:42PM -0700, Simon Glass wrote:
Hi Maxime,
On 14 November 2016 at 13:12, Maxime Ripard maxime.ripard@free-electrons.com wrote:
On Mon, Nov 14, 2016 at 10:14:57AM -0500, Tom Rini wrote:
On Mon, Nov 14, 2016 at 02:42:59PM +0100, Maxime Ripard wrote:
Hi,
On Fri, Nov 11, 2016 at 11:16:39AM -0800, Moritz Fischer wrote:
> +U_BOOT_DRIVER(ds2431) = { > + .name = "ds2431", > + .id = UCLASS_EEPROM, > + .ops = &ds2431_ops,
Do you want to add a .flags = DM_UC_FLAG_SEQ_ALIAS here?
I don't know. I was kind of wondering why U-Boot relies on aliases so much, especially when the Linux DT maintainers are saying that aliases should be avoided entirely, and we'll won't be able to upstream those changes.
Bah. Do you have a pointer to some discussion about this handy?
Rob said this multiple times, but here is an example: http://lkml.iu.edu/hypermail/linux/kernel/1609.1/00653.html
Well that's not really an explanation!
Don't shoot the messenger :)
I think I found some other discussion where they discuss it at length: https://patchwork.kernel.org/patch/9133903/
The interesting part is when Rob Herring is involved.
Oh dear. That sounds like a change to me. I'm going to leave this one to Tom :-)
Regards, Simon

Hi Maxime,
On 14 November 2016 at 06:42, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Hi,
On Fri, Nov 11, 2016 at 11:16:39AM -0800, Moritz Fischer wrote:
+U_BOOT_DRIVER(ds2431) = {
.name = "ds2431",
.id = UCLASS_EEPROM,
.ops = &ds2431_ops,
Do you want to add a .flags = DM_UC_FLAG_SEQ_ALIAS here?
I don't know. I was kind of wondering why U-Boot relies on aliases so much, especially when the Linux DT maintainers are saying that aliases should be avoided entirely, and we'll won't be able to upstream those changes.
U-Boot uses numbering on the command line for lots of device types. E.g. the i2c bus number in the 'i2c' command. The aliases set the numbering.
We should add support for moving away from numbering and using names, at least as an option. I have not looked at that yet. Probably we should consider changing command-line parsing to be handled in a common library, with each command receiving a 'parsed' list of args and options. I have not looked at that either.
Regards, Simon

Some boards might need to some additional setup right before initialising the video console.
Add some hook to allow that.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- drivers/video/cfb_console.c | 9 +++++++++ 1 file changed, 9 insertions(+), 0 deletions(-)
diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index c0b1b8dc17e2..724ae16c5a9b 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -2119,6 +2119,11 @@ __weak int board_video_skip(void) return 0; }
+__weak int board_video_pre_init(void) +{ + return 0; +} + int drv_video_init(void) { struct stdio_dev console_dev; @@ -2129,6 +2134,10 @@ int drv_video_init(void) if (board_video_skip()) return 0;
+ /* Allow the board to setup a few things */ + if (board_video_pre_init()) + return 0; + /* Init video chip - returns with framebuffer cleared */ if (cfg_video_init() == -1) return 0;

Hi Maxime,
On 8 November 2016 at 03:19, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Some boards might need to some additional setup right before initialising the video console.
Add some hook to allow that.
Instead of this, can you use driver model (UCLASS_VIDEO)?
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
drivers/video/cfb_console.c | 9 +++++++++ 1 file changed, 9 insertions(+), 0 deletions(-)
diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index c0b1b8dc17e2..724ae16c5a9b 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -2119,6 +2119,11 @@ __weak int board_video_skip(void) return 0; }
+__weak int board_video_pre_init(void) +{
return 0;
+}
int drv_video_init(void) { struct stdio_dev console_dev; @@ -2129,6 +2134,10 @@ int drv_video_init(void) if (board_video_skip()) return 0;
/* Allow the board to setup a few things */
if (board_video_pre_init())
return 0;
/* Init video chip - returns with framebuffer cleared */ if (cfg_video_init() == -1) return 0;
-- git-series 0.8.11
Regards, SImon

Hi Simon,
On Fri, Nov 11, 2016 at 09:17:28AM -0700, Simon Glass wrote:
Hi Maxime,
On 8 November 2016 at 03:19, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Some boards might need to some additional setup right before initialising the video console.
Add some hook to allow that.
Instead of this, can you use driver model (UCLASS_VIDEO)?
I don't really know the device model that well, hence 'm not really sure how would that help. Can a board register a hook to be called before a driver is probed?
Thanks! Maxime

Hi Maxime,
On 14 November 2016 at 13:24, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Hi Simon,
On Fri, Nov 11, 2016 at 09:17:28AM -0700, Simon Glass wrote:
Hi Maxime,
On 8 November 2016 at 03:19, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Some boards might need to some additional setup right before initialising the video console.
Add some hook to allow that.
Instead of this, can you use driver model (UCLASS_VIDEO)?
I don't really know the device model that well, hence 'm not really sure how would that help. Can a board register a hook to be called before a driver is probed?
My suggest would be that the driver can do whatever is required. What is the board-specific code actually wanting to do?
Regards, Simon

Hi Simon,
On Mon, Nov 14, 2016 at 01:44:45PM -0700, Simon Glass wrote:
Hi Maxime,
On 14 November 2016 at 13:24, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Hi Simon,
On Fri, Nov 11, 2016 at 09:17:28AM -0700, Simon Glass wrote:
Hi Maxime,
On 8 November 2016 at 03:19, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Some boards might need to some additional setup right before initialising the video console.
Add some hook to allow that.
Instead of this, can you use driver model (UCLASS_VIDEO)?
I don't really know the device model that well, hence 'm not really sure how would that help. Can a board register a hook to be called before a driver is probed?
My suggest would be that the driver can do whatever is required. What is the board-specific code actually wanting to do?
We need to adjust the video-mode environment variable that is going to be used in the driver's initialisation. We're doing that by identifying which daughter board is attached, and then setting that value according to the display output we want to use.
So we need some hook that would run between the environment initialisation and the video driver's init.
Maxime

Hi Maxime,
On 22 November 2016 at 05:41, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Hi Simon,
On Mon, Nov 14, 2016 at 01:44:45PM -0700, Simon Glass wrote:
Hi Maxime,
On 14 November 2016 at 13:24, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Hi Simon,
On Fri, Nov 11, 2016 at 09:17:28AM -0700, Simon Glass wrote:
Hi Maxime,
On 8 November 2016 at 03:19, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Some boards might need to some additional setup right before initialising the video console.
Add some hook to allow that.
Instead of this, can you use driver model (UCLASS_VIDEO)?
I don't really know the device model that well, hence 'm not really sure how would that help. Can a board register a hook to be called before a driver is probed?
My suggest would be that the driver can do whatever is required. What is the board-specific code actually wanting to do?
We need to adjust the video-mode environment variable that is going to be used in the driver's initialisation. We're doing that by identifying which daughter board is attached, and then setting that value according to the display output we want to use.
So we need some hook that would run between the environment initialisation and the video driver's init.
As an example of how you might pass platform data to the video driver, see board_init() in gurnard.c.
Regards, Simon

On Wed, Nov 23, 2016 at 07:21:07PM -0700, Simon Glass wrote:
Hi Maxime,
On 22 November 2016 at 05:41, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Hi Simon,
On Mon, Nov 14, 2016 at 01:44:45PM -0700, Simon Glass wrote:
Hi Maxime,
On 14 November 2016 at 13:24, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Hi Simon,
On Fri, Nov 11, 2016 at 09:17:28AM -0700, Simon Glass wrote:
Hi Maxime,
On 8 November 2016 at 03:19, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Some boards might need to some additional setup right before initialising the video console.
Add some hook to allow that.
Instead of this, can you use driver model (UCLASS_VIDEO)?
I don't really know the device model that well, hence 'm not really sure how would that help. Can a board register a hook to be called before a driver is probed?
My suggest would be that the driver can do whatever is required. What is the board-specific code actually wanting to do?
We need to adjust the video-mode environment variable that is going to be used in the driver's initialisation. We're doing that by identifying which daughter board is attached, and then setting that value according to the display output we want to use.
So we need some hook that would run between the environment initialisation and the video driver's init.
As an example of how you might pass platform data to the video driver, see board_init() in gurnard.c.
That's very useful, thanks!
Can you also postpone the probe of one driver, or at least defer a driver's probe until some other is done?
The way we identify the daughter boards also rely on some driver in the device model (a 1-Wire EEPROM, connected on a GPIO, both of them also being part of the device model), so we need to defer the video driver probe until both of them have been probed and accessed.
Thanks! Maxime

Hi Maxime,
On 6 December 2016 at 12:39, Maxime Ripard maxime.ripard@free-electrons.com wrote:
On Wed, Nov 23, 2016 at 07:21:07PM -0700, Simon Glass wrote:
Hi Maxime,
On 22 November 2016 at 05:41, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Hi Simon,
On Mon, Nov 14, 2016 at 01:44:45PM -0700, Simon Glass wrote:
Hi Maxime,
On 14 November 2016 at 13:24, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Hi Simon,
On Fri, Nov 11, 2016 at 09:17:28AM -0700, Simon Glass wrote:
Hi Maxime,
On 8 November 2016 at 03:19, Maxime Ripard maxime.ripard@free-electrons.com wrote: > Some boards might need to some additional setup right before initialising > the video console. > > Add some hook to allow that.
Instead of this, can you use driver model (UCLASS_VIDEO)?
I don't really know the device model that well, hence 'm not really sure how would that help. Can a board register a hook to be called before a driver is probed?
My suggest would be that the driver can do whatever is required. What is the board-specific code actually wanting to do?
We need to adjust the video-mode environment variable that is going to be used in the driver's initialisation. We're doing that by identifying which daughter board is attached, and then setting that value according to the display output we want to use.
So we need some hook that would run between the environment initialisation and the video driver's init.
As an example of how you might pass platform data to the video driver, see board_init() in gurnard.c.
That's very useful, thanks!
Can you also postpone the probe of one driver, or at least defer a driver's probe until some other is done?
Well a device is not probed until it is used (or at least that is how it is supposed to work).
So perhaps you can probe the device in the driver that needs it? For example, if video probe needs GPIOs you can probe the GPIOs automatically (e.g. if you use something like gpio_request_by_name() in your video probe()).
The way we identify the daughter boards also rely on some driver in the device model (a 1-Wire EEPROM, connected on a GPIO, both of them also being part of the device model), so we need to defer the video driver probe until both of them have been probed and accessed.
Regards, Simon

The NextThing CHIP comes with expansion boards called DIPs.
These DIPs comes with a 1-Wire EEPROM used to enumerate and identify the DIPs currently attached.
Once we know what is connected, we need to do various things, such a load and apply an overlay if relevant, adjust the U-boot environment and the kernel command line, etc.
Add support for this.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- board/sunxi/Kconfig | 9 ++- board/sunxi/Makefile | 1 +- board/sunxi/board.c | 6 +- board/sunxi/dip.c | 227 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 243 insertions(+), 0 deletions(-) create mode 100644 board/sunxi/dip.c
diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index e1d4ab148f08..1e70f3a2ed97 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -667,4 +667,13 @@ config SPL_STACK_R_ADDR default 0x4fe00000 if MACH_SUN4I || MACH_SUN5I || MACH_SUN6I || MACH_SUN7I || MACH_SUN8I || MACH_SUN50I default 0x2fe00000 if MACH_SUN9I
+config CHIP_DIP + bool "Enable NextThing's CHIP DIP support" + depends on W1_GPIO + depends on EEPROM_DS2431 + ---help--- + The NextThing's CHIP allows to plug expansion boards. These boards can + be enumerated at runtime through a 1-Wire bus, each board having an + EEPROM connected to it, holding data to identify the board holding it. + endif diff --git a/board/sunxi/Makefile b/board/sunxi/Makefile index 43766e0ef482..0f0ac90ef185 100644 --- a/board/sunxi/Makefile +++ b/board/sunxi/Makefile @@ -9,6 +9,7 @@ # SPDX-License-Identifier: GPL-2.0+ # obj-y += board.o +obj-$(CONFIG_CHIP_DIP) += dip.o obj-$(CONFIG_SUNXI_GMAC) += gmac.o obj-$(CONFIG_SUNXI_AHCI) += ahci.o obj-$(CONFIG_MACH_SUN4I) += dram_sun4i_auto.o diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 53656383d512..8cb7267b0a10 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -719,6 +719,12 @@ int ft_board_setup(void *blob, bd_t *bd) */ setup_environment(blob);
+#ifdef CONFIG_CHIP_DIP + r = chip_dip_dt_setup(blob); + if (r) + return r; +#endif + #ifdef CONFIG_VIDEO_DT_SIMPLEFB r = sunxi_simplefb_setup(blob); if (r) diff --git a/board/sunxi/dip.c b/board/sunxi/dip.c new file mode 100644 index 000000000000..af908917edfe --- /dev/null +++ b/board/sunxi/dip.c @@ -0,0 +1,227 @@ +/* + * (C) Copyright 2016 NextThing Co + * (C) Copyright 2016 Free Electrons + * + * Maxime Ripard maxime.ripard@free-electrons.com + * + * CHIP's DIP spec implementation in U-Boot + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <eeprom.h> +#include <w1.h> + +#include <asm/arch/gpio.h> + +#include <dm/device-internal.h> + + +#define dip_convert(field) \ + ( \ + (sizeof(field) == 1) ? field : \ + (sizeof(field) == 2) ? be16_to_cpu(field) : \ + (sizeof(field) == 4) ? be32_to_cpu(field) : \ + -1 \ + ) + +#define DIP_MAGIC 0x50494843 /* CHIP */ + +#define DIP_VID_NTC 0x9d011a +#define DIP_PID_NTC_POCKET 0x1 +#define DIP_PID_NTC_VGA 0x2 +#define DIP_PID_NTC_HDMI 0x3 + +struct dip_header { + u32 magic; /* CHIP */ + u8 version; /* spec version */ + u32 vendor_id; + u16 product_id; + u8 product_version; + char vendor_name[32]; + char product_name[32]; + u8 rsvd[36]; /* rsvd for futre spec versions */ + u8 data[16]; /* user data, per-dip specific */ +} __packed; + +struct dip { + struct list_head list; + char file[64]; +}; + +enum disp_output { + DISPLAY_COMPOSITE, + DISPLAY_RGB_HDMI_BRIDGE, + DISPLAY_RGB_VGA_BRIDGE, + DISPLAY_RGB_POCKET, +}; + +static LIST_HEAD(dip_list); + +static void dip_setup_pocket_display(enum disp_output display) +{ + char kernel[128]; + char video[128]; + char *s, *kmode; + int x, y; + + s = getenv("dip-auto-video"); + if (s && !strcmp(s, "no")) { + printf("DIP: User disabled auto setup. Aborting.\n"); + return; + } + + switch (display) { + case DISPLAY_RGB_HDMI_BRIDGE: + strncpy(kernel, "video=HDMI-A-1:1024x768@60", sizeof(kernel)); + strncpy(video, "sunxi:1024x768-24@60,monitor=hdmi", + sizeof(video)); + break; + + case DISPLAY_RGB_VGA_BRIDGE: + strncpy(kernel, "video=VGA-1:1024x768@60", sizeof(kernel)); + strncpy(video, "sunxi:1024x768-24@60,monitor=vga", + sizeof(video)); + break; + + case DISPLAY_RGB_POCKET: + strncpy(video, "sunxi:480x272-16@60,monitor=lcd", + sizeof(video)); + break; + + default: + s = getenv("tv-mode"); + if (!s) + s = "ntsc"; + + if (!strcmp(s, "ntsc")) { + x = 720; + y = 480; + kmode = "NTSC"; + } else if (!strcmp(s, "pal")) { + x = 720; + y = 576; + kmode = "PAL"; + } else { + printf("DIP: Unknown TV format: %s\n", s); + return; + } + + snprintf(kernel, sizeof(kernel), "video=Composite-1:%s", + kmode); + snprintf(video, sizeof(video), + "sunxi:%dx%d-24@60,monitor=composite-%s,overscan_x=40,overscan_y=20", + x, y, s); + + break; + } + + setenv("kernelarg_video", kernel); + setenv("video-mode", video); +} + +static void dip_detect(void) +{ + struct udevice *bus, *dev; + u8 display = DISPLAY_COMPOSITE; + u32 vid; + u16 pid; + int ret; + + sunxi_gpio_set_pull(SUNXI_GPD(2), SUNXI_GPIO_PULL_UP); + + w1_get_bus(0, &bus); + + for (device_find_first_child(bus, &dev); dev; + device_find_next_child(&dev)) { + struct dip_header header; + struct dip *dip; + + if (w1_get_device_family(dev) != W1_FAMILY_DS2431) + continue; + + ret = device_probe(dev); + if (ret) { + printf("Couldn't probe device %s: error %d", + dev->name, ret); + continue; + } + + eeprom_read_buf(dev, 0, (u8 *)&header, sizeof(header)); + + if (header.magic != DIP_MAGIC) + continue; + + vid = dip_convert(header.vendor_id); + pid = dip_convert(header.product_id); + + printf("DIP: Found %s (0x%x) from %s (0x%x) detected\n", + header.product_name, pid, + header.vendor_name, vid); + + dip = calloc(sizeof(*dip), 1); + if (!dip) + return; + + snprintf(dip->file, sizeof(dip->file), "dip-%x-%x.dtbo", + vid, pid); + list_add_tail(&dip->list, &dip_list); + + if (vid == DIP_VID_NTC) { + switch (pid) { + case DIP_PID_NTC_POCKET: + display = DISPLAY_RGB_POCKET; + break; + + case DIP_PID_NTC_HDMI: + display = DISPLAY_RGB_HDMI_BRIDGE; + break; + + case DIP_PID_NTC_VGA: + display = DISPLAY_RGB_VGA_BRIDGE; + break; + } + } + } + + dip_setup_pocket_display(display); +} + +int board_video_pre_init(void) +{ + dip_detect(); + + return 0; +} + +int chip_dip_dt_setup(void) +{ + struct dip *dip, *next; + int ret; + char *cmd; + + cmd = getenv("dip_overlay_cmd"); + if (!cmd) + return 0; + + list_for_each_entry_safe(dip, next, &dip_list, list) { + printf("DIP: Applying dip overlay %s\n", dip->file); + setenv("dip_overlay_name", dip->file); + ret = run_command(cmd, 0); + + /* First remove the item from the list */ + list_del(&dip->list); + free(dip); + + /* And then check if there was an error */ + if (ret) + continue; + + ret = run_command("fdt apply $dip_addr_r", 0); + if (ret) + return ret; + } + + return 0; +}

Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- configs/CHIP_defconfig | 5 +++++ 1 file changed, 5 insertions(+), 0 deletions(-)
diff --git a/configs/CHIP_defconfig b/configs/CHIP_defconfig index 65d0c9847a02..2f22e857b4a7 100644 --- a/configs/CHIP_defconfig +++ b/configs/CHIP_defconfig @@ -7,6 +7,7 @@ CONFIG_DRAM_TIMINGS_DDR3_800E_1066G_1333J=y # CONFIG_MMC is not set CONFIG_USB0_VBUS_PIN="PB10" CONFIG_VIDEO_COMPOSITE=y +CONFIG_CHIP_DIP=y CONFIG_DEFAULT_DEVICE_TREE="sun5i-r8-chip" CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=2" CONFIG_SPL=y @@ -23,3 +24,7 @@ CONFIG_USB_GADGET_DOWNLOAD=y CONFIG_G_DNL_MANUFACTURER="Allwinner Technology" CONFIG_G_DNL_VENDOR_NUM=0x1f3a CONFIG_G_DNL_PRODUCT_NUM=0x1010 +CONFIG_EEPROM=y +CONFIG_EEPROM_DS2431=y +CONFIG_W1=y +CONFIG_W1_GPIO=y

On Tue, Nov 08, 2016 at 11:19:20AM +0100, Maxime Ripard wrote:
[snip]
I think the biggest drawback at the moment is that we maintain a list of DIPs and the actions needed directly into the C code, which will make it quite hard to customise for end users and tedious to maintain in the long term. I couldn't really get my head around a better solution, so feel free to suggest alternative approaches.
A thought I had after reading over 8/9 in the series was, could we try something like: - In C, loop over everything in w1 and set environment variables based on each thing we find. - In a CONFIG_PREBOOT load a U-Boot shell script in that will look for the right env variables to be set for a given DIP and then do whatever is needed to load in the overlay.
Does that make sense?

Hi Tom,
On Tue, Nov 08, 2016 at 10:44:18PM -0500, Tom Rini wrote:
On Tue, Nov 08, 2016 at 11:19:20AM +0100, Maxime Ripard wrote:
[snip]
I think the biggest drawback at the moment is that we maintain a list of DIPs and the actions needed directly into the C code, which will make it quite hard to customise for end users and tedious to maintain in the long term. I couldn't really get my head around a better solution, so feel free to suggest alternative approaches.
A thought I had after reading over 8/9 in the series was, could we try something like:
- In C, loop over everything in w1 and set environment variables based on each thing we find.
- In a CONFIG_PREBOOT load a U-Boot shell script in that will look for the right env variables to be set for a given DIP and then do whatever is needed to load in the overlay.
Does that make sense?
I thought about an environment-based solution too, but I was pretty worried about its scalability. But I guesse that's also the easiest thing to modify by end-users.
However, we have basically, three things to do (so far) 1) Modify the U-Boot environment before the display is initialized 2) Modify the kernel command line 3) Apply an overlay
I think CONFIG_PREBOOT is too late for 1, because the display is already initialized, and too early for 2 and 3, because the command line and DT will probably be set / loaded at bootcmd time.
I think we can decouple 1 and 2 from 3, since doing 2 too early is not an issue, because we might have boards that require to load some overlay but wouldn't need to make environment modifications (like an MMC DIP), or the other way around (even though I fail to see a use case for that at the moment).
So we're left with basically two things: - Do a bunch of modifications in the environment early - And apply the overlay very late (basically right before booting the kernel)
So we could do something like having an environment script called dip-<vid>-<pid>-setup called when a DIP is detected (if it exists), and keep the DT overlay logic in its current form.
What do you think? Maxime

On Wed, Nov 09, 2016 at 03:10:18PM +0100, Maxime Ripard wrote:
Hi Tom,
On Tue, Nov 08, 2016 at 10:44:18PM -0500, Tom Rini wrote:
On Tue, Nov 08, 2016 at 11:19:20AM +0100, Maxime Ripard wrote:
[snip]
I think the biggest drawback at the moment is that we maintain a list of DIPs and the actions needed directly into the C code, which will make it quite hard to customise for end users and tedious to maintain in the long term. I couldn't really get my head around a better solution, so feel free to suggest alternative approaches.
A thought I had after reading over 8/9 in the series was, could we try something like:
- In C, loop over everything in w1 and set environment variables based on each thing we find.
- In a CONFIG_PREBOOT load a U-Boot shell script in that will look for the right env variables to be set for a given DIP and then do whatever is needed to load in the overlay.
Does that make sense?
I thought about an environment-based solution too, but I was pretty worried about its scalability. But I guesse that's also the easiest thing to modify by end-users.
Yeah, I share that concern too. But it's also the "easy" one in terms of debug and test too.
However, we have basically, three things to do (so far)
- Modify the U-Boot environment before the display is initialized
- Modify the kernel command line
- Apply an overlay
I think CONFIG_PREBOOT is too late for 1, because the display is already initialized, and too early for 2 and 3, because the command line and DT will probably be set / loaded at bootcmd time.
Something I honestly wonder about at times is, would it not be better to optimize the default case such that the kernel is up and dealing with the display ASAP? If we go from power-on to Kernel is up and has thrown something on-screen in a few seconds, do we really need a complex solution so that it's up 1 or 2 seconds sooner?
I think we can decouple 1 and 2 from 3, since doing 2 too early is not an issue, because we might have boards that require to load some overlay but wouldn't need to make environment modifications (like an MMC DIP), or the other way around (even though I fail to see a use case for that at the moment).
OK.
So we're left with basically two things:
- Do a bunch of modifications in the environment early
- And apply the overlay very late (basically right before booting the kernel)
So we could do something like having an environment script called dip-<vid>-<pid>-setup called when a DIP is detected (if it exists), and keep the DT overlay logic in its current form.
What do you think?
I think that might work. I think we really want to avoid having modify U-Boot to add a new DIP, so if we can move things to the point of "Here's your DIP and here's a link to the file you install $here on your board and it just works" we'll be in a good spot.

Hi,
On 08-11-16 11:19, Maxime Ripard wrote:
The NextThing's C.H.I.P. can have expansion boards called DIPs. Those DIPs are connected through the external headers, and comes with an identification mechanism based on 1-Wire EEPROMs.
That auto-detection works great, because 1-Wire allows the enumeration, and the EEPROMs are guaranteed to have different addresses, which allows us to stack as many DIPs as possible, without any constraints.
Since those DIPs can be close to anything, ranging from a simple PWM-based buzzer to a full featured device such as the PocketCHIP (which comes with a touchscreen, a keyboard, a battery, etc), some adjustments might be needed in U-Boot to be able to present something that just works to the user.
In particular, we need to:
- Apply an overlay if the device is something that should be dealt with early in the boot process (display, storage device)
- Adjust the U-Boot environment if the DIP has a display to change the default video output
- Adjust the kernel command line in previous case for Linux to have the same default
In order to achieve that, we introduced some logic optionally hooked into the sunxi board, two new uclasses for 1-Wire and EEPROMs, and a bunch of new environment variables:
- dip-auto-video to control the automatic video set up (default to yes)
- dip_overlay_cmd that is a script to load the overlay $dip_overlay_name to $dip_addr_r, from whatever place it was stored in, and later apply it.
- kernelarg_video to host the default kernel output in the kernel command line
I think the biggest drawback at the moment is that we maintain a list of DIPs and the actions needed directly into the C code, which will make it quite hard to customise for end users and tedious to maintain in the long term. I couldn't really get my head around a better solution, so feel free to suggest alternative approaches.
Let me know what you think,
The sunxi specific bits look fine to me. I will leave reviewing of the uclass bits to Simon.
Regards,
Hans
participants (6)
-
Hans de Goede
-
Maxime Ripard
-
Michal Simek
-
Moritz Fischer
-
Simon Glass
-
Tom Rini