|
@@ -0,0 +1,1102 @@
|
|
|
|
+#include <linux/module.h>
|
|
|
|
+#include <linux/kernel.h>
|
|
|
|
+#include <linux/err.h>
|
|
|
|
+#include <linux/slab.h>
|
|
|
|
+#include <linux/stat.h>
|
|
|
|
+#include <linux/delay.h>
|
|
|
|
+#include <linux/i2c.h>
|
|
|
|
+#include <linux/init.h>
|
|
|
|
+#include <linux/interrupt.h>
|
|
|
|
+#include <linux/io.h>
|
|
|
|
+#include <linux/platform_device.h>
|
|
|
|
+#ifdef CONFIG_PM_RUNTIME
|
|
|
|
+#include <linux/pm_runtime.h>
|
|
|
|
+#endif
|
|
|
|
+#include <linux/clk.h>
|
|
|
|
+#include <linux/reset.h>
|
|
|
|
+
|
|
|
|
+#define DRIVER_NAME "ark_i2c"
|
|
|
|
+#define VERSION "Version 0.1"
|
|
|
|
+
|
|
|
|
+//#define CONFIG_I2C_ARK_DBG
|
|
|
|
+#ifdef CONFIG_I2C_ARK_DBG
|
|
|
|
+#define I2C_DBG(d, f, a...) \
|
|
|
|
+ printk(KERN_INFO "I2CDBG: [%s:%d] "f, __FUNCTION__, __LINE__, ##a)
|
|
|
|
+#else /* !CONFIG_I2C_ARK_DBG */
|
|
|
|
+#define I2C_DBG(...)
|
|
|
|
+#endif /* CONFIG_I2C_ARK_DBG */
|
|
|
|
+
|
|
|
|
+enum i2c_speed {
|
|
|
|
+ I2C_STANDARD = 0,
|
|
|
|
+ I2C_FAST,
|
|
|
|
+ I2C_HIGH,
|
|
|
|
+ NUM_SPEEDS
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+enum ark_i2c_status {
|
|
|
|
+ STATUS_IDLE = 0,
|
|
|
|
+ STATUS_READ_START,
|
|
|
|
+ STATUS_READ_IN_PROGRESS,
|
|
|
|
+ STATUS_READ_SUCCESS,
|
|
|
|
+ STATUS_WRITE_START,
|
|
|
|
+ STATUS_WRITE_SUCCESS,
|
|
|
|
+ STATUS_WRITE_IN_PROGRESS,
|
|
|
|
+ STATUS_XFER_ABORT,
|
|
|
|
+ STATUS_STANDBY
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/* Platform data for arkmicro i2c controller */
|
|
|
|
+struct ark_i2c_platdata {
|
|
|
|
+ unsigned int speed_mode;
|
|
|
|
+ unsigned int hs_master_code;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+struct i2c_xfer_info {
|
|
|
|
+ int rw; //0:write 1:read
|
|
|
|
+ u8 *buf;
|
|
|
|
+ int send_left;
|
|
|
|
+ int rev_left;
|
|
|
|
+ int rx_thld;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+struct ark_i2c_private {
|
|
|
|
+ struct i2c_adapter adap;
|
|
|
|
+ struct device *dev;
|
|
|
|
+ void __iomem *base;
|
|
|
|
+ int irq;
|
|
|
|
+ int speed;
|
|
|
|
+ int hs_mcode;
|
|
|
|
+ struct completion complete;
|
|
|
|
+ int abort;
|
|
|
|
+ u8 *rx_buf;
|
|
|
|
+ int rx_buf_len;
|
|
|
|
+ volatile enum ark_i2c_status status;
|
|
|
|
+ struct i2c_msg *msg;
|
|
|
|
+ struct mutex lock;
|
|
|
|
+ struct clk *clk;
|
|
|
|
+ struct i2c_xfer_info xfer_info;
|
|
|
|
+ struct reset_control *rst;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/* =========================================================================
|
|
|
|
+ * Registers definition
|
|
|
|
+ * =========================================================================
|
|
|
|
+ */
|
|
|
|
+/* Control register */
|
|
|
|
+#define IC_CON 0x00
|
|
|
|
+#define SLV_DIS (1 << 6) /* Disable slave mode */
|
|
|
|
+#define RESTART (1 << 5) /* Send a Restart condition */
|
|
|
|
+#define ADDR_10BIT (1 << 4) /* 10-bit addressing */
|
|
|
|
+#define STANDARD_MODE (1 << 1) /* standard mode */
|
|
|
|
+#define FAST_MODE (2 << 1) /* fast mode */
|
|
|
|
+#define HIGH_MODE (3 << 1) /* high speed mode */
|
|
|
|
+#define MASTER_EN (1 << 0) /* Master mode */
|
|
|
|
+
|
|
|
|
+/* Target address register */
|
|
|
|
+#define IC_TAR 0x04
|
|
|
|
+#define IC_TAR_10BIT_ADDR (1 << 12) /* 10-bit addressing */
|
|
|
|
+#define IC_TAR_SPECIAL (1 << 11) /* Perform special I2C cmd */
|
|
|
|
+#define IC_TAR_GC_OR_START (1 << 10) /* 0: Gerneral Call Address */
|
|
|
|
+ /* 1: START BYTE */
|
|
|
|
+/* Slave Address Register */
|
|
|
|
+#define IC_SAR 0x08 /* Not used in Master mode */
|
|
|
|
+
|
|
|
|
+/* High Speed Master Mode Code Address Register */
|
|
|
|
+#define IC_HS_MADDR 0x0c
|
|
|
|
+#define IC_HS_MAR 0xFF
|
|
|
|
+
|
|
|
|
+/* Rx/Tx Data Buffer and Command Register */
|
|
|
|
+#define IC_DATA_CMD 0x10
|
|
|
|
+#define IC_RD (1 << 8) /* 1: Read 0: Write */
|
|
|
|
+
|
|
|
|
+/* Standard Speed Clock SCL High Count Register */
|
|
|
|
+#define IC_SS_SCL_HCNT 0x14
|
|
|
|
+
|
|
|
|
+/* Standard Speed Clock SCL Low Count Register */
|
|
|
|
+#define IC_SS_SCL_LCNT 0x18
|
|
|
|
+
|
|
|
|
+/* Fast Speed Clock SCL High Count Register */
|
|
|
|
+#define IC_FS_SCL_HCNT 0x1c
|
|
|
|
+
|
|
|
|
+/* Fast Spedd Clock SCL Low Count Register */
|
|
|
|
+#define IC_FS_SCL_LCNT 0x20
|
|
|
|
+
|
|
|
|
+/* High Speed Clock SCL High Count Register */
|
|
|
|
+#define IC_HS_SCL_HCNT 0x24
|
|
|
|
+
|
|
|
|
+/* High Speed Clock SCL Low Count Register */
|
|
|
|
+#define IC_HS_SCL_LCNT 0x28
|
|
|
|
+
|
|
|
|
+/* Interrupt Status Register */
|
|
|
|
+#define IC_INTR_STAT 0x2c /* Read only */
|
|
|
|
+#define R_GEN_CALL (1 << 11)
|
|
|
|
+#define R_START_DET (1 << 10)
|
|
|
|
+#define R_STOP_DET (1 << 9)
|
|
|
|
+#define R_ACTIVITY (1 << 8)
|
|
|
|
+#define R_RX_DONE (1 << 7)
|
|
|
|
+#define R_TX_ABRT (1 << 6)
|
|
|
|
+#define R_RD_REQ (1 << 5)
|
|
|
|
+#define R_TX_EMPTY (1 << 4)
|
|
|
|
+#define R_TX_OVER (1 << 3)
|
|
|
|
+#define R_RX_FULL (1 << 2)
|
|
|
|
+#define R_RX_OVER (1 << 1)
|
|
|
|
+#define R_RX_UNDER (1 << 0)
|
|
|
|
+
|
|
|
|
+/* Interrupt Mask Register */
|
|
|
|
+#define IC_INTR_MASK 0x30 /* Read and Write */
|
|
|
|
+#define M_GEN_CALL (1 << 11)
|
|
|
|
+#define M_START_DET (1 << 10)
|
|
|
|
+#define M_STOP_DET (1 << 9)
|
|
|
|
+#define M_ACTIVITY (1 << 8)
|
|
|
|
+#define M_RX_DONE (1 << 7)
|
|
|
|
+#define M_TX_ABRT (1 << 6)
|
|
|
|
+#define M_RD_REQ (1 << 5)
|
|
|
|
+#define M_TX_EMPTY (1 << 4)
|
|
|
|
+#define M_TX_OVER (1 << 3)
|
|
|
|
+#define M_RX_FULL (1 << 2)
|
|
|
|
+#define M_RX_OVER (1 << 1)
|
|
|
|
+#define M_RX_UNDER (1 << 0)
|
|
|
|
+
|
|
|
|
+/* Raw Interrupt Status Register */
|
|
|
|
+#define IC_RAW_INTR_STAT 0x34 /* Read Only */
|
|
|
|
+#define GEN_CALL (1 << 11) /* General call */
|
|
|
|
+#define START_DET (1 << 10) /* (RE)START occurred */
|
|
|
|
+#define STOP_DET (1 << 9) /* STOP occurred */
|
|
|
|
+#define ACTIVITY (1 << 8) /* Bus busy */
|
|
|
|
+#define RX_DONE (1 << 7) /* Not used in Master mode */
|
|
|
|
+#define TX_ABRT (1 << 6) /* Transmit Abort */
|
|
|
|
+#define RD_REQ (1 << 5) /* Not used in Master mode */
|
|
|
|
+#define TX_EMPTY (1 << 4) /* TX FIFO <= threshold */
|
|
|
|
+#define TX_OVER (1 << 3) /* TX FIFO overflow */
|
|
|
|
+#define RX_FULL (1 << 2) /* RX FIFO >= threshold */
|
|
|
|
+#define RX_OVER (1 << 1) /* RX FIFO overflow */
|
|
|
|
+#define RX_UNDER (1 << 0) /* RX FIFO empty */
|
|
|
|
+
|
|
|
|
+/* Receive FIFO Threshold Register */
|
|
|
|
+#define IC_RX_TL 0x38
|
|
|
|
+
|
|
|
|
+/* Transmit FIFO Treshold Register */
|
|
|
|
+#define IC_TX_TL 0x3c
|
|
|
|
+
|
|
|
|
+/* Clear Combined and Individual Interrupt Register */
|
|
|
|
+#define IC_CLR_INTR 0x40
|
|
|
|
+#define CLR_INTR (1 << 0)
|
|
|
|
+
|
|
|
|
+/* Clear RX_UNDER Interrupt Register */
|
|
|
|
+#define IC_CLR_RX_UNDER 0x44
|
|
|
|
+#define CLR_RX_UNDER (1 << 0)
|
|
|
|
+
|
|
|
|
+/* Clear RX_OVER Interrupt Register */
|
|
|
|
+#define IC_CLR_RX_OVER 0x48
|
|
|
|
+#define CLR_RX_OVER (1 << 0)
|
|
|
|
+
|
|
|
|
+/* Clear TX_OVER Interrupt Register */
|
|
|
|
+#define IC_CLR_TX_OVER 0x4c
|
|
|
|
+#define CLR_TX_OVER (1 << 0)
|
|
|
|
+
|
|
|
|
+#define IC_CLR_RD_REQ 0x50
|
|
|
|
+
|
|
|
|
+/* Clear TX_ABRT Interrupt Register */
|
|
|
|
+#define IC_CLR_TX_ABRT 0x54
|
|
|
|
+#define CLR_TX_ABRT (1 << 0)
|
|
|
|
+#define IC_CLR_RX_DONE 0x58
|
|
|
|
+
|
|
|
|
+/* Clear ACTIVITY Interrupt Register */
|
|
|
|
+#define IC_CLR_ACTIVITY 0x5c
|
|
|
|
+#define CLR_ACTIVITY (1 << 0)
|
|
|
|
+
|
|
|
|
+/* Clear STOP_DET Interrupt Register */
|
|
|
|
+#define IC_CLR_STOP_DET 0x60
|
|
|
|
+#define CLR_STOP_DET (1 << 0)
|
|
|
|
+
|
|
|
|
+/* Clear START_DET Interrupt Register */
|
|
|
|
+#define IC_CLR_START_DET 0x64
|
|
|
|
+#define CLR_START_DET (1 << 0)
|
|
|
|
+
|
|
|
|
+/* Clear GEN_CALL Interrupt Register */
|
|
|
|
+#define IC_CLR_GEN_CALL 0x68
|
|
|
|
+#define CLR_GEN_CALL (1 << 0)
|
|
|
|
+
|
|
|
|
+/* Enable Register */
|
|
|
|
+#define IC_ENABLE 0x6c
|
|
|
|
+#define ENABLE (1 << 0)
|
|
|
|
+
|
|
|
|
+/* Status Register */
|
|
|
|
+#define IC_STATUS 0x70 /* Read Only */
|
|
|
|
+#define STAT_SLV_ACTIVITY (1 << 6) /* Slave not in idle */
|
|
|
|
+#define STAT_MST_ACTIVITY (1 << 5) /* Master not in idle */
|
|
|
|
+#define STAT_RFF (1 << 4) /* RX FIFO Full */
|
|
|
|
+#define STAT_RFNE (1 << 3) /* RX FIFO Not Empty */
|
|
|
|
+#define STAT_TFE (1 << 2) /* TX FIFO Empty */
|
|
|
|
+#define STAT_TFNF (1 << 1) /* TX FIFO Not Full */
|
|
|
|
+#define STAT_ACTIVITY (1 << 0) /* Activity Status */
|
|
|
|
+
|
|
|
|
+/* Transmit FIFO Level Register */
|
|
|
|
+#define IC_TXFLR 0x74 /* Read Only */
|
|
|
|
+#define TXFLR (1 << 0) /* TX FIFO level */
|
|
|
|
+
|
|
|
|
+/* Receive FIFO Level Register */
|
|
|
|
+#define IC_RXFLR 0x78 /* Read Only */
|
|
|
|
+#define RXFLR (1 << 0) /* RX FIFO level */
|
|
|
|
+
|
|
|
|
+/* Transmit Abort Source Register */
|
|
|
|
+#define IC_TX_ABRT_SOURCE 0x80
|
|
|
|
+#define ABRT_SLVRD_INTX (1 << 15)
|
|
|
|
+#define ABRT_SLV_ARBLOST (1 << 14)
|
|
|
|
+#define ABRT_SLVFLUSH_TXFIFO (1 << 13)
|
|
|
|
+#define ARB_LOST (1 << 12)
|
|
|
|
+#define ABRT_MASTER_DIS (1 << 11)
|
|
|
|
+#define ABRT_10B_RD_NORSTRT (1 << 10)
|
|
|
|
+#define ABRT_SBYTE_NORSTRT (1 << 9)
|
|
|
|
+#define ABRT_HS_NORSTRT (1 << 8)
|
|
|
|
+#define ABRT_SBYTE_ACKDET (1 << 7)
|
|
|
|
+#define ABRT_HS_ACKDET (1 << 6)
|
|
|
|
+#define ABRT_GCALL_READ (1 << 5)
|
|
|
|
+#define ABRT_GCALL_NOACK (1 << 4)
|
|
|
|
+#define ABRT_TXDATA_NOACK (1 << 3)
|
|
|
|
+#define ABRT_10ADDR2_NOACK (1 << 2)
|
|
|
|
+#define ABRT_10ADDR1_NOACK (1 << 1)
|
|
|
|
+#define ABRT_7B_ADDR_NOACK (1 << 0)
|
|
|
|
+
|
|
|
|
+/* Enable Status Register */
|
|
|
|
+#define IC_ENABLE_STATUS 0x9c
|
|
|
|
+#define IC_EN (1 << 0) /* I2C in an enabled state */
|
|
|
|
+
|
|
|
|
+/* Component Parameter Register 1*/
|
|
|
|
+#define IC_COMP_PARAM_1 0xf4
|
|
|
|
+#define APB_DATA_WIDTH (0x3 << 0)
|
|
|
|
+
|
|
|
|
+/* Minimum High/Low Period
|
|
|
|
+ * IC_xCNT = ROUNDUP(MIN_SCL_xxx(ns) * IC_CLK(MHz))
|
|
|
|
+ */
|
|
|
|
+#define SS_MIN_SCL_HIGH 4000
|
|
|
|
+#define SS_MIN_SCL_LOW 4700
|
|
|
|
+#define FS_MIN_SCL_HIGH 600
|
|
|
|
+#define FS_MIN_SCL_LOW 1300
|
|
|
|
+#define HS_MIN_SCL_HIGH_100PF 60
|
|
|
|
+#define HS_MIN_SCL_LOW_100PF 120
|
|
|
|
+
|
|
|
|
+#define I2C_FIFO_DEPTH 8
|
|
|
|
+
|
|
|
|
+/* =========================================================================
|
|
|
|
+ * Functions
|
|
|
|
+ * =========================================================================
|
|
|
|
+ */
|
|
|
|
+static int ark_i2c_disable(struct i2c_adapter *adap)
|
|
|
|
+{
|
|
|
|
+ struct ark_i2c_private *i2c = i2c_get_adapdata(adap);
|
|
|
|
+ int err = 0;
|
|
|
|
+ int count = 0;
|
|
|
|
+ int ret1, ret2;
|
|
|
|
+ static const u16 delay[NUM_SPEEDS] = {100, 25, 3};
|
|
|
|
+
|
|
|
|
+ /* Set IC_ENABLE to 0 */
|
|
|
|
+ writel(0, i2c->base + IC_ENABLE);
|
|
|
|
+
|
|
|
|
+ /* Check if device is busy */
|
|
|
|
+ I2C_DBG(&adap->dev, "i2c disable\n");
|
|
|
|
+ while ((ret1 = readl(i2c->base + IC_ENABLE_STATUS) & 0x1)
|
|
|
|
+ || (ret2 = readl(i2c->base + IC_STATUS) & 0x1)) {
|
|
|
|
+ udelay(delay[i2c->speed]);
|
|
|
|
+ writel(0, i2c->base + IC_ENABLE);
|
|
|
|
+ I2C_DBG(&adap->dev, "i2c is busy, count is %d speed %d\n",
|
|
|
|
+ count, i2c->speed);
|
|
|
|
+ if (count++ > 10) {
|
|
|
|
+ err = -ETIMEDOUT;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Clear all interrupts */
|
|
|
|
+ readl(i2c->base + IC_CLR_INTR);
|
|
|
|
+ readl(i2c->base + IC_CLR_STOP_DET);
|
|
|
|
+ readl(i2c->base + IC_CLR_START_DET);
|
|
|
|
+ readl(i2c->base + IC_CLR_ACTIVITY);
|
|
|
|
+ readl(i2c->base + IC_CLR_TX_ABRT);
|
|
|
|
+ readl(i2c->base + IC_CLR_RX_OVER);
|
|
|
|
+ readl(i2c->base + IC_CLR_RX_UNDER);
|
|
|
|
+ readl(i2c->base + IC_CLR_TX_OVER);
|
|
|
|
+ readl(i2c->base + IC_CLR_RX_DONE);
|
|
|
|
+ readl(i2c->base + IC_CLR_GEN_CALL);
|
|
|
|
+
|
|
|
|
+ /* Disable all interupts */
|
|
|
|
+ writel(0x0000, i2c->base + IC_INTR_MASK);
|
|
|
|
+
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const unsigned short _hcnt[NUM_SPEEDS] = {
|
|
|
|
+ 500, 120, 14
|
|
|
|
+};
|
|
|
|
+static const unsigned short _lcnt[NUM_SPEEDS] = {
|
|
|
|
+ 570, 150, 18
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int ark_i2c_hwinit(struct ark_i2c_private *i2c)
|
|
|
|
+{
|
|
|
|
+ int err;
|
|
|
|
+ unsigned int hcnt, lcnt;
|
|
|
|
+ unsigned long i2c_clk = clk_get_rate(i2c->clk) / 1000000;
|
|
|
|
+
|
|
|
|
+ /* ASIC IC_CLK, 24MHz
|
|
|
|
+ * FPGA IC_CLK, system clock
|
|
|
|
+ */
|
|
|
|
+ hcnt = (_hcnt[i2c->speed] * i2c_clk) / 100;
|
|
|
|
+ lcnt = (_lcnt[i2c->speed] * i2c_clk) / 100;
|
|
|
|
+
|
|
|
|
+ //printk("i2c clk %lu hcnt %u lcnt %u\n", i2c_clk, hcnt, lcnt);
|
|
|
|
+
|
|
|
|
+ /* Disable i2c first */
|
|
|
|
+ err = ark_i2c_disable(&i2c->adap);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Setup clock frequency and speed mode
|
|
|
|
+ * Enable restart condition,
|
|
|
|
+ * enable master FSM, disable slave FSM,
|
|
|
|
+ * use target address when initiating transfer
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ writel((i2c->speed + 1) << 1 | SLV_DIS | RESTART | MASTER_EN,
|
|
|
|
+ i2c->base + IC_CON);
|
|
|
|
+ writel(hcnt, i2c->base + (IC_SS_SCL_HCNT + (i2c->speed << 3)));
|
|
|
|
+ writel(lcnt, i2c->base + (IC_SS_SCL_LCNT + (i2c->speed << 3)));
|
|
|
|
+
|
|
|
|
+ /* Set SDA phase delay */
|
|
|
|
+ writel(0x30, i2c->base + 0x84);
|
|
|
|
+
|
|
|
|
+ /* Set tranmit & receive FIFO threshold to zero */
|
|
|
|
+ writel(I2C_FIFO_DEPTH / 2, i2c->base + IC_RX_TL);
|
|
|
|
+ writel(I2C_FIFO_DEPTH / 2, i2c->base + IC_TX_TL);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void ark_i2c_reset(struct ark_i2c_private *i2c)
|
|
|
|
+{
|
|
|
|
+ reset_control_assert(i2c->rst);
|
|
|
|
+ udelay(10);
|
|
|
|
+ reset_control_deassert(i2c->rst);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static u32 ark_i2c_func(struct i2c_adapter *adapter)
|
|
|
|
+{
|
|
|
|
+ return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline bool ark_i2c_address_neq(
|
|
|
|
+ const struct i2c_msg *p1, const struct i2c_msg *p2)
|
|
|
|
+{
|
|
|
|
+ if (p1->addr != p2->addr)
|
|
|
|
+ return 1;
|
|
|
|
+ if ((p1->flags ^ p2->flags) & I2C_M_TEN)
|
|
|
|
+ return 1;
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void ark_i2c_abort(struct ark_i2c_private *i2c)
|
|
|
|
+{
|
|
|
|
+ /* Read about source register */
|
|
|
|
+ int abort = i2c->abort;
|
|
|
|
+ struct i2c_adapter *adap = &i2c->adap;
|
|
|
|
+
|
|
|
|
+ /* Single transfer error check:
|
|
|
|
+ * According to databook, TX/RX FIFOs would be flushed when
|
|
|
|
+ * the abort interrupt occurred.
|
|
|
|
+ */
|
|
|
|
+ if (abort & ABRT_MASTER_DIS)
|
|
|
|
+ dev_err(&adap->dev,
|
|
|
|
+ "initiate master operation with master mode disabled.\n");
|
|
|
|
+ if (abort & ABRT_10B_RD_NORSTRT)
|
|
|
|
+ dev_err(&adap->dev,
|
|
|
|
+ "RESTART disabled and master sent READ cmd in 10-bit addressing.\n");
|
|
|
|
+
|
|
|
|
+ if (abort & ABRT_SBYTE_NORSTRT) {
|
|
|
|
+ dev_err(&adap->dev,
|
|
|
|
+ "RESTART disabled and user is trying to send START byte.\n");
|
|
|
|
+ writel(~ABRT_SBYTE_NORSTRT, i2c->base + IC_TX_ABRT_SOURCE);
|
|
|
|
+ writel(RESTART, i2c->base + IC_CON);
|
|
|
|
+ writel(~IC_TAR_SPECIAL, i2c->base + IC_TAR);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (abort & ABRT_SBYTE_ACKDET)
|
|
|
|
+ dev_err(&adap->dev,
|
|
|
|
+ "START byte was not acknowledged.\n");
|
|
|
|
+ if (abort & ABRT_TXDATA_NOACK)
|
|
|
|
+ I2C_DBG(&adap->dev,
|
|
|
|
+ "No acknowledgement received from slave.\n");
|
|
|
|
+ if (abort & ABRT_10ADDR2_NOACK)
|
|
|
|
+ I2C_DBG(&adap->dev,
|
|
|
|
+ "The 2nd address byte of the 10-bit address was not acknowledged.\n");
|
|
|
|
+ if (abort & ABRT_10ADDR1_NOACK)
|
|
|
|
+ I2C_DBG(&adap->dev,
|
|
|
|
+ "The 1st address byte of 10-bit address was not acknowledged.\n");
|
|
|
|
+ if (abort & ABRT_7B_ADDR_NOACK)
|
|
|
|
+ I2C_DBG(&adap->dev,
|
|
|
|
+ "I2C slave device not acknowledged.\n");
|
|
|
|
+
|
|
|
|
+ /* Clear TX_ABRT bit */
|
|
|
|
+ readl(i2c->base + IC_CLR_TX_ABRT);
|
|
|
|
+ i2c->status = STATUS_XFER_ABORT;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length)
|
|
|
|
+{
|
|
|
|
+ struct ark_i2c_private *i2c = i2c_get_adapdata(adap);
|
|
|
|
+ int num;
|
|
|
|
+ int err;
|
|
|
|
+ int thld;
|
|
|
|
+
|
|
|
|
+ /*if (length >= 256) {
|
|
|
|
+ dev_err(&adap->dev,
|
|
|
|
+ "I2C FIFO cannot support larger than 256 bytes\n");
|
|
|
|
+ return -EMSGSIZE;
|
|
|
|
+ }*/
|
|
|
|
+
|
|
|
|
+ reinit_completion(&i2c->complete);
|
|
|
|
+
|
|
|
|
+ thld = length - 1;
|
|
|
|
+ if(thld > I2C_FIFO_DEPTH / 2) thld = I2C_FIFO_DEPTH / 2;
|
|
|
|
+
|
|
|
|
+ writel(thld, i2c->base + IC_RX_TL);
|
|
|
|
+ readl(i2c->base + IC_CLR_INTR);
|
|
|
|
+
|
|
|
|
+ i2c->status = STATUS_READ_START;
|
|
|
|
+
|
|
|
|
+ i2c->xfer_info.rw = 1;
|
|
|
|
+ i2c->xfer_info.buf = buf;
|
|
|
|
+ i2c->xfer_info.rx_thld = thld;
|
|
|
|
+ num = length;
|
|
|
|
+ if(num > I2C_FIFO_DEPTH) num = I2C_FIFO_DEPTH;
|
|
|
|
+ i2c->xfer_info.send_left = length - num;
|
|
|
|
+ i2c->xfer_info.rev_left = length;
|
|
|
|
+ while (num--)
|
|
|
|
+ writel(IC_RD, i2c->base + IC_DATA_CMD);
|
|
|
|
+
|
|
|
|
+ writel(0x0044, i2c->base + IC_INTR_MASK);
|
|
|
|
+
|
|
|
|
+ err = wait_for_completion_interruptible_timeout(&i2c->complete, adap->timeout);
|
|
|
|
+ if (!err) {
|
|
|
|
+ dev_err(&adap->dev, "Timeout for ACK from I2C slave device\n");
|
|
|
|
+ ark_i2c_hwinit(i2c);
|
|
|
|
+ return -ETIMEDOUT;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (i2c->status == STATUS_READ_SUCCESS) {
|
|
|
|
+ struct timeval tv1, tv2, tv3;
|
|
|
|
+
|
|
|
|
+ do_gettimeofday(&tv1);
|
|
|
|
+ do_gettimeofday(&tv2);
|
|
|
|
+
|
|
|
|
+ while(i2c->xfer_info.rev_left) {
|
|
|
|
+ u32 status = readl(i2c->base + IC_STATUS);
|
|
|
|
+
|
|
|
|
+ if(status & STAT_RFNE) {
|
|
|
|
+ *i2c->xfer_info.buf++ = readl(i2c->base + IC_DATA_CMD);
|
|
|
|
+ i2c->xfer_info.rev_left--;
|
|
|
|
+ } else {
|
|
|
|
+ u32 interval;
|
|
|
|
+
|
|
|
|
+ do_gettimeofday(&tv3);
|
|
|
|
+ interval = (tv3.tv_sec - tv1.tv_sec) * 1000000 + tv3.tv_usec - tv1.tv_usec;
|
|
|
|
+ if(interval > 50000)
|
|
|
|
+ break;
|
|
|
|
+ interval = (tv3.tv_sec - tv2.tv_sec) * 1000000 + tv3.tv_usec - tv2.tv_usec;
|
|
|
|
+ if (interval > 500) {
|
|
|
|
+ cpu_relax();
|
|
|
|
+ do_gettimeofday(&tv2);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (i2c->xfer_info.rev_left) {
|
|
|
|
+ printk(KERN_INFO "rev timeout.\n");
|
|
|
|
+ return -EIO;
|
|
|
|
+ } else {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ return -EIO;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int xfer_write(struct i2c_adapter *adap, unsigned char *buf, int length)
|
|
|
|
+{
|
|
|
|
+ struct ark_i2c_private *i2c = i2c_get_adapdata(adap);
|
|
|
|
+ int err;
|
|
|
|
+ u32 val;
|
|
|
|
+ int timeout = 500;
|
|
|
|
+
|
|
|
|
+ /*if (length >= 256) {
|
|
|
|
+ dev_err(&adap->dev,
|
|
|
|
+ "I2C FIFO cannot support larger than 256 bytes\n");
|
|
|
|
+ return -EMSGSIZE;
|
|
|
|
+ }*/
|
|
|
|
+ reinit_completion(&i2c->complete);
|
|
|
|
+
|
|
|
|
+ /* XXXJACK - set TX threshold to length - 1 */
|
|
|
|
+ //writel(length - 1, i2c->base + IC_TX_TL);
|
|
|
|
+
|
|
|
|
+ readl(i2c->base + IC_CLR_INTR);
|
|
|
|
+
|
|
|
|
+ i2c->xfer_info.rw = 0;
|
|
|
|
+ i2c->xfer_info.buf = buf;
|
|
|
|
+ i2c->xfer_info.send_left = length;
|
|
|
|
+ i2c->status = STATUS_WRITE_START;
|
|
|
|
+
|
|
|
|
+ writel(0x0050, i2c->base + IC_INTR_MASK);
|
|
|
|
+
|
|
|
|
+ err = wait_for_completion_interruptible_timeout(&i2c->complete, adap->timeout);
|
|
|
|
+ if (!err) {
|
|
|
|
+ dev_err(&adap->dev, "Timeout for I2C write\n");
|
|
|
|
+ ark_i2c_hwinit(i2c);
|
|
|
|
+ return -ETIMEDOUT;
|
|
|
|
+ } else {
|
|
|
|
+ if (i2c->status != STATUS_WRITE_SUCCESS) {
|
|
|
|
+ dev_err(&adap->dev, "I2C write status error i2c->status=%d.\n", i2c->status);
|
|
|
|
+ if (i2c->status == STATUS_XFER_ABORT) {
|
|
|
|
+ printk(KERN_INFO "i2c tx abort 1.\n");
|
|
|
|
+ ark_i2c_reset(i2c);
|
|
|
|
+ ark_i2c_hwinit(i2c);
|
|
|
|
+ }
|
|
|
|
+ return -EIO;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ writel(0x40, i2c->base + IC_INTR_MASK);
|
|
|
|
+ //udelay(100);
|
|
|
|
+
|
|
|
|
+ while (timeout--) {
|
|
|
|
+ if (i2c->status == STATUS_XFER_ABORT) {
|
|
|
|
+ printk(KERN_INFO "i2c tx abort 2.\n");
|
|
|
|
+ ark_i2c_reset(i2c);
|
|
|
|
+ ark_i2c_hwinit(i2c);
|
|
|
|
+ return -EIO;
|
|
|
|
+ }
|
|
|
|
+ val = readl(i2c->base + IC_STATUS);
|
|
|
|
+ if ((val & STAT_TFE) && !(val & STAT_MST_ACTIVITY))
|
|
|
|
+ break;
|
|
|
|
+ udelay(100);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (timeout <= 0) {
|
|
|
|
+ printk(KERN_INFO "wait i2c idle timeout.\n");
|
|
|
|
+ ark_i2c_reset(i2c);
|
|
|
|
+ ark_i2c_hwinit(i2c);
|
|
|
|
+ return -EIO;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int ark_i2c_setup(struct i2c_adapter *adap, struct i2c_msg *pmsg)
|
|
|
|
+{
|
|
|
|
+ struct ark_i2c_private *i2c = i2c_get_adapdata(adap);
|
|
|
|
+ int err;
|
|
|
|
+ u32 reg;
|
|
|
|
+ u32 bit_mask;
|
|
|
|
+ u32 mode;
|
|
|
|
+
|
|
|
|
+ /* Disable device first */
|
|
|
|
+ err = ark_i2c_disable(adap);
|
|
|
|
+ if (err) {
|
|
|
|
+ dev_err(&adap->dev, "Cannot disable i2c controller, timeout\n");
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mode = (1 + i2c->speed) << 1;
|
|
|
|
+ /* set the speed mode */
|
|
|
|
+ reg = readl(i2c->base + IC_CON);
|
|
|
|
+ if ((reg & 0x06) != mode) {
|
|
|
|
+ I2C_DBG(&adap->dev, "set mode %d\n", i2c->speed);
|
|
|
|
+ writel((reg & ~0x6) | mode, i2c->base + IC_CON);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* use 7-bit addressing */
|
|
|
|
+ if (pmsg->flags & I2C_M_TEN) {
|
|
|
|
+ if ((reg & ADDR_10BIT) != ADDR_10BIT) {
|
|
|
|
+ I2C_DBG(&adap->dev, "set i2c 10 bit address mode\n");
|
|
|
|
+ writel(reg | ADDR_10BIT, i2c->base + IC_CON);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if ((reg & ADDR_10BIT) != 0x0) {
|
|
|
|
+ I2C_DBG(&adap->dev, "set i2c 7 bit address mode\n");
|
|
|
|
+ writel(reg & ~ADDR_10BIT, i2c->base + IC_CON);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ /* enable restart conditions */
|
|
|
|
+ if (pmsg->flags & I2C_M_NOSTART) {
|
|
|
|
+ if ((reg & RESTART) != RESTART) {
|
|
|
|
+ I2C_DBG(&adap->dev, "enable restart conditions\n");
|
|
|
|
+ writel(reg | RESTART, i2c->base + IC_CON);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if ((reg & RESTART) != 0) {
|
|
|
|
+ I2C_DBG(&adap->dev, "enable restart conditions\n");
|
|
|
|
+ writel(reg & ~RESTART, i2c->base + IC_CON);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* enable master FSM */
|
|
|
|
+ reg = readl(i2c->base + IC_CON);
|
|
|
|
+ I2C_DBG(&adap->dev, "ic_con reg is 0x%x\n", reg);
|
|
|
|
+ writel(reg | MASTER_EN, i2c->base + IC_CON);
|
|
|
|
+ if ((reg & SLV_DIS) != SLV_DIS) {
|
|
|
|
+ I2C_DBG(&adap->dev, "enable master FSM\n");
|
|
|
|
+ writel(reg | SLV_DIS, i2c->base + IC_CON);
|
|
|
|
+ I2C_DBG(&adap->dev, "ic_con reg is 0x%x\n", reg);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* use target address when initiating transfer */
|
|
|
|
+ reg = readl(i2c->base + IC_TAR);
|
|
|
|
+ bit_mask = IC_TAR_SPECIAL | IC_TAR_GC_OR_START;
|
|
|
|
+
|
|
|
|
+ if ((reg & bit_mask) != 0x0) {
|
|
|
|
+ I2C_DBG(&adap->dev,
|
|
|
|
+ "WR: use target address when intiating transfer, i2c_tx_target\n");
|
|
|
|
+ writel(reg & ~bit_mask, i2c->base + IC_TAR);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* set target address to the I2C slave address */
|
|
|
|
+ I2C_DBG(&adap->dev,
|
|
|
|
+ "set target address to the I2C slave address, addr is %x\n",
|
|
|
|
+ pmsg->addr);
|
|
|
|
+ writel(pmsg->addr | (pmsg->flags & I2C_M_TEN ? IC_TAR_10BIT_ADDR : 0),
|
|
|
|
+ i2c->base + IC_TAR);
|
|
|
|
+
|
|
|
|
+ /* set master code for high-speed mode */
|
|
|
|
+ if (i2c->speed == I2C_HIGH) {
|
|
|
|
+ writel((i2c->hs_mcode & IC_HS_MAR), i2c->base + IC_HS_MADDR);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Enable I2C controller */
|
|
|
|
+ writel(ENABLE, i2c->base + IC_ENABLE);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int ark_i2c_xfer(
|
|
|
|
+ struct i2c_adapter *adap, struct i2c_msg *pmsg, int num)
|
|
|
|
+{
|
|
|
|
+ struct ark_i2c_private *i2c = i2c_get_adapdata(adap);
|
|
|
|
+ int i, err = 0;
|
|
|
|
+
|
|
|
|
+ /* if number of messages equal 0*/
|
|
|
|
+ if (num == 0)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_PM_RUNTIME
|
|
|
|
+ pm_runtime_get(i2c->dev);
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ mutex_lock(&i2c->lock);
|
|
|
|
+ I2C_DBG(&adap->dev, "ark_i2c_xfer, process %d msg(s)\n", num);
|
|
|
|
+ I2C_DBG(&adap->dev, "slave address is %x\n", pmsg->addr);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if (i2c->status != STATUS_IDLE) {
|
|
|
|
+ dev_err(&adap->dev, "Adapter %d in transfer/standby\n", adap->nr);
|
|
|
|
+ mutex_unlock(&i2c->lock);
|
|
|
|
+#ifdef CONFIG_PM_RUNTIME
|
|
|
|
+ pm_runtime_put(i2c->dev);
|
|
|
|
+#endif
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ for (i = 1; i < num; i++) {
|
|
|
|
+ /* Message address equal? */
|
|
|
|
+ if (unlikely(ark_i2c_address_neq(&pmsg[0], &pmsg[i]))) {
|
|
|
|
+ dev_err(&adap->dev, "Invalid address in msg[%d]\n", i);
|
|
|
|
+ mutex_unlock(&i2c->lock);
|
|
|
|
+#ifdef CONFIG_PM_RUNTIME
|
|
|
|
+ pm_runtime_put(i2c->dev);
|
|
|
|
+#endif
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (ark_i2c_setup(adap, pmsg)) {
|
|
|
|
+ mutex_unlock(&i2c->lock);
|
|
|
|
+#ifdef CONFIG_PM_RUNTIME
|
|
|
|
+ pm_runtime_put(i2c->dev);
|
|
|
|
+#endif
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < num; i++) {
|
|
|
|
+ i2c->msg = pmsg;
|
|
|
|
+ i2c->status = STATUS_IDLE;
|
|
|
|
+ /* Read or Write */
|
|
|
|
+ if (pmsg->flags & I2C_M_RD) {
|
|
|
|
+ I2C_DBG(&adap->dev, "I2C_M_RD\n");
|
|
|
|
+ err = xfer_read(adap, pmsg->buf, pmsg->len);
|
|
|
|
+ } else {
|
|
|
|
+ I2C_DBG(&adap->dev, "I2C_M_WR\n");
|
|
|
|
+ err = xfer_write(adap, pmsg->buf, pmsg->len);
|
|
|
|
+ }
|
|
|
|
+ if (err < 0)
|
|
|
|
+ break;
|
|
|
|
+ I2C_DBG(&adap->dev, "msg[%d] transfer complete\n", i);
|
|
|
|
+ pmsg++; /* next message */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Mask interrupts */
|
|
|
|
+ writel(0x0000, i2c->base + IC_INTR_MASK);
|
|
|
|
+ /* Clear all interrupts */
|
|
|
|
+ readl(i2c->base + IC_CLR_INTR);
|
|
|
|
+
|
|
|
|
+ i2c->status = STATUS_IDLE;
|
|
|
|
+ mutex_unlock(&i2c->lock);
|
|
|
|
+#ifdef CONFIG_PM_RUNTIME
|
|
|
|
+ pm_runtime_put(i2c->dev);
|
|
|
|
+#endif
|
|
|
|
+ return err ? err : i;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_PM_RUNTIME
|
|
|
|
+static int ark_i2c_runtime_suspend(struct device *dev)
|
|
|
|
+{
|
|
|
|
+ struct platform_device *pdev = to_platform_device(dev);
|
|
|
|
+ struct ark_i2c_private *i2c = platform_get_drvdata(pdev);
|
|
|
|
+ struct i2c_adapter *adap = to_i2c_adapter(dev);
|
|
|
|
+
|
|
|
|
+ if (i2c->status != STATUS_IDLE)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ ark_i2c_disable(adap);
|
|
|
|
+
|
|
|
|
+ i2c->status = STATUS_STANDBY;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+static int ark_i2c_runtime_resume(struct device *dev)
|
|
|
|
+{
|
|
|
|
+ struct platform_device *pdev = to_platform_device(dev);
|
|
|
|
+ struct ark_i2c_private *i2c = platform_get_drvdata(pdev);
|
|
|
|
+
|
|
|
|
+ if (i2c->status != STATUS_STANDBY)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ i2c->status = STATUS_IDLE;
|
|
|
|
+
|
|
|
|
+ ark_i2c_hwinit(i2c);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+#endif /* CONFIG_PM_RUNTIME */
|
|
|
|
+
|
|
|
|
+static int i2c_isr_read(struct ark_i2c_private *i2c)
|
|
|
|
+{
|
|
|
|
+ u32 status;
|
|
|
|
+ u32 tx_level, rx_level;
|
|
|
|
+ int wnum, rnum;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ if (!(i2c->xfer_info.rw == 1 && i2c->xfer_info.buf != NULL))
|
|
|
|
+ {
|
|
|
|
+ dev_err(i2c->dev, "i2c_isr_read invalid parameter.\n");
|
|
|
|
+ return 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ status = readl(i2c->base + IC_STATUS);
|
|
|
|
+ tx_level = readl(i2c->base + IC_TXFLR);
|
|
|
|
+ rx_level = readl(i2c->base + IC_RXFLR);
|
|
|
|
+ if(tx_level == 0 && !(status & STAT_TFE)) tx_level = I2C_FIFO_DEPTH;
|
|
|
|
+ if(rx_level == 0 && (status & STAT_RFF)) rx_level = I2C_FIFO_DEPTH;
|
|
|
|
+
|
|
|
|
+ wnum = I2C_FIFO_DEPTH - tx_level;
|
|
|
|
+ if(i2c->xfer_info.send_left < wnum) wnum = i2c->xfer_info.send_left;
|
|
|
|
+ for(i = 0; i < wnum; i++)
|
|
|
|
+ writel(IC_RD, i2c->base + IC_DATA_CMD);
|
|
|
|
+ i2c->xfer_info.send_left -= wnum;
|
|
|
|
+
|
|
|
|
+ rnum = rx_level;
|
|
|
|
+ for(i = 0; i < rnum; i++)
|
|
|
|
+ *i2c->xfer_info.buf++ = readl(i2c->base + IC_DATA_CMD);
|
|
|
|
+ i2c->xfer_info.rev_left -= rnum;
|
|
|
|
+
|
|
|
|
+ if(i2c->xfer_info.rev_left <= i2c->xfer_info.rx_thld)
|
|
|
|
+ {
|
|
|
|
+ i2c->status = STATUS_READ_SUCCESS;
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ i2c->status = STATUS_READ_IN_PROGRESS;
|
|
|
|
+
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int i2c_isr_write(struct ark_i2c_private *i2c)
|
|
|
|
+{
|
|
|
|
+ u32 status;
|
|
|
|
+ u32 tx_level;
|
|
|
|
+ int wnum;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ if(!(i2c->xfer_info.rw == 0 && i2c->xfer_info.buf != NULL))
|
|
|
|
+ {
|
|
|
|
+ dev_err(i2c->dev, "i2c_isr_write invalid parameter.\n");
|
|
|
|
+ return 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(i2c->xfer_info.send_left == 0 && i2c->status == STATUS_WRITE_IN_PROGRESS)
|
|
|
|
+ {
|
|
|
|
+ i2c->status = STATUS_WRITE_SUCCESS;
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ status = readl(i2c->base + IC_STATUS);
|
|
|
|
+ tx_level = readl(i2c->base + IC_TXFLR);
|
|
|
|
+ if(tx_level == 0 && !(status & STAT_TFE)) tx_level = I2C_FIFO_DEPTH;
|
|
|
|
+ wnum = I2C_FIFO_DEPTH - tx_level;
|
|
|
|
+ if(i2c->xfer_info.send_left < wnum) wnum = i2c->xfer_info.send_left;
|
|
|
|
+ for(i = 0; i < wnum; i++)
|
|
|
|
+ writel((u16)(*(i2c->xfer_info.buf + i)), i2c->base + IC_DATA_CMD);
|
|
|
|
+ i2c->xfer_info.buf += wnum;
|
|
|
|
+ i2c->xfer_info.send_left -= wnum;
|
|
|
|
+
|
|
|
|
+ i2c->status = STATUS_WRITE_IN_PROGRESS;
|
|
|
|
+
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static irqreturn_t ark_i2c_isr(int this_irq, void *dev)
|
|
|
|
+{
|
|
|
|
+ struct ark_i2c_private *i2c = dev;
|
|
|
|
+ u32 stat;
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_PM_RUNTIME
|
|
|
|
+ if (pm_runtime_suspended(i2c->dev))
|
|
|
|
+ return IRQ_NONE;
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ stat = readl(i2c->base + IC_INTR_STAT);
|
|
|
|
+ if (!stat)
|
|
|
|
+ return IRQ_NONE;
|
|
|
|
+
|
|
|
|
+ //I2C_DBG(&i2c->adap.dev, "%s, stat = 0x%x\n", __func__, stat);
|
|
|
|
+
|
|
|
|
+ if (i2c->status == STATUS_IDLE || i2c->status == STATUS_STANDBY)
|
|
|
|
+ goto err;
|
|
|
|
+
|
|
|
|
+ if (stat & TX_ABRT) {
|
|
|
|
+ i2c->abort = readl(i2c->base + IC_TX_ABRT_SOURCE);
|
|
|
|
+ printk(KERN_INFO "abort=0x%x.\n", i2c->abort);
|
|
|
|
+ readl(i2c->base + IC_CLR_INTR);
|
|
|
|
+ ark_i2c_abort(i2c);
|
|
|
|
+ goto exit;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (stat & RX_FULL) {
|
|
|
|
+ if(i2c_isr_read(i2c)) goto err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (stat & TX_EMPTY) {
|
|
|
|
+ if(i2c_isr_write(i2c)) goto err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+exit:
|
|
|
|
+ if (i2c->status == STATUS_READ_SUCCESS ||
|
|
|
|
+ i2c->status == STATUS_WRITE_SUCCESS ||
|
|
|
|
+ i2c->status == STATUS_XFER_ABORT) {
|
|
|
|
+ /* Mask interrupts */
|
|
|
|
+ writel(0, i2c->base + IC_INTR_MASK);
|
|
|
|
+ complete(&i2c->complete);
|
|
|
|
+ }
|
|
|
|
+err:
|
|
|
|
+ return IRQ_HANDLED;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct i2c_algorithm ark_i2c_algorithm = {
|
|
|
|
+ .master_xfer = ark_i2c_xfer,
|
|
|
|
+ .functionality = ark_i2c_func,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const struct i2c_adapter_quirks ark_i2c_quirks = {
|
|
|
|
+ .flags = I2C_AQ_NO_ZERO_LEN,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_PM_RUNTIME
|
|
|
|
+static const struct dev_pm_ops ark_i2c_pm_ops = {
|
|
|
|
+ .runtime_suspend = ark_i2c_runtime_suspend,
|
|
|
|
+ .runtime_resume = ark_i2c_runtime_resume,
|
|
|
|
+};
|
|
|
|
+#define ARK_I2C_PM_OPS (&ark_i2c_pm_ops)
|
|
|
|
+#else /* !CONFIG_PM_RUNTIME */
|
|
|
|
+#define ARK_I2C_PM_OPS NULL
|
|
|
|
+#endif /* CONFIG_PM_RUNTIME */
|
|
|
|
+
|
|
|
|
+static int ark_i2c_probe(struct platform_device *pdev)
|
|
|
|
+{
|
|
|
|
+ struct ark_i2c_private *i2c;
|
|
|
|
+ struct resource *mem;
|
|
|
|
+ int err, irq;
|
|
|
|
+ void __iomem *base = NULL;
|
|
|
|
+ struct clk *i2c_clk;
|
|
|
|
+ struct reset_control *rst;
|
|
|
|
+ u32 speed;
|
|
|
|
+
|
|
|
|
+ I2C_DBG(&pdev->dev, "Get into probe function for I2C\n");
|
|
|
|
+
|
|
|
|
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
+ if (!mem) {
|
|
|
|
+ dev_err(&pdev->dev, "no mem resource?\n");
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ irq = platform_get_irq(pdev, 0);
|
|
|
|
+ if (irq < 0) {
|
|
|
|
+ dev_err(&pdev->dev, "no irq resource?\n");
|
|
|
|
+ return irq; /* -ENXIO */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ base = devm_ioremap_resource(&pdev->dev, mem);
|
|
|
|
+ if (base == NULL) {
|
|
|
|
+ dev_err(&pdev->dev, "failure mapping io resources\n");
|
|
|
|
+ err = -EBUSY;
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ i2c_clk = devm_clk_get(&pdev->dev, NULL);
|
|
|
|
+ if (IS_ERR(i2c_clk)) {
|
|
|
|
+ err = PTR_ERR(i2c_clk);
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rst = devm_reset_control_get(&pdev->dev, NULL);
|
|
|
|
+ if (IS_ERR(rst)) {
|
|
|
|
+ dev_err(&pdev->dev,
|
|
|
|
+ "missing or invalid reset controller device tree entry\n");
|
|
|
|
+ err = PTR_ERR(rst);
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Allocate the per-device data structure, ark_i2c_private */
|
|
|
|
+ i2c = devm_kzalloc(&pdev->dev, sizeof(struct ark_i2c_private), GFP_KERNEL);
|
|
|
|
+ if (i2c == NULL) {
|
|
|
|
+ dev_err(&pdev->dev, "can't allocate interface\n");
|
|
|
|
+ err = -ENOMEM;
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Initialize struct members */
|
|
|
|
+ strlcpy(i2c->adap.name, "ArkMicro I2C adapter", sizeof(i2c->adap.name));
|
|
|
|
+ i2c->adap.owner = THIS_MODULE;
|
|
|
|
+ i2c->adap.class = I2C_CLASS_DEPRECATED;
|
|
|
|
+ i2c->adap.retries = 3;
|
|
|
|
+ i2c->adap.timeout = 3 * HZ;
|
|
|
|
+ i2c->adap.algo = &ark_i2c_algorithm;
|
|
|
|
+ i2c->adap.quirks = &ark_i2c_quirks;
|
|
|
|
+ i2c->adap.nr = pdev->id;
|
|
|
|
+ i2c->adap.dev.parent = &pdev->dev;
|
|
|
|
+ i2c->adap.dev.of_node = pdev->dev.of_node;
|
|
|
|
+
|
|
|
|
+ i2c->dev = &pdev->dev;
|
|
|
|
+ i2c->base = base;
|
|
|
|
+ i2c->irq = irq;
|
|
|
|
+ i2c->rst = rst;
|
|
|
|
+ i2c->speed = I2C_STANDARD;
|
|
|
|
+ i2c->abort = 0;
|
|
|
|
+ i2c->rx_buf_len = 0;
|
|
|
|
+ i2c->status = STATUS_IDLE;
|
|
|
|
+
|
|
|
|
+ platform_set_drvdata(pdev, i2c);
|
|
|
|
+ i2c_set_adapdata(&i2c->adap, i2c);
|
|
|
|
+
|
|
|
|
+ i2c->clk = i2c_clk;
|
|
|
|
+
|
|
|
|
+ if(!of_property_read_u32(pdev->dev.of_node, "speed-mode", &speed)) {
|
|
|
|
+ if(speed < NUM_SPEEDS)
|
|
|
|
+ i2c->speed = speed;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Initialize i2c controller */
|
|
|
|
+ err = ark_i2c_hwinit(i2c);
|
|
|
|
+ if (err < 0) {
|
|
|
|
+ dev_err(&pdev->dev, "I2C interface initialization failed\n");
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mutex_init(&i2c->lock);
|
|
|
|
+ init_completion(&i2c->complete);
|
|
|
|
+
|
|
|
|
+ /* Clear all interrupts */
|
|
|
|
+ readl(i2c->base + IC_CLR_INTR);
|
|
|
|
+ writel(0x0000, i2c->base + IC_INTR_MASK);
|
|
|
|
+
|
|
|
|
+ err = devm_request_irq(&pdev->dev, i2c->irq, ark_i2c_isr, IRQF_SHARED, pdev->name, i2c);
|
|
|
|
+ if (err) {
|
|
|
|
+ dev_err(&pdev->dev, "Failed to request IRQ %d for I2C controller: "
|
|
|
|
+ "%s\n", i2c->irq, pdev->name);
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Adapter registration */
|
|
|
|
+ err = i2c_add_numbered_adapter(&i2c->adap);
|
|
|
|
+ if (err) {
|
|
|
|
+ dev_err(&pdev->dev, "Adapter %s registration failed\n",
|
|
|
|
+ i2c->adap.name);
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ I2C_DBG(&pdev->dev, "ArkMicro I2C bus %d driver bind success.\n",
|
|
|
|
+ pdev->id);
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_PM_RUNTIME
|
|
|
|
+ pm_runtime_enable(&pdev->dev);
|
|
|
|
+#endif
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+fail:
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int ark_i2c_remove(struct platform_device *pdev)
|
|
|
|
+{
|
|
|
|
+ struct ark_i2c_private *i2c = platform_get_drvdata(pdev);
|
|
|
|
+
|
|
|
|
+ ark_i2c_disable(&i2c->adap);
|
|
|
|
+ i2c_del_adapter(&i2c->adap);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const struct of_device_id ark_i2c_dt_ids[] = {
|
|
|
|
+ {
|
|
|
|
+ .compatible = "arkmicro,ark-i2c",
|
|
|
|
+ } , {
|
|
|
|
+ /* sentinel */
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+MODULE_DEVICE_TABLE(of, ark_i2c_dt_ids);
|
|
|
|
+
|
|
|
|
+static struct platform_driver ark_i2c_driver = {
|
|
|
|
+ .driver = {
|
|
|
|
+ .name = DRIVER_NAME,
|
|
|
|
+ .of_match_table = of_match_ptr(ark_i2c_dt_ids),
|
|
|
|
+ .pm = ARK_I2C_PM_OPS,
|
|
|
|
+ },
|
|
|
|
+ .probe = ark_i2c_probe,
|
|
|
|
+ .remove = ark_i2c_remove,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int __init ark_i2c_init_driver(void)
|
|
|
|
+{
|
|
|
|
+ return platform_driver_register(&ark_i2c_driver);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void __exit ark_i2c_exit_driver(void)
|
|
|
|
+{
|
|
|
|
+ platform_driver_unregister(&ark_i2c_driver);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+subsys_initcall(ark_i2c_init_driver);
|
|
|
|
+module_exit(ark_i2c_exit_driver);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+MODULE_AUTHOR("Sim");
|
|
|
|
+MODULE_DESCRIPTION("I2C driver for Ark Platform");
|
|
|
|
+MODULE_LICENSE("GPL");
|
|
|
|
+MODULE_VERSION(VERSION);
|