mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH v2] i2c: Add support for DesignWare controllers
@ 2015-11-09  0:18 Andrey Smirnov
  2015-11-09  0:18 ` [PATCH] i2c-imx: Use xzalloc instead of kzalloc Andrey Smirnov
  2015-11-09  6:20 ` [PATCH v2] i2c: Add support for DesignWare controllers Sascha Hauer
  0 siblings, 2 replies; 6+ messages in thread
From: Andrey Smirnov @ 2015-11-09  0:18 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov

Add a driver for DesignWare I2C controller IP block found on several
SoCs including Altera SoC products

Tested using Terrasic SoCKit board and GPIO expander board with I2C
EEPROM on it

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---

New in version 2:

    - kzalloc() replaced with xzalloc()
    - removed redundant logging


 drivers/i2c/busses/Kconfig          |   6 +
 drivers/i2c/busses/Makefile         |   1 +
 drivers/i2c/busses/i2c-designware.c | 574 ++++++++++++++++++++++++++++++++++++
 3 files changed, 581 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-designware.c

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 @@ 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 1dbfbdf..8dccc38 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 0000000..1b3462b
--- /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), GFP_KERNEL);
+
+	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);
--
2.1.4

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH] i2c-imx: Use xzalloc instead of kzalloc
  2015-11-09  0:18 [PATCH v2] i2c: Add support for DesignWare controllers Andrey Smirnov
@ 2015-11-09  0:18 ` Andrey Smirnov
  2015-11-09  6:20 ` [PATCH v2] i2c: Add support for DesignWare controllers Sascha Hauer
  1 sibling, 0 replies; 6+ messages in thread
From: Andrey Smirnov @ 2015-11-09  0:18 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov

Driver's private data structure is allocated with kzalloc without any
code to check for kzalloc's possible failure. Fix that by replaceing
kzalloc with xzalloc.

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 drivers/i2c/busses/i2c-imx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 7df4a26..47a50db 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(struct fsl_i2c_struct), GFP_KERNEL);

 #ifdef CONFIG_COMMON_CLK
 	i2c_fsl->clk = clk_get(pdev, NULL);
--
2.1.4

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2] i2c: Add support for DesignWare controllers
  2015-11-09  0:18 [PATCH v2] i2c: Add support for DesignWare controllers Andrey Smirnov
  2015-11-09  0:18 ` [PATCH] i2c-imx: Use xzalloc instead of kzalloc Andrey Smirnov
@ 2015-11-09  6:20 ` Sascha Hauer
  2015-11-19  1:26   ` Trent Piepho
  1 sibling, 1 reply; 6+ messages in thread
From: Sascha Hauer @ 2015-11-09  6:20 UTC (permalink / raw)
  To: Andrey Smirnov; +Cc: barebox

On Sun, Nov 08, 2015 at 04:18:46PM -0800, Andrey Smirnov wrote:
> Add a driver for DesignWare I2C controller IP block found on several
> SoCs including Altera SoC products
> 
> Tested using Terrasic SoCKit board and GPIO expander board with I2C
> EEPROM on it
> 
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>

Applied, thanks

Sascha

> ---
> 
> New in version 2:
> 
>     - kzalloc() replaced with xzalloc()
>     - removed redundant logging
> 
> 
>  drivers/i2c/busses/Kconfig          |   6 +
>  drivers/i2c/busses/Makefile         |   1 +
>  drivers/i2c/busses/i2c-designware.c | 574 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 581 insertions(+)
>  create mode 100644 drivers/i2c/busses/i2c-designware.c
> 
> 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 @@ 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 1dbfbdf..8dccc38 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 0000000..1b3462b
> --- /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), GFP_KERNEL);
> +
> +	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);
> --
> 2.1.4
> 
> _______________________________________________
> barebox mailing list
> barebox@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2] i2c: Add support for DesignWare controllers
  2015-11-09  6:20 ` [PATCH v2] i2c: Add support for DesignWare controllers Sascha Hauer
@ 2015-11-19  1:26   ` Trent Piepho
  2015-11-19  4:09     ` Andrey Smirnov
  0 siblings, 1 reply; 6+ messages in thread
From: Trent Piepho @ 2015-11-19  1:26 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: Andrey Smirnov, barebox

