diff --git a/commands/i2c.c b/commands/i2c.c index d6c5412..f4ffc99 100644 --- a/commands/i2c.c +++ b/commands/i2c.c @@ -22,30 +22,16 @@ #include #include -static int do_i2c_probe(int argc, char *argv[]) +static void i2c_probe_range(struct i2c_adapter *adapter, int startaddr, int stopaddr) { - struct i2c_adapter *adapter; - struct i2c_client client; - int startaddr = -1, stopaddr = -1, addr, ret; + struct i2c_client client = {}; + int addr; + int ret; u8 reg; - if (argc < 4) - return COMMAND_ERROR_USAGE; - - adapter = i2c_get_adapter(simple_strtoul(argv[1], NULL, 0)); - if (!adapter) - return -ENODEV; client.adapter = adapter; - startaddr = simple_strtol(argv[2], NULL, 0); - stopaddr = simple_strtol(argv[3], NULL, 0); - if ((startaddr == -1) || (stopaddr == -1) || (startaddr > stopaddr)) - return COMMAND_ERROR_USAGE; - - if (stopaddr > 0x7F) - stopaddr = 0x7F; - - printf("probing i2c range 0x%02x - 0x%02x :\n", startaddr, stopaddr); + printf("probing i2c%d range 0x%02x-0x%02x: ", adapter->nr, startaddr, stopaddr); for (addr = startaddr; addr <= stopaddr; addr++) { client.addr = addr; ret = i2c_write_reg(&client, 0x00, ®, 0); @@ -53,6 +39,38 @@ printf("0x%02x ", addr); } printf("\n"); +} + +static int do_i2c_probe(int argc, char *argv[]) +{ + struct i2c_adapter *adapter = NULL; + int startaddr = 0, stopaddr = 0x7f; + + if (argc > 1) { + adapter = i2c_get_adapter(simple_strtoul(argv[1], NULL, 0)); + if (!adapter) + return -ENODEV; + } + + if (argc > 2) + startaddr = simple_strtol(argv[2], NULL, 0); + if (argc > 3) + startaddr = simple_strtol(argv[3], NULL, 0); + + + if (startaddr > stopaddr) + return COMMAND_ERROR_USAGE; + + if (stopaddr > 0x7F) + stopaddr = 0x7F; + + if (adapter) { + i2c_probe_range(adapter, startaddr, stopaddr); + } else { + for_each_i2c_adapter(adapter) + i2c_probe_range(adapter, startaddr, stopaddr); + } + return 0; } diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 1264e40..b889a48 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -54,9 +54,6 @@ drv->of_compatible) return of_match(dev, drv); - if (!strcmp(dev->name, drv->name)) - return 0; - if (drv->id_table) { const struct platform_device_id *id = drv->id_table; @@ -67,6 +64,8 @@ } id++; } + } else if (!strcmp(dev->name, drv->name)) { + return 0; } return -1; diff --git a/drivers/eeprom/at24.c b/drivers/eeprom/at24.c index 76f30e7..3c0a7a9 100644 --- a/drivers/eeprom/at24.c +++ b/drivers/eeprom/at24.c @@ -84,7 +84,9 @@ */ static unsigned write_timeout = 25; +/* number of bits in driver_data reserved for eeprom byte length */ #define AT24_SIZE_BYTELEN 5 +/* number of bits in driver_data reserved for flags */ #define AT24_SIZE_FLAGS 8 #define AT24_BITMASK(x) (BIT(x) - 1) @@ -113,6 +115,7 @@ { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) }, { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) }, { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) }, + { "24c1025", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16 | AT24_FLAG_BANK_BIT_2) }, { "at24", 0 }, { /* END OF LIST */ } }; @@ -381,6 +384,7 @@ chip = *(struct at24_platform_data *)dev->platform_data; } else { unsigned long magic; + u32 page_size; err = dev_get_drvdata(dev, (const void **)&magic); if (err) @@ -389,12 +393,17 @@ chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN)); magic >>= AT24_SIZE_BYTELEN; chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS); - /* - * This is slow, but we can't know all eeproms, so we better - * play safe. Specifying custom eeprom-types via platform_data - * is recommended anyhow. - */ - chip.page_size = 1; + if (dev->device_node && + !of_property_read_u32(dev->device_node, "pagesize", &page_size)) + chip.page_size = page_size; + else { + /* + * This is slow, but we can't know all eeproms, so we better + * play safe. Specifying custom eeprom-types via platform_data + * is recommended anyhow. + */ + chip.page_size = 1; + } } if (!is_power_of_2(chip.byte_len)) @@ -460,11 +469,12 @@ /* use dummy devices for multiple-address chips */ for (i = 1; i < num_addresses; i++) { + const int shift = (chip.flags & AT24_FLAG_BANK_BIT_2) ? 2 : 0; at24->client[i] = i2c_new_dummy(client->adapter, - client->addr + i); + client->addr + (i << shift)); if (!at24->client[i]) { dev_err(&client->dev, "address 0x%02x unavailable\n", - client->addr + i); + client->addr + (i << shift)); err = -EADDRINUSE; goto err_clients; } diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 0c6aec3..56259d8 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -6,4 +6,13 @@ source drivers/i2c/algos/Kconfig source drivers/i2c/busses/Kconfig +config I2C_MUX + tristate "I2C bus multiplexing support" + help + Say Y here if you want the I2C core to support the ability to + handle multiplexed I2C bus topologies, by presenting each + multiplexed segment as a I2C adapter. + +source drivers/i2c/muxes/Kconfig + endif diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 648d844..c936534 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -1 +1,2 @@ -obj-$(CONFIG_I2C) += i2c.o i2c-smbus.o busses/ algos/ +obj-$(CONFIG_I2C) += i2c.o i2c-smbus.o busses/ algos/ muxes/ +obj-$(CONFIG_I2C_MUX) += i2c-mux.o diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 181321b..a25a871 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -25,6 +25,12 @@ for many i.MX ARM based SoCs, for MPC85xx and MPC5200 PowerPC based SoCs. +config I2C_DESIGNWARE + bool "Synopsys DesignWare I2C Master driver" + help + If you say yes to this option, support will be included for the + Synopsys DesignWare I2C adapter. Only master mode is supported. + config I2C_MV64XXX bool "Marvell mv64xxx I2C Controller" depends on HAVE_CLK && OFDEVICE diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 1dbfbdf..8dccc38 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_I2C_OMAP) += i2c-omap.o obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o +obj-$(CONFIG_I2C_DESIGNWARE) += i2c-designware.o diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c new file mode 100644 index 0000000..a51439f --- /dev/null +++ b/drivers/i2c/busses/i2c-designware.c @@ -0,0 +1,574 @@ +/* + * Synopsys DesignWare I2C adapter driver (master only). + * + * Partly based on code of similar driver from U-Boot: + * Copyright (C) 2009 ST Micoelectronics + * + * and corresponding code from Linux Kernel + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd. + * + * Copyright (C) 2015 Andrey Smirnov + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DW_I2C_BIT_RATE 100000 + +#define DW_IC_CON 0x0 +#define DW_IC_CON_MASTER (1 << 0) +#define DW_IC_CON_SPEED_STD (1 << 1) +#define DW_IC_CON_SPEED_FAST (1 << 2) +#define DW_IC_CON_SLAVE_DISABLE (1 << 6) + +#define DW_IC_TAR 0x4 + +#define DW_IC_DATA_CMD 0x10 +#define DW_IC_DATA_CMD_CMD (1 << 8) +#define DW_IC_DATA_CMD_STOP (1 << 9) + +#define DW_IC_SS_SCL_HCNT 0x14 +#define DW_IC_SS_SCL_LCNT 0x18 +#define DW_IC_FS_SCL_HCNT 0x1c +#define DW_IC_FS_SCL_LCNT 0x20 + +#define DW_IC_INTR_MASK 0x30 + +#define DW_IC_RAW_INTR_STAT 0x34 +#define DW_IC_INTR_RX_UNDER (1 << 0) +#define DW_IC_INTR_RX_OVER (1 << 1) +#define DW_IC_INTR_RX_FULL (1 << 2) +#define DW_IC_INTR_TX_OVER (1 << 3) +#define DW_IC_INTR_TX_EMPTY (1 << 4) +#define DW_IC_INTR_RD_REQ (1 << 5) +#define DW_IC_INTR_TX_ABRT (1 << 6) +#define DW_IC_INTR_RX_DONE (1 << 7) +#define DW_IC_INTR_ACTIVITY (1 << 8) +#define DW_IC_INTR_STOP_DET (1 << 9) +#define DW_IC_INTR_START_DET (1 << 10) +#define DW_IC_INTR_GEN_CALL (1 << 11) + +#define DW_IC_RX_TL 0x38 +#define DW_IC_TX_TL 0x3c +#define DW_IC_CLR_INTR 0x40 +#define DW_IC_CLR_TX_ABRT 0x54 + +#define DW_IC_ENABLE 0x6c +#define DW_IC_ENABLE_ENABLE (1 << 0) + +#define DW_IC_STATUS 0x70 +#define DW_IC_STATUS_TFNF (1 << 1) +#define DW_IC_STATUS_TFE (1 << 2) +#define DW_IC_STATUS_RFNE (1 << 3) +#define DW_IC_STATUS_MST_ACTIVITY (1 << 5) + +#define DW_IC_TX_ABRT_SOURCE 0x80 + +#define DW_IC_ENABLE_STATUS 0x9c +#define DW_IC_ENABLE_STATUS_IC_EN (1 << 0) + +#define DW_IC_COMP_TYPE 0xfc +#define DW_IC_COMP_TYPE_VALUE 0x44570140 + +#define MAX_T_POLL_COUNT 100 + +#define DW_TIMEOUT_IDLE (40 * MSECOND) +#define DW_TIMEOUT_TX (2 * MSECOND) +#define DW_TIMEOUT_RX (2 * MSECOND) + +struct dw_i2c_dev { + void __iomem *base; + struct clk *clk; + struct i2c_adapter adapter; +}; + +static inline struct dw_i2c_dev *to_dw_i2c_dev(struct i2c_adapter *a) +{ + return container_of(a, struct dw_i2c_dev, adapter); +} + +static void i2c_dw_enable(struct dw_i2c_dev *dw, bool enable) +{ + /* + * This subrotine is an implementation of an algorithm + * described in "Cyclone V Hard Processor System Technical + * Reference * Manual" p. 20-19, "Disabling the I2C Controller" + */ + int timeout = MAX_T_POLL_COUNT; + + enable = enable ? DW_IC_ENABLE_ENABLE : 0; + + do { + uint32_t ic_enable_status; + + writel(enable, dw->base + DW_IC_ENABLE); + + ic_enable_status = readl(dw->base + DW_IC_ENABLE_STATUS); + if ((ic_enable_status & DW_IC_ENABLE_STATUS_IC_EN) == enable) + return; + + udelay(250); + } while (timeout--); + + dev_warn(&dw->adapter.dev, "timeout in %sabling adapter\n", + enable ? "en" : "dis"); +} + +/* + * All of the code pertaining to tming calculation is taken from + * analogous driver in Linux kernel + */ +static uint32_t +i2c_dw_scl_hcnt(uint32_t ic_clk, uint32_t tSYMBOL, uint32_t tf, int cond, + int offset) +{ + /* + * DesignWare I2C core doesn't seem to have solid strategy to meet + * the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec + * will result in violation of the tHD;STA spec. + */ + if (cond) + /* + * Conditional expression: + * + * IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH + * + * This is based on the DW manuals, and represents an ideal + * configuration. The resulting I2C bus speed will be + * faster than any of the others. + * + * If your hardware is free from tHD;STA issue, try this one. + */ + return (ic_clk * tSYMBOL + 500000) / 1000000 - 8 + offset; + else + /* + * Conditional expression: + * + * IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf) + * + * This is just experimental rule; the tHD;STA period turned + * out to be proportinal to (_HCNT + 3). With this setting, + * we could meet both tHIGH and tHD;STA timing specs. + * + * If unsure, you'd better to take this alternative. + * + * The reason why we need to take into account "tf" here, + * is the same as described in i2c_dw_scl_lcnt(). + */ + return (ic_clk * (tSYMBOL + tf) + 500000) / 1000000 + - 3 + offset; +} + +static uint32_t +i2c_dw_scl_lcnt(uint32_t ic_clk, uint32_t tLOW, uint32_t tf, int offset) +{ + /* + * Conditional expression: + * + * IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf) + * + * DW I2C core starts counting the SCL CNTs for the LOW period + * of the SCL clock (tLOW) as soon as it pulls the SCL line. + * In order to meet the tLOW timing spec, we need to take into + * account the fall time of SCL signal (tf). Default tf value + * should be 0.3 us, for safety. + */ + return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset; +} + +static void i2c_dw_setup_timings(struct dw_i2c_dev *dw) +{ + uint32_t hcnt, lcnt; + + const uint32_t sda_falling_time = 300; /* ns */ + const uint32_t scl_falling_time = 300; /* ns */ + + const unsigned int input_clock_khz = clk_get_rate(dw->clk) / 1000; + + /* Set SCL timing parameters for standard-mode */ + hcnt = i2c_dw_scl_hcnt(input_clock_khz, + 4000, /* tHD;STA = tHIGH = 4.0 us */ + sda_falling_time, + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + lcnt = i2c_dw_scl_lcnt(input_clock_khz, + 4700, /* tLOW = 4.7 us */ + scl_falling_time, + 0); /* No offset */ + + writel(hcnt, dw->base + DW_IC_SS_SCL_HCNT); + writel(lcnt, dw->base + DW_IC_SS_SCL_LCNT); + + hcnt = i2c_dw_scl_hcnt(input_clock_khz, + 600, /* tHD;STA = tHIGH = 0.6 us */ + sda_falling_time, + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + lcnt = i2c_dw_scl_lcnt(input_clock_khz, + 1300, /* tLOW = 1.3 us */ + scl_falling_time, + 0); /* No offset */ + + writel(hcnt, dw->base + DW_IC_FS_SCL_HCNT); + writel(lcnt, dw->base + DW_IC_FS_SCL_LCNT); +} + +static int i2c_dw_wait_for_bits(struct dw_i2c_dev *dw, uint32_t offset, + uint32_t mask, uint32_t value, uint64_t timeout) +{ + const uint64_t start = get_time_ns(); + + do { + const uint32_t reg = readl(dw->base + offset); + + if ((reg & mask) == value) + return 0; + + } while (!is_timeout(start, timeout)); + + return -ETIMEDOUT; +} + +static int i2c_dw_wait_for_idle(struct dw_i2c_dev *dw) +{ + const uint32_t mask = DW_IC_STATUS_MST_ACTIVITY | DW_IC_STATUS_TFE; + const uint32_t value = DW_IC_STATUS_TFE; + + return i2c_dw_wait_for_bits(dw, DW_IC_STATUS, mask, value, + DW_TIMEOUT_IDLE); +} + +static int i2c_dw_wait_for_tx_fifo_not_full(struct dw_i2c_dev *dw) +{ + const uint32_t mask = DW_IC_STATUS_TFNF; + const uint32_t value = DW_IC_STATUS_TFNF; + + return i2c_dw_wait_for_bits(dw, DW_IC_STATUS, mask, value, + DW_TIMEOUT_TX); +} + +static int i2c_dw_wait_for_rx_fifo_not_empty(struct dw_i2c_dev *dw) +{ + const uint32_t mask = DW_IC_STATUS_RFNE; + const uint32_t value = DW_IC_STATUS_RFNE; + + return i2c_dw_wait_for_bits(dw, DW_IC_STATUS, mask, value, + DW_TIMEOUT_RX); +} + +static void i2c_dw_reset(struct dw_i2c_dev *dw) +{ + i2c_dw_enable(dw, false); + i2c_dw_enable(dw, true); +} + +static void i2c_dw_abort_tx(struct dw_i2c_dev *dw) +{ + i2c_dw_reset(dw); +} + +static void i2c_dw_abort_rx(struct dw_i2c_dev *dw) +{ + i2c_dw_reset(dw); +} + +static int i2c_dw_read(struct dw_i2c_dev *dw, + const struct i2c_msg *msg) +{ + int i; + for (i = 0; i < msg->len; i++) { + int ret; + const bool last_byte = i == msg->len - 1; + uint32_t ic_cmd_data = DW_IC_DATA_CMD_CMD; + + if (last_byte) + ic_cmd_data |= DW_IC_DATA_CMD_STOP; + + writel(ic_cmd_data, dw->base + DW_IC_DATA_CMD); + + ret = i2c_dw_wait_for_rx_fifo_not_empty(dw); + if (ret < 0) { + i2c_dw_abort_rx(dw); + return ret; + } + + msg->buf[i] = (uint8_t)readl(dw->base + DW_IC_DATA_CMD); + } + + return msg->len; +} + +static int i2c_dw_write(struct dw_i2c_dev *dw, + const struct i2c_msg *msg) +{ + int i; + uint32_t ic_int_stat; + + for (i = 0; i < msg->len; i++) { + int ret; + uint32_t ic_cmd_data; + const bool last_byte = i == msg->len - 1; + + ic_int_stat = readl(dw->base + DW_IC_RAW_INTR_STAT); + + if (ic_int_stat & DW_IC_INTR_TX_ABRT) + return -EIO; + + ret = i2c_dw_wait_for_tx_fifo_not_full(dw); + if (ret < 0) { + i2c_dw_abort_tx(dw); + return ret; + } + + ic_cmd_data = msg->buf[i]; + + if (last_byte) + ic_cmd_data |= DW_IC_DATA_CMD_STOP; + + writel(ic_cmd_data, dw->base + DW_IC_DATA_CMD); + } + + return msg->len; +} + +static int i2c_dw_wait_for_stop(struct dw_i2c_dev *dw) +{ + const uint32_t mask = DW_IC_INTR_STOP_DET; + const uint32_t value = DW_IC_INTR_STOP_DET; + + return i2c_dw_wait_for_bits(dw, DW_IC_RAW_INTR_STAT, mask, value, + DW_TIMEOUT_IDLE); +} + +static int i2c_dw_finish_xfer(struct dw_i2c_dev *dw) +{ + int ret; + uint32_t ic_int_stat; + + /* + * We expect the controller to signal STOP condition on the + * bus, so we are going to wait for that first. + */ + ret = i2c_dw_wait_for_stop(dw); + if (ret < 0) + return ret; + + /* + * Now that we now that the stop condition has been signaled + * we need to wait for controller to go into IDLE state to + * make sure all of the possible error conditions on the bus + * have been propagated to apporpriate status + * registers. Experiment shows that not doing so often results + * in false positive "successful" transfers + */ + ret = i2c_dw_wait_for_idle(dw); + + if (ret >= 0) { + ic_int_stat = readl(dw->base + DW_IC_RAW_INTR_STAT); + + if (ic_int_stat & DW_IC_INTR_TX_ABRT) + return -EIO; + } + + return ret; +} + +static int i2c_dw_set_address(struct dw_i2c_dev *dw, uint8_t address) +{ + int ret; + uint32_t ic_tar; + /* + * As per "Cyclone V Hard Processor System Technical Reference + * Manual" p. 20-19, we have to wait for controller to be in + * idle state in order to be able to set the address + * dynamically + */ + ret = i2c_dw_wait_for_idle(dw); + if (ret < 0) + return ret; + + ic_tar = readl(dw->base + DW_IC_TAR); + ic_tar &= 0xfffffc00; + + writel(ic_tar | address, dw->base + DW_IC_TAR); + + return 0; +} + +static int i2c_dw_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + int i, ret = 0; + struct dw_i2c_dev *dw = to_dw_i2c_dev(adapter); + + for (i = 0; i < num; i++) { + if (msgs[i].flags & I2C_M_DATA_ONLY) + return -ENOTSUPP; + + ret = i2c_dw_set_address(dw, msgs[i].addr); + if (ret < 0) + break; + + if (msgs[i].flags & I2C_M_RD) + ret = i2c_dw_read(dw, &msgs[i]); + else + ret = i2c_dw_write(dw, &msgs[i]); + + if (ret < 0) + break; + + ret = i2c_dw_finish_xfer(dw); + if (ret < 0) + break; + } + + if (ret == -EIO) { + /* + * If we got -EIO it means that transfer was for some + * reason aborted, so we should figure out the reason + * and take steps to clear that condition + */ + const uint32_t ic_tx_abrt_source = + readl(dw->base + DW_IC_TX_ABRT_SOURCE); + dev_dbg(&dw->adapter.dev, + "<%s> ic_tx_abrt_source: 0x%04x\n", + __func__, ic_tx_abrt_source); + readl(dw->base + DW_IC_CLR_TX_ABRT); + + return ret; + } + + if (ret < 0) { + i2c_dw_reset(dw); + return ret; + } + + return num; +} + + +static int i2c_dw_probe(struct device_d *pdev) +{ + struct dw_i2c_dev *dw; + struct i2c_platform_data *pdata; + int ret, bitrate; + uint32_t ic_con, ic_comp_type_value; + + pdata = pdev->platform_data; + + dw = xzalloc(sizeof(*dw)); + + if (IS_ENABLED(CONFIG_COMMON_CLK)) { + dw->clk = clk_get(pdev, NULL); + if (IS_ERR(dw->clk)) { + ret = PTR_ERR(dw->clk); + goto fail; + } + } + + dw->adapter.master_xfer = i2c_dw_xfer; + dw->adapter.nr = pdev->id; + dw->adapter.dev.parent = pdev; + dw->adapter.dev.device_node = pdev->device_node; + + dw->base = dev_request_mem_region(pdev, 0); + if (IS_ERR(dw->base)) { + ret = PTR_ERR(dw->base); + goto fail; + } + + ic_comp_type_value = readl(dw->base + DW_IC_COMP_TYPE); + if (ic_comp_type_value != DW_IC_COMP_TYPE_VALUE) { + dev_err(pdev, + "unknown DesignWare IP block 0x%08x", + ic_comp_type_value); + ret = -ENODEV; + goto fail; + } + + i2c_dw_enable(dw, false); + + if (IS_ENABLED(CONFIG_COMMON_CLK)) + i2c_dw_setup_timings(dw); + + bitrate = (pdata && pdata->bitrate) ? pdata->bitrate : DW_I2C_BIT_RATE; + + /* + * We have to clear 'ic_10bitaddr_master' in 'ic_tar' + * register, otherwise 'ic_10bitaddr_master' in 'ic_con' + * wouldn't clear. We don't care about preserving the contents + * of that register so we set it to zero. + */ + writel(0, dw->base + DW_IC_TAR); + + switch (bitrate) { + case 400000: + ic_con = DW_IC_CON_SPEED_FAST; + break; + default: + dev_warn(pdev, "requested bitrate (%d) is not supported." + " Falling back to 100kHz", bitrate); + case 100000: /* FALLTHROUGH */ + ic_con = DW_IC_CON_SPEED_STD; + break; + } + + ic_con |= DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE; + + writel(ic_con, dw->base + DW_IC_CON); + + /* + * Since we will be working in polling mode set both + * thresholds to their minimum + */ + writel(0, dw->base + DW_IC_RX_TL); + writel(0, dw->base + DW_IC_TX_TL); + + /* Disable and clear all interrrupts */ + writel(0, dw->base + DW_IC_INTR_MASK); + readl(dw->base + DW_IC_CLR_INTR); + + i2c_dw_enable(dw, true); + + ret = i2c_add_numbered_adapter(&dw->adapter); +fail: + if (ret < 0) + kfree(dw); + + return ret; +} + +static __maybe_unused struct of_device_id i2c_dw_dt_ids[] = { + { .compatible = "snps,designware-i2c", }, + { /* sentinel */ } +}; + +static struct driver_d i2c_dw_driver = { + .probe = i2c_dw_probe, + .name = "i2c-designware", + .of_compatible = DRV_OF_COMPAT(i2c_dw_dt_ids), +}; +coredevice_platform_driver(i2c_dw_driver); diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 7df4a26..affc277 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -590,7 +590,7 @@ pdata = pdev->platform_data; - i2c_fsl = kzalloc(sizeof(struct fsl_i2c_struct), GFP_KERNEL); + i2c_fsl = xzalloc(sizeof(*i2c_fsl)); #ifdef CONFIG_COMMON_CLK i2c_fsl->clk = clk_get(pdev, NULL); diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c new file mode 100644 index 0000000..f87e1fa --- /dev/null +++ b/drivers/i2c/i2c-mux.c @@ -0,0 +1,148 @@ +/* + * Multiplexed I2C bus driver. + * + * Copyright (c) 2008-2009 Rodolfo Giometti + * Copyright (c) 2008-2009 Eurotech S.p.A. + * Copyright (c) 2009-2010 NSN GmbH & Co KG + * + * Ported to barebox from linux-v4.4-rc1 + * Copyright (C) 2015 Antony Pavlov + * + * Simplifies access to complex multiplexed I2C bus topologies, by presenting + * each multiplexed bus segment as an additional I2C adapter. + * Supports multi-level mux'ing (mux behind a mux). + * + * Based on: + * i2c-virt.c from Kumar Gala + * i2c-virtual.c from Ken Harrenstien, Copyright (c) 2004 Google, Inc. + * i2c-virtual.c from Brian Kuschak + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include + +/* multiplexer per channel data */ +struct i2c_mux_priv { + struct i2c_adapter adap; + + struct i2c_adapter *parent; + struct device_d *mux_dev; + void *mux_priv; + u32 chan_id; + + int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id); + int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id); +}; + +static int i2c_mux_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_adapter *parent = priv->parent; + int ret; + + /* Switch to the right mux port and perform the transfer. */ + + ret = priv->select(parent, priv->mux_priv, priv->chan_id); + if (ret >= 0) + ret = parent->master_xfer(parent, msgs, num); + if (priv->deselect) + priv->deselect(parent, priv->mux_priv, priv->chan_id); + + return ret; +} + +struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, + struct device_d *mux_dev, + void *mux_priv, u32 force_nr, u32 chan_id, + int (*select) (struct i2c_adapter *, + void *, u32), + int (*deselect) (struct i2c_adapter *, + void *, u32)) +{ + struct i2c_mux_priv *priv; + int ret; + + priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL); + if (!priv) + return NULL; + + /* Set up private adapter data */ + priv->parent = parent; + priv->mux_dev = mux_dev; + priv->mux_priv = mux_priv; + priv->chan_id = chan_id; + priv->select = select; + priv->deselect = deselect; + + /* Need to do algo dynamically because we don't know ahead + * of time what sort of physical adapter we'll be dealing with. + */ + if (parent->master_xfer) + priv->adap.master_xfer = i2c_mux_master_xfer; + + /* Now fill out new adapter structure */ + priv->adap.algo_data = priv; + priv->adap.dev.parent = &parent->dev; + priv->adap.retries = parent->retries; + + /* + * Try to populate the mux adapter's device_node, expands to + * nothing if !CONFIG_OF. + */ + if (mux_dev->device_node) { + struct device_node *child; + u32 reg; + + for_each_child_of_node(mux_dev->device_node, child) { + ret = of_property_read_u32(child, "reg", ®); + if (ret) + continue; + if (chan_id == reg) { + priv->adap.dev.device_node = child; + break; + } + } + } + + if (force_nr) { + priv->adap.nr = force_nr; + } else { + priv->adap.nr = -1; + } + + ret = i2c_add_numbered_adapter(&priv->adap); + if (ret < 0) { + dev_err(&parent->dev, + "failed to add mux-adapter (error=%d)\n", + ret); + kfree(priv); + return NULL; + } + + dev_info(&parent->dev, "Added multiplexed i2c bus %d\n", + i2c_adapter_id(&priv->adap)); + + return &priv->adap; +} +EXPORT_SYMBOL_GPL(i2c_add_mux_adapter); + +void i2c_del_mux_adapter(struct i2c_adapter *adap) +{ + struct i2c_mux_priv *priv = adap->algo_data; + + free(priv); +} +EXPORT_SYMBOL_GPL(i2c_del_mux_adapter); + +MODULE_AUTHOR("Rodolfo Giometti "); +MODULE_DESCRIPTION("I2C driver for multiplexed I2C busses"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c index 52aaea8..fa2c0cd 100644 --- a/drivers/i2c/i2c.c +++ b/drivers/i2c/i2c.c @@ -42,7 +42,7 @@ }; static LIST_HEAD(board_list); -static LIST_HEAD(adapter_list); +LIST_HEAD(i2c_adapter_list); /** * i2c_transfer - execute a single or combined I2C message @@ -358,6 +358,14 @@ return adap->bus_recovery_info->recover_bus(adap); } +static void i2c_info(struct device_d *dev) +{ + const struct i2c_client *client = to_i2c_client(dev); + + printf(" Address: 0x%02x\n", client->addr); + return; +} + /** * i2c_new_device - instantiate one new I2C device * @@ -396,6 +404,7 @@ free(client); return NULL; } + client->dev.info = i2c_info; return client; } @@ -529,7 +538,7 @@ { struct i2c_adapter *adap; - list_for_each_entry(adap, &adapter_list, list) + for_each_i2c_adapter(adap) if (adap->nr == busnum) return adap; return NULL; @@ -539,7 +548,7 @@ { struct i2c_adapter *adap; - list_for_each_entry(adap, &adapter_list, list) + for_each_i2c_adapter(adap) if (adap->dev.device_node == node) return adap; @@ -584,7 +593,7 @@ if (ret) return ret; - list_add_tail(&adapter->list, &adapter_list); + list_add_tail(&adapter->list, &i2c_adapter_list); /* populate children from any i2c device tables */ scan_boardinfo(adapter); diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig new file mode 100644 index 0000000..74b7345 --- /dev/null +++ b/drivers/i2c/muxes/Kconfig @@ -0,0 +1,14 @@ +# +# Multiplexer I2C chip drivers configuration +# + +menu "Multiplexer I2C Chip support" + depends on I2C_MUX + +config I2C_MUX_PCA954x + tristate "Philips PCA954x I2C Mux/switches" + help + If you say yes here you get support for the Philips PCA954x + I2C mux/switch devices. + +endmenu diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile new file mode 100644 index 0000000..f35d40d --- /dev/null +++ b/drivers/i2c/muxes/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for multiplexer I2C chip drivers. + +obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c new file mode 100644 index 0000000..baeae7b --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -0,0 +1,251 @@ +/* + * I2C multiplexer + * + * Copyright (c) 2008-2009 Rodolfo Giometti + * Copyright (c) 2008-2009 Eurotech S.p.A. + * + * Ported to barebox from linux-v4.4-rc1 + * Copyright (C) 2015 Antony Pavlov + * + * This module supports the PCA954x series of I2C multiplexer/switch chips + * made by Philips Semiconductors. + * This includes the: + * PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547 + * and PCA9548. + * + * These chips are all controlled via the I2C bus itself, and all have a + * single 8-bit register. The upstream "parent" bus fans out to two, + * four, or eight downstream busses or channels; which of these + * are selected is determined by the chip type and register contents. A + * mux can select only one sub-bus at a time; a switch can select any + * combination simultaneously. + * + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PCA954X_MAX_NCHANS 8 + +enum pca_type { + pca_9540, + pca_9542, + pca_9543, + pca_9544, + pca_9545, + pca_9546, + pca_9547, + pca_9548, +}; + +struct pca954x { + enum pca_type type; + struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS]; + + u8 last_chan; /* last register value */ +}; + +struct chip_desc { + u8 nchans; + u8 enable; /* used for muxes only */ + enum muxtype { + pca954x_ismux = 0, + pca954x_isswi + } muxtype; +}; + +/* Provide specs for the PCA954x types we know about */ +static const struct chip_desc chips[] = { + [pca_9540] = { + .nchans = 2, + .enable = 0x4, + .muxtype = pca954x_ismux, + }, + [pca_9543] = { + .nchans = 2, + .muxtype = pca954x_isswi, + }, + [pca_9544] = { + .nchans = 4, + .enable = 0x4, + .muxtype = pca954x_ismux, + }, + [pca_9545] = { + .nchans = 4, + .muxtype = pca954x_isswi, + }, + [pca_9547] = { + .nchans = 8, + .enable = 0x8, + .muxtype = pca954x_ismux, + }, + [pca_9548] = { + .nchans = 8, + .muxtype = pca954x_isswi, + }, +}; + +static const struct platform_device_id pca954x_id[] = { + { "pca9540", pca_9540 }, + { "pca9542", pca_9540 }, + { "pca9543", pca_9543 }, + { "pca9544", pca_9544 }, + { "pca9545", pca_9545 }, + { "pca9546", pca_9545 }, + { "pca9547", pca_9547 }, + { "pca9548", pca_9548 }, + { } +}; + +/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer() + for this as they will try to lock adapter a second time */ +static int pca954x_reg_write(struct i2c_adapter *adap, + struct i2c_client *client, u8 val) +{ + int ret = -ENODEV; + + if (adap->master_xfer) { + struct i2c_msg msg; + char buf[1]; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 1; + buf[0] = val; + msg.buf = buf; + ret = adap->master_xfer(adap, &msg, 1); + } else { + union i2c_smbus_data data; + ret = i2c_smbus_xfer(adap, client->addr, + 0 /* client->flags */, + I2C_SMBUS_WRITE, + val, I2C_SMBUS_BYTE, &data); + } + + return ret; +} + +static int pca954x_select_chan(struct i2c_adapter *adap, + void *client, u32 chan) +{ + struct pca954x *data = i2c_get_clientdata(client); + const struct chip_desc *chip = &chips[data->type]; + u8 regval; + int ret = 0; + + /* we make switches look like muxes, not sure how to be smarter */ + if (chip->muxtype == pca954x_ismux) + regval = chan | chip->enable; + else + regval = 1 << chan; + + /* Only select the channel if its different from the last channel */ + if (data->last_chan != regval) { + ret = pca954x_reg_write(adap, client, regval); + data->last_chan = regval; + } + + return ret; +} + +/* + * I2C init/probing/exit functions + */ +static int pca954x_probe(struct device_d *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + int num, force; + struct pca954x *data; + int ret = -ENODEV; + + data = kzalloc(sizeof(struct pca954x), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto err; + } + + i2c_set_clientdata(client, data); + + /* Read the mux register at addr to verify + * that the mux is in fact present. + */ + if (i2c_smbus_read_byte(client) < 0) { + dev_warn(&client->dev, "probe failed\n"); + goto exit_free; + } + + ret = dev_get_drvdata(dev, (const void **)&data->type); + if (ret) + goto exit_free; + + data->last_chan = 0; /* force the first selection */ + + /* Now create an adapter for each channel */ + for (num = 0; num < chips[data->type].nchans; num++) { + + data->virt_adaps[num] = + i2c_add_mux_adapter(adap, &client->dev, client, + 0, num, pca954x_select_chan, NULL); + + if (data->virt_adaps[num] == NULL) { + ret = -ENODEV; + dev_err(&client->dev, + "failed to register multiplexed adapter" + " %d as bus %d\n", num, force); + goto virt_reg_failed; + } + } + + dev_info(&client->dev, + "registered %d multiplexed busses for I2C %s\n", + num, chips[data->type].muxtype == pca954x_ismux + ? "mux" : "switch"); + + return 0; + +virt_reg_failed: + for (num--; num >= 0; num--) + i2c_del_mux_adapter(data->virt_adaps[num]); +exit_free: + kfree(data); +err: + return ret; +} + +static struct driver_d pca954x_driver = { + .name = "pca954x", + .probe = pca954x_probe, + .id_table = pca954x_id, +}; + +static int __init pca954x_init(void) +{ + return i2c_driver_register(&pca954x_driver); +} +device_initcall(pca954x_init); + +MODULE_AUTHOR("Rodolfo Giometti "); +MODULE_DESCRIPTION("PCA954x I2C mux/switch driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/i2c/at24.h b/include/i2c/at24.h index 1013308..44a64a0 100644 --- a/include/i2c/at24.h +++ b/include/i2c/at24.h @@ -30,6 +30,7 @@ #define AT24_FLAG_READONLY 0x40 /* sysfs-entry will be read-only */ #define AT24_FLAG_IRUGO 0x20 /* sysfs-entry will be world-readable */ #define AT24_FLAG_TAKE8ADDR 0x10 /* take always 8 addresses (24c00) */ +#define AT24_FLAG_BANK_BIT_2 0x08 /* blank select at bit 2 (vs lsb) */ }; #endif /* _LINUX_AT24_H */ diff --git a/include/i2c/i2c-mux.h b/include/i2c/i2c-mux.h new file mode 100644 index 0000000..8022399 --- /dev/null +++ b/include/i2c/i2c-mux.h @@ -0,0 +1,40 @@ +/* + * + * i2c-mux.h - functions for the i2c-bus mux support + * + * Copyright (c) 2008-2009 Rodolfo Giometti + * Copyright (c) 2008-2009 Eurotech S.p.A. + * Michael Lawnick + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_I2C_MUX_H +#define _LINUX_I2C_MUX_H + +/* + * Called to create a i2c bus on a multiplexed bus segment. + * The mux_dev and chan_id parameters are passed to the select + * and deselect callback functions to perform hardware-specific + * mux control. + */ +struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, + struct device_d *mux_dev, + void *mux_priv, u32 force_nr, u32 chan_id, + int (*select) (struct i2c_adapter *, + void *mux_dev, u32 chan_id), + int (*deselect) (struct i2c_adapter *, + void *mux_dev, u32 chan_id)); + +void i2c_del_mux_adapter(struct i2c_adapter *adap); + +#endif /* _LINUX_I2C_MUX_H */ diff --git a/include/i2c/i2c.h b/include/i2c/i2c.h index 12e4827..33a4791 100644 --- a/include/i2c/i2c.h +++ b/include/i2c/i2c.h @@ -121,15 +121,28 @@ struct i2c_bus_recovery_info *bus_recovery_info; }; +#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) struct i2c_client { struct device_d dev; struct i2c_adapter *adapter; unsigned short addr; + void *driver_data; /* Driver data, set and get with + dev_set/get_drvdata */ }; #define to_i2c_client(a) container_of(a, struct i2c_client, dev) +static inline void *i2c_get_clientdata(const struct i2c_client *dev) +{ + return dev->driver_data; +} + +static inline void i2c_set_clientdata(struct i2c_client *dev, void *data) +{ + dev->driver_data = data; +} + /*flags for the client struct: */ #define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */ #define I2C_CLIENT_SCCB 0x9000 /* Use Omnivision SCCB protocol */ @@ -235,13 +248,21 @@ struct i2c_adapter *i2c_get_adapter(int busnum); struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node); +extern struct list_head i2c_adapter_list; +#define for_each_i2c_adapter(adap) \ + list_for_each_entry(adap, &i2c_adapter_list, list) + /* For devices that use several addresses, use i2c_new_dummy() to make * client handles for the extra addresses. */ extern struct i2c_client * i2c_new_dummy(struct i2c_adapter *adap, u16 address); - +/* Return the adapter number for a specific adapter */ +static inline int i2c_adapter_id(struct i2c_adapter *adap) +{ + return adap->nr; +} extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); extern int i2c_master_send(struct i2c_client *client, const char *buf, int count);