From b6036bcd2ac27b3497ec16a63456eb4ed2b23e9f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 13 Jan 2015 12:44:35 +0900 Subject: i2c: add CONFIG_DM_I2C to Kconfig Signed-off-by: Masahiro Yamada Reviewed-by: Simon Glass Acked-by: Simon Glass Acked-by: Heiko Schocher --- drivers/i2c/Kconfig | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index e69de29bb2d..96715d0eedb 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -0,0 +1,6 @@ +config DM_I2C + bool "Enable Driver Model for I2C drivers" + depends on DM + help + If you want to use driver model for I2C drivers, say Y. + To use legacy I2C drivers, say N. -- cgit v1.3.1 From 26f820f3f149ffb2577592eae50d2bdb04e5203f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 13 Jan 2015 12:44:36 +0900 Subject: i2c: UniPhier: add driver for UniPhier i2c controller This commit adds on-chip I2C driver used on some old Panasonic UniPhier SoCs. Signed-off-by: Masahiro Yamada Reviewed-by: Simon Glass Acked-by: Simon Glass Acked-by: Heiko Schocher --- drivers/i2c/Kconfig | 8 ++ drivers/i2c/Makefile | 1 + drivers/i2c/i2c-uniphier.c | 239 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 248 insertions(+) create mode 100644 drivers/i2c/i2c-uniphier.c (limited to 'drivers') diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 96715d0eedb..6a479efd7c6 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -4,3 +4,11 @@ config DM_I2C help If you want to use driver model for I2C drivers, say Y. To use legacy I2C drivers, say N. + +config SYS_I2C_UNIPHIER + bool "UniPhier I2C driver" + depends on ARCH_UNIPHIER && DM_I2C + default y + help + Support for Panasonic UniPhier I2C controller driver. This I2C + controller is used on PH1-LD4, PH1-sLD8 or older UniPhier SoCs. diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 6f3c86c0385..e2fcd24ef3c 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -31,4 +31,5 @@ obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o +obj-$(CONFIG_SYS_I2C_UNIPHIER) += i2c-uniphier.o obj-$(CONFIG_SYS_I2C_ZYNQ) += zynq_i2c.o diff --git a/drivers/i2c/i2c-uniphier.c b/drivers/i2c/i2c-uniphier.c new file mode 100644 index 00000000000..bdac1f90799 --- /dev/null +++ b/drivers/i2c/i2c-uniphier.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2014 Panasonic Corporation + * Author: Masahiro Yamada + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct uniphier_i2c_regs { + u32 dtrm; /* data transmission */ +#define I2C_DTRM_STA (1 << 10) +#define I2C_DTRM_STO (1 << 9) +#define I2C_DTRM_NACK (1 << 8) +#define I2C_DTRM_RD (1 << 0) + u32 drec; /* data reception */ +#define I2C_DREC_STS (1 << 12) +#define I2C_DREC_LRB (1 << 11) +#define I2C_DREC_LAB (1 << 9) + u32 myad; /* slave address */ + u32 clk; /* clock frequency control */ + u32 brst; /* bus reset */ +#define I2C_BRST_FOEN (1 << 1) +#define I2C_BRST_BRST (1 << 0) + u32 hold; /* hold time control */ + u32 bsts; /* bus status monitor */ + u32 noise; /* noise filter control */ + u32 setup; /* setup time control */ +}; + +#define IOBUS_FREQ 100000000 + +struct uniphier_i2c_dev { + struct uniphier_i2c_regs __iomem *regs; /* register base */ + unsigned long input_clk; /* master clock (Hz) */ + unsigned long wait_us; /* wait for every byte transfer (us) */ +}; + +static int uniphier_i2c_probe(struct udevice *dev) +{ + fdt_addr_t addr; + fdt_size_t size; + struct uniphier_i2c_dev *priv = dev_get_priv(dev); + + addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size); + + priv->regs = map_sysmem(addr, size); + + if (!priv->regs) + return -ENOMEM; + + priv->input_clk = IOBUS_FREQ; + + /* deassert reset */ + writel(0x3, &priv->regs->brst); + + return 0; +} + +static int uniphier_i2c_remove(struct udevice *dev) +{ + struct uniphier_i2c_dev *priv = dev_get_priv(dev); + + unmap_sysmem(priv->regs); + + return 0; +} + +static int uniphier_i2c_child_pre_probe(struct udevice *dev) +{ + struct dm_i2c_chip *i2c_chip = dev_get_parentdata(dev); + + if (dev->of_offset == -1) + return 0; + return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, + i2c_chip); +} + +static int send_and_recv_byte(struct uniphier_i2c_dev *dev, u32 dtrm) +{ + writel(dtrm, &dev->regs->dtrm); + + /* + * This controller only provides interruption to inform the completion + * of each byte transfer. (No status register to poll it.) + * Unfortunately, U-Boot does not have a good support of interrupt. + * Wait for a while. + */ + udelay(dev->wait_us); + + return readl(&dev->regs->drec); +} + +static int send_byte(struct uniphier_i2c_dev *dev, u32 dtrm, bool *stop) +{ + int ret = 0; + u32 drec; + + drec = send_and_recv_byte(dev, dtrm); + + if (drec & I2C_DREC_LAB) { + debug("uniphier_i2c: bus arbitration failed\n"); + *stop = false; + ret = -EREMOTEIO; + } + if (drec & I2C_DREC_LRB) { + debug("uniphier_i2c: slave did not return ACK\n"); + ret = -EREMOTEIO; + } + return ret; +} + +static int uniphier_i2c_transmit(struct uniphier_i2c_dev *dev, uint addr, + uint len, const u8 *buf, bool *stop) +{ + int ret; + + debug("%s: addr = %x, len = %d\n", __func__, addr, len); + + ret = send_byte(dev, I2C_DTRM_STA | I2C_DTRM_NACK | addr << 1, stop); + if (ret < 0) + goto fail; + + while (len--) { + ret = send_byte(dev, I2C_DTRM_NACK | *buf++, stop); + if (ret < 0) + goto fail; + } + +fail: + if (*stop) + writel(I2C_DTRM_STO | I2C_DTRM_NACK, &dev->regs->dtrm); + + return ret; +} + +static int uniphier_i2c_receive(struct uniphier_i2c_dev *dev, uint addr, + uint len, u8 *buf, bool *stop) +{ + int ret; + + debug("%s: addr = %x, len = %d\n", __func__, addr, len); + + ret = send_byte(dev, I2C_DTRM_STA | I2C_DTRM_NACK | + I2C_DTRM_RD | addr << 1, stop); + if (ret < 0) + goto fail; + + while (len--) + *buf++ = send_and_recv_byte(dev, len ? 0 : I2C_DTRM_NACK); + +fail: + if (*stop) + writel(I2C_DTRM_STO | I2C_DTRM_NACK, &dev->regs->dtrm); + + return ret; +} + +static int uniphier_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, + int nmsgs) +{ + int ret = 0; + struct uniphier_i2c_dev *dev = dev_get_priv(bus); + bool stop; + + for (; nmsgs > 0; nmsgs--, msg++) { + /* If next message is read, skip the stop condition */ + stop = nmsgs > 1 && msg[1].flags & I2C_M_RD ? false : true; + + if (msg->flags & I2C_M_RD) + ret = uniphier_i2c_receive(dev, msg->addr, msg->len, + msg->buf, &stop); + else + ret = uniphier_i2c_transmit(dev, msg->addr, msg->len, + msg->buf, &stop); + + if (ret < 0) + break; + } + + return ret; +} + +static int uniphier_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) +{ + struct uniphier_i2c_dev *priv = dev_get_priv(bus); + + /* max supported frequency is 400 kHz */ + if (speed > 400000) + return -EINVAL; + + /* bus reset: make sure the bus is idle when change the frequency */ + writel(0x1, &priv->regs->brst); + + writel((priv->input_clk / speed / 2 << 16) | (priv->input_clk / speed), + &priv->regs->clk); + + writel(0x3, &priv->regs->brst); + + /* + * Theoretically, each byte can be transferred in + * 1000000 * 9 / speed usec. For safety, wait more than double. + */ + priv->wait_us = 20000000 / speed; + + return 0; +} + + +static const struct dm_i2c_ops uniphier_i2c_ops = { + .xfer = uniphier_i2c_xfer, + .set_bus_speed = uniphier_i2c_set_bus_speed, +}; + +static const struct udevice_id uniphier_i2c_of_match[] = { + { .compatible = "panasonic,uniphier-i2c" }, + {}, +}; + +U_BOOT_DRIVER(uniphier_i2c) = { + .name = "uniphier-i2c", + .id = UCLASS_I2C, + .of_match = uniphier_i2c_of_match, + .probe = uniphier_i2c_probe, + .remove = uniphier_i2c_remove, + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), + .child_pre_probe = uniphier_i2c_child_pre_probe, + .priv_auto_alloc_size = sizeof(struct uniphier_i2c_dev), + .ops = &uniphier_i2c_ops, +}; -- cgit v1.3.1 From 238bd0b8ce525c3b2c9ddf9f8326a99b58ef4a73 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 13 Jan 2015 12:44:37 +0900 Subject: i2c: UniPhier: add driver for UniPhier FIFO-builtin i2c controller This commit adds on-chip I2C driver used on newer SoCs of Panasonic UniPhier platform. Signed-off-by: Masahiro Yamada Reviewed-by: Simon Glass Acked-by: Simon Glass Acked-by: Heiko Schocher --- drivers/i2c/Kconfig | 8 + drivers/i2c/Makefile | 1 + drivers/i2c/i2c-uniphier-f.c | 379 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 388 insertions(+) create mode 100644 drivers/i2c/i2c-uniphier-f.c (limited to 'drivers') diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 6a479efd7c6..202ea5d6794 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -12,3 +12,11 @@ config SYS_I2C_UNIPHIER help Support for Panasonic UniPhier I2C controller driver. This I2C controller is used on PH1-LD4, PH1-sLD8 or older UniPhier SoCs. + +config SYS_I2C_UNIPHIER_F + bool "UniPhier FIFO-builtin I2C driver" + depends on ARCH_UNIPHIER && DM_I2C + default y + help + Support for Panasonic UniPhier FIFO-builtin I2C controller driver. + This I2C controller is used on PH1-Pro4 or newer UniPhier SoCs. diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index e2fcd24ef3c..0e4c9f466a1 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -32,4 +32,5 @@ obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o 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 diff --git a/drivers/i2c/i2c-uniphier-f.c b/drivers/i2c/i2c-uniphier-f.c new file mode 100644 index 00000000000..b0d30f76c61 --- /dev/null +++ b/drivers/i2c/i2c-uniphier-f.c @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2014 Panasonic Corporation + * Author: Masahiro Yamada + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct uniphier_fi2c_regs { + u32 cr; /* control register */ +#define I2C_CR_MST (1 << 3) /* master mode */ +#define I2C_CR_STA (1 << 2) /* start condition */ +#define I2C_CR_STO (1 << 1) /* stop condition */ +#define I2C_CR_NACK (1 << 0) /* not ACK */ + u32 dttx; /* send FIFO (write-only) */ +#define dtrx dttx /* receive FIFO (read-only) */ +#define I2C_DTTX_CMD (1 << 8) /* send command (slave addr) */ +#define I2C_DTTX_RD (1 << 0) /* read */ + u32 __reserved; /* no register at offset 0x08 */ + u32 slad; /* slave address */ + u32 cyc; /* clock cycle control */ + u32 lctl; /* clock low period control */ + u32 ssut; /* restart/stop setup time control */ + u32 dsut; /* data setup time control */ + u32 intr; /* interrupt status */ + u32 ie; /* interrupt enable */ + u32 ic; /* interrupt clear */ +#define I2C_INT_TE (1 << 9) /* TX FIFO empty */ +#define I2C_INT_RB (1 << 4) /* received specified bytes */ +#define I2C_INT_NA (1 << 2) /* no answer */ +#define I2C_INT_AL (1 << 1) /* arbitration lost */ + u32 sr; /* status register */ +#define I2C_SR_DB (1 << 12) /* device busy */ +#define I2C_SR_BB (1 << 8) /* bus busy */ +#define I2C_SR_RFF (1 << 3) /* Rx FIFO full */ +#define I2C_SR_RNE (1 << 2) /* Rx FIFO not empty */ +#define I2C_SR_TNF (1 << 1) /* Tx FIFO not full */ +#define I2C_SR_TFE (1 << 0) /* Tx FIFO empty */ + u32 __reserved2; /* no register at offset 0x30 */ + u32 rst; /* reset control */ +#define I2C_RST_TBRST (1 << 2) /* clear Tx FIFO */ +#define I2C_RST_RBRST (1 << 1) /* clear Rx FIFO */ +#define I2C_RST_RST (1 << 0) /* forcible bus reset */ + u32 bm; /* bus monitor */ + u32 noise; /* noise filter control */ + u32 tbc; /* Tx byte count setting */ + u32 rbc; /* Rx byte count setting */ + u32 tbcm; /* Tx byte count monitor */ + u32 rbcm; /* Rx byte count monitor */ + u32 brst; /* bus reset */ +#define I2C_BRST_FOEN (1 << 1) /* normal operation */ +#define I2C_BRST_RSCLO (1 << 0) /* release SCL low fixing */ +}; + +#define FIOCLK 50000000 + +struct uniphier_fi2c_dev { + struct uniphier_fi2c_regs __iomem *regs; /* register base */ + unsigned long fioclk; /* internal operation clock */ + unsigned long timeout; /* time out (us) */ +}; + +static int poll_status(u32 __iomem *reg, u32 flag) +{ + int wait = 1000000; /* 1 sec is long enough */ + + while (readl(reg) & flag) { + if (wait-- < 0) + return -EREMOTEIO; + udelay(1); + } + + return 0; +} + +static int reset_bus(struct uniphier_fi2c_regs __iomem *regs) +{ + int ret; + + /* bus forcible reset */ + writel(I2C_RST_RST, ®s->rst); + ret = poll_status(®s->rst, I2C_RST_RST); + if (ret < 0) + debug("error: fail to reset I2C controller\n"); + + return ret; +} + +static int check_device_busy(struct uniphier_fi2c_regs __iomem *regs) +{ + int ret; + + ret = poll_status(®s->sr, I2C_SR_DB); + if (ret < 0) { + debug("error: device busy too long. reset...\n"); + ret = reset_bus(regs); + } + + return ret; +} + +static int uniphier_fi2c_probe(struct udevice *dev) +{ + fdt_addr_t addr; + fdt_size_t size; + struct uniphier_fi2c_dev *priv = dev_get_priv(dev); + int ret; + + addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", + &size); + + priv->regs = map_sysmem(addr, size); + + if (!priv->regs) + return -ENOMEM; + + priv->fioclk = FIOCLK; + + /* bus forcible reset */ + ret = reset_bus(priv->regs); + if (ret < 0) + return ret; + + writel(I2C_BRST_FOEN | I2C_BRST_RSCLO, &priv->regs->brst); + + return 0; +} + +static int uniphier_fi2c_remove(struct udevice *dev) +{ + struct uniphier_fi2c_dev *priv = dev_get_priv(dev); + + unmap_sysmem(priv->regs); + + return 0; +} + +static int uniphier_fi2c_child_pre_probe(struct udevice *dev) +{ + struct dm_i2c_chip *i2c_chip = dev_get_parentdata(dev); + + if (dev->of_offset == -1) + return 0; + return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, + i2c_chip); +} + +static int wait_for_irq(struct uniphier_fi2c_dev *dev, u32 flags, + bool *stop) +{ + u32 irq; + unsigned long wait = dev->timeout; + int ret = -EREMOTEIO; + + do { + udelay(1); + irq = readl(&dev->regs->intr); + } while (!(irq & flags) && wait--); + + if (wait < 0) { + debug("error: time out\n"); + return ret; + } + + if (irq & I2C_INT_AL) { + debug("error: arbitration lost\n"); + *stop = false; + return ret; + } + + if (irq & I2C_INT_NA) { + debug("error: no answer\n"); + return ret; + } + + return 0; +} + +static int issue_stop(struct uniphier_fi2c_dev *dev, int old_ret) +{ + int ret; + + debug("stop condition\n"); + writel(I2C_CR_MST | I2C_CR_STO, &dev->regs->cr); + + ret = poll_status(&dev->regs->sr, I2C_SR_DB); + if (ret < 0) + debug("error: device busy after operation\n"); + + return old_ret ? old_ret : ret; +} + +static int uniphier_fi2c_transmit(struct uniphier_fi2c_dev *dev, uint addr, + uint len, const u8 *buf, bool *stop) +{ + int ret; + const u32 irq_flags = I2C_INT_TE | I2C_INT_NA | I2C_INT_AL; + struct uniphier_fi2c_regs __iomem *regs = dev->regs; + + debug("%s: addr = %x, len = %d\n", __func__, addr, len); + + writel(I2C_DTTX_CMD | addr << 1, ®s->dttx); + + writel(irq_flags, ®s->ie); + writel(irq_flags, ®s->ic); + + debug("start condition\n"); + writel(I2C_CR_MST | I2C_CR_STA, ®s->cr); + + ret = wait_for_irq(dev, irq_flags, stop); + if (ret < 0) + goto error; + + while (len--) { + debug("sending %x\n", *buf); + writel(*buf++, ®s->dttx); + + writel(irq_flags, ®s->ic); + + ret = wait_for_irq(dev, irq_flags, stop); + if (ret < 0) + goto error; + } + +error: + writel(irq_flags, ®s->ic); + + if (*stop) + ret = issue_stop(dev, ret); + + return ret; +} + +static int uniphier_fi2c_receive(struct uniphier_fi2c_dev *dev, uint addr, + uint len, u8 *buf, bool *stop) +{ + int ret = 0; + const u32 irq_flags = I2C_INT_RB | I2C_INT_NA | I2C_INT_AL; + struct uniphier_fi2c_regs __iomem *regs = dev->regs; + + debug("%s: addr = %x, len = %d\n", __func__, addr, len); + + /* + * In case 'len == 0', only the slave address should be sent + * for probing, which is covered by the transmit function. + */ + if (len == 0) + return uniphier_fi2c_transmit(dev, addr, len, buf, stop); + + writel(I2C_DTTX_CMD | I2C_DTTX_RD | addr << 1, ®s->dttx); + + writel(0, ®s->rbc); + writel(irq_flags, ®s->ie); + writel(irq_flags, ®s->ic); + + debug("start condition\n"); + writel(I2C_CR_MST | I2C_CR_STA | (len == 1 ? I2C_CR_NACK : 0), + ®s->cr); + + while (len--) { + ret = wait_for_irq(dev, irq_flags, stop); + if (ret < 0) + goto error; + + *buf++ = readl(®s->dtrx); + debug("received %x\n", *(buf - 1)); + + if (len == 1) + writel(I2C_CR_MST | I2C_CR_NACK, ®s->cr); + + writel(irq_flags, ®s->ic); + } + +error: + writel(irq_flags, ®s->ic); + + if (*stop) + ret = issue_stop(dev, ret); + + return ret; +} + +static int uniphier_fi2c_xfer(struct udevice *bus, struct i2c_msg *msg, + int nmsgs) +{ + int ret; + struct uniphier_fi2c_dev *dev = dev_get_priv(bus); + bool stop; + + ret = check_device_busy(dev->regs); + if (ret < 0) + return ret; + + for (; nmsgs > 0; nmsgs--, msg++) { + /* If next message is read, skip the stop condition */ + stop = nmsgs > 1 && msg[1].flags & I2C_M_RD ? false : true; + + if (msg->flags & I2C_M_RD) + ret = uniphier_fi2c_receive(dev, msg->addr, msg->len, + msg->buf, &stop); + else + ret = uniphier_fi2c_transmit(dev, msg->addr, msg->len, + msg->buf, &stop); + + if (ret < 0) + break; + } + + return ret; +} + +static int uniphier_fi2c_set_bus_speed(struct udevice *bus, unsigned int speed) +{ + int ret; + unsigned int clk_count; + struct uniphier_fi2c_dev *dev = dev_get_priv(bus); + struct uniphier_fi2c_regs __iomem *regs = dev->regs; + + /* max supported frequency is 400 kHz */ + if (speed > 400000) + return -EINVAL; + + ret = check_device_busy(dev->regs); + if (ret < 0) + return ret; + + /* make sure the bus is idle when changing the frequency */ + writel(I2C_BRST_RSCLO, ®s->brst); + + clk_count = dev->fioclk / speed; + + writel(clk_count, ®s->cyc); + writel(clk_count / 2, ®s->lctl); + writel(clk_count / 2, ®s->ssut); + writel(clk_count / 16, ®s->dsut); + + writel(I2C_BRST_FOEN | I2C_BRST_RSCLO, ®s->brst); + + /* + * Theoretically, each byte can be transferred in + * 1000000 * 9 / speed usec. + * This time out value is long enough. + */ + dev->timeout = 100000000L / speed; + + return 0; +} + +static const struct dm_i2c_ops uniphier_fi2c_ops = { + .xfer = uniphier_fi2c_xfer, + .set_bus_speed = uniphier_fi2c_set_bus_speed, +}; + +static const struct udevice_id uniphier_fi2c_of_match[] = { + { .compatible = "panasonic,uniphier-fi2c" }, + {}, +}; + +U_BOOT_DRIVER(uniphier_fi2c) = { + .name = "uniphier-fi2c", + .id = UCLASS_I2C, + .of_match = uniphier_fi2c_of_match, + .probe = uniphier_fi2c_probe, + .remove = uniphier_fi2c_remove, + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), + .child_pre_probe = uniphier_fi2c_child_pre_probe, + .priv_auto_alloc_size = sizeof(struct uniphier_fi2c_dev), + .ops = &uniphier_fi2c_ops, +}; -- cgit v1.3.1 From ae7123f876357a81fc4c393239a378f1058cad5e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:27 -0700 Subject: dm: gpio: Add a native driver model API So far driver model's GPIO uclass just implements the existing GPIO API. This has some limitations: - it requires manual device tree munging to support GPIOs in device tree (fdtdec_get_gpio() and friends) - it does not understand polarity - it is somewhat slower since we must scan for the GPIO device each time - Global GPIO numbering can change if other GPIO drivers are probed - it requires extra steps to set the GPIO direction and value The new functions have a dm_ prefix where necessary to avoid name conflicts but we can remove that when it is no-longer needed. The new struct gpio_desc holds all required information about the GPIO. For now this is intended to be stored by the client requesting the GPIO, but in future it might be brought into the uclass in some way. With these changes the old GPIO API still works, and uses the driver model API underneath. Signed-off-by: Simon Glass --- drivers/gpio/gpio-uclass.c | 218 ++++++++++++++++++++++++++++++--------------- include/asm-generic/gpio.h | 16 ++++ 2 files changed, 163 insertions(+), 71 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 255700ab18d..2f3c36b48ff 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -13,14 +13,16 @@ /** * gpio_to_device() - Convert global GPIO number to device, number - * gpio: The numeric representation of the GPIO * * Convert the GPIO number to an entry in the list of GPIOs * or GPIO blocks registered with the GPIO controller. Returns * entry on success, NULL on error. + * + * @gpio: The numeric representation of the GPIO + * @desc: Returns description (desc->flags will always be 0) + * @return 0 if found, -ENOENT if not found */ -static int gpio_to_device(unsigned int gpio, struct udevice **devp, - unsigned int *offset) +static int gpio_to_device(unsigned int gpio, struct gpio_desc *desc) { struct gpio_dev_priv *uc_priv; struct udevice *dev; @@ -32,14 +34,15 @@ static int gpio_to_device(unsigned int gpio, struct udevice **devp, uc_priv = dev->uclass_priv; if (gpio >= uc_priv->gpio_base && gpio < uc_priv->gpio_base + uc_priv->gpio_count) { - *devp = dev; - *offset = gpio - uc_priv->gpio_base; + desc->dev = dev; + desc->offset = gpio - uc_priv->gpio_base; + desc->flags = 0; return 0; } } /* No such GPIO */ - return ret ? ret : -EINVAL; + return ret ? ret : -ENOENT; } int gpio_lookup_name(const char *name, struct udevice **devp, @@ -88,6 +91,31 @@ int gpio_lookup_name(const char *name, struct udevice **devp, return 0; } +static int dm_gpio_request(struct gpio_desc *desc, const char *label) +{ + struct udevice *dev = desc->dev; + struct gpio_dev_priv *uc_priv; + char *str; + int ret; + + uc_priv = dev->uclass_priv; + if (uc_priv->name[desc->offset]) + return -EBUSY; + str = strdup(label); + if (!str) + return -ENOMEM; + if (gpio_get_ops(dev)->request) { + ret = gpio_get_ops(dev)->request(dev, desc->offset, label); + if (ret) { + free(str); + return ret; + } + } + uc_priv->name[desc->offset] = str; + + return 0; +} + /** * gpio_request() - [COMPAT] Request GPIO * gpio: GPIO number @@ -102,32 +130,14 @@ int gpio_lookup_name(const char *name, struct udevice **devp, */ int gpio_request(unsigned gpio, const char *label) { - struct gpio_dev_priv *uc_priv; - unsigned int offset; - struct udevice *dev; - char *str; + struct gpio_desc desc; int ret; - ret = gpio_to_device(gpio, &dev, &offset); + ret = gpio_to_device(gpio, &desc); if (ret) return ret; - uc_priv = dev->uclass_priv; - if (uc_priv->name[offset]) - return -EBUSY; - str = strdup(label); - if (!str) - return -ENOMEM; - if (gpio_get_ops(dev)->request) { - ret = gpio_get_ops(dev)->request(dev, offset, label); - if (ret) { - free(str); - return ret; - } - } - uc_priv->name[offset] = str; - - return 0; + return dm_gpio_request(&desc, label); } /** @@ -151,25 +161,11 @@ int gpio_requestf(unsigned gpio, const char *fmt, ...) return gpio_request(gpio, buf); } -/** - * gpio_free() - [COMPAT] Relinquish GPIO - * gpio: GPIO number - * - * This function implements the API that's compatible with current - * GPIO API used in U-Boot. The request is forwarded to particular - * GPIO driver. Returns 0 on success, negative value on error. - */ -int gpio_free(unsigned gpio) +int _dm_gpio_free(struct udevice *dev, uint offset) { struct gpio_dev_priv *uc_priv; - unsigned int offset; - struct udevice *dev; int ret; - ret = gpio_to_device(gpio, &dev, &offset); - if (ret) - return ret; - uc_priv = dev->uclass_priv; if (!uc_priv->name[offset]) return -ENXIO; @@ -185,15 +181,35 @@ int gpio_free(unsigned gpio) return 0; } -static int check_reserved(struct udevice *dev, unsigned offset, - const char *func) +/** + * gpio_free() - [COMPAT] Relinquish GPIO + * gpio: GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_free(unsigned gpio) { - struct gpio_dev_priv *uc_priv = dev->uclass_priv; + struct gpio_desc desc; + int ret; + + ret = gpio_to_device(gpio, &desc); + if (ret) + return ret; + + return _dm_gpio_free(desc.dev, desc.offset); +} - if (!uc_priv->name[offset]) { +static int check_reserved(struct gpio_desc *desc, const char *func) +{ + struct gpio_dev_priv *uc_priv = desc->dev->uclass_priv; + + if (!uc_priv->name[desc->offset]) { printf("%s: %s: error: gpio %s%d not reserved\n", - dev->name, func, - uc_priv->bank_name ? uc_priv->bank_name : "", offset); + desc->dev->name, func, + uc_priv->bank_name ? uc_priv->bank_name : "", + desc->offset); return -EBUSY; } @@ -210,16 +226,17 @@ static int check_reserved(struct udevice *dev, unsigned offset, */ int gpio_direction_input(unsigned gpio) { - unsigned int offset; - struct udevice *dev; + struct gpio_desc desc; int ret; - ret = gpio_to_device(gpio, &dev, &offset); + ret = gpio_to_device(gpio, &desc); + if (ret) + return ret; + ret = check_reserved(&desc, "dir_input"); if (ret) return ret; - ret = check_reserved(dev, offset, "dir_input"); - return ret ? ret : gpio_get_ops(dev)->direction_input(dev, offset); + return gpio_get_ops(desc.dev)->direction_input(desc.dev, desc.offset); } /** @@ -233,17 +250,81 @@ int gpio_direction_input(unsigned gpio) */ int gpio_direction_output(unsigned gpio, int value) { - unsigned int offset; - struct udevice *dev; + struct gpio_desc desc; int ret; - ret = gpio_to_device(gpio, &dev, &offset); + ret = gpio_to_device(gpio, &desc); + if (ret) + return ret; + ret = check_reserved(&desc, "dir_output"); + if (ret) + return ret; + + return gpio_get_ops(desc.dev)->direction_output(desc.dev, + desc.offset, value); +} + +int dm_gpio_get_value(struct gpio_desc *desc) +{ + int value; + int ret; + + ret = check_reserved(desc, "get_value"); if (ret) return ret; - ret = check_reserved(dev, offset, "dir_output"); - return ret ? ret : - gpio_get_ops(dev)->direction_output(dev, offset, value); + value = gpio_get_ops(desc->dev)->get_value(desc->dev, desc->offset); + + return desc->flags & GPIOD_ACTIVE_LOW ? !value : value; +} + +int dm_gpio_set_value(struct gpio_desc *desc, int value) +{ + int ret; + + ret = check_reserved(desc, "set_value"); + if (ret) + return ret; + + if (desc->flags & GPIOD_ACTIVE_LOW) + value = !value; + gpio_get_ops(desc->dev)->set_value(desc->dev, desc->offset, value); + return 0; +} + +int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags) +{ + struct udevice *dev = desc->dev; + struct dm_gpio_ops *ops = gpio_get_ops(dev); + int ret; + + ret = check_reserved(desc, "set_dir"); + if (ret) + return ret; + + if (flags & GPIOD_IS_OUT) { + int value = flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0; + + if (flags & GPIOD_ACTIVE_LOW) + value = !value; + ret = ops->direction_output(dev, desc->offset, value); + } else if (flags & GPIOD_IS_IN) { + ret = ops->direction_input(dev, desc->offset); + } + if (ret) + return ret; + /* + * Update desc->flags here, so that GPIO_ACTIVE_LOW is honoured in + * futures + */ + desc->flags = flags; + + return 0; +} + +int dm_gpio_set_dir(struct gpio_desc *desc) +{ + return dm_gpio_set_dir_flags(desc, desc->flags); } /** @@ -257,16 +338,14 @@ int gpio_direction_output(unsigned gpio, int value) */ int gpio_get_value(unsigned gpio) { - unsigned int offset; - struct udevice *dev; int ret; - ret = gpio_to_device(gpio, &dev, &offset); + struct gpio_desc desc; + + ret = gpio_to_device(gpio, &desc); if (ret) return ret; - ret = check_reserved(dev, offset, "get_value"); - - return ret ? ret : gpio_get_ops(dev)->get_value(dev, offset); + return dm_gpio_get_value(&desc); } /** @@ -280,16 +359,13 @@ int gpio_get_value(unsigned gpio) */ int gpio_set_value(unsigned gpio, int value) { - unsigned int offset; - struct udevice *dev; + struct gpio_desc desc; int ret; - ret = gpio_to_device(gpio, &dev, &offset); + ret = gpio_to_device(gpio, &desc); if (ret) return ret; - ret = check_reserved(dev, offset, "set_value"); - - return ret ? ret : gpio_get_ops(dev)->set_value(dev, offset, value); + return dm_gpio_set_value(&desc, value); } const char *gpio_get_bank_info(struct udevice *dev, int *bit_count) diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 36a36c64b8a..a827a56f5e1 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -95,6 +95,22 @@ enum gpio_func_t { struct udevice; +struct gpio_desc { + struct udevice *dev; /* Device, NULL for invalid GPIO */ + unsigned long flags; +#define GPIOD_REQUESTED (1 << 0) /* Requested/claimed */ +#define GPIOD_IS_OUT (1 << 1) /* GPIO is an output */ +#define GPIOD_IS_IN (1 << 2) /* GPIO is an output */ +#define GPIOD_ACTIVE_LOW (1 << 3) /* value has active low */ +#define GPIOD_IS_OUT_ACTIVE (1 << 4) /* set output active */ + + uint offset; /* GPIO offset within the device */ + /* + * We could consider adding the GPIO label in here. Possibly we could + * use this structure for internal GPIO information. + */ +}; + /** * gpio_get_status() - get the current GPIO status as a string * -- cgit v1.3.1 From 0dac4d51f50e9252dbc00075cf65eeba57017926 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:28 -0700 Subject: dm: gpio: Add a driver GPIO translation method Only the GPIO driver knows about the full GPIO device tree binding used by a device. Add a method to allow the driver to provide this information to the uclass, including the GPIO offset within the device and flags such as the polarity. Signed-off-by: Simon Glass --- drivers/gpio/gpio-uclass.c | 16 ++++++++++++++++ include/asm-generic/gpio.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 2f3c36b48ff..0a4d9e42178 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,21 @@ int gpio_lookup_name(const char *name, struct udevice **devp, return 0; } +int gpio_find_and_xlate(struct gpio_desc *desc, + struct fdtdec_phandle_args *args) +{ + struct dm_gpio_ops *ops = gpio_get_ops(desc->dev); + + /* Use the first argument as the offset by default */ + if (args->args_count > 0) + desc->offset = args->args[0]; + else + desc->offset = -1; + desc->flags = 0; + + return ops->xlate ? ops->xlate(desc->dev, desc, args) : 0; +} + static int dm_gpio_request(struct gpio_desc *desc, const char *label) { struct udevice *dev = desc->dev; diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index a827a56f5e1..c08c96340af 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -171,6 +171,8 @@ int gpio_get_raw_function(struct udevice *dev, int offset, const char **namep); int gpio_requestf(unsigned gpio, const char *fmt, ...) __attribute__ ((format (__printf__, 2, 3))); +struct fdtdec_phandle_args; + /** * struct struct dm_gpio_ops - Driver model GPIO operations * @@ -214,6 +216,33 @@ struct dm_gpio_ops { * @return current function - GPIOF_... */ int (*get_function)(struct udevice *dev, unsigned offset); + + /** + * xlate() - Translate phandle arguments into a GPIO description + * + * This function should set up the fields in desc according to the + * information in the arguments. The uclass will have set up: + * + * @desc->dev to @dev + * @desc->flags to 0 + * @desc->offset to the value of the first argument in args, if any, + * otherwise -1 (which is invalid) + * + * This method is optional so if the above defaults suit it can be + * omitted. Typical behaviour is to set up the GPIOD_ACTIVE_LOW flag + * in desc->flags. + * + * Note that @dev is passed in as a parameter to follow driver model + * uclass conventions, even though it is already available as + * desc->dev. + * + * @dev: GPIO device + * @desc: Place to put GPIO description + * @args: Arguments provided in descripion + * @return 0 if OK, -ve on error + */ + int (*xlate)(struct udevice *dev, struct gpio_desc *desc, + struct fdtdec_phandle_args *args); }; /** -- cgit v1.3.1 From 3669e0e759118fed3d371e2427b4b47d3969bfd0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:29 -0700 Subject: dm: gpio: Add better functions to request GPIOs At present U-Boot sort-of supports the standard way of reading GPIOs from device tree nodes, but the support is incomplete, a bit clunky and only works for GPIO bindings where #gpio-cells is 2. Add new functions to request GPIOs, taking full account of the device tree binding. These permit requesting a GPIO with a simple call like: gpio_request_by_name(dev, "cd-gpios", 0, &desc, GPIOD_IS_IN); This will request the GPIO, looking at the device's node which might be this, for example: cd-gpios = <&gpio TEGRA_GPIO(B, 3) GPIO_ACTIVE_LOW>; The GPIO will be set to input mode in this case and polarity will be honoured by the GPIO calls. It is also possible to request and free a list of GPIOs. Signed-off-by: Simon Glass --- drivers/gpio/gpio-uclass.c | 166 ++++++++++++++++++++++++++++++++++++- drivers/gpio/sandbox.c | 20 +++++ include/asm-generic/gpio.h | 199 +++++++++++++++++++++++++++++++++++++++++++++ test/dm/gpio.c | 69 +++++++++++++++- test/dm/test.dts | 11 ++- 5 files changed, 461 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 0a4d9e42178..a69bbd2002e 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -12,6 +12,8 @@ #include #include +DECLARE_GLOBAL_DATA_PTR; + /** * gpio_to_device() - Convert global GPIO number to device, number * @@ -92,8 +94,8 @@ int gpio_lookup_name(const char *name, struct udevice **devp, return 0; } -int gpio_find_and_xlate(struct gpio_desc *desc, - struct fdtdec_phandle_args *args) +static int gpio_find_and_xlate(struct gpio_desc *desc, + struct fdtdec_phandle_args *args) { struct dm_gpio_ops *ops = gpio_get_ops(desc->dev); @@ -132,6 +134,17 @@ static int dm_gpio_request(struct gpio_desc *desc, const char *label) return 0; } +static int dm_gpio_requestf(struct gpio_desc *desc, const char *fmt, ...) +{ + va_list args; + char buf[40]; + + va_start(args, fmt); + vscnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + return dm_gpio_request(desc, buf); +} + /** * gpio_request() - [COMPAT] Request GPIO * gpio: GPIO number @@ -501,6 +514,155 @@ unsigned gpio_get_values_as_int(const int *gpio_num_array) return vector; } +static int _gpio_request_by_name_nodev(const void *blob, int node, + const char *list_name, int index, + struct gpio_desc *desc, int flags, + bool add_index) +{ + struct fdtdec_phandle_args args; + int ret; + + desc->dev = NULL; + desc->offset = 0; + ret = fdtdec_parse_phandle_with_args(blob, node, list_name, + "#gpio-cells", 0, index, &args); + if (ret) { + debug("%s: fdtdec_parse_phandle_with_args failed\n", __func__); + goto err; + } + + ret = uclass_get_device_by_of_offset(UCLASS_GPIO, args.node, + &desc->dev); + if (ret) { + debug("%s: uclass_get_device_by_of_offset failed\n", __func__); + goto err; + } + ret = gpio_find_and_xlate(desc, &args); + if (ret) { + debug("%s: gpio_find_and_xlate failed\n", __func__); + goto err; + } + ret = dm_gpio_requestf(desc, add_index ? "%s.%s%d" : "%s.%s", + fdt_get_name(blob, node, NULL), + list_name, index); + if (ret) { + debug("%s: dm_gpio_requestf failed\n", __func__); + goto err; + } + ret = dm_gpio_set_dir_flags(desc, flags | desc->flags); + if (ret) { + debug("%s: dm_gpio_set_dir failed\n", __func__); + goto err; + } + + return 0; +err: + debug("%s: Node '%s', property '%s', failed to request GPIO index %d: %d\n", + __func__, fdt_get_name(blob, node, NULL), list_name, index, ret); + return ret; +} + +int gpio_request_by_name_nodev(const void *blob, int node, + const char *list_name, int index, + struct gpio_desc *desc, int flags) +{ + return _gpio_request_by_name_nodev(blob, node, list_name, index, desc, + flags, index > 0); +} + +int gpio_request_by_name(struct udevice *dev, const char *list_name, int index, + struct gpio_desc *desc, int flags) +{ + /* + * This isn't ideal since we don't use dev->name in the debug() + * calls in gpio_request_by_name(), but we can do this until + * gpio_request_by_name_nodev() can be dropped. + */ + return gpio_request_by_name_nodev(gd->fdt_blob, dev->of_offset, + list_name, index, desc, flags); +} + +int gpio_request_list_by_name_nodev(const void *blob, int node, + const char *list_name, + struct gpio_desc *desc, int max_count, + int flags) +{ + int count; + int ret; + + for (count = 0; ; count++) { + if (count >= max_count) { + ret = -ENOSPC; + goto err; + } + ret = _gpio_request_by_name_nodev(blob, node, list_name, count, + &desc[count], flags, true); + if (ret == -ENOENT) + break; + else if (ret) + goto err; + } + + /* We ran out of GPIOs in the list */ + return count; + +err: + gpio_free_list_nodev(desc, count - 1); + + return ret; +} + +int gpio_request_list_by_name(struct udevice *dev, const char *list_name, + struct gpio_desc *desc, int max_count, + int flags) +{ + /* + * This isn't ideal since we don't use dev->name in the debug() + * calls in gpio_request_by_name(), but we can do this until + * gpio_request_list_by_name_nodev() can be dropped. + */ + return gpio_request_list_by_name_nodev(gd->fdt_blob, dev->of_offset, + list_name, desc, max_count, + flags); +} + +int gpio_get_list_count(struct udevice *dev, const char *list_name) +{ + int ret; + + ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev->of_offset, + list_name, "#gpio-cells", 0, -1, + NULL); + if (ret) { + debug("%s: Node '%s', property '%s', GPIO count failed: %d\n", + __func__, dev->name, list_name, ret); + } + + return ret; +} + +int dm_gpio_free(struct udevice *dev, struct gpio_desc *desc) +{ + /* For now, we don't do any checking of dev */ + return _dm_gpio_free(desc->dev, desc->offset); +} + +int gpio_free_list(struct udevice *dev, struct gpio_desc *desc, int count) +{ + int i; + + /* For now, we don't do any checking of dev */ + for (i = 0; i < count; i++) + dm_gpio_free(dev, &desc[i]); + + return 0; +} + +int gpio_free_list_nodev(struct gpio_desc *desc, int count) +{ + return gpio_free_list(NULL, desc, count); +} + /* We need to renumber the GPIOs when any driver is probed/removed */ static int gpio_renumber(struct udevice *removed_dev) { diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c index 53c80d5be65..d564c252c7d 100644 --- a/drivers/gpio/sandbox.c +++ b/drivers/gpio/sandbox.c @@ -8,6 +8,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -130,12 +131,31 @@ static int sb_gpio_get_function(struct udevice *dev, unsigned offset) return GPIOF_INPUT; } +static int sb_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, + struct fdtdec_phandle_args *args) +{ + desc->offset = args->args[0]; + if (args->args_count < 2) + return 0; + if (args->args[1] & GPIO_ACTIVE_LOW) + desc->flags |= GPIOD_ACTIVE_LOW; + if (args->args[1] & 2) + desc->flags |= GPIOD_IS_IN; + if (args->args[1] & 4) + desc->flags |= GPIOD_IS_OUT; + if (args->args[1] & 8) + desc->flags |= GPIOD_IS_OUT_ACTIVE; + + return 0; +} + static const struct dm_gpio_ops gpio_sandbox_ops = { .direction_input = sb_gpio_direction_input, .direction_output = sb_gpio_direction_output, .get_value = sb_gpio_get_value, .set_value = sb_gpio_set_value, .get_function = sb_gpio_get_function, + .xlate = sb_gpio_xlate, }; static int sandbox_gpio_ofdata_to_platdata(struct udevice *dev) diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index c08c96340af..26534150f1f 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -111,6 +111,18 @@ struct gpio_desc { */ }; +/** + * dm_gpio_is_valid() - Check if a GPIO is gpio_is_valie + * + * @desc: GPIO description containing device, offset and flags, + * previously returned by gpio_request_by_name() + * @return true if valid, false if not + */ +static inline bool dm_gpio_is_valid(struct gpio_desc *desc) +{ + return desc->dev != NULL; +} + /** * gpio_get_status() - get the current GPIO status as a string * @@ -313,4 +325,191 @@ int gpio_lookup_name(const char *name, struct udevice **devp, */ unsigned gpio_get_values_as_int(const int *gpio_list); +/** + * gpio_request_by_name() - Locate and request a GPIO by name + * + * This operates by looking up the given list name in the device (device + * tree property) and requesting the GPIO for use. The property must exist + * in @dev's node. + * + * Use @flags to specify whether the GPIO should be an input or output. In + * principle this can also come from the device tree binding but most + * bindings don't provide this information. Specifically, when the GPIO uclass + * calls the xlate() method, it can return default flags, which are then + * ORed with this @flags. + * + * If we find that requesting the GPIO is not always needed we could add a + * new function or a new GPIOD_NO_REQUEST flag. + * + * At present driver model has no reference counting so if one device + * requests a GPIO which subsequently is unbound, the @desc->dev pointer + * will be invalid. However this will only happen if the GPIO device is + * unbound, not if it is removed, so this seems like a reasonable limitation + * for now. There is no real use case for unbinding drivers in normal + * operation. + * + * The device tree binding is doc/device-tree-bindings/gpio/gpio.txt in + * generate terms and each specific device may add additional details in + * a binding file in the same directory. + * + * @dev: Device requesting the GPIO + * @list_name: Name of GPIO list (e.g. "board-id-gpios") + * @index: Index number of the GPIO in that list use request (0=first) + * @desc: Returns GPIO description information. If there is no such + * GPIO, dev->dev will be NULL. + * @flags: Indicates the GPIO input/output settings (GPIOD_...) + * @return 0 if OK, -ENOENT if the GPIO does not exist, -EINVAL if there is + * something wrong with the list, or other -ve for another error (e.g. + * -EBUSY if a GPIO was already requested) + */ +int gpio_request_by_name(struct udevice *dev, const char *list_name, + int index, struct gpio_desc *desc, int flags); + +/** + * gpio_request_list_by_name() - Request a list of GPIOs + * + * Reads all the GPIOs from a list and requetss them. See + * gpio_request_by_name() for additional details. Lists should not be + * misused to hold unrelated or optional GPIOs. They should only be used + * for things like parallel data lines. A zero phandle terminates the list + * the list. + * + * This function will either succeed, and request all GPIOs in the list, or + * fail and request none (it will free already-requested GPIOs in case of + * an error part-way through). + * + * @dev: Device requesting the GPIO + * @list_name: Name of GPIO list (e.g. "board-id-gpios") + * @desc_list: Returns a list of GPIO description information + * @max_count: Maximum number of GPIOs to return (@desc_list must be at least + * this big) + * @flags: Indicates the GPIO input/output settings (GPIOD_...) + * @return number of GPIOs requested, or -ve on error + */ +int gpio_request_list_by_name(struct udevice *dev, const char *list_name, + struct gpio_desc *desc_list, int max_count, + int flags); + +/** + * gpio_get_list_count() - Returns the number of GPIOs in a list + * + * Counts the GPIOs in a list. See gpio_request_by_name() for additional + * details. + * + * @dev: Device requesting the GPIO + * @list_name: Name of GPIO list (e.g. "board-id-gpios") + * @return number of GPIOs (0 for an empty property) or -ENOENT if the list + * does not exist + */ +int gpio_get_list_count(struct udevice *dev, const char *list_name); + +/** + * gpio_request_by_name_nodev() - request GPIOs without a device + * + * This is a version of gpio_request_list_by_name() that does not use a + * device. Avoid it unless the caller is not yet using driver model + */ +int gpio_request_by_name_nodev(const void *blob, int node, + const char *list_name, + int index, struct gpio_desc *desc, int flags); + +/** + * gpio_request_list_by_name_nodev() - request GPIOs without a device + * + * This is a version of gpio_request_list_by_name() that does not use a + * device. Avoid it unless the caller is not yet using driver model + */ +int gpio_request_list_by_name_nodev(const void *blob, int node, + const char *list_name, + struct gpio_desc *desc_list, int max_count, + int flags); + +/** + * dm_gpio_free() - Free a single GPIO + * + * This frees a single GPIOs previously returned from gpio_request_by_name(). + * + * @dev: Device which requested the GPIO + * @desc: GPIO to free + * @return 0 if OK, -ve on error + */ +int dm_gpio_free(struct udevice *dev, struct gpio_desc *desc); + +/** + * gpio_free_list() - Free a list of GPIOs + * + * This frees a list of GPIOs previously returned from + * gpio_request_list_by_name(). + * + * @dev: Device which requested the GPIOs + * @desc: List of GPIOs to free + * @count: Number of GPIOs in the list + * @return 0 if OK, -ve on error + */ +int gpio_free_list(struct udevice *dev, struct gpio_desc *desc, int count); + +/** + * gpio_free_list_nodev() - free GPIOs without a device + * + * This is a version of gpio_free_list() that does not use a + * device. Avoid it unless the caller is not yet using driver model + */ +int gpio_free_list_nodev(struct gpio_desc *desc, int count); + +/** + * dm_gpio_get_value() - Get the value of a GPIO + * + * This is the driver model version of the existing gpio_get_value() function + * and should be used instead of that. + * + * For now, these functions have a dm_ prefix since they conflict with + * existing names. + * + * @desc: GPIO description containing device, offset and flags, + * previously returned by gpio_request_by_name() + * @return GPIO value (0 for inactive, 1 for active) or -ve on error + */ +int dm_gpio_get_value(struct gpio_desc *desc); + +int dm_gpio_set_value(struct gpio_desc *desc, int value); + +/** + * dm_gpio_set_dir() - Set the direction for a GPIO + * + * This sets up the direction according tot the provided flags. It will do + * nothing unless the direction is actually specified. + * + * @desc: GPIO description containing device, offset and flags, + * previously returned by gpio_request_by_name() + * @return 0 if OK, -ve on error + */ +int dm_gpio_set_dir(struct gpio_desc *desc); + +/** + * dm_gpio_set_dir_flags() - Set direction using specific flags + * + * This is like dm_gpio_set_dir() except that the flags value is provided + * instead of being used from desc->flags. This is needed because in many + * cases the GPIO description does not include direction information. + * Note that desc->flags is updated by this function. + * + * @desc: GPIO description containing device, offset and flags, + * previously returned by gpio_request_by_name() + * @flags: New flags to use + * @return 0 if OK, -ve on error, in which case desc->flags is not updated + */ +int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags); + +/** + * gpio_get_number() - Get the global GPIO number of a GPIO + * + * This should only be used for debugging or interest. It returns the nummber + * that should be used for gpio_get_value() etc. to access this GPIO. + * + * @desc: GPIO description containing device, offset and flags, + * previously returned by gpio_request_by_name() + * @return GPIO number, or -ve if not found + */ +int gpio_get_number(struct gpio_desc *desc); + #endif /* _ASM_GENERIC_GPIO_H_ */ diff --git a/test/dm/gpio.c b/test/dm/gpio.c index 94bd0d99dc0..b29daf1af4e 100644 --- a/test/dm/gpio.c +++ b/test/dm/gpio.c @@ -174,5 +174,72 @@ static int dm_test_gpio_leak(struct dm_test_state *dms) return 0; } - DM_TEST(dm_test_gpio_leak, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test that we can find GPIOs using phandles */ +static int dm_test_gpio_phandles(struct dm_test_state *dms) +{ + struct gpio_desc desc, desc_list[8], desc_list2[8]; + struct udevice *dev, *gpio_a, *gpio_b; + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + + ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio_a)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 2, &gpio_b)); + ut_asserteq_str("base-gpios", gpio_a->name); + ut_asserteq(true, !!device_active(gpio_a)); + ut_asserteq_ptr(gpio_a, desc.dev); + ut_asserteq(4, desc.offset); + /* GPIOF_INPUT is the sandbox GPIO driver default */ + ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_a, 4, NULL)); + ut_assertok(dm_gpio_free(dev, &desc)); + + ut_asserteq(-ENOENT, gpio_request_by_name(dev, "test-gpios", 3, &desc, + 0)); + ut_asserteq_ptr(NULL, desc.dev); + ut_asserteq(desc.offset, 0); + ut_asserteq(-ENOENT, gpio_request_by_name(dev, "test-gpios", 5, &desc, + 0)); + + /* Last GPIO is ignord as it comes after <0> */ + ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc_list, + ARRAY_SIZE(desc_list), 0)); + ut_asserteq(-EBUSY, gpio_request_list_by_name(dev, "test-gpios", + desc_list2, + ARRAY_SIZE(desc_list2), + 0)); + ut_assertok(gpio_free_list(dev, desc_list, 3)); + ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc_list, + ARRAY_SIZE(desc_list), + GPIOD_IS_OUT | + GPIOD_IS_OUT_ACTIVE)); + ut_asserteq_ptr(gpio_a, desc_list[0].dev); + ut_asserteq(1, desc_list[0].offset); + ut_asserteq_ptr(gpio_a, desc_list[1].dev); + ut_asserteq(4, desc_list[1].offset); + ut_asserteq_ptr(gpio_b, desc_list[2].dev); + ut_asserteq(5, desc_list[2].offset); + ut_asserteq(1, dm_gpio_get_value(desc_list)); + ut_assertok(gpio_free_list(dev, desc_list, 3)); + + ut_asserteq(6, gpio_request_list_by_name(dev, "test2-gpios", desc_list, + ARRAY_SIZE(desc_list), 0)); + /* This was set to output previously, so still will be */ + ut_asserteq(GPIOF_OUTPUT, gpio_get_function(gpio_a, 1, NULL)); + + /* Active low should invert the input value */ + ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_b, 6, NULL)); + ut_asserteq(1, dm_gpio_get_value(&desc_list[2])); + + ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_b, 7, NULL)); + ut_asserteq(GPIOF_OUTPUT, gpio_get_function(gpio_b, 8, NULL)); + ut_asserteq(0, dm_gpio_get_value(&desc_list[4])); + ut_asserteq(GPIOF_OUTPUT, gpio_get_function(gpio_b, 9, NULL)); + ut_asserteq(1, dm_gpio_get_value(&desc_list[5])); + + + return 0; +} +DM_TEST(dm_test_gpio_phandles, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/test.dts b/test/dm/test.dts index fb0272a59cd..33f2c00747d 100644 --- a/test/dm/test.dts +++ b/test/dm/test.dts @@ -22,6 +22,11 @@ ping-expect = <0>; ping-add = <0>; u-boot,dm-pre-reloc; + test-gpios = <&gpio_a 1>, <&gpio_a 4>, <&gpio_b 5 0 3 2 1>, + <0>, <&gpio_a 12>; + test2-gpios = <&gpio_a 1>, <&gpio_a 4>, <&gpio_b 6 1 3 2 1>, + <&gpio_b 7 2 3 2 1>, <&gpio_b 8 4 3 2 1>, + <&gpio_b 9 0xc 3 2 1>; }; junk { @@ -83,12 +88,16 @@ gpio_a: base-gpios { compatible = "sandbox,gpio"; + gpio-controller; + #gpio-cells = <1>; gpio-bank-name = "a"; num-gpios = <20>; }; - extra-gpios { + gpio_b: extra-gpios { compatible = "sandbox,gpio"; + gpio-controller; + #gpio-cells = <5>; gpio-bank-name = "b"; num-gpios = <10>; }; -- cgit v1.3.1 From a02af4aeece40e93bc7d79cd5dc912409efb7020 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:31 -0700 Subject: dm: demo: Add a simple GPIO demonstration Add a new 'demo light' command which uses GPIOs to control imaginary lights. Each light is assigned a bit number in the overall value. This provides an example driver for using the new GPIO API. Signed-off-by: Simon Glass --- arch/sandbox/dts/sandbox.dts | 11 ++++++- common/cmd_demo.c | 29 +++++++++++++++++- drivers/demo/demo-shape.c | 71 ++++++++++++++++++++++++++++++++++++++++++++ drivers/demo/demo-uclass.c | 20 +++++++++++++ include/dm-demo.h | 4 +++ 5 files changed, 133 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index 11748aec799..4c63e4f9ede 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -19,6 +19,7 @@ colour = "cyan"; sides = <3>; character = <83>; + light-gpios = <&gpio_a 2>, <&gpio_b 6 0>; }; square { compatible = "demo-shape"; @@ -126,7 +127,7 @@ 0x070b0067 0x070c0069>; }; - gpio_a: gpios { + gpio_a: gpios@0 { gpio-controller; compatible = "sandbox,gpio"; #gpio-cells = <1>; @@ -134,6 +135,14 @@ num-gpios = <20>; }; + gpio_b: gpios@1 { + gpio-controller; + compatible = "sandbox,gpio"; + #gpio-cells = <2>; + gpio-bank-name = "b"; + num-gpios = <10>; + }; + i2c@0 { #address-cells = <1>; #size-cells = <0>; diff --git a/common/cmd_demo.c b/common/cmd_demo.c index 652c61c7077..bcb34d90456 100644 --- a/common/cmd_demo.c +++ b/common/cmd_demo.c @@ -39,6 +39,26 @@ static int do_demo_status(cmd_tbl_t *cmdtp, int flag, int argc, return 0; } +static int do_demo_light(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int light; + int ret; + + if (argc) { + light = simple_strtoul(argv[0], NULL, 16); + ret = demo_set_light(demo_dev, light); + } else { + ret = demo_get_light(demo_dev); + if (ret >= 0) { + printf("Light: %x\n", ret); + ret = 0; + } + } + + return ret; +} + int do_demo_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { struct udevice *dev; @@ -61,6 +81,7 @@ int do_demo_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) static cmd_tbl_t demo_commands[] = { U_BOOT_CMD_MKENT(list, 0, 1, do_demo_list, "", ""), U_BOOT_CMD_MKENT(hello, 2, 1, do_demo_hello, "", ""), + U_BOOT_CMD_MKENT(light, 2, 1, do_demo_light, "", ""), U_BOOT_CMD_MKENT(status, 1, 1, do_demo_status, "", ""), }; @@ -86,6 +107,10 @@ static int do_demo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return cmd_process_error(cmdtp, ret); argc--; argv++; + } else { + demo_dev = NULL; + if (demo_cmd->cmd != do_demo_list) + return CMD_RET_USAGE; } ret = demo_cmd->cmd(demo_cmd, flag, argc, argv); @@ -98,5 +123,7 @@ U_BOOT_CMD( "Driver model (dm) demo operations", "list List available demo devices\n" "demo hello [] Say hello\n" - "demo status Get demo device status" + "demo light [] Set or get the lights\n" + "demo status Get demo device status\n" + "demo list List available demo devices" ); diff --git a/drivers/demo/demo-shape.c b/drivers/demo/demo-shape.c index 3fa9c599470..d908736cffe 100644 --- a/drivers/demo/demo-shape.c +++ b/drivers/demo/demo-shape.c @@ -11,6 +11,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -20,6 +21,8 @@ DECLARE_GLOBAL_DATA_PTR; struct shape_data { int num_chars; /* Number of non-space characters output so far */ + struct gpio_desc gpio_desc[8]; + int gpio_count; }; /* Crazy little function to draw shapes on the console */ @@ -89,9 +92,52 @@ static int shape_status(struct udevice *dev, int *status) return 0; } +static int set_light(struct udevice *dev, int light) +{ + struct shape_data *priv = dev_get_priv(dev); + struct gpio_desc *desc; + int ret; + int i; + + desc = priv->gpio_desc; + for (i = 0; i < priv->gpio_count; i++, desc++) { + uint mask = 1 << i; + + ret = dm_gpio_set_value(desc, light & mask); + if (ret < 0) + return ret; + } + + return 0; +} + +static int get_light(struct udevice *dev) +{ + struct shape_data *priv = dev_get_priv(dev); + struct gpio_desc *desc; + uint value = 0; + int ret; + int i; + + desc = priv->gpio_desc; + for (i = 0; i < priv->gpio_count; i++, desc++) { + uint mask = 1 << i; + + ret = dm_gpio_get_value(desc); + if (ret < 0) + return ret; + if (ret) + value |= mask; + } + + return value; +} + static const struct demo_ops shape_ops = { .hello = shape_hello, .status = shape_status, + .get_light = get_light, + .set_light = set_light, }; static int shape_ofdata_to_platdata(struct udevice *dev) @@ -111,6 +157,29 @@ static int shape_ofdata_to_platdata(struct udevice *dev) return 0; } +static int dm_shape_probe(struct udevice *dev) +{ + struct shape_data *priv = dev_get_priv(dev); + int ret; + + ret = gpio_request_list_by_name(dev, "light-gpios", priv->gpio_desc, + ARRAY_SIZE(priv->gpio_desc), + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret < 0) + return ret; + priv->gpio_count = ret; + debug("%s: %d GPIOs\n", __func__, priv->gpio_count); + + return 0; +} + +static int dm_shape_remove(struct udevice *dev) +{ + struct shape_data *priv = dev_get_priv(dev); + + return gpio_free_list(dev, priv->gpio_desc, priv->gpio_count); +} + static const struct udevice_id demo_shape_id[] = { { "demo-shape", 0 }, { }, @@ -122,6 +191,8 @@ U_BOOT_DRIVER(demo_shape_drv) = { .id = UCLASS_DEMO, .ofdata_to_platdata = shape_ofdata_to_platdata, .ops = &shape_ops, + .probe = dm_shape_probe, + .remove = dm_shape_remove, .priv_auto_alloc_size = sizeof(struct shape_data), .platdata_auto_alloc_size = sizeof(struct dm_demo_pdata), }; diff --git a/drivers/demo/demo-uclass.c b/drivers/demo/demo-uclass.c index f6510d602c8..725f06898f3 100644 --- a/drivers/demo/demo-uclass.c +++ b/drivers/demo/demo-uclass.c @@ -43,6 +43,26 @@ int demo_status(struct udevice *dev, int *status) return ops->status(dev, status); } +int demo_get_light(struct udevice *dev) +{ + const struct demo_ops *ops = device_get_ops(dev); + + if (!ops->get_light) + return -ENOSYS; + + return ops->get_light(dev); +} + +int demo_set_light(struct udevice *dev, int light) +{ + const struct demo_ops *ops = device_get_ops(dev); + + if (!ops->set_light) + return -ENOSYS; + + return ops->set_light(dev, light); +} + int demo_parse_dt(struct udevice *dev) { struct dm_demo_pdata *pdata = dev_get_platdata(dev); diff --git a/include/dm-demo.h b/include/dm-demo.h index a24fec6658e..03722d0c14d 100644 --- a/include/dm-demo.h +++ b/include/dm-demo.h @@ -25,10 +25,14 @@ struct dm_demo_pdata { struct demo_ops { int (*hello)(struct udevice *dev, int ch); int (*status)(struct udevice *dev, int *status); + int (*set_light)(struct udevice *dev, int light); + int (*get_light)(struct udevice *dev); }; int demo_hello(struct udevice *dev, int ch); int demo_status(struct udevice *dev, int *status); +int demo_set_light(struct udevice *dev, int light); +int demo_get_light(struct udevice *dev); int demo_list(void); int demo_parse_dt(struct udevice *dev); -- cgit v1.3.1 From 32f8a19f6d9aa551dc64868fac4b662c088dfb1f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:32 -0700 Subject: dm: cros_ec: Remove use of fdtdec GPIO support These functions are going away, so use the new uclass support instead. Signed-off-by: Simon Glass --- drivers/misc/cros_ec.c | 24 ++++++------------------ include/cros_ec.h | 3 ++- 2 files changed, 8 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c index 9b4effb2fb5..7b20ffcfa28 100644 --- a/drivers/misc/cros_ec.c +++ b/drivers/misc/cros_ec.c @@ -606,10 +606,10 @@ int cros_ec_reboot(struct cros_ec_dev *dev, enum ec_reboot_cmd cmd, int cros_ec_interrupt_pending(struct cros_ec_dev *dev) { /* no interrupt support : always poll */ - if (!fdt_gpio_isvalid(&dev->ec_int)) + if (!dm_gpio_is_valid(&dev->ec_int)) return -ENOENT; - return !gpio_get_value(dev->ec_int.gpio); + return dm_gpio_get_value(&dev->ec_int); } int cros_ec_info(struct cros_ec_dev *dev, struct ec_response_mkbp_info *info) @@ -1072,7 +1072,8 @@ static int cros_ec_decode_fdt(const void *blob, int node, return -1; } - fdtdec_decode_gpio(blob, node, "ec-interrupt", &dev->ec_int); + gpio_request_by_name_nodev(blob, node, "ec-interrupt", 0, &dev->ec_int, + GPIOD_IS_IN); dev->optimise_flash_write = fdtdec_get_bool(blob, node, "optimise-flash-write"); *devp = dev; @@ -1090,17 +1091,11 @@ int cros_ec_register(struct udevice *dev) char id[MSG_BYTES]; cdev->dev = dev; - fdtdec_decode_gpio(blob, node, "ec-interrupt", &cdev->ec_int); + gpio_request_by_name(dev, "ec-interrupt", 0, &cdev->ec_int, + GPIOD_IS_IN); cdev->optimise_flash_write = fdtdec_get_bool(blob, node, "optimise-flash-write"); - /* we will poll the EC interrupt line */ - fdtdec_setup_gpio(&cdev->ec_int); - if (fdt_gpio_isvalid(&cdev->ec_int)) { - gpio_request(cdev->ec_int.gpio, "cros-ec-irq"); - gpio_direction_input(cdev->ec_int.gpio); - } - if (cros_ec_check_version(cdev)) { debug("%s: Could not detect CROS-EC version\n", __func__); return -CROS_EC_ERR_CHECK_VERSION; @@ -1184,13 +1179,6 @@ int cros_ec_init(const void *blob, struct cros_ec_dev **cros_ecp) } #endif - /* we will poll the EC interrupt line */ - fdtdec_setup_gpio(&dev->ec_int); - if (fdt_gpio_isvalid(&dev->ec_int)) { - gpio_request(dev->ec_int.gpio, "cros-ec-irq"); - gpio_direction_input(dev->ec_int.gpio); - } - if (cros_ec_check_version(dev)) { debug("%s: Could not detect CROS-EC version\n", __func__); return -CROS_EC_ERR_CHECK_VERSION; diff --git a/include/cros_ec.h b/include/cros_ec.h index 9e13146ecbb..8457c80c5ef 100644 --- a/include/cros_ec.h +++ b/include/cros_ec.h @@ -13,6 +13,7 @@ #include #include #include +#include #ifndef CONFIG_DM_CROS_EC /* Which interface is the device on? */ @@ -39,7 +40,7 @@ struct cros_ec_dev { unsigned int bus_num; /* Bus number (for I2C) */ unsigned int max_frequency; /* Maximum interface frequency */ #endif - struct fdt_gpio_state ec_int; /* GPIO used as EC interrupt line */ + struct gpio_desc ec_int; /* GPIO used as EC interrupt line */ int protocol_version; /* Protocol version to use */ int optimise_flash_write; /* Don't write erased flash blocks */ -- cgit v1.3.1 From 838aa5c94adc0da95c858ef70271078fd18dc4c9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:33 -0700 Subject: dm: tegra: Add a GPIO translation function This deals with the polarity bit and selecting the correct bank device given a GPIO number. Signed-off-by: Simon Glass --- drivers/gpio/tegra_gpio.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/tegra_gpio.c b/drivers/gpio/tegra_gpio.c index 88f7ef5bf04..43928b8812c 100644 --- a/drivers/gpio/tegra_gpio.c +++ b/drivers/gpio/tegra_gpio.c @@ -21,6 +21,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -251,6 +252,22 @@ static int tegra_gpio_get_function(struct udevice *dev, unsigned offset) return GPIOF_INPUT; } +static int tegra_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, + struct fdtdec_phandle_args *args) +{ + int gpio, port, ret; + + gpio = args->args[0]; + port = gpio / TEGRA_GPIOS_PER_PORT; + ret = device_get_child(dev, port, &desc->dev); + if (ret) + return ret; + desc->offset = gpio % TEGRA_GPIOS_PER_PORT; + desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0; + + return 0; +} + static const struct dm_gpio_ops gpio_tegra_ops = { .request = tegra_gpio_request, .direction_input = tegra_gpio_direction_input, @@ -258,6 +275,7 @@ static const struct dm_gpio_ops gpio_tegra_ops = { .get_value = tegra_gpio_get_value, .set_value = tegra_gpio_set_value, .get_function = tegra_gpio_get_function, + .xlate = tegra_gpio_xlate, }; /** -- cgit v1.3.1 From 1d08b4b7439f6538d37eb603cee83842bad23bc5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:34 -0700 Subject: dm: exynos: Add a GPIO translation function This deals with the polarity bit. It also changes the GPIO devices so that the correct device tree node is linked to each one. This allows us to use the new uclass phandle functionality to implement a proper GPIO binding. Signed-off-by: Simon Glass --- drivers/gpio/s5p_gpio.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/s5p_gpio.c b/drivers/gpio/s5p_gpio.c index 6c41a42c177..0a245ba18ad 100644 --- a/drivers/gpio/s5p_gpio.c +++ b/drivers/gpio/s5p_gpio.c @@ -13,6 +13,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -275,12 +276,22 @@ static int exynos_gpio_get_function(struct udevice *dev, unsigned offset) return GPIOF_FUNC; } +static int exynos_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, + struct fdtdec_phandle_args *args) +{ + desc->offset = args->args[0]; + desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0; + + return 0; +} + static const struct dm_gpio_ops gpio_exynos_ops = { .direction_input = exynos_gpio_direction_input, .direction_output = exynos_gpio_direction_output, .get_value = exynos_gpio_get_value, .set_value = exynos_gpio_set_value, .get_function = exynos_gpio_get_function, + .xlate = exynos_gpio_xlate, }; static int gpio_exynos_probe(struct udevice *dev) @@ -342,7 +353,7 @@ static int gpio_exynos_bind(struct udevice *parent) plat->bank_name, plat, -1, &dev); if (ret) return ret; - dev->of_offset = parent->of_offset; + dev->of_offset = node; } return 0; -- cgit v1.3.1 From 04072cba1983fd8b3140b1b97e3d544359d178a0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:35 -0700 Subject: dm: tegra: video: Remove use of fdtdec GPIO support These functions are going away, so use the new uclass support instead. Signed-off-by: Simon Glass --- arch/arm/include/asm/arch-tegra20/display.h | 9 ++--- drivers/video/tegra.c | 54 +++++++++++------------------ 2 files changed, 25 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/arch/arm/include/asm/arch-tegra20/display.h b/arch/arm/include/asm/arch-tegra20/display.h index a04c84e54b2..6feeda3ba83 100644 --- a/arch/arm/include/asm/arch-tegra20/display.h +++ b/arch/arm/include/asm/arch-tegra20/display.h @@ -10,6 +10,7 @@ #include #include +#include /* This holds information about a window which can be displayed */ struct disp_ctl_win { @@ -72,10 +73,10 @@ struct fdt_panel_config { int pwm_channel; /* PWM channel to use for backlight */ enum lcd_cache_t cache_type; - struct fdt_gpio_state backlight_en; /* GPIO for backlight enable */ - struct fdt_gpio_state lvds_shutdown; /* GPIO for lvds shutdown */ - struct fdt_gpio_state backlight_vdd; /* GPIO for backlight vdd */ - struct fdt_gpio_state panel_vdd; /* GPIO for panel vdd */ + struct gpio_desc backlight_en; /* GPIO for backlight enable */ + struct gpio_desc lvds_shutdown; /* GPIO for lvds shutdown */ + struct gpio_desc backlight_vdd; /* GPIO for backlight vdd */ + struct gpio_desc panel_vdd; /* GPIO for panel vdd */ /* * Panel required timings * Timing 1: delay between panel_vdd-rise and data-rise diff --git a/drivers/video/tegra.c b/drivers/video/tegra.c index 57cb0074e27..b8f3431f242 100644 --- a/drivers/video/tegra.c +++ b/drivers/video/tegra.c @@ -149,14 +149,18 @@ static int fdt_decode_lcd(const void *blob, struct fdt_panel_config *config) FDT_LCD_CACHE_WRITE_BACK_FLUSH); /* These GPIOs are all optional */ - fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-enable-gpios", - &config->backlight_en); - fdtdec_decode_gpio(blob, display_node, "nvidia,lvds-shutdown-gpios", - &config->lvds_shutdown); - fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-vdd-gpios", - &config->backlight_vdd); - fdtdec_decode_gpio(blob, display_node, "nvidia,panel-vdd-gpios", - &config->panel_vdd); + gpio_request_by_name_nodev(blob, display_node, + "nvidia,backlight-enable-gpios", 0, + &config->backlight_en, GPIOD_IS_OUT); + gpio_request_by_name_nodev(blob, display_node, + "nvidia,lvds-shutdown-gpios", 0, + &config->lvds_shutdown, GPIOD_IS_OUT); + gpio_request_by_name_nodev(blob, display_node, + "nvidia,backlight-vdd-gpios", 0, + &config->backlight_vdd, GPIOD_IS_OUT); + gpio_request_by_name_nodev(blob, display_node, + "nvidia,panel-vdd-gpios", 0, + &config->panel_vdd, GPIOD_IS_OUT); return fdtdec_get_int_array(blob, display_node, "nvidia,panel-timings", config->panel_timings, FDT_LCD_TIMINGS); @@ -196,36 +200,18 @@ static int handle_stage(const void *blob) */ funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); - - fdtdec_setup_gpio(&config.panel_vdd); - fdtdec_setup_gpio(&config.lvds_shutdown); - fdtdec_setup_gpio(&config.backlight_vdd); - fdtdec_setup_gpio(&config.backlight_en); - - /* - * TODO: If fdt includes output flag we can omit this code - * since fdtdec_setup_gpio will do it for us. - */ - if (fdt_gpio_isvalid(&config.panel_vdd)) - gpio_direction_output(config.panel_vdd.gpio, 0); - if (fdt_gpio_isvalid(&config.lvds_shutdown)) - gpio_direction_output(config.lvds_shutdown.gpio, 0); - if (fdt_gpio_isvalid(&config.backlight_vdd)) - gpio_direction_output(config.backlight_vdd.gpio, 0); - if (fdt_gpio_isvalid(&config.backlight_en)) - gpio_direction_output(config.backlight_en.gpio, 0); break; case STAGE_PANEL_VDD: - if (fdt_gpio_isvalid(&config.panel_vdd)) - gpio_direction_output(config.panel_vdd.gpio, 1); + if (dm_gpio_is_valid(&config.panel_vdd)) + dm_gpio_set_value(&config.panel_vdd, 1); break; case STAGE_LVDS: - if (fdt_gpio_isvalid(&config.lvds_shutdown)) - gpio_set_value(config.lvds_shutdown.gpio, 1); + if (dm_gpio_is_valid(&config.lvds_shutdown)) + dm_gpio_set_value(&config.lvds_shutdown, 1); break; case STAGE_BACKLIGHT_VDD: - if (fdt_gpio_isvalid(&config.backlight_vdd)) - gpio_set_value(config.backlight_vdd.gpio, 1); + if (dm_gpio_is_valid(&config.backlight_vdd)) + dm_gpio_set_value(&config.backlight_vdd, 1); break; case STAGE_PWM: /* Enable PWM at 15/16 high, 32768 Hz with divider 1 */ @@ -235,8 +221,8 @@ static int handle_stage(const void *blob) pwm_enable(config.pwm_channel, 32768, 0xdf, 1); break; case STAGE_BACKLIGHT_EN: - if (fdt_gpio_isvalid(&config.backlight_en)) - gpio_set_value(config.backlight_en.gpio, 1); + if (dm_gpio_is_valid(&config.backlight_en)) + dm_gpio_set_value(&config.backlight_en, 1); break; case STAGE_DONE: break; -- cgit v1.3.1 From b0265d56fe24a757638a2a0ca76ec1400f43d348 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:36 -0700 Subject: dm: tegra: nand: Remove use of fdtdec GPIO support These functions are going away, so use the new uclass support instead. Signed-off-by: Simon Glass --- drivers/mtd/nand/tegra_nand.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/tegra_nand.c b/drivers/mtd/nand/tegra_nand.c index 163cf29a398..b660f3b2066 100644 --- a/drivers/mtd/nand/tegra_nand.c +++ b/drivers/mtd/nand/tegra_nand.c @@ -79,7 +79,7 @@ enum { struct fdt_nand { struct nand_ctlr *reg; int enabled; /* 1 to enable, 0 to disable */ - struct fdt_gpio_state wp_gpio; /* write-protect GPIO */ + struct gpio_desc wp_gpio; /* write-protect GPIO */ s32 width; /* bit width, normally 8 */ u32 timing[FDT_NAND_TIMING_COUNT]; }; @@ -945,8 +945,8 @@ static int fdt_decode_nand(const void *blob, int node, struct fdt_nand *config) config->reg = (struct nand_ctlr *)fdtdec_get_addr(blob, node, "reg"); config->enabled = fdtdec_get_is_enabled(blob, node); config->width = fdtdec_get_int(blob, node, "nvidia,nand-width", 8); - err = fdtdec_decode_gpio(blob, node, "nvidia,wp-gpios", - &config->wp_gpio); + err = gpio_request_by_name_nodev(blob, node, "nvidia,wp-gpios", 0, + &config->wp_gpio, GPIOD_IS_OUT); if (err) return err; err = fdtdec_get_int_array(blob, node, "nvidia,timing", @@ -1009,8 +1009,7 @@ int tegra_nand_init(struct nand_chip *nand, int devnum) /* Adjust timing for NAND device */ setup_timing(config->timing, info->reg); - fdtdec_setup_gpio(&config->wp_gpio); - gpio_direction_output(config->wp_gpio.gpio, 1); + dm_gpio_set_value(&config->wp_gpio, 1); our_mtd = &nand_info[devnum]; our_mtd->priv = nand; -- cgit v1.3.1 From 0347960b8732b8d8506c9123c217b503666a4ad8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:38 -0700 Subject: dm: mmc: Remove use of fdtdec GPIO support These functions are going away, so use the new uclass support instead. Signed-off-by: Simon Glass --- arch/arm/include/asm/arch-pantheon/gpio.h | 0 arch/arm/include/asm/arch-tegra/tegra_mmc.h | 7 +++--- drivers/mmc/s5p_sdhci.c | 20 +++++++--------- drivers/mmc/tegra_mmc.c | 36 +++++++++++------------------ include/sdhci.h | 6 ++--- 5 files changed, 28 insertions(+), 41 deletions(-) create mode 100644 arch/arm/include/asm/arch-pantheon/gpio.h (limited to 'drivers') diff --git a/arch/arm/include/asm/arch-pantheon/gpio.h b/arch/arm/include/asm/arch-pantheon/gpio.h new file mode 100644 index 00000000000..e69de29bb2d diff --git a/arch/arm/include/asm/arch-tegra/tegra_mmc.h b/arch/arm/include/asm/arch-tegra/tegra_mmc.h index 84e7b5553de..a20bdaa6187 100644 --- a/arch/arm/include/asm/arch-tegra/tegra_mmc.h +++ b/arch/arm/include/asm/arch-tegra/tegra_mmc.h @@ -10,6 +10,7 @@ #define __TEGRA_MMC_H_ #include +#include /* for mmc_config definition */ #include @@ -134,9 +135,9 @@ struct mmc_host { int enabled; /* 1 to enable, 0 to disable */ int width; /* Bus Width, 1, 4 or 8 */ enum periph_id mmc_id; /* Peripheral ID: PERIPH_ID_... */ - struct fdt_gpio_state cd_gpio; /* Change Detect GPIO */ - struct fdt_gpio_state pwr_gpio; /* Power GPIO */ - struct fdt_gpio_state wp_gpio; /* Write Protect GPIO */ + struct gpio_desc cd_gpio; /* Change Detect GPIO */ + struct gpio_desc pwr_gpio; /* Power GPIO */ + struct gpio_desc wp_gpio; /* Write Protect GPIO */ unsigned int version; /* SDHCI spec. version */ unsigned int clock; /* Current clock (MHz) */ struct mmc_config cfg; /* mmc configuration */ diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c index a5d34876bbb..3899372e0e4 100644 --- a/drivers/mmc/s5p_sdhci.c +++ b/drivers/mmc/s5p_sdhci.c @@ -102,17 +102,14 @@ struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS]; static int do_sdhci_init(struct sdhci_host *host) { - char str[20]; int dev_id, flag; int err = 0; flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; dev_id = host->index + PERIPH_ID_SDMMC0; - if (fdt_gpio_isvalid(&host->pwr_gpio)) { - sprintf(str, "sdhci%d_power", host->index & 0xf); - gpio_request(host->pwr_gpio.gpio, str); - gpio_direction_output(host->pwr_gpio.gpio, 1); + if (dm_gpio_is_valid(&host->pwr_gpio)) { + dm_gpio_set_value(&host->pwr_gpio, 1); err = exynos_pinmux_config(dev_id, flag); if (err) { debug("MMC not configured\n"); @@ -120,11 +117,8 @@ static int do_sdhci_init(struct sdhci_host *host) } } - if (fdt_gpio_isvalid(&host->cd_gpio)) { - sprintf(str, "sdhci%d_cd", host->index & 0xf); - gpio_request(host->cd_gpio.gpio, str); - gpio_direction_input(host->cd_gpio.gpio); - if (gpio_get_value(host->cd_gpio.gpio)) + if (dm_gpio_is_valid(&host->cd_gpio)) { + if (dm_gpio_get_value(&host->cd_gpio)) return -ENODEV; err = exynos_pinmux_config(dev_id, flag); @@ -166,8 +160,10 @@ static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host) } host->ioaddr = (void *)base; - fdtdec_decode_gpio(blob, node, "pwr-gpios", &host->pwr_gpio); - fdtdec_decode_gpio(blob, node, "cd-gpios", &host->cd_gpio); + gpio_request_by_name_nodev(blob, node, "pwr-gpios", 0, &host->pwr_gpio, + GPIOD_IS_OUT); + gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, + GPIOD_IS_IN); return 0; } diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index 2bd36b0ee70..2cd8cf10aec 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -515,8 +515,8 @@ static int tegra_mmc_getcd(struct mmc *mmc) debug("tegra_mmc_getcd called\n"); - if (fdt_gpio_isvalid(&host->cd_gpio)) - return fdtdec_get_gpio(&host->cd_gpio); + if (dm_gpio_is_valid(&host->cd_gpio)) + return dm_gpio_get_value(&host->cd_gpio); return 1; } @@ -531,7 +531,6 @@ static const struct mmc_ops tegra_mmc_ops = { static int do_mmc_init(int dev_index) { struct mmc_host *host; - char gpusage[12]; /* "SD/MMCn PWR" or "SD/MMCn CD" */ struct mmc *mmc; /* DT should have been read & host config filled in */ @@ -539,27 +538,15 @@ static int do_mmc_init(int dev_index) if (!host->enabled) return -1; - debug(" do_mmc_init: index %d, bus width %d " - "pwr_gpio %d cd_gpio %d\n", - dev_index, host->width, - host->pwr_gpio.gpio, host->cd_gpio.gpio); + debug(" do_mmc_init: index %d, bus width %d pwr_gpio %d cd_gpio %d\n", + dev_index, host->width, gpio_get_number(&host->pwr_gpio), + gpio_get_number(&host->cd_gpio)); host->clock = 0; clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); - if (fdt_gpio_isvalid(&host->pwr_gpio)) { - sprintf(gpusage, "SD/MMC%d PWR", dev_index); - gpio_request(host->pwr_gpio.gpio, gpusage); - gpio_direction_output(host->pwr_gpio.gpio, 1); - debug(" Power GPIO name = %s\n", host->pwr_gpio.name); - } - - if (fdt_gpio_isvalid(&host->cd_gpio)) { - sprintf(gpusage, "SD/MMC%d CD", dev_index); - gpio_request(host->cd_gpio.gpio, gpusage); - gpio_direction_input(host->cd_gpio.gpio); - debug(" CD GPIO name = %s\n", host->cd_gpio.name); - } + if (dm_gpio_is_valid(&host->pwr_gpio)) + dm_gpio_set_value(&host->pwr_gpio, 1); memset(&host->cfg, 0, sizeof(host->cfg)); @@ -626,9 +613,12 @@ static int mmc_get_config(const void *blob, int node, struct mmc_host *host) debug("%s: no sdmmc width found\n", __func__); /* These GPIOs are optional */ - fdtdec_decode_gpio(blob, node, "cd-gpios", &host->cd_gpio); - fdtdec_decode_gpio(blob, node, "wp-gpios", &host->wp_gpio); - fdtdec_decode_gpio(blob, node, "power-gpios", &host->pwr_gpio); + gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, + GPIOD_IS_IN); + gpio_request_by_name_nodev(blob, node, "wp-gpios", 0, &host->wp_gpio, + GPIOD_IS_IN); + gpio_request_by_name_nodev(blob, node, "power-gpios", 0, + &host->pwr_gpio, GPIOD_IS_OUT); debug("%s: found controller at %p, width = %d, periph_id = %d\n", __func__, host->reg, host->width, host->mmc_id); diff --git a/include/sdhci.h b/include/sdhci.h index aa4a0e9654d..23893b57408 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -12,7 +12,7 @@ #include #include -#include +#include /* * Controller registers @@ -246,8 +246,8 @@ struct sdhci_host { int index; int bus_width; - struct fdt_gpio_state pwr_gpio; /* Power GPIO */ - struct fdt_gpio_state cd_gpio; /* Card Detect GPIO */ + struct gpio_desc pwr_gpio; /* Power GPIO */ + struct gpio_desc cd_gpio; /* Card Detect GPIO */ void (*set_control_reg)(struct sdhci_host *host); void (*set_clock)(int dev_index, unsigned int div); -- cgit v1.3.1 From 46927e1ef40682656d69cbe18abf327e2f842a23 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:39 -0700 Subject: dm: usb: Remove use of fdtdec GPIO support These functions are going away, so use the new uclass support instead. Signed-off-by: Simon Glass --- drivers/usb/host/ehci-exynos.c | 10 +++++----- drivers/usb/host/ehci-tegra.c | 38 ++++++++++++++++++-------------------- drivers/usb/host/xhci-exynos5.c | 10 +++++----- 3 files changed, 28 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index 6fdbf5724f4..f3c077d82e4 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -31,7 +31,7 @@ DECLARE_GLOBAL_DATA_PTR; struct exynos_ehci { struct exynos_usb_phy *usb; struct ehci_hccr *hcd; - struct fdt_gpio_state vbus_gpio; + struct gpio_desc vbus_gpio; }; static struct exynos_ehci exynos; @@ -61,7 +61,8 @@ static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos) exynos->hcd = (struct ehci_hccr *)addr; /* Vbus gpio */ - fdtdec_decode_gpio(blob, node, "samsung,vbus-gpio", &exynos->vbus_gpio); + gpio_request_by_name_nodev(blob, node, "samsung,vbus-gpio", 0, + &exynos->vbus_gpio, GPIOD_IS_OUT); depth = 0; node = fdtdec_next_compatible_subnode(blob, node, @@ -236,9 +237,8 @@ int ehci_hcd_init(int index, enum usb_init_type init, #ifdef CONFIG_OF_CONTROL /* setup the Vbus gpio here */ - if (fdt_gpio_isvalid(&ctx->vbus_gpio) && - !fdtdec_setup_gpio(&ctx->vbus_gpio)) - gpio_direction_output(ctx->vbus_gpio.gpio, 1); + if (dm_gpio_is_valid(&ctx->vbus_gpio)) + dm_gpio_set_value(&ctx->vbus_gpio, 1); #endif setup_usb_phy(ctx->usb); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 5f0a98e8b80..b5ad1e35e54 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -72,8 +72,8 @@ struct fdt_usb { enum usb_init_type init_type; enum dr_mode dr_mode; /* dual role mode */ enum periph_id periph_id;/* peripheral id */ - struct fdt_gpio_state vbus_gpio; /* GPIO for vbus enable */ - struct fdt_gpio_state phy_reset_gpio; /* GPIO to reset ULPI phy */ + struct gpio_desc vbus_gpio; /* GPIO for vbus enable */ + struct gpio_desc phy_reset_gpio; /* GPIO to reset ULPI phy */ }; static struct fdt_usb port[USB_PORTS_MAX]; /* List of valid USB ports */ @@ -252,17 +252,14 @@ static void set_up_vbus(struct fdt_usb *config, enum usb_init_type init) return; } - if (fdt_gpio_isvalid(&config->vbus_gpio)) { + if (dm_gpio_is_valid(&config->vbus_gpio)) { int vbus_value; - fdtdec_setup_gpio(&config->vbus_gpio); + vbus_value = (init == USB_INIT_HOST); + dm_gpio_set_value(&config->vbus_gpio, vbus_value); - vbus_value = (init == USB_INIT_HOST) ^ - !!(config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW); - gpio_direction_output(config->vbus_gpio.gpio, vbus_value); - - debug("set_up_vbus: GPIO %d %d\n", config->vbus_gpio.gpio, - vbus_value); + debug("set_up_vbus: GPIO %d %d\n", + gpio_get_number(&config->vbus_gpio), vbus_value); } } @@ -360,7 +357,7 @@ static int init_utmi_usb_controller(struct fdt_usb *config, * mux must be switched to actually use a_sess_vld threshold. */ if (config->dr_mode == DR_MODE_OTG && - fdt_gpio_isvalid(&config->vbus_gpio)) + dm_gpio_is_valid(&config->vbus_gpio)) clrsetbits_le32(&usbctlr->usb1_legacy_ctrl, VBUS_SENSE_CTL_MASK, VBUS_SENSE_CTL_A_SESS_VLD << VBUS_SENSE_CTL_SHIFT); @@ -569,11 +566,10 @@ static int init_ulpi_usb_controller(struct fdt_usb *config, clock_set_pllout(CLOCK_ID_PERIPH, PLL_OUT4, CONFIG_ULPI_REF_CLK); /* reset ULPI phy */ - if (fdt_gpio_isvalid(&config->phy_reset_gpio)) { - fdtdec_setup_gpio(&config->phy_reset_gpio); - gpio_direction_output(config->phy_reset_gpio.gpio, 0); + if (dm_gpio_is_valid(&config->phy_reset_gpio)) { + dm_gpio_set_value(&config->phy_reset_gpio, 0); mdelay(5); - gpio_set_value(config->phy_reset_gpio.gpio, 1); + dm_gpio_set_value(&config->phy_reset_gpio, 1); } /* Reset the usb controller */ @@ -685,14 +681,16 @@ static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config) debug("%s: Missing/invalid peripheral ID\n", __func__); return -FDT_ERR_NOTFOUND; } - fdtdec_decode_gpio(blob, node, "nvidia,vbus-gpio", &config->vbus_gpio); - fdtdec_decode_gpio(blob, node, "nvidia,phy-reset-gpio", - &config->phy_reset_gpio); + gpio_request_by_name_nodev(blob, node, "nvidia,vbus-gpio", 0, + &config->vbus_gpio, GPIOD_IS_OUT); + gpio_request_by_name_nodev(blob, node, "nvidia,phy-reset-gpio", 0, + &config->phy_reset_gpio, GPIOD_IS_OUT); debug("enabled=%d, legacy_mode=%d, utmi=%d, ulpi=%d, periph_id=%d, " "vbus=%d, phy_reset=%d, dr_mode=%d\n", config->enabled, config->has_legacy_mode, config->utmi, - config->ulpi, config->periph_id, config->vbus_gpio.gpio, - config->phy_reset_gpio.gpio, config->dr_mode); + config->ulpi, config->periph_id, + gpio_get_number(&config->vbus_gpio), + gpio_get_number(&config->phy_reset_gpio), config->dr_mode); return 0; } diff --git a/drivers/usb/host/xhci-exynos5.c b/drivers/usb/host/xhci-exynos5.c index b4946a3f1cf..a77c8bc9193 100644 --- a/drivers/usb/host/xhci-exynos5.c +++ b/drivers/usb/host/xhci-exynos5.c @@ -40,7 +40,7 @@ struct exynos_xhci { struct exynos_usb3_phy *usb3_phy; struct xhci_hccr *hcd; struct dwc3 *dwc3_reg; - struct fdt_gpio_state vbus_gpio; + struct gpio_desc vbus_gpio; }; static struct exynos_xhci exynos; @@ -69,7 +69,8 @@ static int exynos_usb3_parse_dt(const void *blob, struct exynos_xhci *exynos) exynos->hcd = (struct xhci_hccr *)addr; /* Vbus gpio */ - fdtdec_decode_gpio(blob, node, "samsung,vbus-gpio", &exynos->vbus_gpio); + gpio_request_by_name_nodev(blob, node, "samsung,vbus-gpio", 0, + &exynos->vbus_gpio, GPIOD_IS_OUT); depth = 0; node = fdtdec_next_compatible_subnode(blob, node, @@ -298,9 +299,8 @@ int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor) #ifdef CONFIG_OF_CONTROL /* setup the Vbus gpio here */ - if (fdt_gpio_isvalid(&ctx->vbus_gpio) && - !fdtdec_setup_gpio(&ctx->vbus_gpio)) - gpio_direction_output(ctx->vbus_gpio.gpio, 1); + if (dm_gpio_is_valid(&ctx->vbus_gpio)) + dm_gpio_set_value(&ctx->vbus_gpio, 1); #endif ret = exynos_xhci_core_init(ctx); -- cgit v1.3.1 From 050fb909b630344c5541ff60459fdb06189a8228 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:40 -0700 Subject: dm: spi: Remove use of fdtdec GPIO support These functions are going away, so use the new uclass support instead. Signed-off-by: Simon Glass --- drivers/spi/soft_spi.c | 62 +++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/soft_spi.c b/drivers/spi/soft_spi.c index 558803618a2..423c98d1a5a 100644 --- a/drivers/spi/soft_spi.c +++ b/drivers/spi/soft_spi.c @@ -21,10 +21,10 @@ DECLARE_GLOBAL_DATA_PTR; struct soft_spi_platdata { - struct fdt_gpio_state cs; - struct fdt_gpio_state sclk; - struct fdt_gpio_state mosi; - struct fdt_gpio_state miso; + struct gpio_desc cs; + struct gpio_desc sclk; + struct gpio_desc mosi; + struct gpio_desc miso; int spi_delay_us; }; @@ -35,9 +35,8 @@ struct soft_spi_priv { static int soft_spi_scl(struct udevice *dev, int bit) { struct soft_spi_platdata *plat = dev->platdata; - struct soft_spi_priv *priv = dev_get_priv(dev); - gpio_set_value(plat->sclk.gpio, priv->mode & SPI_CPOL ? bit : !bit); + dm_gpio_set_value(&plat->sclk, bit); return 0; } @@ -46,7 +45,7 @@ static int soft_spi_sda(struct udevice *dev, int bit) { struct soft_spi_platdata *plat = dev->platdata; - gpio_set_value(plat->mosi.gpio, bit); + dm_gpio_set_value(&plat->mosi, bit); return 0; } @@ -54,11 +53,10 @@ static int soft_spi_sda(struct udevice *dev, int bit) static int soft_spi_cs_activate(struct udevice *dev) { struct soft_spi_platdata *plat = dev->platdata; - struct soft_spi_priv *priv = dev_get_priv(dev); - gpio_set_value(plat->cs.gpio, !(priv->mode & SPI_CS_HIGH)); - gpio_set_value(plat->sclk.gpio, priv->mode & SPI_CPOL); - gpio_set_value(plat->cs.gpio, priv->mode & SPI_CS_HIGH); + dm_gpio_set_value(&plat->cs, 0); + dm_gpio_set_value(&plat->sclk, 0); + dm_gpio_set_value(&plat->cs, 1); return 0; } @@ -66,9 +64,8 @@ static int soft_spi_cs_activate(struct udevice *dev) static int soft_spi_cs_deactivate(struct udevice *dev) { struct soft_spi_platdata *plat = dev->platdata; - struct soft_spi_priv *priv = dev_get_priv(dev); - gpio_set_value(plat->cs.gpio, !(priv->mode & SPI_CS_HIGH)); + dm_gpio_set_value(&plat->cs, 0); return 0; } @@ -109,7 +106,6 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen, uchar tmpdout = 0; const u8 *txd = dout; u8 *rxd = din; - int cpol = priv->mode & SPI_CPOL; int cpha = priv->mode & SPI_CPHA; unsigned int j; @@ -137,19 +133,19 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen, } if (!cpha) - soft_spi_scl(dev, !cpol); + soft_spi_scl(dev, 0); soft_spi_sda(dev, tmpdout & 0x80); udelay(plat->spi_delay_us); if (cpha) - soft_spi_scl(dev, !cpol); + soft_spi_scl(dev, 0); else - soft_spi_scl(dev, cpol); + soft_spi_scl(dev, 1); tmpdin <<= 1; - tmpdin |= gpio_get_value(plat->miso.gpio); + tmpdin |= dm_gpio_get_value(&plat->miso); tmpdout <<= 1; udelay(plat->spi_delay_us); if (cpha) - soft_spi_scl(dev, cpol); + soft_spi_scl(dev, 1); } /* * If the number of bits isn't a multiple of 8, shift the last @@ -205,11 +201,6 @@ static int soft_spi_ofdata_to_platdata(struct udevice *dev) const void *blob = gd->fdt_blob; int node = dev->of_offset; - if (fdtdec_decode_gpio(blob, node, "cs-gpio", &plat->cs) || - fdtdec_decode_gpio(blob, node, "sclk-gpio", &plat->sclk) || - fdtdec_decode_gpio(blob, node, "mosi-gpio", &plat->mosi) || - fdtdec_decode_gpio(blob, node, "miso-gpio", &plat->miso)) - return -EINVAL; plat->spi_delay_us = fdtdec_get_int(blob, node, "spi-delay-us", 0); return 0; @@ -219,16 +210,19 @@ static int soft_spi_probe(struct udevice *dev) { struct spi_slave *slave = dev_get_parentdata(dev); struct soft_spi_platdata *plat = dev->platdata; - - gpio_request(plat->cs.gpio, "soft_spi_cs"); - gpio_request(plat->sclk.gpio, "soft_spi_sclk"); - gpio_request(plat->mosi.gpio, "soft_spi_mosi"); - gpio_request(plat->miso.gpio, "soft_spi_miso"); - - gpio_direction_output(plat->sclk.gpio, slave->mode & SPI_CPOL); - gpio_direction_output(plat->mosi.gpio, 1); - gpio_direction_input(plat->miso.gpio); - gpio_direction_output(plat->cs.gpio, !(slave->mode & SPI_CS_HIGH)); + int cs_flags, clk_flags; + + cs_flags = (slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW; + clk_flags = (slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0; + if (gpio_request_by_name(dev, "cs-gpio", 0, &plat->cs, + GPIOD_IS_OUT | cs_flags) || + gpio_request_by_name(dev, "sclk-gpio", 0, &plat->sclk, + GPIOD_IS_OUT | clk_flags) || + gpio_request_by_name(dev, "mosi-gpio", 0, &plat->mosi, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE) || + gpio_request_by_name(dev, "miso-gpio", 0, &plat->miso, + GPIOD_IS_IN)) + return -EINVAL; return 0; } -- cgit v1.3.1 From f9a4c2da72d04e13b05deecb800f232d2948eb85 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 12 Jan 2015 18:02:07 -0700 Subject: dm: i2c: Rename driver model I2C functions to permit compatibility Add a dm_ prefix to driver model I2C functions so that we can keep the old ones around. This is a little unfortunate, but on reflection it is too difficult to change the API. We can undo this rename when most boards and drivers are converted to use driver model for I2C. Signed-off-by: Simon Glass --- board/avionic-design/common/tamonten-ng.c | 2 +- board/nvidia/cardhu/cardhu.c | 8 ++--- board/nvidia/dalmore/dalmore.c | 6 ++-- board/nvidia/whistler/whistler.c | 10 +++--- board/toradex/apalis_t30/apalis_t30.c | 6 ++-- common/cmd_i2c.c | 28 ++++++++------- common/exports.c | 3 +- drivers/i2c/i2c-uclass.c | 9 ++--- drivers/power/as3722.c | 4 +-- drivers/power/tps6586x.c | 4 +-- include/i2c.h | 19 +++++----- test/dm/i2c.c | 60 +++++++++++++++---------------- 12 files changed, 82 insertions(+), 77 deletions(-) (limited to 'drivers') diff --git a/board/avionic-design/common/tamonten-ng.c b/board/avionic-design/common/tamonten-ng.c index 86a0844273d..bca91831e0e 100644 --- a/board/avionic-design/common/tamonten-ng.c +++ b/board/avionic-design/common/tamonten-ng.c @@ -60,7 +60,7 @@ void pmu_write(uchar reg, uchar data) debug("%s: Cannot find PMIC I2C chip\n", __func__); return; } - i2c_write(dev, reg, &data, 1); + dm_i2c_write(dev, reg, &data, 1); } /* diff --git a/board/nvidia/cardhu/cardhu.c b/board/nvidia/cardhu/cardhu.c index 95c4ff25092..fc31d29140b 100644 --- a/board/nvidia/cardhu/cardhu.c +++ b/board/nvidia/cardhu/cardhu.c @@ -57,7 +57,7 @@ void board_sdmmc_voltage_init(void) reg = 0x32; for (i = 0; i < MAX_I2C_RETRY; ++i) { - if (i2c_write(dev, reg, data_buffer, 1)) + if (dm_i2c_write(dev, reg, data_buffer, 1)) udelay(100); } @@ -66,7 +66,7 @@ void board_sdmmc_voltage_init(void) reg = 0x67; for (i = 0; i < MAX_I2C_RETRY; ++i) { - if (i2c_write(dev, reg, data_buffer, 1)) + if (dm_i2c_write(dev, reg, data_buffer, 1)) udelay(100); } } @@ -104,7 +104,7 @@ int tegra_pcie_board_init(void) data[0] = 0x15; addr = 0x30; - err = i2c_write(dev, addr, data, 1); + err = dm_i2c_write(dev, addr, data, 1); if (err) { debug("failed to set VDD supply\n"); return err; @@ -121,7 +121,7 @@ int tegra_pcie_board_init(void) data[0] = 0x15; addr = 0x31; - err = i2c_write(dev, addr, data, 1); + err = dm_i2c_write(dev, addr, data, 1); if (err) { debug("failed to set AVDD supply\n"); return err; diff --git a/board/nvidia/dalmore/dalmore.c b/board/nvidia/dalmore/dalmore.c index 2a737468ddc..c0991c57a97 100644 --- a/board/nvidia/dalmore/dalmore.c +++ b/board/nvidia/dalmore/dalmore.c @@ -65,7 +65,7 @@ void board_sdmmc_voltage_init(void) data_buffer[0] = 0x31; reg = 0x61; - ret = i2c_write(dev, reg, data_buffer, 1); + ret = dm_i2c_write(dev, reg, data_buffer, 1); if (ret) printf("%s: PMU i2c_write %02X<-%02X returned %d\n", __func__, reg, data_buffer[0], ret); @@ -74,7 +74,7 @@ void board_sdmmc_voltage_init(void) data_buffer[0] = 0x01; reg = 0x60; - ret = i2c_write(dev, reg, data_buffer, 1); + ret = dm_i2c_write(dev, reg, data_buffer, 1); if (ret) printf("%s: PMU i2c_write %02X<-%02X returned %d\n", __func__, reg, data_buffer[0], ret); @@ -88,7 +88,7 @@ void board_sdmmc_voltage_init(void) debug("%s: Cannot find charger I2C chip\n", __func__); return; } - ret = i2c_write(dev, reg, data_buffer, 1); + ret = dm_i2c_write(dev, reg, data_buffer, 1); if (ret) printf("%s: BAT i2c_write %02X<-%02X returned %d\n", __func__, reg, data_buffer[0], ret); diff --git a/board/nvidia/whistler/whistler.c b/board/nvidia/whistler/whistler.c index 3114b20be02..ad6ea091df7 100644 --- a/board/nvidia/whistler/whistler.c +++ b/board/nvidia/whistler/whistler.c @@ -33,15 +33,15 @@ void pin_mux_mmc(void) return; } val = 0x29; - ret = i2c_write(dev, 0x46, &val, 1); + ret = dm_i2c_write(dev, 0x46, &val, 1); if (ret) printf("i2c_write 0 0x3c 0x46 failed: %d\n", ret); val = 0x00; - ret = i2c_write(dev, 0x45, &val, 1); + ret = dm_i2c_write(dev, 0x45, &val, 1); if (ret) printf("i2c_write 0 0x3c 0x45 failed: %d\n", ret); val = 0x1f; - ret = i2c_write(dev, 0x44, &val, 1); + ret = dm_i2c_write(dev, 0x44, &val, 1); if (ret) printf("i2c_write 0 0x3c 0x44 failed: %d\n", ret); @@ -70,11 +70,11 @@ void pin_mux_usb(void) return; } val = 0x03; - ret = i2c_write(dev, 2, &val, 1); + ret = dm_i2c_write(dev, 2, &val, 1); if (ret) printf("i2c_write 0 0x20 2 failed: %d\n", ret); val = 0xfc; - ret = i2c_write(dev, 6, &val, 1); + ret = dm_i2c_write(dev, 6, &val, 1); if (ret) printf("i2c_write 0 0x20 6 failed: %d\n", ret); } diff --git a/board/toradex/apalis_t30/apalis_t30.c b/board/toradex/apalis_t30/apalis_t30.c index 5d2c024e890..1c4b4c1cd48 100644 --- a/board/toradex/apalis_t30/apalis_t30.c +++ b/board/toradex/apalis_t30/apalis_t30.c @@ -51,7 +51,7 @@ int tegra_pcie_board_init(void) data[0] = 0x27; addr = 0x25; - err = i2c_write(dev, addr, data, 1); + err = dm_i2c_write(dev, addr, data, 1); if (err) { debug("failed to set VDD supply\n"); return err; @@ -61,7 +61,7 @@ int tegra_pcie_board_init(void) data[0] = 0x0D; addr = 0x24; - err = i2c_write(dev, addr, data, 1); + err = dm_i2c_write(dev, addr, data, 1); if (err) { debug("failed to enable VDD supply\n"); return err; @@ -71,7 +71,7 @@ int tegra_pcie_board_init(void) data[0] = 0x0D; addr = 0x35; - err = i2c_write(dev, addr, data, 1); + err = dm_i2c_write(dev, addr, data, 1); if (err) { debug("failed to set AVDD supply\n"); return err; diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c index f65ab616b01..1e500fbf2cc 100644 --- a/common/cmd_i2c.c +++ b/common/cmd_i2c.c @@ -133,7 +133,7 @@ static uchar i2c_no_probes[] = CONFIG_SYS_I2C_NOPROBES; #ifdef CONFIG_DM_I2C static struct udevice *i2c_cur_bus; -static int i2c_set_bus_num(unsigned int busnum) +static int cmd_i2c_set_bus_num(unsigned int busnum) { struct udevice *bus; int ret; @@ -323,7 +323,7 @@ static int do_i2c_read ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv if (!ret && alen != -1) ret = i2c_set_chip_offset_len(dev, alen); if (!ret) - ret = i2c_read(dev, devaddr, memaddr, length); + ret = dm_i2c_read(dev, devaddr, memaddr, length); #else ret = i2c_read(chip, devaddr, alen, memaddr, length); #endif @@ -381,7 +381,7 @@ static int do_i2c_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[ while (length-- > 0) { #ifdef CONFIG_DM_I2C - ret = i2c_write(dev, devaddr++, memaddr++, 1); + ret = dm_i2c_write(dev, devaddr++, memaddr++, 1); #else ret = i2c_write(chip, devaddr++, alen, memaddr++, 1); #endif @@ -513,7 +513,7 @@ static int do_i2c_md ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes; #ifdef CONFIG_DM_I2C - ret = i2c_read(dev, addr, linebuf, linebytes); + ret = dm_i2c_read(dev, addr, linebuf, linebytes); #else ret = i2c_read(chip, addr, alen, linebuf, linebytes); #endif @@ -611,7 +611,7 @@ static int do_i2c_mw ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] while (count-- > 0) { #ifdef CONFIG_DM_I2C - ret = i2c_write(dev, addr++, &byte, 1); + ret = dm_i2c_write(dev, addr++, &byte, 1); #else ret = i2c_write(chip, addr++, alen, &byte, 1); #endif @@ -698,7 +698,7 @@ static int do_i2c_crc (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] err = 0; while (count-- > 0) { #ifdef CONFIG_DM_I2C - ret = i2c_read(dev, addr, &byte, 1); + ret = dm_i2c_read(dev, addr, &byte, 1); #else ret = i2c_read(chip, addr, alen, &byte, 1); #endif @@ -793,7 +793,7 @@ mod_i2c_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const arg do { printf("%08lx:", addr); #ifdef CONFIG_DM_I2C - ret = i2c_read(dev, addr, (uchar *)&data, size); + ret = dm_i2c_read(dev, addr, (uchar *)&data, size); #else ret = i2c_read(chip, addr, alen, (uchar *)&data, size); #endif @@ -841,8 +841,8 @@ mod_i2c_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const arg */ bootretry_reset_cmd_timeout(); #ifdef CONFIG_DM_I2C - ret = i2c_write(dev, addr, (uchar *)&data, - size); + ret = dm_i2c_write(dev, addr, (uchar *)&data, + size); #else ret = i2c_write(chip, addr, alen, (uchar *)&data, size); @@ -917,7 +917,7 @@ static int do_i2c_probe (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv continue; #endif #ifdef CONFIG_DM_I2C - ret = i2c_probe(bus, j, 0, &dev); + ret = dm_i2c_probe(bus, j, 0, &dev); #else ret = i2c_probe(j); #endif @@ -1010,7 +1010,7 @@ static int do_i2c_loop(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] */ while (1) { #ifdef CONFIG_DM_I2C - ret = i2c_read(dev, addr, bytes, length); + ret = dm_i2c_read(dev, addr, bytes, length); #else ret = i2c_read(chip, addr, alen, bytes, length); #endif @@ -1579,7 +1579,7 @@ int do_edid(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) #ifdef CONFIG_DM_I2C ret = i2c_get_cur_bus_chip(chip, &dev); if (!ret) - ret = i2c_read(dev, 0, (uchar *)&edid, sizeof(edid)); + ret = dm_i2c_read(dev, 0, (uchar *)&edid, sizeof(edid)); #else ret = i2c_read(chip, 0, 1, (uchar *)&edid, sizeof(edid)); #endif @@ -1696,7 +1696,11 @@ static int do_i2c_bus_num(cmd_tbl_t *cmdtp, int flag, int argc, } #endif printf("Setting bus to %d\n", bus_no); +#ifdef CONFIG_DM_I2C + ret = cmd_i2c_set_bus_num(bus_no); +#else ret = i2c_set_bus_num(bus_no); +#endif if (ret) printf("Failure changing bus number (%d)\n", ret); } diff --git a/common/exports.c b/common/exports.c index 88fcfc8cb6f..459e18cb097 100644 --- a/common/exports.c +++ b/common/exports.c @@ -23,7 +23,8 @@ unsigned long get_version(void) # define install_hdlr irq_install_handler # define free_hdlr irq_free_handler #endif -#ifndef CONFIG_CMD_I2C +#if !defined(CONFIG_CMD_I2C) || \ + (defined(CONFIG_DM_I2C) && !defined(CONFIG_DM_I2C_COMPAT)) # define i2c_write dummy # define i2c_read dummy #endif diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 005bf8662f2..25f2c18cf4f 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -100,7 +100,7 @@ static int i2c_write_bytewise(struct udevice *dev, uint offset, return 0; } -int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len) +int dm_i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len) { struct dm_i2c_chip *chip = dev_get_parentdata(dev); struct udevice *bus = dev_get_parent(dev); @@ -130,7 +130,8 @@ int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len) return ops->xfer(bus, msg, msg_count); } -int i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, int len) +int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, + int len) { struct dm_i2c_chip *chip = dev_get_parentdata(dev); struct udevice *bus = dev_get_parent(dev); @@ -303,8 +304,8 @@ int i2c_get_chip_for_busnum(int busnum, int chip_addr, struct udevice **devp) return 0; } -int i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, - struct udevice **devp) +int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, + struct udevice **devp) { int ret; diff --git a/drivers/power/as3722.c b/drivers/power/as3722.c index 4c6de79cd60..3aafdc96704 100644 --- a/drivers/power/as3722.c +++ b/drivers/power/as3722.c @@ -31,7 +31,7 @@ static int as3722_read(struct udevice *pmic, u8 reg, u8 *value) { int err; - err = i2c_read(pmic, reg, value, 1); + err = dm_i2c_read(pmic, reg, value, 1); if (err < 0) return err; @@ -42,7 +42,7 @@ static int as3722_write(struct udevice *pmic, u8 reg, u8 value) { int err; - err = i2c_write(pmic, reg, &value, 1); + err = dm_i2c_write(pmic, reg, &value, 1); if (err < 0) return err; diff --git a/drivers/power/tps6586x.c b/drivers/power/tps6586x.c index 29bab4cc00d..865098386d9 100644 --- a/drivers/power/tps6586x.c +++ b/drivers/power/tps6586x.c @@ -37,7 +37,7 @@ static int tps6586x_read(int reg) int retval = -1; for (i = 0; i < MAX_I2C_RETRY; ++i) { - if (!i2c_read(tps6586x_dev, reg, &data, 1)) { + if (!dm_i2c_read(tps6586x_dev, reg, &data, 1)) { retval = (int)data; goto exit; } @@ -60,7 +60,7 @@ static int tps6586x_write(int reg, uchar *data, uint len) int retval = -1; for (i = 0; i < MAX_I2C_RETRY; ++i) { - if (!i2c_write(tps6586x_dev, reg, data, len)) { + if (!dm_i2c_write(tps6586x_dev, reg, data, len)) { retval = 0; goto exit; } diff --git a/include/i2c.h b/include/i2c.h index 9c6a60cf9ae..30d53c8a9df 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -75,7 +75,7 @@ struct dm_i2c_bus { }; /** - * i2c_read() - read bytes from an I2C chip + * dm_i2c_read() - read bytes from an I2C chip * * To obtain an I2C device (called a 'chip') given the I2C bus address you * can use i2c_get_chip(). To obtain a bus by bus number use @@ -91,13 +91,12 @@ struct dm_i2c_bus { * * @return 0 on success, -ve on failure */ -int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, - int len); +int dm_i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len); /** - * i2c_write() - write bytes to an I2C chip + * dm_i2c_write() - write bytes to an I2C chip * - * See notes for i2c_read() above. + * See notes for dm_i2c_read() above. * * @dev: Chip to write to * @offset: Offset within chip to start writing @@ -106,11 +105,11 @@ int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, * * @return 0 on success, -ve on failure */ -int i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, - int len); +int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, + int len); /** - * i2c_probe() - probe a particular chip address + * dm_i2c_probe() - probe a particular chip address * * This can be useful to check for the existence of a chip on the bus. * It is typically implemented by writing the chip address to the bus @@ -122,8 +121,8 @@ int i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, * @devp: Returns the device found, or NULL if none * @return 0 if a chip was found at that address, -ve if not */ -int i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, - struct udevice **devp); +int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, + struct udevice **devp); /** * i2c_set_bus_speed() - set the speed of a bus diff --git a/test/dm/i2c.c b/test/dm/i2c.c index a53e28dbe5d..4823b0c6483 100644 --- a/test/dm/i2c.c +++ b/test/dm/i2c.c @@ -35,8 +35,8 @@ static int dm_test_i2c_find(struct dm_test_state *dms) * remove the emulation and the slave device. */ ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); - ut_assertok(i2c_probe(bus, chip, 0, &dev)); - ut_asserteq(-ENODEV, i2c_probe(bus, no_chip, 0, &dev)); + ut_assertok(dm_i2c_probe(bus, chip, 0, &dev)); + ut_asserteq(-ENODEV, dm_i2c_probe(bus, no_chip, 0, &dev)); ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_I2C, 1, &bus)); return 0; @@ -50,10 +50,10 @@ static int dm_test_i2c_read_write(struct dm_test_state *dms) ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); ut_assertok(i2c_get_chip(bus, chip, &dev)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0\0\0\0", sizeof(buf))); - ut_assertok(i2c_write(dev, 2, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0AB\0", sizeof(buf))); return 0; @@ -68,11 +68,11 @@ static int dm_test_i2c_speed(struct dm_test_state *dms) ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); ut_assertok(i2c_get_chip(bus, chip, &dev)); ut_assertok(i2c_set_bus_speed(bus, 100000)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(i2c_set_bus_speed(bus, 400000)); ut_asserteq(400000, i2c_get_bus_speed(bus)); - ut_assertok(i2c_read(dev, 0, buf, 5)); - ut_asserteq(-EINVAL, i2c_write(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq(-EINVAL, dm_i2c_write(dev, 0, buf, 5)); return 0; } @@ -86,7 +86,7 @@ static int dm_test_i2c_offset_len(struct dm_test_state *dms) ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); ut_assertok(i2c_get_chip(bus, chip, &dev)); ut_assertok(i2c_set_chip_offset_len(dev, 1)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); /* This is not supported by the uclass */ ut_asserteq(-EINVAL, i2c_set_chip_offset_len(dev, 5)); @@ -100,7 +100,7 @@ static int dm_test_i2c_probe_empty(struct dm_test_state *dms) struct udevice *bus, *dev; ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); - ut_assertok(i2c_probe(bus, SANDBOX_I2C_TEST_ADDR, 0, &dev)); + ut_assertok(dm_i2c_probe(bus, SANDBOX_I2C_TEST_ADDR, 0, &dev)); return 0; } @@ -114,7 +114,7 @@ static int dm_test_i2c_bytewise(struct dm_test_state *dms) ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); ut_assertok(i2c_get_chip(bus, chip, &dev)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0\0\0\0", sizeof(buf))); /* Tell the EEPROM to only read/write one register at a time */ @@ -123,34 +123,34 @@ static int dm_test_i2c_bytewise(struct dm_test_state *dms) sandbox_i2c_eeprom_set_test_mode(eeprom, SIE_TEST_MODE_SINGLE_BYTE); /* Now we only get the first byte - the rest will be 0xff */ - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\xff\xff\xff\xff", sizeof(buf))); /* If we do a separate transaction for each byte, it works */ ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0\0\0\0", sizeof(buf))); /* This will only write A */ ut_assertok(i2c_set_chip_flags(dev, 0)); - ut_assertok(i2c_write(dev, 2, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\xff\xff\xff\xff", sizeof(buf))); /* Check that the B was ignored */ ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0A\0\0\0", sizeof(buf))); /* Now write it again with the new flags, it should work */ ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_WR_ADDRESS)); - ut_assertok(i2c_write(dev, 2, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\xff\xff\xff\xff", sizeof(buf))); ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_WR_ADDRESS | DM_I2C_CHIP_RD_ADDRESS)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0AB\0\0", sizeof(buf))); /* Restore defaults */ @@ -170,42 +170,42 @@ static int dm_test_i2c_offset(struct dm_test_state *dms) ut_assertok(i2c_get_chip_for_busnum(busnum, chip, &dev)); /* Do a transfer so we can find the emulator */ - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(uclass_first_device(UCLASS_I2C_EMUL, &eeprom)); /* Offset length 0 */ sandbox_i2c_eeprom_set_offset_len(eeprom, 0); ut_assertok(i2c_set_chip_offset_len(dev, 0)); - ut_assertok(i2c_write(dev, 10 /* ignored */, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_write(dev, 10 /* ignored */, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "AB\0\0\0\0", sizeof(buf))); /* Offset length 1 */ sandbox_i2c_eeprom_set_offset_len(eeprom, 1); ut_assertok(i2c_set_chip_offset_len(dev, 1)); - ut_assertok(i2c_write(dev, 2, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "ABAB\0", sizeof(buf))); /* Offset length 2 */ sandbox_i2c_eeprom_set_offset_len(eeprom, 2); ut_assertok(i2c_set_chip_offset_len(dev, 2)); - ut_assertok(i2c_write(dev, 0x210, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0x210, buf, 5)); + ut_assertok(dm_i2c_write(dev, 0x210, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x210, buf, 5)); ut_assertok(memcmp(buf, "AB\0\0\0", sizeof(buf))); /* Offset length 3 */ sandbox_i2c_eeprom_set_offset_len(eeprom, 2); ut_assertok(i2c_set_chip_offset_len(dev, 2)); - ut_assertok(i2c_write(dev, 0x410, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0x410, buf, 5)); + ut_assertok(dm_i2c_write(dev, 0x410, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x410, buf, 5)); ut_assertok(memcmp(buf, "AB\0\0\0", sizeof(buf))); /* Offset length 4 */ sandbox_i2c_eeprom_set_offset_len(eeprom, 2); ut_assertok(i2c_set_chip_offset_len(dev, 2)); - ut_assertok(i2c_write(dev, 0x420, (uint8_t *)"AB", 2)); - ut_assertok(i2c_read(dev, 0x420, buf, 5)); + ut_assertok(dm_i2c_write(dev, 0x420, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x420, buf, 5)); ut_assertok(memcmp(buf, "AB\0\0\0", sizeof(buf))); /* Restore defaults */ -- cgit v1.3.1 From 73845350b6281a7afeeb279475e6eb613d7a89f9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 12 Jan 2015 18:02:08 -0700 Subject: dm: i2c: Add a compatbility layer For boards which use multiple I2C devices, or for SOCs which support multiple boards, we might want to convert these to driver model at different times. At present this is difficult because we need to either use CONFIG_DM_I2C for a board or not. Add a compatibility layer which implements the old API, thus allowing a board to move to driver model for I2C without requiring that everything it uses is moved in the same commit. Signed-off-by: Simon Glass --- Makefile | 7 +++ drivers/i2c/Makefile | 1 + drivers/i2c/i2c-uclass-compat.c | 98 +++++++++++++++++++++++++++++++++++++++++ include/i2c.h | 59 +++++++++++++++++++++++++ 4 files changed, 165 insertions(+) create mode 100644 drivers/i2c/i2c-uclass-compat.c (limited to 'drivers') diff --git a/Makefile b/Makefile index ea5ae8fac72..9b406c84470 100644 --- a/Makefile +++ b/Makefile @@ -776,6 +776,13 @@ ifneq ($(CONFIG_SYS_GENERIC_BOARD),y) @echo "See doc/README.generic-board for further information" @echo "====================================================" endif +ifeq ($(CONFIG_DM_I2C_COMPAT),y) + @echo "===================== WARNING ======================" + @echo "This board uses CONFIG_DM_I2C_COMPAT. Please remove" + @echo "(possibly in a subsequent patch in your series)" + @echo "before sending patches to the mailing list." + @echo "====================================================" +endif PHONY += dtbs dtbs dts/dt.dtb: checkdtc u-boot diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 0e4c9f466a1..774bc94a4a7 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0+ # obj-$(CONFIG_DM_I2C) += i2c-uclass.o +obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o diff --git a/drivers/i2c/i2c-uclass-compat.c b/drivers/i2c/i2c-uclass-compat.c new file mode 100644 index 00000000000..c29fc89e048 --- /dev/null +++ b/drivers/i2c/i2c-uclass-compat.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +static int cur_busnum; + +static int i2c_compat_get_device(uint chip_addr, int alen, + struct udevice **devp) +{ + struct dm_i2c_chip *chip; + int ret; + + ret = i2c_get_chip_for_busnum(cur_busnum, chip_addr, devp); + if (ret) + return ret; + chip = dev_get_parentdata(*devp); + if (chip->offset_len != alen) { + printf("Requested alen %d does not match chip offset_len %d\n", + alen, chip->offset_len); + return -EADDRNOTAVAIL; + } + + return 0; +} + +int i2c_probe(uint8_t chip_addr) +{ + struct udevice *bus, *dev; + int ret; + + ret = uclass_get_device_by_seq(UCLASS_I2C, cur_busnum, &bus); + if (ret) { + debug("Cannot find I2C bus %d: err=%d\n", cur_busnum, ret); + return ret; + } + + if (!bus) + return -ENOENT; + + return dm_i2c_probe(bus, chip_addr, 0, &dev); +} + +int i2c_read(uint8_t chip_addr, unsigned int addr, int alen, uint8_t *buffer, + int len) +{ + struct udevice *dev; + int ret; + + ret = i2c_compat_get_device(chip_addr, alen, &dev); + if (ret) + return ret; + + return dm_i2c_read(dev, addr, buffer, len); +} + +int i2c_write(uint8_t chip_addr, unsigned int addr, int alen, uint8_t *buffer, + int len) +{ + struct udevice *dev; + int ret; + + ret = i2c_compat_get_device(chip_addr, alen, &dev); + if (ret) + return ret; + + return dm_i2c_write(dev, addr, buffer, len); +} + +int i2c_get_bus_num_fdt(int node) +{ + struct udevice *bus; + int ret; + + ret = uclass_get_device_by_of_offset(UCLASS_I2C, node, &bus); + if (ret) + return ret; + + return bus->seq; +} + +unsigned int i2c_get_bus_num(void) +{ + return cur_busnum; +} + +int i2c_set_bus_num(unsigned int bus) +{ + cur_busnum = bus; + + return 0; +} diff --git a/include/i2c.h b/include/i2c.h index 30d53c8a9df..47529f4b283 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -184,6 +184,65 @@ int i2c_set_chip_offset_len(struct udevice *dev, uint offset_len); */ int i2c_deblock(struct udevice *bus); +#ifdef CONFIG_DM_I2C_COMPAT +/** + * i2c_probe() - Compatibility function for driver model + * + * Calls dm_i2c_probe() on the current bus + */ +int i2c_probe(uint8_t chip_addr); + +/** + * i2c_read() - Compatibility function for driver model + * + * Calls dm_i2c_read() with the device corresponding to @chip_addr, and offset + * set to @addr. @alen must match the current setting for the device. + */ +int i2c_read(uint8_t chip_addr, unsigned int addr, int alen, uint8_t *buffer, + int len); + +/** + * i2c_write() - Compatibility function for driver model + * + * Calls dm_i2c_write() with the device corresponding to @chip_addr, and offset + * set to @addr. @alen must match the current setting for the device. + */ +int i2c_write(uint8_t chip_addr, unsigned int addr, int alen, uint8_t *buffer, + int len); + +/** + * i2c_get_bus_num_fdt() - Compatibility function for driver model + * + * @return the bus number associated with the given device tree node + */ +int i2c_get_bus_num_fdt(int node); + +/** + * i2c_get_bus_num() - Compatibility function for driver model + * + * @return the 'current' bus number + */ +unsigned int i2c_get_bus_num(void); + +/** + * i2c_set_bus_num(): Compatibility function for driver model + * + * Sets the 'current' bus + */ +int i2c_set_bus_num(unsigned int bus); + +static inline void I2C_SET_BUS(unsigned int bus) +{ + i2c_set_bus_num(bus); +} + +static inline unsigned int I2C_GET_BUS(void) +{ + return i2c_get_bus_num(); +} + +#endif + /* * Not all of these flags are implemented in the U-Boot API */ -- cgit v1.3.1 From 25ab4b0303f2df5e6b94ed92e37875a7c98f4de3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:26:55 -0700 Subject: dm: i2c: Provide an offset length parameter where needed Rather than assuming that the chip offset length is 1, allow it to be provided. This allows chips that don't use the default offset length to be used (at present they are only supported by the command line 'i2c' command which sets the offset length explicitly). Signed-off-by: Simon Glass Acked-by: Heiko Schocher --- arch/arm/cpu/tegra20-common/pmu.c | 2 +- board/avionic-design/common/tamonten-ng.c | 2 +- board/nvidia/cardhu/cardhu.c | 4 ++-- board/nvidia/dalmore/dalmore.c | 4 ++-- board/nvidia/whistler/whistler.c | 4 ++-- board/toradex/apalis_t30/apalis_t30.c | 2 +- common/cmd_i2c.c | 2 +- drivers/i2c/i2c-uclass.c | 16 +++++++++------- drivers/i2c/sandbox_i2c.c | 2 +- drivers/power/as3722.c | 2 +- include/i2c.h | 8 ++++++-- test/dm/i2c.c | 10 +++++----- 12 files changed, 32 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/arch/arm/cpu/tegra20-common/pmu.c b/arch/arm/cpu/tegra20-common/pmu.c index 36a76a24d97..a774246a277 100644 --- a/arch/arm/cpu/tegra20-common/pmu.c +++ b/arch/arm/cpu/tegra20-common/pmu.c @@ -52,7 +52,7 @@ int pmu_set_nominal(void) debug("%s: Cannot find DVC I2C bus\n", __func__); return ret; } - ret = i2c_get_chip(bus, PMI_I2C_ADDRESS, &dev); + ret = i2c_get_chip(bus, PMI_I2C_ADDRESS, 1, &dev); if (ret) { debug("%s: Cannot find DVC I2C chip\n", __func__); return ret; diff --git a/board/avionic-design/common/tamonten-ng.c b/board/avionic-design/common/tamonten-ng.c index bca91831e0e..17046271126 100644 --- a/board/avionic-design/common/tamonten-ng.c +++ b/board/avionic-design/common/tamonten-ng.c @@ -55,7 +55,7 @@ void pmu_write(uchar reg, uchar data) struct udevice *dev; int ret; - ret = i2c_get_chip_for_busnum(4, PMU_I2C_ADDRESS, &dev); + ret = i2c_get_chip_for_busnum(4, PMU_I2C_ADDRESS, 1, &dev); if (ret) { debug("%s: Cannot find PMIC I2C chip\n", __func__); return; diff --git a/board/nvidia/cardhu/cardhu.c b/board/nvidia/cardhu/cardhu.c index fc31d29140b..1540526a613 100644 --- a/board/nvidia/cardhu/cardhu.c +++ b/board/nvidia/cardhu/cardhu.c @@ -46,7 +46,7 @@ void board_sdmmc_voltage_init(void) int ret; int i; - ret = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, &dev); + ret = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, 1, &dev); if (ret) { debug("%s: Cannot find PMIC I2C chip\n", __func__); return; @@ -94,7 +94,7 @@ int tegra_pcie_board_init(void) u8 addr, data[1]; int err; - err = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, &dev); + err = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, 1, &dev); if (err) { debug("failed to find PMU bus\n"); return err; diff --git a/board/nvidia/dalmore/dalmore.c b/board/nvidia/dalmore/dalmore.c index c0991c57a97..d7c1a695ff8 100644 --- a/board/nvidia/dalmore/dalmore.c +++ b/board/nvidia/dalmore/dalmore.c @@ -55,7 +55,7 @@ void board_sdmmc_voltage_init(void) uchar reg, data_buffer[1]; int ret; - ret = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, &dev); + ret = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, 1, &dev); if (ret) { debug("%s: Cannot find PMIC I2C chip\n", __func__); return; @@ -83,7 +83,7 @@ void board_sdmmc_voltage_init(void) data_buffer[0] = 0x03; reg = 0x14; - ret = i2c_get_chip_for_busnum(0, BAT_I2C_ADDRESS, &dev); + ret = i2c_get_chip_for_busnum(0, BAT_I2C_ADDRESS, 1, &dev); if (ret) { debug("%s: Cannot find charger I2C chip\n", __func__); return; diff --git a/board/nvidia/whistler/whistler.c b/board/nvidia/whistler/whistler.c index ad6ea091df7..3476f1159fe 100644 --- a/board/nvidia/whistler/whistler.c +++ b/board/nvidia/whistler/whistler.c @@ -27,7 +27,7 @@ void pin_mux_mmc(void) int ret; /* Turn on MAX8907B LDO12 to 2.8V for J40 power */ - ret = i2c_get_chip_for_busnum(0, 0x3c, &dev); + ret = i2c_get_chip_for_busnum(0, 0x3c, 1, &dev); if (ret) { printf("%s: Cannot find MAX8907B I2C chip\n", __func__); return; @@ -64,7 +64,7 @@ void pin_mux_usb(void) */ /* Turn on TAC6416's GPIO 0+1 for USB1/3's VBUS */ - ret = i2c_get_chip_for_busnum(0, 0x20, &dev); + ret = i2c_get_chip_for_busnum(0, 0x20, 1, &dev); if (ret) { printf("%s: Cannot find TAC6416 I2C chip\n", __func__); return; diff --git a/board/toradex/apalis_t30/apalis_t30.c b/board/toradex/apalis_t30/apalis_t30.c index 1c4b4c1cd48..624421496a4 100644 --- a/board/toradex/apalis_t30/apalis_t30.c +++ b/board/toradex/apalis_t30/apalis_t30.c @@ -42,7 +42,7 @@ int tegra_pcie_board_init(void) u8 addr, data[1]; int err; - err = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, &dev); + err = i2c_get_chip_for_busnum(0, PMU_I2C_ADDRESS, 1, &dev); if (err) { debug("%s: Cannot find PMIC I2C chip\n", __func__); return err; diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c index 1e500fbf2cc..7c3ad00fdf0 100644 --- a/common/cmd_i2c.c +++ b/common/cmd_i2c.c @@ -168,7 +168,7 @@ static int i2c_get_cur_bus_chip(uint chip_addr, struct udevice **devp) if (ret) return ret; - return i2c_get_chip(bus, chip_addr, devp); + return i2c_get_chip(bus, chip_addr, 1, devp); } #endif diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 25f2c18cf4f..24e5ec68f2b 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -220,7 +220,7 @@ static int i2c_probe_chip(struct udevice *bus, uint chip_addr, return ops->xfer(bus, msg, 1); } -static int i2c_bind_driver(struct udevice *bus, uint chip_addr, +static int i2c_bind_driver(struct udevice *bus, uint chip_addr, uint offset_len, struct udevice **devp) { struct dm_i2c_chip chip; @@ -238,7 +238,7 @@ static int i2c_bind_driver(struct udevice *bus, uint chip_addr, /* Tell the device what we know about it */ memset(&chip, '\0', sizeof(chip)); chip.chip_addr = chip_addr; - chip.offset_len = 1; /* we assume */ + chip.offset_len = offset_len; ret = device_probe_child(dev, &chip); debug("%s: device_probe_child: ret=%d\n", __func__, ret); if (ret) @@ -254,7 +254,8 @@ err_bind: return ret; } -int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp) +int i2c_get_chip(struct udevice *bus, uint chip_addr, uint offset_len, + struct udevice **devp) { struct udevice *dev; @@ -281,10 +282,11 @@ int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp) } } debug("not found\n"); - return i2c_bind_driver(bus, chip_addr, devp); + return i2c_bind_driver(bus, chip_addr, offset_len, devp); } -int i2c_get_chip_for_busnum(int busnum, int chip_addr, struct udevice **devp) +int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len, + struct udevice **devp) { struct udevice *bus; int ret; @@ -294,7 +296,7 @@ int i2c_get_chip_for_busnum(int busnum, int chip_addr, struct udevice **devp) debug("Cannot find I2C bus %d\n", busnum); return ret; } - ret = i2c_get_chip(bus, chip_addr, devp); + ret = i2c_get_chip(bus, chip_addr, offset_len, devp); if (ret) { debug("Cannot find I2C chip %02x on bus %d\n", chip_addr, busnum); @@ -319,7 +321,7 @@ int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, return ret; /* The chip was found, see if we have a driver, and probe it */ - ret = i2c_get_chip(bus, chip_addr, devp); + ret = i2c_get_chip(bus, chip_addr, 1, devp); debug("%s: i2c_get_chip: ret=%d\n", __func__, ret); return ret; diff --git a/drivers/i2c/sandbox_i2c.c b/drivers/i2c/sandbox_i2c.c index f0e9f51a1f2..e2f6c3b9bb5 100644 --- a/drivers/i2c/sandbox_i2c.c +++ b/drivers/i2c/sandbox_i2c.c @@ -60,7 +60,7 @@ static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, if (msg->addr == SANDBOX_I2C_TEST_ADDR) return 0; - ret = i2c_get_chip(bus, msg->addr, &dev); + ret = i2c_get_chip(bus, msg->addr, 1, &dev); if (ret) return ret; diff --git a/drivers/power/as3722.c b/drivers/power/as3722.c index 3aafdc96704..a60bb5f83fb 100644 --- a/drivers/power/as3722.c +++ b/drivers/power/as3722.c @@ -242,7 +242,7 @@ int as3722_init(struct udevice **devp) const unsigned int address = 0x40; int err; - err = i2c_get_chip_for_busnum(bus, address, &pmic); + err = i2c_get_chip_for_busnum(bus, address, 1, &pmic); if (err) return err; err = as3722_read_id(pmic, &id, &revision); diff --git a/include/i2c.h b/include/i2c.h index 47529f4b283..76090b7a93a 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -388,10 +388,12 @@ struct dm_i2c_ops { * * @bus: Bus to examine * @chip_addr: Chip address for the new device + * @offset_len: Length of a register offset in bytes (normally 1) * @devp: Returns pointer to new device if found or -ENODEV if not * found */ -int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp); +int i2c_get_chip(struct udevice *bus, uint chip_addr, uint offset_len, + struct udevice **devp); /** * i2c_get_chip() - get a device to use to access a chip on a bus number @@ -401,10 +403,12 @@ int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp); * * @busnum: Bus number to examine * @chip_addr: Chip address for the new device + * @offset_len: Length of a register offset in bytes (normally 1) * @devp: Returns pointer to new device if found or -ENODEV if not * found */ -int i2c_get_chip_for_busnum(int busnum, int chip_addr, struct udevice **devp); +int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len, + struct udevice **devp); /** * i2c_chip_ofdata_to_platdata() - Decode standard I2C platform data diff --git a/test/dm/i2c.c b/test/dm/i2c.c index 4823b0c6483..ef88372d563 100644 --- a/test/dm/i2c.c +++ b/test/dm/i2c.c @@ -49,7 +49,7 @@ static int dm_test_i2c_read_write(struct dm_test_state *dms) uint8_t buf[5]; ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); - ut_assertok(i2c_get_chip(bus, chip, &dev)); + ut_assertok(i2c_get_chip(bus, chip, 1, &dev)); ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0\0\0\0", sizeof(buf))); ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); @@ -66,7 +66,7 @@ static int dm_test_i2c_speed(struct dm_test_state *dms) uint8_t buf[5]; ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); - ut_assertok(i2c_get_chip(bus, chip, &dev)); + ut_assertok(i2c_get_chip(bus, chip, 1, &dev)); ut_assertok(i2c_set_bus_speed(bus, 100000)); ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(i2c_set_bus_speed(bus, 400000)); @@ -84,7 +84,7 @@ static int dm_test_i2c_offset_len(struct dm_test_state *dms) uint8_t buf[5]; ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); - ut_assertok(i2c_get_chip(bus, chip, &dev)); + ut_assertok(i2c_get_chip(bus, chip, 1, &dev)); ut_assertok(i2c_set_chip_offset_len(dev, 1)); ut_assertok(dm_i2c_read(dev, 0, buf, 5)); @@ -113,7 +113,7 @@ static int dm_test_i2c_bytewise(struct dm_test_state *dms) uint8_t buf[5]; ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); - ut_assertok(i2c_get_chip(bus, chip, &dev)); + ut_assertok(i2c_get_chip(bus, chip, 1, &dev)); ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_assertok(memcmp(buf, "\0\0\0\0\0", sizeof(buf))); @@ -167,7 +167,7 @@ static int dm_test_i2c_offset(struct dm_test_state *dms) struct udevice *dev; uint8_t buf[5]; - ut_assertok(i2c_get_chip_for_busnum(busnum, chip, &dev)); + ut_assertok(i2c_get_chip_for_busnum(busnum, chip, 1, &dev)); /* Do a transfer so we can find the emulator */ ut_assertok(dm_i2c_read(dev, 0, buf, 5)); -- cgit v1.3.1 From 2f3b95dbc78ce96b0f9f471e688db66223988419 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:26:58 -0700 Subject: dm: core: Set device tree node for root device The root device corresponds to the root device tree node, so set this up. Also add a few notes to the documentation. Signed-off-by: Simon Glass --- doc/driver-model/README.txt | 4 ++++ drivers/core/root.c | 3 +++ 2 files changed, 7 insertions(+) (limited to 'drivers') diff --git a/doc/driver-model/README.txt b/doc/driver-model/README.txt index eafa825ab44..40415698cf4 100644 --- a/doc/driver-model/README.txt +++ b/doc/driver-model/README.txt @@ -363,6 +363,10 @@ can leave out platdata_auto_alloc_size. In this case you can use malloc in your ofdata_to_platdata (or probe) method to allocate the required memory, and you should free it in the remove method. +The driver model tree is intended to mirror that of the device tree. The +root driver is at device tree offset 0 (the root node, '/'), and its +children are the children of the root node. + Declaring Uclasses ------------------ diff --git a/drivers/core/root.c b/drivers/core/root.c index 47b3acfbe98..a5b0a615016 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -49,6 +49,9 @@ int dm_init(void) ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST); if (ret) return ret; +#ifdef CONFIG_OF_CONTROL + DM_ROOT_NON_CONST->of_offset = 0; +#endif ret = device_probe(DM_ROOT_NON_CONST); if (ret) return ret; -- cgit v1.3.1 From 72ebfe86fac2ca0a0e1af9fe1eaa3a634e3e17a1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:26:59 -0700 Subject: dm: core: Tidy up error handling in device_bind() Make the error handling more standard to make it easier to build on top of it. Also correct a bug in the error path where there is no parent. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/device.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/core/device.c b/drivers/core/device.c index 963b16f26f0..eca8edac26a 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -81,18 +81,13 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, ret = uclass_bind_device(dev); if (ret) - goto fail_bind; + goto fail_uclass_bind; /* if we fail to bind we remove device from successors and free it */ if (drv->bind) { ret = drv->bind(dev); - if (ret) { - if (uclass_unbind_device(dev)) { - dm_warn("Failed to unbind dev '%s' on error path\n", - dev->name); - } + if (ret) goto fail_bind; - } } if (parent) dm_dbg("Bound device %s to %s\n", dev->name, parent->name); @@ -101,8 +96,15 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, return 0; fail_bind: - list_del(&dev->sibling_node); + if (uclass_unbind_device(dev)) { + dm_warn("Failed to unbind dev '%s' on error path\n", + dev->name); + } +fail_uclass_bind: + if (parent) + list_del(&dev->sibling_node); free(dev); + return ret; } -- cgit v1.3.1 From f8a85449ef3e0963add728815771ccc09aa99875 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:00 -0700 Subject: dm: core: Allocate platform data when binding a device When using allocated platform data, allocate it when we bind the device. This makes it possible to fill in this information before the device is probed. This fits with the platform data model (when not using device tree), since platform data exists at bind-time. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/device-remove.c | 8 ++++---- drivers/core/device.c | 22 +++++++++++++--------- test/dm/test-fdt.c | 4 ++-- 3 files changed, 19 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 8fc6b710842..2c8257752b6 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -88,6 +88,10 @@ int device_unbind(struct udevice *dev) if (ret) return ret; + if (dev->flags & DM_FLAG_ALLOC_PDATA) { + free(dev->platdata); + dev->platdata = NULL; + } ret = uclass_unbind_device(dev); if (ret) return ret; @@ -111,10 +115,6 @@ void device_free(struct udevice *dev) free(dev->priv); dev->priv = NULL; } - if (dev->flags & DM_FLAG_ALLOC_PDATA) { - free(dev->platdata); - dev->platdata = NULL; - } size = dev->uclass->uc_drv->per_device_auto_alloc_size; if (size) { free(dev->uclass_priv); diff --git a/drivers/core/device.c b/drivers/core/device.c index eca8edac26a..366cffed89d 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -72,8 +72,14 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, #else dev->req_seq = -1; #endif - if (!dev->platdata && drv->platdata_auto_alloc_size) + if (!dev->platdata && drv->platdata_auto_alloc_size) { dev->flags |= DM_FLAG_ALLOC_PDATA; + dev->platdata = calloc(1, drv->platdata_auto_alloc_size); + if (!dev->platdata) { + ret = -ENOMEM; + goto fail_alloc1; + } + } /* put dev into parent's successor list */ if (parent) @@ -103,6 +109,11 @@ fail_bind: fail_uclass_bind: if (parent) list_del(&dev->sibling_node); + if (dev->flags & DM_FLAG_ALLOC_PDATA) { + free(dev->platdata); + dev->platdata = NULL; + } +fail_alloc1: free(dev); return ret; @@ -139,7 +150,7 @@ int device_probe_child(struct udevice *dev, void *parent_priv) drv = dev->driver; assert(drv); - /* Allocate private data and platdata if requested */ + /* Allocate private data if requested */ if (drv->priv_auto_alloc_size) { dev->priv = calloc(1, drv->priv_auto_alloc_size); if (!dev->priv) { @@ -148,13 +159,6 @@ int device_probe_child(struct udevice *dev, void *parent_priv) } } /* Allocate private data if requested */ - if (dev->flags & DM_FLAG_ALLOC_PDATA) { - dev->platdata = calloc(1, drv->platdata_auto_alloc_size); - if (!dev->platdata) { - ret = -ENOMEM; - goto fail; - } - } size = dev->uclass->uc_drv->per_device_auto_alloc_size; if (size) { dev->uclass_priv = calloc(1, size); diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index cd2c38995e9..dc4ebf9adaf 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -143,12 +143,12 @@ static int dm_test_fdt(struct dm_test_state *dms) /* These are num_devices compatible root-level device tree nodes */ ut_asserteq(num_devices, list_count_items(&uc->dev_head)); - /* Each should have no platdata / priv */ + /* Each should have platform data but no private data */ for (i = 0; i < num_devices; i++) { ret = uclass_find_device(UCLASS_TEST_FDT, i, &dev); ut_assert(!ret); ut_assert(!dev_get_priv(dev)); - ut_assert(!dev->platdata); + ut_assert(dev->platdata); } ut_assertok(dm_check_devices(dms, num_devices)); -- cgit v1.3.1 From cdc133bde9dd221319b9cf7fd99fcb4da8ba195c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:01 -0700 Subject: dm: core: Allow parents to have platform data for their children For buses it is common for parents to need to know the address of the child on the bus, the bus speed to use for that child, and other information. This can be provided in platform data attached to each child. Add driver model support for this, including auto-allocation which can be requested using a new property to specify the size of the data. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/device-remove.c | 4 +++ drivers/core/device.c | 30 ++++++++++++++-- include/dm/device.h | 19 +++++++++++ test/dm/bus.c | 81 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 2c8257752b6..56c358a0ec2 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -92,6 +92,10 @@ int device_unbind(struct udevice *dev) free(dev->platdata); dev->platdata = NULL; } + if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) { + free(dev->parent_platdata); + dev->parent_platdata = NULL; + } ret = uclass_unbind_device(dev); if (ret) return ret; diff --git a/drivers/core/device.c b/drivers/core/device.c index 366cffed89d..ee97cc84fff 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -80,6 +80,18 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, goto fail_alloc1; } } + if (parent) { + int size = parent->driver->per_child_platdata_auto_alloc_size; + + if (size) { + dev->flags |= DM_FLAG_ALLOC_PARENT_PDATA; + dev->parent_platdata = calloc(1, size); + if (!dev->parent_platdata) { + ret = -ENOMEM; + goto fail_alloc2; + } + } + } /* put dev into parent's successor list */ if (parent) @@ -107,8 +119,12 @@ fail_bind: dev->name); } fail_uclass_bind: - if (parent) - list_del(&dev->sibling_node); + list_del(&dev->sibling_node); + if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) { + free(dev->parent_platdata); + dev->parent_platdata = NULL; + } +fail_alloc2: if (dev->flags & DM_FLAG_ALLOC_PDATA) { free(dev->platdata); dev->platdata = NULL; @@ -247,6 +263,16 @@ void *dev_get_platdata(struct udevice *dev) return dev->platdata; } +void *dev_get_parent_platdata(struct udevice *dev) +{ + if (!dev) { + dm_warn("%s: null device", __func__); + return NULL; + } + + return dev->parent_platdata; +} + void *dev_get_priv(struct udevice *dev) { if (!dev) { diff --git a/include/dm/device.h b/include/dm/device.h index 13598a15b68..096d84b7b6f 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -26,6 +26,9 @@ struct driver_info; /* DM should init this device prior to relocation */ #define DM_FLAG_PRE_RELOC (1 << 2) +/* DM is responsible for allocating and freeing parent_platdata */ +#define DM_FLAG_ALLOC_PARENT_PDATA (1 << 3) + /** * struct udevice - An instance of a driver * @@ -46,6 +49,7 @@ struct driver_info; * @driver: The driver used by this device * @name: Name of device, typically the FDT node name * @platdata: Configuration data for this device + * @parent_platdata: The parent bus's configuration data for this device * @of_offset: Device tree node offset for this device (- for none) * @of_id: Pointer to the udevice_id structure which created the device * @parent: Parent of this device, or NULL for the top level device @@ -65,6 +69,7 @@ struct udevice { struct driver *driver; const char *name; void *platdata; + void *parent_platdata; int of_offset; const struct udevice_id *of_id; struct udevice *parent; @@ -146,6 +151,9 @@ struct udevice_id { * device_probe_child() pass it in. So far the use case for allocating it * is SPI, but I found that unsatisfactory. Since it is here I will leave it * until things are clearer. + * @per_child_platdata_auto_alloc_size: A bus likes to store information about + * its children. If non-zero this is the size of this data, to be allocated + * in the child's parent_platdata pointer. * @ops: Driver-specific operations. This is typically a list of function * pointers defined by the driver, to implement driver functions required by * the uclass. @@ -165,6 +173,7 @@ struct driver { int priv_auto_alloc_size; int platdata_auto_alloc_size; int per_child_auto_alloc_size; + int per_child_platdata_auto_alloc_size; const void *ops; /* driver-specific operations */ uint32_t flags; }; @@ -183,6 +192,16 @@ struct driver { */ void *dev_get_platdata(struct udevice *dev); +/** + * dev_get_parent_platdata() - Get the parent platform data for a device + * + * This checks that dev is not NULL, but no other checks for now + * + * @dev Device to check + * @return parent's platform data, or NULL if none + */ +void *dev_get_parent_platdata(struct udevice *dev); + /** * dev_get_parentdata() - Get the parent data for a device * diff --git a/test/dm/bus.c b/test/dm/bus.c index abbaccff509..63c8a9f7385 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -9,11 +9,16 @@ #include #include #include +#include #include #include DECLARE_GLOBAL_DATA_PTR; +struct dm_test_parent_platdata { + int count; +}; + enum { FLAG_CHILD_PROBED = 10, FLAG_CHILD_REMOVED = -7, @@ -62,6 +67,8 @@ U_BOOT_DRIVER(testbus_drv) = { .priv_auto_alloc_size = sizeof(struct dm_test_priv), .platdata_auto_alloc_size = sizeof(struct dm_test_pdata), .per_child_auto_alloc_size = sizeof(struct dm_test_parent_data), + .per_child_platdata_auto_alloc_size = + sizeof(struct dm_test_parent_platdata), .child_pre_probe = testbus_child_pre_probe, .child_post_remove = testbus_child_post_remove, }; @@ -271,3 +278,77 @@ static int dm_test_bus_parent_ops(struct dm_test_state *dms) return 0; } DM_TEST(dm_test_bus_parent_ops, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test that the bus can store platform data about each child */ +static int dm_test_bus_parent_platdata(struct dm_test_state *dms) +{ + struct dm_test_parent_platdata *plat; + struct udevice *bus, *dev; + int child_count; + + /* Check that the bus has no children */ + ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus)); + device_find_first_child(bus, &dev); + ut_asserteq_ptr(NULL, dev); + + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + + for (device_find_first_child(bus, &dev), child_count = 0; + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_platdata(dev); + ut_assert(plat != NULL); + + /* + * Check that it is not affected by the device being + * probed/removed + */ + plat->count++; + ut_asserteq(1, plat->count); + device_probe(dev); + device_remove(dev); + + ut_asserteq_ptr(plat, dev_get_parent_platdata(dev)); + ut_asserteq(1, plat->count); + ut_assertok(device_probe(dev)); + child_count++; + } + ut_asserteq(3, child_count); + + /* Removing the bus should also have no effect (it is still bound) */ + device_remove(bus); + for (device_find_first_child(bus, &dev), child_count = 0; + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_platdata(dev); + ut_assert(plat != NULL); + ut_asserteq(1, plat->count); + child_count++; + } + ut_asserteq(3, child_count); + + /* Unbind all the children */ + do { + device_find_first_child(bus, &dev); + if (dev) + device_unbind(dev); + } while (dev); + + /* Now the child platdata should be removed and re-added */ + device_probe(bus); + for (device_find_first_child(bus, &dev), child_count = 0; + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_platdata(dev); + ut_assert(plat != NULL); + ut_asserteq(0, plat->count); + child_count++; + } + ut_asserteq(3, child_count); + + return 0; +} +DM_TEST(dm_test_bus_parent_platdata, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From ba8da9dc43ac8ae3351345df12dc7f9d1cd07ae0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:02 -0700 Subject: dm: core: Allow uclasses to specify platdata for a device's children In many cases the child platform data for a device's children is defined by the uclass rather than the individual devices. For example, a SPI bus needs to know the chip select and speed for each of its children. It makes sense to allow this information to be defined the SPI uclass rather than each individual driver. If the device provides a size value for its child platdata, then use it. Failng that, fall back to that provided by the uclass. Reviewed-by: Masahiro Yamada Signed-off-by: Simon Glass --- drivers/core/device.c | 4 ++++ include/dm/uclass.h | 5 +++++ test/dm/bus.c | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/core/device.c b/drivers/core/device.c index ee97cc84fff..2f33b0e6a3b 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -83,6 +83,10 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, if (parent) { int size = parent->driver->per_child_platdata_auto_alloc_size; + if (!size) { + size = parent->uclass->uc_drv-> + per_child_platdata_auto_alloc_size; + } if (size) { dev->flags |= DM_FLAG_ALLOC_PARENT_PDATA; dev->parent_platdata = calloc(1, size); diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 2577ae6489b..7d92d34bec0 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -60,6 +60,10 @@ struct udevice; * @per_device_auto_alloc_size: Each device can hold private data owned * by the uclass. If required this will be automatically allocated if this * value is non-zero. + * @per_child_platdata_auto_alloc_size: A bus likes to store information about + * its children. If non-zero this is the size of this data, to be allocated + * in the child device's parent_platdata pointer. This value is only used as + * a falback if this member is 0 in the driver. * @ops: Uclass operations, providing the consistent interface to devices * within the uclass. */ @@ -74,6 +78,7 @@ struct uclass_driver { int (*destroy)(struct uclass *class); int priv_auto_alloc_size; int per_device_auto_alloc_size; + int per_child_platdata_auto_alloc_size; const void *ops; }; diff --git a/test/dm/bus.c b/test/dm/bus.c index 63c8a9f7385..26b82930375 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -279,8 +279,7 @@ static int dm_test_bus_parent_ops(struct dm_test_state *dms) } DM_TEST(dm_test_bus_parent_ops, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -/* Test that the bus can store platform data about each child */ -static int dm_test_bus_parent_platdata(struct dm_test_state *dms) +static int test_bus_parent_platdata(struct dm_test_state *dms) { struct dm_test_parent_platdata *plat; struct udevice *bus, *dev; @@ -351,4 +350,33 @@ static int dm_test_bus_parent_platdata(struct dm_test_state *dms) return 0; } + +/* Test that the bus can store platform data about each child */ +static int dm_test_bus_parent_platdata(struct dm_test_state *dms) +{ + return test_bus_parent_platdata(dms); +} DM_TEST(dm_test_bus_parent_platdata, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* As above but the size is controlled by the uclass */ +static int dm_test_bus_parent_platdata_uclass(struct dm_test_state *dms) +{ + struct udevice *bus; + int size; + int ret; + + /* Set the driver size to 0 so that the uclass size is used */ + ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus)); + size = bus->driver->per_child_platdata_auto_alloc_size; + bus->uclass->uc_drv->per_child_platdata_auto_alloc_size = size; + bus->driver->per_child_platdata_auto_alloc_size = 0; + ret = test_bus_parent_platdata(dms); + if (ret) + return ret; + bus->uclass->uc_drv->per_child_platdata_auto_alloc_size = 0; + bus->driver->per_child_platdata_auto_alloc_size = size; + + return 0; +} +DM_TEST(dm_test_bus_parent_platdata_uclass, + DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From 0118ce79577f9b0881f99a6e4f8a79cd5014cb87 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:03 -0700 Subject: dm: core: Add a post_bind method for parents Allow parent drivers to be called when a new child is bound to them. This allows a bus to set up information it needs for that child. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/device.c | 12 ++++++++++++ include/dm/device.h | 2 ++ test/dm/bus.c | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) (limited to 'drivers') diff --git a/drivers/core/device.c b/drivers/core/device.c index 2f33b0e6a3b..365676b9121 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -111,12 +111,24 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, if (ret) goto fail_bind; } + if (parent && parent->driver->child_post_bind) { + ret = parent->driver->child_post_bind(dev); + if (ret) + goto fail_child_post_bind; + } + if (parent) dm_dbg("Bound device %s to %s\n", dev->name, parent->name); *devp = dev; return 0; +fail_child_post_bind: + if (drv->unbind && drv->unbind(dev)) { + dm_warn("unbind() method failed on dev '%s' on error path\n", + dev->name); + } + fail_bind: if (uclass_unbind_device(dev)) { dm_warn("Failed to unbind dev '%s' on error path\n", diff --git a/include/dm/device.h b/include/dm/device.h index 096d84b7b6f..50f1b4f5087 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -132,6 +132,7 @@ struct udevice_id { * @remove: Called to remove a device, i.e. de-activate it * @unbind: Called to unbind a device from its driver * @ofdata_to_platdata: Called before probe to decode device tree data + * @child_post_bind: Called after a new child has been bound * @child_pre_probe: Called before a child device is probed. The device has * memory allocated but it has not yet been probed. * @child_post_remove: Called after a child device is removed. The device @@ -168,6 +169,7 @@ struct driver { int (*remove)(struct udevice *dev); int (*unbind)(struct udevice *dev); int (*ofdata_to_platdata)(struct udevice *dev); + int (*child_post_bind)(struct udevice *dev); int (*child_pre_probe)(struct udevice *dev); int (*child_post_remove)(struct udevice *dev); int priv_auto_alloc_size; diff --git a/test/dm/bus.c b/test/dm/bus.c index 26b82930375..e18a6f72499 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -17,6 +17,7 @@ DECLARE_GLOBAL_DATA_PTR; struct dm_test_parent_platdata { int count; + int bind_flag; }; enum { @@ -31,6 +32,16 @@ static int testbus_drv_probe(struct udevice *dev) return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); } +static int testbus_child_post_bind(struct udevice *dev) +{ + struct dm_test_parent_platdata *plat; + + plat = dev_get_parent_platdata(dev); + plat->bind_flag = 1; + + return 0; +} + static int testbus_child_pre_probe(struct udevice *dev) { struct dm_test_parent_data *parent_data = dev_get_parentdata(dev); @@ -64,6 +75,7 @@ U_BOOT_DRIVER(testbus_drv) = { .of_match = testbus_ids, .id = UCLASS_TEST_BUS, .probe = testbus_drv_probe, + .child_post_bind = testbus_child_post_bind, .priv_auto_alloc_size = sizeof(struct dm_test_priv), .platdata_auto_alloc_size = sizeof(struct dm_test_pdata), .per_child_auto_alloc_size = sizeof(struct dm_test_parent_data), @@ -380,3 +392,26 @@ static int dm_test_bus_parent_platdata_uclass(struct dm_test_state *dms) } DM_TEST(dm_test_bus_parent_platdata_uclass, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test that the child post_bind method is called */ +static int dm_test_bus_child_post_bind(struct dm_test_state *dms) +{ + struct dm_test_parent_platdata *plat; + struct udevice *bus, *dev; + int child_count; + + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + for (device_find_first_child(bus, &dev), child_count = 0; + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_platdata(dev); + ut_assert(plat != NULL); + ut_asserteq(1, plat->bind_flag); + child_count++; + } + ut_asserteq(3, child_count); + + return 0; +} +DM_TEST(dm_test_bus_child_post_bind, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From b367053102e597eb21b0a5e86c63e8d10f368cb0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:04 -0700 Subject: dm: core: Add a function to get a device's uclass ID This is useful to check which uclass a device is in. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/device.c | 5 +++++ include/dm/device.h | 8 ++++++++ test/dm/core.c | 11 +++++++++++ 3 files changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/core/device.c b/drivers/core/device.c index 365676b9121..2606d187177 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -438,3 +438,8 @@ ulong dev_get_of_data(struct udevice *dev) { return dev->of_id->data; } + +enum uclass_id device_get_uclass_id(struct udevice *dev) +{ + return dev->uclass->uc_drv->id; +} diff --git a/include/dm/device.h b/include/dm/device.h index 50f1b4f5087..81afa8c6281 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -245,6 +245,14 @@ struct udevice *dev_get_parent(struct udevice *child); */ ulong dev_get_of_data(struct udevice *dev); +/* + * device_get_uclass_id() - return the uclass ID of a device + * + * @dev: Device to check + * @return uclass ID for the device + */ +enum uclass_id device_get_uclass_id(struct udevice *dev); + /** * device_get_child() - Get the child of a device by index * diff --git a/test/dm/core.c b/test/dm/core.c index ff5c2a749c5..eccda0974da 100644 --- a/test/dm/core.c +++ b/test/dm/core.c @@ -598,3 +598,14 @@ static int dm_test_uclass_before_ready(struct dm_test_state *dms) } DM_TEST(dm_test_uclass_before_ready, 0); + +static int dm_test_device_get_uclass_id(struct dm_test_state *dms) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_TEST, 0, &dev)); + ut_asserteq(UCLASS_TEST, device_get_uclass_id(dev)); + + return 0; +} +DM_TEST(dm_test_device_get_uclass_id, DM_TESTF_SCAN_PDATA); -- cgit v1.3.1 From 9cc36a2b89ebe5148d69d521745c1e1d26365c3a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:05 -0700 Subject: dm: core: Add a flag to control sequence numbering At present we try to use the 'reg' property and device tree aliases to give devices a sequence number. The 'reg' property is often actually a memory address, so the sequence numbers thus-obtained are not useful. It would be better if the devices were just sequentially numbered in that case. In fact neither I2C nor SPI use this feature, so drop it. Some devices need us to look up an alias to number them within the uclass. Add a flag to control this, so it is not done unless it is needed. Adjust the tests to test this new behaviour. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- doc/driver-model/README.txt | 51 +++++++++++------------------------------- drivers/core/device.c | 28 +++++++++++------------ drivers/i2c/i2c-uclass.c | 1 + drivers/serial/serial-uclass.c | 1 + drivers/spi/spi-uclass.c | 1 + include/dm/uclass.h | 5 +++++ test/dm/bus.c | 3 ++- test/dm/test-fdt.c | 9 ++++---- test/dm/test.dts | 16 +++++++++++++ 9 files changed, 57 insertions(+), 58 deletions(-) (limited to 'drivers') diff --git a/doc/driver-model/README.txt b/doc/driver-model/README.txt index 40415698cf4..0c1292bf125 100644 --- a/doc/driver-model/README.txt +++ b/doc/driver-model/README.txt @@ -388,12 +388,12 @@ Device Sequence Numbers U-Boot numbers devices from 0 in many situations, such as in the command line for I2C and SPI buses, and the device names for serial ports (serial0, serial1, ...). Driver model supports this numbering and permits devices -to be locating by their 'sequence'. This numbering unique identifies a +to be locating by their 'sequence'. This numbering uniquely identifies a device in its uclass, so no two devices within a particular uclass can have the same sequence number. Sequence numbers start from 0 but gaps are permitted. For example, a board -may have I2C buses 0, 1, 4, 5 but no 2 or 3. The choice of how devices are +may have I2C buses 1, 4, 5 but no 0, 2 or 3. The choice of how devices are numbered is up to a particular board, and may be set by the SoC in some cases. While it might be tempting to automatically renumber the devices where there are gaps in the sequence, this can lead to confusion and is @@ -403,7 +403,7 @@ Each device can request a sequence number. If none is required then the device will be automatically allocated the next available sequence number. To specify the sequence number in the device tree an alias is typically -used. +used. Make sure that the uclass has the DM_UC_FLAG_SEQ_ALIAS flag set. aliases { serial2 = "/serial@22230000"; @@ -413,43 +413,18 @@ This indicates that in the uclass called "serial", the named node ("/serial@22230000") will be given sequence number 2. Any command or driver which requests serial device 2 will obtain this device. -Some devices represent buses where the devices on the bus are numbered or -addressed. For example, SPI typically numbers its slaves from 0, and I2C -uses a 7-bit address. In these cases the 'reg' property of the subnode is -used, for example: +More commonly you can use node references, which expand to the full path: -{ - aliases { - spi2 = "/spi@22300000"; - }; - - spi@22300000 { - #address-cells = <1>; - #size-cells = <1>; - spi-flash@0 { - reg = <0>; - ... - } - eeprom@1 { - reg = <1>; - }; - }; - -In this case we have a SPI bus with two slaves at 0 and 1. The SPI bus -itself is numbered 2. So we might access the SPI flash with: - - sf probe 2:0 - -and the eeprom with - - sspi 2:1 32 ef - -These commands simply need to look up the 2nd device in the SPI uclass to -find the right SPI bus. Then, they look at the children of that bus for the -right sequence number (0 or 1 in this case). +aliases { + serial2 = &serial_2; +}; +... +serial_2: serial@22230000 { +... +}; -Typically the alias method is used for top-level nodes and the 'reg' method -is used only for buses. +The alias resolves to the same string in this case, but this version is +easier to read. Device sequence numbers are resolved when a device is probed. Before then the sequence number is only a request which may or may not be honoured, diff --git a/drivers/core/device.c b/drivers/core/device.c index 2606d187177..f78b78a299e 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -53,24 +53,22 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, dev->driver = drv; dev->uclass = uc; - /* - * For some devices, such as a SPI or I2C bus, the 'reg' property - * is a reasonable indicator of the sequence number. But if there is - * an alias, we use that in preference. In any case, this is just - * a 'requested' sequence, and will be resolved (and ->seq updated) - * when the device is probed. - */ dev->seq = -1; + dev->req_seq = -1; #ifdef CONFIG_OF_CONTROL - dev->req_seq = fdtdec_get_int(gd->fdt_blob, of_offset, "reg", -1); - if (!IS_ERR_VALUE(dev->req_seq)) - dev->req_seq &= INT_MAX; - if (uc->uc_drv->name && of_offset != -1) { - fdtdec_get_alias_seq(gd->fdt_blob, uc->uc_drv->name, of_offset, - &dev->req_seq); + /* + * Some devices, such as a SPI bus, I2C bus and serial ports are + * numbered using aliases. + * + * This is just a 'requested' sequence, and will be + * resolved (and ->seq updated) when the device is probed. + */ + if (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS) { + if (uc->uc_drv->name && of_offset != -1) { + fdtdec_get_alias_seq(gd->fdt_blob, uc->uc_drv->name, + of_offset, &dev->req_seq); + } } -#else - dev->req_seq = -1; #endif if (!dev->platdata && drv->platdata_auto_alloc_size) { dev->flags |= DM_FLAG_ALLOC_PDATA; diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 24e5ec68f2b..94b49dfe52b 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -453,6 +453,7 @@ int i2c_post_bind(struct udevice *dev) UCLASS_DRIVER(i2c) = { .id = UCLASS_I2C, .name = "i2c", + .flags = DM_UC_FLAG_SEQ_ALIAS, .per_device_auto_alloc_size = sizeof(struct dm_i2c_bus), .post_bind = i2c_post_bind, .post_probe = i2c_post_probe, diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index d1b5777cecd..9131a8f93d9 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -297,6 +297,7 @@ static int serial_pre_remove(struct udevice *dev) UCLASS_DRIVER(serial) = { .id = UCLASS_SERIAL, .name = "serial", + .flags = DM_UC_FLAG_SEQ_ALIAS, .post_probe = serial_post_probe, .pre_remove = serial_pre_remove, .per_device_auto_alloc_size = sizeof(struct serial_dev_priv), diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 7a57bceb260..35756ad784e 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -344,6 +344,7 @@ int spi_ofdata_to_platdata(const void *blob, int node, UCLASS_DRIVER(spi) = { .id = UCLASS_SPI, .name = "spi", + .flags = DM_UC_FLAG_SEQ_ALIAS, .post_bind = spi_post_bind, .post_probe = spi_post_probe, .per_device_auto_alloc_size = sizeof(struct dm_spi_bus), diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 7d92d34bec0..9000b22c38d 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -40,6 +40,9 @@ struct uclass { struct udevice; +/* Members of this uclass sequence themselves with aliases */ +#define DM_UC_FLAG_SEQ_ALIAS (1 << 0) + /** * struct uclass_driver - Driver for the uclass * @@ -66,6 +69,7 @@ struct udevice; * a falback if this member is 0 in the driver. * @ops: Uclass operations, providing the consistent interface to devices * within the uclass. + * @flags: Flags for this uclass (DM_UC_...) */ struct uclass_driver { const char *name; @@ -80,6 +84,7 @@ struct uclass_driver { int per_device_auto_alloc_size; int per_child_platdata_auto_alloc_size; const void *ops; + uint32_t flags; }; /* Declare a new uclass_driver */ diff --git a/test/dm/bus.c b/test/dm/bus.c index e18a6f72499..972c4497908 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -88,12 +88,13 @@ U_BOOT_DRIVER(testbus_drv) = { UCLASS_DRIVER(testbus) = { .name = "testbus", .id = UCLASS_TEST_BUS, + .flags = DM_UC_FLAG_SEQ_ALIAS, }; /* Test that we can probe for children */ static int dm_test_bus_children(struct dm_test_state *dms) { - int num_devices = 4; + int num_devices = 6; struct udevice *bus; struct uclass *uc; diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index dc4ebf9adaf..dfcb3af61bb 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -89,6 +89,7 @@ int testfdt_ping(struct udevice *dev, int pingval, int *pingret) UCLASS_DRIVER(testfdt) = { .name = "testfdt", .id = UCLASS_TEST_FDT, + .flags = DM_UC_FLAG_SEQ_ALIAS, }; int dm_check_devices(struct dm_test_state *dms, int num_devices) @@ -128,7 +129,7 @@ int dm_check_devices(struct dm_test_state *dms, int num_devices) /* Test that FDT-based binding works correctly */ static int dm_test_fdt(struct dm_test_state *dms) { - const int num_devices = 4; + const int num_devices = 6; struct udevice *dev; struct uclass *uc; int ret; @@ -184,7 +185,7 @@ static int dm_test_fdt_uclass_seq(struct dm_test_state *dms) ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_FDT, 3, true, &dev)); ut_asserteq_str("b-test", dev->name); - ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_FDT, 0, true, &dev)); + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_FDT, 8, true, &dev)); ut_asserteq_str("a-test", dev->name); ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 5, @@ -220,11 +221,11 @@ static int dm_test_fdt_uclass_seq(struct dm_test_state *dms) ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_TEST_FDT, 1, &dev)); ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); - ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 4, &dev)); /* But now that it is probed, we can find it */ ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 1, &dev)); - ut_asserteq_str("a-test", dev->name); + ut_asserteq_str("f-test", dev->name); return 0; } diff --git a/test/dm/test.dts b/test/dm/test.dts index 33f2c00747d..84024a44a3f 100644 --- a/test/dm/test.dts +++ b/test/dm/test.dts @@ -8,7 +8,15 @@ aliases { console = &uart0; + i2c0 = "/i2c@0"; + spi0 = "/spi@0"; testfdt6 = "/e-test"; + testbus3 = "/some-bus"; + testfdt0 = "/some-bus/c-test@0"; + testfdt1 = "/some-bus/c-test@1"; + testfdt3 = "/b-test"; + testfdt5 = "/some-bus/c-test@5"; + testfdt8 = "/a-test"; }; uart0: serial { @@ -86,6 +94,14 @@ compatible = "google,another-fdt-test"; }; + f-test { + compatible = "denx,u-boot-fdt-test"; + }; + + g-test { + compatible = "denx,u-boot-fdt-test"; + }; + gpio_a: base-gpios { compatible = "sandbox,gpio"; gpio-controller; -- cgit v1.3.1 From dac8db2ce66944828e441cccf25703b262a256cd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:06 -0700 Subject: dm: core: Allow uclasses to specify private data for a device's children In many cases the per-child private data for a device's children is defined by the uclass rather than the individual driver. For example, a SPI bus needs to store information about each of its children, but all SPI drivers store the same information. It makes sense to allow the uclass to define this data. If the driver provides a size value for its per-child private data, then use it. Failng that, fall back to that provided by the uclass. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/device-remove.c | 4 ++++ drivers/core/device.c | 4 ++++ include/dm/uclass.h | 4 ++++ test/dm/bus.c | 31 +++++++++++++++++++++++++++++-- 4 files changed, 41 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 56c358a0ec2..3a5f48df7a2 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -126,6 +126,10 @@ void device_free(struct udevice *dev) } if (dev->parent) { size = dev->parent->driver->per_child_auto_alloc_size; + if (!size) { + size = dev->parent->uclass->uc_drv-> + per_child_auto_alloc_size; + } if (size) { free(dev->parent_priv); dev->parent_priv = NULL; diff --git a/drivers/core/device.c b/drivers/core/device.c index f78b78a299e..78bc460d35f 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -201,6 +201,10 @@ int device_probe_child(struct udevice *dev, void *parent_priv) /* Ensure all parents are probed */ if (dev->parent) { size = dev->parent->driver->per_child_auto_alloc_size; + if (!size) { + size = dev->parent->uclass->uc_drv-> + per_child_auto_alloc_size; + } if (size) { dev->parent_priv = calloc(1, size); if (!dev->parent_priv) { diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 9000b22c38d..ac6c85072c9 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -63,6 +63,9 @@ struct udevice; * @per_device_auto_alloc_size: Each device can hold private data owned * by the uclass. If required this will be automatically allocated if this * value is non-zero. + * @per_child_auto_alloc_size: Each child device (of a parent in this + * uclass) can hold parent data for the device/uclass. This value is only + * used as a falback if this member is 0 in the driver. * @per_child_platdata_auto_alloc_size: A bus likes to store information about * its children. If non-zero this is the size of this data, to be allocated * in the child device's parent_platdata pointer. This value is only used as @@ -82,6 +85,7 @@ struct uclass_driver { int (*destroy)(struct uclass *class); int priv_auto_alloc_size; int per_device_auto_alloc_size; + int per_child_auto_alloc_size; int per_child_platdata_auto_alloc_size; const void *ops; uint32_t flags; diff --git a/test/dm/bus.c b/test/dm/bus.c index 972c4497908..e9096970628 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -192,7 +192,7 @@ DM_TEST(dm_test_bus_children_iterators, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); /* Test that the bus can store data about each child */ -static int dm_test_bus_parent_data(struct dm_test_state *dms) +static int test_bus_parent_data(struct dm_test_state *dms) { struct dm_test_parent_data *parent_data; struct udevice *bus, *dev; @@ -251,9 +251,36 @@ static int dm_test_bus_parent_data(struct dm_test_state *dms) return 0; } - +/* Test that the bus can store data about each child */ +static int dm_test_bus_parent_data(struct dm_test_state *dms) +{ + return test_bus_parent_data(dms); +} DM_TEST(dm_test_bus_parent_data, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); +/* As above but the size is controlled by the uclass */ +static int dm_test_bus_parent_data_uclass(struct dm_test_state *dms) +{ + struct udevice *bus; + int size; + int ret; + + /* Set the driver size to 0 so that the uclass size is used */ + ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus)); + size = bus->driver->per_child_auto_alloc_size; + bus->uclass->uc_drv->per_child_auto_alloc_size = size; + bus->driver->per_child_auto_alloc_size = 0; + ret = test_bus_parent_data(dms); + if (ret) + return ret; + bus->uclass->uc_drv->per_child_auto_alloc_size = 0; + bus->driver->per_child_auto_alloc_size = size; + + return 0; +} +DM_TEST(dm_test_bus_parent_data_uclass, + DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + /* Test that the bus ops are called when a child is probed/removed */ static int dm_test_bus_parent_ops(struct dm_test_state *dms) { -- cgit v1.3.1 From 19a25f672c6aa1a9a9b94c0ffbfda3e8246d1a19 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:07 -0700 Subject: dm: spi: Move the per-child data size to the uclass This is common to all SPI drivers and specifies a structure used by the uclass. It makes more sense to define it in the uclass. Reviewed-by: Masahiro Yamada Signed-off-by: Simon Glass --- drivers/spi/cadence_qspi.c | 1 - drivers/spi/designware_spi.c | 1 - drivers/spi/exynos_spi.c | 1 - drivers/spi/sandbox_spi.c | 1 - drivers/spi/soft_spi.c | 1 - drivers/spi/spi-uclass.c | 1 + drivers/spi/tegra114_spi.c | 1 - drivers/spi/tegra20_sflash.c | 1 - drivers/spi/tegra20_slink.c | 1 - 9 files changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index 98ae3b808fb..a75fc46e95d 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -340,6 +340,5 @@ U_BOOT_DRIVER(cadence_spi) = { .ofdata_to_platdata = cadence_spi_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct cadence_spi_platdata), .priv_auto_alloc_size = sizeof(struct cadence_spi_priv), - .per_child_auto_alloc_size = sizeof(struct spi_slave), .probe = cadence_spi_probe, }; diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c index 700f616ad76..2624844d528 100644 --- a/drivers/spi/designware_spi.c +++ b/drivers/spi/designware_spi.c @@ -421,6 +421,5 @@ U_BOOT_DRIVER(dw_spi) = { .ofdata_to_platdata = dw_spi_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct dw_spi_platdata), .priv_auto_alloc_size = sizeof(struct dw_spi_priv), - .per_child_auto_alloc_size = sizeof(struct spi_slave), .probe = dw_spi_probe, }; diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c index f078973531c..a46d8c18766 100644 --- a/drivers/spi/exynos_spi.c +++ b/drivers/spi/exynos_spi.c @@ -425,6 +425,5 @@ U_BOOT_DRIVER(exynos_spi) = { .ofdata_to_platdata = exynos_spi_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct exynos_spi_platdata), .priv_auto_alloc_size = sizeof(struct exynos_spi_priv), - .per_child_auto_alloc_size = sizeof(struct spi_slave), .probe = exynos_spi_probe, }; diff --git a/drivers/spi/sandbox_spi.c b/drivers/spi/sandbox_spi.c index e717424db83..bad56603ba6 100644 --- a/drivers/spi/sandbox_spi.c +++ b/drivers/spi/sandbox_spi.c @@ -160,6 +160,5 @@ U_BOOT_DRIVER(spi_sandbox) = { .name = "spi_sandbox", .id = UCLASS_SPI, .of_match = sandbox_spi_ids, - .per_child_auto_alloc_size = sizeof(struct spi_slave), .ops = &sandbox_spi_ops, }; diff --git a/drivers/spi/soft_spi.c b/drivers/spi/soft_spi.c index 423c98d1a5a..9f7d80ee4da 100644 --- a/drivers/spi/soft_spi.c +++ b/drivers/spi/soft_spi.c @@ -240,7 +240,6 @@ U_BOOT_DRIVER(soft_spi) = { .ofdata_to_platdata = soft_spi_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct soft_spi_platdata), .priv_auto_alloc_size = sizeof(struct soft_spi_priv), - .per_child_auto_alloc_size = sizeof(struct spi_slave), .probe = soft_spi_probe, .child_pre_probe = soft_spi_child_pre_probe, }; diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 35756ad784e..e5dfb300fa1 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -348,6 +348,7 @@ UCLASS_DRIVER(spi) = { .post_bind = spi_post_bind, .post_probe = spi_post_probe, .per_device_auto_alloc_size = sizeof(struct dm_spi_bus), + .per_child_auto_alloc_size = sizeof(struct spi_slave), }; UCLASS_DRIVER(spi_generic) = { diff --git a/drivers/spi/tegra114_spi.c b/drivers/spi/tegra114_spi.c index 2d97625fba7..53ff9ea2210 100644 --- a/drivers/spi/tegra114_spi.c +++ b/drivers/spi/tegra114_spi.c @@ -407,6 +407,5 @@ U_BOOT_DRIVER(tegra114_spi) = { .ofdata_to_platdata = tegra114_spi_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata), .priv_auto_alloc_size = sizeof(struct tegra114_spi_priv), - .per_child_auto_alloc_size = sizeof(struct spi_slave), .probe = tegra114_spi_probe, }; diff --git a/drivers/spi/tegra20_sflash.c b/drivers/spi/tegra20_sflash.c index 7d0d0f37fc7..78c74cdf37f 100644 --- a/drivers/spi/tegra20_sflash.c +++ b/drivers/spi/tegra20_sflash.c @@ -348,6 +348,5 @@ U_BOOT_DRIVER(tegra20_sflash) = { .ofdata_to_platdata = tegra20_sflash_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata), .priv_auto_alloc_size = sizeof(struct tegra20_sflash_priv), - .per_child_auto_alloc_size = sizeof(struct spi_slave), .probe = tegra20_sflash_probe, }; diff --git a/drivers/spi/tegra20_slink.c b/drivers/spi/tegra20_slink.c index 213fa5f7939..597d6ad5cce 100644 --- a/drivers/spi/tegra20_slink.c +++ b/drivers/spi/tegra20_slink.c @@ -361,6 +361,5 @@ U_BOOT_DRIVER(tegra30_spi) = { .ofdata_to_platdata = tegra30_spi_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata), .priv_auto_alloc_size = sizeof(struct tegra30_spi_priv), - .per_child_auto_alloc_size = sizeof(struct spi_slave), .probe = tegra30_spi_probe, }; -- cgit v1.3.1 From 081f2fcbd9a95ba10677065359791f8fea3f8c58 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:08 -0700 Subject: dm: core: Allow the uclass to set up a device's child after binding For buses, after a child is bound, allow the uclass to perform some processing. This can be used to figure out the address of the child (e.g. the chip select for SPI slaves) so that it is ready to be probed. This avoids bus drivers having to repeat the same process, which really should be done by the uclass, since it is common. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/uclass.c | 21 ++++++++++++++++----- include/dm/uclass.h | 2 ++ test/dm/bus.c | 26 ++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 901b06ed2ba..a5445518f57 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -319,18 +319,29 @@ int uclass_bind_device(struct udevice *dev) int ret; uc = dev->uclass; - list_add_tail(&dev->uclass_node, &uc->dev_head); + if (dev->parent) { + struct uclass_driver *uc_drv = dev->parent->uclass->uc_drv; + + if (uc_drv->child_post_bind) { + ret = uc_drv->child_post_bind(dev); + if (ret) + goto err; + } + } if (uc->uc_drv->post_bind) { ret = uc->uc_drv->post_bind(dev); - if (ret) { - list_del(&dev->uclass_node); - return ret; - } + if (ret) + goto err; } return 0; +err: + /* There is no need to undo the parent's post_bind call */ + list_del(&dev->uclass_node); + + return ret; } int uclass_unbind_device(struct udevice *dev) diff --git a/include/dm/uclass.h b/include/dm/uclass.h index ac6c85072c9..5c5b8f42083 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -55,6 +55,7 @@ struct udevice; * @pre_unbind: Called before a device is unbound from this uclass * @post_probe: Called after a new device is probed * @pre_remove: Called before a device is removed + * @child_post_bind: Called after a child is bound to a device in this uclass * @init: Called to set up the uclass * @destroy: Called to destroy the uclass * @priv_auto_alloc_size: If non-zero this is the size of the private data @@ -81,6 +82,7 @@ struct uclass_driver { int (*pre_unbind)(struct udevice *dev); int (*post_probe)(struct udevice *dev); int (*pre_remove)(struct udevice *dev); + int (*child_post_bind)(struct udevice *dev); int (*init)(struct uclass *class); int (*destroy)(struct uclass *class); int priv_auto_alloc_size; diff --git a/test/dm/bus.c b/test/dm/bus.c index e9096970628..c123ed79310 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -18,6 +18,7 @@ DECLARE_GLOBAL_DATA_PTR; struct dm_test_parent_platdata { int count; int bind_flag; + int uclass_bind_flag; }; enum { @@ -38,6 +39,7 @@ static int testbus_child_post_bind(struct udevice *dev) plat = dev_get_parent_platdata(dev); plat->bind_flag = 1; + plat->uclass_bind_flag = 2; return 0; } @@ -443,3 +445,27 @@ static int dm_test_bus_child_post_bind(struct dm_test_state *dms) return 0; } DM_TEST(dm_test_bus_child_post_bind, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test that the child post_bind method is called */ +static int dm_test_bus_child_post_bind_uclass(struct dm_test_state *dms) +{ + struct dm_test_parent_platdata *plat; + struct udevice *bus, *dev; + int child_count; + + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + for (device_find_first_child(bus, &dev), child_count = 0; + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_platdata(dev); + ut_assert(plat != NULL); + ut_asserteq(2, plat->uclass_bind_flag); + child_count++; + } + ut_asserteq(3, child_count); + + return 0; +} +DM_TEST(dm_test_bus_child_post_bind_uclass, + DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From 1603bf3cc189da65362b83b85831e094a2fe8516 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:09 -0700 Subject: dm: sandbox: sf: Tidy up the error handling in sandbox_sf_probe() Use a single exit point when we have an error and add debugging there. Signed-off-by: Simon Glass --- drivers/mtd/spi/sandbox.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c index 3024b988fef..106dda9ccc5 100644 --- a/drivers/mtd/spi/sandbox.c +++ b/drivers/mtd/spi/sandbox.c @@ -141,8 +141,10 @@ static int sandbox_sf_probe(struct udevice *dev) assert(bus->seq != -1); if (bus->seq < CONFIG_SANDBOX_SPI_MAX_BUS) spec = state->spi[bus->seq][cs].spec; - if (!spec) - return -ENOENT; + if (!spec) { + ret = -ENOENT; + goto error; + } file = strchr(spec, ':'); if (!file) { @@ -196,6 +198,7 @@ static int sandbox_sf_probe(struct udevice *dev) return 0; error: + debug("%s: Got error %d\n", __func__, ret); return ret; } -- cgit v1.3.1 From 83c7e434c9dd3ca81f8b763e23c1881b973bcf2f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:10 -0700 Subject: dm: core: Allow uclass to set up a device's child before it is probed Some buses need to set up their devices before they can be used. This setup may well be common to all buses in a particular uclass. Support a common pre-probe method for the uclass, called before any bus devices are probed. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/device.c | 4 ++++ drivers/core/uclass.c | 13 +++++++++++++ include/dm/test.h | 3 +++ include/dm/uclass-internal.h | 11 +++++++++++ include/dm/uclass.h | 1 + test/dm/bus.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ test/dm/test-fdt.c | 7 +++++++ 7 files changed, 85 insertions(+) (limited to 'drivers') diff --git a/drivers/core/device.c b/drivers/core/device.c index 78bc460d35f..b73d3b8961d 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -227,6 +227,10 @@ int device_probe_child(struct udevice *dev, void *parent_priv) } dev->seq = seq; + ret = uclass_pre_probe_child(dev); + if (ret) + goto fail; + if (dev->parent && dev->parent->driver->child_pre_probe) { ret = dev->parent->driver->child_pre_probe(dev); if (ret) diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index a5445518f57..289a5d2d53d 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -391,6 +391,19 @@ int uclass_resolve_seq(struct udevice *dev) return seq; } +int uclass_pre_probe_child(struct udevice *dev) +{ + struct uclass_driver *uc_drv; + + if (!dev->parent) + return 0; + uc_drv = dev->parent->uclass->uc_drv; + if (uc_drv->child_pre_probe) + return uc_drv->child_pre_probe(dev); + + return 0; +} + int uclass_post_probe_device(struct udevice *dev) { struct uclass_driver *uc_drv = dev->uclass->uc_drv; diff --git a/include/dm/test.h b/include/dm/test.h index f08c05da814..707c69e07f0 100644 --- a/include/dm/test.h +++ b/include/dm/test.h @@ -67,6 +67,8 @@ enum { struct dm_test_priv { int ping_total; int op_count[DM_TEST_OP_COUNT]; + int uclass_flag; + int uclass_total; }; /** @@ -88,6 +90,7 @@ struct dm_test_uclass_priv { * * @sum: Test value used to check parent data works correctly * @flag: Used to track calling of parent operations + * @uclass_flag: Used to track calling of parent operations by uclass */ struct dm_test_parent_data { int sum; diff --git a/include/dm/uclass-internal.h b/include/dm/uclass-internal.h index f718f37affb..f2f254a8259 100644 --- a/include/dm/uclass-internal.h +++ b/include/dm/uclass-internal.h @@ -43,6 +43,17 @@ int uclass_bind_device(struct udevice *dev); */ int uclass_unbind_device(struct udevice *dev); +/** + * uclass_pre_probe_child() - Deal with a child that is about to be probed + * + * Perform any pre-processing that is needed by the uclass before it can be + * probed. + * + * @dev: Pointer to the device + * #return 0 on success, -ve on error + */ +int uclass_pre_probe_child(struct udevice *dev); + /** * uclass_post_probe_device() - Deal with a device that has just been probed * diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 5c5b8f42083..d6c40c60dda 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -83,6 +83,7 @@ struct uclass_driver { int (*post_probe)(struct udevice *dev); int (*pre_remove)(struct udevice *dev); int (*child_post_bind)(struct udevice *dev); + int (*child_pre_probe)(struct udevice *dev); int (*init)(struct uclass *class); int (*destroy)(struct uclass *class); int priv_auto_alloc_size; diff --git a/test/dm/bus.c b/test/dm/bus.c index c123ed79310..faffe6a385b 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -53,6 +53,15 @@ static int testbus_child_pre_probe(struct udevice *dev) return 0; } +static int testbus_child_pre_probe_uclass(struct udevice *dev) +{ + struct dm_test_priv *priv = dev_get_priv(dev); + + priv->uclass_flag++; + + return 0; +} + static int testbus_child_post_remove(struct udevice *dev) { struct dm_test_parent_data *parent_data = dev_get_parentdata(dev); @@ -91,6 +100,7 @@ UCLASS_DRIVER(testbus) = { .name = "testbus", .id = UCLASS_TEST_BUS, .flags = DM_UC_FLAG_SEQ_ALIAS, + .child_pre_probe = testbus_child_pre_probe_uclass, }; /* Test that we can probe for children */ @@ -469,3 +479,39 @@ static int dm_test_bus_child_post_bind_uclass(struct dm_test_state *dms) } DM_TEST(dm_test_bus_child_post_bind_uclass, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* + * Test that the bus' uclass' child_pre_probe() is called before the + * device's probe() method + */ +static int dm_test_bus_child_pre_probe_uclass(struct dm_test_state *dms) +{ + struct udevice *bus, *dev; + int child_count; + + /* + * See testfdt_drv_probe() which effectively checks that the uclass + * flag is set before that method is called + */ + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + for (device_find_first_child(bus, &dev), child_count = 0; + dev; + device_find_next_child(&dev)) { + struct dm_test_priv *priv = dev_get_priv(dev); + + /* Check that things happened in the right order */ + ut_asserteq_ptr(NULL, priv); + ut_assertok(device_probe(dev)); + + priv = dev_get_priv(dev); + ut_assert(priv != NULL); + ut_asserteq(1, priv->uclass_flag); + ut_asserteq(1, priv->uclass_total); + child_count++; + } + ut_asserteq(3, child_count); + + return 0; +} +DM_TEST(dm_test_bus_child_pre_probe_uclass, + DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index dfcb3af61bb..b8ee9599a22 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -51,6 +51,13 @@ static int testfdt_drv_probe(struct udevice *dev) priv->ping_total += DM_TEST_START_TOTAL; + /* + * If this device is on a bus, the uclass_flag will be set before + * calling this function. This is used by + * dm_test_bus_child_pre_probe_uclass(). + */ + priv->uclass_total += priv->uclass_flag; + return 0; } -- cgit v1.3.1 From 440714eeb8938e9710d1b2bba17c6c0af7c2cf69 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:11 -0700 Subject: dm: spi: Set up the spi_slave device pointer in child_pre_probe() At present we use struct spi_slave as our device pointer in a lot of places to avoid changing the old SPI API. At some point this will go away. But for now, it is better if the SPI uclass sets up this pointer, rather than relying on passing it into the device when it is probed. We can use the new uclass child_pre_probe() method to do this. Signed-off-by: Simon Glass --- drivers/spi/spi-uclass.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index e5dfb300fa1..2c134eb39f7 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -108,6 +108,15 @@ int spi_post_probe(struct udevice *dev) return 0; } +int spi_child_pre_probe(struct udevice *dev) +{ + struct spi_slave *slave = dev_get_parentdata(dev); + + slave->dev = dev; + + return 0; +} + int spi_chip_select(struct udevice *dev) { struct spi_slave *slave = dev_get_parentdata(dev); @@ -347,6 +356,7 @@ UCLASS_DRIVER(spi) = { .flags = DM_UC_FLAG_SEQ_ALIAS, .post_bind = spi_post_bind, .post_probe = spi_post_probe, + .child_pre_probe = spi_child_pre_probe, .per_device_auto_alloc_size = sizeof(struct dm_spi_bus), .per_child_auto_alloc_size = sizeof(struct spi_slave), }; -- cgit v1.3.1 From d0cff03e187cc1de3d6b477b92c376aae27c95e8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:12 -0700 Subject: dm: spi: Move slave details to child platdata At present we go through various contortions to store the SPI slave's chip select in its private data. This only exists when the slave is active so must be set up when it is probed. Until the device is probed we don't actually know what chip select it will appear on. However, now that we can support per-child platform data, we can use that instead. This allows us to set up the chip select when the child is bound, and avoid the messy contortions. Unfortunately this is a fairly large change and it seems to be difficult to break it down further. Signed-off-by: Simon Glass --- drivers/misc/cros_ec_spi.c | 19 ----------- drivers/mtd/spi/sandbox.c | 5 +++ drivers/mtd/spi/sf_probe.c | 3 +- drivers/spi/soft_spi.c | 9 ----- drivers/spi/spi-uclass.c | 83 +++++++++++++++++++++++++++------------------- include/spi.h | 42 +++++++++++++++++------ test/dm/spi.c | 6 ++-- 7 files changed, 91 insertions(+), 76 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cros_ec_spi.c b/drivers/misc/cros_ec_spi.c index e6dba298b1e..25a5a0468e6 100644 --- a/drivers/misc/cros_ec_spi.c +++ b/drivers/misc/cros_ec_spi.c @@ -202,25 +202,6 @@ int cros_ec_spi_init(struct cros_ec_dev *dev, const void *blob) #ifdef CONFIG_DM_CROS_EC int cros_ec_probe(struct udevice *dev) { - struct spi_slave *slave = dev_get_parentdata(dev); - int ret; - - /* - * TODO(sjg@chromium.org) - * - * This is really horrible at present. It is an artifact of removing - * the child_pre_probe() method for SPI. Everything here could go in - * an automatic function, except that spi_get_bus_and_cs() wants to - * set it up manually and call device_probe_child(). - * - * The solution may be to re-enable the child_pre_probe() method for - * SPI and have it do nothing if the child is already passed in via - * device_probe_child(). - */ - slave->dev = dev; - ret = spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, slave); - if (ret) - return ret; return cros_ec_register(dev); } diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c index 106dda9ccc5..d576d31243a 100644 --- a/drivers/mtd/spi/sandbox.c +++ b/drivers/mtd/spi/sandbox.c @@ -590,6 +590,11 @@ int sandbox_sf_bind_emul(struct sandbox_state *state, int busnum, int cs, void sandbox_sf_unbind_emul(struct sandbox_state *state, int busnum, int cs) { + struct udevice *dev; + + dev = state->spi[busnum][cs].emul; + device_remove(dev); + device_unbind(dev); state->spi[busnum][cs].emul = NULL; } diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index ce9987fd1a8..41037238590 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -481,11 +481,12 @@ int spi_flash_std_erase(struct udevice *dev, u32 offset, size_t len) int spi_flash_std_probe(struct udevice *dev) { struct spi_slave *slave = dev_get_parentdata(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); struct spi_flash *flash; flash = dev->uclass_priv; flash->dev = dev; - debug("%s: slave=%p, cs=%d\n", __func__, slave, slave->cs); + debug("%s: slave=%p, cs=%d\n", __func__, slave, plat->cs); return spi_flash_probe_slave(slave, flash); } diff --git a/drivers/spi/soft_spi.c b/drivers/spi/soft_spi.c index 9f7d80ee4da..6ae45f5377a 100644 --- a/drivers/spi/soft_spi.c +++ b/drivers/spi/soft_spi.c @@ -179,14 +179,6 @@ static int soft_spi_set_mode(struct udevice *dev, unsigned int mode) return 0; } -static int soft_spi_child_pre_probe(struct udevice *dev) -{ - struct spi_slave *slave = dev_get_parentdata(dev); - - slave->dev = dev; - return spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, slave); -} - static const struct dm_spi_ops soft_spi_ops = { .claim_bus = soft_spi_claim_bus, .release_bus = soft_spi_release_bus, @@ -241,5 +233,4 @@ U_BOOT_DRIVER(soft_spi) = { .platdata_auto_alloc_size = sizeof(struct soft_spi_platdata), .priv_auto_alloc_size = sizeof(struct soft_spi_priv), .probe = soft_spi_probe, - .child_pre_probe = soft_spi_child_pre_probe, }; diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 2c134eb39f7..63a6217cc62 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -98,11 +98,21 @@ int spi_post_bind(struct udevice *dev) return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); } -int spi_post_probe(struct udevice *dev) +int spi_child_post_bind(struct udevice *dev) { - struct dm_spi_bus *spi = dev->uclass_priv; + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); - spi->max_hz = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + if (dev->of_offset == -1) + return 0; + + return spi_slave_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, plat); +} + +int spi_post_probe(struct udevice *bus) +{ + struct dm_spi_bus *spi = bus->uclass_priv; + + spi->max_hz = fdtdec_get_int(gd->fdt_blob, bus->of_offset, "spi-max-frequency", 0); return 0; @@ -110,18 +120,29 @@ int spi_post_probe(struct udevice *dev) int spi_child_pre_probe(struct udevice *dev) { + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); struct spi_slave *slave = dev_get_parentdata(dev); + /* + * This is needed because we pass struct spi_slave around the place + * instead slave->dev (a struct udevice). So we have to have some + * way to access the slave udevice given struct spi_slave. Once we + * change the SPI API to use udevice instead of spi_slave, we can + * drop this. + */ slave->dev = dev; + slave->max_hz = plat->max_hz; + slave->mode = plat->mode; + return 0; } int spi_chip_select(struct udevice *dev) { - struct spi_slave *slave = dev_get_parentdata(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); - return slave ? slave->cs : -ENOENT; + return plat ? plat->cs : -ENOENT; } int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp) @@ -130,17 +151,11 @@ int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp) for (device_find_first_child(bus, &dev); dev; device_find_next_child(&dev)) { - struct spi_slave store; - struct spi_slave *slave = dev_get_parentdata(dev); + struct dm_spi_slave_platdata *plat; - if (!slave) { - slave = &store; - spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, - slave); - } - debug("%s: slave=%p, cs=%d\n", __func__, slave, - slave ? slave->cs : -1); - if (slave && slave->cs == cs) { + plat = dev_get_parent_platdata(dev); + debug("%s: plat=%p, cs=%d\n", __func__, plat, plat->cs); + if (plat->cs == cs) { *devp = dev; return 0; } @@ -224,7 +239,6 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, struct udevice **busp, struct spi_slave **devp) { struct udevice *bus, *dev; - struct spi_slave *slave; bool created = false; int ret; @@ -241,11 +255,17 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, * SPI flash chip - we will bind to the correct driver. */ if (ret == -ENODEV && drv_name) { + struct dm_spi_slave_platdata *plat; + debug("%s: Binding new device '%s', busnum=%d, cs=%d, driver=%s\n", __func__, dev_name, busnum, cs, drv_name); ret = device_bind_driver(bus, drv_name, dev_name, &dev); if (ret) return ret; + plat = dev_get_parent_platdata(dev); + plat->cs = cs; + plat->max_hz = speed; + plat->mode = mode; created = true; } else if (ret) { printf("Invalid chip select %d:%d (err=%d)\n", busnum, cs, @@ -254,23 +274,13 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, } if (!device_active(dev)) { - slave = (struct spi_slave *)calloc(1, - sizeof(struct spi_slave)); - if (!slave) { - ret = -ENOMEM; - goto err; - } + struct spi_slave *slave; - ret = spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, - slave); + ret = device_probe(dev); if (ret) goto err; - slave->cs = cs; + slave = dev_get_parentdata(dev); slave->dev = dev; - ret = device_probe_child(dev, slave); - free(slave); - if (ret) - goto err; } ret = spi_set_speed_mode(bus, speed, mode); @@ -284,6 +294,8 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, return 0; err: + debug("%s: Error path, credted=%d, device '%s'\n", __func__, + created, dev->name); if (created) { device_remove(dev); device_unbind(dev); @@ -330,13 +342,13 @@ void spi_free_slave(struct spi_slave *slave) slave->dev = NULL; } -int spi_ofdata_to_platdata(const void *blob, int node, - struct spi_slave *spi) +int spi_slave_ofdata_to_platdata(const void *blob, int node, + struct dm_spi_slave_platdata *plat) { int mode = 0; - spi->cs = fdtdec_get_int(blob, node, "reg", -1); - spi->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", 0); + plat->cs = fdtdec_get_int(blob, node, "reg", -1); + plat->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", 0); if (fdtdec_get_bool(blob, node, "spi-cpol")) mode |= SPI_CPOL; if (fdtdec_get_bool(blob, node, "spi-cpha")) @@ -345,7 +357,7 @@ int spi_ofdata_to_platdata(const void *blob, int node, mode |= SPI_CS_HIGH; if (fdtdec_get_bool(blob, node, "spi-half-duplex")) mode |= SPI_PREAMBLE; - spi->mode = mode; + plat->mode = mode; return 0; } @@ -359,6 +371,9 @@ UCLASS_DRIVER(spi) = { .child_pre_probe = spi_child_pre_probe, .per_device_auto_alloc_size = sizeof(struct dm_spi_bus), .per_child_auto_alloc_size = sizeof(struct spi_slave), + .per_child_platdata_auto_alloc_size = + sizeof(struct dm_spi_slave_platdata), + .child_post_bind = spi_child_post_bind, }; UCLASS_DRIVER(spi_generic) = { diff --git a/include/spi.h b/include/spi.h index ec17bd0bcc8..c58e4535596 100644 --- a/include/spi.h +++ b/include/spi.h @@ -56,20 +56,42 @@ #define SPI_DEFAULT_WORDLEN 8 #ifdef CONFIG_DM_SPI +/* TODO(sjg@chromium.org): Remove this and use max_hz from struct spi_slave */ struct dm_spi_bus { uint max_hz; }; +/** + * struct dm_spi_platdata - platform data for all SPI slaves + * + * This describes a SPI slave, a child device of the SPI bus. To obtain this + * struct from a spi_slave, use dev_get_parent_platdata(dev) or + * dev_get_parent_platdata(slave->dev). + * + * This data is immuatable. Each time the device is probed, @max_hz and @mode + * will be copied to struct spi_slave. + * + * @cs: Chip select number (0..n-1) + * @max_hz: Maximum bus speed that this slave can tolerate + * @mode: SPI mode to use for this device (see SPI mode flags) + */ +struct dm_spi_slave_platdata { + unsigned int cs; + uint max_hz; + uint mode; +}; + #endif /* CONFIG_DM_SPI */ /** * struct spi_slave - Representation of a SPI slave * * For driver model this is the per-child data used by the SPI bus. It can - * be accessed using dev_get_parentdata() on the slave device. Each SPI - * driver should define this child data in its U_BOOT_DRIVER() definition: - * - * .per_child_auto_alloc_size = sizeof(struct spi_slave), + * be accessed using dev_get_parentdata() on the slave device. The SPI uclass + * sets uip per_child_auto_alloc_size to sizeof(struct spi_slave), and the + * driver should not override it. Two platform data fields (max_hz and mode) + * are copied into this structure to provide an initial value. This allows + * them to be changed, since we should never change platform data in drivers. * * If not using driver model, drivers are expected to extend this with * controller-specific data. @@ -97,8 +119,8 @@ struct spi_slave { uint mode; #else unsigned int bus; -#endif unsigned int cs; +#endif u8 op_mode_rx; u8 op_mode_tx; unsigned int wordlen; @@ -545,16 +567,16 @@ int spi_chip_select(struct udevice *slave); int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp); /** - * spi_ofdata_to_platdata() - decode standard SPI platform data + * spi_slave_ofdata_to_platdata() - decode standard SPI platform data * - * This decodes the speed and mode from a device tree node and puts it into - * the spi_slave structure. + * This decodes the speed and mode for a slave from a device tree node * * @blob: Device tree blob * @node: Node offset to read from - * @spi: Place to put the decoded information + * @plat: Place to put the decoded information */ -int spi_ofdata_to_platdata(const void *blob, int node, struct spi_slave *spi); +int spi_slave_ofdata_to_platdata(const void *blob, int node, + struct dm_spi_slave_platdata *plat); /** * spi_cs_info() - Check information on a chip select diff --git a/test/dm/spi.c b/test/dm/spi.c index 61b5b2548c4..c7ee65207b0 100644 --- a/test/dm/spi.c +++ b/test/dm/spi.c @@ -36,7 +36,6 @@ static int dm_test_spi_find(struct dm_test_state *dms) ut_asserteq(0, uclass_get_device_by_seq(UCLASS_SPI, busnum, &bus)); ut_assertok(spi_cs_info(bus, cs, &info)); of_offset = info.dev->of_offset; - sandbox_sf_unbind_emul(state_get_current(), busnum, cs); device_remove(info.dev); device_unbind(info.dev); @@ -45,7 +44,7 @@ static int dm_test_spi_find(struct dm_test_state *dms) * reports that CS 0 is present */ ut_assertok(spi_cs_info(bus, cs, &info)); - ut_asserteq_ptr(info.dev, NULL); + ut_asserteq_ptr(NULL, info.dev); /* This finds nothing because we removed the device */ ut_asserteq(-ENODEV, spi_find_bus_and_cs(busnum, cs, &bus, &dev)); @@ -62,8 +61,9 @@ static int dm_test_spi_find(struct dm_test_state *dms) ut_asserteq(-ENOENT, spi_get_bus_and_cs(busnum, cs, speed, mode, "spi_flash_std", "name", &bus, &slave)); + sandbox_sf_unbind_emul(state_get_current(), busnum, cs); ut_assertok(spi_cs_info(bus, cs, &info)); - ut_asserteq_ptr(info.dev, NULL); + ut_asserteq_ptr(NULL, info.dev); /* Add the emulation and try again */ ut_assertok(sandbox_sf_bind_emul(state, busnum, cs, bus, of_offset, -- cgit v1.3.1 From e6f66ec0e757b49d39885303a94784a342803dd2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:13 -0700 Subject: dm: i2c: Move slave details to child platdata At present we go through various contortions to store the I2C's chip address in its private data. This only exists when the chip is active so must be set up when it is probed. Until the device is probed we don't actually record what address it will appear on. However, now that we can support per-child platform data, we can use that instead. This allows us to set up the address when the child is bound, and avoid the messy contortions. Unfortunately this is a fairly large change and it seems to be difficult to break it down further. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/i2c/i2c-uclass-compat.c | 2 +- drivers/i2c/i2c-uclass.c | 54 ++++++++++++++++++++++++----------------- drivers/i2c/i2c-uniphier-f.c | 12 --------- drivers/i2c/i2c-uniphier.c | 12 --------- drivers/i2c/sandbox_i2c.c | 28 +++++---------------- drivers/i2c/tegra_i2c.c | 18 -------------- include/i2c.h | 4 +-- 7 files changed, 41 insertions(+), 89 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/i2c-uclass-compat.c b/drivers/i2c/i2c-uclass-compat.c index c29fc89e048..11239da2b99 100644 --- a/drivers/i2c/i2c-uclass-compat.c +++ b/drivers/i2c/i2c-uclass-compat.c @@ -20,7 +20,7 @@ static int i2c_compat_get_device(uint chip_addr, int alen, ret = i2c_get_chip_for_busnum(cur_busnum, chip_addr, devp); if (ret) return ret; - chip = dev_get_parentdata(*devp); + chip = dev_get_parent_platdata(*devp); if (chip->offset_len != alen) { printf("Requested alen %d does not match chip offset_len %d\n", alen, chip->offset_len); diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 94b49dfe52b..393cd6f583a 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -50,7 +50,7 @@ static int i2c_setup_offset(struct dm_i2c_chip *chip, uint offset, static int i2c_read_bytewise(struct udevice *dev, uint offset, uint8_t *buffer, int len) { - struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); struct udevice *bus = dev_get_parent(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); struct i2c_msg msg[2], *ptr; @@ -79,7 +79,7 @@ static int i2c_read_bytewise(struct udevice *dev, uint offset, static int i2c_write_bytewise(struct udevice *dev, uint offset, const uint8_t *buffer, int len) { - struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); struct udevice *bus = dev_get_parent(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); struct i2c_msg msg[1]; @@ -102,7 +102,7 @@ static int i2c_write_bytewise(struct udevice *dev, uint offset, int dm_i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len) { - struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); struct udevice *bus = dev_get_parent(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); struct i2c_msg msg[2], *ptr; @@ -133,7 +133,7 @@ int dm_i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len) int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, int len) { - struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); struct udevice *bus = dev_get_parent(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); struct i2c_msg msg[1]; @@ -223,7 +223,7 @@ static int i2c_probe_chip(struct udevice *bus, uint chip_addr, static int i2c_bind_driver(struct udevice *bus, uint chip_addr, uint offset_len, struct udevice **devp) { - struct dm_i2c_chip chip; + struct dm_i2c_chip *chip; char name[30], *str; struct udevice *dev; int ret; @@ -236,11 +236,11 @@ static int i2c_bind_driver(struct udevice *bus, uint chip_addr, uint offset_len, goto err_bind; /* Tell the device what we know about it */ - memset(&chip, '\0', sizeof(chip)); - chip.chip_addr = chip_addr; - chip.offset_len = offset_len; - ret = device_probe_child(dev, &chip); - debug("%s: device_probe_child: ret=%d\n", __func__, ret); + chip = dev_get_parent_platdata(dev); + chip->chip_addr = chip_addr; + chip->offset_len = offset_len; + ret = device_probe(dev); + debug("%s: device_probe: ret=%d\n", __func__, ret); if (ret) goto err_probe; @@ -248,6 +248,10 @@ static int i2c_bind_driver(struct udevice *bus, uint chip_addr, uint offset_len, return 0; err_probe: + /* + * If the device failed to probe, unbind it. There is nothing there + * on the bus so we don't want to leave it lying around + */ device_unbind(dev); err_bind: free(str); @@ -263,15 +267,9 @@ int i2c_get_chip(struct udevice *bus, uint chip_addr, uint offset_len, bus->name, chip_addr); for (device_find_first_child(bus, &dev); dev; device_find_next_child(&dev)) { - struct dm_i2c_chip store; - struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); int ret; - if (!chip) { - chip = &store; - i2c_chip_ofdata_to_platdata(gd->fdt_blob, - dev->of_offset, chip); - } if (chip->chip_addr == chip_addr) { ret = device_probe(dev); debug("found, ret=%d\n", ret); @@ -367,7 +365,7 @@ int i2c_get_bus_speed(struct udevice *bus) int i2c_set_chip_flags(struct udevice *dev, uint flags) { struct udevice *bus = dev->parent; - struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); int ret; @@ -383,7 +381,7 @@ int i2c_set_chip_flags(struct udevice *dev, uint flags) int i2c_get_chip_flags(struct udevice *dev, uint *flagsp) { - struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); *flagsp = chip->flags; @@ -392,7 +390,7 @@ int i2c_get_chip_flags(struct udevice *dev, uint *flagsp) int i2c_set_chip_offset_len(struct udevice *dev, uint offset_len) { - struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); if (offset_len > I2C_MAX_OFFSET_LEN) return -EINVAL; @@ -444,19 +442,31 @@ static int i2c_post_probe(struct udevice *dev) return i2c_set_bus_speed(dev, i2c->speed_hz); } -int i2c_post_bind(struct udevice *dev) +static int i2c_post_bind(struct udevice *dev) { /* Scan the bus for devices */ return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); } +static int i2c_child_post_bind(struct udevice *dev) +{ + struct dm_i2c_chip *plat = dev_get_parent_platdata(dev); + + if (dev->of_offset == -1) + return 0; + + return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, plat); +} + UCLASS_DRIVER(i2c) = { .id = UCLASS_I2C, .name = "i2c", .flags = DM_UC_FLAG_SEQ_ALIAS, - .per_device_auto_alloc_size = sizeof(struct dm_i2c_bus), .post_bind = i2c_post_bind, .post_probe = i2c_post_probe, + .per_device_auto_alloc_size = sizeof(struct dm_i2c_bus), + .per_child_platdata_auto_alloc_size = sizeof(struct dm_i2c_chip), + .child_post_bind = i2c_child_post_bind, }; UCLASS_DRIVER(i2c_generic) = { diff --git a/drivers/i2c/i2c-uniphier-f.c b/drivers/i2c/i2c-uniphier-f.c index b0d30f76c61..6707edd9ef4 100644 --- a/drivers/i2c/i2c-uniphier-f.c +++ b/drivers/i2c/i2c-uniphier-f.c @@ -145,16 +145,6 @@ static int uniphier_fi2c_remove(struct udevice *dev) return 0; } -static int uniphier_fi2c_child_pre_probe(struct udevice *dev) -{ - struct dm_i2c_chip *i2c_chip = dev_get_parentdata(dev); - - if (dev->of_offset == -1) - return 0; - return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, - i2c_chip); -} - static int wait_for_irq(struct uniphier_fi2c_dev *dev, u32 flags, bool *stop) { @@ -372,8 +362,6 @@ U_BOOT_DRIVER(uniphier_fi2c) = { .of_match = uniphier_fi2c_of_match, .probe = uniphier_fi2c_probe, .remove = uniphier_fi2c_remove, - .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), - .child_pre_probe = uniphier_fi2c_child_pre_probe, .priv_auto_alloc_size = sizeof(struct uniphier_fi2c_dev), .ops = &uniphier_fi2c_ops, }; diff --git a/drivers/i2c/i2c-uniphier.c b/drivers/i2c/i2c-uniphier.c index bdac1f90799..64a9ed81d25 100644 --- a/drivers/i2c/i2c-uniphier.c +++ b/drivers/i2c/i2c-uniphier.c @@ -75,16 +75,6 @@ static int uniphier_i2c_remove(struct udevice *dev) return 0; } -static int uniphier_i2c_child_pre_probe(struct udevice *dev) -{ - struct dm_i2c_chip *i2c_chip = dev_get_parentdata(dev); - - if (dev->of_offset == -1) - return 0; - return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, - i2c_chip); -} - static int send_and_recv_byte(struct uniphier_i2c_dev *dev, u32 dtrm) { writel(dtrm, &dev->regs->dtrm); @@ -232,8 +222,6 @@ U_BOOT_DRIVER(uniphier_i2c) = { .of_match = uniphier_i2c_of_match, .probe = uniphier_i2c_probe, .remove = uniphier_i2c_remove, - .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), - .child_pre_probe = uniphier_i2c_child_pre_probe, .priv_auto_alloc_size = sizeof(struct uniphier_i2c_dev), .ops = &uniphier_i2c_ops, }; diff --git a/drivers/i2c/sandbox_i2c.c b/drivers/i2c/sandbox_i2c.c index e2f6c3b9bb5..a943aa63821 100644 --- a/drivers/i2c/sandbox_i2c.c +++ b/drivers/i2c/sandbox_i2c.c @@ -25,24 +25,24 @@ struct dm_sandbox_i2c_emul_priv { static int get_emul(struct udevice *dev, struct udevice **devp, struct dm_i2c_ops **opsp) { - struct dm_i2c_chip *priv; + struct dm_i2c_chip *plat; int ret; *devp = NULL; *opsp = NULL; - priv = dev_get_parentdata(dev); - if (!priv->emul) { + plat = dev_get_parent_platdata(dev); + if (!plat->emul) { ret = dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); if (ret) return ret; - ret = device_get_child(dev, 0, &priv->emul); + ret = device_get_child(dev, 0, &plat->emul); if (ret) return ret; } - *devp = priv->emul; - *opsp = i2c_get_ops(priv->emul); + *devp = plat->emul; + *opsp = i2c_get_ops(plat->emul); return 0; } @@ -82,20 +82,6 @@ static const struct dm_i2c_ops sandbox_i2c_ops = { .xfer = sandbox_i2c_xfer, }; -static int sandbox_i2c_child_pre_probe(struct udevice *dev) -{ - struct dm_i2c_chip *i2c_chip = dev_get_parentdata(dev); - - /* Ignore our test address */ - if (i2c_chip->chip_addr == SANDBOX_I2C_TEST_ADDR) - return 0; - if (dev->of_offset == -1) - return 0; - - return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, - i2c_chip); -} - static const struct udevice_id sandbox_i2c_ids[] = { { .compatible = "sandbox,i2c" }, { } @@ -105,7 +91,5 @@ U_BOOT_DRIVER(i2c_sandbox) = { .name = "i2c_sandbox", .id = UCLASS_I2C, .of_match = sandbox_i2c_ids, - .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), - .child_pre_probe = sandbox_i2c_child_pre_probe, .ops = &sandbox_i2c_ops, }; diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index 87290c31276..f4142870b30 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -484,21 +484,6 @@ static const struct dm_i2c_ops tegra_i2c_ops = { .set_bus_speed = tegra_i2c_set_bus_speed, }; -static int tegra_i2c_child_pre_probe(struct udevice *dev) -{ - struct dm_i2c_chip *i2c_chip = dev_get_parentdata(dev); - - if (dev->of_offset == -1) - return 0; - return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, - i2c_chip); -} - -static int tegra_i2c_ofdata_to_platdata(struct udevice *dev) -{ - return 0; -} - static const struct udevice_id tegra_i2c_ids[] = { { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, { .compatible = "nvidia,tegra20-i2c", .data = TYPE_STD }, @@ -510,10 +495,7 @@ U_BOOT_DRIVER(i2c_tegra) = { .name = "i2c_tegra", .id = UCLASS_I2C, .of_match = tegra_i2c_ids, - .ofdata_to_platdata = tegra_i2c_ofdata_to_platdata, .probe = tegra_i2c_probe, - .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), - .child_pre_probe = tegra_i2c_child_pre_probe, .priv_auto_alloc_size = sizeof(struct i2c_bus), .ops = &tegra_i2c_ops, }; diff --git a/include/i2c.h b/include/i2c.h index 76090b7a93a..95d6f287719 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -39,8 +39,8 @@ enum dm_i2c_chip_flags { * An I2C chip is a device on the I2C bus. It sits at a particular address * and normally supports 7-bit or 10-bit addressing. * - * To obtain this structure, use dev_get_parentdata(dev) where dev is the - * chip to examine. + * To obtain this structure, use dev_get_parent_platdata(dev) where dev is + * the chip to examine. * * @chip_addr: Chip address on bus * @offset_len: Length of offset in bytes. A single byte offset can -- cgit v1.3.1 From 94f7afdf7e9909204d0005196db47cca2a28e4cb Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:16 -0700 Subject: dm: core: Ignore disabled devices when binding We don't want to bind devices which should never be used. Signed-off-by: Simon Glass Reviewed-by: Masahiro Yamada --- drivers/core/root.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/core/root.c b/drivers/core/root.c index a5b0a615016..73e3c7228e3 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -92,6 +93,10 @@ int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset, if (pre_reloc_only && !fdt_getprop(blob, offset, "u-boot,dm-pre-reloc", NULL)) continue; + if (!fdtdec_get_is_enabled(blob, offset)) { + dm_dbg(" - ignoring disabled device\n"); + continue; + } err = lists_bind_fdt(parent, blob, offset, NULL); if (err && !ret) ret = err; -- cgit v1.3.1 From 8bbb38b15fd585a9d51f8ee064ce17e535d8025e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:17 -0700 Subject: dm: cros_ec: Don't require protocol 3 support I2C is now deprecated on ARM platforms and there are no devices that use it with the v3 protocol. We can't require v3 support if we want to support I2C. Adjust the error handling to suit. Signed-off-by: Simon Glass --- drivers/misc/cros_ec.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c index 7b20ffcfa28..5846e76c495 100644 --- a/drivers/misc/cros_ec.c +++ b/drivers/misc/cros_ec.c @@ -154,7 +154,9 @@ static int prepare_proto3_response_buffer(struct cros_ec_dev *dev, int din_len) * @param dev CROS-EC device * @param dinp Returns pointer to response data * @param din_len Maximum size of response in bytes - * @return number of bytes of response data, or <0 if error + * @return number of bytes of response data, or <0 if error. Note that error + * codes can be from errno.h or -ve EC_RES_INVALID_CHECKSUM values (and they + * overlap!) */ static int handle_proto3_response(struct cros_ec_dev *dev, uint8_t **dinp, int din_len) @@ -228,7 +230,7 @@ static int send_command_proto3(struct cros_ec_dev *dev, #ifdef CONFIG_DM_CROS_EC ops = dm_cros_ec_get_ops(dev->dev); - rv = ops->packet(dev->dev, out_bytes, in_bytes); + rv = ops->packet ? ops->packet(dev->dev, out_bytes, in_bytes) : -ENOSYS; #else switch (dev->interface) { #ifdef CONFIG_CROS_EC_SPI @@ -320,7 +322,7 @@ static int send_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, * If not NULL, it will be updated to point to the data * and will always be double word aligned (64-bits) * @param din_len Maximum size of response in bytes - * @return number of bytes in response, or -1 on error + * @return number of bytes in response, or -ve on error */ static int ec_command_inptr(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, const void *dout, int dout_len, uint8_t **dinp, @@ -387,7 +389,7 @@ static int ec_command_inptr(struct cros_ec_dev *dev, uint8_t cmd, * It not NULL, it is a place for ec_command() to copy the * data to. * @param din_len Maximum size of response in bytes - * @return number of bytes in response, or -1 on error + * @return number of bytes in response, or -ve on error */ static int ec_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, const void *dout, int dout_len, -- cgit v1.3.1 From b2568f0d57e45bfc9a87f94030a8bf61f6c479a1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 25 Jan 2015 08:27:19 -0700 Subject: dm: cros_ec_spi: Remove old pre-driver-model code This is no-longer needed since all platforms use SPI for cros_ec. Signed-off-by: Simon Glass --- drivers/misc/cros_ec_spi.c | 51 ++-------------------------------------------- 1 file changed, 2 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cros_ec_spi.c b/drivers/misc/cros_ec_spi.c index 25a5a0468e6..9359c56e876 100644 --- a/drivers/misc/cros_ec_spi.c +++ b/drivers/misc/cros_ec_spi.c @@ -21,14 +21,9 @@ DECLARE_GLOBAL_DATA_PTR; -#ifdef CONFIG_DM_CROS_EC int cros_ec_spi_packet(struct udevice *udev, int out_bytes, int in_bytes) { struct cros_ec_dev *dev = udev->uclass_priv; -#else -int cros_ec_spi_packet(struct cros_ec_dev *dev, int out_bytes, int in_bytes) -{ -#endif struct spi_slave *slave = dev_get_parentdata(dev->dev); int rv; @@ -67,18 +62,11 @@ int cros_ec_spi_packet(struct cros_ec_dev *dev, int out_bytes, int in_bytes) * @param din_len Maximum size of response in bytes * @return number of bytes in response, or -1 on error */ -#ifdef CONFIG_DM_CROS_EC int cros_ec_spi_command(struct udevice *udev, uint8_t cmd, int cmd_version, const uint8_t *dout, int dout_len, uint8_t **dinp, int din_len) { struct cros_ec_dev *dev = udev->uclass_priv; -#else -int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, - const uint8_t *dout, int dout_len, - uint8_t **dinp, int din_len) -{ -#endif struct spi_slave *slave = dev_get_parentdata(dev->dev); int in_bytes = din_len + 4; /* status, length, checksum, trailer */ uint8_t *out; @@ -166,46 +154,12 @@ int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, return len; } -#ifndef CONFIG_DM_CROS_EC -int cros_ec_spi_decode_fdt(struct cros_ec_dev *dev, const void *blob) -{ - /* Decode interface-specific FDT params */ - dev->max_frequency = fdtdec_get_int(blob, dev->node, - "spi-max-frequency", 500000); - dev->cs = fdtdec_get_int(blob, dev->node, "reg", 0); - - return 0; -} - -/** - * Initialize SPI protocol. - * - * @param dev CROS_EC device - * @param blob Device tree blob - * @return 0 if ok, -1 on error - */ -int cros_ec_spi_init(struct cros_ec_dev *dev, const void *blob) -{ - int ret; - - ret = spi_setup_slave_fdt(blob, dev->node, dev->parent_node, - &slave); - if (ret) { - debug("%s: Could not setup SPI slave\n", __func__); - return ret; - } - - return 0; -} -#endif - -#ifdef CONFIG_DM_CROS_EC -int cros_ec_probe(struct udevice *dev) +static int cros_ec_probe(struct udevice *dev) { return cros_ec_register(dev); } -struct dm_cros_ec_ops cros_ec_ops = { +static struct dm_cros_ec_ops cros_ec_ops = { .packet = cros_ec_spi_packet, .command = cros_ec_spi_command, }; @@ -222,4 +176,3 @@ U_BOOT_DRIVER(cros_ec_spi) = { .probe = cros_ec_probe, .ops = &cros_ec_ops, }; -#endif -- cgit v1.3.1 From 31d2b4fd90f9305aad2e79872067e6c0243267de Mon Sep 17 00:00:00 2001 From: Ruchika Gupta Date: Fri, 23 Jan 2015 16:01:52 +0530 Subject: DM: crypto/rsa_mod_exp: Add rsa Modular Exponentiation DM driver Add a new rsa uclass for performing modular exponentiation and implement the software driver basing on this uclass. Signed-off-by: Ruchika Gupta CC: Simon Glass Acked-by: Simon Glass --- drivers/crypto/Makefile | 1 + drivers/crypto/rsa_mod_exp/Kconfig | 5 ++++ drivers/crypto/rsa_mod_exp/Makefile | 7 ++++++ drivers/crypto/rsa_mod_exp/mod_exp_sw.c | 39 +++++++++++++++++++++++++++++ drivers/crypto/rsa_mod_exp/mod_exp_uclass.c | 31 +++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/u-boot/rsa-mod-exp.h | 34 ++++++++++++++++++++++++- 7 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 drivers/crypto/rsa_mod_exp/Kconfig create mode 100644 drivers/crypto/rsa_mod_exp/Makefile create mode 100644 drivers/crypto/rsa_mod_exp/mod_exp_sw.c create mode 100644 drivers/crypto/rsa_mod_exp/mod_exp_uclass.c (limited to 'drivers') diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 7b792371811..fb8c10b38c2 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -6,4 +6,5 @@ # obj-$(CONFIG_EXYNOS_ACE_SHA) += ace_sha.o +obj-y += rsa_mod_exp/ obj-y += fsl/ diff --git a/drivers/crypto/rsa_mod_exp/Kconfig b/drivers/crypto/rsa_mod_exp/Kconfig new file mode 100644 index 00000000000..6dcb39a8d32 --- /dev/null +++ b/drivers/crypto/rsa_mod_exp/Kconfig @@ -0,0 +1,5 @@ +config DM_MOD_EXP + bool "Enable Driver Model for RSA Modular Exponentiation" + depends on DM + help + If you want to use driver model for RSA Modular Exponentiation, say Y. diff --git a/drivers/crypto/rsa_mod_exp/Makefile b/drivers/crypto/rsa_mod_exp/Makefile new file mode 100644 index 00000000000..915b751dbe1 --- /dev/null +++ b/drivers/crypto/rsa_mod_exp/Makefile @@ -0,0 +1,7 @@ +# +# (C) Copyright 2014 Freescale Semiconductor, Inc. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_RSA) += mod_exp_uclass.o mod_exp_sw.o diff --git a/drivers/crypto/rsa_mod_exp/mod_exp_sw.c b/drivers/crypto/rsa_mod_exp/mod_exp_sw.c new file mode 100644 index 00000000000..dc6c064b4e3 --- /dev/null +++ b/drivers/crypto/rsa_mod_exp/mod_exp_sw.c @@ -0,0 +1,39 @@ +/* + * (C) Copyright 2014 Freescale Semiconductor, Inc. + * Author: Ruchika Gupta + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +int mod_exp_sw(struct udevice *dev, const uint8_t *sig, uint32_t sig_len, + struct key_prop *prop, uint8_t *out) +{ + int ret = 0; + + ret = rsa_mod_exp_sw(sig, sig_len, prop, out); + if (ret) { + debug("%s: RSA failed to verify: %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static const struct mod_exp_ops mod_exp_ops_sw = { + .mod_exp = mod_exp_sw, +}; + +U_BOOT_DRIVER(mod_exp_sw) = { + .name = "mod_exp_sw", + .id = UCLASS_MOD_EXP, + .ops = &mod_exp_ops_sw, +}; + +U_BOOT_DEVICE(mod_exp_sw) = { + .name = "mod_exp_sw", +}; diff --git a/drivers/crypto/rsa_mod_exp/mod_exp_uclass.c b/drivers/crypto/rsa_mod_exp/mod_exp_uclass.c new file mode 100644 index 00000000000..266f09484f5 --- /dev/null +++ b/drivers/crypto/rsa_mod_exp/mod_exp_uclass.c @@ -0,0 +1,31 @@ +/* + * (C) Copyright 2014 Freescale Semiconductor, Inc + * Author: Ruchika Gupta + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +int rsa_mod_exp(struct udevice *dev, const uint8_t *sig, uint32_t sig_len, + struct key_prop *node, uint8_t *out) +{ + const struct mod_exp_ops *ops = device_get_ops(dev); + + if (!ops->mod_exp) + return -ENOSYS; + + return ops->mod_exp(dev, sig, sig_len, node, out); +} + +UCLASS_DRIVER(mod_exp) = { + .id = UCLASS_MOD_EXP, + .name = "rsa_mod_exp", +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index f17c3c2b384..91bb90dcfb3 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -33,6 +33,7 @@ enum uclass_id { UCLASS_I2C, /* I2C bus */ UCLASS_I2C_GENERIC, /* Generic I2C device */ UCLASS_I2C_EEPROM, /* I2C EEPROM device */ + UCLASS_MOD_EXP, /* RSA Mod Exp device */ UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/u-boot/rsa-mod-exp.h b/include/u-boot/rsa-mod-exp.h index 59cd9ea2d86..fce445a082a 100644 --- a/include/u-boot/rsa-mod-exp.h +++ b/include/u-boot/rsa-mod-exp.h @@ -35,9 +35,41 @@ struct key_prop { * @sig: RSA PKCS1.5 signature * @sig_len: Length of signature in number of bytes * @node: Node with RSA key elements like modulus, exponent, R^2, n0inv - * @out: Result in form of byte array + * @out: Result in form of byte array of len equal to sig_len */ int rsa_mod_exp_sw(const uint8_t *sig, uint32_t sig_len, struct key_prop *node, uint8_t *out); +int rsa_mod_exp(struct udevice *dev, const uint8_t *sig, uint32_t sig_len, + struct key_prop *node, uint8_t *out); + +/** + * struct struct mod_exp_ops - Driver model for RSA Modular Exponentiation + * operations + * + * The uclass interface is implemented by all crypto devices which use + * driver model. + */ +struct mod_exp_ops { + /** + * Perform Modular Exponentiation + * + * Operation: out[] = sig ^ exponent % modulus + * + * @dev: RSA Device + * @sig: RSA PKCS1.5 signature + * @sig_len: Length of signature in number of bytes + * @node: Node with RSA key elements like modulus, exponent, + * R^2, n0inv + * @out: Result in form of byte array of len equal to sig_len + * + * This function computes exponentiation over the signature. + * Returns: 0 if exponentiation is successful, or a negative value + * if it wasn't. + */ + int (*mod_exp)(struct udevice *dev, const uint8_t *sig, + uint32_t sig_len, struct key_prop *node, + uint8_t *outp); +}; + #endif -- cgit v1.3.1 From 34276478f7b7a0fea9c76e365a477962910ef770 Mon Sep 17 00:00:00 2001 From: Ruchika Gupta Date: Fri, 23 Jan 2015 16:01:55 +0530 Subject: DM: crypto/fsl - Add Freescale rsa DM driver Driver added for RSA Modular Exponentiation using Freescale Hardware Accelerator CAAM. The driver uses UCLASS_MOD_EXP Signed-off-by: Ruchika Gupta CC: Simon Glass Acked-by: Simon Glass --- drivers/crypto/Kconfig | 1 + drivers/crypto/fsl/Kconfig | 6 +++++ drivers/crypto/fsl/Makefile | 1 + drivers/crypto/fsl/fsl_rsa.c | 60 +++++++++++++++++++++++++++++++++++++++++++ drivers/crypto/fsl/jobdesc.c | 28 ++++++++++++++++++++ drivers/crypto/fsl/jobdesc.h | 5 ++++ drivers/crypto/fsl/rsa_caam.h | 28 ++++++++++++++++++++ 7 files changed, 129 insertions(+) create mode 100644 drivers/crypto/fsl/Kconfig create mode 100644 drivers/crypto/fsl/fsl_rsa.c create mode 100644 drivers/crypto/fsl/rsa_caam.h (limited to 'drivers') diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index e69de29bb2d..bd26a2bcfa2 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -0,0 +1 @@ +source drivers/crypto/fsl/Kconfig diff --git a/drivers/crypto/fsl/Kconfig b/drivers/crypto/fsl/Kconfig new file mode 100644 index 00000000000..86b2f2f7ac4 --- /dev/null +++ b/drivers/crypto/fsl/Kconfig @@ -0,0 +1,6 @@ +config FSL_CAAM + bool "Freescale Crypto Driver Support" + help + Enables the Freescale's Cryptographic Accelerator and Assurance + Module (CAAM), also known as the SEC version 4 (SEC4). The driver uses + Job Ring as interface to communicate with CAAM. diff --git a/drivers/crypto/fsl/Makefile b/drivers/crypto/fsl/Makefile index 067d0a917ba..c0cf64229eb 100644 --- a/drivers/crypto/fsl/Makefile +++ b/drivers/crypto/fsl/Makefile @@ -9,3 +9,4 @@ obj-y += sec.o obj-$(CONFIG_FSL_CAAM) += jr.o fsl_hash.o jobdesc.o error.o obj-$(CONFIG_CMD_BLOB) += fsl_blob.o +obj-$(CONFIG_RSA_FREESCALE_EXP) += fsl_rsa.o diff --git a/drivers/crypto/fsl/fsl_rsa.c b/drivers/crypto/fsl/fsl_rsa.c new file mode 100644 index 00000000000..cf1c4c1d458 --- /dev/null +++ b/drivers/crypto/fsl/fsl_rsa.c @@ -0,0 +1,60 @@ +/* + * (C) Copyright 2014 Freescale Semiconductor, Inc. + * Author: Ruchika Gupta + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include "jobdesc.h" +#include "desc.h" +#include "jr.h" +#include "rsa_caam.h" +#include + +int fsl_mod_exp(struct udevice *dev, const uint8_t *sig, uint32_t sig_len, + struct key_prop *prop, uint8_t *out) +{ + uint32_t keylen; + struct pk_in_params pkin; + uint32_t desc[MAX_CAAM_DESCSIZE]; + int ret; + + /* Length in bytes */ + keylen = prop->num_bits / 8; + + pkin.a = sig; + pkin.a_siz = sig_len; + pkin.n = prop->modulus; + pkin.n_siz = keylen; + pkin.e = prop->public_exponent; + pkin.e_siz = prop->exp_len; + + inline_cnstr_jobdesc_pkha_rsaexp(desc, &pkin, out, sig_len); + + ret = run_descriptor_jr(desc); + if (ret) { + debug("%s: RSA failed to verify: %d\n", __func__, ret); + return -EFAULT; + } + + return 0; +} + +static const struct mod_exp_ops fsl_mod_exp_ops = { + .mod_exp = fsl_mod_exp, +}; + +U_BOOT_DRIVER(fsl_rsa_mod_exp) = { + .name = "fsl_rsa_mod_exp", + .id = UCLASS_MOD_EXP, + .ops = &fsl_mod_exp_ops, +}; + +U_BOOT_DEVICE(fsl_rsa) = { + .name = "fsl_rsa_mod_exp", +}; diff --git a/drivers/crypto/fsl/jobdesc.c b/drivers/crypto/fsl/jobdesc.c index 1386baec0fd..cc0dcede7b7 100644 --- a/drivers/crypto/fsl/jobdesc.c +++ b/drivers/crypto/fsl/jobdesc.c @@ -11,6 +11,7 @@ #include #include "desc_constr.h" #include "jobdesc.h" +#include "rsa_caam.h" #define KEY_BLOB_SIZE 32 #define MAC_SIZE 16 @@ -123,3 +124,30 @@ void inline_cnstr_jobdesc_rng_instantiation(uint32_t *desc) append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG | OP_ALG_RNG4_SK); } + +/* Change key size to bytes form bits in calling function*/ +void inline_cnstr_jobdesc_pkha_rsaexp(uint32_t *desc, + struct pk_in_params *pkin, uint8_t *out, + uint32_t out_siz) +{ + dma_addr_t dma_addr_e, dma_addr_a, dma_addr_n, dma_addr_out; + + dma_addr_e = virt_to_phys((void *)pkin->e); + dma_addr_a = virt_to_phys((void *)pkin->a); + dma_addr_n = virt_to_phys((void *)pkin->n); + dma_addr_out = virt_to_phys((void *)out); + + init_job_desc(desc, 0); + append_key(desc, dma_addr_e, pkin->e_siz, KEY_DEST_PKHA_E | CLASS_1); + + append_fifo_load(desc, dma_addr_a, + pkin->a_siz, LDST_CLASS_1_CCB | FIFOLD_TYPE_PK_A); + + append_fifo_load(desc, dma_addr_n, + pkin->n_siz, LDST_CLASS_1_CCB | FIFOLD_TYPE_PK_N); + + append_operation(desc, OP_TYPE_PK | OP_ALG_PK | OP_ALG_PKMODE_MOD_EXPO); + + append_fifo_store(desc, dma_addr_out, out_siz, + LDST_CLASS_1_CCB | FIFOST_TYPE_PKHA_B); +} diff --git a/drivers/crypto/fsl/jobdesc.h b/drivers/crypto/fsl/jobdesc.h index 3cf7226de23..84b3edd6e2f 100644 --- a/drivers/crypto/fsl/jobdesc.h +++ b/drivers/crypto/fsl/jobdesc.h @@ -10,6 +10,7 @@ #include #include +#include "rsa_caam.h" #define KEY_IDNFR_SZ_BYTES 16 @@ -26,4 +27,8 @@ void inline_cnstr_jobdesc_blob_decap(uint32_t *desc, uint8_t *key_idnfr, uint32_t out_sz); void inline_cnstr_jobdesc_rng_instantiation(uint32_t *desc); + +void inline_cnstr_jobdesc_pkha_rsaexp(uint32_t *desc, + struct pk_in_params *pkin, uint8_t *out, + uint32_t out_siz); #endif diff --git a/drivers/crypto/fsl/rsa_caam.h b/drivers/crypto/fsl/rsa_caam.h new file mode 100644 index 00000000000..4ff87efc5bc --- /dev/null +++ b/drivers/crypto/fsl/rsa_caam.h @@ -0,0 +1,28 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __RSA_CAAM_H +#define __RSA_CAAM_H + +#include + +/** + * struct pk_in_params - holder for input to PKHA block in CAAM + * These parameters are required to perform Modular Exponentiation + * using PKHA Block in CAAM + */ +struct pk_in_params { + const uint8_t *e; /* public exponent as byte array */ + uint32_t e_siz; /* size of e[] in number of bytes */ + const uint8_t *n; /* modulus as byte array */ + uint32_t n_siz; /* size of n[] in number of bytes */ + const uint8_t *a; /* Signature as byte array */ + uint32_t a_siz; /* size of a[] in number of bytes */ + uint8_t *b; /* Result exp. modulus in number of bytes */ + uint32_t b_siz; /* size of b[] in number of bytes */ +}; + +#endif -- cgit v1.3.1 From 18a7f6aa2dcac7e78436cba148b25779f25f5c16 Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Tue, 27 Jan 2015 13:36:28 +0100 Subject: dm: i2c-uclass-compat: fix missed argument This patch fixes build error for CONFIG_DM_I2C_COMPAT. In i2c_get_chip_for_busnum() call, one of argument was missed, which was offset_len. Now it is set to 'alen' as previous. Signed-off-by: Przemyslaw Marczak Acked-by: Simon Glass --- drivers/i2c/i2c-uclass-compat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/i2c-uclass-compat.c b/drivers/i2c/i2c-uclass-compat.c index 11239da2b99..841ce0510ca 100644 --- a/drivers/i2c/i2c-uclass-compat.c +++ b/drivers/i2c/i2c-uclass-compat.c @@ -17,7 +17,7 @@ static int i2c_compat_get_device(uint chip_addr, int alen, struct dm_i2c_chip *chip; int ret; - ret = i2c_get_chip_for_busnum(cur_busnum, chip_addr, devp); + ret = i2c_get_chip_for_busnum(cur_busnum, chip_addr, alen, devp); if (ret) return ret; chip = dev_get_parent_platdata(*devp); -- cgit v1.3.1 From 8dfcbaa681667bd72ad1c6213ef548b7a372ff65 Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Tue, 27 Jan 2015 13:36:36 +0100 Subject: dm: i2c: s3c24x0: adjust to dm-i2c api This commit adjusts the s3c24x0 driver to new i2c api based on driver-model. The driver supports standard and high-speed i2c as previous. Tested on Trats2, Odroid U3, Arndale, Odroid XU3 Signed-off-by: Przemyslaw Marczak Tested-by: Simon Glass Cc: Simon Glass Cc: Heiko Schocher Cc: Minkyu Kang Acked-by: Simon Glass --- drivers/i2c/s3c24x0_i2c.c | 237 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 194 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index fd328f05494..0dd1abcf80f 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -9,8 +9,9 @@ * as they seem to have the same I2C controller inside. * The different address mapping is handled by the s3c24xx.h files below. */ - #include +#include +#include #include #if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) #include @@ -121,13 +122,23 @@ #define CONFIG_MAX_I2C_NUM 1 #endif +DECLARE_GLOBAL_DATA_PTR; + /* * For SPL boot some boards need i2c before SDRAM is initialised so force * variables to live in SRAM */ +#ifdef CONFIG_SYS_I2C static struct s3c24x0_i2c_bus i2c_bus[CONFIG_MAX_I2C_NUM] __attribute__((section(".data"))); +#endif + +enum exynos_i2c_type { + EXYNOS_I2C_STD, + EXYNOS_I2C_HS, +}; +#ifdef CONFIG_SYS_I2C /** * Get a pointer to the given bus index * @@ -147,6 +158,7 @@ static struct s3c24x0_i2c_bus *get_bus(unsigned int bus_idx) debug("Undefined bus: %d\n", bus_idx); return NULL; } +#endif #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) static int GetI2CSDA(void) @@ -251,6 +263,7 @@ static void ReadWriteByte(struct s3c24x0_i2c *i2c) writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon); } +#ifdef CONFIG_SYS_I2C static struct s3c24x0_i2c *get_base_i2c(int bus) { #ifdef CONFIG_EXYNOS4 @@ -267,6 +280,7 @@ static struct s3c24x0_i2c *get_base_i2c(int bus) return s3c24x0_get_base_i2c(); #endif } +#endif static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) { @@ -326,7 +340,7 @@ static int hsi2c_get_clk_details(struct s3c24x0_i2c_bus *i2c_bus) return 0; } } - return -1; + return -EINVAL; } static void hsi2c_ch_init(struct s3c24x0_i2c_bus *i2c_bus) @@ -398,18 +412,20 @@ static void exynos5_i2c_reset(struct s3c24x0_i2c_bus *i2c_bus) hsi2c_ch_init(i2c_bus); } +#ifdef CONFIG_SYS_I2C static void s3c24x0_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) { struct s3c24x0_i2c *i2c; struct s3c24x0_i2c_bus *bus; - #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); #endif ulong start_time = get_timer(0); - /* By default i2c channel 0 is the current bus */ i2c = get_base_i2c(adap->hwadapnr); + bus = &i2c_bus[adap->hwadapnr]; + if (!bus) + return; /* * In case the previous transfer is still going, wait to give it a @@ -470,12 +486,13 @@ static void s3c24x0_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) #endif } #endif /* #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) */ + i2c_ch_init(i2c, speed, slaveadd); - bus = &i2c_bus[adap->hwadapnr]; bus->active = true; bus->regs = i2c; } +#endif /* CONFIG_SYS_I2C */ /* * Poll the appropriate bit of the fifo status register until the interface is @@ -698,20 +715,27 @@ static int hsi2c_read(struct exynos5_hsi2c *i2c, return rv; } +#ifdef CONFIG_SYS_I2C static unsigned int s3c24x0_i2c_set_bus_speed(struct i2c_adapter *adap, - unsigned int speed) + unsigned int speed) +#else +static int s3c24x0_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) +#endif { struct s3c24x0_i2c_bus *i2c_bus; +#ifdef CONFIG_SYS_I2C i2c_bus = get_bus(adap->hwadapnr); if (!i2c_bus) - return -1; - + return -EFAULT; +#else + i2c_bus = dev_get_priv(dev); +#endif i2c_bus->clock_frequency = speed; if (i2c_bus->is_highspeed) { if (hsi2c_get_clk_details(i2c_bus)) - return -1; + return -EFAULT; hsi2c_ch_init(i2c_bus); } else { i2c_ch_init(i2c_bus->regs, i2c_bus->clock_frequency, @@ -721,17 +745,6 @@ static unsigned int s3c24x0_i2c_set_bus_speed(struct i2c_adapter *adap, return 0; } -#ifdef CONFIG_EXYNOS5 -static void exynos_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) -{ - /* This will override the speed selected in the fdt for that port */ - debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr); - if (i2c_set_bus_speed(speed)) - printf("i2c_init: failed to init bus %d for speed = %d\n", - adap->hwadapnr, speed); -} -#endif - /* * cmd_type is 0 for write, 1 for read. * @@ -844,15 +857,23 @@ bailout: return result; } +#ifdef CONFIG_SYS_I2C static int s3c24x0_i2c_probe(struct i2c_adapter *adap, uchar chip) +#else +static int s3c24x0_i2c_probe(struct udevice *dev, uint chip, uint chip_flags) +#endif { struct s3c24x0_i2c_bus *i2c_bus; uchar buf[1]; int ret; +#ifdef CONFIG_SYS_I2C i2c_bus = get_bus(adap->hwadapnr); if (!i2c_bus) - return -1; + return -EFAULT; +#else + i2c_bus = dev_get_priv(dev); +#endif buf[0] = 0; /* @@ -871,6 +892,7 @@ static int s3c24x0_i2c_probe(struct i2c_adapter *adap, uchar chip) return ret != I2C_OK; } +#ifdef CONFIG_SYS_I2C static int s3c24x0_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, int alen, uchar *buffer, int len) { @@ -878,9 +900,13 @@ static int s3c24x0_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, uchar xaddr[4]; int ret; + i2c_bus = get_bus(adap->hwadapnr); + if (!i2c_bus) + return -EFAULT; + if (alen > 4) { debug("I2C read: addr len %d not supported\n", alen); - return 1; + return -EADDRNOTAVAIL; } if (alen > 0) { @@ -906,10 +932,6 @@ static int s3c24x0_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); #endif - i2c_bus = get_bus(adap->hwadapnr); - if (!i2c_bus) - return -1; - if (i2c_bus->is_highspeed) ret = hsi2c_read(i2c_bus->hsregs, chip, &xaddr[4 - alen], alen, buffer, len); @@ -921,7 +943,7 @@ static int s3c24x0_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, if (i2c_bus->is_highspeed) exynos5_i2c_reset(i2c_bus); debug("I2c read failed %d\n", ret); - return 1; + return -EIO; } return 0; } @@ -933,9 +955,13 @@ static int s3c24x0_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, uchar xaddr[4]; int ret; + i2c_bus = get_bus(adap->hwadapnr); + if (!i2c_bus) + return -EFAULT; + if (alen > 4) { debug("I2C write: addr len %d not supported\n", alen); - return 1; + return -EINVAL; } if (alen > 0) { @@ -960,10 +986,6 @@ static int s3c24x0_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); #endif - i2c_bus = get_bus(adap->hwadapnr); - if (!i2c_bus) - return -1; - if (i2c_bus->is_highspeed) ret = hsi2c_write(i2c_bus->hsregs, chip, &xaddr[4 - alen], alen, buffer, len, true); @@ -985,7 +1007,7 @@ static void process_nodes(const void *blob, int node_list[], int count, int is_highspeed) { struct s3c24x0_i2c_bus *bus; - int i; + int i, flags; for (i = 0; i < count; i++) { int node = node_list[i]; @@ -997,12 +1019,15 @@ static void process_nodes(const void *blob, int node_list[], int count, bus->active = true; bus->is_highspeed = is_highspeed; - if (is_highspeed) + if (is_highspeed) { + flags = PINMUX_FLAG_HS_MODE; bus->hsregs = (struct exynos5_hsi2c *) fdtdec_get_addr(blob, node, "reg"); - else + } else { + flags = 0; bus->regs = (struct s3c24x0_i2c *) fdtdec_get_addr(blob, node, "reg"); + } bus->id = pinmux_decode_periph_id(blob, node); bus->clock_frequency = fdtdec_get_int(blob, node, @@ -1010,7 +1035,7 @@ static void process_nodes(const void *blob, int node_list[], int count, CONFIG_SYS_I2C_S3C24X0_SPEED); bus->node = node; bus->bus_num = i; - exynos_pinmux_config(bus->id, 0); + exynos_pinmux_config(PERIPH_ID_I2C0 + bus->id, flags); /* Mark position as used */ node_list[i] = -1; @@ -1033,7 +1058,6 @@ void board_i2c_init(const void *blob) COMPAT_SAMSUNG_EXYNOS5_I2C, node_list, CONFIG_MAX_I2C_NUM); process_nodes(blob, node_list, count, 1); - } int i2c_get_bus_num_fdt(int node) @@ -1046,7 +1070,7 @@ int i2c_get_bus_num_fdt(int node) } debug("%s: Can't find any matched I2C bus\n", __func__); - return -1; + return -EINVAL; } int i2c_reset_port_fdt(const void *blob, int node) @@ -1057,18 +1081,18 @@ int i2c_reset_port_fdt(const void *blob, int node) bus = i2c_get_bus_num_fdt(node); if (bus < 0) { debug("could not get bus for node %d\n", node); - return -1; + return bus; } i2c_bus = get_bus(bus); if (!i2c_bus) { - debug("get_bus() failed for node node %d\n", node); - return -1; + debug("get_bus() failed for node %d\n", node); + return -EFAULT; } if (i2c_bus->is_highspeed) { if (hsi2c_get_clk_details(i2c_bus)) - return -1; + return -EINVAL; hsi2c_ch_init(i2c_bus); } else { i2c_ch_init(i2c_bus->regs, i2c_bus->clock_frequency, @@ -1077,7 +1101,17 @@ int i2c_reset_port_fdt(const void *blob, int node) return 0; } -#endif +#endif /* CONFIG_OF_CONTROL */ + +#ifdef CONFIG_EXYNOS5 +static void exynos_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + /* This will override the speed selected in the fdt for that port */ + debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr); + if (i2c_set_bus_speed(speed)) + error("i2c_init: failed to init bus for speed = %d", speed); +} +#endif /* CONFIG_EXYNOS5 */ /* * Register s3c24x0 i2c adapters @@ -1247,3 +1281,120 @@ U_BOOT_I2C_ADAP_COMPLETE(s3c0, s3c24x0_i2c_init, s3c24x0_i2c_probe, CONFIG_SYS_I2C_S3C24X0_SPEED, CONFIG_SYS_I2C_S3C24X0_SLAVE, 0) #endif +#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) +{ + int ret; + + if (i2c_bus->is_highspeed) { + ret = hsi2c_write(i2c_bus->hsregs, chip, 0, 0, + buffer, 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 ret != I2C_OK; +} + +static int i2c_read_data(struct s3c24x0_i2c_bus *i2c_bus, uchar chip, + uchar *buffer, int len) +{ + int ret; + + if (i2c_bus->is_highspeed) { + ret = hsi2c_read(i2c_bus->hsregs, chip, 0, 0, buffer, len); + if (ret) + exynos5_i2c_reset(i2c_bus); + } else { + ret = i2c_transfer(i2c_bus->regs, I2C_READ, + chip << 1, 0, 0, buffer, len); + } + + return ret != I2C_OK; +} + +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; + + 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); + } + if (ret) + return -EREMOTEIO; + } + + return 0; +} + +static int s3c_i2c_ofdata_to_platdata(struct udevice *dev) +{ + const void *blob = gd->fdt_blob; + struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev); + int node, flags; + + i2c_bus->is_highspeed = dev->of_id->data; + node = dev->of_offset; + + if (i2c_bus->is_highspeed) { + flags = PINMUX_FLAG_HS_MODE; + i2c_bus->hsregs = (struct exynos5_hsi2c *) + fdtdec_get_addr(blob, node, "reg"); + } else { + flags = 0; + i2c_bus->regs = (struct s3c24x0_i2c *) + fdtdec_get_addr(blob, node, "reg"); + } + + 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); + i2c_bus->node = node; + i2c_bus->bus_num = dev->seq; + + exynos_pinmux_config(i2c_bus->id, flags); + + i2c_bus->active = true; + + return 0; +} + +static const struct dm_i2c_ops s3c_i2c_ops = { + .xfer = s3c24x0_i2c_xfer, + .probe_chip = s3c24x0_i2c_probe, + .set_bus_speed = s3c24x0_i2c_set_bus_speed, +}; + +static const struct udevice_id s3c_i2c_ids[] = { + { .compatible = "samsung,s3c2440-i2c", .data = EXYNOS_I2C_STD }, + { .compatible = "samsung,exynos5-hsi2c", .data = EXYNOS_I2C_HS }, + { } +}; + +U_BOOT_DRIVER(i2c_s3c) = { + .name = "i2c_s3c", + .id = UCLASS_I2C, + .of_match = s3c_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 = &s3c_i2c_ops, +}; +#endif /* CONFIG_DM_I2C */ -- cgit v1.3.1 From 7132b9fd68a1c76bf557d56dd62756a6607cf761 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 26 Jan 2015 20:29:37 -0700 Subject: dm: i2c: dts: Support an offset-len device tree property Since U-Boot can support different offset lengths (0-4 bytes), add a device tree property to specify this. This avoids hard-coding it in the driver. Signed-off-by: Simon Glass --- doc/device-tree-bindings/i2c/i2c.txt | 28 ++++++++++++++++++++++++++++ drivers/i2c/i2c-uclass.c | 3 ++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 doc/device-tree-bindings/i2c/i2c.txt (limited to 'drivers') diff --git a/doc/device-tree-bindings/i2c/i2c.txt b/doc/device-tree-bindings/i2c/i2c.txt new file mode 100644 index 00000000000..ea918dd61d5 --- /dev/null +++ b/doc/device-tree-bindings/i2c/i2c.txt @@ -0,0 +1,28 @@ +U-Boot I2C +---------- + +U-Boot's I2C model has the concept of an offset within a chip (I2C target +device). The offset can be up to 4 bytes long, but is normally 1 byte, +meaning that offsets from 0 to 255 are supported by the chip. This often +corresponds to register numbers. + +Apart from the controller-specific I2C bindings, U-Boot supports a special +property which allows the chip offset length to be selected. + +Optional properties: +- u-boot,i2c-offset-len - length of chip offset in bytes. If omitted the + default value of 1 is used. + + +Example +------- + +i2c4: i2c@12ca0000 { + cros-ec@1e { + reg = <0x1e>; + compatible = "google,cros-ec"; + i2c-max-frequency = <100000>; + u-boot,i2c-offset-len = <0>; + ec-interrupt = <&gpx1 6 GPIO_ACTIVE_LOW>; + }; +}; diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 393cd6f583a..eafa457845d 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -420,7 +420,8 @@ int i2c_deblock(struct udevice *bus) int i2c_chip_ofdata_to_platdata(const void *blob, int node, struct dm_i2c_chip *chip) { - chip->offset_len = 1; /* default */ + chip->offset_len = fdtdec_get_int(gd->fdt_blob, node, + "u-boot,i2c-offset-len", 1); chip->flags = 0; chip->chip_addr = fdtdec_get_int(gd->fdt_blob, node, "reg", -1); if (chip->chip_addr == -1) { -- cgit v1.3.1 From d744d5613639088246b27edb9ce91ccbd663e5a5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 26 Jan 2015 20:29:39 -0700 Subject: dm: i2c: Add two more I2C init functions to the compatibility layer These functions are useful in case the board calls them. Also fix a missing parameter caused by applying the wrong patch (actually I failed to send v2 and applied v1 by mistake). Signed-off-by: Simon Glass --- drivers/i2c/i2c-uclass-compat.c | 14 ++++++++++++-- include/i2c.h | 17 ++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/i2c-uclass-compat.c b/drivers/i2c/i2c-uclass-compat.c index 841ce0510ca..223f238f4be 100644 --- a/drivers/i2c/i2c-uclass-compat.c +++ b/drivers/i2c/i2c-uclass-compat.c @@ -22,8 +22,8 @@ static int i2c_compat_get_device(uint chip_addr, int alen, return ret; chip = dev_get_parent_platdata(*devp); if (chip->offset_len != alen) { - printf("Requested alen %d does not match chip offset_len %d\n", - alen, chip->offset_len); + printf("I2C chip %x: requested alen %d does not match chip offset_len %d\n", + chip_addr, alen, chip->offset_len); return -EADDRNOTAVAIL; } @@ -96,3 +96,13 @@ int i2c_set_bus_num(unsigned int bus) return 0; } + +void i2c_init(int speed, int slaveaddr) +{ + /* Nothing to do here - the init happens through driver model */ +} + +void board_i2c_init(const void *blob) +{ + /* Nothing to do here - the init happens through driver model */ +} diff --git a/include/i2c.h b/include/i2c.h index 95d6f287719..27fe00f1736 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -225,7 +225,7 @@ int i2c_get_bus_num_fdt(int node); unsigned int i2c_get_bus_num(void); /** - * i2c_set_bus_num(): Compatibility function for driver model + * i2c_set_bus_num() - Compatibility function for driver model * * Sets the 'current' bus */ @@ -241,6 +241,21 @@ static inline unsigned int I2C_GET_BUS(void) return i2c_get_bus_num(); } +/** + * i2c_init() - Compatibility function for driver model + * + * This function does nothing. + */ +void i2c_init(int speed, int slaveaddr); + +/** + * board_i2c_init() - Compatibility function for driver model + * + * @param blob Device tree blbo + * @return the number of I2C bus + */ +void board_i2c_init(const void *blob); + #endif /* -- cgit v1.3.1 From 85df958ce267c602a4ec5f1e41f336c5a8d3b441 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 26 Jan 2015 20:29:40 -0700 Subject: dm: cros_ec: Convert cros_ec_i2c over to driver model Move this driver to use driver model and update the snow configuration to match. Signed-off-by: Simon Glass --- drivers/misc/cros_ec_i2c.c | 82 ++++++++++++++++------------------------------ include/configs/snow.h | 1 + 2 files changed, 30 insertions(+), 53 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cros_ec_i2c.c b/drivers/misc/cros_ec_i2c.c index 513cdb1cb09..f9bc9750d4c 100644 --- a/drivers/misc/cros_ec_i2c.c +++ b/drivers/misc/cros_ec_i2c.c @@ -14,6 +14,7 @@ */ #include +#include #include #include @@ -23,11 +24,11 @@ #define debug_trace(fmt, b...) #endif -int cros_ec_i2c_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, - const uint8_t *dout, int dout_len, - uint8_t **dinp, int din_len) +static int cros_ec_i2c_command(struct udevice *udev, uint8_t cmd, + int cmd_version, const uint8_t *dout, + int dout_len, uint8_t **dinp, int din_len) { - int old_bus = 0; + struct cros_ec_dev *dev = udev->uclass_priv; /* version8, cmd8, arglen8, out8[dout_len], csum8 */ int out_bytes = dout_len + 4; /* response8, arglen8, in8[din_len], checksum8 */ @@ -37,8 +38,6 @@ int cros_ec_i2c_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, uint8_t *in_ptr; int len, csum, ret; - old_bus = i2c_get_bus_num(); - /* * Sanity-check I/O sizes given transaction overhead in internal * buffers. @@ -86,36 +85,24 @@ int cros_ec_i2c_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, *ptr++ = (uint8_t) cros_ec_calc_checksum(dev->dout, dout_len + 3); - /* Set to the proper i2c bus */ - if (i2c_set_bus_num(dev->bus_num)) { - debug("%s: Cannot change to I2C bus %d\n", __func__, - dev->bus_num); - return -1; - } - /* Send output data */ cros_ec_dump_data("out", -1, dev->dout, out_bytes); - ret = i2c_write(dev->addr, 0, 0, dev->dout, out_bytes); + ret = dm_i2c_write(udev, 0, dev->dout, out_bytes); if (ret) { - debug("%s: Cannot complete I2C write to 0x%x\n", - __func__, dev->addr); + debug("%s: Cannot complete I2C write to %s\n", __func__, + udev->name); ret = -1; } if (!ret) { - ret = i2c_read(dev->addr, 0, 0, in_ptr, in_bytes); + ret = dm_i2c_read(udev, 0, in_ptr, in_bytes); if (ret) { - debug("%s: Cannot complete I2C read from 0x%x\n", - __func__, dev->addr); + debug("%s: Cannot complete I2C read from %s\n", + __func__, udev->name); ret = -1; } } - /* Return to original bus number */ - i2c_set_bus_num(old_bus); - if (ret) - return ret; - if (*in_ptr != EC_RES_SUCCESS) { debug("%s: Received bad result code %d\n", __func__, *in_ptr); return -(int)*in_ptr; @@ -142,35 +129,24 @@ int cros_ec_i2c_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, return din_len; } -int cros_ec_i2c_decode_fdt(struct cros_ec_dev *dev, const void *blob) +static int cros_ec_probe(struct udevice *dev) { - /* Decode interface-specific FDT params */ - dev->max_frequency = fdtdec_get_int(blob, dev->node, - "i2c-max-frequency", 100000); - dev->bus_num = i2c_get_bus_num_fdt(dev->parent_node); - if (dev->bus_num == -1) { - debug("%s: Failed to read bus number\n", __func__); - return -1; - } - dev->addr = fdtdec_get_int(blob, dev->node, "reg", -1); - if (dev->addr == -1) { - debug("%s: Failed to read device address\n", __func__); - return -1; - } - - return 0; + return cros_ec_register(dev); } -/** - * Initialize I2C protocol. - * - * @param dev CROS_EC device - * @param blob Device tree blob - * @return 0 if ok, -1 on error - */ -int cros_ec_i2c_init(struct cros_ec_dev *dev, const void *blob) -{ - i2c_init(dev->max_frequency, dev->addr); - - return 0; -} +static struct dm_cros_ec_ops cros_ec_ops = { + .command = cros_ec_i2c_command, +}; + +static const struct udevice_id cros_ec_ids[] = { + { .compatible = "google,cros-ec" }, + { } +}; + +U_BOOT_DRIVER(cros_ec_i2c) = { + .name = "cros_ec", + .id = UCLASS_CROS_EC, + .of_match = cros_ec_ids, + .probe = cros_ec_probe, + .ops = &cros_ec_ops, +}; diff --git a/include/configs/snow.h b/include/configs/snow.h index 7eaa58697e4..ce6676eae7f 100644 --- a/include/configs/snow.h +++ b/include/configs/snow.h @@ -22,6 +22,7 @@ #define CONFIG_CROS_EC_I2C /* Support CROS_EC over I2C */ #define CONFIG_POWER_TPS65090_I2C +#define CONFIG_DM_CROS_EC #define CONFIG_BOARD_COMMON #define CONFIG_ARCH_EARLY_INIT_R -- cgit v1.3.1