On Mon, 2015-11-09 at 07:20 +0100, Sascha Hauer wrote:
> On Sun, Nov 08, 2015 at 04:18:46PM -0800, Andrey Smirnov wrote:
> > Add a driver for DesignWare I2C controller IP block found on several
> > SoCs including Altera SoC products
> > 
> > Tested using Terrasic SoCKit board and GPIO expander board with I2C
> > EEPROM on it
> > 
> > Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>

> > +	uint32_t ic_con, ic_comp_type_value;
> > +
> > +	pdata = pdev->platform_data;
> > +
> > +	dw = xzalloc(sizeof(*dw), GFP_KERNEL);
> > +

Doesn't compile, xzalloc() has one argument, no page flags.
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2] i2c: Add support for DesignWare controllers
  2015-11-19  1:26   ` Trent Piepho
@ 2015-11-19  4:09     ` Andrey Smirnov
  2015-11-19  7:59       ` Sascha Hauer
  0 siblings, 1 reply; 6+ messages in thread
From: Andrey Smirnov @ 2015-11-19  4:09 UTC (permalink / raw)
  To: Trent Piepho; +Cc: barebox

On Wed, Nov 18, 2015 at 5:26 PM, Trent Piepho <tpiepho@kymetacorp.com> wrote:
> On Mon, 2015-11-09 at 07:20 +0100, Sascha Hauer wrote:
>> On Sun, Nov 08, 2015 at 04:18:46PM -0800, Andrey Smirnov wrote:
>> > Add a driver for DesignWare I2C controller IP block found on several
>> > SoCs including Altera SoC products
>> >
>> > Tested using Terrasic SoCKit board and GPIO expander board with I2C
>> > EEPROM on it
>> >
>> > Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
>
>> > +   uint32_t ic_con, ic_comp_type_value;
>> > +
>> > +   pdata = pdev->platform_data;
>> > +
>> > +   dw = xzalloc(sizeof(*dw), GFP_KERNEL);
>> > +
>
> Doesn't compile, xzalloc() has one argument, no page flags.

*Sigh* Looks like I didn't do a good job testing v2 patchset, my bad,
sorry. I'll submit a patch to fix this, unless either of you guys
already did so.

Once again, I apologize for making such an obvious mistake.

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2] i2c: Add support for DesignWare controllers
  2015-11-19  4:09     ` Andrey Smirnov
@ 2015-11-19  7:59       ` Sascha Hauer
  0 siblings, 0 replies; 6+ messages in thread
From: Sascha Hauer @ 2015-11-19  7:59 UTC (permalink / raw)
  To: Andrey Smirnov; +Cc: barebox, Trent Piepho

On Wed, Nov 18, 2015 at 08:09:05PM -0800, Andrey Smirnov wrote:
> On Wed, Nov 18, 2015 at 5:26 PM, Trent Piepho <tpiepho@kymetacorp.com> wrote:
> > On Mon, 2015-11-09 at 07:20 +0100, Sascha Hauer wrote:
> >> On Sun, Nov 08, 2015 at 04:18:46PM -0800, Andrey Smirnov wrote:
> >> > Add a driver for DesignWare I2C controller IP block found on several
> >> > SoCs including Altera SoC products
> >> >
> >> > Tested using Terrasic SoCKit board and GPIO expander board with I2C
> >> > EEPROM on it
> >> >
> >> > Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> >
> >> > +   uint32_t ic_con, ic_comp_type_value;
> >> > +
> >> > +   pdata = pdev->platform_data;
> >> > +
> >> > +   dw = xzalloc(sizeof(*dw), GFP_KERNEL);
> >> > +
> >
> > Doesn't compile, xzalloc() has one argument, no page flags.
> 
> *Sigh* Looks like I didn't do a good job testing v2 patchset, my bad,
> sorry. I'll submit a patch to fix this, unless either of you guys
> already did so.

Just did that, everything fine now.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2015-11-19  7:59 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-09  0:18 [PATCH v2] i2c: Add support for DesignWare controllers Andrey Smirnov
2015-11-09  0:18 ` [PATCH] i2c-imx: Use xzalloc instead of kzalloc Andrey Smirnov
2015-11-09  6:20 ` [PATCH v2] i2c: Add support for DesignWare controllers Sascha Hauer
2015-11-19  1:26   ` Trent Piepho
2015-11-19  4:09     ` Andrey Smirnov
2015-11-19  7:59       ` Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox