From 389f1856bd0882471214c2ac94da2b415362c10f Mon Sep 17 00:00:00 2001 From: Marcel Ziswiler Date: Wed, 5 Aug 2015 16:58:17 +0200 Subject: dm: usb: fix USB Ethernet without CONFIG_DM_ETH regression The following commit enforces CONFIG_DM_ETH for USB Ethernet which breaks any board using CONFIG_USB_HOST_ETHER without CONFIG_DM_ETH which this patch fixes. commit 69559093f6173dcfcb041df0995063bdbd07d49b dm: usb: Avoid using USB ethernet with CONFIG_DM_USB and no DM_ETH Tested on Colibri T20/T30 as well as Apalis T30 with CONFIG_USB_HOST_ETHER and CONFIG_USB_ETHER_ASIX enabled and a LevelOne USB-0301 ASIX AX88772 dongle. Signed-off-by: Marcel Ziswiler Acked-by: Simon Glass diff --git a/common/cmd_usb.c b/common/cmd_usb.c index 0ade775..6874af7 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -530,13 +530,16 @@ static void do_usb_start(void) /* try to recognize storage devices immediately */ usb_stor_curr_dev = usb_stor_scan(1); #endif +#endif #ifdef CONFIG_USB_HOST_ETHER # ifdef CONFIG_DM_ETH -# error "You must use CONFIG_DM_USB if you want to use CONFIG_USB_HOST_ETHER with CONFIG_DM_ETH" -# endif +# ifndef CONFIG_DM_USB +# error "You must use CONFIG_DM_USB if you want to use CONFIG_USB_HOST_ETHER with CONFIG_DM_ETH" +# endif +# else /* try to recognize ethernet devices immediately */ usb_ether_curr_dev = usb_host_eth_scan(1); -#endif +# endif #endif #ifdef CONFIG_USB_KEYBOARD drv_usb_kbd_init(); -- cgit v0.10.2 From d82ba4c0b457f0cb30da7bbee935aad7793e5fac Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:38 -0600 Subject: dm: core: Support finding a device by phandle It is common for one node to reference another via a phandle. Add support for obtaining an attached device by this method. As an example, a node may have a 'power-supply' property which references a regulator, allowing the driver to turn on its power. Signed-off-by: Simon Glass diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index ffe6995..5c4a66d 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -273,6 +273,37 @@ static int uclass_find_device_by_of_offset(enum uclass_id id, int node, return -ENODEV; } +static int uclass_find_device_by_phandle(enum uclass_id id, + struct udevice *parent, + const char *name, + struct udevice **devp) +{ + struct udevice *dev; + struct uclass *uc; + int find_phandle; + int ret; + + *devp = NULL; + find_phandle = fdtdec_get_int(gd->fdt_blob, parent->of_offset, name, + -1); + if (find_phandle <= 0) + return -ENOENT; + ret = uclass_get(id, &uc); + if (ret) + return ret; + + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + uint phandle = fdt_get_phandle(gd->fdt_blob, dev->of_offset); + + if (phandle == find_phandle) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + int uclass_get_device_tail(struct udevice *dev, int ret, struct udevice **devp) { @@ -338,6 +369,17 @@ int uclass_get_device_by_of_offset(enum uclass_id id, int node, return uclass_get_device_tail(dev, ret, devp); } +int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, + const char *name, struct udevice **devp) +{ + struct udevice *dev; + int ret; + + *devp = NULL; + ret = uclass_find_device_by_phandle(id, parent, name, &dev); + return uclass_get_device_tail(dev, ret, devp); +} + int uclass_first_device(enum uclass_id id, struct udevice **devp) { struct udevice *dev; diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 4cfc0df..3fe1739 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -177,6 +177,23 @@ int uclass_get_device_by_of_offset(enum uclass_id id, int node, struct udevice **devp); /** + * uclass_get_device_by_phandle() - Get a uclass device by phandle + * + * This searches the devices in the uclass for one with the given phandle. + * + * The device is probed to activate it ready for use. + * + * @id: uclass ID to look up + * @parent: Parent device containing the phandle pointer + * @name: Name of property in the parent device node + * @devp: Returns pointer to device (there is only one for each node) + * @return 0 if OK, -ENOENT if there is no @name present in the node, other + * -ve on error + */ +int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, + const char *name, struct udevice **devp); + +/** * uclass_first_device() - Get the first device in a uclass * * The device returned is probed if necessary, and ready for use -- cgit v0.10.2 From 7d7db2225c5e63a389ee04d63919f012e7ba880d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:39 -0600 Subject: dm: i2c: Add a message debug function Add a way to dump the contents of an I2C message for debugging purposes. Signed-off-by: Simon Glass Acked-by: Heiko Schocher diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index eaba965..42d6e89 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -18,6 +18,22 @@ DECLARE_GLOBAL_DATA_PTR; #define I2C_MAX_OFFSET_LEN 4 +/* Useful debugging function */ +void i2c_dump_msgs(struct i2c_msg *msg, int nmsgs) +{ + int i; + + for (i = 0; i < nmsgs; i++) { + struct i2c_msg *m = &msg[i]; + + printf(" %s %x len=%x", m->flags & I2C_M_RD ? "R" : "W", + msg->addr, msg->len); + if (!(m->flags & I2C_M_RD)) + printf(": %x", m->buf[0]); + printf("\n"); + } +} + /** * i2c_setup_offset() - Set up a new message with a chip offset * diff --git a/include/i2c.h b/include/i2c.h index 9300d97..a5498a3 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -473,6 +473,16 @@ int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len, int i2c_chip_ofdata_to_platdata(const void *blob, int node, struct dm_i2c_chip *chip); +/** + * i2c_dump_msgs() - Dump a list of I2C messages + * + * This may be useful for debugging. + * + * @msg: Message list to dump + * @nmsgs: Number of messages + */ +void i2c_dump_msgs(struct i2c_msg *msg, int nmsgs); + #ifndef CONFIG_DM_I2C /* -- cgit v0.10.2 From 25a0fb4385a79e6d0b186d1f13a3da0c6797da6d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:40 -0600 Subject: dm: i2c: Correct comment nits in dm_i2c_reg_read/write() Add documentation for the @dev parameter. Signed-off-by: Simon Glass Acked-by: Heiko Schocher diff --git a/include/i2c.h b/include/i2c.h index a5498a3..e1ad8d8 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -129,6 +129,7 @@ int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, * * This reads a single value from the given address in an I2C chip * + * @dev: Device to use for transfer * @addr: Address to read from * @return value read, or -ve on error */ @@ -139,6 +140,7 @@ int dm_i2c_reg_read(struct udevice *dev, uint offset); * * This writes a single value to the given address in an I2C chip * + * @dev: Device to use for transfer * @addr: Address to write to * @val: Value to write (normally a byte) * @return 0 on success, -ve on error -- cgit v0.10.2 From 7fc65bcf8a0a5463db86efbb273a40448c845efc Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:41 -0600 Subject: dm: i2c: Move definitions to the top of the header file Move the flags and struct definitions higher in the file so that we can reference them with functions declared in the driver model section. Signed-off-by: Simon Glass Acked-by: Heiko Schocher diff --git a/include/i2c.h b/include/i2c.h index e1ad8d8..4c0e263 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -74,6 +74,49 @@ struct dm_i2c_bus { int speed_hz; }; +/* + * Not all of these flags are implemented in the U-Boot API + */ +enum dm_i2c_msg_flags { + I2C_M_TEN = 0x0010, /* ten-bit chip address */ + I2C_M_RD = 0x0001, /* read data, from slave to master */ + I2C_M_STOP = 0x8000, /* send stop after this message */ + I2C_M_NOSTART = 0x4000, /* no start before this message */ + I2C_M_REV_DIR_ADDR = 0x2000, /* invert polarity of R/W bit */ + I2C_M_IGNORE_NAK = 0x1000, /* continue after NAK */ + I2C_M_NO_RD_ACK = 0x0800, /* skip the Ack bit on reads */ + I2C_M_RECV_LEN = 0x0400, /* length is first received byte */ +}; + +/** + * struct i2c_msg - an I2C message + * + * @addr: Slave address + * @flags: Flags (see enum dm_i2c_msg_flags) + * @len: Length of buffer in bytes, may be 0 for a probe + * @buf: Buffer to send/receive, or NULL if no data + */ +struct i2c_msg { + uint addr; + uint flags; + uint len; + u8 *buf; +}; + +/** + * struct i2c_msg_list - a list of I2C messages + * + * This is called i2c_rdwr_ioctl_data in Linux but the name does not seem + * appropriate in U-Boot. + * + * @msg: Pointer to i2c_msg array + * @nmsgs: Number of elements in the array + */ +struct i2c_msg_list { + struct i2c_msg *msgs; + uint nmsgs; +}; + /** * dm_i2c_read() - read bytes from an I2C chip * @@ -294,49 +337,6 @@ void i2c_reg_write(uint8_t addr, uint8_t reg, uint8_t val); #endif -/* - * Not all of these flags are implemented in the U-Boot API - */ -enum dm_i2c_msg_flags { - I2C_M_TEN = 0x0010, /* ten-bit chip address */ - I2C_M_RD = 0x0001, /* read data, from slave to master */ - I2C_M_STOP = 0x8000, /* send stop after this message */ - I2C_M_NOSTART = 0x4000, /* no start before this message */ - I2C_M_REV_DIR_ADDR = 0x2000, /* invert polarity of R/W bit */ - I2C_M_IGNORE_NAK = 0x1000, /* continue after NAK */ - I2C_M_NO_RD_ACK = 0x0800, /* skip the Ack bit on reads */ - I2C_M_RECV_LEN = 0x0400, /* length is first received byte */ -}; - -/** - * struct i2c_msg - an I2C message - * - * @addr: Slave address - * @flags: Flags (see enum dm_i2c_msg_flags) - * @len: Length of buffer in bytes, may be 0 for a probe - * @buf: Buffer to send/receive, or NULL if no data - */ -struct i2c_msg { - uint addr; - uint flags; - uint len; - u8 *buf; -}; - -/** - * struct i2c_msg_list - a list of I2C messages - * - * This is called i2c_rdwr_ioctl_data in Linux but the name does not seem - * appropriate in U-Boot. - * - * @msg: Pointer to i2c_msg array - * @nmsgs: Number of elements in the array - */ -struct i2c_msg_list { - struct i2c_msg *msgs; - uint nmsgs; -}; - /** * struct dm_i2c_ops - driver operations for I2C uclass * -- cgit v0.10.2 From df358c6beca8f7cc2f2b67c54f0bf255ebd5cc22 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:42 -0600 Subject: dm: i2c: Add a function to transfer messages Sometimes it is useful to be able to transfer a raw I2C message. This happens when the chip address needs to be set manually, or when the data to be sent/received is in another buffer. Add a function to provide access to this. Signed-off-by: Simon Glass Acked-by: Heiko Schocher diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 42d6e89..50b99ea 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -202,6 +202,17 @@ int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, } } +int dm_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{ + struct udevice *bus = dev_get_parent(dev); + struct dm_i2c_ops *ops = i2c_get_ops(bus); + + if (!ops->xfer) + return -ENOSYS; + + return ops->xfer(bus, msg, nmsgs); +} + int dm_i2c_reg_read(struct udevice *dev, uint offset) { uint8_t val; diff --git a/include/i2c.h b/include/i2c.h index 4c0e263..d191829 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -191,6 +191,19 @@ int dm_i2c_reg_read(struct udevice *dev, uint offset); int dm_i2c_reg_write(struct udevice *dev, uint offset, unsigned int val); /** + * dm_i2c_xfer() - Transfer messages over I2C + * + * This transfers a raw message. It is best to use dm_i2c_reg_read/write() + * instead. + * + * @dev: Device to use for transfer + * @msg: List of messages to transfer + * @nmsgs: Number of messages to transfer + * @return 0 on success, -ve on error + */ +int dm_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs); + +/** * dm_i2c_set_bus_speed() - set the speed of a bus * * @bus: Bus to adjust -- cgit v0.10.2 From 3d1957f0ea0133ec06f9c6fd85dc1acdf66ad29c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:21 -0600 Subject: dm: i2c: Add support for multiplexed I2C buses Add a new I2C_MUX uclass. Devices in this class can multiplex between several I2C buses, selecting them one at a time for use by the system. The multiplexing mechanism is left to the driver to decide - it may be controlled by GPIOs, for example. The uclass supports only two methods: select() and deselect(). The current mux state is expected to be stored in the mux itself since it is the only thing that knows how to make things work. The mux can record the current state and then avoid switching unless it is necessary. So select() can be skipped if the mux is already in the correct state. Also deselect() can be made a nop if required. Signed-off-by: Simon Glass diff --git a/doc/device-tree-bindings/i2c/i2c-mux.txt b/doc/device-tree-bindings/i2c/i2c-mux.txt new file mode 100644 index 0000000..af84cce --- /dev/null +++ b/doc/device-tree-bindings/i2c/i2c-mux.txt @@ -0,0 +1,60 @@ +Common i2c bus multiplexer/switch properties. + +An i2c bus multiplexer/switch will have several child busses that are +numbered uniquely in a device dependent manner. The nodes for an i2c bus +multiplexer/switch will have one child node for each child +bus. + +Required properties: +- #address-cells = <1>; +- #size-cells = <0>; + +Required properties for child nodes: +- #address-cells = <1>; +- #size-cells = <0>; +- reg : The sub-bus number. + +Optional properties for child nodes: +- Other properties specific to the multiplexer/switch hardware. +- Child nodes conforming to i2c bus binding + + +Example : + + /* + An NXP pca9548 8 channel I2C multiplexer at address 0x70 + with two NXP pca8574 GPIO expanders attached, one each to + ports 3 and 4. + */ + + mux@70 { + compatible = "nxp,pca9548"; + reg = <0x70>; + #address-cells = <1>; + #size-cells = <0>; + + i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + + gpio1: gpio@38 { + compatible = "nxp,pca8574"; + reg = <0x38>; + #gpio-cells = <2>; + gpio-controller; + }; + }; + i2c@4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + + gpio2: gpio@38 { + compatible = "nxp,pca8574"; + reg = <0x38>; + #gpio-cells = <2>; + gpio-controller; + }; + }; + }; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 86fb36b..caee3d8 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -73,3 +73,5 @@ config SYS_I2C_UNIPHIER_F help Support for UniPhier FIFO-builtin I2C controller driver. This I2C controller is used on PH1-Pro4 or newer UniPhier SoCs. + +source "drivers/i2c/muxes/Kconfig" diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index d9e912f..dc9e81b 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -37,3 +37,5 @@ obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o obj-$(CONFIG_SYS_I2C_UNIPHIER) += i2c-uniphier.o obj-$(CONFIG_SYS_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_SYS_I2C_ZYNQ) += zynq_i2c.o + +obj-y += muxes/ diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig new file mode 100644 index 0000000..a05b32d --- /dev/null +++ b/drivers/i2c/muxes/Kconfig @@ -0,0 +1,8 @@ +config I2C_MUX + bool "Suport I2C multiplexers" + depends on DM_I2C + help + This enables I2C buses to be multiplexed, so that you can select + one of several buses using some sort of control mechanism. The + bus select is handled automatically when that bus is accessed, + using a suitable I2C MUX driver. diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile new file mode 100644 index 0000000..7583e3a --- /dev/null +++ b/drivers/i2c/muxes/Makefile @@ -0,0 +1,6 @@ +# +# Copyright (c) 2015 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# +obj-$(CONFIG_I2C_MUX) += i2c-mux-uclass.o diff --git a/drivers/i2c/muxes/i2c-mux-uclass.c b/drivers/i2c/muxes/i2c-mux-uclass.c new file mode 100644 index 0000000..3f52bff --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-uclass.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct i2c_mux: Information the uclass stores about an I2C mux + * + * @selected: Currently selected mux, or -1 for none + * @i2c_bus: I2C bus to use for communcation + */ +struct i2c_mux { + int selected; + struct udevice *i2c_bus; +}; + +/** + * struct i2c_mux_bus: Information about each bus the mux controls + * + * @channel: Channel number used to select this bus + */ +struct i2c_mux_bus { + uint channel; +}; + +/* Find out the mux channel number */ +static int i2c_mux_child_post_bind(struct udevice *dev) +{ + struct i2c_mux_bus *plat = dev_get_parent_platdata(dev); + int channel; + + channel = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1); + if (channel < 0) + return -EINVAL; + plat->channel = channel; + + return 0; +} + +/* Find the I2C buses selected by this mux */ +static int i2c_mux_post_bind(struct udevice *mux) +{ + const void *blob = gd->fdt_blob; + int ret; + int offset; + + debug("%s: %s\n", __func__, mux->name); + /* + * There is no compatible string in the sub-nodes, so we must manually + * bind these + */ + for (offset = fdt_first_subnode(blob, mux->of_offset); + offset > 0; + offset = fdt_next_subnode(blob, offset)) { + struct udevice *dev; + const char *name; + + name = fdt_get_name(blob, offset, NULL); + ret = device_bind_driver_to_node(mux, "i2c_mux_bus_drv", name, + offset, &dev); + debug(" - bind ret=%d, %s\n", ret, dev ? dev->name : NULL); + if (ret) + return ret; + } + + return 0; +} + +/* Set up the mux ready for use */ +static int i2c_mux_post_probe(struct udevice *mux) +{ + struct i2c_mux *priv = dev_get_uclass_priv(mux); + int ret; + + debug("%s: %s\n", __func__, mux->name); + priv->selected = -1; + + ret = uclass_get_device_by_phandle(UCLASS_I2C, mux, "i2c-parent", + &priv->i2c_bus); + if (ret) + return ret; + debug("%s: bus=%p/%s\n", __func__, priv->i2c_bus, priv->i2c_bus->name); + + return 0; +} + +int i2c_mux_select(struct udevice *dev) +{ + struct i2c_mux_bus *plat = dev_get_parent_platdata(dev); + struct udevice *mux = dev->parent; + struct i2c_mux_ops *ops = i2c_mux_get_ops(mux); + + if (!ops->select) + return -ENOSYS; + + return ops->select(mux, dev, plat->channel); +} + +int i2c_mux_deselect(struct udevice *dev) +{ + struct i2c_mux_bus *plat = dev_get_parent_platdata(dev); + struct udevice *mux = dev->parent; + struct i2c_mux_ops *ops = i2c_mux_get_ops(mux); + + if (!ops->deselect) + return -ENOSYS; + + return ops->deselect(mux, dev, plat->channel); +} + +static int i2c_mux_bus_set_bus_speed(struct udevice *dev, unsigned int speed) +{ + struct udevice *mux = dev->parent; + struct i2c_mux *priv = dev_get_uclass_priv(mux); + int ret, ret2; + + ret = i2c_mux_select(dev); + if (ret) + return ret; + ret = dm_i2c_set_bus_speed(priv->i2c_bus, speed); + ret2 = i2c_mux_deselect(dev); + + return ret ? ret : ret2; +} + +static int i2c_mux_bus_probe(struct udevice *dev, uint chip_addr, + uint chip_flags) +{ + struct udevice *mux = dev->parent; + struct i2c_mux *priv = dev_get_uclass_priv(mux); + struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus); + int ret, ret2; + + debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name); + if (!ops->probe_chip) + return -ENOSYS; + ret = i2c_mux_select(dev); + if (ret) + return ret; + ret = ops->probe_chip(priv->i2c_bus, chip_addr, chip_flags); + ret2 = i2c_mux_deselect(dev); + + return ret ? ret : ret2; +} + +static int i2c_mux_bus_xfer(struct udevice *dev, struct i2c_msg *msg, + int nmsgs) +{ + struct udevice *mux = dev->parent; + struct i2c_mux *priv = dev_get_uclass_priv(mux); + struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus); + int ret, ret2; + + debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name); + if (!ops->xfer) + return -ENOSYS; + ret = i2c_mux_select(dev); + if (ret) + return ret; + ret = ops->xfer(priv->i2c_bus, msg, nmsgs); + ret2 = i2c_mux_deselect(dev); + + return ret ? ret : ret2; +} + +static const struct dm_i2c_ops i2c_mux_bus_ops = { + .xfer = i2c_mux_bus_xfer, + .probe_chip = i2c_mux_bus_probe, + .set_bus_speed = i2c_mux_bus_set_bus_speed, +}; + +U_BOOT_DRIVER(i2c_mux_bus) = { + .name = "i2c_mux_bus_drv", + .id = UCLASS_I2C, + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), + .ops = &i2c_mux_bus_ops, +}; + +UCLASS_DRIVER(i2c_mux) = { + .id = UCLASS_I2C_MUX, + .name = "i2c_mux", + .post_bind = i2c_mux_post_bind, + .post_probe = i2c_mux_post_probe, + .per_device_auto_alloc_size = sizeof(struct i2c_mux), + .per_child_platdata_auto_alloc_size = sizeof(struct i2c_mux_bus), + .child_post_bind = i2c_mux_child_post_bind, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index bc057d7..777b101 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -35,6 +35,7 @@ enum uclass_id { UCLASS_I2C, /* I2C bus */ UCLASS_I2C_EEPROM, /* I2C EEPROM device */ UCLASS_I2C_GENERIC, /* Generic I2C device */ + UCLASS_I2C_MUX, /* I2C multiplexer */ UCLASS_LED, /* Light-emitting diode (LED) */ UCLASS_LPC, /* x86 'low pin count' interface */ UCLASS_MASS_STORAGE, /* Mass storage device */ diff --git a/include/i2c.h b/include/i2c.h index d191829..6493931 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -445,6 +445,45 @@ struct dm_i2c_ops { #define i2c_get_ops(dev) ((struct dm_i2c_ops *)(dev)->driver->ops) /** + * struct i2c_mux_ops - operations for an I2C mux + * + * The current mux state is expected to be stored in the mux itself since + * it is the only thing that knows how to make things work. The mux can + * record the current state and then avoid switching unless it is necessary. + * So select() can be skipped if the mux is already in the correct state. + * Also deselect() can be made a nop if required. + */ +struct i2c_mux_ops { + /** + * select() - select one of of I2C buses attached to a mux + * + * This will be called when there is no bus currently selected by the + * mux. This method does not need to deselect the old bus since + * deselect() will be already have been called if necessary. + * + * @mux: Mux device + * @bus: I2C bus to select + * @channel: Channel number correponding to the bus to select + * @return 0 if OK, -ve on error + */ + int (*select)(struct udevice *mux, struct udevice *bus, uint channel); + + /** + * deselect() - select one of of I2C buses attached to a mux + * + * This is used to deselect the currently selected I2C bus. + * + * @mux: Mux device + * @bus: I2C bus to deselect + * @channel: Channel number correponding to the bus to deselect + * @return 0 if OK, -ve on error + */ + int (*deselect)(struct udevice *mux, struct udevice *bus, uint channel); +}; + +#define i2c_mux_get_ops(dev) ((struct i2c_mux_ops *)(dev)->driver->ops) + +/** * i2c_get_chip() - get a device to use to access a chip on a bus * * This returns the device for the given chip address. The device can then -- cgit v0.10.2 From b725dc458f9db44957d4f541d05defe178dda7b5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:22 -0600 Subject: i2c: Add a mux for GPIO-based I2C bus arbitration While I2C supports multi-master buses this is difficult to get right. The implementation on the master side in software is quite complex. Clock-stretching and the arbitrary time that an I2C transaction can take make it difficult to share the bus fairly in the face of high traffic. When one or more masters can be reset independently part-way through a transaction it is hard to know the state of the bus. This driver provides a scheme based on two 'claim' GPIOs, one driven by the AP (Application Processor, meaning the main CPU) and one driven by the EC (Embedded Controller, a small CPU aimed at handling system tasks). With these they can communicate and reliably share the bus. This scheme has minimal overhead and involves very little code. It is used on snow to permit the EC and the AP to share access to the main system PMIC and battery. The scheme can survive reboots by either side without difficulty. This scheme has been tested in the field with millions of devices. Since U-Boot runs on the AP, the terminology used is 'our' claim GPIO, meaning the AP's, and 'their' claim GPIO, meaning the EC's. This terminology is used by the device tree bindings in Linux also. Signed-off-by: Simon Glass diff --git a/doc/README.i2c b/doc/README.i2c new file mode 100644 index 0000000..07cd8df --- /dev/null +++ b/doc/README.i2c @@ -0,0 +1,60 @@ +I2C Bus Arbitration +=================== + +While I2C supports multi-master buses this is difficult to get right. +The implementation on the master side in software is quite complex. +Clock-stretching and the arbitrary time that an I2C transaction can take +make it difficult to share the bus fairly in the face of high traffic. +When one or more masters can be reset independently part-way through a +transaction it is hard to know the state of the bus. + +U-Boot provides a scheme based on two 'claim' GPIOs, one driven by the +AP (Application Processor, meaning the main CPU) and one driven by the EC +(Embedded Controller, a small CPU aimed at handling system tasks). With +these they can communicate and reliably share the bus. This scheme has +minimal overhead and involves very little code. The scheme can survive +reboots by either side without difficulty. + +Since U-Boot runs on the AP, the terminology used is 'our' claim GPIO, +meaning the AP's, and 'their' claim GPIO, meaning the EC's. This terminology +is used by the device tree bindings in Linux also. + +The driver is implemented as an I2C mux, as it is in Linux. See +i2c-arb-gpio-challenge for the implementation. + +GPIO lines are shared between the AP and EC to manage the bus. The AP and EC +each have a 'bus claim' line, which is an output that the other can see. + +- AP_CLAIM: output from AP, signalling to the EC that the AP wants the bus +- EC_CLAIM: output from EC, signalling to the AP that the EC wants the bus + +The basic algorithm is to assert your line when you want the bus, then make +sure that the other side doesn't want it also. A detailed explanation is best +done with an example. + +Let's say the AP wants to claim the bus. It: + +1. Asserts AP_CLAIM +2. Waits a little bit for the other side to notice (slew time) +3. Checks EC_CLAIM. If this is not asserted, then the AP has the bus, and we + are done +4. Otherwise, wait for a few milliseconds (retry time) and see if EC_CLAIM is + released +5. If not, back off, release the claim and wait for a few more milliseconds + (retry time again) +6. Go back to 1 if things don't look wedged (wait time has expired) +7. Panic. The other side is hung with the CLAIM line set. + +The same algorithm applies on the EC. + +To release the bus, just de-assert the claim line. + +Typical delays are: +- slew time 10 us +- retry time 3 ms +- wait time - 50ms + +In general the traffic is fairly light, and in particular the EC wants access +to the bus quite rarely (maybe every 10s or 30s to check the battery). This +scheme works very nicely with very low contention. There is only a 10 us +wait for access to the bus assuming that the other side isn't using it. diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index a05b32d..bd3e078 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -6,3 +6,12 @@ config I2C_MUX one of several buses using some sort of control mechanism. The bus select is handled automatically when that bus is accessed, using a suitable I2C MUX driver. + +config I2C_ARB_GPIO_CHALLENGE + bool "GPIO-based I2C arbitration" + depends on I2C_MUX + help + If you say yes to this option, support will be included for an + I2C multimaster arbitration scheme using GPIOs and a challenge & + response mechanism where masters have to claim the bus by asserting + a GPIO. diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index 7583e3a..612cc27 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -3,4 +3,5 @@ # # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o obj-$(CONFIG_I2C_MUX) += i2c-mux-uclass.o diff --git a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c new file mode 100644 index 0000000..3f072c7 --- /dev/null +++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct i2c_arbitrator_priv { + struct gpio_desc ap_claim; + struct gpio_desc ec_claim; + uint slew_delay_us; + uint wait_retry_ms; + uint wait_free_ms; +}; + +int i2c_arbitrator_deselect(struct udevice *mux, struct udevice *bus, + uint channel) +{ + struct i2c_arbitrator_priv *priv = dev_get_priv(mux); + int ret; + + debug("%s: %s\n", __func__, mux->name); + ret = dm_gpio_set_value(&priv->ap_claim, 0); + udelay(priv->slew_delay_us); + + return ret; +} + +int i2c_arbitrator_select(struct udevice *mux, struct udevice *bus, + uint channel) +{ + struct i2c_arbitrator_priv *priv = dev_get_priv(mux); + unsigned start; + int ret; + + debug("%s: %s\n", __func__, mux->name); + /* Start a round of trying to claim the bus */ + start = get_timer(0); + do { + unsigned start_retry; + int waiting = 0; + + /* Indicate that we want to claim the bus */ + ret = dm_gpio_set_value(&priv->ap_claim, 1); + if (ret) + goto err; + udelay(priv->slew_delay_us); + + /* Wait for the EC to release it */ + start_retry = get_timer(0); + while (get_timer(start_retry) < priv->wait_retry_ms) { + ret = dm_gpio_get_value(&priv->ec_claim); + if (ret < 0) { + goto err; + } else if (!ret) { + /* We got it, so return */ + return 0; + } + + if (!waiting) + waiting = 1; + } + + /* It didn't release, so give up, wait, and try again */ + ret = dm_gpio_set_value(&priv->ap_claim, 0); + if (ret) + goto err; + + mdelay(priv->wait_retry_ms); + } while (get_timer(start) < priv->wait_free_ms); + + /* Give up, release our claim */ + printf("I2C: Could not claim bus, timeout %lu\n", get_timer(start)); + ret = -ETIMEDOUT; + ret = 0; +err: + return ret; +} + +static int i2c_arbitrator_probe(struct udevice *dev) +{ + struct i2c_arbitrator_priv *priv = dev_get_priv(dev); + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + int ret; + + debug("%s: %s\n", __func__, dev->name); + priv->slew_delay_us = fdtdec_get_int(blob, node, "slew-delay-us", 0); + priv->wait_retry_ms = fdtdec_get_int(blob, node, "wait-retry-us", 0) / + 1000; + priv->wait_free_ms = fdtdec_get_int(blob, node, "wait-free-us", 0) / + 1000; + ret = gpio_request_by_name(dev, "our-claim-gpio", 0, &priv->ap_claim, + GPIOD_IS_OUT); + if (ret) + goto err; + ret = gpio_request_by_name(dev, "their-claim-gpios", 0, &priv->ec_claim, + GPIOD_IS_IN); + if (ret) + goto err_ec_gpio; + + return 0; + +err_ec_gpio: + dm_gpio_free(dev, &priv->ap_claim); +err: + debug("%s: ret=%d\n", __func__, ret); + return ret; +} + +static int i2c_arbitrator_remove(struct udevice *dev) +{ + struct i2c_arbitrator_priv *priv = dev_get_priv(dev); + + dm_gpio_free(dev, &priv->ap_claim); + dm_gpio_free(dev, &priv->ec_claim); + + return 0; +} + +static const struct i2c_mux_ops i2c_arbitrator_ops = { + .select = i2c_arbitrator_select, + .deselect = i2c_arbitrator_deselect, +}; + +static const struct udevice_id i2c_arbitrator_ids[] = { + { .compatible = "i2c-arb-gpio-challenge" }, + { } +}; + +U_BOOT_DRIVER(i2c_arbitrator) = { + .name = "i2c_arbitrator", + .id = UCLASS_I2C_MUX, + .of_match = i2c_arbitrator_ids, + .probe = i2c_arbitrator_probe, + .remove = i2c_arbitrator_remove, + .ops = &i2c_arbitrator_ops, + .priv_auto_alloc_size = sizeof(struct i2c_arbitrator_priv), +}; -- cgit v0.10.2 From 26ea76850e136651d0b02f6623b5c39fb98b10eb Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:46 -0600 Subject: exynos: i2c: Fix code style with ReadWriteByte() This function should not use mixed case, and it is simpler to use clrbits_le32() when clearing bits. Fix it. Signed-off-by: Simon Glass Acked-by: Heiko Schocher diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index 9a04e48..4bbd012 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -258,9 +258,9 @@ static int hsi2c_wait_for_trx(struct exynos5_hsi2c *i2c) return I2C_NOK_TOUT; } -static void ReadWriteByte(struct s3c24x0_i2c *i2c) +static void read_write_byte(struct s3c24x0_i2c *i2c) { - writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon); + clrbits_le32(&i2c->iiccon, I2CCON_IRPND); } #ifdef CONFIG_SYS_I2C @@ -794,7 +794,7 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c, if (addr && addr_len) { while ((i < addr_len) && (result == I2C_OK)) { writel(addr[i++], &i2c->iicds); - ReadWriteByte(i2c); + read_write_byte(i2c); result = WaitForXfer(i2c); } i = 0; @@ -806,7 +806,7 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c, case I2C_WRITE: while ((i < data_len) && (result == I2C_OK)) { writel(data[i++], &i2c->iicds); - ReadWriteByte(i2c); + read_write_byte(i2c); result = WaitForXfer(i2c); } break; @@ -822,7 +822,7 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c, /* Generate a re-START. */ writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP, &i2c->iicstat); - ReadWriteByte(i2c); + read_write_byte(i2c); result = WaitForXfer(i2c); if (result != I2C_OK) @@ -835,7 +835,7 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c, writel(readl(&i2c->iiccon) & ~I2CCON_ACKGEN, &i2c->iiccon); - ReadWriteByte(i2c); + read_write_byte(i2c); result = WaitForXfer(i2c); data[i++] = readl(&i2c->iicds); } @@ -852,7 +852,7 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c, bailout: /* Send STOP. */ writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); - ReadWriteByte(i2c); + read_write_byte(i2c); return result; } -- cgit v0.10.2 From 45d9ae87cbe9b2e76a856647c35da83535af2984 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:47 -0600 Subject: exynos: i2c: Tidy up the driver model code The existing driver model implementation uses the old non-driver-model code to operate, but has become impossibly tangled as a result. The actual algorithm is quite simple. Also the normal-speed and high-speed buses are quite different and it doesn't seem that useful to put them in the same driver. Finally, there is a bug which breaks communication with the Maxim sound codec and may cause problems with other device. Rewrite the driver model code for normal-speed operation so that it is easier to understand, and fix the bug. Add a TODO to split the drivers. Signed-off-by: Simon Glass Reviewed-by: Heiko Schocher diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index 4bbd012..c11a6be 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -1284,62 +1284,106 @@ U_BOOT_I2C_ADAP_COMPLETE(s3c0, s3c24x0_i2c_init, s3c24x0_i2c_probe, #endif /* CONFIG_SYS_I2C */ #ifdef CONFIG_DM_I2C -static int i2c_write_data(struct s3c24x0_i2c_bus *i2c_bus, uchar chip, - uchar *buffer, int len, bool end_with_repeated_start) +static int exynos_hs_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, + int nmsgs) { + struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev); + struct exynos5_hsi2c *hsregs = i2c_bus->hsregs; int ret; - if (i2c_bus->is_highspeed) { - ret = hsi2c_write(i2c_bus->hsregs, chip, 0, 0, - buffer, len, true); - if (ret) + for (; nmsgs > 0; nmsgs--, msg++) { + if (msg->flags & I2C_M_RD) { + ret = hsi2c_read(hsregs, msg->addr, 0, 0, msg->buf, + msg->len); + } else { + ret = hsi2c_write(hsregs, msg->addr, 0, 0, msg->buf, + msg->len, true); + } + if (ret) { exynos5_i2c_reset(i2c_bus); - } else { - ret = i2c_transfer(i2c_bus->regs, I2C_WRITE, - chip << 1, 0, 0, buffer, len); + return -EREMOTEIO; + } } - return ret != I2C_OK; + return 0; } -static int i2c_read_data(struct s3c24x0_i2c_bus *i2c_bus, uchar chip, - uchar *buffer, int len) +static int s3c24x0_do_msg(struct s3c24x0_i2c_bus *i2c_bus, struct i2c_msg *msg, + int seq) { - int ret; + struct s3c24x0_i2c *i2c = i2c_bus->regs; + bool is_read = msg->flags & I2C_M_RD; + uint status; + uint addr; + int ret, i; - if (i2c_bus->is_highspeed) { - ret = hsi2c_read(i2c_bus->hsregs, chip, 0, 0, buffer, len); - if (ret) - exynos5_i2c_reset(i2c_bus); + if (!seq) + setbits_le32(&i2c->iiccon, I2CCON_ACKGEN); + + /* Get the slave chip address going */ + addr = msg->addr << 1; + writel(addr, &i2c->iicds); + status = I2C_TXRX_ENA | I2C_START_STOP; + if (is_read) + status |= I2C_MODE_MR; + else + status |= I2C_MODE_MT; + writel(status, &i2c->iicstat); + if (seq) + read_write_byte(i2c); + + /* Wait for chip address to transmit */ + ret = WaitForXfer(i2c); + if (ret) + goto err; + + if (is_read) { + for (i = 0; !ret && i < msg->len; i++) { + /* disable ACK for final READ */ + if (i == msg->len - 1) + clrbits_le32(&i2c->iiccon, I2CCON_ACKGEN); + read_write_byte(i2c); + ret = WaitForXfer(i2c); + msg->buf[i] = readl(&i2c->iicds); + } + if (ret == I2C_NACK) + ret = I2C_OK; /* Normal terminated read */ } else { - ret = i2c_transfer(i2c_bus->regs, I2C_READ, - chip << 1, 0, 0, buffer, len); + for (i = 0; !ret && i < msg->len; i++) { + writel(msg->buf[i], &i2c->iicds); + read_write_byte(i2c); + ret = WaitForXfer(i2c); + } } - return ret != I2C_OK; +err: + return ret; } static int s3c24x0_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) { struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev); - int ret; + struct s3c24x0_i2c *i2c = i2c_bus->regs; + ulong start_time; + int ret, i; - for (; nmsgs > 0; nmsgs--, msg++) { - bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD); - - if (msg->flags & I2C_M_RD) { - ret = i2c_read_data(i2c_bus, msg->addr, msg->buf, - msg->len); - } else { - ret = i2c_write_data(i2c_bus, msg->addr, msg->buf, - msg->len, next_is_read); + start_time = get_timer(0); + while (readl(&i2c->iicstat) & I2CSTAT_BSY) { + if (get_timer(start_time) > I2C_TIMEOUT_MS) { + debug("Timeout\n"); + return -ETIMEDOUT; } - if (ret) - return -EREMOTEIO; } - return 0; + for (ret = 0, i = 0; !ret && i < nmsgs; i++) + ret = s3c24x0_do_msg(i2c_bus, &msg[i], i); + + /* Send STOP */ + writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); + read_write_byte(i2c); + + return ret ? -EREMOTEIO : 0; } static int s3c_i2c_ofdata_to_platdata(struct udevice *dev) @@ -1364,8 +1408,7 @@ static int s3c_i2c_ofdata_to_platdata(struct udevice *dev) i2c_bus->id = pinmux_decode_periph_id(blob, node); i2c_bus->clock_frequency = fdtdec_get_int(blob, node, - "clock-frequency", - CONFIG_SYS_I2C_S3C24X0_SPEED); + "clock-frequency", 100000); i2c_bus->node = node; i2c_bus->bus_num = dev->seq; @@ -1384,7 +1427,6 @@ static const struct dm_i2c_ops s3c_i2c_ops = { static const struct udevice_id s3c_i2c_ids[] = { { .compatible = "samsung,s3c2440-i2c", .data = EXYNOS_I2C_STD }, - { .compatible = "samsung,exynos5-hsi2c", .data = EXYNOS_I2C_HS }, { } }; @@ -1397,4 +1439,29 @@ U_BOOT_DRIVER(i2c_s3c) = { .priv_auto_alloc_size = sizeof(struct s3c24x0_i2c_bus), .ops = &s3c_i2c_ops, }; + +/* + * TODO(sjg@chromium.org): Move this to a separate file when everything uses + * driver model + */ +static const struct dm_i2c_ops exynos_hs_i2c_ops = { + .xfer = exynos_hs_i2c_xfer, + .probe_chip = s3c24x0_i2c_probe, + .set_bus_speed = s3c24x0_i2c_set_bus_speed, +}; + +static const struct udevice_id exynos_hs_i2c_ids[] = { + { .compatible = "samsung,exynos5-hsi2c", .data = EXYNOS_I2C_HS }, + { } +}; + +U_BOOT_DRIVER(hs_i2c) = { + .name = "i2c_s3c_hs", + .id = UCLASS_I2C, + .of_match = exynos_hs_i2c_ids, + .ofdata_to_platdata = s3c_i2c_ofdata_to_platdata, + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), + .priv_auto_alloc_size = sizeof(struct s3c24x0_i2c_bus), + .ops = &exynos_hs_i2c_ops, +}; #endif /* CONFIG_DM_I2C */ -- cgit v0.10.2 From f1ac35b7a6c6bc878d473b5955370b1cc49dba54 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:48 -0600 Subject: exynos: dts: Sync up I2C ports with the kernel The kernel uses upper case for I2C unit addresses. Follow the same convention to reduce differences. Signed-off-by: Simon Glass Acked-by: Przemyslaw Marczak diff --git a/arch/arm/dts/exynos5.dtsi b/arch/arm/dts/exynos5.dtsi index 238acb8..179584c 100644 --- a/arch/arm/dts/exynos5.dtsi +++ b/arch/arm/dts/exynos5.dtsi @@ -72,39 +72,39 @@ interrupts = <1 9 0xf04>; }; - i2c@12c60000 { - #address-cells = <1>; - #size-cells = <0>; + i2c_0: i2c@12C60000 { compatible = "samsung,s3c2440-i2c"; reg = <0x12C60000 0x100>; interrupts = <0 56 0>; - }; - - i2c@12c70000 { #address-cells = <1>; #size-cells = <0>; + }; + + i2c_1: i2c@12C70000 { compatible = "samsung,s3c2440-i2c"; reg = <0x12C70000 0x100>; interrupts = <0 57 0>; - }; - - i2c@12c80000 { #address-cells = <1>; #size-cells = <0>; + }; + + i2c_2: i2c@12C80000 { compatible = "samsung,s3c2440-i2c"; reg = <0x12C80000 0x100>; interrupts = <0 58 0>; - }; - - i2c@12c90000 { #address-cells = <1>; #size-cells = <0>; + }; + + i2c_3: i2c@12C90000 { compatible = "samsung,s3c2440-i2c"; reg = <0x12C90000 0x100>; interrupts = <0 59 0>; + #address-cells = <1>; + #size-cells = <0>; }; - spi@12d20000 { + spi_0: spi@12d20000 { #address-cells = <1>; #size-cells = <0>; compatible = "samsung,exynos-spi"; @@ -112,7 +112,7 @@ interrupts = <0 68 0>; }; - spi@12d30000 { + spi_1: spi@12d30000 { #address-cells = <1>; #size-cells = <0>; compatible = "samsung,exynos-spi"; @@ -120,7 +120,7 @@ interrupts = <0 69 0>; }; - spi@12d40000 { + spi_2: spi@12d40000 { #address-cells = <1>; #size-cells = <0>; compatible = "samsung,exynos-spi"; @@ -129,7 +129,7 @@ interrupts = <0 70 0>; }; - spi@131a0000 { + spi_3: spi@131a0000 { #address-cells = <1>; #size-cells = <0>; compatible = "samsung,exynos-spi"; @@ -137,7 +137,7 @@ interrupts = <0 129 0>; }; - spi@131b0000 { + spi_4: spi@131b0000 { #address-cells = <1>; #size-cells = <0>; compatible = "samsung,exynos-spi"; diff --git a/arch/arm/dts/exynos5250-arndale.dts b/arch/arm/dts/exynos5250-arndale.dts index 21c0a21..031c622 100644 --- a/arch/arm/dts/exynos5250-arndale.dts +++ b/arch/arm/dts/exynos5250-arndale.dts @@ -15,14 +15,14 @@ compatible = "samsung,arndale", "samsung,exynos5250"; aliases { - i2c0 = "/i2c@12c60000"; - i2c1 = "/i2c@12c70000"; - i2c2 = "/i2c@12c80000"; - i2c3 = "/i2c@12c90000"; - i2c4 = "/i2c@12ca0000"; - i2c5 = "/i2c@12cb0000"; - i2c6 = "/i2c@12cc0000"; - i2c7 = "/i2c@12cd0000"; + i2c0 = "/i2c@12C60000"; + i2c1 = "/i2c@12C70000"; + i2c2 = "/i2c@12C80000"; + i2c3 = "/i2c@12C90000"; + i2c4 = "/i2c@12CA0000"; + i2c5 = "/i2c@12CB0000"; + i2c6 = "/i2c@12CC0000"; + i2c7 = "/i2c@12CD0000"; serial0 = "/serial@12C20000"; console = "/serial@12C20000"; }; diff --git a/arch/arm/dts/exynos5250-smdk5250.dts b/arch/arm/dts/exynos5250-smdk5250.dts index 3cebfc2..636fec9 100644 --- a/arch/arm/dts/exynos5250-smdk5250.dts +++ b/arch/arm/dts/exynos5250-smdk5250.dts @@ -11,20 +11,21 @@ /dts-v1/; #include "exynos5250.dtsi" +#include / { model = "SAMSUNG SMDK5250 board based on EXYNOS5250"; compatible = "samsung,smdk5250", "samsung,exynos5250"; aliases { - i2c0 = "/i2c@12c60000"; - i2c1 = "/i2c@12c70000"; - i2c2 = "/i2c@12c80000"; - i2c3 = "/i2c@12c90000"; - i2c4 = "/i2c@12ca0000"; - i2c5 = "/i2c@12cb0000"; - i2c6 = "/i2c@12cc0000"; - i2c7 = "/i2c@12cd0000"; + i2c0 = "/i2c@12C60000"; + i2c1 = "/i2c@12C70000"; + i2c2 = "/i2c@12C80000"; + i2c3 = "/i2c@12C90000"; + i2c4 = "/i2c@12CA0000"; + i2c5 = "/i2c@12CB0000"; + i2c6 = "/i2c@12CC0000"; + i2c7 = "/i2c@12CD0000"; spi0 = "/spi@12d20000"; spi1 = "/spi@12d30000"; spi2 = "/spi@12d40000"; @@ -58,14 +59,14 @@ status = "disabled"; }; - i2c@12c70000 { + i2c@12C70000 { soundcodec@1a { reg = <0x1a>; compatible = "wolfson,wm8994-codec"; }; }; - i2c@12c60000 { + i2c@12C60000 { pmic@9 { reg = <0x9>; compatible = "maxim,max77686"; diff --git a/arch/arm/dts/exynos5250-snow.dts b/arch/arm/dts/exynos5250-snow.dts index e4b3dc2..9abe0b7 100644 --- a/arch/arm/dts/exynos5250-snow.dts +++ b/arch/arm/dts/exynos5250-snow.dts @@ -7,24 +7,27 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. -*/ + */ /dts-v1/; +#include +#include +#include #include "exynos5250.dtsi" / { model = "Google Snow"; - compatible = "google,snow", "samsung,exynos5250"; + compatible = "google,snow", "samsung,exynos5250", "samsung,exynos5"; aliases { - i2c0 = "/i2c@12c60000"; - i2c1 = "/i2c@12c70000"; - i2c2 = "/i2c@12c80000"; - i2c3 = "/i2c@12c90000"; - i2c4 = "/i2c@12ca0000"; - i2c5 = "/i2c@12cb0000"; - i2c6 = "/i2c@12cc0000"; - i2c7 = "/i2c@12cd0000"; + i2c0 = "/i2c@12C60000"; + i2c1 = "/i2c@12C70000"; + i2c2 = "/i2c@12C80000"; + i2c3 = "/i2c@12C90000"; + i2c4 = "/i2c@12CA0000"; + i2c5 = "/i2c@12CB0000"; + i2c6 = "/i2c@12CC0000"; + i2c7 = "/i2c@12CD0000"; spi0 = "/spi@12d20000"; spi1 = "/spi@12d30000"; spi2 = "/spi@12d40000"; @@ -39,7 +42,7 @@ i2s = "/sound@3830000"; }; - i2c4: i2c@12ca0000 { + i2c4: i2c@12CA0000 { cros_ec: cros-ec@1e { reg = <0x1e>; compatible = "google,cros-ec-i2c"; @@ -66,6 +69,7 @@ spi-max-frequency = <1000000>; spi-deactivate-delay = <100>; + /* Snow did support SPI but the released version used I2C */ embedded-controller { compatible = "google,cros-ec-i2c"; reg = <0x1e>; @@ -85,7 +89,7 @@ status = "disabled"; }; - i2c@12cd0000 { + i2c@12CD0000 { soundcodec@22 { reg = <0x22>; compatible = "maxim,max98095-codec"; @@ -105,8 +109,8 @@ }; }; - i2c@12c60000 { - pmic@9 { + i2c@12C60000 { + max77686@09 { reg = <0x9>; compatible = "maxim,max77686"; }; diff --git a/arch/arm/dts/exynos5250.dtsi b/arch/arm/dts/exynos5250.dtsi index ccbafe9..7eef3e3 100644 --- a/arch/arm/dts/exynos5250.dtsi +++ b/arch/arm/dts/exynos5250.dtsi @@ -47,36 +47,36 @@ interrupts = <0 47 0>; }; - i2c@12ca0000 { - #address-cells = <1>; - #size-cells = <0>; + i2c_4: i2c@12CA0000 { compatible = "samsung,s3c2440-i2c"; reg = <0x12CA0000 0x100>; interrupts = <0 60 0>; - }; - - i2c@12cb0000 { #address-cells = <1>; #size-cells = <0>; + }; + + i2c_5: i2c@12CB0000 { compatible = "samsung,s3c2440-i2c"; reg = <0x12CB0000 0x100>; interrupts = <0 61 0>; - }; - - i2c@12cc0000 { #address-cells = <1>; #size-cells = <0>; + }; + + i2c_6: i2c@12CC0000 { compatible = "samsung,s3c2440-i2c"; reg = <0x12CC0000 0x100>; interrupts = <0 62 0>; - }; - - i2c@12cd0000 { #address-cells = <1>; #size-cells = <0>; + }; + + i2c_7: i2c@12CD0000 { compatible = "samsung,s3c2440-i2c"; reg = <0x12CD0000 0x100>; interrupts = <0 63 0>; + #address-cells = <1>; + #size-cells = <0>; }; sound@3830000 { diff --git a/arch/arm/dts/exynos5420-peach-pit.dts b/arch/arm/dts/exynos5420-peach-pit.dts index 6fe762d..0f1002e 100644 --- a/arch/arm/dts/exynos5420-peach-pit.dts +++ b/arch/arm/dts/exynos5420-peach-pit.dts @@ -25,7 +25,7 @@ aliases { serial0 = "/serial@12C30000"; console = "/serial@12C30000"; - pmic = "/i2c@12ca0000"; + pmic = "/i2c@12CA0000"; }; dmc { @@ -49,7 +49,7 @@ }; /* MAX77802 is on i2c bus 4 */ - i2c@12ca0000 { + i2c@12CA0000 { clock-frequency = <400000>; power-regulator@9 { compatible = "maxim,max77802-pmic"; @@ -57,7 +57,7 @@ }; }; - i2c@12cd0000 { /* i2c7 */ + i2c@12CD0000 { /* i2c7 */ clock-frequency = <100000>; soundcodec@20 { reg = <0x20>; @@ -76,7 +76,7 @@ samsung,codec-type = "max98090"; }; - i2c@12e10000 { /* i2c9 */ + i2c@12E10000 { /* i2c9 */ clock-frequency = <400000>; tpm@20 { compatible = "infineon,slb9645tt"; diff --git a/arch/arm/dts/exynos5420-smdk5420.dts b/arch/arm/dts/exynos5420-smdk5420.dts index 6855027..015ff15 100644 --- a/arch/arm/dts/exynos5420-smdk5420.dts +++ b/arch/arm/dts/exynos5420-smdk5420.dts @@ -37,7 +37,7 @@ }; /* s2mps11 is on i2c bus 4 */ - i2c@12ca0000 { + i2c@12CA0000 { #address-cells = <1>; #size-cells = <0>; pmic@66 { @@ -82,7 +82,7 @@ samsung,codec-type = "wm8994"; }; - i2c@12c70000 { + i2c@12C70000 { soundcodec@1a { reg = <0x1a>; compatible = "wolfson,wm8994-codec"; diff --git a/arch/arm/dts/exynos54xx.dtsi b/arch/arm/dts/exynos54xx.dtsi index 31fabb1..bd3619d 100644 --- a/arch/arm/dts/exynos54xx.dtsi +++ b/arch/arm/dts/exynos54xx.dtsi @@ -14,17 +14,17 @@ }; aliases { - i2c0 = "/i2c@12c60000"; - i2c1 = "/i2c@12c70000"; - i2c2 = "/i2c@12c80000"; - i2c3 = "/i2c@12c90000"; - i2c4 = "/i2c@12ca0000"; - i2c5 = "/i2c@12cb0000"; - i2c6 = "/i2c@12cc0000"; - i2c7 = "/i2c@12cd0000"; - i2c8 = "/i2c@12e00000"; - i2c9 = "/i2c@12e10000"; - i2c10 = "/i2c@12e20000"; + i2c0 = "/i2c@12C60000"; + i2c1 = "/i2c@12C70000"; + i2c2 = "/i2c@12C80000"; + i2c3 = "/i2c@12C90000"; + i2c4 = "/i2c@12CA0000"; + i2c5 = "/i2c@12CB0000"; + i2c6 = "/i2c@12CC0000"; + i2c7 = "/i2c@12CD0000"; + i2c8 = "/i2c@12E00000"; + i2c9 = "/i2c@12E10000"; + i2c10 = "/i2c@12E20000"; pinctrl0 = &pinctrl_0; pinctrl1 = &pinctrl_1; pinctrl2 = &pinctrl_2; @@ -42,7 +42,7 @@ xhci1 = "/xhci@12400000"; }; - i2c@12ca0000 { + i2c@12CA0000 { #address-cells = <1>; #size-cells = <0>; compatible = "samsung,exynos5-hsi2c"; @@ -50,7 +50,7 @@ interrupts = <0 60 0>; }; - i2c@12cb0000 { + i2c@12CB0000 { #address-cells = <1>; #size-cells = <0>; compatible = "samsung,exynos5-hsi2c"; @@ -58,7 +58,7 @@ interrupts = <0 61 0>; }; - i2c@12cc0000 { + i2c@12CC0000 { #address-cells = <1>; #size-cells = <0>; compatible = "samsung,exynos5-hsi2c"; @@ -66,7 +66,7 @@ interrupts = <0 62 0>; }; - i2c@12cd0000 { + i2c@12CD0000 { #address-cells = <1>; #size-cells = <0>; compatible = "samsung,exynos5-hsi2c"; @@ -74,7 +74,7 @@ interrupts = <0 63 0>; }; - i2c@12e00000 { + i2c@12E00000 { #address-cells = <1>; #size-cells = <0>; compatible = "samsung,exynos5-hsi2c"; @@ -82,7 +82,7 @@ interrupts = <0 87 0>; }; - i2c@12e10000 { + i2c@12E10000 { #address-cells = <1>; #size-cells = <0>; compatible = "samsung,exynos5-hsi2c"; @@ -90,7 +90,7 @@ interrupts = <0 88 0>; }; - i2c@12e20000 { + i2c@12E20000 { #address-cells = <1>; #size-cells = <0>; compatible = "samsung,exynos5-hsi2c"; diff --git a/arch/arm/dts/exynos5800-peach-pi.dts b/arch/arm/dts/exynos5800-peach-pi.dts index 176ce55..4e548f7 100644 --- a/arch/arm/dts/exynos5800-peach-pi.dts +++ b/arch/arm/dts/exynos5800-peach-pi.dts @@ -25,7 +25,7 @@ aliases { serial0 = "/serial@12C30000"; console = "/serial@12C30000"; - pmic = "/i2c@12ca0000"; + pmic = "/i2c@12CA0000"; }; dmc { @@ -49,7 +49,7 @@ }; /* MAX77802 is on i2c bus 4 */ - i2c@12ca0000 { + i2c@12CA0000 { clock-frequency = <400000>; power-regulator@9 { compatible = "maxim,max77802-pmic"; @@ -57,7 +57,7 @@ }; }; - i2c@12cd0000 { /* i2c7 */ + i2c@12CD0000 { /* i2c7 */ clock-frequency = <100000>; soundcodec@20 { reg = <0x20>; @@ -69,7 +69,7 @@ samsung,codec-type = "max98090"; }; - i2c@12e10000 { /* i2c9 */ + i2c@12E10000 { /* i2c9 */ clock-frequency = <400000>; tpm@20 { compatible = "infineon,slb9645tt"; -- cgit v0.10.2 From 1a17c39c3a23f3675913fb768b6e8cbbc6ee4bd0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:49 -0600 Subject: exynos: dts: Add PMIC and regulator definitions Snow and smdk5250 use a max77686 PMIC. We have a driver for this, so add the relevant node to the device tree so it can be used. Signed-off-by: Simon Glass Acked-by: Przemyslaw Marczak diff --git a/arch/arm/dts/exynos5250-smdk5250.dts b/arch/arm/dts/exynos5250-smdk5250.dts index 636fec9..8b69544 100644 --- a/arch/arm/dts/exynos5250-smdk5250.dts +++ b/arch/arm/dts/exynos5250-smdk5250.dts @@ -150,3 +150,153 @@ samsung,vbus-gpio = <&gpx2 6 GPIO_ACTIVE_HIGH>; }; }; + +&i2c_0 { + status = "okay"; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <20000>; + + max77686@09 { + compatible = "maxim,max77686"; + reg = <0x09>; + interrupt-parent = <&gpx3>; + interrupts = <2 IRQ_TYPE_NONE>; + + voltage-regulators { + ldo1_reg: LDO1 { + regulator-name = "P1.0V_LDO_OUT1"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + ldo2_reg: LDO2 { + regulator-name = "P1.2V_LDO_OUT2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; + + ldo3_reg: LDO3 { + regulator-name = "P1.8V_LDO_OUT3"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo4_reg: LDO4 { + regulator-name = "P2.8V_LDO_OUT4"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + + ldo5_reg: LDO5 { + regulator-name = "P1.8V_LDO_OUT5"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo6_reg: LDO6 { + regulator-name = "P1.1V_LDO_OUT6"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + ldo7_reg: LDO7 { + regulator-name = "P1.1V_LDO_OUT7"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + ldo8_reg: LDO8 { + regulator-name = "P1.0V_LDO_OUT8"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + }; + + ldo10_reg: LDO10 { + regulator-name = "P1.8V_LDO_OUT10"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo11_reg: LDO11 { + regulator-name = "P1.8V_LDO_OUT11"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo12_reg: LDO12 { + regulator-name = "P3.0V_LDO_OUT12"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + }; + + ldo13_reg: LDO13 { + regulator-name = "P1.8V_LDO_OUT13"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo14_reg: LDO14 { + regulator-name = "P1.8V_LDO_OUT14"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo15_reg: LDO15 { + regulator-name = "P1.0V_LDO_OUT15"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + }; + + ldo16_reg: LDO16 { + regulator-name = "P1.8V_LDO_OUT16"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + buck1_reg: BUCK1 { + regulator-name = "vdd_mif"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + regulator-boot-on; + }; + + buck2_reg: BUCK2 { + regulator-name = "vdd_arm"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + + buck3_reg: BUCK3 { + regulator-name = "vdd_int"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + }; + + buck4_reg: BUCK4 { + regulator-name = "vdd_g3d"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + regulator-boot-on; + }; + + buck5_reg: BUCK5 { + regulator-name = "P1.8V_BUCK_OUT5"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; +}; diff --git a/arch/arm/dts/exynos5250-snow.dts b/arch/arm/dts/exynos5250-snow.dts index 9abe0b7..06d675b 100644 --- a/arch/arm/dts/exynos5250-snow.dts +++ b/arch/arm/dts/exynos5250-snow.dts @@ -203,4 +203,158 @@ }; +&i2c_0 { + status = "okay"; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <378000>; + + max77686: max77686@09 { + compatible = "maxim,max77686"; + interrupt-parent = <&gpx3>; + interrupts = <2 IRQ_TYPE_NONE>; + wakeup-source; + reg = <0x09>; + #clock-cells = <1>; + + voltage-regulators { + ldo1_reg: LDO1 { + regulator-name = "P1.0V_LDO_OUT1"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + ldo2_reg: LDO2 { + regulator-name = "P1.8V_LDO_OUT2"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo3_reg: LDO3 { + regulator-name = "P1.8V_LDO_OUT3"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo7_reg: LDO7 { + regulator-name = "P1.1V_LDO_OUT7"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + ldo8_reg: LDO8 { + regulator-name = "P1.0V_LDO_OUT8"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + ldo10_reg: LDO10 { + regulator-name = "P1.8V_LDO_OUT10"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo12_reg: LDO12 { + regulator-name = "P3.0V_LDO_OUT12"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + }; + + ldo14_reg: LDO14 { + regulator-name = "P1.8V_LDO_OUT14"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo15_reg: LDO15 { + regulator-name = "P1.0V_LDO_OUT15"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + ldo16_reg: LDO16 { + regulator-name = "P1.8V_LDO_OUT16"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo17_reg: LDO17 { + regulator-name = "vdd_mydp"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + buck1_reg: BUCK1 { + regulator-name = "vdd_mif"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + regulator-boot-on; + }; + + buck2_reg: BUCK2 { + regulator-name = "vdd_arm"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + + buck3_reg: BUCK3 { + regulator-name = "vdd_int"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + }; + + buck4_reg: BUCK4 { + regulator-name = "vdd_g3d"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + regulator-boot-on; + }; + + buck5_reg: BUCK5 { + regulator-name = "P1.8V_BUCK_OUT5"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + buck6_reg: BUCK6 { + regulator-name = "P1.35V_BUCK_OUT6"; + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + }; + + buck7_reg: BUCK7 { + regulator-name = "P2.0V_BUCK_OUT7"; + regulator-min-microvolt = <2000000>; + regulator-max-microvolt = <2000000>; + regulator-always-on; + }; + + buck8_reg: BUCK8 { + regulator-name = "P2.85V_BUCK_OUT8"; + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; + regulator-always-on; + }; + }; + }; +}; + #include "cros-ec-keyboard.dtsi" -- cgit v0.10.2 From a0942a6d3e08490be2871396c977fb5520ecdf33 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:50 -0600 Subject: exynos: dts: Support EC tunnel and main TPS65090 regulator On pit and pi the TPS65090 regulator is connected only to the EC and we must use a tunnel to get to it. The existing U-Boot support relies on a special driver. Add a tunnel definition so that the new device-model TPS65090 driver can be used unmodified. Signed-off-by: Simon Glass diff --git a/arch/arm/dts/exynos5250-snow.dts b/arch/arm/dts/exynos5250-snow.dts index 06d675b..653efb4 100644 --- a/arch/arm/dts/exynos5250-snow.dts +++ b/arch/arm/dts/exynos5250-snow.dts @@ -25,6 +25,7 @@ i2c2 = "/i2c@12C80000"; i2c3 = "/i2c@12C90000"; i2c4 = "/i2c@12CA0000"; + i2c104 = &i2c_104; i2c5 = "/i2c@12CB0000"; i2c6 = "/i2c@12CC0000"; i2c7 = "/i2c@12CD0000"; @@ -43,7 +44,7 @@ }; i2c4: i2c@12CA0000 { - cros_ec: cros-ec@1e { + cros_ec_old: cros-ec@1e { reg = <0x1e>; compatible = "google,cros-ec-i2c"; i2c-max-frequency = <100000>; @@ -57,6 +58,101 @@ }; }; + i2c-arbitrator { + compatible = "i2c-arb-gpio-challenge"; + #address-cells = <1>; + #size-cells = <0>; + + i2c-parent = <&{/i2c@12CA0000}>; + + our-claim-gpio = <&gpf0 3 GPIO_ACTIVE_LOW>; + their-claim-gpios = <&gpe0 4 GPIO_ACTIVE_LOW>; + slew-delay-us = <10>; + wait-retry-us = <3000>; + wait-free-us = <50000>; + + /* Use ID 104 as a hint that we're on physical bus 4 */ + i2c_104: i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + battery: sbs-battery@b { + compatible = "sbs,sbs-battery"; + reg = <0xb>; + sbs,poll-retry-count = <1>; + }; + + cros_ec: embedded-controller { + compatible = "google,cros-ec-i2c"; + reg = <0x1e>; + interrupts = <6 IRQ_TYPE_NONE>; + interrupt-parent = <&gpx1>; + wakeup-source; + i2c-max-frequency = <100000>; + u-boot,i2c-offset-len = <0>; + ec-interrupt = <&gpx1 6 GPIO_ACTIVE_LOW>; + }; + + power-regulator { + compatible = "ti,tps65090"; + reg = <0x48>; + + regulators { + dcdc1 { + ti,enable-ext-control; + }; + dcdc2 { + ti,enable-ext-control; + }; + dcdc3 { + ti,enable-ext-control; + }; + fet1: fet1 { + regulator-name = "vcd_led"; + ti,overcurrent-wait = <3>; + }; + tps65090_fet2: fet2 { + regulator-name = "video_mid"; + regulator-always-on; + ti,overcurrent-wait = <3>; + }; + fet3 { + regulator-name = "wwan_r"; + regulator-always-on; + ti,overcurrent-wait = <3>; + }; + fet4 { + regulator-name = "sdcard"; + ti,overcurrent-wait = <3>; + }; + fet5 { + regulator-name = "camout"; + regulator-always-on; + ti,overcurrent-wait = <3>; + }; + fet6: fet6 { + regulator-name = "lcd_vdd"; + ti,overcurrent-wait = <3>; + }; + tps65090_fet7: fet7 { + regulator-name = "video_mid_1a"; + regulator-always-on; + ti,overcurrent-wait = <3>; + }; + ldo1 { + }; + ldo2 { + }; + }; + + charger { + compatible = "ti,tps65090-charger"; + }; + }; + }; + }; + spi@12d30000 { spi-max-frequency = <50000000>; firmware_storage_spi: flash@0 { diff --git a/arch/arm/dts/exynos5420-peach-pit.dts b/arch/arm/dts/exynos5420-peach-pit.dts index 0f1002e..5182b2b 100644 --- a/arch/arm/dts/exynos5420-peach-pit.dts +++ b/arch/arm/dts/exynos5420-peach-pit.dts @@ -26,6 +26,7 @@ serial0 = "/serial@12C30000"; console = "/serial@12C30000"; pmic = "/i2c@12CA0000"; + i2c104 = &i2c_tunnel; }; dmc { @@ -101,30 +102,6 @@ }; }; - spi@12d40000 { /* spi2 */ - spi-max-frequency = <4000000>; - spi-deactivate-delay = <200>; - - cros_ec: cros-ec@0 { - compatible = "google,cros-ec-spi"; - reg = <0>; - spi-half-duplex; - spi-max-timeout-ms = <1100>; - ec-interrupt = <&gpx1 5 GPIO_ACTIVE_LOW>; - - /* - * This describes the flash memory within the EC. Note - * that the STM32L flash erases to 0, not 0xff. - */ - #address-cells = <1>; - #size-cells = <1>; - flash@8000000 { - reg = <0x08000000 0x20000>; - erase-value = <0>; - }; - }; - }; - xhci@12000000 { samsung,vbus-gpio = <&gph0 0 GPIO_ACTIVE_HIGH>; }; @@ -159,4 +136,103 @@ }; }; +&spi_2 { + spi-max-frequency = <3125000>; + spi-deactivate-delay = <200>; + status = "okay"; + num-cs = <1>; + samsung,spi-src-clk = <0>; + cs-gpios = <&gpb1 2 0>; + + cros_ec: cros-ec@0 { + compatible = "google,cros-ec-spi"; + interrupt-parent = <&gpx1>; + interrupts = <5 0>; + reg = <0>; + spi-half-duplex; + spi-max-timeout-ms = <1100>; + ec-interrupt = <&gpx1 5 GPIO_ACTIVE_LOW>; + #address-cells = <1>; + #size-cells = <1>; + + /* + * This describes the flash memory within the EC. Note + * that the STM32L flash erases to 0, not 0xff. + */ + flash@8000000 { + reg = <0x08000000 0x20000>; + erase-value = <0>; + }; + + controller-data { + samsung,spi-feedback-delay = <1>; + }; + + i2c_tunnel: i2c-tunnel { + compatible = "google,cros-ec-i2c-tunnel"; + #address-cells = <1>; + #size-cells = <0>; + google,remote-bus = <0>; + + battery: sbs-battery@b { + compatible = "sbs,sbs-battery"; + reg = <0xb>; + sbs,poll-retry-count = <1>; + sbs,i2c-retry-count = <2>; + }; + + power-regulator@48 { + compatible = "ti,tps65090"; + reg = <0x48>; + + regulators { + tps65090_dcdc1: dcdc1 { + ti,enable-ext-control; + }; + tps65090_dcdc2: dcdc2 { + ti,enable-ext-control; + }; + tps65090_dcdc3: dcdc3 { + ti,enable-ext-control; + }; + tps65090_fet1: fet1 { + regulator-name = "vcd_led"; + }; + tps65090_fet2: fet2 { + regulator-name = "video_mid"; + regulator-always-on; + }; + tps65090_fet3: fet3 { + regulator-name = "wwan_r"; + regulator-always-on; + }; + tps65090_fet4: fet4 { + regulator-name = "sdcard"; + regulator-always-on; + }; + tps65090_fet5: fet5 { + regulator-name = "camout"; + regulator-always-on; + }; + tps65090_fet6: fet6 { + regulator-name = "lcd_vdd"; + }; + tps65090_fet7: fet7 { + regulator-name = "video_mid_1a"; + regulator-always-on; + }; + tps65090_ldo1: ldo1 { + }; + tps65090_ldo2: ldo2 { + }; + }; + + charger { + compatible = "ti,tps65090-charger"; + }; + }; + }; + }; +}; + #include "cros-ec-keyboard.dtsi" diff --git a/arch/arm/dts/exynos5800-peach-pi.dts b/arch/arm/dts/exynos5800-peach-pi.dts index 4e548f7..600c294 100644 --- a/arch/arm/dts/exynos5800-peach-pi.dts +++ b/arch/arm/dts/exynos5800-peach-pi.dts @@ -26,6 +26,7 @@ serial0 = "/serial@12C30000"; console = "/serial@12C30000"; pmic = "/i2c@12CA0000"; + i2c104 = &i2c_tunnel; }; dmc { @@ -93,29 +94,6 @@ }; }; - spi@12d40000 { /* spi2 */ - spi-max-frequency = <4000000>; - spi-deactivate-delay = <200>; - cros_ec: cros-ec@0 { - compatible = "google,cros-ec-spi"; - reg = <0>; - spi-half-duplex; - spi-max-timeout-ms = <1100>; - ec-interrupt = <&gpx1 5 GPIO_ACTIVE_LOW>; - - /* - * This describes the flash memory within the EC. Note - * that the STM32L flash erases to 0, not 0xff. - */ - #address-cells = <1>; - #size-cells = <1>; - flash@8000000 { - reg = <0x08000000 0x20000>; - erase-value = <0>; - }; - }; - }; - xhci@12000000 { samsung,vbus-gpio = <&gph0 0 GPIO_ACTIVE_HIGH>; }; @@ -153,4 +131,103 @@ }; }; +&spi_2 { + spi-max-frequency = <3125000>; + spi-deactivate-delay = <200>; + status = "okay"; + num-cs = <1>; + samsung,spi-src-clk = <0>; + cs-gpios = <&gpb1 2 0>; + + cros_ec: cros-ec@0 { + compatible = "google,cros-ec-spi"; + interrupt-parent = <&gpx1>; + interrupts = <5 0>; + reg = <0>; + spi-half-duplex; + spi-max-timeout-ms = <1100>; + ec-interrupt = <&gpx1 5 GPIO_ACTIVE_LOW>; + + /* + * This describes the flash memory within the EC. Note + * that the STM32L flash erases to 0, not 0xff. + */ + #address-cells = <1>; + #size-cells = <1>; + flash@8000000 { + reg = <0x08000000 0x20000>; + erase-value = <0>; + }; + + controller-data { + samsung,spi-feedback-delay = <1>; + }; + + i2c_tunnel: i2c-tunnel { + compatible = "google,cros-ec-i2c-tunnel"; + #address-cells = <1>; + #size-cells = <0>; + google,remote-bus = <0>; + + battery: sbs-battery@b { + compatible = "sbs,sbs-battery"; + reg = <0xb>; + sbs,poll-retry-count = <1>; + sbs,i2c-retry-count = <2>; + }; + + power-regulator@48 { + compatible = "ti,tps65090"; + reg = <0x48>; + + regulators { + tps65090_dcdc1: dcdc1 { + ti,enable-ext-control; + }; + tps65090_dcdc2: dcdc2 { + ti,enable-ext-control; + }; + tps65090_dcdc3: dcdc3 { + ti,enable-ext-control; + }; + tps65090_fet1: fet1 { + regulator-name = "vcd_led"; + }; + tps65090_fet2: fet2 { + regulator-name = "video_mid"; + regulator-always-on; + }; + tps65090_fet3: fet3 { + regulator-name = "wwan_r"; + regulator-always-on; + }; + tps65090_fet4: fet4 { + regulator-name = "sdcard"; + regulator-always-on; + }; + tps65090_fet5: fet5 { + regulator-name = "camout"; + regulator-always-on; + }; + tps65090_fet6: fet6 { + regulator-name = "lcd_vdd"; + }; + tps65090_fet7: fet7 { + regulator-name = "video_mid_1a"; + regulator-always-on; + }; + tps65090_ldo1: ldo1 { + }; + tps65090_ldo2: ldo2 { + }; + }; + + charger { + compatible = "ti,tps65090-charger"; + }; + }; + }; + }; +}; + #include "cros-ec-keyboard.dtsi" -- cgit v0.10.2 From cc456bd7df06225819258dec9d4a5047e8da4952 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:23 -0600 Subject: dm: cros_ec: Convert the I2C tunnel code to use driver model The Chrome OS EC supports tunnelling through to an I2C bus on the EC. This currently uses a copy of the I2C command code and a special 'crosec' sub-command. With driver model we can define an I2C bus which tunnels through to the EC, and use the normal 'i2c' command to access it. This simplifies the code and removes some duplication. Add an I2C driver which tunnels through to the EC. Adjust the EC code to support binding child devices so that it can be set up. Adjust the existing I2C xfer function to fit driver model better. For now the old code remains to allow things to still work. It will be removed in a later patch once the new flow is fully enabled. Signed-off-by: Simon Glass diff --git a/configs/peach-pi_defconfig b/configs/peach-pi_defconfig index c17fc73..5050f5b 100644 --- a/configs/peach-pi_defconfig +++ b/configs/peach-pi_defconfig @@ -12,3 +12,5 @@ CONFIG_CROS_EC_SPI=y CONFIG_CROS_EC_KEYB=y CONFIG_USB=y CONFIG_DM_USB=y +CONFIG_I2C_MUX=y +CONFIG_I2C_CROS_EC_TUNNEL=y diff --git a/configs/peach-pit_defconfig b/configs/peach-pit_defconfig index 8f21722..d19bff2 100644 --- a/configs/peach-pit_defconfig +++ b/configs/peach-pit_defconfig @@ -12,3 +12,5 @@ CONFIG_CROS_EC_SPI=y CONFIG_CROS_EC_KEYB=y CONFIG_USB=y CONFIG_DM_USB=y +CONFIG_I2C_MUX=y +CONFIG_I2C_CROS_EC_TUNNEL=y diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index caee3d8..e861b53 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -19,6 +19,17 @@ config DM_I2C_COMPAT to convert all code for a board in a single commit. It should not be enabled for any board in an official release. +config I2C_CROS_EC_TUNNEL + tristate "Chrome OS EC tunnel I2C bus" + depends on CROS_EC + help + This provides an I2C bus that will tunnel i2c commands through to + the other side of the Chrome OS EC to the I2C bus connected there. + This will work whatever the interface used to talk to the EC (SPI, + I2C or LPC). Some Chromebooks use this when the hardware design + does not allow direct access to the main PMIC from the AP. + + config DM_I2C_GPIO bool "Enable Driver Model for software emulated I2C bus driver" depends on DM_I2C && DM_GPIO diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index dc9e81b..7f01fce 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_DM_I2C) += i2c-uclass.o obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o obj-$(CONFIG_DM_I2C_GPIO) += i2c-gpio.o +obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o diff --git a/drivers/i2c/cros_ec_tunnel.c b/drivers/i2c/cros_ec_tunnel.c new file mode 100644 index 0000000..7ab1fd8 --- /dev/null +++ b/drivers/i2c/cros_ec_tunnel.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +static int cros_ec_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) +{ + return 0; +} + +static int cros_ec_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, + int nmsgs) +{ + return cros_ec_i2c_tunnel(dev->parent, msg, nmsgs); +} + +static const struct dm_i2c_ops cros_ec_i2c_ops = { + .xfer = cros_ec_i2c_xfer, + .set_bus_speed = cros_ec_i2c_set_bus_speed, +}; + +static const struct udevice_id cros_ec_i2c_ids[] = { + { .compatible = "google,cros-ec-i2c-tunnel" }, + { } +}; + +U_BOOT_DRIVER(cros_ec_tunnel) = { + .name = "cros_ec_tunnel", + .id = UCLASS_I2C, + .of_match = cros_ec_i2c_ids, + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), + .ops = &cros_ec_i2c_ops, +}; diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c index 4b6ac6a..ae52561 100644 --- a/drivers/misc/cros_ec.c +++ b/drivers/misc/cros_ec.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #ifdef DEBUG_TRACE @@ -1053,8 +1054,8 @@ int cros_ec_decode_ec_flash(const void *blob, int node, return 0; } -int cros_ec_i2c_xfer(struct cros_ec_dev *dev, uchar chip, uint addr, - int alen, uchar *buffer, int len, int is_read) +int cros_ec_i2c_xfer_old(struct cros_ec_dev *dev, uchar chip, uint addr, + int alen, uchar *buffer, int len, int is_read) { union { struct ec_params_i2c_passthru p; @@ -1134,6 +1135,81 @@ int cros_ec_i2c_xfer(struct cros_ec_dev *dev, uchar chip, uint addr, return 0; } +int cros_ec_i2c_tunnel(struct udevice *dev, struct i2c_msg *in, int nmsgs) +{ + struct cros_ec_dev *cdev = dev_get_uclass_priv(dev); + union { + struct ec_params_i2c_passthru p; + uint8_t outbuf[EC_PROTO2_MAX_PARAM_SIZE]; + } params; + union { + struct ec_response_i2c_passthru r; + uint8_t inbuf[EC_PROTO2_MAX_PARAM_SIZE]; + } response; + struct ec_params_i2c_passthru *p = ¶ms.p; + struct ec_response_i2c_passthru *r = &response.r; + struct ec_params_i2c_passthru_msg *msg; + uint8_t *pdata, *read_ptr = NULL; + int read_len; + int size; + int rv; + int i; + + p->port = 0; + + p->num_msgs = nmsgs; + size = sizeof(*p) + p->num_msgs * sizeof(*msg); + + /* Create a message to write the register address and optional data */ + pdata = (uint8_t *)p + size; + + read_len = 0; + for (i = 0, msg = p->msg; i < nmsgs; i++, msg++, in++) { + bool is_read = in->flags & I2C_M_RD; + + msg->addr_flags = in->addr; + msg->len = in->len; + if (is_read) { + msg->addr_flags |= EC_I2C_FLAG_READ; + read_len += in->len; + read_ptr = in->buf; + if (sizeof(*r) + read_len > sizeof(response)) { + puts("Read length too big for buffer\n"); + return -1; + } + } else { + if (pdata - (uint8_t *)p + in->len > sizeof(params)) { + puts("Params too large for buffer\n"); + return -1; + } + memcpy(pdata, in->buf, in->len); + pdata += in->len; + } + } + + rv = ec_command(cdev, EC_CMD_I2C_PASSTHRU, 0, p, pdata - (uint8_t *)p, + r, sizeof(*r) + read_len); + if (rv < 0) + return rv; + + /* Parse response */ + if (r->i2c_status & EC_I2C_STATUS_ERROR) { + printf("Transfer failed with status=0x%x\n", r->i2c_status); + return -1; + } + + if (rv < sizeof(*r) + read_len) { + puts("Truncated read response\n"); + return -1; + } + + /* We only support a single read message for each transfer */ + if (read_len) + memcpy(read_ptr, r->data, read_len); + + return 0; +} + #ifdef CONFIG_CMD_CROS_EC /** @@ -1267,8 +1343,8 @@ static int cros_ec_i2c_md(struct cros_ec_dev *dev, int flag, int argc, linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes; - if (cros_ec_i2c_xfer(dev, chip, addr, alen, linebuf, linebytes, - 1)) + if (cros_ec_i2c_xfer_old(dev, chip, addr, alen, linebuf, + linebytes, 1)) puts("Error reading the chip.\n"); else { printf("%04x:", addr); @@ -1333,7 +1409,7 @@ static int cros_ec_i2c_mw(struct cros_ec_dev *dev, int flag, int argc, count = 1; while (count-- > 0) { - if (cros_ec_i2c_xfer(dev, chip, addr++, alen, &byte, 1, 0)) + if (cros_ec_i2c_xfer_old(dev, chip, addr++, alen, &byte, 1, 0)) puts("Error writing the chip.\n"); /* * Wait for the write to complete. The write can take @@ -1633,6 +1709,12 @@ static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return ret; } +int cros_ec_post_bind(struct udevice *dev) +{ + /* Scan for available EC devices (e.g. I2C tunnel) */ + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); +} + U_BOOT_CMD( crosec, 6, 1, do_cros_ec, "CROS-EC utility command", @@ -1661,4 +1743,5 @@ UCLASS_DRIVER(cros_ec) = { .id = UCLASS_CROS_EC, .name = "cros_ec", .per_device_auto_alloc_size = sizeof(struct cros_ec_dev), + .post_bind = cros_ec_post_bind, }; diff --git a/drivers/power/pmic/pmic_tps65090_ec.c b/drivers/power/pmic/pmic_tps65090_ec.c index ac0d44f..f79a878 100644 --- a/drivers/power/pmic/pmic_tps65090_ec.c +++ b/drivers/power/pmic/pmic_tps65090_ec.c @@ -56,8 +56,8 @@ enum { */ static int tps65090_read(u32 reg, u8 *val) { - return cros_ec_i2c_xfer(config.dev, TPS65090_ADDR, reg, 1, - val, 1, true); + return cros_ec_i2c_xfer_old(config.dev, TPS65090_ADDR, reg, 1, + val, 1, true); } /** @@ -69,8 +69,8 @@ static int tps65090_read(u32 reg, u8 *val) */ static int tps65090_write(u32 reg, u8 val) { - return cros_ec_i2c_xfer(config.dev, TPS65090_ADDR, reg, 1, - &val, 1, false); + return cros_ec_i2c_xfer_old(config.dev, TPS65090_ADDR, reg, 1, + &val, 1, false); } /** diff --git a/include/cros_ec.h b/include/cros_ec.h index 3b2be2c..41951c3 100644 --- a/include/cros_ec.h +++ b/include/cros_ec.h @@ -390,6 +390,16 @@ int cros_ec_decode_ec_flash(const void *blob, int node, */ void cros_ec_check_keyboard(struct cros_ec_dev *dev); +struct i2c_msg; +/* + * Tunnel an I2C transfer to the EC + * + * @param dev CROS-EC device + * @param msg List of messages to transfer + * @param nmsgs Number of messages to transfer + */ +int cros_ec_i2c_tunnel(struct udevice *dev, struct i2c_msg *msg, int nmsgs); + /* * Tunnel an I2C transfer to the EC * @@ -401,7 +411,7 @@ void cros_ec_check_keyboard(struct cros_ec_dev *dev); * @param len Length of buffer * @param is_read 1 if this is a read, 0 if this is a write */ -int cros_ec_i2c_xfer(struct cros_ec_dev *dev, uchar chip, uint addr, - int alen, uchar *buffer, int len, int is_read); +int cros_ec_i2c_xfer_old(struct cros_ec_dev *dev, uchar chip, uint addr, + int alen, uchar *buffer, int len, int is_read); #endif -- cgit v0.10.2 From f48eaf01b2f7212987166aae970b895c7e215466 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:24 -0600 Subject: cros_ec: Support the LDO access method used by spring Add a driver to support the special LDO access used by spring. This is a custom method in the cros_ec protocol - it does not use an I2C pass-through. There are two implementation choices: 1. Write a special LDO driver which can talk across the EC. Duplicate all the logic from TPS65090 for retrying when the LDO fails to come up. 2. Write a special I2C bus driver which pretends to be a TPS65090 and transfers reads and writes using the LDO message. Either is distasteful. The latter method is chosen since it results in less code duplication and a fairly simple (30-line) implementation of the core logic. The crosec 'ldo' subcommand could be removed (since i2c md/mw will work instead) but is retained as a convenience. Signed-off-by: Simon Glass diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index e861b53..9a62ddd 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -29,6 +29,19 @@ config I2C_CROS_EC_TUNNEL I2C or LPC). Some Chromebooks use this when the hardware design does not allow direct access to the main PMIC from the AP. +config I2C_CROS_EC_LDO + bool "Provide access to LDOs on the Chrome OS EC" + depends on CROS_EC + ---help--- + On many Chromebooks the main PMIC is inaccessible to the AP. This is + often dealt with by using an I2C pass-through interface provided by + the EC. On some unfortunate models (e.g. Spring) the pass-through + is not available, and an LDO message is available instead. This + option enables a driver which provides very basic access to those + regulators, via the EC. We implement this as an I2C bus which + emulates just the TPS65090 messages we know about. This is done to + avoid duplicating the logic in the TPS65090 regulator driver for + enabling/disabling an LDO. config DM_I2C_GPIO bool "Enable Driver Model for software emulated I2C bus driver" diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 7f01fce..9b45248 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_DM_I2C) += i2c-uclass.o obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o obj-$(CONFIG_DM_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o +obj-$(CONFIG_I2C_CROS_EC_LDO) += cros_ec_ldo.o obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o diff --git a/drivers/i2c/cros_ec_ldo.c b/drivers/i2c/cros_ec_ldo.c new file mode 100644 index 0000000..b817c61 --- /dev/null +++ b/drivers/i2c/cros_ec_ldo.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +static int cros_ec_ldo_set_bus_speed(struct udevice *dev, unsigned int speed) +{ + return 0; +} + +static int cros_ec_ldo_xfer(struct udevice *dev, struct i2c_msg *msg, + int nmsgs) +{ + bool is_read = nmsgs > 1; + int fet_id, ret; + + /* + * Look for reads and writes of the LDO registers. In either case the + * first message is a write with the register number as the first byte. + */ + if (!nmsgs || !msg->len || (msg->flags & I2C_M_RD)) { + debug("%s: Invalid message\n", __func__); + goto err; + } + + fet_id = msg->buf[0] - REG_FET_BASE; + if (fet_id < 1 || fet_id > MAX_FET_NUM) { + debug("%s: Invalid FET %d\n", __func__, fet_id); + goto err; + } + + if (is_read) { + uint8_t state; + + ret = cros_ec_get_ldo(dev->parent, fet_id, &state); + if (!ret) + msg[1].buf[0] = state ? + FET_CTRL_ENFET | FET_CTRL_PGFET : 0; + } else { + bool on = msg->buf[1] & FET_CTRL_ENFET; + + ret = cros_ec_set_ldo(dev->parent, fet_id, on); + } + + return ret; + +err: + /* Indicate that the message is unimplemented */ + return -ENOSYS; +} + +static const struct dm_i2c_ops cros_ec_i2c_ops = { + .xfer = cros_ec_ldo_xfer, + .set_bus_speed = cros_ec_ldo_set_bus_speed, +}; + +static const struct udevice_id cros_ec_i2c_ids[] = { + { .compatible = "google,cros-ec-ldo-tunnel" }, + { } +}; + +U_BOOT_DRIVER(cros_ec_ldo) = { + .name = "cros_ec_ldo_tunnel", + .id = UCLASS_I2C, + .of_match = cros_ec_i2c_ids, + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), + .ops = &cros_ec_i2c_ops, +}; diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c index ae52561..6027177 100644 --- a/drivers/misc/cros_ec.c +++ b/drivers/misc/cros_ec.c @@ -931,31 +931,32 @@ int cros_ec_write_vbnvcontext(struct cros_ec_dev *dev, const uint8_t *block) return 0; } -int cros_ec_set_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t state) +int cros_ec_set_ldo(struct udevice *dev, uint8_t index, uint8_t state) { + struct cros_ec_dev *cdev = dev_get_uclass_priv(dev); struct ec_params_ldo_set params; params.index = index; params.state = state; - if (ec_command_inptr(dev, EC_CMD_LDO_SET, 0, - ¶ms, sizeof(params), - NULL, 0)) + if (ec_command_inptr(cdev, EC_CMD_LDO_SET, 0, ¶ms, sizeof(params), + NULL, 0)) return -1; return 0; } -int cros_ec_get_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t *state) +int cros_ec_get_ldo(struct udevice *dev, uint8_t index, uint8_t *state) { + struct cros_ec_dev *cdev = dev_get_uclass_priv(dev); struct ec_params_ldo_get params; struct ec_response_ldo_get *resp; params.index = index; - if (ec_command_inptr(dev, EC_CMD_LDO_GET, 0, - ¶ms, sizeof(params), - (uint8_t **)&resp, sizeof(*resp)) != sizeof(*resp)) + if (ec_command_inptr(cdev, EC_CMD_LDO_GET, 0, ¶ms, sizeof(params), + (uint8_t **)&resp, sizeof(*resp)) != + sizeof(*resp)) return -1; *state = resp->state; @@ -1681,9 +1682,9 @@ static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) state = simple_strtoul(argv[3], &endp, 10); if (*argv[3] == 0 || *endp != 0) return CMD_RET_USAGE; - ret = cros_ec_set_ldo(dev, index, state); + ret = cros_ec_set_ldo(udev, index, state); } else { - ret = cros_ec_get_ldo(dev, index, &state); + ret = cros_ec_get_ldo(udev, index, &state); if (!ret) { printf("LDO%d: %s\n", index, state == EC_LDO_STATE_ON ? diff --git a/include/cros_ec.h b/include/cros_ec.h index 41951c3..0ad9d81 100644 --- a/include/cros_ec.h +++ b/include/cros_ec.h @@ -350,7 +350,7 @@ int cros_ec_read_build_info(struct cros_ec_dev *dev, char **strp); * @param state new state of the LDO/FET : EC_LDO_STATE_ON|OFF * @return 0 if ok, -1 on error */ -int cros_ec_set_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t state); +int cros_ec_set_ldo(struct udevice *dev, uint8_t index, uint8_t state); /** * Read back a LDO / FET current state. @@ -360,7 +360,7 @@ int cros_ec_set_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t state); * @param state current state of the LDO/FET : EC_LDO_STATE_ON|OFF * @return 0 if ok, -1 on error */ -int cros_ec_get_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t *state); +int cros_ec_get_ldo(struct udevice *dev, uint8_t index, uint8_t *state); /** * Get access to the error reported when cros_ec_board_init() was called -- cgit v0.10.2 From 89ca9351cf85ed0843a1a82d626560e0dadb3fe5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:53 -0600 Subject: exynos: serial: Refactor init code for debug UART The debug UART code needs to perform the same init as the normal UART driver. In preparation for this, move the init code into two functions, one for the basic init and one for setting the baud rate. This will make adding debug UART support easier. Signed-off-by: Simon Glass diff --git a/drivers/serial/serial_s5p.c b/drivers/serial/serial_s5p.c index 8469afd..7abec53 100644 --- a/drivers/serial/serial_s5p.c +++ b/drivers/serial/serial_s5p.c @@ -14,8 +14,8 @@ #include #include #include -#include #include +#include #include DECLARE_GLOBAL_DATA_PTR; @@ -59,11 +59,20 @@ static const int udivslot[] = { 0xffdf, }; -int s5p_serial_setbrg(struct udevice *dev, int baudrate) +static void __maybe_unused s5p_serial_init(struct s5p_uart *uart) +{ + /* enable FIFOs, auto clear Rx FIFO */ + writel(0x3, &uart->ufcon); + writel(0, &uart->umcon); + /* 8N1 */ + writel(0x3, &uart->ulcon); + /* No interrupts, no DMA, pure polling */ + writel(0x245, &uart->ucon); +} + +static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, uint uclk, + int baudrate) { - struct s5p_serial_platdata *plat = dev->platdata; - struct s5p_uart *const uart = plat->reg; - u32 uclk = get_uart_clk(plat->port_id); u32 val; val = uclk / baudrate; @@ -74,6 +83,15 @@ int s5p_serial_setbrg(struct udevice *dev, int baudrate) writew(udivslot[val % 16], &uart->rest.slot); else writeb(val % 16, &uart->rest.value); +} + +int s5p_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct s5p_serial_platdata *plat = dev->platdata; + struct s5p_uart *const uart = plat->reg; + u32 uclk = get_uart_clk(plat->port_id); + + s5p_serial_baud(uart, uclk, baudrate); return 0; } @@ -83,13 +101,7 @@ static int s5p_serial_probe(struct udevice *dev) struct s5p_serial_platdata *plat = dev->platdata; struct s5p_uart *const uart = plat->reg; - /* enable FIFOs, auto clear Rx FIFO */ - writel(0x3, &uart->ufcon); - writel(0, &uart->umcon); - /* 8N1 */ - writel(0x3, &uart->ulcon); - /* No interrupts, no DMA, pure polling */ - writel(0x245, &uart->ucon); + s5p_serial_init(uart); return 0; } -- cgit v0.10.2 From bf6e702232e3b8c38fe0934c1f0240958eaabed3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:54 -0600 Subject: exynos: Add debug UART support for Samsung S5P serial Add a debug UART implementation for this serial driver. It does not set up pinmux automatically - this must be done before calling debug_uart_init(). Signed-off-by: Simon Glass diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index b5a91b7..fd126a8 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -53,6 +53,13 @@ config DEBUG_EFI_CONSOLE U-Boot when running on top of EFI (Extensive Firmware Interface). This is a type of BIOS used by PCs. +config DEBUG_UART_S5P + bool "Samsung S5P" + help + Select this to enable a debug UART using the serial_s5p driver. You + will need to provide parameters to make this work. The driver will + be available until the real driver-model serial is running. + endchoice config DEBUG_UART_BASE diff --git a/drivers/serial/serial_s5p.c b/drivers/serial/serial_s5p.c index 7abec53..4a553a3 100644 --- a/drivers/serial/serial_s5p.c +++ b/drivers/serial/serial_s5p.c @@ -200,3 +200,28 @@ U_BOOT_DRIVER(serial_s5p) = { .ops = &s5p_serial_ops, .flags = DM_FLAG_PRE_RELOC, }; + +#ifdef CONFIG_DEBUG_UART_S5P + +#include + +void debug_uart_init(void) +{ + struct s5p_uart *uart = (struct s5p_uart *)CONFIG_DEBUG_UART_BASE; + + s5p_serial_init(uart); + s5p_serial_baud(uart, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); +} + +static inline void _debug_uart_putc(int ch) +{ + struct s5p_uart *uart = (struct s5p_uart *)CONFIG_DEBUG_UART_BASE; + + while (readl(&uart->ufstat) & TX_FIFO_FULL); + + writeb(ch, &uart->utxh); +} + +DEBUG_UART_FUNCS + +#endif -- cgit v0.10.2 From 7fb57396e632126867c360e0dc0700db3d059aef Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:55 -0600 Subject: exynos: Enable the debug UART in SPL As a debugging aid, allow UART3 to be used as a debug UART in SPL. This is a precursor to proper UART support, which requires a substantial refactor. Signed-off-by: Simon Glass diff --git a/arch/arm/cpu/armv7/exynos/lowlevel_init.c b/arch/arm/cpu/armv7/exynos/lowlevel_init.c index 120aaf8..3774607 100644 --- a/arch/arm/cpu/armv7/exynos/lowlevel_init.c +++ b/arch/arm/cpu/armv7/exynos/lowlevel_init.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -216,6 +217,10 @@ int do_lowlevel_init(void) if (actions & DO_CLOCKS) { system_clock_init(); +#ifdef CONFIG_DEBUG_UART + exynos_pinmux_config(PERIPH_ID_UART3, PINMUX_FLAG_NONE); + debug_uart_init(); +#endif mem_ctrl_init(actions & DO_MEM_RESET); tzpc_init(); } diff --git a/drivers/serial/serial_s5p.c b/drivers/serial/serial_s5p.c index 4a553a3..21cb566 100644 --- a/drivers/serial/serial_s5p.c +++ b/drivers/serial/serial_s5p.c @@ -85,6 +85,7 @@ static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, uint uclk, writeb(val % 16, &uart->rest.value); } +#ifndef CONFIG_SPL_BUILD int s5p_serial_setbrg(struct udevice *dev, int baudrate) { struct s5p_serial_platdata *plat = dev->platdata; @@ -200,6 +201,7 @@ U_BOOT_DRIVER(serial_s5p) = { .ops = &s5p_serial_ops, .flags = DM_FLAG_PRE_RELOC, }; +#endif #ifdef CONFIG_DEBUG_UART_S5P -- cgit v0.10.2 From 151b223b9c4e309d65166558afdfa0ce3c3b3213 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:58 -0600 Subject: dm: power: Add a new driver for the TPS65090 PMIC The existing TPS65090 driver does not support driver model. Add a new one that does. This can be used as a base for a regulator driver also. It uses the standard device tree binding. Signed-off-by: Simon Glass Acked-by: Przemyslaw Marczak diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 164f421..fd8af81 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -41,3 +41,12 @@ config DM_PMIC_SANDBOX - set by i2c emul driver's probe() (defaults in header) Driver binding info: doc/device-tree-bindings/pmic/sandbox.txt + +config PMIC_TPS65090 + bool "Enable driver for Texas Instruments TPS65090 PMIC" + depends on DM_PMIC + ---help--- + The TPS65090 is a PMIC containing several LDOs, DC to DC convertors, + FETs and a battery charger. This driver provides register access + only, and you can enable the regulator/charger drivers separately if + required. diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 4ad6df3..43b706b 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_DM_PMIC) += pmic-uclass.o obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o +obj-$(CONFIG_PMIC_TPS65090) += tps65090.o obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o obj-$(CONFIG_POWER_MAX77696) += pmic_max77696.o obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o diff --git a/drivers/power/pmic/tps65090.c b/drivers/power/pmic/tps65090.c new file mode 100644 index 0000000..4797f32 --- /dev/null +++ b/drivers/power/pmic/tps65090.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "fet", .driver = TPS65090_FET_DRIVER }, + { }, +}; + +static int tps65090_reg_count(struct udevice *dev) +{ + return TPS65090_NUM_REGS; +} + +static int tps65090_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + if (dm_i2c_write(dev, reg, buff, len)) { + error("write error to device: %p register: %#x!", dev, reg); + return -EIO; + } + + return 0; +} + +static int tps65090_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + int ret; + + ret = dm_i2c_read(dev, reg, buff, len); + if (ret) { + error("read error %d from device: %p register: %#x!", ret, dev, + reg); + return -EIO; + } + + return 0; +} + +static int tps65090_bind(struct udevice *dev) +{ + int regulators_node; + const void *blob = gd->fdt_blob; + int children; + + regulators_node = fdt_subnode_offset(blob, dev->of_offset, + "regulators"); + if (regulators_node <= 0) { + debug("%s: %s regulators subnode not found!", __func__, + dev->name); + return -ENXIO; + } + + debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); + + children = pmic_bind_children(dev, regulators_node, pmic_children_info); + if (!children) + debug("%s: %s - no child found\n", __func__, dev->name); + + /* Always return success for this device */ + return 0; +} + +static struct dm_pmic_ops tps65090_ops = { + .reg_count = tps65090_reg_count, + .read = tps65090_read, + .write = tps65090_write, +}; + +static const struct udevice_id tps65090_ids[] = { + { .compatible = "ti,tps65090" }, + { } +}; + +U_BOOT_DRIVER(pmic_tps65090) = { + .name = "tps65090 pmic", + .id = UCLASS_PMIC, + .of_match = tps65090_ids, + .bind = tps65090_bind, + .ops = &tps65090_ops, +}; diff --git a/include/power/tps65090.h b/include/power/tps65090.h new file mode 100644 index 0000000..3a0690b --- /dev/null +++ b/include/power/tps65090.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __TPS65090_PMIC_H_ +#define __TPS65090_PMIC_H_ + +/* I2C device address for TPS65090 PMU */ +#define TPS65090_I2C_ADDR 0x48 + +/* TPS65090 register addresses */ +enum { + REG_IRQ1 = 0, + REG_CG_CTRL0 = 4, + REG_CG_STATUS1 = 0xa, + REG_FET_BASE = 0xe, /* Not a real register, FETs count from here */ + REG_FET1_CTRL, + REG_FET2_CTRL, + REG_FET3_CTRL, + REG_FET4_CTRL, + REG_FET5_CTRL, + REG_FET6_CTRL, + REG_FET7_CTRL, + TPS65090_NUM_REGS, +}; + +enum { + IRQ1_VBATG = 1 << 3, + CG_CTRL0_ENC_MASK = 0x01, + + MAX_FET_NUM = 7, + MAX_CTRL_READ_TRIES = 5, + + /* TPS65090 FET_CTRL register values */ + FET_CTRL_TOFET = 1 << 7, /* Timeout, startup, overload */ + FET_CTRL_PGFET = 1 << 4, /* Power good for FET status */ + FET_CTRL_WAIT = 3 << 2, /* Overcurrent timeout max */ + FET_CTRL_ADENFET = 1 << 1, /* Enable output auto discharge */ + FET_CTRL_ENFET = 1 << 0, /* Enable FET */ +}; + +enum { + /* Status register fields */ + TPS65090_ST1_OTC = 1 << 0, + TPS65090_ST1_OCC = 1 << 1, + TPS65090_ST1_STATE_SHIFT = 4, + TPS65090_ST1_STATE_MASK = 0xf << TPS65090_ST1_STATE_SHIFT, +}; + +/* Drivers name */ +#define TPS65090_FET_DRIVER "tps65090_fet" + +#endif /* __TPS65090_PMIC_H_ */ -- cgit v0.10.2 From 1c88b67ec8faa33c12438cab8a5191bbab6c85e3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:15:59 -0600 Subject: dm: power: Add support for TPS65090 FETs The TPS65090 has 7 FETs which are modelled as regulators. This allows them to be controlled by drivers easier, accessed through the 'regulator' command and used by other drivers. Signed-off-by: Simon Glass Acked-by: Przemyslaw Marczak diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 6289b83..d090650 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -61,3 +61,13 @@ config DM_REGULATOR_SANDBOX A detailed information can be found in header: '' Binding info: 'doc/device-tree-bindings/pmic/max77686.txt' + +config REGULATOR_TPS65090 + bool "Enable driver for TPS65090 PMIC regulators" + depends on PMIC_TPS65090 + ---help--- + The TPS65090 provides several FETs (Field-effect Transistors, + effectively switches) which are supported by this driver as + regulators, one for each FET. The standard regulator interface is + supported, but it is only possible to turn the regulators on or off. + There is no voltage/current control. diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 96aa624..43f4d2c 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_DM_REGULATOR_FIXED) += fixed.o obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o +obj-$(CONFIG_REGULATOR_TPS65090) += tps65090_regulator.o diff --git a/drivers/power/regulator/tps65090_regulator.c b/drivers/power/regulator/tps65090_regulator.c new file mode 100644 index 0000000..affc504 --- /dev/null +++ b/drivers/power/regulator/tps65090_regulator.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +static int tps65090_fet_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + + uc_pdata->type = REGULATOR_TYPE_OTHER; + uc_pdata->mode_count = 0; + + return 0; +} + +static bool tps65090_fet_get_enable(struct udevice *dev) +{ + struct udevice *pmic = dev_get_parent(dev); + int ret, fet_id; + + fet_id = dev->driver_data; + debug("%s: fet_id=%d\n", __func__, fet_id); + + ret = pmic_reg_read(pmic, REG_FET_BASE + fet_id); + if (ret < 0) + return ret; + + return ret & FET_CTRL_ENFET; +} + +/** + * Set the power state for a FET + * + * @param pmic pmic structure for the tps65090 + * @param fet_id FET number to set (1..MAX_FET_NUM) + * @param set 1 to power on FET, 0 to power off + * @return -EIO if we got a comms error, -EAGAIN if the FET failed to + * change state. If all is ok, returns 0. + */ +static int tps65090_fet_set(struct udevice *pmic, int fet_id, bool set) +{ + int retry; + u32 value; + int ret; + + value = FET_CTRL_ADENFET | FET_CTRL_WAIT; + if (set) + value |= FET_CTRL_ENFET; + + if (pmic_reg_write(pmic, REG_FET_BASE + fet_id, value)) + return -EIO; + + /* Try reading until we get a result */ + for (retry = 0; retry < MAX_CTRL_READ_TRIES; retry++) { + ret = pmic_reg_read(pmic, REG_FET_BASE + fet_id); + if (ret < 0) + return ret; + + /* Check that the FET went into the expected state */ + debug("%s: flags=%x\n", __func__, ret); + if (!!(ret & FET_CTRL_PGFET) == set) + return 0; + + /* If we got a timeout, there is no point in waiting longer */ + if (ret & FET_CTRL_TOFET) + break; + + mdelay(1); + } + + debug("FET %d: Power good should have set to %d but reg=%#02x\n", + fet_id, set, ret); + return -EAGAIN; +} + +static int tps65090_fet_set_enable(struct udevice *dev, bool enable) +{ + struct udevice *pmic = dev_get_parent(dev); + int ret, fet_id; + ulong start; + int loops; + + fet_id = dev->driver_data; + debug("%s: fet_id=%d, enable=%d\n", __func__, fet_id, enable); + + start = get_timer(0); + for (loops = 0;; loops++) { + ret = tps65090_fet_set(pmic, fet_id, enable); + if (!ret) + break; + + if (get_timer(start) > 100) + break; + + /* Turn it off and try again until we time out */ + tps65090_fet_set(pmic, fet_id, false); + } + + if (ret) + debug("%s: FET%d failed to power on: time=%lums, loops=%d\n", + __func__, fet_id, get_timer(start), loops); + else if (loops) + debug("%s: FET%d powered on after %lums, loops=%d\n", + __func__, fet_id, get_timer(start), loops); + + /* + * Unfortunately there are some conditions where the power-good bit + * will be 0, but the FET still comes up. One such case occurs with + * the LCD backlight on snow. We'll just return 0 here and assume + * that the FET will eventually come up. + */ + if (ret == -EAGAIN) + ret = 0; + + return ret; +} + +static const struct dm_regulator_ops tps65090_fet_ops = { + .get_enable = tps65090_fet_get_enable, + .set_enable = tps65090_fet_set_enable, +}; + +U_BOOT_DRIVER(tps65090_fet) = { + .name = TPS65090_FET_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps65090_fet_ops, + .probe = tps65090_fet_probe, +}; -- cgit v0.10.2 From d308c0136d8a530d4846ab08ce02e6d40614d7e7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:00 -0600 Subject: dm: power: Add support for the S5M8767 PMIC This PMIC is used with SoCs which need a combination of BUCKs and LDOs. The driver supports probing and basic register access. It supports the standard device tree binding and supports driver model. A regulator driver can be provided also. Signed-off-by: Simon Glass Acked-by: Przemyslaw Marczak diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index fd8af81..7b98189 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -42,6 +42,15 @@ config DM_PMIC_SANDBOX Driver binding info: doc/device-tree-bindings/pmic/sandbox.txt +config PMIC_S5M8767 + bool "Enable Driver Model for the Samsung S5M8767 PMIC" + depends on DM_PMIC + ---help--- + The S5M8767 PMIC provides a large array of LDOs and BUCKs for use + as a SoC power controller. It also provides 32KHz clock outputs. This + driver provides basic register access and sets up the attached + regulators if regulator support is enabled. + config PMIC_TPS65090 bool "Enable driver for Texas Instruments TPS65090 PMIC" depends on DM_PMIC diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 43b706b..322c2c6 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -9,6 +9,8 @@ obj-$(CONFIG_DM_PMIC) += pmic-uclass.o obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o obj-$(CONFIG_PMIC_TPS65090) += tps65090.o +obj-$(CONFIG_PMIC_S5M8767) += s5m8767.o + obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o obj-$(CONFIG_POWER_MAX77696) += pmic_max77696.o obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o diff --git a/drivers/power/pmic/s5m8767.c b/drivers/power/pmic/s5m8767.c new file mode 100644 index 0000000..075fe7e --- /dev/null +++ b/drivers/power/pmic/s5m8767.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "LDO", .driver = S5M8767_LDO_DRIVER }, + { .prefix = "BUCK", .driver = S5M8767_BUCK_DRIVER }, + { }, +}; + +static int s5m8767_reg_count(struct udevice *dev) +{ + return S5M8767_NUM_OF_REGS; +} + +static int s5m8767_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + if (dm_i2c_write(dev, reg, buff, len)) { + error("write error to device: %p register: %#x!", dev, reg); + return -EIO; + } + + return 0; +} + +static int s5m8767_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + if (dm_i2c_read(dev, reg, buff, len)) { + error("read error from device: %p register: %#x!", dev, reg); + return -EIO; + } + + return 0; +} + +int s5m8767_enable_32khz_cp(struct udevice *dev) +{ + return pmic_clrsetbits(dev, S5M8767_EN32KHZ_CP, 0, 1 << 1); +} + +static int s5m8767_bind(struct udevice *dev) +{ + int node; + const void *blob = gd->fdt_blob; + int children; + + node = fdt_subnode_offset(blob, dev->of_offset, "regulators"); + if (node <= 0) { + debug("%s: %s regulators subnode not found!", __func__, + dev->name); + return -ENXIO; + } + + debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); + + children = pmic_bind_children(dev, node, pmic_children_info); + if (!children) + debug("%s: %s - no child found\n", __func__, dev->name); + + /* Always return success for this device */ + return 0; +} + +static struct dm_pmic_ops s5m8767_ops = { + .reg_count = s5m8767_reg_count, + .read = s5m8767_read, + .write = s5m8767_write, +}; + +static const struct udevice_id s5m8767_ids[] = { + { .compatible = "samsung,s5m8767-pmic" }, + { } +}; + +U_BOOT_DRIVER(pmic_s5m8767) = { + .name = "s5m8767_pmic", + .id = UCLASS_PMIC, + .of_match = s5m8767_ids, + .bind = s5m8767_bind, + .ops = &s5m8767_ops, +}; diff --git a/include/power/s5m8767.h b/include/power/s5m8767.h new file mode 100644 index 0000000..ba88ff7 --- /dev/null +++ b/include/power/s5m8767.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __S5M8767_H_ +#define __S5M8767_H_ + +enum s5m8767_regnum { + S5M8767_BUCK1 = 0, + S5M8767_BUCK2, + S5M8767_BUCK3, + S5M8767_BUCK4, + S5M8767_BUCK5, + S5M8767_BUCK6, + S5M8767_BUCK7, + S5M8767_BUCK8, + S5M8767_BUCK9, + S5M8767_LDO1, + S5M8767_LDO2, + S5M8767_LDO3, + S5M8767_LDO4, + S5M8767_LDO5, + S5M8767_LDO6, + S5M8767_LDO7, + S5M8767_LDO8, + S5M8767_LDO9, + S5M8767_LDO10, + S5M8767_LDO11, + S5M8767_LDO12, + S5M8767_LDO13, + S5M8767_LDO14, + S5M8767_LDO15, + S5M8767_LDO16, + S5M8767_LDO17, + S5M8767_LDO18, + S5M8767_LDO19, + S5M8767_LDO20, + S5M8767_LDO21, + S5M8767_LDO22, + S5M8767_LDO23, + S5M8767_LDO24, + S5M8767_LDO25, + S5M8767_LDO26, + S5M8767_LDO27, + S5M8767_LDO28, + S5M8767_EN32KHZ_CP, + + S5M8767_NUM_OF_REGS, +}; + +struct sec_voltage_desc { + int max; + int min; + int step; +}; + +/** + * struct s5m8767_para - s5m8767 register parameters + * @param vol_addr i2c address of the given buck/ldo register + * @param vol_bitpos bit position to be set or clear within register + * @param vol_bitmask bit mask value + * @param reg_enaddr control register address, which enable the given + * given buck/ldo. + * @param reg_enbiton value to be written to buck/ldo to make it ON + * @param vol Voltage information + */ +struct s5m8767_para { + enum s5m8767_regnum regnum; + u8 vol_addr; + u8 vol_bitpos; + u8 vol_bitmask; + u8 reg_enaddr; + u8 reg_enbiton; + const struct sec_voltage_desc *vol; +}; + +/* Drivers name */ +#define S5M8767_LDO_DRIVER "s5m8767_ldo" +#define S5M8767_BUCK_DRIVER "s5m8767_buck" + +int s5m8767_enable_32khz_cp(struct udevice *dev); + +#endif /* __S5M8767_PMIC_H_ */ -- cgit v0.10.2 From f615e6a64d23952d3c68b9ac92b4ef5fa5ebdf8a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:01 -0600 Subject: dm: power: Add support for S5M8767 regulators This PMIC is used with SoCs which need a combination of BUCKs and LDOs. The driver supports changing voltage and enabling/disabling each regulator. It supports the standard device tree binding and supports driver model. Signed-off-by: Simon Glass Acked-by: Przemyslaw Marczak diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index d090650..e85c692 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -32,6 +32,15 @@ config DM_REGULATOR_FIXED features for fixed value regulators. The driver implements get/set api for enable and get only for voltage value. +config REGULATOR_S5M8767 + bool "Enable support for S5M8767 regulator" + depends on DM_REGULATOR && PMIC_S5M8767 + ---help--- + This enables the regulator features of the S5M8767, allowing voltages + to be set, etc. The driver is not fully complete but supports most + common requirements, including all LDOs and BUCKs. This allows many + supplies to be set automatically using the device tree values. + config DM_REGULATOR_SANDBOX bool "Enable Driver Model for Sandbox PMIC regulator" depends on DM_REGULATOR && DM_PMIC_SANDBOX diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 43f4d2c..08d7b0d 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -8,5 +8,6 @@ obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_DM_REGULATOR_FIXED) += fixed.o +obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o obj-$(CONFIG_REGULATOR_TPS65090) += tps65090_regulator.o diff --git a/drivers/power/regulator/s5m8767.c b/drivers/power/regulator/s5m8767.c new file mode 100644 index 0000000..93a3c94 --- /dev/null +++ b/drivers/power/regulator/s5m8767.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static const struct sec_voltage_desc buck_v1 = { + .max = 2225000, + .min = 650000, + .step = 6250, +}; + +static const struct sec_voltage_desc buck_v2 = { + .max = 1600000, + .min = 600000, + .step = 6250, +}; + +static const struct sec_voltage_desc buck_v3 = { + .max = 3000000, + .min = 750000, + .step = 12500, +}; + +static const struct sec_voltage_desc ldo_v1 = { + .max = 3950000, + .min = 800000, + .step = 50000, +}; + +static const struct sec_voltage_desc ldo_v2 = { + .max = 2375000, + .min = 800000, + .step = 25000, +}; + +static const struct s5m8767_para buck_param[] = { + /* + * | voltage ----| | enable -| voltage + * regnum addr bpos mask addr on desc + */ + {S5M8767_BUCK1, 0x33, 0x0, 0xff, 0x32, 0x3, &buck_v1}, + {S5M8767_BUCK2, 0x35, 0x0, 0xff, 0x34, 0x1, &buck_v2}, + {S5M8767_BUCK3, 0x3e, 0x0, 0xff, 0x3d, 0x1, &buck_v2}, + {S5M8767_BUCK4, 0x47, 0x0, 0xff, 0x46, 0x1, &buck_v2}, + {S5M8767_BUCK5, 0x50, 0x0, 0xff, 0x4f, 0x3, &buck_v1}, + {S5M8767_BUCK6, 0x55, 0x0, 0xff, 0x54, 0x3, &buck_v1}, + {S5M8767_BUCK7, 0x57, 0x0, 0xff, 0x56, 0x3, &buck_v3}, + {S5M8767_BUCK8, 0x59, 0x0, 0xff, 0x58, 0x3, &buck_v3}, + {S5M8767_BUCK9, 0x5b, 0x0, 0xff, 0x5a, 0x3, &buck_v3}, +}; + +static const struct s5m8767_para ldo_param[] = { + {S5M8767_LDO1, 0x5c, 0x0, 0x3f, 0x5c, 0x3, &ldo_v2}, + {S5M8767_LDO2, 0x5d, 0x0, 0x3f, 0x5d, 0x1, &ldo_v2}, + {S5M8767_LDO3, 0x61, 0x0, 0x3f, 0x61, 0x3, &ldo_v1}, + {S5M8767_LDO4, 0x62, 0x0, 0x3f, 0x62, 0x3, &ldo_v1}, + {S5M8767_LDO5, 0x63, 0x0, 0x3f, 0x63, 0x3, &ldo_v1}, + {S5M8767_LDO6, 0x64, 0x0, 0x3f, 0x64, 0x1, &ldo_v2}, + {S5M8767_LDO7, 0x65, 0x0, 0x3f, 0x65, 0x1, &ldo_v2}, + {S5M8767_LDO8, 0x66, 0x0, 0x3f, 0x66, 0x1, &ldo_v2}, + {S5M8767_LDO9, 0x67, 0x0, 0x3f, 0x67, 0x3, &ldo_v1}, + {S5M8767_LDO10, 0x68, 0x0, 0x3f, 0x68, 0x1, &ldo_v1}, + {S5M8767_LDO11, 0x69, 0x0, 0x3f, 0x69, 0x1, &ldo_v1}, + {S5M8767_LDO12, 0x6a, 0x0, 0x3f, 0x6a, 0x1, &ldo_v1}, + {S5M8767_LDO13, 0x6b, 0x0, 0x3f, 0x6b, 0x3, &ldo_v1}, + {S5M8767_LDO14, 0x6c, 0x0, 0x3f, 0x6c, 0x1, &ldo_v1}, + {S5M8767_LDO15, 0x6d, 0x0, 0x3f, 0x6d, 0x1, &ldo_v2}, + {S5M8767_LDO16, 0x6e, 0x0, 0x3f, 0x6e, 0x1, &ldo_v1}, + {S5M8767_LDO17, 0x6f, 0x0, 0x3f, 0x6f, 0x3, &ldo_v1}, + {S5M8767_LDO18, 0x70, 0x0, 0x3f, 0x70, 0x3, &ldo_v1}, + {S5M8767_LDO19, 0x71, 0x0, 0x3f, 0x71, 0x3, &ldo_v1}, + {S5M8767_LDO20, 0x72, 0x0, 0x3f, 0x72, 0x3, &ldo_v1}, + {S5M8767_LDO21, 0x73, 0x0, 0x3f, 0x73, 0x3, &ldo_v1}, + {S5M8767_LDO22, 0x74, 0x0, 0x3f, 0x74, 0x3, &ldo_v1}, + {S5M8767_LDO23, 0x75, 0x0, 0x3f, 0x75, 0x3, &ldo_v1}, + {S5M8767_LDO24, 0x76, 0x0, 0x3f, 0x76, 0x3, &ldo_v1}, + {S5M8767_LDO25, 0x77, 0x0, 0x3f, 0x77, 0x3, &ldo_v1}, + {S5M8767_LDO26, 0x78, 0x0, 0x3f, 0x78, 0x3, &ldo_v1}, + {S5M8767_LDO27, 0x79, 0x0, 0x3f, 0x79, 0x3, &ldo_v1}, + {S5M8767_LDO28, 0x7a, 0x0, 0x3f, 0x7a, 0x3, &ldo_v1}, +}; + +enum { + ENABLE_SHIFT = 6, + ENABLE_MASK = 3, +}; + +static int reg_get_value(struct udevice *dev, const struct s5m8767_para *param) +{ + const struct sec_voltage_desc *desc; + int ret, uv, val; + + ret = pmic_reg_read(dev->parent, param->vol_addr); + if (ret < 0) + return ret; + + desc = param->vol; + val = (ret >> param->vol_bitpos) & param->vol_bitmask; + uv = desc->min + val * desc->step; + + return uv; +} + +static int reg_set_value(struct udevice *dev, const struct s5m8767_para *param, + int uv) +{ + const struct sec_voltage_desc *desc; + int ret, val; + + desc = param->vol; + if (uv < desc->min || uv > desc->max) + return -EINVAL; + val = (uv - desc->min) / desc->step; + val = (val & param->vol_bitmask) << param->vol_bitpos; + ret = pmic_clrsetbits(dev->parent, param->vol_addr, + param->vol_bitmask << param->vol_bitpos, + val); + + return ret; +} + +static int s5m8767_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + + uc_pdata->type = REGULATOR_TYPE_LDO; + uc_pdata->mode_count = 0; + + return 0; +} +static int ldo_get_value(struct udevice *dev) +{ + int ldo = dev->driver_data; + + return reg_get_value(dev, &ldo_param[ldo]); +} + +static int ldo_set_value(struct udevice *dev, int uv) +{ + int ldo = dev->driver_data; + + return reg_set_value(dev, &ldo_param[ldo], uv); +} + +static int reg_get_enable(struct udevice *dev, const struct s5m8767_para *param) +{ + bool enable; + int ret; + + ret = pmic_reg_read(dev->parent, param->reg_enaddr); + if (ret < 0) + return ret; + + enable = (ret >> ENABLE_SHIFT) & ENABLE_MASK; + + return enable; +} + +static int reg_set_enable(struct udevice *dev, const struct s5m8767_para *param, + bool enable) +{ + int ret; + + ret = pmic_reg_read(dev->parent, param->reg_enaddr); + if (ret < 0) + return ret; + + ret = pmic_clrsetbits(dev->parent, param->reg_enaddr, + ENABLE_MASK << ENABLE_SHIFT, + enable ? param->reg_enbiton << ENABLE_SHIFT : 0); + + return ret; +} + +static bool ldo_get_enable(struct udevice *dev) +{ + int ldo = dev->driver_data; + + return reg_get_enable(dev, &ldo_param[ldo]); +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + int ldo = dev->driver_data; + + return reg_set_enable(dev, &ldo_param[ldo], enable); +} + +static int s5m8767_buck_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + uc_pdata->mode_count = 0; + + return 0; +} + +static int buck_get_value(struct udevice *dev) +{ + int buck = dev->driver_data; + + return reg_get_value(dev, &buck_param[buck]); +} + +static int buck_set_value(struct udevice *dev, int uv) +{ + int buck = dev->driver_data; + + return reg_set_value(dev, &buck_param[buck], uv); +} + +static bool buck_get_enable(struct udevice *dev) +{ + int buck = dev->driver_data; + + return reg_get_enable(dev, &buck_param[buck]); +} + +static int buck_set_enable(struct udevice *dev, bool enable) +{ + int buck = dev->driver_data; + + return reg_set_enable(dev, &buck_param[buck], enable); +} + +static const struct dm_regulator_ops s5m8767_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .get_enable = ldo_get_enable, + .set_enable = ldo_set_enable, +}; + +U_BOOT_DRIVER(s5m8767_ldo) = { + .name = S5M8767_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &s5m8767_ldo_ops, + .probe = s5m8767_ldo_probe, +}; + +static const struct dm_regulator_ops s5m8767_buck_ops = { + .get_value = buck_get_value, + .set_value = buck_set_value, + .get_enable = buck_get_enable, + .set_enable = buck_set_enable, +}; + +U_BOOT_DRIVER(s5m8767_buck) = { + .name = S5M8767_BUCK_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &s5m8767_buck_ops, + .probe = s5m8767_buck_probe, +}; -- cgit v0.10.2 From b5ffa4fdcb656b8995d6800f9c10dad76a44d38b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:02 -0600 Subject: dm: pmic: max77686: Correct a few nits The driver name should not have a space in it. Also the regulator names should match the case of the device tree. Fix these problems. Signed-off-by: Simon Glass Acked-by: Przemyslaw Marczak diff --git a/drivers/power/pmic/max77686.c b/drivers/power/pmic/max77686.c index 3523b4a..dc5a54a 100644 --- a/drivers/power/pmic/max77686.c +++ b/drivers/power/pmic/max77686.c @@ -17,8 +17,8 @@ DECLARE_GLOBAL_DATA_PTR; static const struct pmic_child_info pmic_children_info[] = { - { .prefix = "ldo", .driver = MAX77686_LDO_DRIVER }, - { .prefix = "buck", .driver = MAX77686_BUCK_DRIVER }, + { .prefix = "LDO", .driver = MAX77686_LDO_DRIVER }, + { .prefix = "BUCK", .driver = MAX77686_BUCK_DRIVER }, { }, }; @@ -84,7 +84,7 @@ static const struct udevice_id max77686_ids[] = { }; U_BOOT_DRIVER(pmic_max77686) = { - .name = "max77686 pmic", + .name = "max77686_pmic", .id = UCLASS_PMIC, .of_match = max77686_ids, .bind = max77686_bind, -- cgit v0.10.2 From cd367d89979c0476e2fcbca445a49008b372872b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:03 -0600 Subject: dm: pmic: Correct the pmic_reg_write() implementation This should write the register, not read it. Fix this bug. Signed-off-by: Simon Glass Acked-by: Przemyslaw Marczak diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index d99cb9a..49709f3 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -142,7 +142,7 @@ int pmic_reg_write(struct udevice *dev, uint reg, uint value) u8 byte = value; debug("%s: reg=%x, value=%x\n", __func__, reg, value); - return pmic_read(dev, reg, &byte, 1); + return pmic_write(dev, reg, &byte, 1); } int pmic_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set) -- cgit v0.10.2 From 8c4287090cc9bfa172370976c77b420d9c37f94c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:04 -0600 Subject: dm: power: max77686: Correct BUCK register access Some regulators use the wrong voltage register and thus it is not possible to control them. Fix this. Signed-off-by: Simon Glass Acked-by: Przemyslaw Marczak diff --git a/drivers/power/regulator/max77686.c b/drivers/power/regulator/max77686.c index 37ebe94..21173fc 100644 --- a/drivers/power/regulator/max77686.c +++ b/drivers/power/regulator/max77686.c @@ -61,10 +61,14 @@ static struct dm_regulator_mode max77686_buck_mode_onoff[] = { MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"), }; -static const char max77686_buck_addr[] = { +static const char max77686_buck_ctrl[] = { 0xff, 0x10, 0x12, 0x1c, 0x26, 0x30, 0x32, 0x34, 0x36, 0x38 }; +static const char max77686_buck_out[] = { + 0xff, 0x11, 0x14, 0x1e, 0x28, 0x31, 0x33, 0x35, 0x37, 0x39 +}; + static int max77686_buck_volt2hex(int buck, int uV) { unsigned int hex = 0; @@ -368,7 +372,7 @@ static int max77686_buck_val(struct udevice *dev, int op, int *uV) *uV = 0; /* &buck_out = ctrl + 1 */ - adr = max77686_buck_addr[buck] + 1; + adr = max77686_buck_out[buck]; /* mask */ switch (buck) { @@ -549,7 +553,7 @@ static int max77686_buck_mode(struct udevice *dev, int op, int *opmode) return -EINVAL; } - adr = max77686_buck_addr[buck]; + adr = max77686_buck_ctrl[buck]; /* mask */ switch (buck) { -- cgit v0.10.2 From 75a429f1a299cf4cd4ab3f18eb75a7eb632776c9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:25 -0600 Subject: dm: pmic: max77686: Support all BUCK regulators Add support for all BUCK regulators, now that the correct register is accessed for each. Signed-off-by: Simon Glass diff --git a/drivers/power/regulator/max77686.c b/drivers/power/regulator/max77686.c index 21173fc..946b87c 100644 --- a/drivers/power/regulator/max77686.c +++ b/drivers/power/regulator/max77686.c @@ -81,13 +81,15 @@ static int max77686_buck_volt2hex(int buck, int uV) /* hex = (uV - 600000) / 12500; */ hex = (uV - MAX77686_BUCK_UV_LMIN) / MAX77686_BUCK_UV_LSTEP; hex_max = MAX77686_BUCK234_VOLT_MAX_HEX; - /** - * Those use voltage scaller - temporary not implemented - * so return just 0 - */ - return -ENOSYS; + break; default: - /* hex = (uV - 750000) / 50000; */ + /* + * hex = (uV - 750000) / 50000. We assume that dynamic voltage + * scaling via GPIOs is not enabled and don't support that. + * If this is enabled then the driver will need to take that + * into account anrd check different registers depending on + * the current setting See the datasheet for details. + */ hex = (uV - MAX77686_BUCK_UV_HMIN) / MAX77686_BUCK_UV_HSTEP; hex_max = MAX77686_BUCK_VOLT_MAX_HEX; break; @@ -379,11 +381,11 @@ static int max77686_buck_val(struct udevice *dev, int op, int *uV) case 2: case 3: case 4: - /* Those use voltage scallers - will support in the future */ mask = MAX77686_BUCK234_VOLT_MASK; - return -ENOSYS; + break; default: mask = MAX77686_BUCK_VOLT_MASK; + break; } ret = pmic_read(dev->parent, adr, &val, 1); -- cgit v0.10.2 From d08504d18a0a6af2ba6171a4a3e71a032d2836b6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:06 -0600 Subject: dm: power: Don't return an error when regulators are not autoset Not all regulators can be set up automatically. Adjust the code so that regulators_enable_boot_on() will return success when some are skipped. Only genuine errors are reported. Signed-off-by: Simon Glass Acked-by: Przemyslaw Marczak diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index 12e141b..f3fe7a5 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -319,8 +319,10 @@ int regulators_enable_boot_on(bool verbose) dev && !ret; uclass_next_device(&dev)) { ret = regulator_autoset(dev); - if (ret == -EMEDIUMTYPE) + if (ret == -EMEDIUMTYPE) { + ret = 0; continue; + } if (verbose) regulator_show(dev, ret); } -- cgit v0.10.2 From 224d1ddcc5fa18d88e2c735778c39b8df61016ea Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:07 -0600 Subject: dm: pmic: Display the regulator limits on error When a regulator command cannot honour the requested voltage, display the limits to try to be helpful. Signed-off-by: Simon Glass Acked-by: Przemyslaw Marczak diff --git a/common/cmd_regulator.c b/common/cmd_regulator.c index 6149d1e..793f08e 100644 --- a/common/cmd_regulator.c +++ b/common/cmd_regulator.c @@ -241,7 +241,8 @@ static int do_value(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) value = simple_strtoul(argv[1], NULL, 0); if ((value < uc_pdata->min_uV || value > uc_pdata->max_uV) && !force) { - printf("Value exceeds regulator constraint limits\n"); + printf("Value exceeds regulator constraint limits %d..%d uV\n", + uc_pdata->min_uV, uc_pdata->max_uV); return CMD_RET_FAILURE; } -- cgit v0.10.2 From 801ab9e93c228b30bb526bb1b431e042d46ba2e8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:08 -0600 Subject: dm: video: Add support for video bridges A video bridge typically converts video from one format to another, e.g. DisplayPort to LVDS. Add driver model support for these with a simple interface to control activation and backlight. The uclass supports GPIO control of power and reset lines. Signed-off-by: Simon Glass diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 9ae23e8..3244cd7 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -240,3 +240,5 @@ config VIDEO_TEGRA124 HDMI. At present only eDP is supported by U-Boot. This option enables this support which can be used on devices which have an eDP display connected. + +source "drivers/video/bridge/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 2ead7f1..599fe83 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -54,3 +54,5 @@ obj-$(CONFIG_AM335X_LCD) += am335x-fb.o obj-$(CONFIG_VIDEO_PARADE) += parade.o obj-${CONFIG_VIDEO_TEGRA124} += tegra124/ + +obj-y += bridge/ diff --git a/drivers/video/bridge/Kconfig b/drivers/video/bridge/Kconfig new file mode 100644 index 0000000..7ba6b17 --- /dev/null +++ b/drivers/video/bridge/Kconfig @@ -0,0 +1,8 @@ +config VIDEO_BRIDGE + bool "Support video bridges" + depends on DM + help + Some platforms use video bridges to convert from one output to + another. For example, where the SoC only supports eDP and the LCD + requires LVDS, an eDP->LVDS bridge chip can be used to provide the + necessary conversion. This option enables support for these devices. diff --git a/drivers/video/bridge/Makefile b/drivers/video/bridge/Makefile new file mode 100644 index 0000000..f4bf087 --- /dev/null +++ b/drivers/video/bridge/Makefile @@ -0,0 +1,7 @@ +# +# Copyright (C) 2015 Google, Inc +# Written by Simon Glass +# +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_VIDEO_BRIDGE) += video-bridge-uclass.o diff --git a/drivers/video/bridge/video-bridge-uclass.c b/drivers/video/bridge/video-bridge-uclass.c new file mode 100644 index 0000000..e1ea428 --- /dev/null +++ b/drivers/video/bridge/video-bridge-uclass.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +int video_bridge_set_backlight(struct udevice *dev, int percent) +{ + struct video_bridge_ops *ops = video_bridge_get_ops(dev); + + if (!ops->set_backlight) + return -ENOSYS; + + return ops->set_backlight(dev, percent); +} + +int video_bridge_attach(struct udevice *dev) +{ + struct video_bridge_ops *ops = video_bridge_get_ops(dev); + + if (!ops->attach) + return -ENOSYS; + + return ops->attach(dev); +} + +int video_bridge_check_attached(struct udevice *dev) +{ + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); + struct video_bridge_ops *ops = video_bridge_get_ops(dev); + int ret; + + if (!ops->check_attached) { + ret = dm_gpio_get_value(&uc_priv->hotplug); + + return ret > 0 ? 0 : ret == 0 ? -ENOTCONN : ret; + } + + return ops->check_attached(dev); +} + +static int video_bridge_pre_probe(struct udevice *dev) +{ + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); + int ret; + + debug("%s\n", __func__); + ret = gpio_request_by_name(dev, "sleep-gpios", 0, + &uc_priv->sleep, GPIOD_IS_OUT); + if (ret) { + debug("%s: Could not decode sleep-gpios (%d)\n", __func__, ret); + return ret; + } + ret = dm_gpio_set_pull(&uc_priv->sleep, GPIO_PULL_NONE); + if (ret) { + debug("%s: Could not set sleep pull value\n", __func__); + return ret; + } + ret = gpio_request_by_name(dev, "reset-gpios", 0, &uc_priv->reset, + GPIOD_IS_OUT); + if (ret) { + debug("%s: Could not decode reset-gpios (%d)\n", __func__, ret); + return ret; + } + ret = dm_gpio_set_pull(&uc_priv->reset, GPIO_PULL_NONE); + if (ret) { + debug("%s: Could not set reset pull value\n", __func__); + return ret; + } + ret = gpio_request_by_name(dev, "hotplug-gpios", 0, &uc_priv->hotplug, + GPIOD_IS_IN); + if (ret && ret != -ENOENT) { + debug("%s: Could not decode hotplug (%d)\n", __func__, ret); + return ret; + } + + return 0; +} + +int video_bridge_set_active(struct udevice *dev, bool active) +{ + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); + int ret; + + debug("%s: %d\n", __func__, active); + ret = dm_gpio_set_value(&uc_priv->sleep, !active); + if (ret) + return ret; + if (active) { + ret = dm_gpio_set_value(&uc_priv->reset, true); + if (ret) + return ret; + udelay(10); + ret = dm_gpio_set_value(&uc_priv->reset, false); + } + + return ret; +} + +UCLASS_DRIVER(video_bridge) = { + .id = UCLASS_VIDEO_BRIDGE, + .name = "video_bridge", + .per_device_auto_alloc_size = sizeof(struct video_bridge_priv), + .pre_probe = video_bridge_pre_probe, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 777b101..c744044 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -57,6 +57,7 @@ enum uclass_id { UCLASS_USB, /* USB bus */ UCLASS_USB_DEV_GENERIC, /* USB generic device */ UCLASS_USB_HUB, /* USB hub */ + UCLASS_VIDEO_BRIDGE, /* Video bridge, e.g. DisplayPort to LVDS */ UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/video_bridge.h b/include/video_bridge.h new file mode 100644 index 0000000..c7b8681 --- /dev/null +++ b/include/video_bridge.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __VIDEO_BRIDGE +#define __VIDEO_BRIDGE + +#include + +/** + * struct video_bridge_priv - uclass information for video bridges + * + * @sleep: GPIO to assert to power down the bridge + * @reset: GPIO to assert to reset the bridge + * @hotplug: Optional GPIO to check if bridge is connected + */ +struct video_bridge_priv { + struct gpio_desc sleep; + struct gpio_desc reset; + struct gpio_desc hotplug; +}; + +/** + * Operations for video bridges + */ +struct video_bridge_ops { + /** + * attach() - attach a video bridge + * + * @return 0 if OK, -ve on error + */ + int (*attach)(struct udevice *dev); + + /** + * check_attached() - check if a bridge is correctly attached + * + * This method is optional - if not provided then the hotplug GPIO + * will be checked instead. + * + * @dev: Device to check + * @return 0 if attached, -EENOTCONN if not, or other -ve error + */ + int (*check_attached)(struct udevice *dev); + + /** + * set_backlight() - Set the backlight brightness + * + * @dev: device to adjust + * @percent: brightness percentage (0=off, 100=full brightness) + * @return 0 if OK, -ve on error + */ + int (*set_backlight)(struct udevice *dev, int percent); +}; + +#define video_bridge_get_ops(dev) \ + ((struct video_bridge_ops *)(dev)->driver->ops) + +/** + * video_bridge_attach() - attach a video bridge + * + * @return 0 if OK, -ve on error + */ +int video_bridge_attach(struct udevice *dev); + +/** + * video_bridge_set_backlight() - Set the backlight brightness + * + * @percent: brightness percentage (0=off, 100=full brightness) + * @return 0 if OK, -ve on error + */ +int video_bridge_set_backlight(struct udevice *dev, int percent); + +/** + * video_bridge_set_active() - take the bridge in/out of reset/powerdown + * + * @dev: Device to adjust + * @active: true to power up and reset, false to power down + */ +int video_bridge_set_active(struct udevice *dev, bool active); + +/** + * check_attached() - check if a bridge is correctly attached + * + * @dev: Device to check + * @return 0 if attached, -EENOTCONN if not, or other -ve error + */ +int video_bridge_check_attached(struct udevice *dev); + +#endif -- cgit v0.10.2 From 5eaeadaa3afee088421e26cbbc07b6969a9e3425 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:20 -0600 Subject: video: Work around lack of pinctrl We haven't quite got pinctrl ready to apply to mainline. We don't want to GPIO pull-up/down support to the driver model GPIO layer either. So work around this for now. We can address this when pinctrl is complete. Signed-off-by: Simon Glass diff --git a/drivers/video/bridge/video-bridge-uclass.c b/drivers/video/bridge/video-bridge-uclass.c index e1ea428..6c5990f 100644 --- a/drivers/video/bridge/video-bridge-uclass.c +++ b/drivers/video/bridge/video-bridge-uclass.c @@ -57,22 +57,30 @@ static int video_bridge_pre_probe(struct udevice *dev) debug("%s: Could not decode sleep-gpios (%d)\n", __func__, ret); return ret; } - ret = dm_gpio_set_pull(&uc_priv->sleep, GPIO_PULL_NONE); - if (ret) { - debug("%s: Could not set sleep pull value\n", __func__); - return ret; - } + /* + * Drop this for now as we do not have driver model pinctrl support + * + * ret = dm_gpio_set_pull(&uc_priv->sleep, GPIO_PULL_NONE); + * if (ret) { + * debug("%s: Could not set sleep pull value\n", __func__); + * return ret; + * } + */ ret = gpio_request_by_name(dev, "reset-gpios", 0, &uc_priv->reset, GPIOD_IS_OUT); if (ret) { debug("%s: Could not decode reset-gpios (%d)\n", __func__, ret); return ret; } - ret = dm_gpio_set_pull(&uc_priv->reset, GPIO_PULL_NONE); - if (ret) { - debug("%s: Could not set reset pull value\n", __func__); - return ret; - } + /* + * Drop this for now as we do not have driver model pinctrl support + * + * ret = dm_gpio_set_pull(&uc_priv->reset, GPIO_PULL_NONE); + * if (ret) { + * debug("%s: Could not set reset pull value\n", __func__); + * return ret; + * } + */ ret = gpio_request_by_name(dev, "hotplug-gpios", 0, &uc_priv->hotplug, GPIOD_IS_IN); if (ret && ret != -ENOENT) { -- cgit v0.10.2 From bcd5dfffe6d7827c5c1df96b2e5168036d522220 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:09 -0600 Subject: dm: video: Add support for the Parade PS8622/625 bridge This chip provides an eDP to LVDS bridge which is useful for SoCs that don't support LVDS displays (or it would waste scarce pins). The setup is included in the device tree. Signed-off-by: Simon Glass diff --git a/doc/device-tree-bindings/video/bridge/ps8622.txt b/doc/device-tree-bindings/video/bridge/ps8622.txt new file mode 100644 index 0000000..66d5d07 --- /dev/null +++ b/doc/device-tree-bindings/video/bridge/ps8622.txt @@ -0,0 +1,33 @@ +ps8622-bridge bindings + +Required properties: + - compatible: "parade,ps8622" or "parade,ps8625" + - reg: first i2c address of the bridge + - sleep-gpios: OF device-tree gpio specification for PD_ pin. + - reset-gpios: OF device-tree gpio specification for RST_ pin. + - parade,regs: List of 3-byte registers tuples to write: + + +Optional properties: + - lane-count: number of DP lanes to use + - use-external-pwm: backlight will be controlled by an external PWM + - video interfaces: Device node can contain video interface port + nodes for panel according to [1]. + +[1]: Documentation/devicetree/bindings/media/video-interfaces.txt + +Example: + lvds-bridge@48 { + compatible = "parade,ps8622"; + reg = <0x48>; + sleep-gpios = <&gpc3 6 1 0 0>; + reset-gpios = <&gpc3 1 1 0 0>; + lane-count = <1>; + ports { + port@0 { + bridge_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + }; diff --git a/drivers/video/bridge/Kconfig b/drivers/video/bridge/Kconfig index 7ba6b17..589795d 100644 --- a/drivers/video/bridge/Kconfig +++ b/drivers/video/bridge/Kconfig @@ -6,3 +6,13 @@ config VIDEO_BRIDGE another. For example, where the SoC only supports eDP and the LCD requires LVDS, an eDP->LVDS bridge chip can be used to provide the necessary conversion. This option enables support for these devices. + +config VIDEO_BRIDGE_PARADE_PS862X + bool "Support Parade PS862X DP->LVDS bridge" + depends on VIDEO_BRIDGE + help + The Parade PS8622 and PS8625 are DisplayPort-to-LVDS (Low voltage + differential signalling) converters. They enable an LVDS LCD panel + to be connected to an eDP output device such as an SoC that lacks + LVDS capability, or where LVDS requires too many signals to route + on the PCB. Setup parameters are provided in the device tree. diff --git a/drivers/video/bridge/Makefile b/drivers/video/bridge/Makefile index f4bf087..c7cc563 100644 --- a/drivers/video/bridge/Makefile +++ b/drivers/video/bridge/Makefile @@ -5,3 +5,4 @@ # SPDX-License-Identifier: GPL-2.0+ obj-$(CONFIG_VIDEO_BRIDGE) += video-bridge-uclass.o +obj-$(CONFIG_VIDEO_BRIDGE_PARADE_PS862X) += ps862x.o diff --git a/drivers/video/bridge/ps862x.c b/drivers/video/bridge/ps862x.c new file mode 100644 index 0000000..80f63e3 --- /dev/null +++ b/drivers/video/bridge/ps862x.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Initialisation of the chip is a process of writing certain values into + * certain registers over i2c bus. The chip in fact responds to a range of + * addresses on the i2c bus, so for each written value three parameters are + * required: i2c address, register address and the actual value. + * + * The base address is derived from the device tree, but oddly the chip + * responds on several addresses with different register sets for each. + */ + +/** + * ps8622_write() Write a PS8622 eDP bridge i2c register + * + * @param dev I2C device + * @param addr_off offset from the i2c base address for ps8622 + * @param reg_addr register address to write + * @param value value to be written + * @return 0 on success, non-0 on failure + */ +static int ps8622_write(struct udevice *dev, unsigned addr_off, + unsigned char reg_addr, unsigned char value) +{ + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); + uint8_t buf[2]; + struct i2c_msg msg; + int ret; + + msg.addr = chip->chip_addr + addr_off; + msg.flags = 0; + buf[0] = reg_addr; + buf[1] = value; + msg.buf = buf; + msg.len = 2; + ret = dm_i2c_xfer(dev, &msg, 1); + if (ret) { + debug("%s: write failed, reg=%#x, value=%#x, ret=%d\n", + __func__, reg_addr, value, ret); + return ret; + } + + return 0; +} + +static int ps8622_set_backlight(struct udevice *dev, int percent) +{ + int level = percent * 255 / 100; + + debug("%s: level=%d\n", __func__, level); + return ps8622_write(dev, 0x01, 0xa7, level); +} + +static int ps8622_attach(struct udevice *dev) +{ + const uint8_t *params; + struct udevice *reg; + int ret, i, len; + + debug("%s: %s\n", __func__, dev->name); + /* set the LDO providing the 1.2V rail to the Parade bridge */ + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "power-supply", ®); + if (!ret) { + ret = regulator_autoset(reg); + } else if (ret != -ENOENT) { + debug("%s: Failed to enable power: ret=%d\n", __func__, ret); + return ret; + } + + ret = video_bridge_set_active(dev, true); + if (ret) + return ret; + + params = fdt_getprop(gd->fdt_blob, dev->of_offset, "parade,regs", &len); + if (!params || len % 3) { + debug("%s: missing/invalid params=%p, len=%x\n", __func__, + params, len); + return -EINVAL; + } + + /* need to wait 20ms after power on before doing I2C writes */ + mdelay(20); + for (i = 0; i < len; i += 3) { + ret = ps8622_write(dev, params[i + 0], params[i + 1], + params[i + 2]); + if (ret) + return ret; + } + + return 0; +} + +static int ps8622_probe(struct udevice *dev) +{ + debug("%s\n", __func__); + if (device_get_uclass_id(dev->parent) != UCLASS_I2C) + return -EPROTONOSUPPORT; + + return 0; +} + +struct video_bridge_ops ps8622_ops = { + .attach = ps8622_attach, + .set_backlight = ps8622_set_backlight, +}; + +static const struct udevice_id ps8622_ids[] = { + { .compatible = "parade,ps8622", }, + { .compatible = "parade,ps8625", }, + { } +}; + +U_BOOT_DRIVER(parade_ps8622) = { + .name = "parade_ps8622", + .id = UCLASS_VIDEO_BRIDGE, + .of_match = ps8622_ids, + .probe = ps8622_probe, + .ops = &ps8622_ops, +}; -- cgit v0.10.2 From 048dba0191b55f635468671d942347651067d7e4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:10 -0600 Subject: dm: video: Add support for the NXP PTN3460 bridge This chip provides an eDP to LVDS bridge which is useful for SoCs that don't support LVDS displays (or it would waste scarce pins). There is no setup required by this chip, other than to adjust power-down and reset pins, and those are managed by the uclass. Signed-off-by: Simon Glass diff --git a/drivers/video/bridge/Kconfig b/drivers/video/bridge/Kconfig index 589795d..2a3b6c4 100644 --- a/drivers/video/bridge/Kconfig +++ b/drivers/video/bridge/Kconfig @@ -16,3 +16,12 @@ config VIDEO_BRIDGE_PARADE_PS862X to be connected to an eDP output device such as an SoC that lacks LVDS capability, or where LVDS requires too many signals to route on the PCB. Setup parameters are provided in the device tree. + +config VIDEO_BRIDGE_NXP_PTN3460 + bool "Support NXP PTN3460 DP->LVDS bridge" + depends on VIDEO_BRIDGE + help + The NXP PTN3460 is a DisplayPort-to-LVDS (Low voltage differential + signalling) converter. It enables an LVDS LCD panel to be connected + to an eDP output device such as an SoC that lacks LVDS capability, + or where LVDS requires too many signals to route on the PCB. diff --git a/drivers/video/bridge/Makefile b/drivers/video/bridge/Makefile index c7cc563..ce731fa 100644 --- a/drivers/video/bridge/Makefile +++ b/drivers/video/bridge/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_VIDEO_BRIDGE) += video-bridge-uclass.o obj-$(CONFIG_VIDEO_BRIDGE_PARADE_PS862X) += ps862x.o +obj-$(CONFIG_VIDEO_BRIDGE_NXP_PTN3460) += ptn3460.o diff --git a/drivers/video/bridge/ptn3460.c b/drivers/video/bridge/ptn3460.c new file mode 100644 index 0000000..2e2ae7c --- /dev/null +++ b/drivers/video/bridge/ptn3460.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +static int ptn3460_attach(struct udevice *dev) +{ + int ret; + + debug("%s: %s\n", __func__, dev->name); + ret = video_bridge_set_active(dev, true); + if (ret) + return ret; + + return 0; +} + +struct video_bridge_ops ptn3460_ops = { + .attach = ptn3460_attach, +}; + +static const struct udevice_id ptn3460_ids[] = { + { .compatible = "nxp,ptn3460", }, + { } +}; + +U_BOOT_DRIVER(parade_ptn3460) = { + .name = "nmp_ptn3460", + .id = UCLASS_VIDEO_BRIDGE, + .of_match = ptn3460_ids, + .ops = &ptn3460_ops, +}; -- cgit v0.10.2 From c7d50e7fb9cc7e71dde7d630b157334e7f8b63fb Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:11 -0600 Subject: exynos: spi: Convert the timeout to debug() Since the timeout is reported through normal channels, and is sometimes expected (e.g. if the bus is being probed for a non-existent device), don't display the message in the driver. In general, drivers should not write to the console as this limits their usefulness in error conditions. Signed-off-by: Simon Glass diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c index 67f6b2d..6d77c31 100644 --- a/drivers/spi/exynos_spi.c +++ b/drivers/spi/exynos_spi.c @@ -190,9 +190,9 @@ static int spi_rx_tx(struct exynos_spi_priv *priv, int todo, spi_request_bytes(regs, toread, step); } if (priv->skip_preamble && get_timer(start) > 100) { - printf("SPI timeout: in_bytes=%d, out_bytes=%d, ", - in_bytes, out_bytes); - return -1; + debug("SPI timeout: in_bytes=%d, out_bytes=%d, ", + in_bytes, out_bytes); + return -ETIMEDOUT; } } -- cgit v0.10.2 From c95ed7d9e76e81c3728438ccbd3bbd119cf46886 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:12 -0600 Subject: exynos: Correct return value in exynos_mmc_init() This function should return 0 on success, not 1. Fix it. Signed-off-by: Simon Glass diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c index 8e1968a..522eab9 100644 --- a/drivers/mmc/s5p_sdhci.c +++ b/drivers/mmc/s5p_sdhci.c @@ -202,6 +202,6 @@ int exynos_mmc_init(const void *blob) process_nodes(blob, node_list, count); - return 1; + return 0; } #endif -- cgit v0.10.2 From a507454b137e870cd418be326b5bc2e96f580efa Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:13 -0600 Subject: exynos: Add support for the DisplayPort hotplug detect Allow this function to be selected using the pinmux API. Signed-off-by: Simon Glass diff --git a/arch/arm/cpu/armv7/exynos/pinmux.c b/arch/arm/cpu/armv7/exynos/pinmux.c index be43e22..e97cb37 100644 --- a/arch/arm/cpu/armv7/exynos/pinmux.c +++ b/arch/arm/cpu/armv7/exynos/pinmux.c @@ -496,6 +496,16 @@ static int exynos5_pinmux_config(int peripheral, int flags) case PERIPH_ID_SPI4: exynos5_spi_config(peripheral); break; + case PERIPH_ID_DPHPD: + /* Set Hotplug detect for DP */ + gpio_cfg_pin(EXYNOS5_GPIO_X07, S5P_GPIO_FUNC(0x3)); + + /* + * Hotplug detect should have an external pullup; disable the + * internal pulldown so they don't fight. + */ + gpio_set_pull(EXYNOS5_GPIO_X07, S5P_GPIO_PULL_NONE); + break; default: debug("%s: invalid peripheral %d", __func__, peripheral); return -1; diff --git a/arch/arm/include/asm/arch-exynos/periph.h b/arch/arm/include/asm/arch-exynos/periph.h index 5c1c3d4..fdc9e87 100644 --- a/arch/arm/include/asm/arch-exynos/periph.h +++ b/arch/arm/include/asm/arch-exynos/periph.h @@ -53,6 +53,7 @@ enum periph_id { PERIPH_ID_PWM2, PERIPH_ID_PWM3, PERIPH_ID_PWM4, + PERIPH_ID_DPHPD, PERIPH_ID_I2C10 = 203, PERIPH_ID_NONE = -1, -- cgit v0.10.2 From 129c942f321ae6f634e1b5668a9a30b19ad9ac3a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:14 -0600 Subject: exynos: video: Correct debug output We should not print a message from the driver when the display is set up. This is normal behaviour. Change this message to use debug(). Also remove the double newline on another debug message. Signed-off-by: Simon Glass diff --git a/drivers/video/exynos_dp.c b/drivers/video/exynos_dp.c index f60b060..c3f7a3a 100644 --- a/drivers/video/exynos_dp.c +++ b/drivers/video/exynos_dp.c @@ -967,7 +967,7 @@ unsigned int exynos_init_dp(void) return ret; } - printf("Exynos DP init done\n"); + debug("Exynos DP init done\n"); return ret; } diff --git a/drivers/video/exynos_dp_lowlevel.c b/drivers/video/exynos_dp_lowlevel.c index bf0ea10..05118f8 100644 --- a/drivers/video/exynos_dp_lowlevel.c +++ b/drivers/video/exynos_dp_lowlevel.c @@ -823,7 +823,7 @@ int exynos_dp_read_bytes_from_i2c(unsigned int device_addr, reg = readl(&dp_regs->aux_rx_comm); if (reg == AUX_RX_COMM_AUX_DEFER || reg == AUX_RX_COMM_I2C_DEFER) { - printf("DP Defer: %d\n\n", reg); + printf("DP Defer: %d\n", reg); defer = 1; } } -- cgit v0.10.2 From 71db6341c53038db5a73343da2c559dea6c89a7a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:15 -0600 Subject: exynos: Tidy up CPU frequency display Line up the display with the line below, e.g.: CPU: Exynos5250 @ 1.7 GHz Model: Google Spring DRAM: 2 GiB MMC: EXYNOS DWMMC: 0 Also show the speed as GHz where appropriate. Signed-off-by: Simon Glass diff --git a/arch/arm/cpu/armv7/s5p-common/cpu_info.c b/arch/arm/cpu/armv7/s5p-common/cpu_info.c index a8d91e7..154d674 100644 --- a/arch/arm/cpu/armv7/s5p-common/cpu_info.c +++ b/arch/arm/cpu/armv7/s5p-common/cpu_info.c @@ -30,11 +30,8 @@ u32 get_device_type(void) #ifdef CONFIG_DISPLAY_CPUINFO int print_cpuinfo(void) { - char buf[32]; - - printf("CPU:\t%s%X@%sMHz\n", - s5p_get_cpu_name(), s5p_cpu_id, - strmhz(buf, get_arm_clk())); + printf("CPU: %s%X @ ", s5p_get_cpu_name(), s5p_cpu_id); + print_freq(get_arm_clk(), "\n"); return 0; } -- cgit v0.10.2 From eca4866586ca36a47cd94811a294ac62ebd17abc Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:16 -0600 Subject: dm: gpio: Check a GPIO is valid before using it Since a gpio_desc is allowed to be invalid we should return an error indicating that the operation cannot be completed. This can happen if the GPIO is optional - e.g. some devices may have a reset line and some may not. Signed-off-by: Simon Glass diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 4efda31..4cce11f 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -250,8 +250,12 @@ int gpio_free(unsigned gpio) static int check_reserved(struct gpio_desc *desc, const char *func) { - struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(desc->dev); + struct gpio_dev_priv *uc_priv; + + if (!dm_gpio_is_valid(desc)) + return -ENOENT; + uc_priv = dev_get_uclass_priv(desc->dev); if (!uc_priv->name[desc->offset]) { printf("%s: %s: error: gpio %s%d not reserved\n", desc->dev->name, func, -- cgit v0.10.2 From 59408eb2051d2432533480b6ff0788d323c553a6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:17 -0600 Subject: dts: exynos: snow: Add memory layout description Add a description of the snow memory layout to assist flashing tools which want to be able to deal with any exynos image. Signed-off-by: Simon Glass diff --git a/arch/arm/dts/exynos5250-snow.dts b/arch/arm/dts/exynos5250-snow.dts index 653efb4..3591252 100644 --- a/arch/arm/dts/exynos5250-snow.dts +++ b/arch/arm/dts/exynos5250-snow.dts @@ -43,6 +43,59 @@ i2s = "/sound@3830000"; }; + memory { + reg = <0x40000000 0x80000000>; + }; + + chosen { + bootargs = "console=tty1"; + stdout-path = "serial3:115200n8"; + }; + + iram { + reg = <0x02020000 0x60000>; + }; + + config { + samsung,bl1-offset = <0x1400>; + samsung,bl2-offset = <0x3400>; + u-boot-memory = "/memory"; + u-boot-offset = <0x3e00000 0x100000>; + }; + + flash { + reg = <0 0x100000>; + #address-cells = <1>; + #size-cells = <1>; + pre-boot { + label = "bl1 pre-boot"; + reg = <0 0x2000>; + read-only; + filename = "e5250.nbl1.bin"; + type = "blob exynos-bl1"; + required; + }; + + spl { + label = "bl2 spl"; + reg = <0x2000 0x4000>; + read-only; + filename = "bl2.bin"; + type = "blob exynos-bl2 boot,dtb"; + payload = "/flash/ro-boot"; + required; + }; + + ro-boot { + label = "u-boot"; + reg = <0x6000 0x9a000>; + read-only; + type = "blob boot,dtb"; + required; + }; + }; + + i2c4: i2c@12CA0000 { cros_ec_old: cros-ec@1e { reg = <0x1e>; -- cgit v0.10.2 From 48b6c32d7706e58f5113b539cf695ec0403c3b45 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:18 -0600 Subject: dts: exynos: pit: Add a new node for the parade video bridge driver The new driver supports driver model and configuration via device tree. Add a node for pit, which needs this driver. Signed-off-by: Simon Glass diff --git a/arch/arm/dts/exynos5420-peach-pit.dts b/arch/arm/dts/exynos5420-peach-pit.dts index 5182b2b..2d2b7c9 100644 --- a/arch/arm/dts/exynos5420-peach-pit.dts +++ b/arch/arm/dts/exynos5420-peach-pit.dts @@ -65,11 +65,129 @@ compatible = "maxim,max98090-codec"; }; - edp-lvds-bridge@48 { - compatible = "parade,ps8625"; - reg = <0x48>; - sleep-gpio = <&gpx3 5 GPIO_ACTIVE_HIGH>; - reset-gpio = <&gpy7 7 GPIO_ACTIVE_HIGH>; + edp-lvds-bridge@48 { + compatible = "parade,ps8625"; + reg = <0x48>; + sleep-gpios = <&gpx3 5 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpy7 7 GPIO_ACTIVE_LOW>; + parade,regs = /bits/ 8 < + 0x02 0xa1 0x01 /* HPD low */ + /* + * SW setting + * [1:0] SW output 1.2V voltage is lower to 96% + */ + 0x04 0x14 0x01 + /* + * RCO SS setting + * [5:4] = b01 0.5%, b10 1%, b11 1.5% + */ + 0x04 0xe3 0x20 + 0x04 0xe2 0x80 /* [7] RCO SS enable */ + /* + * RPHY Setting + * [3:2] CDR tune wait cycle before + * measure for fine tune b00: 1us, + * 01: 0.5us, 10:2us, 11:4us. + */ + 0x04 0x8a 0x0c + 0x04 0x89 0x08 /* [3] RFD always on */ + /* + * CTN lock in/out: + * 20000ppm/80000ppm. Lock out 2 + * times. + */ + 0x04 0x71 0x2d + /* + * 2.7G CDR settings + * NOF=40LSB for HBR CDR setting + */ + 0x04 0x7d 0x07 + 0x04 0x7b 0x00 /* [1:0] Fmin=+4bands */ + 0x04 0x7a 0xfd /* [7:5] DCO_FTRNG=+-40% */ + /* + * 1.62G CDR settings + * [5:2]NOF=64LSB [1:0]DCO scale is 2/5 + */ + 0x04 0xc0 0x12 + 0x04 0xc1 0x92 /* Gitune=-37% */ + 0x04 0xc2 0x1c /* Fbstep=100% */ + 0x04 0x32 0x80 /* [7]LOS signal disable */ + /* + * RPIO Setting + * [7:4] LVDS driver bias current : + * 75% (250mV swing) + */ + 0x04 0x00 0xb0 + /* + * [7:6] Right-bar GPIO output strength is 8mA + */ + 0x04 0x15 0x40 + /* EQ Training State Machine Setting */ + 0x04 0x54 0x10 /* RCO calibration start */ + /* [4:0] MAX_LANE_COUNT set to one lane */ + 0x01 0x02 0x81 + /* [4:0] LANE_COUNT_SET set to one lane */ + 0x01 0x21 0x81 + 0x00 0x52 0x20 + 0x00 0xf1 0x03 /* HPD CP toggle enable */ + 0x00 0x62 0x41 + /* Counter number add 1ms counter delay */ + 0x00 0xf6 0x01 + /* + * [6]PWM function control by + * DPCD0040f[7], default is PWM + * block always works. + */ + 0x00 0x77 0x06 + /* + * 04h Adjust VTotal tolerance to + * fix the 30Hz no display issue + */ + 0x00 0x4c 0x04 + /* DPCD00400='h00, Parade OUI = 'h001cf8 */ + 0x01 0xc0 0x00 + 0x01 0xc1 0x1c /* DPCD00401='h1c */ + 0x01 0xc2 0xf8 /* DPCD00402='hf8 */ + /* + * DPCD403~408 = ASCII code + * D2SLV5='h4432534c5635 + */ + 0x01 0xc3 0x44 + 0x01 0xc4 0x32 /* DPCD404 */ + 0x01 0xc5 0x53 /* DPCD405 */ + 0x01 0xc6 0x4c /* DPCD406 */ + 0x01 0xc7 0x56 /* DPCD407 */ + 0x01 0xc8 0x35 /* DPCD408 */ + /* + * DPCD40A, Initial Code major revision + * '01' + */ + 0x01 0xca 0x01 + /* DPCD40B Initial Code minor revision '05' */ + 0x01 0xcb 0x05 + /* DPCD720 Select internal PWM */ + 0x01 0xa5 0xa0 + /* + * FFh for 100% PWM of brightness, 0h for 0% + * brightness + */ + 0x01 0xa7 0xff + /* + * Set LVDS output as 6bit-VESA mapping, + * single LVDS channel + */ + 0x01 0xcc 0x13 + /* Enable SSC set by register */ + 0x02 0xb1 0x20 + /* + * Set SSC enabled and +/-1% central + * spreading + */ + 0x04 0x10 0x16 + /* MPU Clock source: LC => RCO */ + 0x04 0x59 0x60 + 0x04 0x54 0x14 /* LC -> RCO */ + 0x02 0xa1 0x91>; /* HPD high */ }; }; -- cgit v0.10.2 From fa9ec45ca4d31bd24864139696d699aa3165427b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:19 -0600 Subject: dts: exynos: snow: Add a new node for the NXP video bridge driver The driver supports driver model. Add a node for snow, which needs it. Signed-off-by: Simon Glass diff --git a/arch/arm/dts/exynos5250-snow.dts b/arch/arm/dts/exynos5250-snow.dts index 3591252..334a5af 100644 --- a/arch/arm/dts/exynos5250-snow.dts +++ b/arch/arm/dts/exynos5250-snow.dts @@ -206,6 +206,22 @@ }; }; + i2c@12CD0000 { + ptn3460: lvds-bridge@20 { + compatible = "nxp,ptn3460"; + reg = <0x20>; + sleep-gpios = <&gpy2 5 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpx1 5 GPIO_ACTIVE_LOW>; + hotplug-gpios = <&gpx0 7 GPIO_ACTIVE_HIGH>; + edid-emulation = <5>; + }; + + soundcodec@22 { + reg = <0x22>; + compatible = "maxim,max98095-codec"; + }; + }; + spi@12d30000 { spi-max-frequency = <50000000>; firmware_storage_spi: flash@0 { @@ -238,26 +254,6 @@ status = "disabled"; }; - i2c@12CD0000 { - soundcodec@22 { - reg = <0x22>; - compatible = "maxim,max98095-codec"; - }; - - ptn3460-bridge@20 { - compatible = "nxp,ptn3460"; - reg = <0x20>; - /* - * TODO(sjg@chromium.org): Use GPIOs here - * powerdown-gpio = <&gpy2 5 0>; - * reset-gpio = <&gpx1 5 0>; - * edid-emulation = <5>; - * pinctrl-names = "default"; - * pinctrl-0 = <&ptn3460_gpios>; - */ - }; - }; - i2c@12C60000 { max77686@09 { reg = <0x9>; -- cgit v0.10.2 From 8bba6cc0db3f72b313c0e45ae57d9b733ab00249 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:26 -0600 Subject: exynos: dts: Drop the old TPS65090 I2C node While the AP can access the main PMIC on snow, it must coordinate with the EC which also wants access. Drop the old definition, which can in principle generate collision errors. We will use the new arbitration driver instead. Signed-off-by: Simon Glass diff --git a/arch/arm/dts/exynos5250-snow.dts b/arch/arm/dts/exynos5250-snow.dts index 334a5af..32c0098 100644 --- a/arch/arm/dts/exynos5250-snow.dts +++ b/arch/arm/dts/exynos5250-snow.dts @@ -95,22 +95,6 @@ }; }; - - i2c4: i2c@12CA0000 { - cros_ec_old: cros-ec@1e { - reg = <0x1e>; - compatible = "google,cros-ec-i2c"; - i2c-max-frequency = <100000>; - u-boot,i2c-offset-len = <0>; - ec-interrupt = <&gpx1 6 GPIO_ACTIVE_LOW>; - }; - - power-regulator@48 { - compatible = "ti,tps65090"; - reg = <0x48>; - }; - }; - i2c-arbitrator { compatible = "i2c-arb-gpio-challenge"; #address-cells = <1>; -- cgit v0.10.2 From c65dc7d8742837addfb1b0da3b090967423f4a9b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:27 -0600 Subject: exynos: Add common board code for exynos5 boards that use device tree Some boards use device tree for almost all board-specific configuration. They therefore do not need their own separate board code, but can all use the same version. Add a common version of the board code. It uses the PMIC, regulator and video bridge uclasses. This will support smdk5250, smdk5420, snow, spring, pit and pi. Signed-off-by: Simon Glass diff --git a/board/samsung/common/Makefile b/board/samsung/common/Makefile index 5fb01ce..6cbd906 100644 --- a/board/samsung/common/Makefile +++ b/board/samsung/common/Makefile @@ -11,4 +11,5 @@ obj-$(CONFIG_MISC_COMMON) += misc.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_BOARD_COMMON) += board.o +obj-$(CONFIG_EXYNOS5_DT) += exynos5-dt.o endif diff --git a/board/samsung/common/exynos5-dt.c b/board/samsung/common/exynos5-dt.c new file mode 100644 index 0000000..7d1b88a --- /dev/null +++ b/board/samsung/common/exynos5-dt.c @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static void board_enable_audio_codec(void) +{ + int node, ret; + struct gpio_desc en_gpio; + + node = fdtdec_next_compatible(gd->fdt_blob, 0, + COMPAT_SAMSUNG_EXYNOS5_SOUND); + if (node <= 0) + return; + + ret = gpio_request_by_name_nodev(gd->fdt_blob, node, + "codec-enable-gpio", 0, &en_gpio, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret == -FDT_ERR_NOTFOUND) + return; + + /* Turn on the GPIO which connects to the codec's "enable" line. */ + gpio_set_pull(gpio_get_number(&en_gpio), S5P_GPIO_PULL_NONE); + +#ifdef CONFIG_SOUND_MAX98095 + /* Enable MAX98095 Codec */ + gpio_request(EXYNOS5_GPIO_X17, "max98095_enable"); + gpio_direction_output(EXYNOS5_GPIO_X17, 1); + gpio_set_pull(EXYNOS5_GPIO_X17, S5P_GPIO_PULL_NONE); +#endif +} + +int exynos_init(void) +{ + board_enable_audio_codec(); + + return 0; +} + +static int exynos_set_regulator(const char *name, uint uv) +{ + struct udevice *dev; + int ret; + + ret = regulator_get_by_platname(name, &dev); + if (ret) { + debug("%s: Cannot find regulator %s\n", __func__, name); + return ret; + } + ret = regulator_set_value(dev, uv); + if (ret) { + debug("%s: Cannot set regulator %s\n", __func__, name); + return ret; + } + + return 0; +} + +int exynos_power_init(void) +{ + struct udevice *dev; + int ret; + + ret = pmic_get("max77686", &dev); + if (!ret) { + /* TODO(sjg@chromium.org): Move into the clock/pmic API */ + ret = pmic_clrsetbits(dev, MAX77686_REG_PMIC_32KHZ, 0, + MAX77686_32KHCP_EN); + if (ret) + return ret; + ret = pmic_clrsetbits(dev, MAX77686_REG_PMIC_BBAT, 0, + MAX77686_BBCHOSTEN | MAX77686_BBCVS_3_5V); + if (ret) + return ret; + } else { + ret = pmic_get("s5m8767-pmic", &dev); + /* TODO(sjg@chromium.org): Use driver model to access clock */ +#ifdef CONFIG_PMIC_S5M8767 + if (!ret) + s5m8767_enable_32khz_cp(dev); +#endif + } + if (ret == -ENODEV) + return 0; + + ret = regulators_enable_boot_on(false); + if (ret) + return ret; + + ret = exynos_set_regulator("vdd_mif", 1100000); + if (ret) + return ret; + + /* + * This would normally be 1.3V, but since we are running slowly 1V + * is enough. For spring it helps reduce CPU temperature and avoid + * hangs with the case open. + */ + ret = exynos_set_regulator("vdd_arm", 1000000); + if (ret) + return ret; + ret = exynos_set_regulator("vdd_int", 1012500); + if (ret) + return ret; + ret = exynos_set_regulator("vdd_g3d", 1200000); + if (ret) + return ret; + + return 0; +} + +int board_get_revision(void) +{ + return 0; +} + +#ifdef CONFIG_LCD + +static int board_dp_bridge_init(struct udevice *dev) +{ + const int max_tries = 10; + int num_tries; + int ret; + + debug("%s\n", __func__); + ret = video_bridge_attach(dev); + if (ret) { + debug("video bridge init failed: %d\n", ret); + return ret; + } + + /* + * We need to wait for 90ms after bringing up the bridge since there + * is a phantom "high" on the HPD chip during its bootup. The phantom + * high comes within 7ms of de-asserting PD and persists for at least + * 15ms. The real high comes roughly 50ms after PD is de-asserted. The + * phantom high makes it hard for us to know when the NXP chip is up. + */ + mdelay(90); + + for (num_tries = 0; num_tries < max_tries; num_tries++) { + /* Check HPD. If it's high, or we don't have it, all is well */ + ret = video_bridge_check_attached(dev); + if (!ret || ret == -ENOENT) + return 0; + + debug("%s: eDP bridge failed to come up; try %d of %d\n", + __func__, num_tries, max_tries); + } + + /* Immediately go into bridge reset if the hp line is not high */ + return -EIO; +} + +static int board_dp_bridge_setup(const void *blob) +{ + const int max_tries = 2; + int num_tries; + struct udevice *dev; + int ret; + + /* Configure I2C registers for Parade bridge */ + ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &dev); + if (ret) { + debug("video bridge init failed: %d\n", ret); + return ret; + } + + if (strncmp(dev->driver->name, "parade", 6)) { + /* Mux HPHPD to the special hotplug detect mode */ + exynos_pinmux_config(PERIPH_ID_DPHPD, 0); + } + + for (num_tries = 0; num_tries < max_tries; num_tries++) { + ret = board_dp_bridge_init(dev); + if (!ret) + return 0; + if (num_tries == max_tries - 1) + break; + + /* + * If we're here, the bridge chip failed to initialise. + * Power down the bridge in an attempt to reset. + */ + video_bridge_set_active(dev, false); + + /* + * Arbitrarily wait 300ms here with DP_N low. Don't know for + * sure how long we should wait, but we're being paranoid. + */ + mdelay(300); + } + + return ret; +} + +void exynos_cfg_lcd_gpio(void) +{ + /* For Backlight */ + gpio_request(EXYNOS5_GPIO_B20, "lcd_backlight"); + gpio_cfg_pin(EXYNOS5_GPIO_B20, S5P_GPIO_OUTPUT); + gpio_set_value(EXYNOS5_GPIO_B20, 1); +} + +void exynos_set_dp_phy(unsigned int onoff) +{ + set_dp_phy_ctrl(onoff); +} + +static int board_dp_set_backlight(int percent) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &dev); + if (!ret) + ret = video_bridge_set_backlight(dev, percent); + + return ret; +} + +void exynos_backlight_on(unsigned int on) +{ + struct udevice *dev; + int ret; + + debug("%s(%u)\n", __func__, on); + if (!on) + return; + + ret = regulator_get_by_platname("vcd_led", &dev); + if (!ret) + ret = regulator_set_enable(dev, true); + if (ret) + debug("Failed to enable backlight: ret=%d\n", ret); + + /* T5 in the LCD timing spec (defined as > 10ms) */ + mdelay(10); + + /* board_dp_backlight_pwm */ + gpio_direction_output(EXYNOS5_GPIO_B20, 1); + + /* T6 in the LCD timing spec (defined as > 10ms) */ + mdelay(10); + + /* try to set the backlight in the bridge registers */ + ret = board_dp_set_backlight(80); + + /* if we have no bridge or it does not support backlight, use a GPIO */ + if (ret == -ENODEV || ret == -ENOSYS) { + gpio_request(EXYNOS5_GPIO_X30, "board_dp_backlight_en"); + gpio_direction_output(EXYNOS5_GPIO_X30, 1); + } +} + +void exynos_lcd_power_on(void) +{ + struct udevice *dev; + int ret; + + debug("%s\n", __func__); + ret = regulator_get_by_platname("lcd_vdd", &dev); + if (!ret) + ret = regulator_set_enable(dev, true); + if (ret) + debug("Failed to enable LCD panel: ret=%d\n", ret); + + ret = board_dp_bridge_setup(gd->fdt_blob); + if (ret && ret != -ENODEV) + printf("LCD bridge failed to enable: %d\n", ret); +} + +#endif + +#ifdef CONFIG_USB_DWC3 +static struct dwc3_device dwc3_device_data = { + .maximum_speed = USB_SPEED_SUPER, + .base = 0x12400000, + .dr_mode = USB_DR_MODE_PERIPHERAL, + .index = 0, +}; + +int usb_gadget_handle_interrupts(void) +{ + dwc3_uboot_handle_interrupt(0); + return 0; +} + +int board_usb_init(int index, enum usb_init_type init) +{ + struct exynos_usb3_phy *phy = (struct exynos_usb3_phy *) + samsung_get_base_usb3_phy(); + + if (!phy) { + error("usb3 phy not supported"); + return -ENODEV; + } + + set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_EN); + exynos5_usb3_phy_init(phy); + + return dwc3_uboot_init(&dwc3_device_data); +} +#endif +#ifdef CONFIG_SET_DFU_ALT_INFO +char *get_dfu_alt_system(char *interface, char *devstr) +{ + return getenv("dfu_alt_system"); +} + +char *get_dfu_alt_boot(char *interface, char *devstr) +{ + struct mmc *mmc; + char *alt_boot; + int dev_num; + + dev_num = simple_strtoul(devstr, NULL, 10); + + mmc = find_mmc_device(dev_num); + if (!mmc) + return NULL; + + if (mmc_init(mmc)) + return NULL; + + if (IS_SD(mmc)) + alt_boot = CONFIG_DFU_ALT_BOOT_SD; + else + alt_boot = CONFIG_DFU_ALT_BOOT_EMMC; + + return alt_boot; +} +#endif -- cgit v0.10.2 From d4061aa0e9bb48e55a958b59950e314c1c03f124 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:28 -0600 Subject: exynos: Enable new features for exynos5 boards Enable PMICs, regulators and the like so that new drivers will be made available. Signed-off-by: Simon Glass diff --git a/board/samsung/smdk5420/smdk5420.c b/board/samsung/smdk5420/smdk5420.c index 88f4044..57cc92c 100644 --- a/board/samsung/smdk5420/smdk5420.c +++ b/board/samsung/smdk5420/smdk5420.c @@ -50,9 +50,9 @@ static int has_edp_bridge(void) void exynos_lcd_power_on(void) { +#ifdef CONFIG_POWER_TPS65090 int ret; -#ifdef CONFIG_POWER_TPS65090 ret = tps65090_init(); if (ret < 0) { printf("%s: tps65090_init() failed\n", __func__); diff --git a/configs/arndale_defconfig b/configs/arndale_defconfig index aa489cf..ebac9ad 100644 --- a/configs/arndale_defconfig +++ b/configs/arndale_defconfig @@ -13,3 +13,5 @@ CONFIG_SOUND_MAX98095=y CONFIG_SOUND_WM8994=y CONFIG_USB=y CONFIG_DM_USB=y +CONFIG_DM_I2C=y +CONFIG_DM_I2C_COMPAT=y diff --git a/configs/odroid-xu3_defconfig b/configs/odroid-xu3_defconfig index 2b960d5..155ce39 100644 --- a/configs/odroid-xu3_defconfig +++ b/configs/odroid-xu3_defconfig @@ -7,3 +7,9 @@ CONFIG_DEFAULT_DEVICE_TREE="exynos5422-odroidxu3" # CONFIG_CMD_SETEXPR is not set CONFIG_USB=y CONFIG_DM_USB=y +CONFIG_CMD_NET=y +CONFIG_DM_I2C=y +CONFIG_DM_I2C_COMPAT=y +CONFIG_DM_PMIC=y +CONFIG_DM_REGULATOR=y +CONFIG_VIDEO_BRIDGE=y diff --git a/configs/peach-pi_defconfig b/configs/peach-pi_defconfig index 5050f5b..86d5a0b 100644 --- a/configs/peach-pi_defconfig +++ b/configs/peach-pi_defconfig @@ -14,3 +14,20 @@ CONFIG_USB=y CONFIG_DM_USB=y CONFIG_I2C_MUX=y CONFIG_I2C_CROS_EC_TUNNEL=y +CONFIG_SOUND=y +CONFIG_I2S=y +CONFIG_I2S_SAMSUNG=y +CONFIG_SOUND_MAX98095=y +CONFIG_SOUND_WM8994=y +CONFIG_DM_I2C=y +CONFIG_DM_PMIC=y +CONFIG_DM_REGULATOR=y +CONFIG_PMIC_TPS65090=y +CONFIG_REGULATOR_TPS65090=y +CONFIG_DM_I2C_COMPAT=y +CONFIG_I2C_ARB_GPIO_CHALLENGE=y +CONFIG_CMD_PMIC=y +CONFIG_CMD_REGULATOR=y +CONFIG_ERRNO_STR=y +CONFIG_VIDEO_BRIDGE=y +CONFIG_VIDEO_BRIDGE_PARADE_PS862X=y diff --git a/configs/peach-pit_defconfig b/configs/peach-pit_defconfig index d19bff2..8fe423e 100644 --- a/configs/peach-pit_defconfig +++ b/configs/peach-pit_defconfig @@ -14,3 +14,20 @@ CONFIG_USB=y CONFIG_DM_USB=y CONFIG_I2C_MUX=y CONFIG_I2C_CROS_EC_TUNNEL=y +CONFIG_SOUND=y +CONFIG_I2S=y +CONFIG_I2S_SAMSUNG=y +CONFIG_SOUND_MAX98095=y +CONFIG_SOUND_WM8994=y +CONFIG_DM_I2C=y +CONFIG_DM_PMIC=y +CONFIG_DM_REGULATOR=y +CONFIG_PMIC_TPS65090=y +CONFIG_REGULATOR_TPS65090=y +CONFIG_DM_I2C_COMPAT=y +CONFIG_I2C_ARB_GPIO_CHALLENGE=y +CONFIG_CMD_PMIC=y +CONFIG_CMD_REGULATOR=y +CONFIG_ERRNO_STR=y +CONFIG_VIDEO_BRIDGE=y +CONFIG_VIDEO_BRIDGE_PARADE_PS862X=y diff --git a/configs/smdk5250_defconfig b/configs/smdk5250_defconfig index 8412d6f..b061e47 100644 --- a/configs/smdk5250_defconfig +++ b/configs/smdk5250_defconfig @@ -14,3 +14,13 @@ CONFIG_SOUND_MAX98095=y CONFIG_SOUND_WM8994=y CONFIG_USB=y CONFIG_DM_USB=y +CONFIG_DM_I2C=y +CONFIG_DM_I2C_COMPAT=y +CONFIG_DM_PMIC=y +CONFIG_DM_REGULATOR=y +CONFIG_CMD_PMIC=y +CONFIG_CMD_REGULATOR=y +CONFIG_ERRNO_STR=y +CONFIG_DM_PMIC_MAX77686=y +CONFIG_DM_REGULATOR_MAX77686=y +CONFIG_VIDEO_BRIDGE=y diff --git a/configs/smdk5420_defconfig b/configs/smdk5420_defconfig index a96b368..1561f6a 100644 --- a/configs/smdk5420_defconfig +++ b/configs/smdk5420_defconfig @@ -8,3 +8,9 @@ CONFIG_SPL=y CONFIG_SPI_FLASH=y CONFIG_USB=y CONFIG_DM_USB=y +CONFIG_CMD_NET=y +CONFIG_DM_I2C=y +CONFIG_DM_I2C_COMPAT=y +CONFIG_DM_PMIC=y +CONFIG_DM_REGULATOR=y +CONFIG_VIDEO_BRIDGE=y diff --git a/configs/snow_defconfig b/configs/snow_defconfig index 93fbcae..a7d9e7a 100644 --- a/configs/snow_defconfig +++ b/configs/snow_defconfig @@ -18,3 +18,26 @@ CONFIG_SOUND_MAX98095=y CONFIG_SOUND_WM8994=y CONFIG_USB=y CONFIG_DM_USB=y +CONFIG_DM_I2C=y +CONFIG_DM_PMIC=y +CONFIG_DM_REGULATOR=y +CONFIG_PMIC_TPS65090=y +CONFIG_REGULATOR_TPS65090=y +CONFIG_DM_I2C_COMPAT=y +CONFIG_I2C_ARB_GPIO_CHALLENGE=y +CONFIG_I2C_MUX=y +CONFIG_CMD_PMIC=y +CONFIG_CMD_REGULATOR=y +CONFIG_ERRNO_STR=y +CONFIG_DM_PMIC_MAX77686=y +CONFIG_DM_REGULATOR_MAX77686=y +CONFIG_DEBUG_UART=y +CONFIG_DEBUG_UART_S5P=y +CONFIG_DEBUG_UART_CLOCK=100000000 +CONFIG_DEBUG_UART_BASE=0x12c30000 +CONFIG_I2C_CROS_EC_LDO=y +CONFIG_PMIC_S5M8767=y +CONFIG_REGULATOR_S5M8767=y +CONFIG_VIDEO_BRIDGE=y +CONFIG_VIDEO_BRIDGE_PARADE_PS862X=y +CONFIG_VIDEO_BRIDGE_NXP_PTN3460=y diff --git a/include/configs/arndale.h b/include/configs/arndale.h index 3ad4a9b..91e32df 100644 --- a/include/configs/arndale.h +++ b/include/configs/arndale.h @@ -60,4 +60,8 @@ /* The PERIPHBASE in the CBAR register is wrong on the Arndale, so override it */ #define CONFIG_ARM_GIC_BASE_ADDRESS 0x10480000 +/* Power */ +#define CONFIG_POWER +#define CONFIG_POWER_I2C + #endif /* __CONFIG_H */ diff --git a/include/configs/exynos5-common.h b/include/configs/exynos5-common.h index e04dec7..15363d0 100644 --- a/include/configs/exynos5-common.h +++ b/include/configs/exynos5-common.h @@ -67,6 +67,8 @@ #define CONFIG_SPL_LIBCOMMON_SUPPORT #define CONFIG_SPL_GPIO_SUPPORT +#define CONFIG_SPL_SERIAL_SUPPORT +#define CONFIG_SPL_LIBGENERIC_SUPPORT /* specific .lds file */ #define CONFIG_SPL_LDSCRIPT "board/samsung/common/exynos-uboot-spl.lds" @@ -126,10 +128,6 @@ #define SPI_FLASH_UBOOT_POS (CONFIG_SEC_FW_SIZE + CONFIG_BL1_SIZE) /* I2C */ - -/* TODO(sjg@chromium.org): Move these two options to Kconfig */ -#define CONFIG_DM_I2C -#define CONFIG_DM_I2C_COMPAT #define CONFIG_CMD_I2C #define CONFIG_SYS_I2C_S3C24X0 #define CONFIG_SYS_I2C_S3C24X0_SPEED 100000 /* 100 Kbps */ @@ -149,10 +147,6 @@ #define CONFIG_OF_SPI #endif -/* Power */ -#define CONFIG_POWER -#define CONFIG_POWER_I2C - #ifdef CONFIG_ENV_IS_IN_SPI_FLASH #define CONFIG_ENV_SPI_MODE SPI_MODE_0 #define CONFIG_ENV_SECT_SIZE CONFIG_ENV_SIZE diff --git a/include/configs/exynos5-dt-common.h b/include/configs/exynos5-dt-common.h index b1b8e1a..098734b 100644 --- a/include/configs/exynos5-dt-common.h +++ b/include/configs/exynos5-dt-common.h @@ -18,11 +18,6 @@ #include "exynos5-common.h" -/* PMIC */ -#define CONFIG_POWER -#define CONFIG_POWER_I2C -#define CONFIG_POWER_TPS65090 - /* Enable keyboard */ #define CONFIG_KEYBOARD diff --git a/include/configs/smdk5250.h b/include/configs/smdk5250.h index e5655fc..bbec672 100644 --- a/include/configs/smdk5250.h +++ b/include/configs/smdk5250.h @@ -17,9 +17,6 @@ #include -/* PMIC */ -#define CONFIG_POWER_MAX77686 - #define CONFIG_BOARD_COMMON #define CONFIG_USB_XHCI diff --git a/include/configs/snow.h b/include/configs/snow.h index 557f86c..a467792 100644 --- a/include/configs/snow.h +++ b/include/configs/snow.h @@ -18,9 +18,6 @@ #include #include - -#define CONFIG_POWER_TPS65090_I2C - #define CONFIG_BOARD_COMMON #define CONFIG_USB_XHCI -- cgit v0.10.2 From bae34335e8aef46d7103907580c40f18f1876bb0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 Jul 2015 18:16:23 -0600 Subject: exynos: Remove unneeded device tree control #ifdefs Since device tree is used for all exynos5 boards, we can remove the #ifdef and reduce confusion. Signed-off-by: Simon Glass diff --git a/board/samsung/common/board.c b/board/samsung/common/board.c index 1a4e8c9..d32c75d 100644 --- a/board/samsung/common/board.c +++ b/board/samsung/common/board.c @@ -152,13 +152,14 @@ int board_early_init_f(void) board_i2c_init(gd->fdt_blob); #endif -#if defined(CONFIG_OF_CONTROL) && defined(CONFIG_EXYNOS_FB) -/* - * board_init_f(arch/arm/lib/board.c) calls lcd_setmem() which needs - * panel_info.vl_col, panel_info.vl_row and panel_info.vl_bpix, to reserve - * FB memory at a very early stage. So, we need to fill panel_info.vl_col, - * panel_info.vl_row and panel_info.vl_bpix before lcd_setmem() is called. - */ +#if defined(CONFIG_EXYNOS_FB) + /* + * board_init_f(arch/arm/lib/board.c) calls lcd_setmem() which needs + * panel_info.vl_col, panel_info.vl_row and panel_info.vl_bpix, + * to reserve frame-buffer memory at a very early stage. So, we need + * to fill panel_info.vl_col, panel_info.vl_row and panel_info.vl_bpix + * before lcd_setmem() is called. + */ err = exynos_lcd_early_init(gd->fdt_blob); if (err) { debug("LCD early init failed\n"); @@ -179,7 +180,6 @@ int power_init_board(void) } #endif -#ifdef CONFIG_OF_CONTROL #ifdef CONFIG_SMC911X static int decode_sromc(const void *blob, struct fdt_sromc *config) { @@ -310,7 +310,6 @@ int checkboard(void) return 0; } #endif -#endif /* CONFIG_OF_CONTROL */ #ifdef CONFIG_BOARD_LATE_INIT int board_late_init(void) -- cgit v0.10.2 From bf637ea5e48b26e01c440ed7d108eada1345ffd2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:29 -0600 Subject: exynos: config: Move common options to the common headers and tidy up Many options are duplicated on the exynos5 boards. Move these to the common files. Also some options are not used so can be removed. Tidy this up to make the files easier to maintain. Signed-off-by: Simon Glass Acked-by: Przemyslaw Marczak diff --git a/include/configs/arndale.h b/include/configs/arndale.h index 91e32df..8784c4e 100644 --- a/include/configs/arndale.h +++ b/include/configs/arndale.h @@ -13,6 +13,7 @@ "fdtfile=exynos5250-arndale.dtb\0" #include "exynos5250-common.h" +#include /* SD/MMC configuration */ #define CONFIG_SUPPORT_EMMC_BOOT @@ -20,15 +21,6 @@ /* allow to overwrite serial and ethaddr */ #define CONFIG_ENV_OVERWRITE -/* USB */ -#define CONFIG_USB_EHCI -#define CONFIG_USB_EHCI_EXYNOS - -#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 3 -#define CONFIG_USB_HOST_ETHER -#define CONFIG_USB_ETHER_ASIX -#define CONFIG_USB_ETHER_ASIX88179 - /* MMC SPL */ #define CONFIG_EXYNOS_SPL @@ -36,9 +28,6 @@ #define CONFIG_SYS_PROMPT "ARNDALE # " #define CONFIG_DEFAULT_CONSOLE "console=ttySAC2,115200n8\0" -#define CONFIG_NR_DRAM_BANKS 8 -#define SDRAM_BANK_SIZE (256UL << 20UL) /* 256 MB */ - #define CONFIG_IDENT_STRING " for ARNDALE" #define CONFIG_ENV_IS_IN_MMC @@ -49,6 +38,7 @@ #define CONFIG_SYS_INIT_SP_ADDR CONFIG_IRAM_STACK /* PMIC */ +#define CONFIG_POWER #define CONFIG_PMIC #define CONFIG_POWER_I2C diff --git a/include/configs/exynos5-common.h b/include/configs/exynos5-common.h index 15363d0..e710f41 100644 --- a/include/configs/exynos5-common.h +++ b/include/configs/exynos5-common.h @@ -143,8 +143,6 @@ #define CONFIG_SPI_FLASH_GIGADEVICE #define CONFIG_SF_DEFAULT_MODE SPI_MODE_0 #define CONFIG_SF_DEFAULT_SPEED 50000000 -#define EXYNOS5_SPI_NUM_CONTROLLERS 5 -#define CONFIG_OF_SPI #endif #ifdef CONFIG_ENV_IS_IN_SPI_FLASH @@ -194,7 +192,6 @@ #define CONFIG_FIT #define CONFIG_FIT_BEST_MATCH - #define BOOT_TARGET_DEVICES(func) \ func(MMC, mmc, 1) \ func(MMC, mmc, 0) \ diff --git a/include/configs/exynos5-dt-common.h b/include/configs/exynos5-dt-common.h index 098734b..29ef84b 100644 --- a/include/configs/exynos5-dt-common.h +++ b/include/configs/exynos5-dt-common.h @@ -16,7 +16,21 @@ "stdout=serial,lcd\0" \ "stderr=serial,lcd\0" -#include "exynos5-common.h" +#define CONFIG_ENV_IS_IN_SPI_FLASH +#define CONFIG_ENV_SPI_BASE 0x12D30000 +#define FLASH_SIZE (4 << 20) +#define CONFIG_ENV_OFFSET (FLASH_SIZE - CONFIG_ENV_SECT_SIZE) +#define CONFIG_SPI_BOOTING + +#define CONFIG_BOARD_COMMON + +/* Display */ +#define CONFIG_LCD +#ifdef CONFIG_LCD +#define CONFIG_EXYNOS_FB +#define CONFIG_EXYNOS_DP +#define LCD_BPP LCD_COLOR16 +#endif /* Enable keyboard */ #define CONFIG_KEYBOARD diff --git a/include/configs/exynos5250-common.h b/include/configs/exynos5250-common.h index 95e96ec..7d8921f 100644 --- a/include/configs/exynos5250-common.h +++ b/include/configs/exynos5250-common.h @@ -10,7 +10,6 @@ #ifndef __CONFIG_5250_H #define __CONFIG_5250_H -#include #define CONFIG_EXYNOS5250 #define CONFIG_SYS_SDRAM_BASE 0x40000000 @@ -28,16 +27,13 @@ #define CONFIG_SYS_INIT_SP_ADDR CONFIG_IRAM_STACK -/* I2C */ -#define CONFIG_MAX_I2C_NUM 8 +/* USB */ +#define CONFIG_USB_EHCI +#define CONFIG_USB_EHCI_EXYNOS -/* Display */ -#define CONFIG_LCD -#ifdef CONFIG_LCD -#define CONFIG_EXYNOS_FB -#define CONFIG_EXYNOS_DP -#define LCD_BPP LCD_COLOR16 -#endif +#define CONFIG_USB_HOST_ETHER +#define CONFIG_USB_ETHER_ASIX +#define CONFIG_USB_ETHER_ASIX88179 /* DRAM Memory Banks */ #define CONFIG_NR_DRAM_BANKS 8 diff --git a/include/configs/exynos5420-common.h b/include/configs/exynos5420-common.h index 3b1ac2c..3cdec51 100644 --- a/include/configs/exynos5420-common.h +++ b/include/configs/exynos5420-common.h @@ -13,8 +13,6 @@ /* A variant of Exynos5420 (Exynos5 Family) */ #define CONFIG_EXYNOS5800 -#include - #define MACH_TYPE_SMDK5420 8002 #define CONFIG_MACH_TYPE MACH_TYPE_SMDK5420 @@ -32,10 +30,6 @@ #define CONFIG_DEVICE_TREE_LIST "exynos5800-peach-pi" \ "exynos5420-peach-pit exynos5420-smdk5420" -#define CONFIG_MAX_I2C_NUM 11 - -#define CONFIG_BOARD_REV_GPIO_COUNT 2 - #define CONFIG_PHY_IRAM_BASE 0x02020000 /* Address for relocating helper code (Last 4 KB of IRAM) */ @@ -52,4 +46,7 @@ */ #define CONFIG_CORE_COUNT 0x8 +#define CONFIG_USB_XHCI +#define CONFIG_USB_XHCI_EXYNOS + #endif /* __CONFIG_EXYNOS5420_H */ diff --git a/include/configs/odroid_xu3.h b/include/configs/odroid_xu3.h index 8d5c736..0deff46 100644 --- a/include/configs/odroid_xu3.h +++ b/include/configs/odroid_xu3.h @@ -9,7 +9,9 @@ #define __CONFIG_ODROID_XU3_H #include "exynos5420-common.h" +#include +#undef CONFIG_ENV_IS_IN_SPI_FLASH #define CONFIG_SYS_PROMPT "ODROID-XU3 # " #define CONFIG_IDENT_STRING " for ODROID-XU3" diff --git a/include/configs/peach-pi.h b/include/configs/peach-pi.h index 46699ff..b97faf2 100644 --- a/include/configs/peach-pi.h +++ b/include/configs/peach-pi.h @@ -9,12 +9,6 @@ #ifndef __CONFIG_PEACH_PI_H #define __CONFIG_PEACH_PI_H -#define CONFIG_ENV_IS_IN_SPI_FLASH -#define CONFIG_ENV_SPI_BASE 0x12D30000 -#define FLASH_SIZE (0x4 << 20) -#define CONFIG_ENV_OFFSET (FLASH_SIZE - CONFIG_BL2_SIZE) -#define CONFIG_SPI_BOOTING - #define MEM_LAYOUT_ENV_SETTINGS \ "bootm_size=0x10000000\0" \ "kernel_addr_r=0x22000000\0" \ @@ -25,8 +19,7 @@ #include #include - -#define CONFIG_BOARD_COMMON +#include #define CONFIG_SYS_SDRAM_BASE 0x20000000 #define CONFIG_SYS_TEXT_BASE 0x23E00000 @@ -51,9 +44,6 @@ #define CONFIG_POWER_TPS65090_EC -#define CONFIG_USB_XHCI -#define CONFIG_USB_XHCI_EXYNOS - /* DRAM Memory Banks */ #define CONFIG_NR_DRAM_BANKS 7 #define SDRAM_BANK_SIZE (512UL << 20UL) /* 512 MB */ diff --git a/include/configs/peach-pit.h b/include/configs/peach-pit.h index c5c9e3a..18be42c 100644 --- a/include/configs/peach-pit.h +++ b/include/configs/peach-pit.h @@ -9,12 +9,6 @@ #ifndef __CONFIG_PEACH_PIT_H #define __CONFIG_PEACH_PIT_H -#define CONFIG_ENV_IS_IN_SPI_FLASH -#define CONFIG_ENV_SPI_BASE 0x12D30000 -#define FLASH_SIZE (0x4 << 20) -#define CONFIG_ENV_OFFSET (FLASH_SIZE - CONFIG_BL2_SIZE) -#define CONFIG_SPI_BOOTING - #define MEM_LAYOUT_ENV_SETTINGS \ "bootm_size=0x10000000\0" \ "kernel_addr_r=0x22000000\0" \ @@ -25,8 +19,7 @@ #include #include - -#define CONFIG_BOARD_COMMON +#include #define CONFIG_SYS_SDRAM_BASE 0x20000000 #define CONFIG_SYS_TEXT_BASE 0x23E00000 @@ -41,19 +34,8 @@ #define CONFIG_VIDEO_PARADE -/* Display */ -#define CONFIG_LCD -#ifdef CONFIG_LCD -#define CONFIG_EXYNOS_FB -#define CONFIG_EXYNOS_DP -#define LCD_BPP LCD_COLOR16 -#endif - #define CONFIG_POWER_TPS65090_EC -#define CONFIG_USB_XHCI -#define CONFIG_USB_XHCI_EXYNOS - /* DRAM Memory Banks */ #define CONFIG_NR_DRAM_BANKS 4 #define SDRAM_BANK_SIZE (512UL << 20UL) /* 512 MB */ diff --git a/include/configs/smdk5250.h b/include/configs/smdk5250.h index bbec672..82d41cc 100644 --- a/include/configs/smdk5250.h +++ b/include/configs/smdk5250.h @@ -9,23 +9,16 @@ #ifndef __CONFIG_SMDK_H #define __CONFIG_SMDK_H -#define CONFIG_ENV_IS_IN_SPI_FLASH -#define CONFIG_ENV_SPI_BASE 0x12D30000 -#define FLASH_SIZE (0x4 << 20) -#define CONFIG_ENV_OFFSET (FLASH_SIZE - CONFIG_BL2_SIZE) -#define CONFIG_SPI_BOOTING - #include +#include +#include -#define CONFIG_BOARD_COMMON +#undef CONFIG_KEYBOARD -#define CONFIG_USB_XHCI -#define CONFIG_USB_XHCI_EXYNOS +#define CONFIG_BOARD_COMMON #define CONFIG_SYS_PROMPT "SMDK5250 # " #define CONFIG_IDENT_STRING " for SMDK5250" - -/* Miscellaneous configurable options */ #define CONFIG_DEFAULT_CONSOLE "console=ttySAC1,115200n8\0" #endif /* __CONFIG_SMDK_H */ diff --git a/include/configs/smdk5420.h b/include/configs/smdk5420.h index 607877c..623efa8 100644 --- a/include/configs/smdk5420.h +++ b/include/configs/smdk5420.h @@ -9,13 +9,11 @@ #ifndef __CONFIG_SMDK5420_H #define __CONFIG_SMDK5420_H -#define CONFIG_ENV_IS_IN_SPI_FLASH -#define CONFIG_ENV_SPI_BASE 0x12D30000 -#define FLASH_SIZE (0x4 << 20) -#define CONFIG_ENV_OFFSET (FLASH_SIZE - CONFIG_BL2_SIZE) -#define CONFIG_SPI_BOOTING - #include +#include +#include + +#undef CONFIG_KEYBOARD #define CONFIG_BOARD_COMMON diff --git a/include/configs/snow.h b/include/configs/snow.h index a467792..bf3377c 100644 --- a/include/configs/snow.h +++ b/include/configs/snow.h @@ -9,22 +9,12 @@ #ifndef __CONFIG_SNOW_H #define __CONFIG_SNOW_H -#define CONFIG_ENV_IS_IN_SPI_FLASH -#define CONFIG_ENV_SPI_BASE 0x12D30000 -#define FLASH_SIZE (0x4 << 20) -#define CONFIG_ENV_OFFSET (FLASH_SIZE - CONFIG_BL2_SIZE) -#define CONFIG_SPI_BOOTING - #include #include +#include #define CONFIG_BOARD_COMMON -#define CONFIG_USB_XHCI -#define CONFIG_USB_EHCI -#define CONFIG_USB_XHCI_EXYNOS -#define CONFIG_USB_EHCI_EXYNOS - #define CONFIG_SYS_PROMPT "snow # " #define CONFIG_IDENT_STRING " for snow" #define CONFIG_DEFAULT_CONSOLE "console=ttySAC1,115200n8\0" -- cgit v0.10.2 From c51212332af188d8bdeb5488d57d6c19a4326d22 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:30 -0600 Subject: exynos: Drop old exynos5420-specific board code Now that exynos5420 boards can use the generic exynos5 code, switch over to it and remove the old code. Signed-off-by: Simon Glass Acked-by: Przemyslaw Marczak diff --git a/board/samsung/smdk5420/Makefile b/board/samsung/smdk5420/Makefile index c2f8886..96a400a 100644 --- a/board/samsung/smdk5420/Makefile +++ b/board/samsung/smdk5420/Makefile @@ -5,7 +5,3 @@ # obj-y += smdk5420_spl.o - -ifndef CONFIG_SPL_BUILD -obj-y += smdk5420.o -endif diff --git a/board/samsung/smdk5420/smdk5420.c b/board/samsung/smdk5420/smdk5420.c deleted file mode 100644 index 57cc92c..0000000 --- a/board/samsung/smdk5420/smdk5420.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2013 Samsung Electronics - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -int exynos_init(void) -{ - return 0; -} - -#ifdef CONFIG_LCD -static int has_edp_bridge(void) -{ - int node; - - node = fdtdec_next_compatible(gd->fdt_blob, 0, COMPAT_PARADE_PS8625); - - /* No node for bridge in device tree. */ - if (node <= 0) - return 0; - - /* Default is with bridge ic */ - return 1; -} - -void exynos_lcd_power_on(void) -{ -#ifdef CONFIG_POWER_TPS65090 - int ret; - - ret = tps65090_init(); - if (ret < 0) { - printf("%s: tps65090_init() failed\n", __func__); - return; - } - - tps65090_fet_enable(6); -#endif - - mdelay(5); - - if (has_edp_bridge()) - if (parade_init(gd->fdt_blob)) - printf("%s: ps8625_init() failed\n", __func__); -} - -void exynos_backlight_on(unsigned int onoff) -{ -#ifdef CONFIG_POWER_TPS65090 - tps65090_fet_enable(1); -#endif -} -#endif - -int board_get_revision(void) -{ - return 0; -} - -#ifdef CONFIG_USB_DWC3 -static struct dwc3_device dwc3_device_data = { - .maximum_speed = USB_SPEED_SUPER, - .base = 0x12400000, - .dr_mode = USB_DR_MODE_PERIPHERAL, - .index = 0, -}; - -int usb_gadget_handle_interrupts(void) -{ - dwc3_uboot_handle_interrupt(0); - return 0; -} - -int board_usb_init(int index, enum usb_init_type init) -{ - struct exynos_usb3_phy *phy = (struct exynos_usb3_phy *) - samsung_get_base_usb3_phy(); - - if (!phy) { - error("usb3 phy not supported"); - return -ENODEV; - } - - set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_EN); - exynos5_usb3_phy_init(phy); - - return dwc3_uboot_init(&dwc3_device_data); -} -#endif -#ifdef CONFIG_SET_DFU_ALT_INFO -char *get_dfu_alt_system(char *interface, char *devstr) -{ - return getenv("dfu_alt_system"); -} - -char *get_dfu_alt_boot(char *interface, char *devstr) -{ - struct mmc *mmc; - char *alt_boot; - int dev_num; - - dev_num = simple_strtoul(devstr, NULL, 10); - - mmc = find_mmc_device(dev_num); - if (!mmc) - return NULL; - - if (mmc_init(mmc)) - return NULL; - - if (IS_SD(mmc)) - alt_boot = CONFIG_DFU_ALT_BOOT_SD; - else - alt_boot = CONFIG_DFU_ALT_BOOT_EMMC; - - return alt_boot; -} -#endif diff --git a/include/configs/exynos5420-common.h b/include/configs/exynos5420-common.h index 3cdec51..cd86e06 100644 --- a/include/configs/exynos5420-common.h +++ b/include/configs/exynos5420-common.h @@ -13,6 +13,8 @@ /* A variant of Exynos5420 (Exynos5 Family) */ #define CONFIG_EXYNOS5800 +#define CONFIG_EXYNOS5_DT + #define MACH_TYPE_SMDK5420 8002 #define CONFIG_MACH_TYPE MACH_TYPE_SMDK5420 -- cgit v0.10.2 From 54abb000e3d76ab7c2d0df11a2b4074aec7f3c1f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:31 -0600 Subject: exynos: Drop old exynos5250-specific board code Now that most exynos5250 boards can use the generic exynos5 code, switch over to it and remove the old code. Signed-off-by: Simon Glass diff --git a/board/samsung/smdk5250/Makefile b/board/samsung/smdk5250/Makefile index 3d96b07..501cab6 100644 --- a/board/samsung/smdk5250/Makefile +++ b/board/samsung/smdk5250/Makefile @@ -5,7 +5,3 @@ # obj-y += smdk5250_spl.o - -ifndef CONFIG_SPL_BUILD -obj-y += exynos5-dt.o -endif diff --git a/board/samsung/smdk5250/exynos5-dt.c b/board/samsung/smdk5250/exynos5-dt.c deleted file mode 100644 index 53ff706..0000000 --- a/board/samsung/smdk5250/exynos5-dt.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -#ifdef CONFIG_SOUND_MAX98095 -static void board_enable_audio_codec(void) -{ - /* Enable MAX98095 Codec */ - gpio_request(EXYNOS5_GPIO_X17, "max98095_enable"); - gpio_direction_output(EXYNOS5_GPIO_X17, 1); - gpio_set_pull(EXYNOS5_GPIO_X17, S5P_GPIO_PULL_NONE); -} -#endif - -int exynos_init(void) -{ -#ifdef CONFIG_SOUND_MAX98095 - board_enable_audio_codec(); -#endif - return 0; -} - -#if defined(CONFIG_POWER) -#ifdef CONFIG_POWER_MAX77686 -static int pmic_reg_update(struct pmic *p, int reg, uint regval) -{ - u32 val; - int ret = 0; - - ret = pmic_reg_read(p, reg, &val); - if (ret) { - debug("%s: PMIC %d register read failed\n", __func__, reg); - return -1; - } - val |= regval; - ret = pmic_reg_write(p, reg, val); - if (ret) { - debug("%s: PMIC %d register write failed\n", __func__, reg); - return -1; - } - return 0; -} - -static int max77686_init(void) -{ - struct pmic *p; - - if (pmic_init(I2C_PMIC)) - return -1; - - p = pmic_get("MAX77686_PMIC"); - if (!p) - return -ENODEV; - - if (pmic_probe(p)) - return -1; - - if (pmic_reg_update(p, MAX77686_REG_PMIC_32KHZ, MAX77686_32KHCP_EN)) - return -1; - - if (pmic_reg_update(p, MAX77686_REG_PMIC_BBAT, - MAX77686_BBCHOSTEN | MAX77686_BBCVS_3_5V)) - return -1; - - /* VDD_MIF */ - if (pmic_reg_write(p, MAX77686_REG_PMIC_BUCK1OUT, - MAX77686_BUCK1OUT_1V)) { - debug("%s: PMIC %d register write failed\n", __func__, - MAX77686_REG_PMIC_BUCK1OUT); - return -1; - } - - if (pmic_reg_update(p, MAX77686_REG_PMIC_BUCK1CRTL, - MAX77686_BUCK1CTRL_EN)) - return -1; - - /* VDD_ARM */ - if (pmic_reg_write(p, MAX77686_REG_PMIC_BUCK2DVS1, - MAX77686_BUCK2DVS1_1_3V)) { - debug("%s: PMIC %d register write failed\n", __func__, - MAX77686_REG_PMIC_BUCK2DVS1); - return -1; - } - - if (pmic_reg_update(p, MAX77686_REG_PMIC_BUCK2CTRL1, - MAX77686_BUCK2CTRL_ON)) - return -1; - - /* VDD_INT */ - if (pmic_reg_write(p, MAX77686_REG_PMIC_BUCK3DVS1, - MAX77686_BUCK3DVS1_1_0125V)) { - debug("%s: PMIC %d register write failed\n", __func__, - MAX77686_REG_PMIC_BUCK3DVS1); - return -1; - } - - if (pmic_reg_update(p, MAX77686_REG_PMIC_BUCK3CTRL, - MAX77686_BUCK3CTRL_ON)) - return -1; - - /* VDD_G3D */ - if (pmic_reg_write(p, MAX77686_REG_PMIC_BUCK4DVS1, - MAX77686_BUCK4DVS1_1_2V)) { - debug("%s: PMIC %d register write failed\n", __func__, - MAX77686_REG_PMIC_BUCK4DVS1); - return -1; - } - - if (pmic_reg_update(p, MAX77686_REG_PMIC_BUCK4CTRL1, - MAX77686_BUCK3CTRL_ON)) - return -1; - - /* VDD_LDO2 */ - if (pmic_reg_update(p, MAX77686_REG_PMIC_LDO2CTRL1, - MAX77686_LD02CTRL1_1_5V | EN_LDO)) - return -1; - - /* VDD_LDO3 */ - if (pmic_reg_update(p, MAX77686_REG_PMIC_LDO3CTRL1, - MAX77686_LD03CTRL1_1_8V | EN_LDO)) - return -1; - - /* VDD_LDO5 */ - if (pmic_reg_update(p, MAX77686_REG_PMIC_LDO5CTRL1, - MAX77686_LD05CTRL1_1_8V | EN_LDO)) - return -1; - - /* VDD_LDO10 */ - if (pmic_reg_update(p, MAX77686_REG_PMIC_LDO10CTRL1, - MAX77686_LD10CTRL1_1_8V | EN_LDO)) - return -1; - - return 0; -} -#endif /* CONFIG_POWER_MAX77686 */ - -int exynos_power_init(void) -{ - int ret = 0; - -#ifdef CONFIG_POWER_MAX77686 - ret = max77686_init(); - if (ret) - return ret; -#endif -#ifdef CONFIG_POWER_TPS65090 - /* - * The TPS65090 may not be in the device tree. If so, it is not - * an error. - */ - ret = tps65090_init(); - if (ret == 0 || ret == -ENODEV) - return 0; -#endif - - return ret; -} -#endif /* CONFIG_POWER */ - -#ifdef CONFIG_LCD -static int board_dp_bridge_setup(void) -{ - const int max_tries = 10; - int num_tries, node; - - /* - * TODO(sjg): Use device tree for GPIOs when exynos GPIO - * numbering patch is in mainline. - */ - debug("%s\n", __func__); - node = fdtdec_next_compatible(gd->fdt_blob, 0, COMPAT_NXP_PTN3460); - if (node < 0) { - debug("%s: No node for DP bridge in device tree\n", __func__); - return -ENODEV; - } - - /* Setup the GPIOs */ - - /* PD is ACTIVE_LOW, and initially de-asserted */ - gpio_request(EXYNOS5_GPIO_Y25, "dp_bridge_pd"); - gpio_set_pull(EXYNOS5_GPIO_Y25, S5P_GPIO_PULL_NONE); - gpio_direction_output(EXYNOS5_GPIO_Y25, 1); - - /* Reset is ACTIVE_LOW */ - gpio_request(EXYNOS5_GPIO_X15, "dp_bridge_reset"); - gpio_set_pull(EXYNOS5_GPIO_X15, S5P_GPIO_PULL_NONE); - gpio_direction_output(EXYNOS5_GPIO_X15, 0); - - udelay(10); - gpio_set_value(EXYNOS5_GPIO_X15, 1); - - gpio_request(EXYNOS5_GPIO_X07, "dp_bridge_hpd"); - gpio_direction_input(EXYNOS5_GPIO_X07); - - /* - * We need to wait for 90ms after bringing up the bridge since there - * is a phantom "high" on the HPD chip during its bootup. The phantom - * high comes within 7ms of de-asserting PD and persists for at least - * 15ms. The real high comes roughly 50ms after PD is de-asserted. The - * phantom high makes it hard for us to know when the NXP chip is up. - */ - mdelay(90); - - for (num_tries = 0; num_tries < max_tries; num_tries++) { - /* Check HPD. If it's high, we're all good. */ - if (gpio_get_value(EXYNOS5_GPIO_X07)) - return 0; - - debug("%s: eDP bridge failed to come up; try %d of %d\n", - __func__, num_tries, max_tries); - } - - /* Immediately go into bridge reset if the hp line is not high */ - return -ENODEV; -} - -void exynos_cfg_lcd_gpio(void) -{ - /* For Backlight */ - gpio_request(EXYNOS5_GPIO_B20, "lcd_backlight"); - gpio_cfg_pin(EXYNOS5_GPIO_B20, S5P_GPIO_OUTPUT); - gpio_set_value(EXYNOS5_GPIO_B20, 1); - - /* LCD power on */ - gpio_request(EXYNOS5_GPIO_X15, "lcd_power"); - gpio_cfg_pin(EXYNOS5_GPIO_X15, S5P_GPIO_OUTPUT); - gpio_set_value(EXYNOS5_GPIO_X15, 1); - - /* Set Hotplug detect for DP */ - gpio_cfg_pin(EXYNOS5_GPIO_X07, S5P_GPIO_FUNC(0x3)); -} - -void exynos_set_dp_phy(unsigned int onoff) -{ - set_dp_phy_ctrl(onoff); -} - -void exynos_backlight_on(unsigned int on) -{ - debug("%s(%u)\n", __func__, on); - - if (!on) - return; - -#ifdef CONFIG_POWER_TPS65090 - int ret; - - ret = tps65090_fet_enable(1); /* Enable FET1, backlight */ - if (ret) - return; - - /* T5 in the LCD timing spec (defined as > 10ms) */ - mdelay(10); - - /* board_dp_backlight_pwm */ - gpio_direction_output(EXYNOS5_GPIO_B20, 1); - - /* T6 in the LCD timing spec (defined as > 10ms) */ - mdelay(10); - - /* board_dp_backlight_en */ - gpio_request(EXYNOS5_GPIO_X30, "board_dp_backlight_en"); - gpio_direction_output(EXYNOS5_GPIO_X30, 1); -#endif -} - -void exynos_lcd_power_on(void) -{ - int ret; - - debug("%s\n", __func__); - -#ifdef CONFIG_POWER_TPS65090 - /* board_dp_lcd_vdd */ - tps65090_fet_enable(6); /* Enable FET6, lcd panel */ -#endif - - ret = board_dp_bridge_setup(); - if (ret && ret != -ENODEV) - printf("LCD bridge failed to enable: %d\n", ret); -} - -#endif diff --git a/include/configs/exynos5-dt-common.h b/include/configs/exynos5-dt-common.h index 29ef84b..8b61a52 100644 --- a/include/configs/exynos5-dt-common.h +++ b/include/configs/exynos5-dt-common.h @@ -16,6 +16,8 @@ "stdout=serial,lcd\0" \ "stderr=serial,lcd\0" +#define CONFIG_EXYNOS5_DT + #define CONFIG_ENV_IS_IN_SPI_FLASH #define CONFIG_ENV_SPI_BASE 0x12D30000 #define FLASH_SIZE (4 << 20) -- cgit v0.10.2 From 05bccbcd761e7170e236c982915781b143051abb Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:32 -0600 Subject: power: Remove old TPS65090 drivers Remove the old drivers (both the normal one and the cros_ec one) now that we have new drivers that use driver model. Signed-off-by: Simon Glass diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 322c2c6..6ef149a 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -18,8 +18,6 @@ obj-$(CONFIG_POWER_MAX8997) += pmic_max8997.o obj-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o obj-$(CONFIG_POWER_MAX77686) += pmic_max77686.o obj-$(CONFIG_POWER_PFUZE100) += pmic_pfuze100.o -obj-$(CONFIG_POWER_TPS65090_I2C) += pmic_tps65090.o -obj-$(CONFIG_POWER_TPS65090_EC) += pmic_tps65090_ec.o obj-$(CONFIG_POWER_TPS65217) += pmic_tps65217.o obj-$(CONFIG_POWER_TPS65218) += pmic_tps62362.o obj-$(CONFIG_POWER_TPS65218) += pmic_tps65218.o diff --git a/drivers/power/pmic/pmic_tps65090.c b/drivers/power/pmic/pmic_tps65090.c deleted file mode 100644 index 337903a..0000000 --- a/drivers/power/pmic/pmic_tps65090.c +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright (c) 2012 The Chromium OS Authors. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -#define TPS65090_NAME "TPS65090_PMIC" - -/* TPS65090 register addresses */ -enum { - REG_IRQ1 = 0, - REG_CG_CTRL0 = 4, - REG_CG_STATUS1 = 0xa, - REG_FET1_CTRL = 0x0f, - REG_FET2_CTRL, - REG_FET3_CTRL, - REG_FET4_CTRL, - REG_FET5_CTRL, - REG_FET6_CTRL, - REG_FET7_CTRL, - TPS65090_NUM_REGS, -}; - -enum { - IRQ1_VBATG = 1 << 3, - CG_CTRL0_ENC_MASK = 0x01, - - MAX_FET_NUM = 7, - MAX_CTRL_READ_TRIES = 5, - - /* TPS65090 FET_CTRL register values */ - FET_CTRL_TOFET = 1 << 7, /* Timeout, startup, overload */ - FET_CTRL_PGFET = 1 << 4, /* Power good for FET status */ - FET_CTRL_WAIT = 3 << 2, /* Overcurrent timeout max */ - FET_CTRL_ADENFET = 1 << 1, /* Enable output auto discharge */ - FET_CTRL_ENFET = 1 << 0, /* Enable FET */ -}; - -/** - * Checks for a valid FET number - * - * @param fet_id FET number to check - * @return 0 if ok, -EINVAL if FET value is out of range - */ -static int tps65090_check_fet(unsigned int fet_id) -{ - if (fet_id == 0 || fet_id > MAX_FET_NUM) { - debug("parameter fet_id is out of range, %u not in 1 ~ %u\n", - fet_id, MAX_FET_NUM); - return -EINVAL; - } - - return 0; -} - -/** - * Set the power state for a FET - * - * @param pmic pmic structure for the tps65090 - * @param fet_id Fet number to set (1..MAX_FET_NUM) - * @param set 1 to power on FET, 0 to power off - * @return -EIO if we got a comms error, -EAGAIN if the FET failed to - * change state. If all is ok, returns 0. - */ -static int tps65090_fet_set(struct pmic *pmic, int fet_id, bool set) -{ - int retry; - u32 reg, value; - - value = FET_CTRL_ADENFET | FET_CTRL_WAIT; - if (set) - value |= FET_CTRL_ENFET; - - if (pmic_reg_write(pmic, REG_FET1_CTRL + fet_id - 1, value)) - return -EIO; - - /* Try reading until we get a result */ - for (retry = 0; retry < MAX_CTRL_READ_TRIES; retry++) { - if (pmic_reg_read(pmic, REG_FET1_CTRL + fet_id - 1, ®)) - return -EIO; - - /* Check that the fet went into the expected state */ - if (!!(reg & FET_CTRL_PGFET) == set) - return 0; - - /* If we got a timeout, there is no point in waiting longer */ - if (reg & FET_CTRL_TOFET) - break; - - mdelay(1); - } - - debug("FET %d: Power good should have set to %d but reg=%#02x\n", - fet_id, set, reg); - return -EAGAIN; -} - -int tps65090_fet_enable(unsigned int fet_id) -{ - struct pmic *pmic; - ulong start; - int loops; - int ret; - - ret = tps65090_check_fet(fet_id); - if (ret) - return ret; - - pmic = pmic_get(TPS65090_NAME); - if (!pmic) - return -EACCES; - - start = get_timer(0); - for (loops = 0;; loops++) { - ret = tps65090_fet_set(pmic, fet_id, true); - if (!ret) - break; - - if (get_timer(start) > 100) - break; - - /* Turn it off and try again until we time out */ - tps65090_fet_set(pmic, fet_id, false); - } - - if (ret) - debug("%s: FET%d failed to power on: time=%lums, loops=%d\n", - __func__, fet_id, get_timer(start), loops); - else if (loops) - debug("%s: FET%d powered on after %lums, loops=%d\n", - __func__, fet_id, get_timer(start), loops); - - /* - * Unfortunately, there are some conditions where the power - * good bit will be 0, but the fet still comes up. One such - * case occurs with the lcd backlight. We'll just return 0 here - * and assume that the fet will eventually come up. - */ - if (ret == -EAGAIN) - ret = 0; - - return ret; -} - -int tps65090_fet_disable(unsigned int fet_id) -{ - struct pmic *pmic; - int ret; - - ret = tps65090_check_fet(fet_id); - if (ret) - return ret; - - pmic = pmic_get(TPS65090_NAME); - if (!pmic) - return -EACCES; - ret = tps65090_fet_set(pmic, fet_id, false); - - return ret; -} - -int tps65090_fet_is_enabled(unsigned int fet_id) -{ - struct pmic *pmic; - u32 reg; - int ret; - - ret = tps65090_check_fet(fet_id); - if (ret) - return ret; - - pmic = pmic_get(TPS65090_NAME); - if (!pmic) - return -ENODEV; - ret = pmic_reg_read(pmic, REG_FET1_CTRL + fet_id - 1, ®); - if (ret) { - debug("fail to read FET%u_CTRL register over I2C", fet_id); - return -EIO; - } - - return reg & FET_CTRL_ENFET; -} - -int tps65090_get_charging(void) -{ - struct pmic *pmic; - u32 val; - int ret; - - pmic = pmic_get(TPS65090_NAME); - if (!pmic) - return -EACCES; - - ret = pmic_reg_read(pmic, REG_CG_CTRL0, &val); - if (ret) - return ret; - - return !!(val & CG_CTRL0_ENC_MASK); -} - -static int tps65090_charger_state(struct pmic *pmic, int state, - int current) -{ - u32 val; - int ret; - - ret = pmic_reg_read(pmic, REG_CG_CTRL0, &val); - if (!ret) { - if (state == PMIC_CHARGER_ENABLE) - val |= CG_CTRL0_ENC_MASK; - else - val &= ~CG_CTRL0_ENC_MASK; - ret = pmic_reg_write(pmic, REG_CG_CTRL0, val); - } - if (ret) { - debug("%s: Failed to read/write register\n", __func__); - return ret; - } - - return 0; -} - -int tps65090_get_status(void) -{ - struct pmic *pmic; - u32 val; - int ret; - - pmic = pmic_get(TPS65090_NAME); - if (!pmic) - return -EACCES; - - ret = pmic_reg_read(pmic, REG_CG_STATUS1, &val); - if (ret) - return ret; - - return val; -} - -static int tps65090_charger_bat_present(struct pmic *pmic) -{ - u32 val; - int ret; - - ret = pmic_reg_read(pmic, REG_IRQ1, &val); - if (ret) - return ret; - - return !!(val & IRQ1_VBATG); -} - -static struct power_chrg power_chrg_pmic_ops = { - .chrg_bat_present = tps65090_charger_bat_present, - .chrg_state = tps65090_charger_state, -}; - -int tps65090_init(void) -{ - struct pmic *p; - int bus; - int addr; - const void *blob = gd->fdt_blob; - int node, parent; - - node = fdtdec_next_compatible(blob, 0, COMPAT_TI_TPS65090); - if (node < 0) { - debug("PMIC: No node for PMIC Chip in device tree\n"); - debug("node = %d\n", node); - return -ENODEV; - } - - parent = fdt_parent_offset(blob, node); - if (parent < 0) { - debug("%s: Cannot find node parent\n", __func__); - return -EINVAL; - } - - bus = i2c_get_bus_num_fdt(parent); - if (bus < 0) { - debug("%s: Cannot find I2C bus\n", __func__); - return -ENOENT; - } - addr = fdtdec_get_int(blob, node, "reg", TPS65090_I2C_ADDR); - p = pmic_alloc(); - if (!p) { - printf("%s: POWER allocation error!\n", __func__); - return -ENOMEM; - } - - p->name = TPS65090_NAME; - p->bus = bus; - p->interface = PMIC_I2C; - p->number_of_regs = TPS65090_NUM_REGS; - p->hw.i2c.addr = addr; - p->hw.i2c.tx_num = 1; - p->chrg = &power_chrg_pmic_ops; - - puts("TPS65090 PMIC init\n"); - - return 0; -} diff --git a/drivers/power/pmic/pmic_tps65090_ec.c b/drivers/power/pmic/pmic_tps65090_ec.c deleted file mode 100644 index f79a878..0000000 --- a/drivers/power/pmic/pmic_tps65090_ec.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (c) 2013 The Chromium OS Authors. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -#define TPS65090_ADDR 0x48 - -static struct tps65090 { - struct cros_ec_dev *dev; /* The CROS_EC device */ -} config; - -/* TPS65090 register addresses */ -enum { - REG_IRQ1 = 0, - REG_CG_CTRL0 = 4, - REG_CG_STATUS1 = 0xa, - REG_FET1_CTRL = 0x0f, - REG_FET2_CTRL, - REG_FET3_CTRL, - REG_FET4_CTRL, - REG_FET5_CTRL, - REG_FET6_CTRL, - REG_FET7_CTRL, - TPS65090_NUM_REGS, -}; - -enum { - IRQ1_VBATG = 1 << 3, - CG_CTRL0_ENC_MASK = 0x01, - - MAX_FET_NUM = 7, - MAX_CTRL_READ_TRIES = 5, - - /* TPS65090 FET_CTRL register values */ - FET_CTRL_TOFET = 1 << 7, /* Timeout, startup, overload */ - FET_CTRL_PGFET = 1 << 4, /* Power good for FET status */ - FET_CTRL_WAIT = 3 << 2, /* Overcurrent timeout max */ - FET_CTRL_ADENFET = 1 << 1, /* Enable output auto discharge */ - FET_CTRL_ENFET = 1 << 0, /* Enable FET */ -}; - -/** - * tps65090_read - read a byte from tps6090 - * - * @param reg The register address to read from. - * @param val We'll return value value read here. - * @return 0 if ok; error if EC returns failure. - */ -static int tps65090_read(u32 reg, u8 *val) -{ - return cros_ec_i2c_xfer_old(config.dev, TPS65090_ADDR, reg, 1, - val, 1, true); -} - -/** - * tps65090_write - write a byte to tps6090 - * - * @param reg The register address to write to. - * @param val The value to write. - * @return 0 if ok; error if EC returns failure. - */ -static int tps65090_write(u32 reg, u8 val) -{ - return cros_ec_i2c_xfer_old(config.dev, TPS65090_ADDR, reg, 1, - &val, 1, false); -} - -/** - * Checks for a valid FET number - * - * @param fet_id FET number to check - * @return 0 if ok, -EINVAL if FET value is out of range - */ -static int tps65090_check_fet(unsigned int fet_id) -{ - if (fet_id == 0 || fet_id > MAX_FET_NUM) { - debug("parameter fet_id is out of range, %u not in 1 ~ %u\n", - fet_id, MAX_FET_NUM); - return -EINVAL; - } - - return 0; -} - -/** - * Set the power state for a FET - * - * @param fet_id Fet number to set (1..MAX_FET_NUM) - * @param set 1 to power on FET, 0 to power off - * @return -EIO if we got a comms error, -EAGAIN if the FET failed to - * change state. If all is ok, returns 0. - */ -static int tps65090_fet_set(int fet_id, bool set) -{ - int retry; - u8 reg, value; - - value = FET_CTRL_ADENFET | FET_CTRL_WAIT; - if (set) - value |= FET_CTRL_ENFET; - - if (tps65090_write(REG_FET1_CTRL + fet_id - 1, value)) - return -EIO; - - /* Try reading until we get a result */ - for (retry = 0; retry < MAX_CTRL_READ_TRIES; retry++) { - if (tps65090_read(REG_FET1_CTRL + fet_id - 1, ®)) - return -EIO; - - /* Check that the fet went into the expected state */ - if (!!(reg & FET_CTRL_PGFET) == set) - return 0; - - /* If we got a timeout, there is no point in waiting longer */ - if (reg & FET_CTRL_TOFET) - break; - - mdelay(1); - } - - debug("FET %d: Power good should have set to %d but reg=%#02x\n", - fet_id, set, reg); - return -EAGAIN; -} - -int tps65090_fet_enable(unsigned int fet_id) -{ - ulong start; - int loops; - int ret; - - ret = tps65090_check_fet(fet_id); - if (ret) - return ret; - - start = get_timer(0); - for (loops = 0;; loops++) { - ret = tps65090_fet_set(fet_id, true); - if (!ret) - break; - - if (get_timer(start) > 100) - break; - - /* Turn it off and try again until we time out */ - tps65090_fet_set(fet_id, false); - } - - if (ret) { - debug("%s: FET%d failed to power on: time=%lums, loops=%d\n", - __func__, fet_id, get_timer(start), loops); - } else if (loops) { - debug("%s: FET%d powered on after %lums, loops=%d\n", - __func__, fet_id, get_timer(start), loops); - } - /* - * Unfortunately, there are some conditions where the power - * good bit will be 0, but the fet still comes up. One such - * case occurs with the lcd backlight. We'll just return 0 here - * and assume that the fet will eventually come up. - */ - if (ret == -EAGAIN) - ret = 0; - - return ret; -} - -int tps65090_fet_disable(unsigned int fet_id) -{ - int ret; - - ret = tps65090_check_fet(fet_id); - if (ret) - return ret; - - ret = tps65090_fet_set(fet_id, false); - - return ret; -} - -int tps65090_fet_is_enabled(unsigned int fet_id) -{ - u8 reg = 0; - int ret; - - ret = tps65090_check_fet(fet_id); - if (ret) - return ret; - ret = tps65090_read(REG_FET1_CTRL + fet_id - 1, ®); - if (ret) { - debug("fail to read FET%u_CTRL register over I2C", fet_id); - return -EIO; - } - - return reg & FET_CTRL_ENFET; -} - -int tps65090_init(void) -{ - puts("TPS65090 PMIC EC init\n"); - - config.dev = board_get_cros_ec_dev(); - if (!config.dev) { - debug("%s: no cros_ec device: cannot init tps65090\n", - __func__); - return -ENODEV; - } - - return 0; -} diff --git a/include/configs/peach-pit.h b/include/configs/peach-pit.h index 18be42c..beb65b0 100644 --- a/include/configs/peach-pit.h +++ b/include/configs/peach-pit.h @@ -34,8 +34,6 @@ #define CONFIG_VIDEO_PARADE -#define CONFIG_POWER_TPS65090_EC - /* DRAM Memory Banks */ #define CONFIG_NR_DRAM_BANKS 4 #define SDRAM_BANK_SIZE (512UL << 20UL) /* 512 MB */ diff --git a/include/fdtdec.h b/include/fdtdec.h index 4b3f8d1..12dc5fd 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -170,7 +170,6 @@ enum fdt_compat_id { COMPAT_INFINEON_SLB9645_TPM, /* Infineon SLB9645 TPM */ COMPAT_SAMSUNG_EXYNOS5_I2C, /* Exynos5 High Speed I2C Controller */ COMPAT_SANDBOX_LCD_SDL, /* Sandbox LCD emulation with SDL */ - COMPAT_TI_TPS65090, /* Texas Instrument TPS65090 */ COMPAT_NXP_PTN3460, /* NXP PTN3460 DP/LVDS bridge */ COMPAT_SAMSUNG_EXYNOS_SYSMMU, /* Exynos sysmmu */ COMPAT_PARADE_PS8625, /* Parade PS8622 EDP->LVDS bridge */ diff --git a/include/power/tps65090_pmic.h b/include/power/tps65090_pmic.h deleted file mode 100644 index dcf99c9..0000000 --- a/include/power/tps65090_pmic.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2012 The Chromium OS Authors. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#ifndef __TPS65090_PMIC_H_ -#define __TPS65090_PMIC_H_ - -/* I2C device address for TPS65090 PMU */ -#define TPS65090_I2C_ADDR 0x48 - -enum { - /* Status register fields */ - TPS65090_ST1_OTC = 1 << 0, - TPS65090_ST1_OCC = 1 << 1, - TPS65090_ST1_STATE_SHIFT = 4, - TPS65090_ST1_STATE_MASK = 0xf << TPS65090_ST1_STATE_SHIFT, -}; - -/** - * Enable FET - * - * @param fet_id FET ID, value between 1 and 7 - * @return 0 on success, non-0 on failure - */ -int tps65090_fet_enable(unsigned int fet_id); - -/** - * Disable FET - * - * @param fet_id FET ID, value between 1 and 7 - * @return 0 on success, non-0 on failure - */ -int tps65090_fet_disable(unsigned int fet_id); - -/** - * Is FET enabled? - * - * @param fet_id FET ID, value between 1 and 7 - * @return 1 enabled, 0 disabled, negative value on failure - */ -int tps65090_fet_is_enabled(unsigned int fet_id); - -/** - * Enable / disable the battery charger - * - * @param enable 0 to disable charging, non-zero to enable - */ -int tps65090_set_charge_enable(int enable); - -/** - * Check whether we have enabled battery charging - * - * @return 1 if enabled, 0 if disabled - */ -int tps65090_get_charging(void); - -/** - * Return the value of the status register - * - * @return status register value, or -1 on error - */ -int tps65090_get_status(void); - -/** - * Initialize the TPS65090 PMU. - * - * @return 0 on success, non-0 on failure - */ -int tps65090_init(void); - -#endif /* __TPS65090_PMIC_H_ */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 48667ef..3d20f38 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -62,7 +62,6 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(INFINEON_SLB9645_TPM, "infineon,slb9645tt"), COMPAT(SAMSUNG_EXYNOS5_I2C, "samsung,exynos5-hsi2c"), COMPAT(SANDBOX_LCD_SDL, "sandbox,lcd-sdl"), - COMPAT(TI_TPS65090, "ti,tps65090"), COMPAT(COMPAT_NXP_PTN3460, "nxp,ptn3460"), COMPAT(SAMSUNG_EXYNOS_SYSMMU, "samsung,sysmmu-v3.3"), COMPAT(PARADE_PS8625, "parade,ps8625"), -- cgit v0.10.2 From 24696e2f214b3a14a7fbaae032800fb8f8fc848b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:33 -0600 Subject: cros_ec: Remove the old tunnel code This is not needed with driver mode. Remove it. Signed-off-by: Simon Glass diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c index 6027177..ba36795 100644 --- a/drivers/misc/cros_ec.c +++ b/drivers/misc/cros_ec.c @@ -1055,87 +1055,6 @@ int cros_ec_decode_ec_flash(const void *blob, int node, return 0; } -int cros_ec_i2c_xfer_old(struct cros_ec_dev *dev, uchar chip, uint addr, - int alen, uchar *buffer, int len, int is_read) -{ - union { - struct ec_params_i2c_passthru p; - uint8_t outbuf[EC_PROTO2_MAX_PARAM_SIZE]; - } params; - union { - struct ec_response_i2c_passthru r; - uint8_t inbuf[EC_PROTO2_MAX_PARAM_SIZE]; - } response; - struct ec_params_i2c_passthru *p = ¶ms.p; - struct ec_response_i2c_passthru *r = &response.r; - struct ec_params_i2c_passthru_msg *msg = p->msg; - uint8_t *pdata; - int read_len, write_len; - int size; - int rv; - - p->port = 0; - - if (alen != 1) { - printf("Unsupported address length %d\n", alen); - return -1; - } - if (is_read) { - read_len = len; - write_len = alen; - p->num_msgs = 2; - } else { - read_len = 0; - write_len = alen + len; - p->num_msgs = 1; - } - - size = sizeof(*p) + p->num_msgs * sizeof(*msg); - if (size + write_len > sizeof(params)) { - puts("Params too large for buffer\n"); - return -1; - } - if (sizeof(*r) + read_len > sizeof(response)) { - puts("Read length too big for buffer\n"); - return -1; - } - - /* Create a message to write the register address and optional data */ - pdata = (uint8_t *)p + size; - msg->addr_flags = chip; - msg->len = write_len; - pdata[0] = addr; - if (!is_read) - memcpy(pdata + 1, buffer, len); - msg++; - - if (read_len) { - msg->addr_flags = chip | EC_I2C_FLAG_READ; - msg->len = read_len; - } - - rv = ec_command(dev, EC_CMD_I2C_PASSTHRU, 0, p, size + write_len, - r, sizeof(*r) + read_len); - if (rv < 0) - return rv; - - /* Parse response */ - if (r->i2c_status & EC_I2C_STATUS_ERROR) { - printf("Transfer failed with status=0x%x\n", r->i2c_status); - return -1; - } - - if (rv < sizeof(*r) + read_len) { - puts("Truncated read response\n"); - return -1; - } - - if (read_len) - memcpy(buffer, r->data, read_len); - - return 0; -} - int cros_ec_i2c_tunnel(struct udevice *dev, struct i2c_msg *in, int nmsgs) { struct cros_ec_dev *cdev = dev_get_uclass_priv(dev); @@ -1266,187 +1185,6 @@ static int do_read_write(struct cros_ec_dev *dev, int is_write, int argc, return 0; } -/** - * get_alen() - Small parser helper function to get address length - * - * Returns the address length. - */ -static uint get_alen(char *arg) -{ - int j; - int alen; - - alen = 1; - for (j = 0; j < 8; j++) { - if (arg[j] == '.') { - alen = arg[j+1] - '0'; - break; - } else if (arg[j] == '\0') { - break; - } - } - return alen; -} - -#define DISP_LINE_LEN 16 - -/* - * TODO(sjg@chromium.org): This code copied almost verbatim from cmd_i2c.c - * so we can remove it later. - */ -static int cros_ec_i2c_md(struct cros_ec_dev *dev, int flag, int argc, - char * const argv[]) -{ - u_char chip; - uint addr, alen, length = 0x10; - int j, nbytes, linebytes; - - if (argc < 2) - return CMD_RET_USAGE; - - if (1 || (flag & CMD_FLAG_REPEAT) == 0) { - /* - * New command specified. - */ - - /* - * I2C chip address - */ - chip = simple_strtoul(argv[0], NULL, 16); - - /* - * I2C data address within the chip. This can be 1 or - * 2 bytes long. Some day it might be 3 bytes long :-). - */ - addr = simple_strtoul(argv[1], NULL, 16); - alen = get_alen(argv[1]); - if (alen > 3) - return CMD_RET_USAGE; - - /* - * If another parameter, it is the length to display. - * Length is the number of objects, not number of bytes. - */ - if (argc > 2) - length = simple_strtoul(argv[2], NULL, 16); - } - - /* - * Print the lines. - * - * We buffer all read data, so we can make sure data is read only - * once. - */ - nbytes = length; - do { - unsigned char linebuf[DISP_LINE_LEN]; - unsigned char *cp; - - linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes; - - if (cros_ec_i2c_xfer_old(dev, chip, addr, alen, linebuf, - linebytes, 1)) - puts("Error reading the chip.\n"); - else { - printf("%04x:", addr); - cp = linebuf; - for (j = 0; j < linebytes; j++) { - printf(" %02x", *cp++); - addr++; - } - puts(" "); - cp = linebuf; - for (j = 0; j < linebytes; j++) { - if ((*cp < 0x20) || (*cp > 0x7e)) - puts("."); - else - printf("%c", *cp); - cp++; - } - putc('\n'); - } - nbytes -= linebytes; - } while (nbytes > 0); - - return 0; -} - -static int cros_ec_i2c_mw(struct cros_ec_dev *dev, int flag, int argc, - char * const argv[]) -{ - uchar chip; - ulong addr; - uint alen; - uchar byte; - int count; - - if ((argc < 3) || (argc > 4)) - return CMD_RET_USAGE; - - /* - * Chip is always specified. - */ - chip = simple_strtoul(argv[0], NULL, 16); - - /* - * Address is always specified. - */ - addr = simple_strtoul(argv[1], NULL, 16); - alen = get_alen(argv[1]); - if (alen > 3) - return CMD_RET_USAGE; - - /* - * Value to write is always specified. - */ - byte = simple_strtoul(argv[2], NULL, 16); - - /* - * Optional count - */ - if (argc == 4) - count = simple_strtoul(argv[3], NULL, 16); - else - count = 1; - - while (count-- > 0) { - if (cros_ec_i2c_xfer_old(dev, chip, addr++, alen, &byte, 1, 0)) - puts("Error writing the chip.\n"); - /* - * Wait for the write to complete. The write can take - * up to 10mSec (we allow a little more time). - */ -/* - * No write delay with FRAM devices. - */ -#if !defined(CONFIG_SYS_I2C_FRAM) - udelay(11000); -#endif - } - - return 0; -} - -/* Temporary code until we have driver model and can use the i2c command */ -static int cros_ec_i2c_passthrough(struct cros_ec_dev *dev, int flag, - int argc, char * const argv[]) -{ - const char *cmd; - - if (argc < 1) - return CMD_RET_USAGE; - cmd = *argv++; - argc--; - if (0 == strcmp("md", cmd)) - cros_ec_i2c_md(dev, flag, argc, argv); - else if (0 == strcmp("mw", cmd)) - cros_ec_i2c_mw(dev, flag, argc, argv); - else - return CMD_RET_USAGE; - - return 0; -} - static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { struct cros_ec_dev *dev; @@ -1696,8 +1434,6 @@ static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) debug("%s: Could not access LDO%d\n", __func__, index); return ret; } - } else if (0 == strcmp("i2c", cmd)) { - ret = cros_ec_i2c_passthrough(dev, flag, argc - 2, argv + 2); } else { return CMD_RET_USAGE; } @@ -1734,9 +1470,7 @@ U_BOOT_CMD( "crosec vbnvcontext [hexstring] Read [write] VbNvContext from EC\n" "crosec ldo [] Switch/Read LDO state\n" "crosec test run tests on cros_ec\n" - "crosec version Read CROS-EC version\n" - "crosec i2c md chip address[.0, .1, .2] [# of objects] - read from I2C passthru\n" - "crosec i2c mw chip address[.0, .1, .2] value [count] - write to I2C passthru (fill)" + "crosec version Read CROS-EC version" ); #endif diff --git a/include/cros_ec.h b/include/cros_ec.h index 0ad9d81..b926934 100644 --- a/include/cros_ec.h +++ b/include/cros_ec.h @@ -400,18 +400,4 @@ struct i2c_msg; */ int cros_ec_i2c_tunnel(struct udevice *dev, struct i2c_msg *msg, int nmsgs); -/* - * Tunnel an I2C transfer to the EC - * - * @param dev CROS-EC device - * @param chip Chip address (7-bit I2C address) - * @param addr Register address to read/write - * @param alen Length of register address in bytes - * @param buffer Buffer containing data to read/write - * @param len Length of buffer - * @param is_read 1 if this is a read, 0 if this is a write - */ -int cros_ec_i2c_xfer_old(struct cros_ec_dev *dev, uchar chip, uint addr, - int alen, uchar *buffer, int len, int is_read); - #endif -- cgit v0.10.2 From 68964dbc838077c8ecd796fd43b0aa43bb309a50 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:34 -0600 Subject: video: Remove the old parade driver We have a new one which uses driver model and device tree configuration. Remove the old one. Signed-off-by: Simon Glass diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 599fe83..c2c4dfc 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -51,7 +51,6 @@ obj-$(CONFIG_VIDEO_VESA) += vesa_fb.o obj-$(CONFIG_FORMIKE) += formike.o obj-$(CONFIG_LG4573) += lg4573.o obj-$(CONFIG_AM335X_LCD) += am335x-fb.o -obj-$(CONFIG_VIDEO_PARADE) += parade.o obj-${CONFIG_VIDEO_TEGRA124} += tegra124/ diff --git a/drivers/video/parade.c b/drivers/video/parade.c deleted file mode 100644 index ae50971..0000000 --- a/drivers/video/parade.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (c) 2014 The Chromium OS Authors. All rights reserved. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * This file is a driver for Parade dP<->LVDS bridges. The original submission - * is for the ps8625 chip. - */ -#include -#include -#include -#include -#include - -/* - * Initialization of the chip is a process of writing certaing values into - * certain registers over i2c bus. The chip in fact responds to a range of - * addresses on the i2c bus, so for each written value three parameters are - * required: i2c address, register address and the actual value. - * - * The base address is derived from the device tree, only address offset is - * stored in the table below. - */ -/** - * struct reg_data() - data for a parade register write - * - * @addr_off offset from the i2c base address for parade - * @reg_addr register address to write - * @value value to be written - */ -struct reg_data { - uint8_t addr_off; - uint8_t reg; - uint8_t value; -} _packed; - -#define END_OF_TABLE 0xff /* Ficticious offset */ - -static const struct reg_data parade_values[] = { - {0x02, 0xa1, 0x01}, /* HPD low */ - /* - * SW setting - * [1:0] SW output 1.2V voltage is lower to 96% - */ - {0x04, 0x14, 0x01}, - /* - * RCO SS setting - * [5:4] = b01 0.5%, b10 1%, b11 1.5% - */ - {0x04, 0xe3, 0x20}, - {0x04, 0xe2, 0x80}, /* [7] RCO SS enable */ - /* - * RPHY Setting - * [3:2] CDR tune wait cycle before - * measure for fine tune b00: 1us, - * 01: 0.5us, 10:2us, 11:4us. - */ - {0x04, 0x8a, 0x0c}, - {0x04, 0x89, 0x08}, /* [3] RFD always on */ - /* - * CTN lock in/out: - * 20000ppm/80000ppm. Lock out 2 - * times. - */ - {0x04, 0x71, 0x2d}, - /* - * 2.7G CDR settings - * NOF=40LSB for HBR CDR setting - */ - {0x04, 0x7d, 0x07}, - {0x04, 0x7b, 0x00}, /* [1:0] Fmin=+4bands */ - {0x04, 0x7a, 0xfd}, /* [7:5] DCO_FTRNG=+-40% */ - /* - * 1.62G CDR settings - * [5:2]NOF=64LSB [1:0]DCO scale is 2/5 - */ - {0x04, 0xc0, 0x12}, - {0x04, 0xc1, 0x92}, /* Gitune=-37% */ - {0x04, 0xc2, 0x1c}, /* Fbstep=100% */ - {0x04, 0x32, 0x80}, /* [7] LOS signal disable */ - /* - * RPIO Setting - * [7:4] LVDS driver bias current : - * 75% (250mV swing) - */ - {0x04, 0x00, 0xb0}, - /* - * [7:6] Right-bar GPIO output strength is 8mA - */ - {0x04, 0x15, 0x40}, - /* EQ Training State Machine Setting */ - {0x04, 0x54, 0x10}, /* RCO calibration start */ - /* [4:0] MAX_LANE_COUNT set to one lane */ - {0x01, 0x02, 0x81}, - /* [4:0] LANE_COUNT_SET set to one lane */ - {0x01, 0x21, 0x81}, - {0x00, 0x52, 0x20}, - {0x00, 0xf1, 0x03}, /* HPD CP toggle enable */ - {0x00, 0x62, 0x41}, - /* Counter number, add 1ms counter delay */ - {0x00, 0xf6, 0x01}, - /* - * [6]PWM function control by - * DPCD0040f[7], default is PWM - * block always works. - */ - {0x00, 0x77, 0x06}, - /* - * 04h Adjust VTotal tolerance to - * fix the 30Hz no display issue - */ - {0x00, 0x4c, 0x04}, - /* DPCD00400='h00, Parade OUI = 'h001cf8 */ - {0x01, 0xc0, 0x00}, - {0x01, 0xc1, 0x1c}, /* DPCD00401='h1c */ - {0x01, 0xc2, 0xf8}, /* DPCD00402='hf8 */ - /* - * DPCD403~408 = ASCII code - * D2SLV5='h4432534c5635 - */ - {0x01, 0xc3, 0x44}, - {0x01, 0xc4, 0x32}, /* DPCD404 */ - {0x01, 0xc5, 0x53}, /* DPCD405 */ - {0x01, 0xc6, 0x4c}, /* DPCD406 */ - {0x01, 0xc7, 0x56}, /* DPCD407 */ - {0x01, 0xc8, 0x35}, /* DPCD408 */ - /* - * DPCD40A, Initial Code major revision - * '01' - */ - {0x01, 0xca, 0x01}, - /* DPCD40B, Initial Code minor revision '05' */ - {0x01, 0xcb, 0x05}, - /* DPCD720, Select internal PWM */ - {0x01, 0xa5, 0xa0}, - /* - * FFh for 100% PWM of brightness, 0h for 0% - * brightness - */ - {0x01, 0xa7, 0xff}, - /* - * Set LVDS output as 6bit-VESA mapping, - * single LVDS channel - */ - {0x01, 0xcc, 0x13}, - /* Enable SSC set by register */ - {0x02, 0xb1, 0x20}, - /* - * Set SSC enabled and +/-1% central - * spreading - */ - {0x04, 0x10, 0x16}, - /* MPU Clock source: LC => RCO */ - {0x04, 0x59, 0x60}, - {0x04, 0x54, 0x14}, /* LC -> RCO */ - {0x02, 0xa1, 0x91}, /* HPD high */ - {END_OF_TABLE} -}; - -/** - * Write values table into the Parade eDP bridge - * - * @return 0 on success, non-0 on failure - */ - -static int parade_write_regs(int base_addr, const struct reg_data *table) -{ - int ret = 0; - - while (!ret && (table->addr_off != END_OF_TABLE)) { - ret = i2c_write(base_addr + table->addr_off, - table->reg, 1, - (uint8_t *)&table->value, - sizeof(table->value)); - table++; - } - return ret; -} - -int parade_init(const void *blob) -{ - struct gpio_desc rst_gpio; - struct gpio_desc slp_gpio; - int bus, old_bus; - int parent; - int node; - int addr; - int ret; - - node = fdtdec_next_compatible(blob, 0, COMPAT_PARADE_PS8625); - if (node < 0) - return 0; - - parent = fdt_parent_offset(blob, node); - if (parent < 0) { - debug("%s: Could not find parent i2c node\n", __func__); - return -1; - } - addr = fdtdec_get_int(blob, node, "reg", -1); - if (addr < 0) { - debug("%s: Could not find i2c address\n", __func__); - return -1; - } - - gpio_request_by_name_nodev(blob, node, "sleep-gpio", 0, &slp_gpio, - GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); - - mdelay(10); - - gpio_request_by_name_nodev(blob, node, "reset-gpio", 0, &rst_gpio, - GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); - - bus = i2c_get_bus_num_fdt(parent); - old_bus = i2c_get_bus_num(); - - debug("%s: Using i2c bus %d\n", __func__, bus); - - /* - * TODO(sjg@chromium.org): Hmmm we seem to need some sort of delay - * here. - */ - mdelay(40); - i2c_set_bus_num(bus); - ret = parade_write_regs(addr, parade_values); - - i2c_set_bus_num(old_bus); - - return ret; -} diff --git a/include/configs/peach-pi.h b/include/configs/peach-pi.h index b97faf2..0f5e9fe 100644 --- a/include/configs/peach-pi.h +++ b/include/configs/peach-pi.h @@ -32,8 +32,6 @@ #define CONFIG_SYS_PROMPT "Peach-Pi # " #define CONFIG_IDENT_STRING " for Peach-Pi" -#define CONFIG_VIDEO_PARADE - /* Display */ #define CONFIG_LCD #ifdef CONFIG_LCD diff --git a/include/configs/peach-pit.h b/include/configs/peach-pit.h index beb65b0..f259434 100644 --- a/include/configs/peach-pit.h +++ b/include/configs/peach-pit.h @@ -32,8 +32,6 @@ #define CONFIG_SYS_PROMPT "Peach-Pit # " #define CONFIG_IDENT_STRING " for Peach-Pit" -#define CONFIG_VIDEO_PARADE - /* DRAM Memory Banks */ #define CONFIG_NR_DRAM_BANKS 4 #define SDRAM_BANK_SIZE (512UL << 20UL) /* 512 MB */ diff --git a/include/fdtdec.h b/include/fdtdec.h index 12dc5fd..b994f2c 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -172,7 +172,6 @@ enum fdt_compat_id { COMPAT_SANDBOX_LCD_SDL, /* Sandbox LCD emulation with SDL */ COMPAT_NXP_PTN3460, /* NXP PTN3460 DP/LVDS bridge */ COMPAT_SAMSUNG_EXYNOS_SYSMMU, /* Exynos sysmmu */ - COMPAT_PARADE_PS8625, /* Parade PS8622 EDP->LVDS bridge */ COMPAT_INTEL_MICROCODE, /* Intel microcode update */ COMPAT_MEMORY_SPD, /* Memory SPD information */ COMPAT_INTEL_PANTHERPOINT_AHCI, /* Intel Pantherpoint AHCI */ diff --git a/include/parade.h b/include/parade.h deleted file mode 100644 index 887f56d..0000000 --- a/include/parade.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * (C) Copyright 2012 Samsung Electronics - * Donghwa Lee - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#ifndef __PARADE_H__ -#define __PARADE_H__ - -/* Initialize the Parade dP<->LVDS bridge if present */ -#ifdef CONFIG_VIDEO_PARADE -int parade_init(const void *blob); -#else -static inline int parade_init(const void *blob) { return -1; } -#endif - -#endif /* __PARADE_H__ */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 3d20f38..5279163 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -64,7 +64,6 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(SANDBOX_LCD_SDL, "sandbox,lcd-sdl"), COMPAT(COMPAT_NXP_PTN3460, "nxp,ptn3460"), COMPAT(SAMSUNG_EXYNOS_SYSMMU, "samsung,sysmmu-v3.3"), - COMPAT(PARADE_PS8625, "parade,ps8625"), COMPAT(INTEL_MICROCODE, "intel,microcode"), COMPAT(MEMORY_SPD, "memory-spd"), COMPAT(INTEL_PANTHERPOINT_AHCI, "intel,pantherpoint-ahci"), -- cgit v0.10.2 From 5ae3a5e887d06340469f6b4b2d3a1d57655b04d0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:35 -0600 Subject: dts: Drop unused compatible ID for the NXP video bridge This has moved to driver model so we can drop the fdtdec support. Signed-off-by: Simon Glass diff --git a/include/fdtdec.h b/include/fdtdec.h index b994f2c..cd4ec66 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -170,7 +170,6 @@ enum fdt_compat_id { COMPAT_INFINEON_SLB9645_TPM, /* Infineon SLB9645 TPM */ COMPAT_SAMSUNG_EXYNOS5_I2C, /* Exynos5 High Speed I2C Controller */ COMPAT_SANDBOX_LCD_SDL, /* Sandbox LCD emulation with SDL */ - COMPAT_NXP_PTN3460, /* NXP PTN3460 DP/LVDS bridge */ COMPAT_SAMSUNG_EXYNOS_SYSMMU, /* Exynos sysmmu */ COMPAT_INTEL_MICROCODE, /* Intel microcode update */ COMPAT_MEMORY_SPD, /* Memory SPD information */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 5279163..0153109 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -62,7 +62,6 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(INFINEON_SLB9645_TPM, "infineon,slb9645tt"), COMPAT(SAMSUNG_EXYNOS5_I2C, "samsung,exynos5-hsi2c"), COMPAT(SANDBOX_LCD_SDL, "sandbox,lcd-sdl"), - COMPAT(COMPAT_NXP_PTN3460, "nxp,ptn3460"), COMPAT(SAMSUNG_EXYNOS_SYSMMU, "samsung,sysmmu-v3.3"), COMPAT(INTEL_MICROCODE, "intel,microcode"), COMPAT(MEMORY_SPD, "memory-spd"), -- cgit v0.10.2 From a7a73ef85fc894d52013abf8616c1f9c0aea9d26 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:36 -0600 Subject: exynos: video: Remove non-device-tree code We always use device tree on exynos, so remove the unused code. Signed-off-by: Simon Glass diff --git a/arch/arm/include/asm/arch-exynos/dp_info.h b/arch/arm/include/asm/arch-exynos/dp_info.h index 3f6750a..17e8f56 100644 --- a/arch/arm/include/asm/arch-exynos/dp_info.h +++ b/arch/arm/include/asm/arch-exynos/dp_info.h @@ -197,6 +197,4 @@ unsigned int exynos_init_dp(void) } #endif -void exynos_set_dp_platform_data(struct exynos_dp_platform_data *pd); - #endif /* _DP_INFO_H */ diff --git a/drivers/video/exynos_dp.c b/drivers/video/exynos_dp.c index c3f7a3a..5b6fc14 100644 --- a/drivers/video/exynos_dp.c +++ b/drivers/video/exynos_dp.c @@ -22,8 +22,6 @@ DECLARE_GLOBAL_DATA_PTR; -static struct exynos_dp_platform_data *dp_pd; - void __exynos_set_dp_phy(unsigned int onoff) { } @@ -851,7 +849,6 @@ static unsigned int exynos_dp_config_video(struct edp_device_info *edp_info) return ret; } -#ifdef CONFIG_OF_CONTROL int exynos_dp_parse_dt(const void *blob, struct edp_device_info *edp_info) { unsigned int node = fdtdec_next_compatible(blob, 0, @@ -905,7 +902,6 @@ int exynos_dp_parse_dt(const void *blob, struct edp_device_info *edp_info) "samsung,color-depth", 0); return 0; } -#endif unsigned int exynos_init_dp(void) { @@ -918,16 +914,8 @@ unsigned int exynos_init_dp(void) return -EFAULT; } -#ifdef CONFIG_OF_CONTROL if (exynos_dp_parse_dt(gd->fdt_blob, edp_info)) debug("unable to parse DP DT node\n"); -#else - edp_info = dp_pd->edp_dev_info; - if (edp_info == NULL) { - debug("failed to get edp_info data.\n"); - return -EFAULT; - } -#endif exynos_dp_set_base_addr(); @@ -971,13 +959,3 @@ unsigned int exynos_init_dp(void) return ret; } - -void exynos_set_dp_platform_data(struct exynos_dp_platform_data *pd) -{ - if (pd == NULL) { - debug("pd is NULL\n"); - return; - } - - dp_pd = pd; -} -- cgit v0.10.2 From d1de41d7fa68b29510da1320d6646fcc19113af2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:37 -0600 Subject: exynos: Add support for spring Spring is the first ARM-based HP Chromebook 11. It is similar to snow and it uses the same Samsung Exynos5250 chip. But has some unusual features. Mainline support for it has lagged snow (both in kernel and U-Boot). Now that the exynos5 code is common we can support spring just by adding a device tree and a few lines of configuration. Signed-off-by: Simon Glass diff --git a/arch/arm/cpu/armv7/exynos/Kconfig b/arch/arm/cpu/armv7/exynos/Kconfig index 4a7d82f..37b89b0 100644 --- a/arch/arm/cpu/armv7/exynos/Kconfig +++ b/arch/arm/cpu/armv7/exynos/Kconfig @@ -51,6 +51,12 @@ config TARGET_SNOW select OF_CONTROL select SPL_DISABLE_OF_CONTROL +config TARGET_SPRING + bool "Spring board" + select SUPPORT_SPL + select OF_CONTROL + select SPL_DISABLE_OF_CONTROL + config TARGET_SMDK5420 bool "SMDK5420 board" select SUPPORT_SPL diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index ba63553..2df957c 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -9,6 +9,7 @@ dtb-$(CONFIG_EXYNOS4) += exynos4210-origen.dtb \ dtb-$(CONFIG_EXYNOS5) += exynos5250-arndale.dtb \ exynos5250-snow.dtb \ + exynos5250-spring.dtb \ exynos5250-smdk5250.dtb \ exynos5420-smdk5420.dtb \ exynos5420-peach-pit.dtb \ diff --git a/arch/arm/dts/exynos5250-spring.dts b/arch/arm/dts/exynos5250-spring.dts new file mode 100644 index 0000000..76d5323 --- /dev/null +++ b/arch/arm/dts/exynos5250-spring.dts @@ -0,0 +1,588 @@ +/* + * Google Spring board device tree source + * + * Copyright (c) 2013 Google, Inc + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * SPDX-License-Identifier: GPL-2.0 + */ + +/dts-v1/; +#include +#include +#include +#include "exynos5250.dtsi" + +/ { + model = "Google Spring"; + compatible = "google,spring", "samsung,exynos5250", "samsung,exynos5"; + + aliases { + i2c0 = "/i2c@12C60000"; + i2c1 = "/i2c@12C70000"; + i2c2 = "/i2c@12C80000"; + i2c3 = "/i2c@12C90000"; + i2c4 = "/i2c@12CA0000"; + i2c5 = "/i2c@12CB0000"; + i2c6 = "/i2c@12CC0000"; + i2c7 = "/i2c@12CD0000"; + i2c104 = &cros_ec_ldo_tunnel; + spi0 = "/spi@12d20000"; + spi1 = "/spi@12d30000"; + spi2 = "/spi@12d40000"; + spi3 = "/spi@131a0000"; + spi4 = "/spi@131b0000"; + mmc0 = "/mmc@12000000"; + serial0 = "/serial@12C30000"; + console = "/serial@12C30000"; + i2s = "/sound@3830000"; + }; + + memory { + reg = <0x40000000 0x80000000>; + }; + + flash@0 { + spl { /* spl size override */ + size = <0x8000>; + }; + }; + + chosen { + bootargs = "console=tty1"; + stdout-path = "serial3:115200n8"; + }; + + board-rev { + compatible = "google,board-revision"; + google,board-rev-gpios = <&gpy4 0 0>, <&gpy4 1 0>, + <&gpy4 2 0>; + }; + + mmc@12200000 { + samsung,bus-width = <8>; + samsung,timing = <1 3 3>; + samsung,removable = <0>; + }; + + mmc@12210000 { + status = "disabled"; + }; + + mmc@12220000 { + /* MMC2 pins are used as GPIO for eDP bridge */ + status = "disabled"; + }; + + mmc@12230000 { + status = "disabled"; + }; + + ehci@12110000 { + samsung,vbus-gpio = <&gpx1 1 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; + + xhci@12000000 { + samsung,vbus-gpio = <&gpx2 7 GPIO_ACTIVE_HIGH>; + }; + + spi@12d30000 { + spi-max-frequency = <50000000>; + firmware_storage_spi: flash@0 { + compatible = "spi-flash"; + reg = <0>; + }; + }; + + tmu@10060000 { + samsung,min-temp = <25>; + samsung,max-temp = <125>; + samsung,start-warning = <95>; + samsung,start-tripping = <105>; + samsung,hw-tripping = <110>; + samsung,efuse-min-value = <40>; + samsung,efuse-value = <55>; + samsung,efuse-max-value = <100>; + samsung,slope = <274761730>; + samsung,dc-value = <25>; + }; + + fimd@14400000 { + samsung,vl-freq = <60>; + samsung,vl-col = <1366>; + samsung,vl-row = <768>; + samsung,vl-width = <1366>; + samsung,vl-height = <768>; + + samsung,vl-clkp; + samsung,vl-dp; + samsung,vl-hsp; + samsung,vl-vsp; + + samsung,vl-bpix = <4>; + + samsung,vl-hspw = <32>; + samsung,vl-hbpd = <80>; + samsung,vl-hfpd = <48>; + samsung,vl-vspw = <5>; + samsung,vl-vbpd = <14>; + samsung,vl-vfpd = <3>; + samsung,vl-cmd-allow-len = <0xf>; + + samsung,winid = <0>; + samsung,interface-mode = <1>; + samsung,dp-enabled = <1>; + samsung,dual-lcd-enabled = <0>; + }; + + dp@145b0000 { + samsung,lt-status = <0>; + + samsung,master-mode = <0>; + samsung,bist-mode = <0>; + samsung,bist-pattern = <0>; + samsung,h-sync-polarity = <0>; + samsung,v-sync-polarity = <0>; + samsung,interlaced = <0>; + samsung,color-space = <0>; + samsung,dynamic-range = <0>; + samsung,ycbcr-coeff = <0>; + samsung,color-depth = <1>; + }; +}; + +&i2c_0 { + status = "okay"; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <378000>; + + s5m8767-pmic@66 { + compatible = "samsung,s5m8767-pmic"; + reg = <0x66>; + interrupt-parent = <&gpx3>; + wakeup-source; + + s5m8767,pmic-buck-dvs-gpios = <&gpd1 0 GPIO_ACTIVE_LOW>, /* DVS1 */ + <&gpd1 1 GPIO_ACTIVE_LOW>, /* DVS2 */ + <&gpd1 2 GPIO_ACTIVE_LOW>; /* DVS3 */ + + s5m8767,pmic-buck-ds-gpios = <&gpx2 3 GPIO_ACTIVE_LOW>, /* SET1 */ + <&gpx2 4 GPIO_ACTIVE_LOW>, /* SET2 */ + <&gpx2 5 GPIO_ACTIVE_LOW>; /* SET3 */ + + /* + * The following arrays of DVS voltages are not used, since we are + * not using GPIOs to control PMIC bucks, but they must be defined + * to please the driver. + */ + s5m8767,pmic-buck2-dvs-voltage = <1350000>, <1300000>, + <1250000>, <1200000>, + <1150000>, <1100000>, + <1000000>, <950000>; + + s5m8767,pmic-buck3-dvs-voltage = <1100000>, <1100000>, + <1100000>, <1100000>, + <1000000>, <1000000>, + <1000000>, <1000000>; + + s5m8767,pmic-buck4-dvs-voltage = <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>; + + clocks { + compatible = "samsung,s5m8767-clk"; + #clock-cells = <1>; + clock-output-names = "en32khz_ap", + "en32khz_cp", + "en32khz_bt"; + }; + + regulators { + ldo4_reg: LDO4 { + regulator-name = "P1.0V_LDO_OUT4"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + op_mode = <0>; + }; + + ldo5_reg: LDO5 { + regulator-name = "P1.8V_LDO_OUT5"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + op_mode = <0>; + }; + + ldo6_reg: LDO6 { + regulator-name = "vdd_mydp"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + op_mode = <3>; + }; + + ldo7_reg: LDO7 { + regulator-name = "P1.1V_LDO_OUT7"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + op_mode = <3>; + }; + + ldo8_reg: LDO8 { + regulator-name = "P1.0V_LDO_OUT8"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + op_mode = <3>; + }; + + ldo10_reg: LDO10 { + regulator-name = "P1.8V_LDO_OUT10"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + op_mode = <3>; + }; + + ldo11_reg: LDO11 { + regulator-name = "P1.8V_LDO_OUT11"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + op_mode = <0>; + }; + + ldo12_reg: LDO12 { + regulator-name = "P3.0V_LDO_OUT12"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + op_mode = <3>; + }; + + ldo13_reg: LDO13 { + regulator-name = "P1.8V_LDO_OUT13"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + op_mode = <0>; + }; + + ldo14_reg: LDO14 { + regulator-name = "P1.8V_LDO_OUT14"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + op_mode = <3>; + }; + + ldo15_reg: LDO15 { + regulator-name = "P1.0V_LDO_OUT15"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + op_mode = <3>; + }; + + ldo16_reg: LDO16 { + regulator-name = "P1.8V_LDO_OUT16"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + op_mode = <3>; + }; + + ldo17_reg: LDO17 { + regulator-name = "P1.2V_LDO_OUT17"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + op_mode = <0>; + }; + + ldo25_reg: LDO25 { + regulator-name = "vdd_bridge"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + op_mode = <1>; + }; + + buck1_reg: BUCK1 { + regulator-name = "vdd_mif"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + regulator-boot-on; + op_mode = <3>; + }; + + buck2_reg: BUCK2 { + regulator-name = "vdd_arm"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + op_mode = <3>; + }; + + buck3_reg: BUCK3 { + regulator-name = "vdd_int"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + op_mode = <3>; + }; + + buck4_reg: BUCK4 { + regulator-name = "vdd_g3d"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1300000>; + regulator-boot-on; + op_mode = <3>; + }; + + buck5_reg: BUCK5 { + regulator-name = "P1.8V_BUCK_OUT5"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + op_mode = <1>; + }; + + buck6_reg: BUCK6 { + regulator-name = "P1.2V_BUCK_OUT6"; + regulator-min-microvolt = <2050000>; + regulator-max-microvolt = <2050000>; + regulator-always-on; + regulator-boot-on; + op_mode = <0>; + }; + + buck9_reg: BUCK9 { + regulator-name = "vdd_ummc"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + regulator-boot-on; + op_mode = <3>; + }; + }; + }; +}; + +&i2c_1 { + status = "okay"; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <378000>; +}; + +&i2c_2 { + status = "okay"; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <66000>; +}; + +&i2c_3 { + status = "okay"; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <66000>; +}; + +&i2c_4 { + status = "okay"; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <66000>; + clock-frequency = <66000>; + + cros_ec: embedded-controller { + compatible = "google,cros-ec-i2c"; + reg = <0x1e>; + interrupts = <6 IRQ_TYPE_NONE>; + interrupt-parent = <&gpx1>; + wakeup-source; + u-boot,i2c-offset-len = <0>; + ec-interrupt = <&gpx1 6 GPIO_ACTIVE_LOW>; + cros_ec_ldo_tunnel: cros-ec-ldo-tunnel { + compatible = "google,cros-ec-ldo-tunnel"; + #address-cells = <1>; + #size-cells = <0>; + power-regulator { + compatible = "ti,tps65090"; + reg = <0x48>; + + regulators { + dcdc1 { + ti,enable-ext-control; + }; + dcdc2 { + ti,enable-ext-control; + }; + dcdc3 { + ti,enable-ext-control; + }; + fet1: fet1 { + regulator-name = "vcd_led"; + ti,overcurrent-wait = <3>; + }; + tps65090_fet2: fet2 { + regulator-name = "video_mid"; + regulator-always-on; + ti,overcurrent-wait = <3>; + }; + fet3 { + regulator-name = "wwan_r"; + regulator-always-on; + ti,overcurrent-wait = <3>; + }; + fet4 { + regulator-name = "sdcard"; + ti,overcurrent-wait = <3>; + }; + fet5 { + regulator-name = "camout"; + regulator-always-on; + ti,overcurrent-wait = <3>; + }; + fet6: fet6 { + regulator-name = "lcd_vdd"; + ti,overcurrent-wait = <3>; + }; + tps65090_fet7: fet7 { + regulator-name = "video_mid_1a"; + regulator-always-on; + ti,overcurrent-wait = <3>; + }; + ldo1 { + }; + ldo2 { + }; + }; + }; + }; + }; +}; + +&i2c_5 { + status = "okay"; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <66000>; +}; + +&i2c_7 { + status = "okay"; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <66000>; + + ps8622-bridge@8 { + compatible = "parade,ps8622"; + reg = <0x8>; + sleep-gpios = <&gpc3 6 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpc3 1 GPIO_ACTIVE_LOW>; + hotplug-gpios = <&gpc3 0 GPIO_ACTIVE_HIGH>; + power-supply = <&ldo6_reg>; + parade,regs = /bits/ 8 < + 0x02 0xa1 0x01 /* HPD low */ + /* + * SW setting: [1:0] SW output 1.2V voltage is + * lower to 96% + */ + 0x04 0x14 0x01 + /* RCO SS setting: [5:4] = b01 0.5%, b10 1%, b11 1.5% */ + 0x04 0xe3 0x20 + 0x04 0xe2 0x80 /* [7] RCO SS enable */ + /* + * RPHY Setting: [3:2] CDR tune wait cycle before + * measure for fine tune b00: 1us, + * 01: 0.5us, 10:2us, 11:4us + */ + 0x04 0x8a 0x0c + 0x04 0x89 0x08 /* [3] RFD always on */ + /* + * CTN lock in/out: 20000ppm/80000ppm. Lock out 2 times + */ + 0x04 0x71 0x2d + /* 2.7G CDR settings */ + 0x04 0x7d 0x07 /* NOF=40LSB for HBR CDR setting */ + 0x04 0x7b 0x00 /* [1:0] Fmin=+4bands */ + 0x04 0x7a 0xfd /* [7:5] DCO_FTRNG=+-40% */ + /* + * 1.62G CDR settings: + * [5:2]NOF=64LSB [1:0]DCO scale is 2/5 + */ + 0x04 0xc0 0x12 + 0x04 0xc1 0x92 /* Gitune=-37% */ + 0x04 0xc2 0x1c /* Fbstep=100% */ + 0x04 0x32 0x80 /* [7] LOS signal disable */ + /* RPIO Setting */ + /* [7:4] LVDS driver bias current 75% (250mV swing) */ + 0x04 0x00 0xb0 + /* [7:6] Right-bar GPIO output strength is 8mA */ + 0x04 0x15 0x40 + /* EQ Training State Machine Setting */ + 0x04 0x54 0x10 /* RCO calibration start */ + /* [4:0] MAX_LANE_COUNT set to one lane */ + 0x01 0x02 0x81 + /* [4:0] LANE_COUNT_SET set to one lane */ + 0x01 0x21 0x81 + 0x00 0x52 0x20 + 0x00 0xf1 0x03 /* HPD CP toggle enable */ + 0x00 0x62 0x41 + /* Counter number add 1ms counter delay */ + 0x00 0xf6 0x01 + /* + * [6]PWM function control by DPCD0040f[7], default + * is PWM block always works + */ + 0x00 0x77 0x06 + 0x00 0x4c 0x04 + /* + * 04h Adjust VTotal tolerance to fix the 30Hz no- + * display issue + * DPCD00400='h00 Parade OUI = 'h001cf8 + */ + 0x01 0xc0 0x00 + 0x01 0xc1 0x1c /* DPCD00401='h1c */ + 0x01 0xc2 0xf8 /* DPCD00402='hf8 */ + /* DPCD403~408 = ASCII code D2SLV5='h4432534c5635 */ + 0x01 0xc3 0x44 + 0x01 0xc4 0x32 /* DPCD404 */ + 0x01 0xc5 0x53 /* DPCD405 */ + 0x01 0xc6 0x4c /* DPCD406 */ + 0x01 0xc7 0x56 /* DPCD407 */ + 0x01 0xc8 0x35 /* DPCD408 */ + /* DPCD40A Initial Code major revision '01' */ + 0x01 0xca 0x01 + /* DPCD40B Initial Code minor revision '05' */ + 0x01 0xcb 0x05 + 0x01 0xa5 0xa0 /* DPCD720, Select internal PWM */ + /* + * 0xff for 100% PWM of brightness, 0h for 0% brightness + */ + 0x01 0xa7 0x00 + /* + * Set LVDS output as 6bit-VESA mapping, single LVDS + * channel + */ + 0x01 0xcc 0x13 + 0x02 0xb1 0x20 /* Enable SSC set by register */ + /* Set SSC enabled and +/-1% central spreading */ + 0x04 0x10 0x16 + 0x04 0x59 0x60 /* MPU Clock source: LC => RCO */ + 0x04 0x54 0x14 /* LC -> RCO */ + 0x02 0xa1 0x91>; /* HPD high */ + }; + + soundcodec@20 { + reg = <0x20>; + compatible = "maxim,max98088-codec"; + }; +}; + +#include "cros-ec-keyboard.dtsi" diff --git a/board/samsung/smdk5250/Kconfig b/board/samsung/smdk5250/Kconfig index 698ee91..11ffaee 100644 --- a/board/samsung/smdk5250/Kconfig +++ b/board/samsung/smdk5250/Kconfig @@ -23,3 +23,16 @@ config SYS_CONFIG_NAME default "snow" endif + +if TARGET_SPRING + +config SYS_BOARD + default "smdk5250" + +config SYS_VENDOR + default "samsung" + +config SYS_CONFIG_NAME + default "spring" + +endif diff --git a/board/samsung/smdk5250/MAINTAINERS b/board/samsung/smdk5250/MAINTAINERS index 070593e..cde966f 100644 --- a/board/samsung/smdk5250/MAINTAINERS +++ b/board/samsung/smdk5250/MAINTAINERS @@ -10,3 +10,9 @@ M: Akshay Saraswat S: Maintained F: include/configs/snow.h F: configs/snow_defconfig + +SPRING BOARD +M: Simon Glass +S: Maintained +F: include/configs/spring.h +F: configs/spring_defconfig diff --git a/configs/spring_defconfig b/configs/spring_defconfig new file mode 100644 index 0000000..a3abb35 --- /dev/null +++ b/configs/spring_defconfig @@ -0,0 +1,42 @@ +CONFIG_ARM=y +CONFIG_ARCH_EXYNOS=y +CONFIG_TARGET_SPRING=y +CONFIG_DEFAULT_DEVICE_TREE="exynos5250-spring" +CONFIG_SPL=y +# CONFIG_CMD_IMLS is not set +# CONFIG_CMD_SETEXPR is not set +CONFIG_CMD_SOUND=y +CONFIG_SPI_FLASH=y +CONFIG_CMD_CROS_EC=y +CONFIG_CROS_EC=y +CONFIG_CROS_EC_I2C=y +CONFIG_CROS_EC_KEYB=y +CONFIG_SOUND=y +CONFIG_I2S=y +CONFIG_I2S_SAMSUNG=y +CONFIG_SOUND_MAX98095=y +CONFIG_SOUND_WM8994=y +CONFIG_USB=y +CONFIG_DM_USB=y +CONFIG_DM_I2C=y +CONFIG_DM_PMIC=y +CONFIG_DM_REGULATOR=y +CONFIG_PMIC_TPS65090=y +CONFIG_REGULATOR_TPS65090=y +CONFIG_DM_I2C_COMPAT=y +CONFIG_I2C_ARB_GPIO_CHALLENGE=y +CONFIG_I2C_MUX=y +CONFIG_CMD_PMIC=y +CONFIG_CMD_REGULATOR=y +CONFIG_ERRNO_STR=y +CONFIG_DM_PMIC_MAX77686=y +CONFIG_DM_REGULATOR_MAX77686=y +CONFIG_DEBUG_UART=y +CONFIG_DEBUG_UART_S5P=y +CONFIG_DEBUG_UART_CLOCK=100000000 +CONFIG_DEBUG_UART_BASE=0x12c30000 +CONFIG_I2C_CROS_EC_LDO=y +CONFIG_PMIC_S5M8767=y +CONFIG_REGULATOR_S5M8767=y +CONFIG_VIDEO_BRIDGE=y +CONFIG_VIDEO_BRIDGE_PARADE_PS862X=y diff --git a/include/configs/spring.h b/include/configs/spring.h new file mode 100644 index 0000000..a692dfd --- /dev/null +++ b/include/configs/spring.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __CONFIG_SPRING_H +#define __CONFIG_SPRING_H + +#include +#include +#include + +#define CONFIG_BOARD_COMMON + +#define CONFIG_SYS_PROMPT "spring # " +#define CONFIG_IDENT_STRING " for spring" +#define CONFIG_DEFAULT_CONSOLE "console=ttySAC1,115200n8\0" + +#endif /* __CONFIG_SPRING_H */ -- cgit v0.10.2 From fdb9f349be255b9b69a5e1a300296779b48a36b0 Mon Sep 17 00:00:00 2001 From: York Sun Date: Mon, 3 Aug 2015 12:02:05 -0700 Subject: lib/fdtdec: Fix compiling warning caused by changing fdt_addr_t type fdt_addr_t is changed to phys_addr_t. The format in debug should be updated to %pa to match the type. Signed-off-by: York Sun CC: Simon Glass diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c index 67b5fdf..ebb959f 100644 --- a/drivers/pci/pci_tegra.c +++ b/drivers/pci/pci_tegra.c @@ -445,11 +445,11 @@ static int tegra_pcie_parse_dt_ranges(const void *fdt, int node, } debug("PCI regions:\n"); - debug(" I/O: %#x-%#x\n", pcie->io.start, pcie->io.end); - debug(" non-prefetchable memory: %#x-%#x\n", pcie->mem.start, - pcie->mem.end); - debug(" prefetchable memory: %#x-%#x\n", pcie->prefetch.start, - pcie->prefetch.end); + debug(" I/O: %pa-%pa\n", &pcie->io.start, &pcie->io.end); + debug(" non-prefetchable memory: %pa-%pa\n", &pcie->mem.start, + &pcie->mem.end); + debug(" prefetchable memory: %pa-%pa\n", &pcie->prefetch.start, + &pcie->prefetch.end); return 0; } diff --git a/drivers/spi/fsl_dspi.c b/drivers/spi/fsl_dspi.c index 7928531..3881b2e 100644 --- a/drivers/spi/fsl_dspi.c +++ b/drivers/spi/fsl_dspi.c @@ -664,8 +664,8 @@ static int fsl_dspi_ofdata_to_platdata(struct udevice *bus) plat->speed_hz = fdtdec_get_int(blob, node, "spi-max-frequency", FSL_DSPI_DEFAULT_SCK_FREQ); - debug("DSPI: regs=0x%llx, max-frequency=%d, endianess=%s, num-cs=%d\n", - (u64)plat->regs_addr, plat->speed_hz, + debug("DSPI: regs=%pa, max-frequency=%d, endianess=%s, num-cs=%d\n", + &plat->regs_addr, plat->speed_hz, plat->flags & DSPI_FLAG_REGMAP_ENDIAN_BIG ? "be" : "le", plat->num_chipselect); diff --git a/drivers/video/tegra.c b/drivers/video/tegra.c index b8f3431..33c6103 100644 --- a/drivers/video/tegra.c +++ b/drivers/video/tegra.c @@ -92,7 +92,7 @@ void lcd_ctrl_init(void *lcdbase) /* Enable flushing after LCD writes if requested */ lcd_set_flush_dcache(config.cache_type & FDT_LCD_CACHE_FLUSH); - debug("LCD frame buffer at %08X\n", disp_config->frame_buffer); + debug("LCD frame buffer at %pa\n", &disp_config->frame_buffer); } ulong calc_fbsize(void) -- cgit v0.10.2 From 28445aa7ec099bf8d7dc49db5fbf6bdd9098b410 Mon Sep 17 00:00:00 2001 From: York Sun Date: Mon, 3 Aug 2015 12:02:04 -0700 Subject: lib/fdtdec: Fix fdt_addr_t and fdt_size_t typedef fdt_addr_t is a physical address. It can be either 64-bit or 32-bit, depending on the architecture. It should be phys_addr_t instead of u64 or u32. Similarly, fdt_size_t is changed to phys_size_t. Signed-off-by: York Sun CC: Simon Glass diff --git a/include/fdtdec.h b/include/fdtdec.h index cd4ec66..c9a5c9a 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -21,15 +21,13 @@ * A typedef for a physical address. Note that fdt data is always big * endian even on a litle endian machine. */ +typedef phys_addr_t fdt_addr_t; +typedef phys_size_t fdt_size_t; #ifdef CONFIG_PHYS_64BIT -typedef u64 fdt_addr_t; -typedef u64 fdt_size_t; #define FDT_ADDR_T_NONE (-1ULL) #define fdt_addr_to_cpu(reg) be64_to_cpu(reg) #define fdt_size_to_cpu(reg) be64_to_cpu(reg) #else -typedef u32 fdt_addr_t; -typedef u32 fdt_size_t; #define FDT_ADDR_T_NONE (-1U) #define fdt_addr_to_cpu(reg) be32_to_cpu(reg) #define fdt_size_to_cpu(reg) be32_to_cpu(reg) -- cgit v0.10.2 From 12dc8e7522230b269b4ca8fecc64bc8e59070a53 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 17 Jul 2015 09:22:07 -0600 Subject: dm: Make regmap and syscon optional Not all boards use garbage collection in their link step, so we should avoid adding options that rely on this for prevention of code bloat. Add separate Kconfig options for syscon and regmap uclasses. Signed-off-by: Simon Glass diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 5535746..e6a4590 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -52,3 +52,4 @@ CONFIG_DM_MMC=y CONFIG_LED=y CONFIG_LED_GPIO=y CONFIG_SYSCON=y +CONFIG_REGMAP=y diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index e40372d..5d0e949 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -59,3 +59,22 @@ config DM_SEQ_ALIAS Most boards will have a '/aliases' node containing the path to numbered devices (e.g. serial0 = &serial0). This feature can be disabled if it is not required, to save code space in SPL. + +config REGMAP + bool "Support register maps" + depends on DM + help + Hardware peripherals tend to have one or more sets of registers + which can be accessed to control the hardware. A register map + models this with a simple read/write interface. It can in principle + support any bus type (I2C, SPI) but so far this only supports + direct memory access. + +config SYSCON + bool "Support system controllers" + depends on REGMAP + help + Many SoCs have a number of system controllers which are dealt with + as a group by a single driver. Some common functionality is provided + by this uclass, including accessing registers via regmap and + assigning a unique number to each. diff --git a/drivers/core/Makefile b/drivers/core/Makefile index 5c2ead8..ce3027a 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -10,5 +10,5 @@ obj-$(CONFIG_OF_CONTROL) += simple-bus.o endif obj-$(CONFIG_DM_DEVICE_REMOVE) += device-remove.o obj-$(CONFIG_DM) += dump.o -obj-$(CONFIG_OF_CONTROL) += regmap.o -obj-$(CONFIG_OF_CONTROL) += syscon-uclass.o +obj-$(CONFIG_REGMAP) += regmap.o +obj-$(CONFIG_SYSCON) += syscon-uclass.o -- cgit v0.10.2 From 2e5350fee0f6d17306c851cbf1fbc9e010bd8478 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2015 20:53:35 -0600 Subject: dm: usb: Add an errno.h header to usb_ether.c This is required on some platforms, so add it. Signed-off-by: Simon Glass diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c index 63785a9..3c3e082 100644 --- a/drivers/usb/eth/usb_ether.c +++ b/drivers/usb/eth/usb_ether.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include -- cgit v0.10.2 From cc3e3a9e0eab3bc15be1766222fc2739f375252a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2015 20:53:36 -0600 Subject: dm: usb: Prepare dwc2 driver for driver-model conversion Put all global data in a structure and move (what will be) common code into common functions. This will make the driver-model conversion much easier. Signed-off-by: Simon Glass diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index ad35841..72ca682 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -21,18 +21,22 @@ #define DWC2_STATUS_BUF_SIZE 64 #define DWC2_DATA_BUF_SIZE (64 * 1024) -/* We need doubleword-aligned buffers for DMA transfers */ -DEFINE_ALIGN_BUFFER(uint8_t, aligned_buffer, DWC2_DATA_BUF_SIZE, 8); -DEFINE_ALIGN_BUFFER(uint8_t, status_buffer, DWC2_STATUS_BUF_SIZE, 8); - #define MAX_DEVICE 16 #define MAX_ENDPOINT 16 -static int bulk_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; -static int root_hub_devnum; +struct dwc2_priv { + uint8_t *aligned_buffer; + uint8_t *status_buffer; + int bulk_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; + struct dwc2_core_regs *regs; + int root_hub_devnum; +}; -static struct dwc2_core_regs *regs = - (struct dwc2_core_regs *)CONFIG_USB_DWC2_REG_ADDR; +/* We need doubleword-aligned buffers for DMA transfers */ +DEFINE_ALIGN_BUFFER(uint8_t, aligned_buffer_addr, DWC2_DATA_BUF_SIZE, 8); +DEFINE_ALIGN_BUFFER(uint8_t, status_buffer_addr, DWC2_STATUS_BUF_SIZE, 8); + +static struct dwc2_priv local; /* * DWC2 IP interface @@ -428,7 +432,8 @@ static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num, * DWC2 to USB API interface */ /* Direction: In ; Request: Status */ -static int dwc_otg_submit_rh_msg_in_status(struct usb_device *dev, void *buffer, +static int dwc_otg_submit_rh_msg_in_status(struct dwc2_core_regs *regs, + struct usb_device *dev, void *buffer, int txlen, struct devrequest *cmd) { uint32_t hprt0 = 0; @@ -602,13 +607,13 @@ static int dwc_otg_submit_rh_msg_in_configuration(struct usb_device *dev, } /* Direction: In */ -static int dwc_otg_submit_rh_msg_in(struct usb_device *dev, - void *buffer, int txlen, - struct devrequest *cmd) +static int dwc_otg_submit_rh_msg_in(struct dwc2_priv *priv, + struct usb_device *dev, void *buffer, + int txlen, struct devrequest *cmd) { switch (cmd->request) { case USB_REQ_GET_STATUS: - return dwc_otg_submit_rh_msg_in_status(dev, buffer, + return dwc_otg_submit_rh_msg_in_status(priv->regs, dev, buffer, txlen, cmd); case USB_REQ_GET_DESCRIPTOR: return dwc_otg_submit_rh_msg_in_descriptor(dev, buffer, @@ -623,10 +628,12 @@ static int dwc_otg_submit_rh_msg_in(struct usb_device *dev, } /* Direction: Out */ -static int dwc_otg_submit_rh_msg_out(struct usb_device *dev, - void *buffer, int txlen, - struct devrequest *cmd) +static int dwc_otg_submit_rh_msg_out(struct dwc2_priv *priv, + struct usb_device *dev, + void *buffer, int txlen, + struct devrequest *cmd) { + struct dwc2_core_regs *regs = priv->regs; int len = 0; int stat = 0; uint16_t bmrtype_breq = cmd->requesttype | (cmd->request << 8); @@ -673,7 +680,7 @@ static int dwc_otg_submit_rh_msg_out(struct usb_device *dev, } break; case (USB_REQ_SET_ADDRESS << 8): - root_hub_devnum = wValue; + priv->root_hub_devnum = wValue; break; case (USB_REQ_SET_CONFIGURATION << 8): break; @@ -690,8 +697,8 @@ static int dwc_otg_submit_rh_msg_out(struct usb_device *dev, return stat; } -static int dwc_otg_submit_rh_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int txlen, +static int dwc_otg_submit_rh_msg(struct dwc2_priv *priv, struct usb_device *dev, + unsigned long pipe, void *buffer, int txlen, struct devrequest *cmd) { int stat = 0; @@ -702,16 +709,17 @@ static int dwc_otg_submit_rh_msg(struct usb_device *dev, unsigned long pipe, } if (cmd->requesttype & USB_DIR_IN) - stat = dwc_otg_submit_rh_msg_in(dev, buffer, txlen, cmd); + stat = dwc_otg_submit_rh_msg_in(priv, dev, buffer, txlen, cmd); else - stat = dwc_otg_submit_rh_msg_out(dev, buffer, txlen, cmd); + stat = dwc_otg_submit_rh_msg_out(priv, dev, buffer, txlen, cmd); mdelay(1); return stat; } -int wait_for_chhltd(uint32_t *sub, int *toggle, bool ignore_ack) +int wait_for_chhltd(struct dwc2_core_regs *regs, uint32_t *sub, int *toggle, + bool ignore_ack) { uint32_t hcint_comp_hlt_ack = DWC2_HCINT_XFERCOMP | DWC2_HCINT_CHHLTD; struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; @@ -751,9 +759,11 @@ static int dwc2_eptype[] = { DWC2_HCCHAR_EPTYPE_BULK, }; -int chunk_msg(struct usb_device *dev, unsigned long pipe, int *pid, int in, - void *buffer, int len, bool ignore_ack) +int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev, + unsigned long pipe, int *pid, int in, void *buffer, int len, + bool ignore_ack) { + struct dwc2_core_regs *regs = priv->regs; struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; int devnum = usb_pipedevice(pipe); int ep = usb_pipeendpoint(pipe); @@ -802,10 +812,12 @@ int chunk_msg(struct usb_device *dev, unsigned long pipe, int *pid, int in, (*pid << DWC2_HCTSIZ_PID_OFFSET), &hc_regs->hctsiz); - if (!in) - memcpy(aligned_buffer, (char *)buffer + done, len); + if (!in) { + memcpy(priv->aligned_buffer, (char *)buffer + done, + len); + } - writel(phys_to_bus((unsigned long)aligned_buffer), + writel(phys_to_bus((unsigned long)priv->aligned_buffer), &hc_regs->hcdma); /* Set host channel enable after all other setup is complete. */ @@ -814,13 +826,13 @@ int chunk_msg(struct usb_device *dev, unsigned long pipe, int *pid, int in, (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | DWC2_HCCHAR_CHEN); - ret = wait_for_chhltd(&sub, pid, ignore_ack); + ret = wait_for_chhltd(regs, &sub, pid, ignore_ack); if (ret) break; if (in) { xfer_len -= sub; - memcpy(buffer + done, aligned_buffer, xfer_len); + memcpy(buffer + done, priv->aligned_buffer, xfer_len); if (sub) stop_transfer = 1; } @@ -839,43 +851,45 @@ int chunk_msg(struct usb_device *dev, unsigned long pipe, int *pid, int in, } /* U-Boot USB transmission interface */ -int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int len) +int _submit_bulk_msg(struct dwc2_priv *priv, struct usb_device *dev, + unsigned long pipe, void *buffer, int len) { int devnum = usb_pipedevice(pipe); int ep = usb_pipeendpoint(pipe); - if (devnum == root_hub_devnum) { + if (devnum == priv->root_hub_devnum) { dev->status = 0; return -EINVAL; } - return chunk_msg(dev, pipe, &bulk_data_toggle[devnum][ep], + return chunk_msg(priv, dev, pipe, &priv->bulk_data_toggle[devnum][ep], usb_pipein(pipe), buffer, len, true); } -int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int len, struct devrequest *setup) +static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev, + unsigned long pipe, void *buffer, int len, + struct devrequest *setup) { int devnum = usb_pipedevice(pipe); int pid, ret, act_len; /* For CONTROL endpoint pid should start with DATA1 */ int status_direction; - if (devnum == root_hub_devnum) { + if (devnum == priv->root_hub_devnum) { dev->status = 0; dev->speed = USB_SPEED_HIGH; - return dwc_otg_submit_rh_msg(dev, pipe, buffer, len, setup); + return dwc_otg_submit_rh_msg(priv, dev, pipe, buffer, len, + setup); } pid = DWC2_HC_PID_SETUP; - ret = chunk_msg(dev, pipe, &pid, 0, setup, 8, true); + ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8, true); if (ret) return ret; if (buffer) { pid = DWC2_HC_PID_DATA1; - ret = chunk_msg(dev, pipe, &pid, usb_pipein(pipe), buffer, + ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe), buffer, len, false); if (ret) return ret; @@ -891,8 +905,8 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, status_direction = 0; pid = DWC2_HC_PID_DATA1; - ret = chunk_msg(dev, pipe, &pid, status_direction, status_buffer, 0, - false); + ret = chunk_msg(priv, dev, pipe, &pid, status_direction, + priv->status_buffer, 0, false); if (ret) return ret; @@ -901,8 +915,8 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, return 0; } -int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int len, int interval) +int _submit_int_msg(struct dwc2_priv *priv, struct usb_device *dev, + unsigned long pipe, void *buffer, int len, int interval) { unsigned long timeout; int ret; @@ -915,24 +929,18 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, printf("Timeout poll on interrupt endpoint\n"); return -ETIMEDOUT; } - ret = submit_bulk_msg(dev, pipe, buffer, len); + ret = _submit_bulk_msg(priv, dev, pipe, buffer, len); if (ret != -EAGAIN) return ret; } } -/* U-Boot USB control interface */ -int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +static int dwc2_init_common(struct dwc2_priv *priv) { + struct dwc2_core_regs *regs = priv->regs; uint32_t snpsid; int i, j; - root_hub_devnum = 0; - - /* board dependant init */ - if (board_usb_init(index, USB_INIT_HOST)) - return -1; - snpsid = readl(®s->gsnpsid); printf("Core Release: %x.%03x\n", snpsid >> 12 & 0xf, snpsid & 0xfff); @@ -956,18 +964,60 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) for (i = 0; i < MAX_DEVICE; i++) { for (j = 0; j < MAX_ENDPOINT; j++) - bulk_data_toggle[i][j] = DWC2_HC_PID_DATA0; + priv->bulk_data_toggle[i][j] = DWC2_HC_PID_DATA0; } return 0; } -int usb_lowlevel_stop(int index) +static void dwc2_uninit_common(struct dwc2_core_regs *regs) { /* Put everything in reset. */ clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG, DWC2_HPRT0_PRTRST); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, struct devrequest *setup) +{ + return _submit_control_msg(&local, dev, pipe, buffer, len, setup); +} + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len) +{ + return _submit_bulk_msg(&local, dev, pipe, buffer, len); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, int interval) +{ + return _submit_int_msg(&local, dev, pipe, buffer, len, interval); +} + +/* U-Boot USB control interface */ +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ + struct dwc2_priv *priv = &local; + + memset(priv, '\0', sizeof(*priv)); + priv->root_hub_devnum = 0; + priv->regs = (struct dwc2_core_regs *)CONFIG_USB_DWC2_REG_ADDR; + priv->aligned_buffer = aligned_buffer_addr; + priv->status_buffer = status_buffer_addr; + + /* board-dependant init */ + if (board_usb_init(index, USB_INIT_HOST)) + return -1; + + return dwc2_init_common(priv); +} + +int usb_lowlevel_stop(int index) +{ + dwc2_uninit_common(local.regs); + return 0; } -- cgit v0.10.2 From f58a41e001c5ad5b9ae7e49b37b031f7880b5638 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2015 20:53:37 -0600 Subject: dm: usb: Add driver-model support to dwc2 Add driver model support to this driver so it can be used with the new USB stack. Signed-off-by: Simon Glass diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index 72ca682..702ef63 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -25,18 +26,25 @@ #define MAX_ENDPOINT 16 struct dwc2_priv { +#ifdef CONFIG_DM_USB + uint8_t aligned_buffer[DWC2_DATA_BUF_SIZE] __aligned(8); + uint8_t status_buffer[DWC2_STATUS_BUF_SIZE] __aligned(8); +#else uint8_t *aligned_buffer; uint8_t *status_buffer; +#endif int bulk_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; struct dwc2_core_regs *regs; int root_hub_devnum; }; +#ifndef CONFIG_DM_USB /* We need doubleword-aligned buffers for DMA transfers */ DEFINE_ALIGN_BUFFER(uint8_t, aligned_buffer_addr, DWC2_DATA_BUF_SIZE, 8); DEFINE_ALIGN_BUFFER(uint8_t, status_buffer_addr, DWC2_STATUS_BUF_SIZE, 8); static struct dwc2_priv local; +#endif /* * DWC2 IP interface @@ -979,6 +987,7 @@ static void dwc2_uninit_common(struct dwc2_core_regs *regs) DWC2_HPRT0_PRTRST); } +#ifndef CONFIG_DM_USB int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len, struct devrequest *setup) { @@ -1021,3 +1030,91 @@ int usb_lowlevel_stop(int index) return 0; } +#endif + +#ifdef CONFIG_DM_USB +static int dwc2_submit_control_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + struct devrequest *setup) +{ + struct dwc2_priv *priv = dev_get_priv(dev); + + debug("%s: dev='%s', udev=%p, udev->dev='%s', portnr=%d\n", __func__, + dev->name, udev, udev->dev->name, udev->portnr); + + return _submit_control_msg(priv, udev, pipe, buffer, length, setup); +} + +static int dwc2_submit_bulk_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length) +{ + struct dwc2_priv *priv = dev_get_priv(dev); + + debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); + + return _submit_bulk_msg(priv, udev, pipe, buffer, length); +} + +static int dwc2_submit_int_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + int interval) +{ + struct dwc2_priv *priv = dev_get_priv(dev); + + debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); + + return _submit_int_msg(priv, udev, pipe, buffer, length, interval); +} + +static int dwc2_usb_ofdata_to_platdata(struct udevice *dev) +{ + struct dwc2_priv *priv = dev_get_priv(dev); + fdt_addr_t addr; + + addr = dev_get_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + priv->regs = (struct dwc2_core_regs *)addr; + + return 0; +} + +static int dwc2_usb_probe(struct udevice *dev) +{ + struct dwc2_priv *priv = dev_get_priv(dev); + + return dwc2_init_common(priv); +} + +static int dwc2_usb_remove(struct udevice *dev) +{ + struct dwc2_priv *priv = dev_get_priv(dev); + + dwc2_uninit_common(priv->regs); + + return 0; +} + +struct dm_usb_ops dwc2_usb_ops = { + .control = dwc2_submit_control_msg, + .bulk = dwc2_submit_bulk_msg, + .interrupt = dwc2_submit_int_msg, +}; + +static const struct udevice_id dwc2_usb_ids[] = { + { .compatible = "brcm,bcm2835-usb" }, + { } +}; + +U_BOOT_DRIVER(usb_dwc2) = { + .name = "dwc2_exynos", + .id = UCLASS_USB, + .of_match = dwc2_usb_ids, + .ofdata_to_platdata = dwc2_usb_ofdata_to_platdata, + .probe = dwc2_usb_probe, + .remove = dwc2_usb_remove, + .ops = &dwc2_usb_ops, + .priv_auto_alloc_size = sizeof(struct dwc2_priv), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; +#endif -- cgit v0.10.2 From a26925913cb6b14182ab7bc31154079cd99206dd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2015 20:53:38 -0600 Subject: net: smsc95xx: Sort the include files Tidy up the include file order before adding more. Signed-off-by: Simon Glass diff --git a/drivers/usb/eth/smsc95xx.c b/drivers/usb/eth/smsc95xx.c index a7e50d6..8ebe4d6 100644 --- a/drivers/usb/eth/smsc95xx.c +++ b/drivers/usb/eth/smsc95xx.c @@ -6,12 +6,13 @@ * SPDX-License-Identifier: GPL-2.0+ */ -#include #include +#include +#include #include +#include #include #include "usb_ether.h" -#include /* SMSC LAN95xx based USB 2.0 Ethernet Devices */ -- cgit v0.10.2 From d62a1dc6bcfe9d210081d70669b39b4c77a07430 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2015 20:53:39 -0600 Subject: net: smsc95xx: Rename AX_RX_URB_SIZE to RX_URB_SIZE The AX_ prefix comes from the Asix driver. Since this is not that, we should avoid this confusing prefix. Signed-off-by: Simon Glass diff --git a/drivers/usb/eth/smsc95xx.c b/drivers/usb/eth/smsc95xx.c index 8ebe4d6..2d109fb 100644 --- a/drivers/usb/eth/smsc95xx.c +++ b/drivers/usb/eth/smsc95xx.c @@ -132,7 +132,7 @@ #define USB_BULK_SEND_TIMEOUT 5000 #define USB_BULK_RECV_TIMEOUT 5000 -#define AX_RX_URB_SIZE 2048 +#define RX_URB_SIZE 2048 #define PHY_CONNECT_TIMEOUT 5000 #define TURBO_MODE @@ -712,7 +712,7 @@ static int smsc95xx_send(struct eth_device *eth, void* packet, int length) static int smsc95xx_recv(struct eth_device *eth) { struct ueth_data *dev = (struct ueth_data *)eth->priv; - DEFINE_CACHE_ALIGN_BUFFER(unsigned char, recv_buf, AX_RX_URB_SIZE); + DEFINE_CACHE_ALIGN_BUFFER(unsigned char, recv_buf, RX_URB_SIZE); unsigned char *buf_ptr; int err; int actual_len; @@ -723,16 +723,16 @@ static int smsc95xx_recv(struct eth_device *eth) err = usb_bulk_msg(dev->pusb_dev, usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in), (void *)recv_buf, - AX_RX_URB_SIZE, + RX_URB_SIZE, &actual_len, USB_BULK_RECV_TIMEOUT); - debug("Rx: len = %u, actual = %u, err = %d\n", AX_RX_URB_SIZE, + debug("Rx: len = %u, actual = %u, err = %d\n", RX_URB_SIZE, actual_len, err); if (err != 0) { debug("Rx: failed to receive\n"); return -1; } - if (actual_len > AX_RX_URB_SIZE) { + if (actual_len > RX_URB_SIZE) { debug("Rx: received too many bytes %d\n", actual_len); return -1; } -- cgit v0.10.2 From 25a9e98038ca6e0335f760f58ef1fd418122ea13 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2015 20:53:40 -0600 Subject: net: smsc95xx: Correct the error numbers Instead of returning -1 on error, we should use a proper error number. Fix the code to conform to this. Signed-off-by: Simon Glass diff --git a/drivers/usb/eth/smsc95xx.c b/drivers/usb/eth/smsc95xx.c index 2d109fb..81ea0a3 100644 --- a/drivers/usb/eth/smsc95xx.c +++ b/drivers/usb/eth/smsc95xx.c @@ -165,7 +165,7 @@ static int smsc95xx_write_reg(struct ueth_data *dev, u32 index, u32 data) if (len != sizeof(data)) { debug("smsc95xx_write_reg failed: index=%d, data=%d, len=%d", index, data, len); - return -1; + return -EIO; } return 0; } @@ -183,7 +183,7 @@ static int smsc95xx_read_reg(struct ueth_data *dev, u32 index, u32 *data) if (len != sizeof(data)) { debug("smsc95xx_read_reg failed: index=%d, len=%d", index, len); - return -1; + return -EIO; } le32_to_cpus(data); @@ -202,7 +202,7 @@ static int smsc95xx_phy_wait_not_busy(struct ueth_data *dev) return 0; } while (get_timer(start_time) < 1 * 1000 * 1000); - return -1; + return -ETIMEDOUT; } static int smsc95xx_mdio_read(struct ueth_data *dev, int phy_id, int idx) @@ -212,7 +212,7 @@ static int smsc95xx_mdio_read(struct ueth_data *dev, int phy_id, int idx) /* confirm MII not busy */ if (smsc95xx_phy_wait_not_busy(dev)) { debug("MII is busy in smsc95xx_mdio_read\n"); - return -1; + return -ETIMEDOUT; } /* set the address, index & direction (read from PHY) */ @@ -221,7 +221,7 @@ static int smsc95xx_mdio_read(struct ueth_data *dev, int phy_id, int idx) if (smsc95xx_phy_wait_not_busy(dev)) { debug("Timed out reading MII reg %02X\n", idx); - return -1; + return -ETIMEDOUT; } smsc95xx_read_reg(dev, MII_DATA, &val); @@ -264,7 +264,7 @@ static int smsc95xx_eeprom_confirm_not_busy(struct ueth_data *dev) } while (get_timer(start_time) < 1 * 1000 * 1000); debug("EEPROM is busy\n"); - return -1; + return -ETIMEDOUT; } static int smsc95xx_wait_eeprom(struct ueth_data *dev) @@ -281,7 +281,7 @@ static int smsc95xx_wait_eeprom(struct ueth_data *dev) if (val & (E2P_CMD_TIMEOUT_ | E2P_CMD_BUSY_)) { debug("EEPROM read operation timeout\n"); - return -1; + return -ETIMEDOUT; } return 0; } @@ -367,7 +367,9 @@ static int smsc95xx_init_mac_address(struct eth_device *eth, * No eeprom, or eeprom values are invalid. Generating a random MAC * address is not safe. Just return an error. */ - return -1; + debug("Invalid MAC address read from EEPROM\n"); + + return -ENXIO; } static int smsc95xx_write_hwaddr(struct eth_device *eth) @@ -488,7 +490,7 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd) if (timeout >= 100) { debug("timeout waiting for completion of Lite Reset\n"); - return -1; + return -ETIMEDOUT; } write_buf = PM_CTL_PHY_RST_; @@ -506,16 +508,17 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd) } while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100)); if (timeout >= 100) { debug("timeout waiting for PHY Reset\n"); - return -1; + return -ETIMEDOUT; } if (!priv->have_hwaddr && smsc95xx_init_mac_address(eth, dev) == 0) priv->have_hwaddr = 1; if (!priv->have_hwaddr) { puts("Error: SMSC95xx: No MAC address set - set usbethaddr\n"); - return -1; + return -EADDRNOTAVAIL; } - if (smsc95xx_write_hwaddr(eth) < 0) - return -1; + ret = smsc95xx_write_hwaddr(eth); + if (ret < 0) + return ret; ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); if (ret < 0) @@ -636,8 +639,9 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd) } smsc95xx_set_multicast(dev); - if (smsc95xx_phy_initialize(dev) < 0) - return -1; + ret = smsc95xx_phy_initialize(dev); + if (ret < 0) + return ret; ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf); if (ret < 0) return ret; @@ -668,7 +672,7 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd) printf("done.\n"); } else { printf("unable to connect.\n"); - return -1; + return -EIO; } return 0; } @@ -685,7 +689,7 @@ static int smsc95xx_send(struct eth_device *eth, void* packet, int length) debug("** %s(), len %d, buf %#x\n", __func__, length, (int)msg); if (length > PKTSIZE) - return -1; + return -ENOSPC; tx_cmd_a = (u32)length | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_; tx_cmd_b = (u32)length; @@ -730,11 +734,11 @@ static int smsc95xx_recv(struct eth_device *eth) actual_len, err); if (err != 0) { debug("Rx: failed to receive\n"); - return -1; + return err; } if (actual_len > RX_URB_SIZE) { debug("Rx: received too many bytes %d\n", actual_len); - return -1; + return -ENOSPC; } buf_ptr = recv_buf; @@ -745,19 +749,19 @@ static int smsc95xx_recv(struct eth_device *eth) */ if (actual_len < sizeof(packet_len)) { debug("Rx: incomplete packet length\n"); - return -1; + return -EIO; } memcpy(&packet_len, buf_ptr, sizeof(packet_len)); le32_to_cpus(&packet_len); if (packet_len & RX_STS_ES_) { debug("Rx: Error header=%#x", packet_len); - return -1; + return -EIO; } packet_len = ((packet_len & RX_STS_FL_) >> 16); if (packet_len > actual_len - sizeof(packet_len)) { debug("Rx: too large packet: %d\n", packet_len); - return -1; + return -EIO; } /* Notify net stack */ -- cgit v0.10.2 From 527298c4b51963c7f3ed072d26fcb3e1bcafd092 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2015 20:53:41 -0600 Subject: net: smsc95xx: Prepare for conversion to driver model At present struct eth_device is passed around all over the place. This does not exist with driver model. Add explicit arguments instead, so that with driver model we can pass the correct things. Signed-off-by: Simon Glass diff --git a/drivers/usb/eth/smsc95xx.c b/drivers/usb/eth/smsc95xx.c index 81ea0a3..b0c610d 100644 --- a/drivers/usb/eth/smsc95xx.c +++ b/drivers/usb/eth/smsc95xx.c @@ -150,7 +150,7 @@ struct smsc95xx_private { /* * Smsc95xx infrastructure commands */ -static int smsc95xx_write_reg(struct ueth_data *dev, u32 index, u32 data) +static int smsc95xx_write_reg(struct usb_device *udev, u32 index, u32 data) { int len; ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1); @@ -158,10 +158,11 @@ static int smsc95xx_write_reg(struct ueth_data *dev, u32 index, u32 data) cpu_to_le32s(&data); tmpbuf[0] = data; - len = usb_control_msg(dev->pusb_dev, usb_sndctrlpipe(dev->pusb_dev, 0), - USB_VENDOR_REQUEST_WRITE_REGISTER, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 00, index, tmpbuf, sizeof(data), USB_CTRL_SET_TIMEOUT); + len = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_VENDOR_REQUEST_WRITE_REGISTER, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, tmpbuf, sizeof(data), + USB_CTRL_SET_TIMEOUT); if (len != sizeof(data)) { debug("smsc95xx_write_reg failed: index=%d, data=%d, len=%d", index, data, len); @@ -170,15 +171,16 @@ static int smsc95xx_write_reg(struct ueth_data *dev, u32 index, u32 data) return 0; } -static int smsc95xx_read_reg(struct ueth_data *dev, u32 index, u32 *data) +static int smsc95xx_read_reg(struct usb_device *udev, u32 index, u32 *data) { int len; ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1); - len = usb_control_msg(dev->pusb_dev, usb_rcvctrlpipe(dev->pusb_dev, 0), - USB_VENDOR_REQUEST_READ_REGISTER, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 00, index, tmpbuf, sizeof(data), USB_CTRL_GET_TIMEOUT); + len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + USB_VENDOR_REQUEST_READ_REGISTER, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, tmpbuf, sizeof(data), + USB_CTRL_GET_TIMEOUT); *data = tmpbuf[0]; if (len != sizeof(data)) { debug("smsc95xx_read_reg failed: index=%d, len=%d", @@ -191,73 +193,73 @@ static int smsc95xx_read_reg(struct ueth_data *dev, u32 index, u32 *data) } /* Loop until the read is completed with timeout */ -static int smsc95xx_phy_wait_not_busy(struct ueth_data *dev) +static int smsc95xx_phy_wait_not_busy(struct usb_device *udev) { unsigned long start_time = get_timer(0); u32 val; do { - smsc95xx_read_reg(dev, MII_ADDR, &val); + smsc95xx_read_reg(udev, MII_ADDR, &val); if (!(val & MII_BUSY_)) return 0; - } while (get_timer(start_time) < 1 * 1000 * 1000); + } while (get_timer(start_time) < 1000); return -ETIMEDOUT; } -static int smsc95xx_mdio_read(struct ueth_data *dev, int phy_id, int idx) +static int smsc95xx_mdio_read(struct usb_device *udev, int phy_id, int idx) { u32 val, addr; /* confirm MII not busy */ - if (smsc95xx_phy_wait_not_busy(dev)) { + if (smsc95xx_phy_wait_not_busy(udev)) { debug("MII is busy in smsc95xx_mdio_read\n"); return -ETIMEDOUT; } /* set the address, index & direction (read from PHY) */ addr = (phy_id << 11) | (idx << 6) | MII_READ_; - smsc95xx_write_reg(dev, MII_ADDR, addr); + smsc95xx_write_reg(udev, MII_ADDR, addr); - if (smsc95xx_phy_wait_not_busy(dev)) { + if (smsc95xx_phy_wait_not_busy(udev)) { debug("Timed out reading MII reg %02X\n", idx); return -ETIMEDOUT; } - smsc95xx_read_reg(dev, MII_DATA, &val); + smsc95xx_read_reg(udev, MII_DATA, &val); return (u16)(val & 0xFFFF); } -static void smsc95xx_mdio_write(struct ueth_data *dev, int phy_id, int idx, +static void smsc95xx_mdio_write(struct usb_device *udev, int phy_id, int idx, int regval) { u32 val, addr; /* confirm MII not busy */ - if (smsc95xx_phy_wait_not_busy(dev)) { + if (smsc95xx_phy_wait_not_busy(udev)) { debug("MII is busy in smsc95xx_mdio_write\n"); return; } val = regval; - smsc95xx_write_reg(dev, MII_DATA, val); + smsc95xx_write_reg(udev, MII_DATA, val); /* set the address, index & direction (write to PHY) */ addr = (phy_id << 11) | (idx << 6) | MII_WRITE_; - smsc95xx_write_reg(dev, MII_ADDR, addr); + smsc95xx_write_reg(udev, MII_ADDR, addr); - if (smsc95xx_phy_wait_not_busy(dev)) + if (smsc95xx_phy_wait_not_busy(udev)) debug("Timed out writing MII reg %02X\n", idx); } -static int smsc95xx_eeprom_confirm_not_busy(struct ueth_data *dev) +static int smsc95xx_eeprom_confirm_not_busy(struct usb_device *udev) { unsigned long start_time = get_timer(0); u32 val; do { - smsc95xx_read_reg(dev, E2P_CMD, &val); + smsc95xx_read_reg(udev, E2P_CMD, &val); if (!(val & E2P_CMD_BUSY_)) return 0; udelay(40); @@ -267,13 +269,13 @@ static int smsc95xx_eeprom_confirm_not_busy(struct ueth_data *dev) return -ETIMEDOUT; } -static int smsc95xx_wait_eeprom(struct ueth_data *dev) +static int smsc95xx_wait_eeprom(struct usb_device *udev) { unsigned long start_time = get_timer(0); u32 val; do { - smsc95xx_read_reg(dev, E2P_CMD, &val); + smsc95xx_read_reg(udev, E2P_CMD, &val); if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_)) break; udelay(40); @@ -286,25 +288,25 @@ static int smsc95xx_wait_eeprom(struct ueth_data *dev) return 0; } -static int smsc95xx_read_eeprom(struct ueth_data *dev, u32 offset, u32 length, +static int smsc95xx_read_eeprom(struct usb_device *udev, u32 offset, u32 length, u8 *data) { u32 val; int i, ret; - ret = smsc95xx_eeprom_confirm_not_busy(dev); + ret = smsc95xx_eeprom_confirm_not_busy(udev); if (ret) return ret; for (i = 0; i < length; i++) { val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_); - smsc95xx_write_reg(dev, E2P_CMD, val); + smsc95xx_write_reg(udev, E2P_CMD, val); - ret = smsc95xx_wait_eeprom(dev); + ret = smsc95xx_wait_eeprom(udev); if (ret < 0) return ret; - smsc95xx_read_reg(dev, E2P_DATA, &val); + smsc95xx_read_reg(udev, E2P_DATA, &val); data[i] = val & 0xFF; offset++; } @@ -316,51 +318,55 @@ static int smsc95xx_read_eeprom(struct ueth_data *dev, u32 offset, u32 length, * * Returns 0 on success, negative on error. */ -static int mii_nway_restart(struct ueth_data *dev) +static int mii_nway_restart(struct usb_device *udev, struct ueth_data *dev) { int bmcr; int r = -1; /* if autoneg is off, it's an error */ - bmcr = smsc95xx_mdio_read(dev, dev->phy_id, MII_BMCR); + bmcr = smsc95xx_mdio_read(udev, dev->phy_id, MII_BMCR); if (bmcr & BMCR_ANENABLE) { bmcr |= BMCR_ANRESTART; - smsc95xx_mdio_write(dev, dev->phy_id, MII_BMCR, bmcr); + smsc95xx_mdio_write(udev, dev->phy_id, MII_BMCR, bmcr); r = 0; } return r; } -static int smsc95xx_phy_initialize(struct ueth_data *dev) +static int smsc95xx_phy_initialize(struct usb_device *udev, + struct ueth_data *dev) { - smsc95xx_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET); - smsc95xx_mdio_write(dev, dev->phy_id, MII_ADVERTISE, - ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | - ADVERTISE_PAUSE_ASYM); + smsc95xx_mdio_write(udev, dev->phy_id, MII_BMCR, BMCR_RESET); + smsc95xx_mdio_write(udev, dev->phy_id, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_CSMA | + ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); /* read to clear */ - smsc95xx_mdio_read(dev, dev->phy_id, PHY_INT_SRC); + smsc95xx_mdio_read(udev, dev->phy_id, PHY_INT_SRC); - smsc95xx_mdio_write(dev, dev->phy_id, PHY_INT_MASK, - PHY_INT_MASK_DEFAULT_); - mii_nway_restart(dev); + smsc95xx_mdio_write(udev, dev->phy_id, PHY_INT_MASK, + PHY_INT_MASK_DEFAULT_); + mii_nway_restart(udev, dev); debug("phy initialised succesfully\n"); return 0; } -static int smsc95xx_init_mac_address(struct eth_device *eth, - struct ueth_data *dev) +static int smsc95xx_init_mac_address(unsigned char *enetaddr, + struct usb_device *udev) { + int ret; + /* try reading mac address from EEPROM */ - if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, - eth->enetaddr) == 0) { - if (is_valid_ethaddr(eth->enetaddr)) { - /* eeprom values are valid so use them */ - debug("MAC address read from EEPROM\n"); - return 0; - } + ret = smsc95xx_read_eeprom(udev, EEPROM_MAC_OFFSET, ETH_ALEN, enetaddr); + if (ret) + return ret; + + if (is_valid_ethaddr(enetaddr)) { + /* eeprom values are valid so use them */ + debug("MAC address read from EEPROM\n"); + return 0; } /* @@ -372,35 +378,36 @@ static int smsc95xx_init_mac_address(struct eth_device *eth, return -ENXIO; } -static int smsc95xx_write_hwaddr(struct eth_device *eth) +static int smsc95xx_write_hwaddr_common(struct usb_device *udev, + struct smsc95xx_private *priv, + unsigned char *enetaddr) { - struct ueth_data *dev = (struct ueth_data *)eth->priv; - struct smsc95xx_private *priv = dev->dev_priv; - u32 addr_lo = __get_unaligned_le32(ð->enetaddr[0]); - u32 addr_hi = __get_unaligned_le16(ð->enetaddr[4]); + u32 addr_lo = __get_unaligned_le32(&enetaddr[0]); + u32 addr_hi = __get_unaligned_le16(&enetaddr[4]); int ret; /* set hardware address */ debug("** %s()\n", __func__); - ret = smsc95xx_write_reg(dev, ADDRL, addr_lo); + ret = smsc95xx_write_reg(udev, ADDRL, addr_lo); if (ret < 0) return ret; - ret = smsc95xx_write_reg(dev, ADDRH, addr_hi); + ret = smsc95xx_write_reg(udev, ADDRH, addr_hi); if (ret < 0) return ret; - debug("MAC %pM\n", eth->enetaddr); + debug("MAC %pM\n", enetaddr); priv->have_hwaddr = 1; + return 0; } /* Enable or disable Tx & Rx checksum offload engines */ -static int smsc95xx_set_csums(struct ueth_data *dev, - int use_tx_csum, int use_rx_csum) +static int smsc95xx_set_csums(struct usb_device *udev, int use_tx_csum, + int use_rx_csum) { u32 read_buf; - int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf); + int ret = smsc95xx_read_reg(udev, COE_CR, &read_buf); if (ret < 0) return ret; @@ -414,7 +421,7 @@ static int smsc95xx_set_csums(struct ueth_data *dev, else read_buf &= ~Rx_COE_EN_; - ret = smsc95xx_write_reg(dev, COE_CR, read_buf); + ret = smsc95xx_write_reg(udev, COE_CR, read_buf); if (ret < 0) return ret; @@ -422,52 +429,45 @@ static int smsc95xx_set_csums(struct ueth_data *dev, return 0; } -static void smsc95xx_set_multicast(struct ueth_data *dev) +static void smsc95xx_set_multicast(struct smsc95xx_private *priv) { - struct smsc95xx_private *priv = dev->dev_priv; - /* No multicast in u-boot */ priv->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_); } /* starts the TX path */ -static void smsc95xx_start_tx_path(struct ueth_data *dev) +static void smsc95xx_start_tx_path(struct usb_device *udev, + struct smsc95xx_private *priv) { - struct smsc95xx_private *priv = dev->dev_priv; u32 reg_val; /* Enable Tx at MAC */ priv->mac_cr |= MAC_CR_TXEN_; - smsc95xx_write_reg(dev, MAC_CR, priv->mac_cr); + smsc95xx_write_reg(udev, MAC_CR, priv->mac_cr); /* Enable Tx at SCSRs */ reg_val = TX_CFG_ON_; - smsc95xx_write_reg(dev, TX_CFG, reg_val); + smsc95xx_write_reg(udev, TX_CFG, reg_val); } /* Starts the Receive path */ -static void smsc95xx_start_rx_path(struct ueth_data *dev) +static void smsc95xx_start_rx_path(struct usb_device *udev, + struct smsc95xx_private *priv) { - struct smsc95xx_private *priv = dev->dev_priv; - priv->mac_cr |= MAC_CR_RXEN_; - smsc95xx_write_reg(dev, MAC_CR, priv->mac_cr); + smsc95xx_write_reg(udev, MAC_CR, priv->mac_cr); } -/* - * Smsc95xx callbacks - */ -static int smsc95xx_init(struct eth_device *eth, bd_t *bd) +static int smsc95xx_init_common(struct usb_device *udev, struct ueth_data *dev, + struct smsc95xx_private *priv, + unsigned char *enetaddr) { int ret; u32 write_buf; u32 read_buf; u32 burst_cap; int timeout; - struct ueth_data *dev = (struct ueth_data *)eth->priv; - struct smsc95xx_private *priv = - (struct smsc95xx_private *)dev->dev_priv; #define TIMEOUT_RESOLUTION 50 /* ms */ int link_detected; @@ -475,13 +475,13 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd) dev->phy_id = SMSC95XX_INTERNAL_PHY_ID; /* fixed phy id */ write_buf = HW_CFG_LRST_; - ret = smsc95xx_write_reg(dev, HW_CFG, write_buf); + ret = smsc95xx_write_reg(udev, HW_CFG, write_buf); if (ret < 0) return ret; timeout = 0; do { - ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf); if (ret < 0) return ret; udelay(10 * 1000); @@ -494,13 +494,13 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd) } write_buf = PM_CTL_PHY_RST_; - ret = smsc95xx_write_reg(dev, PM_CTRL, write_buf); + ret = smsc95xx_write_reg(udev, PM_CTRL, write_buf); if (ret < 0) return ret; timeout = 0; do { - ret = smsc95xx_read_reg(dev, PM_CTRL, &read_buf); + ret = smsc95xx_read_reg(udev, PM_CTRL, &read_buf); if (ret < 0) return ret; udelay(10 * 1000); @@ -510,27 +510,28 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd) debug("timeout waiting for PHY Reset\n"); return -ETIMEDOUT; } - if (!priv->have_hwaddr && smsc95xx_init_mac_address(eth, dev) == 0) + if (!priv->have_hwaddr && smsc95xx_init_mac_address(enetaddr, udev) == + 0) priv->have_hwaddr = 1; if (!priv->have_hwaddr) { puts("Error: SMSC95xx: No MAC address set - set usbethaddr\n"); return -EADDRNOTAVAIL; } - ret = smsc95xx_write_hwaddr(eth); + ret = smsc95xx_write_hwaddr_common(udev, priv, enetaddr); if (ret < 0) return ret; - ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf); if (ret < 0) return ret; debug("Read Value from HW_CFG : 0x%08x\n", read_buf); read_buf |= HW_CFG_BIR_; - ret = smsc95xx_write_reg(dev, HW_CFG, read_buf); + ret = smsc95xx_write_reg(udev, HW_CFG, read_buf); if (ret < 0) return ret; - ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf); if (ret < 0) return ret; debug("Read Value from HW_CFG after writing " @@ -550,27 +551,27 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd) #endif debug("rx_urb_size=%ld\n", (ulong)priv->rx_urb_size); - ret = smsc95xx_write_reg(dev, BURST_CAP, burst_cap); + ret = smsc95xx_write_reg(udev, BURST_CAP, burst_cap); if (ret < 0) return ret; - ret = smsc95xx_read_reg(dev, BURST_CAP, &read_buf); + ret = smsc95xx_read_reg(udev, BURST_CAP, &read_buf); if (ret < 0) return ret; debug("Read Value from BURST_CAP after writing: 0x%08x\n", read_buf); read_buf = DEFAULT_BULK_IN_DELAY; - ret = smsc95xx_write_reg(dev, BULK_IN_DLY, read_buf); + ret = smsc95xx_write_reg(udev, BULK_IN_DLY, read_buf); if (ret < 0) return ret; - ret = smsc95xx_read_reg(dev, BULK_IN_DLY, &read_buf); + ret = smsc95xx_read_reg(udev, BULK_IN_DLY, &read_buf); if (ret < 0) return ret; debug("Read Value from BULK_IN_DLY after writing: " "0x%08x\n", read_buf); - ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf); if (ret < 0) return ret; debug("Read Value from HW_CFG: 0x%08x\n", read_buf); @@ -583,21 +584,21 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd) #define NET_IP_ALIGN 0 read_buf |= NET_IP_ALIGN << 9; - ret = smsc95xx_write_reg(dev, HW_CFG, read_buf); + ret = smsc95xx_write_reg(udev, HW_CFG, read_buf); if (ret < 0) return ret; - ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf); if (ret < 0) return ret; debug("Read Value from HW_CFG after writing: 0x%08x\n", read_buf); write_buf = 0xFFFFFFFF; - ret = smsc95xx_write_reg(dev, INT_STS, write_buf); + ret = smsc95xx_write_reg(udev, INT_STS, write_buf); if (ret < 0) return ret; - ret = smsc95xx_read_reg(dev, ID_REV, &read_buf); + ret = smsc95xx_read_reg(udev, ID_REV, &read_buf); if (ret < 0) return ret; debug("ID_REV = 0x%08x\n", read_buf); @@ -605,60 +606,60 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd) /* Configure GPIO pins as LED outputs */ write_buf = LED_GPIO_CFG_SPD_LED | LED_GPIO_CFG_LNK_LED | LED_GPIO_CFG_FDX_LED; - ret = smsc95xx_write_reg(dev, LED_GPIO_CFG, write_buf); + ret = smsc95xx_write_reg(udev, LED_GPIO_CFG, write_buf); if (ret < 0) return ret; debug("LED_GPIO_CFG set\n"); /* Init Tx */ write_buf = 0; - ret = smsc95xx_write_reg(dev, FLOW, write_buf); + ret = smsc95xx_write_reg(udev, FLOW, write_buf); if (ret < 0) return ret; read_buf = AFC_CFG_DEFAULT; - ret = smsc95xx_write_reg(dev, AFC_CFG, read_buf); + ret = smsc95xx_write_reg(udev, AFC_CFG, read_buf); if (ret < 0) return ret; - ret = smsc95xx_read_reg(dev, MAC_CR, &priv->mac_cr); + ret = smsc95xx_read_reg(udev, MAC_CR, &priv->mac_cr); if (ret < 0) return ret; /* Init Rx. Set Vlan */ write_buf = (u32)ETH_P_8021Q; - ret = smsc95xx_write_reg(dev, VLAN1, write_buf); + ret = smsc95xx_write_reg(udev, VLAN1, write_buf); if (ret < 0) return ret; /* Disable checksum offload engines */ - ret = smsc95xx_set_csums(dev, 0, 0); + ret = smsc95xx_set_csums(udev, 0, 0); if (ret < 0) { debug("Failed to set csum offload: %d\n", ret); return ret; } - smsc95xx_set_multicast(dev); + smsc95xx_set_multicast(priv); - ret = smsc95xx_phy_initialize(dev); + ret = smsc95xx_phy_initialize(udev, dev); if (ret < 0) return ret; - ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf); + ret = smsc95xx_read_reg(udev, INT_EP_CTL, &read_buf); if (ret < 0) return ret; /* enable PHY interrupts */ read_buf |= INT_EP_CTL_PHY_INT_; - ret = smsc95xx_write_reg(dev, INT_EP_CTL, read_buf); + ret = smsc95xx_write_reg(udev, INT_EP_CTL, read_buf); if (ret < 0) return ret; - smsc95xx_start_tx_path(dev); - smsc95xx_start_rx_path(dev); + smsc95xx_start_tx_path(udev, priv); + smsc95xx_start_rx_path(udev, priv); timeout = 0; do { - link_detected = smsc95xx_mdio_read(dev, dev->phy_id, MII_BMSR) + link_detected = smsc95xx_mdio_read(udev, dev->phy_id, MII_BMSR) & BMSR_LSTATUS; if (!link_detected) { if (timeout == 0) @@ -677,9 +678,8 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd) return 0; } -static int smsc95xx_send(struct eth_device *eth, void* packet, int length) +static int smsc95xx_send_common(struct ueth_data *dev, void *packet, int length) { - struct ueth_data *dev = (struct ueth_data *)eth->priv; int err; int actual_len; u32 tx_cmd_a; @@ -710,9 +710,30 @@ static int smsc95xx_send(struct eth_device *eth, void* packet, int length) debug("Tx: len = %u, actual = %u, err = %d\n", length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b), actual_len, err); + return err; } +/* + * Smsc95xx callbacks + */ +static int smsc95xx_init(struct eth_device *eth, bd_t *bd) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct usb_device *udev = dev->pusb_dev; + struct smsc95xx_private *priv = + (struct smsc95xx_private *)dev->dev_priv; + + return smsc95xx_init_common(udev, dev, priv, eth->enetaddr); +} + +static int smsc95xx_send(struct eth_device *eth, void *packet, int length) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + + return smsc95xx_send_common(dev, packet, length); +} + static int smsc95xx_recv(struct eth_device *eth) { struct ueth_data *dev = (struct ueth_data *)eth->priv; @@ -725,16 +746,14 @@ static int smsc95xx_recv(struct eth_device *eth) debug("** %s()\n", __func__); err = usb_bulk_msg(dev->pusb_dev, - usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in), - (void *)recv_buf, - RX_URB_SIZE, - &actual_len, - USB_BULK_RECV_TIMEOUT); + usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in), + (void *)recv_buf, RX_URB_SIZE, &actual_len, + USB_BULK_RECV_TIMEOUT); debug("Rx: len = %u, actual = %u, err = %d\n", RX_URB_SIZE, actual_len, err); if (err != 0) { debug("Rx: failed to receive\n"); - return err; + return -err; } if (actual_len > RX_URB_SIZE) { debug("Rx: received too many bytes %d\n", actual_len); @@ -788,6 +807,15 @@ static void smsc95xx_halt(struct eth_device *eth) debug("** %s()\n", __func__); } +static int smsc95xx_write_hwaddr(struct eth_device *eth) +{ + struct ueth_data *dev = eth->priv; + struct usb_device *udev = dev->pusb_dev; + struct smsc95xx_private *priv = dev->dev_priv; + + return smsc95xx_write_hwaddr_common(udev, priv, eth->enetaddr); +} + /* * SMSC probing functions */ -- cgit v0.10.2 From 0990fcb772192af615f23cd78bbfc081ec40236b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2015 20:53:42 -0600 Subject: net: smsc95xx: Add driver-model support Add support for driver model, so that CONFIG_DM_ETH can be defined and used with this driver. Signed-off-by: Simon Glass diff --git a/drivers/usb/eth/smsc95xx.c b/drivers/usb/eth/smsc95xx.c index b0c610d..1dcd088 100644 --- a/drivers/usb/eth/smsc95xx.c +++ b/drivers/usb/eth/smsc95xx.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2015 Google, Inc * Copyright (c) 2011 The Chromium OS Authors. * Copyright (C) 2009 NVIDIA, Corporation * Copyright (C) 2007-2008 SMSC (Steve Glendinning) @@ -7,6 +8,7 @@ */ #include +#include #include #include #include @@ -137,11 +139,16 @@ #define TURBO_MODE +#ifndef CONFIG_DM_ETH /* local vars */ static int curr_eth_dev; /* index for name of next device detected */ +#endif /* driver private */ struct smsc95xx_private { +#ifdef CONFIG_DM_ETH + struct ueth_data ueth; +#endif size_t rx_urb_size; /* maximum USB URB size */ u32 mac_cr; /* MAC control register value */ int have_hwaddr; /* 1 if we have a hardware MAC address */ @@ -714,6 +721,7 @@ static int smsc95xx_send_common(struct ueth_data *dev, void *packet, int length) return err; } +#ifndef CONFIG_DM_ETH /* * Smsc95xx callbacks */ @@ -931,3 +939,137 @@ int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss, eth->priv = ss; return 1; } +#endif /* !CONFIG_DM_ETH */ + +#ifdef CONFIG_DM_ETH +static int smsc95xx_eth_start(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parentdata(dev); + struct smsc95xx_private *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + + /* Driver-model Ethernet ensures we have this */ + priv->have_hwaddr = 1; + + return smsc95xx_init_common(udev, &priv->ueth, priv, pdata->enetaddr); +} + +void smsc95xx_eth_stop(struct udevice *dev) +{ + debug("** %s()\n", __func__); +} + +int smsc95xx_eth_send(struct udevice *dev, void *packet, int length) +{ + struct smsc95xx_private *priv = dev_get_priv(dev); + + return smsc95xx_send_common(&priv->ueth, packet, length); +} + +int smsc95xx_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct smsc95xx_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + uint8_t *ptr; + int ret, len; + u32 packet_len; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: first try, len=%d\n", __func__, len); + if (!len) { + if (!(flags & ETH_RECV_CHECK_DEVICE)) + return -EAGAIN; + ret = usb_ether_receive(ueth, RX_URB_SIZE); + if (ret == -EAGAIN) + return ret; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: second try, len=%d\n", __func__, len); + } + + /* + * 1st 4 bytes contain the length of the actual data plus error info. + * Extract data length. + */ + if (len < sizeof(packet_len)) { + debug("Rx: incomplete packet length\n"); + goto err; + } + memcpy(&packet_len, ptr, sizeof(packet_len)); + le32_to_cpus(&packet_len); + if (packet_len & RX_STS_ES_) { + debug("Rx: Error header=%#x", packet_len); + goto err; + } + packet_len = ((packet_len & RX_STS_FL_) >> 16); + + if (packet_len > len - sizeof(packet_len)) { + debug("Rx: too large packet: %d\n", packet_len); + goto err; + } + + *packetp = ptr + sizeof(packet_len); + return packet_len; + +err: + usb_ether_advance_rxbuf(ueth, -1); + return -EINVAL; +} + +static int smsc95xx_free_pkt(struct udevice *dev, uchar *packet, int packet_len) +{ + struct smsc95xx_private *priv = dev_get_priv(dev); + + packet_len = ALIGN(packet_len, 4); + usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len); + + return 0; +} + +int smsc95xx_write_hwaddr(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parentdata(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + struct smsc95xx_private *priv = dev_get_priv(dev); + + return smsc95xx_write_hwaddr_common(udev, priv, pdata->enetaddr); +} + +static int smsc95xx_eth_probe(struct udevice *dev) +{ + struct smsc95xx_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + + return usb_ether_register(dev, ueth, RX_URB_SIZE); +} + +static const struct eth_ops smsc95xx_eth_ops = { + .start = smsc95xx_eth_start, + .send = smsc95xx_eth_send, + .recv = smsc95xx_eth_recv, + .free_pkt = smsc95xx_free_pkt, + .stop = smsc95xx_eth_stop, + .write_hwaddr = smsc95xx_write_hwaddr, +}; + +U_BOOT_DRIVER(smsc95xx_eth) = { + .name = "smsc95xx_eth", + .id = UCLASS_ETH, + .probe = smsc95xx_eth_probe, + .ops = &smsc95xx_eth_ops, + .priv_auto_alloc_size = sizeof(struct smsc95xx_private), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; + +static const struct usb_device_id smsc95xx_eth_id_table[] = { + { USB_DEVICE(0x05ac, 0x1402) }, + { USB_DEVICE(0x0424, 0xec00) }, /* LAN9512/LAN9514 Ethernet */ + { USB_DEVICE(0x0424, 0x9500) }, /* LAN9500 Ethernet */ + { USB_DEVICE(0x0424, 0x9730) }, /* LAN9730 Ethernet (HSIC) */ + { USB_DEVICE(0x0424, 0x9900) }, /* SMSC9500 USB Ethernet (SAL10) */ + { USB_DEVICE(0x0424, 0x9e00) }, /* LAN9500A Ethernet */ + { } /* Terminating entry */ +}; + +U_BOOT_USB_DEVICE(smsc95xx_eth, smsc95xx_eth_id_table); +#endif -- cgit v0.10.2 From f33017716e5c430d84366ecc4476ba2b655f3fef Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2015 20:53:44 -0600 Subject: dm: Support address translation for simple-bus The 'ranges' property can be used to specify a translation from the system address to the bus address. Add support for this using the dev_get_addr() function, which devices should use to find their address. Signed-off-by: Simon Glass diff --git a/drivers/core/device.c b/drivers/core/device.c index 51b1b44..d65717d 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -552,17 +552,22 @@ const char *dev_get_uclass_name(struct udevice *dev) return dev->uclass->uc_drv->name; } -#ifdef CONFIG_OF_CONTROL fdt_addr_t dev_get_addr(struct udevice *dev) { - return fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); -} +#ifdef CONFIG_OF_CONTROL + fdt_addr_t addr; + + addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); + if (addr != FDT_ADDR_T_NONE) { + if (device_get_uclass_id(dev->parent) == UCLASS_SIMPLE_BUS) + addr = simple_bus_translate(dev->parent, addr); + } + + return addr; #else -fdt_addr_t dev_get_addr(struct udevice *dev) -{ return FDT_ADDR_T_NONE; -} #endif +} bool device_has_children(struct udevice *dev) { diff --git a/drivers/core/simple-bus.c b/drivers/core/simple-bus.c index 3ea4d82..913c3cc 100644 --- a/drivers/core/simple-bus.c +++ b/drivers/core/simple-bus.c @@ -10,8 +10,37 @@ DECLARE_GLOBAL_DATA_PTR; +struct simple_bus_plat { + u32 base; + u32 size; + u32 target; +}; + +fdt_addr_t simple_bus_translate(struct udevice *dev, fdt_addr_t addr) +{ + struct simple_bus_plat *plat = dev_get_uclass_platdata(dev); + + if (addr >= plat->base && addr < plat->base + plat->size) + addr = (addr - plat->base) + plat->target; + + return addr; +} + static int simple_bus_post_bind(struct udevice *dev) { + u32 cell[3]; + int ret; + + ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, "ranges", + cell, ARRAY_SIZE(cell)); + if (!ret) { + struct simple_bus_plat *plat = dev_get_uclass_platdata(dev); + + plat->base = cell[0]; + plat->target = cell[1]; + plat->size = cell[2]; + } + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); } @@ -19,6 +48,7 @@ UCLASS_DRIVER(simple_bus) = { .id = UCLASS_SIMPLE_BUS, .name = "simple_bus", .post_bind = simple_bus_post_bind, + .per_device_platdata_auto_alloc_size = sizeof(struct simple_bus_plat), }; static const struct udevice_id generic_simple_bus_ids[] = { diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h index 402304f..1a9ba01 100644 --- a/include/dm/device-internal.h +++ b/include/dm/device-internal.h @@ -139,6 +139,18 @@ void device_free(struct udevice *dev); static inline void device_free(struct udevice *dev) {} #endif +/** + * simple_bus_translate() - translate a bus address to a system address + * + * This handles the 'ranges' property in a simple bus. It translates the + * device address @addr to a system address using this property. + * + * @dev: Simple bus device (parent of target device) + * @addr: Address to translate + * @return new address + */ +fdt_addr_t simple_bus_translate(struct udevice *dev, fdt_addr_t addr); + /* Cast away any volatile pointer */ #define DM_ROOT_NON_CONST (((gd_t *)gd)->dm_root) #define DM_UCLASS_ROOT_NON_CONST (((gd_t *)gd)->uclass_root) -- cgit v0.10.2 From aed1a4dd88e94001b811b297c1ff734c3f8d22d9 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 25 Jul 2015 21:52:34 +0900 Subject: dm: add DM_FLAG_BOUND flag Currently, we only have DM_FLAG_ACTIVATED to indicate the device status, but we still cannot know in which stage is in progress, binding or probing. This commit introduces a new flag, DM_FLAG_BOUND, which is set when the device is really bound, and cleared when it is unbound. Signed-off-by: Masahiro Yamada Acked-by: Simon Glass diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 6b87f86..45d6543 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -61,6 +61,9 @@ int device_unbind(struct udevice *dev) if (dev->flags & DM_FLAG_ACTIVATED) return -EINVAL; + if (!(dev->flags & DM_FLAG_BOUND)) + return -EINVAL; + drv = dev->driver; assert(drv); diff --git a/drivers/core/device.c b/drivers/core/device.c index d65717d..bf6f271 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -132,6 +132,8 @@ int device_bind(struct udevice *parent, const struct driver *drv, dm_dbg("Bound device %s to %s\n", dev->name, parent->name); *devp = dev; + dev->flags |= DM_FLAG_BOUND; + return 0; fail_child_post_bind: diff --git a/include/dm/device.h b/include/dm/device.h index 12fd02d..4cd7ba3 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -36,6 +36,9 @@ struct driver_info; /* Allocate driver private data on a DMA boundary */ #define DM_FLAG_ALLOC_PRIV_DMA (1 << 5) +/* Device is bound */ +#define DM_FLAG_BOUND (1 << 6) + /** * struct udevice - An instance of a driver * -- cgit v0.10.2 From 608f26c51bebc68db7f2edc7590ee513d2bc5465 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 25 Jul 2015 21:52:35 +0900 Subject: devres: introduce Devres (Managed Device Resource) framework In U-Boot's driver model, memory is basically allocated and freed in the core framework. So, low level drivers generally only have to specify the size of needed memory with .priv_auto_alloc_size, .platdata_auto_alloc_size, etc. Nevertheless, some drivers still need to allocate/free memory on their own in case they cannot statically know the necessary memory size. So, I believe it is reasonable enough to port Devres into U-boot. Devres, which originates in Linux, manages device resources for each device and automatically releases them on driver detach. With devres, device resources are guaranteed to be freed whether initialization fails half-way or the device gets detached. The basic idea is totally the same to that of Linux, but I tweaked it a bit so that it fits in U-Boot's driver model. In U-Boot, drivers are activated in two steps: binding and probing. Binding puts a driver and a device together. It is just data manipulation on the system memory, so nothing has happened on the hardware device at this moment. When the device is really used, it is probed. Probing initializes the real hardware device to make it really ready for use. So, the resources acquired during the probing process must be freed when the device is removed. Likewise, what has been allocated in binding should be released when the device is unbound. The struct devres has a member "probe" to remember when the resource was allocated. CONFIG_DEBUG_DEVRES is also supported for easier debugging. If enabled, debug messages are printed each time a resource is allocated/freed. Signed-off-by: Masahiro Yamada Acked-by: Simon Glass diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 5d0e949..a002d69 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -78,3 +78,13 @@ config SYSCON as a group by a single driver. Some common functionality is provided by this uclass, including accessing registers via regmap and assigning a unique number to each. + +config DEBUG_DEVRES + bool "Managed device resources verbose debug messages" + depends on DM + help + If this option is enabled, devres debug messages are printed. + Select this if you are having a problem with devres or want to + debug resource management for a managed device. + + If you are unsure about this, Say N here. diff --git a/drivers/core/Makefile b/drivers/core/Makefile index ce3027a..260833e 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -4,7 +4,7 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-y += device.o lists.o root.o uclass.o util.o +obj-y += device.o lists.o root.o uclass.o util.o devres.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_OF_CONTROL) += simple-bus.o endif diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 45d6543..bd6d406 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -95,6 +95,9 @@ int device_unbind(struct udevice *dev) if (dev->parent) list_del(&dev->sibling_node); + + devres_release_all(dev); + free(dev); return 0; @@ -128,6 +131,8 @@ void device_free(struct udevice *dev) dev->parent_priv = NULL; } } + + devres_release_probe(dev); } int device_remove(struct udevice *dev) diff --git a/drivers/core/device.c b/drivers/core/device.c index bf6f271..e3a42dc 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -47,6 +47,7 @@ int device_bind(struct udevice *parent, const struct driver *drv, INIT_LIST_HEAD(&dev->sibling_node); INIT_LIST_HEAD(&dev->child_head); INIT_LIST_HEAD(&dev->uclass_node); + INIT_LIST_HEAD(&dev->devres_head); dev->platdata = platdata; dev->name = name; dev->of_offset = of_offset; @@ -170,6 +171,8 @@ fail_alloc2: dev->platdata = NULL; } fail_alloc1: + devres_release_all(dev); + free(dev); return ret; diff --git a/drivers/core/devres.c b/drivers/core/devres.c new file mode 100644 index 0000000..49c270c --- /dev/null +++ b/drivers/core/devres.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2015 Masahiro Yamada + * + * Based on the original work in Linux by + * Copyright (c) 2006 SUSE Linux Products GmbH + * Copyright (c) 2006 Tejun Heo + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +/** + * struct devres - Bookkeeping info for managed device resource + * @entry: List to associate this structure with a device + * @release: Callback invoked when this resource is released + * @probe: Flag to show when this resource was allocated + (true = probe, false = bind) + * @name: Name of release function + * @size: Size of resource data + * @data: Resource data + */ +struct devres { + struct list_head entry; + dr_release_t release; + bool probe; +#ifdef CONFIG_DEBUG_DEVRES + const char *name; + size_t size; +#endif + unsigned long long data[]; +}; + +#ifdef CONFIG_DEBUG_DEVRES +static void set_node_dbginfo(struct devres *dr, const char *name, size_t size) +{ + dr->name = name; + dr->size = size; +} + +static void devres_log(struct udevice *dev, struct devres *dr, + const char *op) +{ + printf("%s: DEVRES %3s %p %s (%lu bytes)\n", + dev->name, op, dr, dr->name, (unsigned long)dr->size); +} +#else /* CONFIG_DEBUG_DEVRES */ +#define set_node_dbginfo(dr, n, s) do {} while (0) +#define devres_log(dev, dr, op) do {} while (0) +#endif + +#if CONFIG_DEBUG_DEVRES +void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp, + const char *name) +#else +void *_devres_alloc(dr_release_t release, size_t size, gfp_t gfp) +#endif +{ + size_t tot_size = sizeof(struct devres) + size; + struct devres *dr; + + dr = kmalloc(tot_size, gfp); + if (unlikely(!dr)) + return NULL; + + INIT_LIST_HEAD(&dr->entry); + dr->release = release; + set_node_dbginfo(dr, name, size); + + return dr->data; +} + +void devres_free(void *res) +{ + if (res) { + struct devres *dr = container_of(res, struct devres, data); + + BUG_ON(!list_empty(&dr->entry)); + kfree(dr); + } +} + +void devres_add(struct udevice *dev, void *res) +{ + struct devres *dr = container_of(res, struct devres, data); + + devres_log(dev, dr, "ADD"); + BUG_ON(!list_empty(&dr->entry)); + dr->probe = dev->flags & DM_FLAG_BOUND ? true : false; + list_add_tail(&dr->entry, &dev->devres_head); +} + +void *devres_find(struct udevice *dev, dr_release_t release, + dr_match_t match, void *match_data) +{ + struct devres *dr; + + list_for_each_entry_reverse(dr, &dev->devres_head, entry) { + if (dr->release != release) + continue; + if (match && !match(dev, dr->data, match_data)) + continue; + return dr->data; + } + + return NULL; +} + +void *devres_get(struct udevice *dev, void *new_res, + dr_match_t match, void *match_data) +{ + struct devres *new_dr = container_of(new_res, struct devres, data); + void *res; + + res = devres_find(dev, new_dr->release, match, match_data); + if (!res) { + devres_add(dev, new_res); + res = new_res; + new_res = NULL; + } + devres_free(new_res); + + return res; +} + +void *devres_remove(struct udevice *dev, dr_release_t release, + dr_match_t match, void *match_data) +{ + void *res; + + res = devres_find(dev, release, match, match_data); + if (res) { + struct devres *dr = container_of(res, struct devres, data); + + list_del_init(&dr->entry); + devres_log(dev, dr, "REM"); + } + + return res; +} + +int devres_destroy(struct udevice *dev, dr_release_t release, + dr_match_t match, void *match_data) +{ + void *res; + + res = devres_remove(dev, release, match, match_data); + if (unlikely(!res)) + return -ENOENT; + + devres_free(res); + return 0; +} + +int devres_release(struct udevice *dev, dr_release_t release, + dr_match_t match, void *match_data) +{ + void *res; + + res = devres_remove(dev, release, match, match_data); + if (unlikely(!res)) + return -ENOENT; + + (*release)(dev, res); + devres_free(res); + return 0; +} + +static void release_nodes(struct udevice *dev, struct list_head *head, + bool probe_only) +{ + struct devres *dr, *tmp; + + list_for_each_entry_safe_reverse(dr, tmp, head, entry) { + if (probe_only && !dr->probe) + break; + devres_log(dev, dr, "REL"); + dr->release(dev, dr->data); + list_del(&dr->entry); + kfree(dr); + } +} + +void devres_release_probe(struct udevice *dev) +{ + release_nodes(dev, &dev->devres_head, true); +} + +void devres_release_all(struct udevice *dev) +{ + release_nodes(dev, &dev->devres_head, false); +} diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h index 1a9ba01..a2bf057 100644 --- a/include/dm/device-internal.h +++ b/include/dm/device-internal.h @@ -155,4 +155,23 @@ fdt_addr_t simple_bus_translate(struct udevice *dev, fdt_addr_t addr); #define DM_ROOT_NON_CONST (((gd_t *)gd)->dm_root) #define DM_UCLASS_ROOT_NON_CONST (((gd_t *)gd)->uclass_root) +/* device resource management */ +/** + * devres_release_probe - Release managed resources allocated after probing + * @dev: Device to release resources for + * + * Release all resources allocated for @dev when it was probed or later. + * This function is called on driver removal. + */ +void devres_release_probe(struct udevice *dev); + +/** + * devres_release_all - Release all managed resources + * @dev: Device to release resources for + * + * Release all resources associated with @dev. This function is + * called on driver unbinding. + */ +void devres_release_all(struct udevice *dev); + #endif diff --git a/include/dm/device.h b/include/dm/device.h index 4cd7ba3..85513e1 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -96,6 +96,7 @@ struct udevice { uint32_t flags; int req_seq; int seq; + struct list_head devres_head; }; /* Maximum sequence number supported */ @@ -465,4 +466,143 @@ bool device_has_active_children(struct udevice *dev); */ bool device_is_last_sibling(struct udevice *dev); +/* device resource management */ +typedef void (*dr_release_t)(struct udevice *dev, void *res); +typedef int (*dr_match_t)(struct udevice *dev, void *res, void *match_data); + +#ifdef CONFIG_DEBUG_DEVRES +void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp, + const char *name); +#define _devres_alloc(release, size, gfp) \ + __devres_alloc(release, size, gfp, #release) +#else +void *_devres_alloc(dr_release_t release, size_t size, gfp_t gfp); +#endif + +/** + * devres_alloc - Allocate device resource data + * @release: Release function devres will be associated with + * @size: Allocation size + * @gfp: Allocation flags + * + * Allocate devres of @size bytes. The allocated area is associated + * with @release. The returned pointer can be passed to + * other devres_*() functions. + * + * RETURNS: + * Pointer to allocated devres on success, NULL on failure. + */ +#define devres_alloc(release, size, gfp) \ + _devres_alloc(release, size, gfp | __GFP_ZERO) + +/** + * devres_free - Free device resource data + * @res: Pointer to devres data to free + * + * Free devres created with devres_alloc(). + */ +void devres_free(void *res); + +/** + * devres_add - Register device resource + * @dev: Device to add resource to + * @res: Resource to register + * + * Register devres @res to @dev. @res should have been allocated + * using devres_alloc(). On driver detach, the associated release + * function will be invoked and devres will be freed automatically. + */ +void devres_add(struct udevice *dev, void *res); + +/** + * devres_find - Find device resource + * @dev: Device to lookup resource from + * @release: Look for resources associated with this release function + * @match: Match function (optional) + * @match_data: Data for the match function + * + * Find the latest devres of @dev which is associated with @release + * and for which @match returns 1. If @match is NULL, it's considered + * to match all. + * + * RETURNS: + * Pointer to found devres, NULL if not found. + */ +void *devres_find(struct udevice *dev, dr_release_t release, + dr_match_t match, void *match_data); + +/** + * devres_get - Find devres, if non-existent, add one atomically + * @dev: Device to lookup or add devres for + * @new_res: Pointer to new initialized devres to add if not found + * @match: Match function (optional) + * @match_data: Data for the match function + * + * Find the latest devres of @dev which has the same release function + * as @new_res and for which @match return 1. If found, @new_res is + * freed; otherwise, @new_res is added atomically. + * + * RETURNS: + * Pointer to found or added devres. + */ +void *devres_get(struct udevice *dev, void *new_res, + dr_match_t match, void *match_data); + +/** + * devres_remove - Find a device resource and remove it + * @dev: Device to find resource from + * @release: Look for resources associated with this release function + * @match: Match function (optional) + * @match_data: Data for the match function + * + * Find the latest devres of @dev associated with @release and for + * which @match returns 1. If @match is NULL, it's considered to + * match all. If found, the resource is removed atomically and + * returned. + * + * RETURNS: + * Pointer to removed devres on success, NULL if not found. + */ +void *devres_remove(struct udevice *dev, dr_release_t release, + dr_match_t match, void *match_data); + +/** + * devres_destroy - Find a device resource and destroy it + * @dev: Device to find resource from + * @release: Look for resources associated with this release function + * @match: Match function (optional) + * @match_data: Data for the match function + * + * Find the latest devres of @dev associated with @release and for + * which @match returns 1. If @match is NULL, it's considered to + * match all. If found, the resource is removed atomically and freed. + * + * Note that the release function for the resource will not be called, + * only the devres-allocated data will be freed. The caller becomes + * responsible for freeing any other data. + * + * RETURNS: + * 0 if devres is found and freed, -ENOENT if not found. + */ +int devres_destroy(struct udevice *dev, dr_release_t release, + dr_match_t match, void *match_data); + +/** + * devres_release - Find a device resource and destroy it, calling release + * @dev: Device to find resource from + * @release: Look for resources associated with this release function + * @match: Match function (optional) + * @match_data: Data for the match function + * + * Find the latest devres of @dev associated with @release and for + * which @match returns 1. If @match is NULL, it's considered to + * match all. If found, the resource is removed atomically, the + * release function called and the resource freed. + * + * RETURNS: + * 0 if devres is found and freed, -ENOENT if not found. + */ +int devres_release(struct udevice *dev, dr_release_t release, + dr_match_t match, void *match_data); + #endif -- cgit v0.10.2 From 2b07f6859ad17b74ce490f371f4878add6ae5a11 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 25 Jul 2015 21:52:36 +0900 Subject: devres: add devm_kmalloc() and friends (managed memory allocators) devm_kmalloc() is identical to kmalloc() except that the memory allocated with it is managed and will be automatically released when the device is removed/unbound. Likewise for the other variants. Signed-off-by: Masahiro Yamada Acked-by: Simon Glass diff --git a/drivers/core/devres.c b/drivers/core/devres.c index 49c270c..f235c1b 100644 --- a/drivers/core/devres.c +++ b/drivers/core/devres.c @@ -194,3 +194,37 @@ void devres_release_all(struct udevice *dev) { release_nodes(dev, &dev->devres_head, false); } + +/* + * Managed kmalloc/kfree + */ +static void devm_kmalloc_release(struct udevice *dev, void *res) +{ + /* noop */ +} + +static int devm_kmalloc_match(struct udevice *dev, void *res, void *data) +{ + return res == data; +} + +void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp) +{ + void *data; + + data = _devres_alloc(devm_kmalloc_release, size, gfp); + if (unlikely(!data)) + return NULL; + + devres_add(dev, data); + + return data; +} + +void devm_kfree(struct udevice *dev, void *p) +{ + int rc; + + rc = devres_destroy(dev, devm_kmalloc_release, devm_kmalloc_match, p); + WARN_ON(rc); +} diff --git a/include/dm/device.h b/include/dm/device.h index 85513e1..c474f2d 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include struct driver_info; @@ -605,4 +607,46 @@ int devres_destroy(struct udevice *dev, dr_release_t release, int devres_release(struct udevice *dev, dr_release_t release, dr_match_t match, void *match_data); +/* managed devm_k.alloc/kfree for device drivers */ +/** + * devm_kmalloc - Resource-managed kmalloc + * @dev: Device to allocate memory for + * @size: Allocation size + * @gfp: Allocation gfp flags + * + * Managed kmalloc. Memory allocated with this function is + * automatically freed on driver detach. Like all other devres + * resources, guaranteed alignment is unsigned long long. + * + * RETURNS: + * Pointer to allocated memory on success, NULL on failure. + */ +void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp); +static inline void *devm_kzalloc(struct udevice *dev, size_t size, gfp_t gfp) +{ + return devm_kmalloc(dev, size, gfp | __GFP_ZERO); +} +static inline void *devm_kmalloc_array(struct udevice *dev, + size_t n, size_t size, gfp_t flags) +{ + if (size != 0 && n > SIZE_MAX / size) + return NULL; + return devm_kmalloc(dev, n * size, flags); +} +static inline void *devm_kcalloc(struct udevice *dev, + size_t n, size_t size, gfp_t flags) +{ + return devm_kmalloc_array(dev, n, size, flags | __GFP_ZERO); +} + +/** + * devm_kfree - Resource-managed kfree + * @dev: Device this memory belongs to + * @p: Memory to free + * + * Free memory allocated with devm_kmalloc(). + */ +void devm_kfree(struct udevice *dev, void *p); + + #endif -- cgit v0.10.2 From e2282d70763ddf06f9b02007445729c841ef4083 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 25 Jul 2015 21:52:37 +0900 Subject: devres: make Devres optional with CONFIG_DEVRES Currently, Devres requires additional 16 byte for each allocation, which is not so insignificant in some cases. Add CONFIG_DEVRES to make this framework optional. If the option is disabled, devres functions fall back to non-managed variants. For example, devres_alloc() to kzalloc(), devm_kmalloc() to kmalloc(), etc. Because devres_head is also surrounded by an ifdef conditional, there is no memory overhead when CONFIG_DEVRES is disabled. Signed-off-by: Masahiro Yamada Suggested-by: Simon Glass Acked-by: Simon Glass diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index a002d69..8ae0072 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -79,9 +79,22 @@ config SYSCON by this uclass, including accessing registers via regmap and assigning a unique number to each. +config DEVRES + bool "Managed device resources" + depends on DM + help + This option enables the Managed device resources core support. + Device resources managed by the devres framework are automatically + released whether initialization fails half-way or the device gets + detached. + + If this option is disabled, devres functions fall back to + non-managed variants. For example, devres_alloc() to kzalloc(), + devm_kmalloc() to kmalloc(), etc. + config DEBUG_DEVRES bool "Managed device resources verbose debug messages" - depends on DM + depends on DEVRES help If this option is enabled, devres debug messages are printed. Select this if you are having a problem with devres or want to diff --git a/drivers/core/Makefile b/drivers/core/Makefile index 260833e..ccc2fd4 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -4,7 +4,8 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-y += device.o lists.o root.o uclass.o util.o devres.o +obj-y += device.o lists.o root.o uclass.o util.o +obj-$(CONFIG_DEVRES) += devres.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_OF_CONTROL) += simple-bus.o endif diff --git a/drivers/core/device.c b/drivers/core/device.c index e3a42dc..caaf231 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -47,7 +47,9 @@ int device_bind(struct udevice *parent, const struct driver *drv, INIT_LIST_HEAD(&dev->sibling_node); INIT_LIST_HEAD(&dev->child_head); INIT_LIST_HEAD(&dev->uclass_node); +#ifdef CONFIG_DEVRES INIT_LIST_HEAD(&dev->devres_head); +#endif dev->platdata = platdata; dev->name = name; dev->of_offset = of_offset; diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h index a2bf057..2cd2fe9 100644 --- a/include/dm/device-internal.h +++ b/include/dm/device-internal.h @@ -156,6 +156,8 @@ fdt_addr_t simple_bus_translate(struct udevice *dev, fdt_addr_t addr); #define DM_UCLASS_ROOT_NON_CONST (((gd_t *)gd)->uclass_root) /* device resource management */ +#ifdef CONFIG_DEVRES + /** * devres_release_probe - Release managed resources allocated after probing * @dev: Device to release resources for @@ -174,4 +176,15 @@ void devres_release_probe(struct udevice *dev); */ void devres_release_all(struct udevice *dev); +#else /* ! CONFIG_DEVRES */ + +static inline void devres_release_probe(struct udevice *dev) +{ +} + +static inline void devres_release_all(struct udevice *dev) +{ +} + +#endif /* ! CONFIG_DEVRES */ #endif diff --git a/include/dm/device.h b/include/dm/device.h index c474f2d..53773a8 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -98,7 +98,9 @@ struct udevice { uint32_t flags; int req_seq; int seq; +#ifdef CONFIG_DEVRES struct list_head devres_head; +#endif }; /* Maximum sequence number supported */ @@ -472,6 +474,8 @@ bool device_is_last_sibling(struct udevice *dev); typedef void (*dr_release_t)(struct udevice *dev, void *res); typedef int (*dr_match_t)(struct udevice *dev, void *res, void *match_data); +#ifdef CONFIG_DEVRES + #ifdef CONFIG_DEBUG_DEVRES void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp, const char *name); @@ -648,5 +652,83 @@ static inline void *devm_kcalloc(struct udevice *dev, */ void devm_kfree(struct udevice *dev, void *p); +#else /* ! CONFIG_DEVRES */ + +static inline void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp) +{ + return kzalloc(size, gfp); +} + +static inline void devres_free(void *res) +{ + kfree(res); +} + +static inline void devres_add(struct udevice *dev, void *res) +{ +} + +static inline void *devres_find(struct udevice *dev, dr_release_t release, + dr_match_t match, void *match_data) +{ + return NULL; +} + +static inline void *devres_get(struct udevice *dev, void *new_res, + dr_match_t match, void *match_data) +{ + return NULL; +} + +static inline void *devres_remove(struct udevice *dev, dr_release_t release, + dr_match_t match, void *match_data) +{ + return NULL; +} + +static inline int devres_destroy(struct udevice *dev, dr_release_t release, + dr_match_t match, void *match_data) +{ + return 0; +} + +static inline int devres_release(struct udevice *dev, dr_release_t release, + dr_match_t match, void *match_data) +{ + return 0; +} + +static inline void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp) +{ + return kmalloc(size, gfp); +} + +static inline void *devm_kzalloc(struct udevice *dev, size_t size, gfp_t gfp) +{ + return kzalloc(size, gfp); +} + +static inline void *devm_kmaloc_array(struct udevice *dev, + size_t n, size_t size, gfp_t flags) +{ + /* TODO: add kmalloc_array() to linux/compat.h */ + if (size != 0 && n > SIZE_MAX / size) + return NULL; + return kmalloc(n * size, flags); +} + +static inline void *devm_kcalloc(struct udevice *dev, + size_t n, size_t size, gfp_t flags) +{ + /* TODO: add kcalloc() to linux/compat.h */ + return kmalloc(n * size, flags | __GFP_ZERO); +} + +static inline void devm_kfree(struct udevice *dev, void *p) +{ + kfree(p); +} + +#endif /* ! CONFIG_DEVRES */ #endif -- cgit v0.10.2 From 40b6f2d020ca8074ed38a1b6bfa170aead1a170e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 25 Jul 2015 21:52:38 +0900 Subject: devres: add debug command to dump device resources This new command can dump all device resources associated to each device. The fields in every line shows: - The address of the resource - The size of the resource - The name of the release function - The stage in which the resource has been acquired (BIND/PROBE) Currently, there is no driver using devres, but if such drivers are implemented, the output of this command should look like this: => dm devres - root_driver - soc - extbus - serial@54006800 bfb541e8 (8 byte) devm_kmalloc_release BIND bfb54440 (4 byte) devm_kmalloc_release PROBE bfb54460 (4 byte) devm_kmalloc_release PROBE - serial@54006900 bfb54270 (8 byte) devm_kmalloc_release BIND - gpio@55000000 - i2c@58780000 bfb5bce8 (12 byte) devm_kmalloc_release PROBE bfb5bd10 (4 byte) devm_kmalloc_release PROBE - eeprom bfb54418 (12 byte) devm_kmalloc_release BIND Signed-off-by: Masahiro Yamada Acked-by: Simon Glass diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 8ae0072..c82b564 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -93,10 +93,11 @@ config DEVRES devm_kmalloc() to kmalloc(), etc. config DEBUG_DEVRES - bool "Managed device resources verbose debug messages" + bool "Managed device resources debugging functions" depends on DEVRES help If this option is enabled, devres debug messages are printed. + Also, a function is available to dump a list of device resources. Select this if you are having a problem with devres or want to debug resource management for a managed device. diff --git a/drivers/core/devres.c b/drivers/core/devres.c index f235c1b..605295b 100644 --- a/drivers/core/devres.c +++ b/drivers/core/devres.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include /** * struct devres - Bookkeeping info for managed device resource @@ -195,6 +197,33 @@ void devres_release_all(struct udevice *dev) release_nodes(dev, &dev->devres_head, false); } +#ifdef CONFIG_DEBUG_DEVRES +static void dump_resources(struct udevice *dev, int depth) +{ + struct devres *dr; + struct udevice *child; + + printf("- %s\n", dev->name); + + list_for_each_entry(dr, &dev->devres_head, entry) + printf(" %p (%lu byte) %s %s\n", dr, + (unsigned long)dr->size, dr->name, + dr->probe ? "PROBE" : "BIND"); + + list_for_each_entry(child, &dev->child_head, sibling_node) + dump_resources(child, depth + 1); +} + +void dm_dump_devres(void) +{ + struct udevice *root; + + root = dm_root(); + if (root) + dump_resources(root, 0); +} +#endif + /* * Managed kmalloc/kfree */ diff --git a/include/dm/util.h b/include/dm/util.h index 7dbed67..15daa3d 100644 --- a/include/dm/util.h +++ b/include/dm/util.h @@ -39,4 +39,13 @@ void dm_dump_all(void); /* Dump out a list of uclasses and their devices */ void dm_dump_uclass(void); +#ifdef CONFIG_DEBUG_DEVRES +/* Dump out a list of device resources */ +void dm_dump_devres(void); +#else +static inline void dm_dump_devres(void) +{ +} +#endif + #endif diff --git a/test/dm/cmd_dm.c b/test/dm/cmd_dm.c index 5c501ec..caff49a 100644 --- a/test/dm/cmd_dm.c +++ b/test/dm/cmd_dm.c @@ -32,9 +32,18 @@ static int do_dm_dump_uclass(cmd_tbl_t *cmdtp, int flag, int argc, return 0; } +static int do_dm_dump_devres(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + dm_dump_devres(); + + return 0; +} + static cmd_tbl_t test_commands[] = { U_BOOT_CMD_MKENT(tree, 0, 1, do_dm_dump_all, "", ""), U_BOOT_CMD_MKENT(uclass, 1, 1, do_dm_dump_uclass, "", ""), + U_BOOT_CMD_MKENT(devres, 1, 1, do_dm_dump_devres, "", ""), }; static int do_dm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) @@ -60,5 +69,6 @@ U_BOOT_CMD( dm, 3, 1, do_dm, "Driver model low level access", "tree Dump driver model tree ('*' = activated)\n" - "dm uclass Dump list of instances for each uclass" + "dm uclass Dump list of instances for each uclass\n" + "dm devres Dump list of device resources for each device" ); -- cgit v0.10.2 From c3e6847bb33c061ce248d0178d8ca8daf1104aef Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 28 Jul 2015 11:53:14 -0600 Subject: sandbox: Enable devres subsystem This should be used for sandbox. We can convert at least one driver to use it, but in the meantime, enable the feature so that the code is build-tested. Signed-off-by: Simon Glass diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index e6a4590..874a26b 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -53,3 +53,4 @@ CONFIG_LED=y CONFIG_LED_GPIO=y CONFIG_SYSCON=y CONFIG_REGMAP=y +CONFIG_DEVRES=y -- cgit v0.10.2 From f5c67ea036be174f75cb48b8f91894bc710811dd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 30 Jul 2015 13:40:39 -0600 Subject: dm: core: Add a way to set a device name Some devices are bound entirely by probing and do not have the benefit of a device tree to give them a name. This is very common with PCI and USB. In most cases this is fine, but we should add an official way to set a device name. This should be called in the device's bind() method. Signed-off-by: Simon Glass Reviewed-by: Bin Meng diff --git a/drivers/core/device.c b/drivers/core/device.c index caaf231..bbe7a94 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -603,3 +603,13 @@ bool device_is_last_sibling(struct udevice *dev) return false; return list_is_last(&dev->sibling_node, &parent->child_head); } + +int device_set_name(struct udevice *dev, const char *name) +{ + name = strdup(name); + if (!name) + return -ENOMEM; + dev->name = name; + + return 0; +} diff --git a/include/dm/device.h b/include/dm/device.h index 53773a8..1f78963 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -470,6 +470,21 @@ bool device_has_active_children(struct udevice *dev); */ bool device_is_last_sibling(struct udevice *dev); +/** + * device_set_name() - set the name of a device + * + * This must be called in the device's bind() method and no later. Normally + * this is unnecessary but for probed devices which don't get a useful name + * this function can be helpful. + * + * @dev: Device to update + * @name: New name (this string is allocated new memory and attached to + * the device) + * @return 0 if OK, -ENOMEM if there is not enough memory to allocate the + * string + */ +int device_set_name(struct udevice *dev, const char *name); + /* device resource management */ typedef void (*dr_release_t)(struct udevice *dev, void *res); typedef int (*dr_match_t)(struct udevice *dev, void *res, void *match_data); -- cgit v0.10.2 From 74356d7fb1715db41dcf5530108cc0db82f278d7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 30 Jul 2015 13:40:40 -0600 Subject: dm: core: Fix a typo in the uclass_get_device_by_name() comment This function comment has a typo. Fix it. Signed-off-by: Simon Glass Reviewed-by: Bin Meng diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 3fe1739..d56877c 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -130,7 +130,7 @@ int uclass_get(enum uclass_id key, struct uclass **ucp); int uclass_get_device(enum uclass_id id, int index, struct udevice **devp); /** - * uclass_get_device_by_name() - Get a uclass device by it's name + * uclass_get_device_by_name() - Get a uclass device by its name * * This searches the devices in the uclass for one with the exactly given name. * -- cgit v0.10.2 From bbbe55f6f25e686ad799c2a70aa941eeb276adb6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 2 Aug 2015 18:07:21 -0600 Subject: x86: Enable debug UART for Minnowmax Enable the debug UART and emit a single 'a' early in the init sequence to show that it is working. Unfortunately the debug UART implementation needs a stack to work. I cannot seem to remove this limitation as the absolute 'jmp %eax' instruction goes off into the weeds. So this means that the character output cannot be any earlier than car_init_ret, where memory is available for a stack. Signed-off-by: Simon Glass Reviewed-by: Lukasz Majewski diff --git a/arch/x86/cpu/start.S b/arch/x86/cpu/start.S index e5c1733..313fa3f 100644 --- a/arch/x86/cpu/start.S +++ b/arch/x86/cpu/start.S @@ -128,6 +128,15 @@ car_init_ret: andl $0xfffffff0, %esp post_code(POST_START_STACK) + /* + * Debug UART is available here although it may not be plumbed out + * to pins depending on the board. To use it: + * + * call debug_uart_init + * mov $'a', %eax + * call printch + */ + /* Zero the global data since it won't happen later */ xorl %eax, %eax movl $GENERATED_GBL_DATA_SIZE, %ecx diff --git a/configs/minnowmax_defconfig b/configs/minnowmax_defconfig index e98f5eb..a67597d 100644 --- a/configs/minnowmax_defconfig +++ b/configs/minnowmax_defconfig @@ -24,3 +24,7 @@ CONFIG_FRAMEBUFFER_VESA_MODE_11A=y CONFIG_DM_RTC=y CONFIG_USE_PRIVATE_LIBGCC=y CONFIG_SYS_VSNPRINTF=y +CONFIG_DEBUG_UART=y +CONFIG_DEBUG_UART_NS16550=y +CONFIG_DEBUG_UART_BASE=0x3f8 +CONFIG_DEBUG_UART_CLOCK=1843200 -- cgit v0.10.2 From fac971b2b5efbdb6ed2d12ebdbf7e029c5ed30e8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 3 Aug 2015 08:19:19 -0600 Subject: exynos: dts: Correct LDO and BUCK naming At present lower case is used for the regulator names in the device tree. The kernel uses upper case and U-Boot will require this also since it will move to a case-sensitive name check. Signed-off-by: Simon Glass diff --git a/arch/arm/dts/exynos4412-odroid.dts b/arch/arm/dts/exynos4412-odroid.dts index d572f1e..a63e8ab 100644 --- a/arch/arm/dts/exynos4412-odroid.dts +++ b/arch/arm/dts/exynos4412-odroid.dts @@ -42,103 +42,103 @@ #clock-cells = <1>; voltage-regulators { - ldo1_reg: ldo1 { + ldo1_reg: LDO1 { regulator-name = "VDD_ALIVE_1.0V"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; }; - ldo2_reg: ldo2 { + ldo2_reg: LDO2 { regulator-name = "VDDQ_VM1M2_1.2V"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; }; - ldo3_reg: ldo3 { + ldo3_reg: LDO3 { regulator-name = "VCC_1.8V_AP"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; }; - ldo4_reg: ldo4 { + ldo4_reg: LDO4 { regulator-name = "VDDQ_MMC2_2.8V"; regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; }; - ldo5_reg: ldo5 { + ldo5_reg: LDO5 { regulator-name = "VDDQ_MMC0/1/3_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; }; - ldo6_reg: ldo6 { + ldo6_reg: LDO6 { regulator-name = "VMPLL_1.0V"; regulator-min-microvolt = <1100000>; regulator-max-microvolt = <1100000>; }; - ldo7_reg: ldo7 { + ldo7_reg: LDO7 { regulator-name = "VPLL_1.1V"; regulator-min-microvolt = <1100000>; regulator-max-microvolt = <1100000>; }; - ldo8_reg: ldo8 { + ldo8_reg: LDO8 { regulator-name = "VDD_MIPI/HDMI_1.0V"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; }; - ldo10_reg: ldo10 { + ldo10_reg: LDO10 { regulator-name = "VDD_MIPI/HDMI_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; }; - ldo11_reg: ldo11 { + ldo11_reg: LDO11 { regulator-name = "VDD_ABB1_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; }; - ldo12_reg: ldo12 { + ldo12_reg: LDO12 { regulator-name = "VDD_UOTG_3.0V"; regulator-min-microvolt = <3000000>; regulator-max-microvolt = <3000000>; }; - ldo13_reg: ldo13 { + ldo13_reg: LDO13 { regulator-name = "VDD_C2C_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; }; - ldo14_reg: ldo14 { + ldo14_reg: LDO14 { regulator-name = "VDD_ABB02_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; }; - ldo15_reg: ldo15 { + ldo15_reg: LDO15 { regulator-name = "VDD_HSIC/OTG_1.0V"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; }; - ldo16_reg: ldo16 { + ldo16_reg: LDO16 { regulator-name = "VDD_HSIC_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; }; - ldo17_reg: ldo17 { + ldo17_reg: LDO17 { regulator-name = "VDDQ_CAM_1.2V"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; }; - ldo20_reg: ldo20 { + ldo20_reg: LDO20 { regulator-name = "VDDQ_EMMC_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; @@ -146,7 +146,7 @@ regulator-boot-on; }; - ldo21_reg: ldo21 { + ldo21_reg: LDO21 { regulator-name = "TFLASH_2.8V"; regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; @@ -154,7 +154,7 @@ regulator-boot-on; }; - ldo22_reg: ldo22 { + ldo22_reg: LDO22 { regulator-name = "VDDQ_EMMC_2.8V"; regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; @@ -162,56 +162,56 @@ regulator-boot-on; }; - ldo25_reg: ldo25 { + ldo25_reg: LDO25 { regulator-compatible = "LDO25"; regulator-name = "VDDQ_LCD_3.0V"; regulator-min-microvolt = <3000000>; regulator-max-microvolt = <3000000>; }; - buck1_reg: buck1 { + buck1_reg: BUCK1 { regulator-name = "VDD_MIF_1.0V"; regulator-min-microvolt = <8500000>; regulator-max-microvolt = <1100000>; }; - buck2_reg: buck2 { + buck2_reg: BUCK2 { regulator-name = "VDD_ARM_1.0V"; regulator-min-microvolt = <850000>; regulator-max-microvolt = <1500000>; }; - buck3_reg: buck3 { + buck3_reg: BUCK3 { regulator-name = "VDD_INT_1.1V"; regulator-min-microvolt = <850000>; regulator-max-microvolt = <1150000>; }; - buck4_reg: buck4 { + buck4_reg: BUCK4 { regulator-name = "VDD_G3D_1.0V"; regulator-min-microvolt = <850000>; regulator-max-microvolt = <1150000>; }; - buck5_reg: buck5 { + buck5_reg: BUCK5 { regulator-name = "VDDQ_AP_1.2V"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; }; - buck6_reg: buck6 { + buck6_reg: BUCK6 { regulator-name = "VCC_INL1/7_1.35V"; regulator-min-microvolt = <1350000>; regulator-max-microvolt = <1350000>; }; - buck7_reg: buck7 { + buck7_reg: BUCK7 { regulator-name = "VCC_INL2/3/5_2.0V"; regulator-min-microvolt = <2000000>; regulator-max-microvolt = <2000000>; }; - buck8_reg: buck8 { + buck8_reg: BUCK8 { regulator-name = "VCC_P3V3_2.85V"; regulator-min-microvolt = <2850000>; regulator-max-microvolt = <3300000>; diff --git a/arch/arm/dts/exynos4412-trats2.dts b/arch/arm/dts/exynos4412-trats2.dts index 5c0bb91..2d4e522 100644 --- a/arch/arm/dts/exynos4412-trats2.dts +++ b/arch/arm/dts/exynos4412-trats2.dts @@ -47,7 +47,7 @@ #clock-cells = <1>; voltage-regulators { - ldo1_reg: ldo1 { + ldo1_reg: LDO1 { regulator-compatible = "LDO1"; regulator-name = "VALIVE_1.0V_AP"; regulator-min-microvolt = <1000000>; @@ -56,7 +56,7 @@ regulator-mem-on; }; - ldo2_reg: ldo2 { + ldo2_reg: LDO2 { regulator-compatible = "LDO2"; regulator-name = "VM1M2_1.2V_AP"; regulator-min-microvolt = <1200000>; @@ -65,7 +65,7 @@ regulator-mem-on; }; - ldo3_reg: ldo3 { + ldo3_reg: LDO3 { regulator-compatible = "LDO3"; regulator-name = "VCC_1.8V_AP"; regulator-min-microvolt = <1800000>; @@ -74,7 +74,7 @@ regulator-mem-on; }; - ldo4_reg: ldo4 { + ldo4_reg: LDO4 { regulator-compatible = "LDO4"; regulator-name = "VCC_2.8V_AP"; regulator-min-microvolt = <2800000>; @@ -83,7 +83,7 @@ regulator-mem-on; }; - ldo5_reg: ldo5 { + ldo5_reg: LDO5 { regulator-compatible = "LDO5"; regulator-name = "VCC_1.8V_IO"; regulator-min-microvolt = <1800000>; @@ -92,7 +92,7 @@ regulator-mem-on; }; - ldo6_reg: ldo6 { + ldo6_reg: LDO6 { regulator-compatible = "LDO6"; regulator-name = "VMPLL_1.0V_AP"; regulator-min-microvolt = <1000000>; @@ -101,7 +101,7 @@ regulator-mem-on; }; - ldo7_reg: ldo7 { + ldo7_reg: LDO7 { regulator-compatible = "LDO7"; regulator-name = "VPLL_1.0V_AP"; regulator-min-microvolt = <1000000>; @@ -110,7 +110,7 @@ regulator-mem-on; }; - ldo8_reg: ldo8 { + ldo8_reg: LDO8 { regulator-compatible = "LDO8"; regulator-name = "VMIPI_1.0V"; regulator-min-microvolt = <1000000>; @@ -118,7 +118,7 @@ regulator-mem-off; }; - ldo9_reg: ldo9 { + ldo9_reg: LDO9 { regulator-compatible = "LDO9"; regulator-name = "CAM_ISP_MIPI_1.2V"; regulator-min-microvolt = <1200000>; @@ -126,7 +126,7 @@ regulator-mem-idle; }; - ldo10_reg: ldo10 { + ldo10_reg: LDO10 { regulator-compatible = "LDO10"; regulator-name = "VMIPI_1.8V"; regulator-min-microvolt = <1800000>; @@ -134,7 +134,7 @@ regulator-mem-off; }; - ldo11_reg: ldo11 { + ldo11_reg: LDO11 { regulator-compatible = "LDO11"; regulator-name = "VABB1_1.95V"; regulator-min-microvolt = <1950000>; @@ -143,7 +143,7 @@ regulator-mem-off; }; - ldo12_reg: ldo12 { + ldo12_reg: LDO12 { regulator-compatible = "LDO12"; regulator-name = "VUOTG_3.0V"; regulator-min-microvolt = <3000000>; @@ -151,7 +151,7 @@ regulator-mem-off; }; - ldo13_reg: ldo13 { + ldo13_reg: LDO13 { regulator-compatible = "LDO13"; regulator-name = "NFC_AVDD_1.8V"; regulator-min-microvolt = <1800000>; @@ -159,7 +159,7 @@ regulator-mem-idle; }; - ldo14_reg: ldo14 { + ldo14_reg: LDO14 { regulator-compatible = "LDO14"; regulator-name = "VABB2_1.95V"; regulator-min-microvolt = <1950000>; @@ -168,7 +168,7 @@ regulator-mem-off; }; - ldo15_reg: ldo15 { + ldo15_reg: LDO15 { regulator-compatible = "LDO15"; regulator-name = "VHSIC_1.0V"; regulator-min-microvolt = <1000000>; @@ -176,7 +176,7 @@ regulator-mem-off; }; - ldo16_reg: ldo16 { + ldo16_reg: LDO16 { regulator-compatible = "LDO16"; regulator-name = "VHSIC_1.8V"; regulator-min-microvolt = <1800000>; @@ -184,7 +184,7 @@ regulator-mem-off; }; - ldo17_reg: ldo17 { + ldo17_reg: LDO17 { regulator-compatible = "LDO17"; regulator-name = "CAM_SENSOR_CORE_1.2V"; regulator-min-microvolt = <1200000>; @@ -192,7 +192,7 @@ regulator-mem-idle; }; - ldo18_reg: ldo18 { + ldo18_reg: LDO18 { regulator-compatible = "LDO18"; regulator-name = "CAM_ISP_SEN_IO_1.8V"; regulator-min-microvolt = <1800000>; @@ -200,7 +200,7 @@ regulator-mem-idle; }; - ldo19_reg: ldo19 { + ldo19_reg: LDO19 { regulator-compatible = "LDO19"; regulator-name = "VT_CAM_1.8V"; regulator-min-microvolt = <1800000>; @@ -208,7 +208,7 @@ regulator-mem-idle; }; - ldo20_reg: ldo20 { + ldo20_reg: LDO20 { regulator-compatible = "LDO20"; regulator-name = "VDDQ_PRE_1.8V"; regulator-min-microvolt = <1800000>; @@ -216,7 +216,7 @@ regulator-mem-idle; }; - ldo21_reg: ldo21 { + ldo21_reg: LDO21 { regulator-compatible = "LDO21"; regulator-name = "VTF_2.8V"; regulator-min-microvolt = <2800000>; @@ -224,7 +224,7 @@ regulator-mem-idle; }; - ldo22_reg: ldo22 { + ldo22_reg: LDO22 { regulator-compatible = "LDO22"; regulator-name = "VMEM_VDD_2.8V"; regulator-min-microvolt = <2800000>; @@ -233,7 +233,7 @@ regulator-mem-off; }; - ldo23_reg: ldo23 { + ldo23_reg: LDO23 { regulator-compatible = "LDO23"; regulator-name = "TSP_AVDD_3.3V"; regulator-min-microvolt = <3300000>; @@ -241,7 +241,7 @@ regulator-mem-idle; }; - ldo24_reg: ldo24 { + ldo24_reg: LDO24 { regulator-compatible = "LDO24"; regulator-name = "TSP_VDD_1.8V"; regulator-min-microvolt = <1800000>; @@ -249,7 +249,7 @@ regulator-mem-idle; }; - ldo25_reg: ldo25 { + ldo25_reg: LDO25 { regulator-compatible = "LDO25"; regulator-name = "LCD_VCC_3.3V"; regulator-min-microvolt = <2800000>; @@ -257,7 +257,7 @@ regulator-mem-idle; }; - ldo26_reg: ldo26 { + ldo26_reg: LDO26 { regulator-compatible = "LDO26"; regulator-name = "MOTOR_VCC_3.0V"; regulator-min-microvolt = <3000000>; @@ -265,7 +265,7 @@ regulator-mem-idle; }; - buck1_reg: buck1 { + buck1_reg: BUCK1 { regulator-compatible = "BUCK1"; regulator-name = "vdd_mif"; regulator-min-microvolt = <850000>; @@ -275,7 +275,7 @@ regulator-mem-off; }; - buck2_reg: buck2 { + buck2_reg: BUCK2 { regulator-compatible = "BUCK2"; regulator-name = "vdd_arm"; regulator-min-microvolt = <850000>; @@ -285,7 +285,7 @@ regulator-mem-off; }; - buck3_reg: buck3 { + buck3_reg: BUCK3 { regulator-compatible = "BUCK3"; regulator-name = "vdd_int"; regulator-min-microvolt = <850000>; @@ -295,7 +295,7 @@ regulator-mem-off; }; - buck4_reg: buck4 { + buck4_reg: BUCK4 { regulator-compatible = "BUCK4"; regulator-name = "vdd_g3d"; regulator-min-microvolt = <850000>; @@ -304,7 +304,7 @@ regulator-mem-off; }; - buck5_reg: buck5 { + buck5_reg: BUCK5 { regulator-compatible = "BUCK5"; regulator-name = "VMEM_1.2V_AP"; regulator-min-microvolt = <1200000>; @@ -312,7 +312,7 @@ regulator-always-on; }; - buck6_reg: buck6 { + buck6_reg: BUCK6 { regulator-compatible = "BUCK6"; regulator-name = "VCC_SUB_1.35V"; regulator-min-microvolt = <1350000>; @@ -320,7 +320,7 @@ regulator-always-on; }; - buck7_reg: buck7 { + buck7_reg: BUCK7 { regulator-compatible = "BUCK7"; regulator-name = "VCC_SUB_2.0V"; regulator-min-microvolt = <2000000>; @@ -328,7 +328,7 @@ regulator-always-on; }; - buck8_reg: buck8 { + buck8_reg: BUCK8 { regulator-compatible = "BUCK8"; regulator-name = "VMEM_VDDF_3.0V"; regulator-min-microvolt = <2850000>; @@ -337,7 +337,7 @@ regulator-mem-off; }; - buck9_reg: buck9 { + buck9_reg: BUCK9 { regulator-compatible = "BUCK9"; regulator-name = "CAM_ISP_CORE_1.2V"; regulator-min-microvolt = <1000000>; -- cgit v0.10.2