summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/base/bus.c5
-rw-r--r--drivers/eeprom/at24.c26
-rw-r--r--drivers/i2c/Kconfig9
-rw-r--r--drivers/i2c/Makefile3
-rw-r--r--drivers/i2c/busses/Kconfig6
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-designware.c574
-rw-r--r--drivers/i2c/busses/i2c-imx.c2
-rw-r--r--drivers/i2c/i2c-mux.c148
-rw-r--r--drivers/i2c/i2c.c17
-rw-r--r--drivers/i2c/muxes/Kconfig14
-rw-r--r--drivers/i2c/muxes/Makefile4
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c251
-rw-r--r--drivers/mtd/core.c2
-rw-r--r--drivers/net/smc91111.c2
-rw-r--r--drivers/usb/imx/imx-usb-misc.c2
-rw-r--r--drivers/video/backlight-pwm.c27
-rw-r--r--drivers/video/fbconsole.c39
18 files changed, 1086 insertions, 46 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/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", &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/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/usb/imx/imx-usb-misc.c b/drivers/usb/imx/imx-usb-misc.c
index be95b89aff..af1a32110c 100644
--- a/drivers/usb/imx/imx-usb-misc.c
+++ b/drivers/usb/imx/imx-usb-misc.c
@@ -31,7 +31,7 @@
#define MX25_H1_SIC_SHIFT 21
#define MX25_H1_SIC_MASK (0x3 << MX25_H1_SIC_SHIFT)
#define MX25_H1_PP_BIT (1 << 18)
-#define MX25_H1_PM_BIT (1 << 8)
+#define MX25_H1_PM_BIT (1 << 16)
#define MX25_H1_IPPUE_UP_BIT (1 << 7)
#define MX25_H1_IPPUE_DOWN_BIT (1 << 6)
#define MX25_H1_TLL_BIT (1 << 5)
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;
}