diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/bus.c | 5 | ||||
-rw-r--r-- | drivers/base/driver.c | 4 | ||||
-rw-r--r-- | drivers/eeprom/at24.c | 26 | ||||
-rw-r--r-- | drivers/i2c/Kconfig | 9 | ||||
-rw-r--r-- | drivers/i2c/Makefile | 3 | ||||
-rw-r--r-- | drivers/i2c/busses/Kconfig | 6 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-designware.c | 574 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-imx.c | 2 | ||||
-rw-r--r-- | drivers/i2c/i2c-mux.c | 148 | ||||
-rw-r--r-- | drivers/i2c/i2c.c | 17 | ||||
-rw-r--r-- | drivers/i2c/muxes/Kconfig | 14 | ||||
-rw-r--r-- | drivers/i2c/muxes/Makefile | 4 | ||||
-rw-r--r-- | drivers/i2c/muxes/i2c-mux-pca954x.c | 251 | ||||
-rw-r--r-- | drivers/mci/dw_mmc.c | 31 | ||||
-rw-r--r-- | drivers/mci/mci-core.c | 38 | ||||
-rw-r--r-- | drivers/mtd/core.c | 2 | ||||
-rw-r--r-- | drivers/net/smc91111.c | 2 | ||||
-rw-r--r-- | drivers/spi/imx_spi.c | 73 | ||||
-rw-r--r-- | drivers/video/backlight-pwm.c | 27 | ||||
-rw-r--r-- | drivers/video/fbconsole.c | 39 | ||||
-rw-r--r-- | drivers/video/imx-ipu-v3/imx-ldb.c | 26 | ||||
-rw-r--r-- | drivers/watchdog/imxwd.c | 5 |
23 files changed, 1223 insertions, 84 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 1264e40250..b889a48662 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -54,9 +54,6 @@ int device_match(struct device_d *dev, struct driver_d *drv) 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 @@ int device_match(struct device_d *dev, struct driver_d *drv) } id++; } + } else if (!strcmp(dev->name, drv->name)) { + return 0; } return -1; diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 943deb489c..a70fbb217a 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -380,7 +380,7 @@ void __iomem *dev_request_mem_region_by_name(struct device_d *dev, const char *n if (IS_ERR(res)) return ERR_CAST(res); - return (void __force __iomem *)res->start; + return IOMEM(res->start); } EXPORT_SYMBOL(dev_request_mem_region_by_name); @@ -396,7 +396,7 @@ void __iomem *dev_request_mem_region_err_null(struct device_d *dev, int num) if (IS_ERR(res)) return NULL; - return (void __force __iomem *)res->start; + return IOMEM(res->start); } EXPORT_SYMBOL(dev_request_mem_region_err_null); diff --git a/drivers/eeprom/at24.c b/drivers/eeprom/at24.c index 76f30e7e22..3c0a7a9d47 100644 --- a/drivers/eeprom/at24.c +++ b/drivers/eeprom/at24.c @@ -84,7 +84,9 @@ static unsigned io_limit = 128; */ 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 @@ static struct platform_device_id at24_ids[] = { { "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 @@ static int at24_probe(struct device_d *dev) 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 @@ static int at24_probe(struct device_d *dev) 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 @@ static int at24_probe(struct device_d *dev) /* 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 0c6aec39c9..56259d82d4 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -6,4 +6,13 @@ if I2C 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 648d844252..c93653414e 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 181321b159..a25a871809 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -25,6 +25,12 @@ config I2C_IMX 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 1dbfbdf93b..8dccc38379 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o 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 0000000000..a51439f2d5 --- /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 <andrew.smirnov@gmail.com> + * + * 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 <clock.h> +#include <common.h> +#include <driver.h> +#include <init.h> +#include <of.h> +#include <malloc.h> +#include <types.h> +#include <xfuncs.h> +#include <linux/clk.h> +#include <linux/err.h> + +#include <io.h> +#include <i2c/i2c.h> + +#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 7df4a26dd3..affc277164 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -590,7 +590,7 @@ static int __init i2c_fsl_probe(struct device_d *pdev) 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 0000000000..f87e1fadb6 --- /dev/null +++ b/drivers/i2c/i2c-mux.c @@ -0,0 +1,148 @@ +/* + * Multiplexed I2C bus driver. + * + * Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it> + * Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it> + * Copyright (c) 2009-2010 NSN GmbH & Co KG <michael.lawnick.ext@nsn.com> + * + * Ported to barebox from linux-v4.4-rc1 + * Copyright (C) 2015 Antony Pavlov <antonynpavlov@gmail.com> + * + * 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 <galak@kernel.crashing.org> + * i2c-virtual.c from Ken Harrenstien, Copyright (c) 2004 Google, Inc. + * i2c-virtual.c from Brian Kuschak <bkuschak@yahoo.com> + * + * 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 <common.h> +#include <malloc.h> +#include <xfuncs.h> +#include <of.h> +#include <i2c/i2c.h> +#include <i2c/i2c-mux.h> + +/* 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 <giometti@linux.it>"); +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 52aaea8170..fa2c0cd136 100644 --- a/drivers/i2c/i2c.c +++ b/drivers/i2c/i2c.c @@ -42,7 +42,7 @@ struct boardinfo { }; 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 @@ int i2c_recover_bus(struct i2c_adapter *adap) 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 @@ static struct i2c_client *i2c_new_device(struct i2c_adapter *adapter, free(client); return NULL; } + client->dev.info = i2c_info; return client; } @@ -529,7 +538,7 @@ struct i2c_adapter *i2c_get_adapter(int busnum) { 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 *of_find_i2c_adapter_by_node(struct device_node *node) { 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 @@ int i2c_add_numbered_adapter(struct i2c_adapter *adapter) 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 0000000000..74b73459aa --- /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 0000000000..f35d40d0e3 --- /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 0000000000..baeae7bb94 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -0,0 +1,251 @@ +/* + * I2C multiplexer + * + * Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it> + * Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it> + * + * Ported to barebox from linux-v4.4-rc1 + * Copyright (C) 2015 Antony Pavlov <antonynpavlov@gmail.com> + * + * 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 <galak@kernel.crashing.org> + * 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 <bkuschak@yahoo.com> + * and + * pca9540.c from Jean Delvare <jdelvare@suse.de>. + * + * 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 <common.h> +#include <driver.h> +#include <malloc.h> +#include <i2c/i2c.h> +#include <i2c/i2c-algo-bit.h> +#include <i2c/i2c-mux.h> +#include <init.h> + +#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 <giometti@linux.it>"); +MODULE_DESCRIPTION("PCA954x I2C mux/switch driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mci/dw_mmc.c b/drivers/mci/dw_mmc.c index acdf795ac1..cbd3f00646 100644 --- a/drivers/mci/dw_mmc.c +++ b/drivers/mci/dw_mmc.c @@ -676,11 +676,9 @@ static int dw_mmc_detect(struct device_d *dev) static int dw_mmc_probe(struct device_d *dev) { struct dwmci_host *host; - struct mci_host *mci; struct dw_mmc_platform_data *pdata = dev->platform_data; host = xzalloc(sizeof(*host)); - mci = &host->mci; host->clk_biu = clk_get(dev, "biu"); if (IS_ERR(host->clk_biu)) @@ -698,13 +696,27 @@ static int dw_mmc_probe(struct device_d *dev) if (IS_ERR(host->ioaddr)) return PTR_ERR(host->ioaddr); + host->idmac = dma_alloc_coherent(sizeof(*host->idmac) * DW_MMC_NUM_IDMACS, + DMA_ADDRESS_BROKEN); + + host->mci.send_cmd = dwmci_cmd; + host->mci.set_ios = dwmci_set_ios; + host->mci.init = dwmci_init; + host->mci.card_present = dwmci_card_present; + host->mci.hw_dev = dev; + host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + host->mci.host_caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; + host->mci.host_caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED_52MHZ | + MMC_CAP_SD_HIGHSPEED; + if (pdata) { - mci->devname = pdata->devname; host->ciu_div = pdata->ciu_div; + host->mci.host_caps &= ~MMC_CAP_BIT_DATA_MASK; + host->mci.host_caps |= pdata->bus_width_caps; } else if (dev->device_node) { const char *alias = of_alias_get(dev->device_node); if (alias) - mci->devname = xstrdup(alias); + host->mci.devname = xstrdup(alias); of_property_read_u32(dev->device_node, "dw-mshc-ciu-div", &host->ciu_div); } @@ -712,17 +724,6 @@ static int dw_mmc_probe(struct device_d *dev) /* divider is 0 based in pdata and 1 based in our private struct */ host->ciu_div++; - host->idmac = dma_alloc_coherent(sizeof(*host->idmac) * DW_MMC_NUM_IDMACS, - DMA_ADDRESS_BROKEN); - - host->mci.send_cmd = dwmci_cmd; - host->mci.set_ios = dwmci_set_ios; - host->mci.init = dwmci_init; - host->mci.card_present = dwmci_card_present; - host->mci.hw_dev = dev; - host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; - host->mci.host_caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; - if (of_device_is_compatible(dev->device_node, "rockchip,rk2928-dw-mshc")) host->pwren_value = 0; diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c index 29c0d5474e..4e6b83be5f 100644 --- a/drivers/mci/mci-core.c +++ b/drivers/mci/mci-core.c @@ -1819,23 +1819,29 @@ void mci_of_parse(struct mci_host *host) /* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */ if (of_property_read_u32(np, "bus-width", &bus_width) < 0) { + /* If bus-width is missing we get the driver's default, which + * is, unfortunately, not consistent from driver to driver. + * Better to specify it in the device tree. */ dev_dbg(host->hw_dev, - "\"bus-width\" property is missing, assuming 1 bit.\n"); - bus_width = 1; - } - - switch (bus_width) { - case 8: - host->host_caps |= MMC_CAP_8_BIT_DATA; - /* Hosts capable of 8-bit transfers can also do 4 bits */ - case 4: - host->host_caps |= MMC_CAP_4_BIT_DATA; - break; - case 1: - break; - default: - dev_err(host->hw_dev, - "Invalid \"bus-width\" value %u!\n", bus_width); + "\"bus-width\" property missing, default is %d\n", + (host->host_caps & MMC_CAP_8_BIT_DATA) ? 8 : + (host->host_caps & MMC_CAP_4_BIT_DATA) ? 4 : 1); + } else { + /* Set data width caps to exactly those specified in the DT. + * bus-width isn't a list, so widths smaller than the specified + * value are implictly supported as well. */ + host->host_caps &= ~MMC_CAP_BIT_DATA_MASK; + switch (bus_width) { + case 8: + host->host_caps |= MMC_CAP_8_BIT_DATA; + case 4: /* note fall through from above */ + host->host_caps |= MMC_CAP_4_BIT_DATA; + case 1: + break; + default: + dev_err(host->hw_dev, + "Invalid \"bus-width\" value %u!\n", bus_width); + } } /* f_max is obtained from the optional "max-frequency" property */ diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c index d873369d5d..62307db709 100644 --- a/drivers/mtd/core.c +++ b/drivers/mtd/core.c @@ -340,6 +340,8 @@ int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { + *retlen = 0; + return mtd->write(mtd, to, len, retlen, buf); } diff --git a/drivers/net/smc91111.c b/drivers/net/smc91111.c index 5ea1bc3259..1199b372b6 100644 --- a/drivers/net/smc91111.c +++ b/drivers/net/smc91111.c @@ -1392,7 +1392,7 @@ static int smc91c111_set_ethaddr(struct eth_device *edev, SMC_outw(priv, address, (ADDR0_REG + i)); } - return -1; + return 0; } #if (SMC_DEBUG > 2 ) diff --git a/drivers/spi/imx_spi.c b/drivers/spi/imx_spi.c index 9a3f05cdec..2e489674d8 100644 --- a/drivers/spi/imx_spi.c +++ b/drivers/spi/imx_spi.c @@ -380,6 +380,77 @@ static void imx_spi_do_transfer(struct spi_device *spi) } } +static int cspi_2_3_xchg_burst(struct spi_device *spi) +{ + struct imx_spi *imx = container_of(spi->master, struct imx_spi, master); + int now, txlen, rxlen; + u32 ctrl; + void __iomem *base = imx->regs; + + now = min(imx->xfer_len, 512); + now >>= 2; + + if (!now) + return 0; + + txlen = rxlen = now; + + ctrl = readl(base + CSPI_2_3_CTRL); + ctrl &= ~(0xfff << CSPI_2_3_CTRL_BL_OFFSET); + ctrl |= ((txlen * 32) - 1) << CSPI_2_3_CTRL_BL_OFFSET; + ctrl |= 1 << 3; + writel(ctrl, base + CSPI_2_3_CTRL); + + while (txlen || rxlen) { + u32 status = readl(base + CSPI_2_3_STAT); + + if (txlen && !(status & CSPI_2_3_STAT_TF)) { + if (imx->tx_buf) { + u32 data = swab32(*(u32 *)imx->tx_buf); + writel(data, base + CSPI_2_3_TXDATA); + imx->tx_buf += sizeof(u32); + } else { + writel(0, base + CSPI_2_3_TXDATA); + } + txlen--; + } + + if (rxlen && (status & CSPI_2_3_STAT_RR)) { + u32 data = readl(base + CSPI_2_3_RXDATA); + + if (imx->rx_buf) { + *(u32 *)imx->rx_buf = swab32(data); + imx->rx_buf += sizeof(u32); + } + + rxlen--; + } + } + + imx->xfer_len -= now * 4; + + return now; +} + +static void cspi_2_3_do_transfer(struct spi_device *spi) +{ + struct imx_spi *imx = container_of(spi->master, struct imx_spi, master); + u32 ctrl; + + if (imx->bits_per_word == 8 && !(spi->mode & SPI_LSB_FIRST)) + while (cspi_2_3_xchg_burst(spi) > 0); + + if (!imx->xfer_len) + return; + + ctrl = readl(imx->regs + CSPI_2_3_CTRL); + ctrl &= ~(0xfff << CSPI_2_3_CTRL_BL_OFFSET); + ctrl |= (spi->bits_per_word - 1) << CSPI_2_3_CTRL_BL_OFFSET; + writel(ctrl, imx->regs + CSPI_2_3_CTRL); + + imx_spi_do_transfer(spi); +} + static int imx_spi_transfer(struct spi_device *spi, struct spi_message *mesg) { struct imx_spi *imx = container_of(spi->master, struct imx_spi, master); @@ -437,7 +508,7 @@ static __maybe_unused struct spi_imx_devtype_data spi_imx_devtype_data_0_7 = { static __maybe_unused struct spi_imx_devtype_data spi_imx_devtype_data_2_3 = { .chipselect = cspi_2_3_chipselect, - .do_transfer = imx_spi_do_transfer, + .do_transfer = cspi_2_3_do_transfer, .xchg_single = cspi_2_3_xchg_single, }; diff --git a/drivers/video/backlight-pwm.c b/drivers/video/backlight-pwm.c index ba6f9bcb41..91435f8d0a 100644 --- a/drivers/video/backlight-pwm.c +++ b/drivers/video/backlight-pwm.c @@ -39,6 +39,7 @@ struct pwm_backlight { int enable_active_high; int max_value; int enabled; + unsigned int scale; }; static int backlight_pwm_enable(struct pwm_backlight *pwm_backlight) @@ -87,18 +88,26 @@ static int backlight_pwm_disable(struct pwm_backlight *pwm_backlight) return 0; } +static int compute_duty_cycle(struct pwm_backlight *pwm_backlight, int brightness) +{ + int duty_cycle; + + if (pwm_backlight->levels) + duty_cycle = pwm_backlight->levels[brightness]; + else + duty_cycle = brightness; + + return duty_cycle * pwm_backlight->period / pwm_backlight->scale; +} + static int backlight_pwm_set(struct backlight_device *backlight, int brightness) { struct pwm_backlight *pwm_backlight = container_of(backlight, struct pwm_backlight, backlight); - unsigned long long duty = pwm_backlight->period; - unsigned int max = pwm_backlight->backlight.brightness_max; - - duty *= brightness; - do_div(duty, max); - pwm_config(pwm_backlight->pwm, duty, pwm_backlight->period); + pwm_config(pwm_backlight->pwm, compute_duty_cycle(pwm_backlight, brightness), + pwm_backlight->period); if (brightness) return backlight_pwm_enable(pwm_backlight); @@ -113,7 +122,7 @@ static int pwm_backlight_parse_dt(struct device_d *dev, struct property *prop; int length; u32 value; - int ret; + int ret, i; enum of_gpio_flags flags; if (!node) @@ -141,6 +150,10 @@ static int pwm_backlight_parse_dt(struct device_d *dev, if (ret < 0) return ret; + for (i = 0; i <= pwm_backlight->backlight.brightness_max; i++) + if (pwm_backlight->levels[i] > pwm_backlight->scale) + pwm_backlight->scale = pwm_backlight->levels[i]; + ret = of_property_read_u32(node, "default-brightness-level", &value); if (ret < 0) diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c index c38d13c304..693c21f547 100644 --- a/drivers/video/fbconsole.c +++ b/drivers/video/fbconsole.c @@ -22,8 +22,8 @@ struct fbc_priv { struct param_d *par_font; int par_font_val; - int font_width, font_height; - const u8 *fontdata; + const struct font_desc *font; + unsigned int cols, rows; unsigned int x, y; /* cursor position */ @@ -84,7 +84,7 @@ static struct rgb colors[] = { { 255, 255, 255 }, }; -static void drawchar(struct fbc_priv *priv, int x, int y, char c) +static void drawchar(struct fbc_priv *priv, int x, int y, int c) { void *buf; int bpp = priv->fb->bits_per_pixel >> 3; @@ -97,7 +97,8 @@ static void drawchar(struct fbc_priv *priv, int x, int y, char c) buf = gui_screen_render_buffer(priv->sc); - inbuf = &priv->fontdata[c * priv->font_height]; + i = find_font_index(priv->font, c); + inbuf = priv->font->data + i; line_length = priv->fb->line_length; @@ -113,13 +114,13 @@ static void drawchar(struct fbc_priv *priv, int x, int y, char c) rgb = &colors[bgcolor]; bgcolor = gu_rgb_to_pixel(priv->fb, rgb->r, rgb->g, rgb->b, 0xff); - for (i = 0; i < priv->font_height; i++) { + for (i = 0; i < priv->font->height; i++) { uint8_t t = inbuf[i]; int j; - adr = buf + line_length * (y * priv->font_height + i) + x * priv->font_width * bpp; + adr = buf + line_length * (y * priv->font->height + i) + x * priv->font->width * bpp; - for (j = 0; j < priv->font_width; j++) { + for (j = 0; j < priv->font->width; j++) { if (t & 0x80) gu_set_pixel(priv->fb, adr, color); else @@ -137,10 +138,10 @@ static void video_invertchar(struct fbc_priv *priv, int x, int y) buf = gui_screen_render_buffer(priv->sc); - gu_invert_area(priv->fb, buf, x * priv->font_width, y * priv->font_height, - priv->font_width, priv->font_height); - gu_screen_blit_area(priv->sc, x * priv->font_width, y * priv->font_height, - priv->font_width, priv->font_height); + gu_invert_area(priv->fb, buf, x * priv->font->width, y * priv->font->height, + priv->font->width, priv->font->height); + gu_screen_blit_area(priv->sc, x * priv->font->width, y * priv->font->height, + priv->font->width, priv->font->height); } static void printchar(struct fbc_priv *priv, int c) @@ -174,9 +175,9 @@ static void printchar(struct fbc_priv *priv, int c) default: drawchar(priv, priv->x, priv->y, c); - gu_screen_blit_area(priv->sc, priv->x * priv->font_width, - priv->y * priv->font_height, - priv->font_width, priv->font_height); + gu_screen_blit_area(priv->sc, priv->x * priv->font->width, + priv->y * priv->font->height, + priv->font->width, priv->font->height); priv->x++; if (priv->x > priv->cols) { @@ -188,7 +189,7 @@ static void printchar(struct fbc_priv *priv, int c) if (priv->y > priv->rows) { void *buf; u32 line_length = priv->fb->line_length; - int line_height = line_length * priv->font_height; + int line_height = line_length * priv->font->height; buf = gui_screen_render_buffer(priv->sc); @@ -356,12 +357,10 @@ static int setup_font(struct fbc_priv *priv) return -ENOENT; } - priv->font_width = font->width; - priv->font_height = font->height; - priv->fontdata = font->data; + priv->font = font; - priv->rows = fb->yres / priv->font_height - 1; - priv->cols = fb->xres / priv->font_width - 1; + priv->rows = fb->yres / priv->font->height - 1; + priv->cols = fb->xres / priv->font->width - 1; return 0; } diff --git a/drivers/video/imx-ipu-v3/imx-ldb.c b/drivers/video/imx-ipu-v3/imx-ldb.c index a41eb1a3fc..2340b9b1d6 100644 --- a/drivers/video/imx-ipu-v3/imx-ldb.c +++ b/drivers/video/imx-ipu-v3/imx-ldb.c @@ -68,7 +68,7 @@ struct imx_ldb_channel { struct imx_ldb_data { void __iomem *base; - int (*prepare)(struct imx_ldb_channel *imx_ldb_ch, int di); + int (*prepare)(struct imx_ldb_channel *imx_ldb_ch, int di, unsigned long clkrate); unsigned ipu_mask; int have_mux; }; @@ -114,7 +114,7 @@ static int imx_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, struct fb_videomo { struct imx_ldb *ldb = imx_ldb_ch->ldb; - ldb->soc_data->prepare(imx_ldb_ch, di); + ldb->soc_data->prepare(imx_ldb_ch, di, PICOS2KHZ(mode->pixclock) * 1000UL); /* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */ if (imx_ldb_ch == &ldb->channel[0]) { @@ -146,7 +146,8 @@ static int imx_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, struct fb_videomo return 0; } -static int imx6q_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di) +static int imx6q_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di, + unsigned long pixclk) { struct clk *diclk, *ldbclk; struct imx_ldb *ldb = imx_ldb_ch->ldb; @@ -155,6 +156,7 @@ static int imx6q_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di) void __iomem *gpr3 = (void *)MX6_IOMUXC_BASE_ADDR + 0xc; uint32_t val; int shift; + int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; ipuno = ((di >> 1) & 1) + 1; dino = di & 0x1; @@ -181,6 +183,11 @@ static int imx6q_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di) return ret; } + if (!dual) + pixclk *= 2; + + clk_set_rate(clk_get_parent(ldbclk), pixclk); + val = readl(gpr3); shift = (imx_ldb_ch->chno == 0) ? 6 : 8; val &= ~(3 << shift); @@ -190,12 +197,14 @@ static int imx6q_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di) return 0; } -static int imx53_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di) +static int imx53_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di, + unsigned long pixclk) { struct clk *diclk, *ldbclk; struct imx_ldb *ldb = imx_ldb_ch->ldb; int ret, dino; char *clkname; + int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; dino = di & 0x1; @@ -221,6 +230,11 @@ static int imx53_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di) return ret; } + if (!dual) + pixclk *= 2; + + clk_set_rate(clk_get_parent(ldbclk), pixclk); + return 0; } @@ -299,6 +313,10 @@ static int imx_ldb_probe(struct device_d *dev) imx_ldb->base = devtype->base; imx_ldb->soc_data = devtype; + dual = of_property_read_bool(np, "fsl,dual-channel"); + if (dual) + imx_ldb->ldb_ctrl |= LDB_SPLIT_MODE_EN; + for_each_child_of_node(np, child) { struct imx_ldb_channel *channel; struct device_node *port; diff --git a/drivers/watchdog/imxwd.c b/drivers/watchdog/imxwd.c index dd11a62613..1952548fa7 100644 --- a/drivers/watchdog/imxwd.c +++ b/drivers/watchdog/imxwd.c @@ -97,9 +97,12 @@ static int imx21_watchdog_set_timeout(struct imx_wd *priv, int timeout) dev_dbg(priv->dev, "%s: %d\n", __func__, timeout); - if (!timeout || timeout > 128) + if (timeout < -1 || timeout > 128) return -EINVAL; + if (timeout == 0) /* bit 2 (WDE) cannot be set to 0 again */ + return -ENOSYS; + if (timeout > 0) val = ((timeout * 2 - 1) << 8) | IMX21_WDOG_WCR_SRS | IMX21_WDOG_WCR_WDA; |