mirror of https://github.com/OpenIPC/firmware.git
1099 lines
29 KiB
Diff
1099 lines
29 KiB
Diff
diff -drupN a/drivers/i2c/busses/i2c-ingenic.c b/drivers/i2c/busses/i2c-ingenic.c
|
|
--- a/drivers/i2c/busses/i2c-ingenic.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/i2c/busses/i2c-ingenic.c 2022-06-09 05:02:29.000000000 +0300
|
|
@@ -0,0 +1,1094 @@
|
|
+/* drivers/i2c/busses/i2c-v12-ingenic.c
|
|
+ *
|
|
+ * Copyright (C) 2014 Ingenic Semiconductor Co., Ltd.
|
|
+ * http://www.ingenic.com
|
|
+ * Sun Jiwei<jwsun@ingenic.cn>
|
|
+ *
|
|
+ * I2C adapter driver for the Ingenic I2C controller
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/time.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/completion.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/clk-provider.h>
|
|
+
|
|
+#define I2C_CTRL (0x00)
|
|
+#define I2C_TAR (0x04)
|
|
+#define I2C_SAR (0x08)
|
|
+#define I2C_DC (0x10)
|
|
+#define I2C_SHCNT (0x14)
|
|
+#define I2C_SLCNT (0x18)
|
|
+#define I2C_FHCNT (0x1C)
|
|
+#define I2C_FLCNT (0x20)
|
|
+#define I2C_INTST (0x2C)
|
|
+#define I2C_INTM (0x30)
|
|
+#define I2C_RAW_INTR_STAT (0x34)
|
|
+#define I2C_RXTL (0x38)
|
|
+#define I2C_TXTL (0x3c)
|
|
+#define I2C_CINTR (0x40)
|
|
+#define I2C_CRXUF (0x44)
|
|
+#define I2C_CRXOF (0x48)
|
|
+#define I2C_CTXOF (0x4C)
|
|
+#define I2C_CRXREQ (0x50)
|
|
+#define I2C_CTXABRT (0x54)
|
|
+#define I2C_CRXDONE (0x58)
|
|
+#define I2C_CACT (0x5C)
|
|
+#define I2C_CSTP (0x60)
|
|
+#define I2C_CSTT (0x64)
|
|
+#define I2C_CGC (0x68)
|
|
+#define I2C_ENB (0x6C)
|
|
+#define I2C_STA (0x70)
|
|
+#define I2C_TXFLR (0x74)
|
|
+#define I2C_RXFLR (0x78)
|
|
+#define I2C_SDAHD (0x7C)
|
|
+#define I2C_TXABRT (0x80)
|
|
+#define I2C_DMACR (0x88)
|
|
+#define I2C_DMATDLR (0x8c)
|
|
+#define I2C_DMARDLR (0x90)
|
|
+#define I2C_SDASU (0x94)
|
|
+#define I2C_ACKGC (0x98)
|
|
+#define I2C_ENSTA (0x9C)
|
|
+#define I2C_FLT (0xA0)
|
|
+
|
|
+/* I2C Control Register (I2C_CTRL) */
|
|
+#define I2C_CTRL_SLVDIS (1 << 6) /* after reset slave is disabled */
|
|
+#define I2C_CTRL_REST (1 << 5)
|
|
+#define I2C_CTRL_MATP (1 << 4) /* 1: 10bit address 0: 7bit addressing */
|
|
+#define I2C_CTRL_SATP (1 << 3) /* 1: 10bit address 0: 7bit address */
|
|
+#define I2C_CTRL_SPDF (2 << 1) /* fast mode 400kbps */
|
|
+#define I2C_CTRL_SPDS (1 << 1) /* standard mode 100kbps */
|
|
+#define I2C_CTRL_MD (1 << 0) /* master enabled */
|
|
+
|
|
+/* I2C Status Register (I2C_STA) */
|
|
+#define I2C_STA_SLVACT (1 << 6) /* Slave FSM is not in IDLE state */
|
|
+#define I2C_STA_MSTACT (1 << 5) /* Master FSM is not in IDLE state */
|
|
+#define I2C_STA_RFF (1 << 4) /* RFIFO if full */
|
|
+#define I2C_STA_RFNE (1 << 3) /* RFIFO is not empty */
|
|
+#define I2C_STA_TFE (1 << 2) /* TFIFO is empty */
|
|
+#define I2C_STA_TFNF (1 << 1) /* TFIFO is not full */
|
|
+#define I2C_STA_ACT (1 << 0) /* I2C Activity Status */
|
|
+
|
|
+/* i2c interrupt status (I2C_INTST) */
|
|
+#define I2C_INTST_IGC (1 << 11)
|
|
+#define I2C_INTST_ISTT (1 << 10)
|
|
+#define I2C_INTST_ISTP (1 << 9)
|
|
+#define I2C_INTST_IACT (1 << 8)
|
|
+#define I2C_INTST_RXDN (1 << 7)
|
|
+#define I2C_INTST_TXABT (1 << 6)
|
|
+#define I2C_INTST_RDREQ (1 << 5)
|
|
+#define I2C_INTST_TXEMP (1 << 4)
|
|
+#define I2C_INTST_TXOF (1 << 3)
|
|
+#define I2C_INTST_RXFL (1 << 2)
|
|
+#define I2C_INTST_RXOF (1 << 1)
|
|
+#define I2C_INTST_RXUF (1 << 0)
|
|
+
|
|
+/* i2c interrupt mask status (I2C_INTM) */
|
|
+#define I2C_INTM_MIGC (1 << 11)
|
|
+#define I2C_INTM_MISTT (1 << 10)
|
|
+#define I2C_INTM_MISTP (1 << 9)
|
|
+#define I2C_INTM_MIACT (1 << 8)
|
|
+#define I2C_INTM_MRXDN (1 << 7)
|
|
+#define I2C_INTM_MTXABT (1 << 6)
|
|
+#define I2C_INTM_MRDREQ (1 << 5)
|
|
+#define I2C_INTM_MTXEMP (1 << 4)
|
|
+#define I2C_INTM_MTXOF (1 << 3)
|
|
+#define I2C_INTM_MRXFL (1 << 2)
|
|
+#define I2C_INTM_MRXOF (1 << 1)
|
|
+#define I2C_INTM_MRXUF (1 << 0)
|
|
+
|
|
+#define I2C_DC_REST (1 << 10)
|
|
+#define I2C_DC_STP (1 << 9)
|
|
+#define I2C_DC_READ (1 << 8)
|
|
+
|
|
+#define I2C_ENB_I2CENB (1 << 0) /* Enable the i2c */
|
|
+
|
|
+#ifdef CONFIG_I2C_FIFO_LEN
|
|
+#define I2C_FIFO_LEN (CONFIG_I2C_FIFO_LEN)
|
|
+#else
|
|
+#define I2C_FIFO_LEN (64)
|
|
+#endif
|
|
+
|
|
+#define TX_LEVEL (I2C_FIFO_LEN / 2)
|
|
+#define RX_LEVEL (I2C_FIFO_LEN / 2 - 1)
|
|
+#define TIMEOUT 0xff
|
|
+#define DEBUG_INFO 2
|
|
+#define DEBUG_WARN 1
|
|
+
|
|
+//#define I2C_DEBUG
|
|
+/*
|
|
+ * msg_end_type: The bus control which need to be send at end of transfer.
|
|
+ * @MSG_END_STOP: Send stop pulse at end of transfer.
|
|
+ * @MSG_END_REPEAT_START: Send repeat start at end of transfer.
|
|
+ */
|
|
+enum msg_end_type {
|
|
+ MSG_END_STOP,
|
|
+ MSG_END_CONTINUE,
|
|
+ MSG_END_REPEAT_START,
|
|
+};
|
|
+
|
|
+/* I2C Transmit Abort Status Register (I2C_TXABRT) */
|
|
+static const char *abrt_src[] = {
|
|
+ "I2C_TXABRT_ABRT_7B_ADDR_NOACK",
|
|
+ "I2C_TXABRT_ABRT_10ADDR1_NOACK",
|
|
+ "I2C_TXABRT_ABRT_10ADDR2_NOACK",
|
|
+ "I2C_TXABRT_ABRT_XDATA_NOACK",
|
|
+ "I2C_TXABRT_ABRT_GCALL_NOACK",
|
|
+ "I2C_TXABRT_ABRT_GCALL_READ",
|
|
+ "I2C_TXABRT_ABRT_HS_ACKD",
|
|
+ "I2C_TXABRT_SBYTE_ACKDET",
|
|
+ "I2C_TXABRT_ABRT_HS_NORSTRT",
|
|
+ "I2C_TXABRT_SBYTE_NORSTRT",
|
|
+ "I2C_TXABRT_ABRT_10B_RD_NORSTRT",
|
|
+ "I2C_TXABRT_ABRT_MASTER_DIS",
|
|
+ "I2C_TXABRT_ARB_LOST",
|
|
+ "I2C_TXABRT_SLVFLUSH_TXFIFO",
|
|
+ "I2C_TXABRT_SLV_ARBLOST",
|
|
+ "I2C_TXABRT_SLVRD_INTX",
|
|
+};
|
|
+
|
|
+/* I2C standard mode high count register(I2CSHCNT) */
|
|
+#define I2CSHCNT_ADJUST(n) (((n) - 8) < 6 ? 6 : ((n) - 8))
|
|
+/* I2C standard mode low count register(I2CSLCNT) */
|
|
+#define I2CSLCNT_ADJUST(n) (((n) - 1) < 8 ? 8 : ((n) - 1))
|
|
+/* I2C fast mode high count register(I2CFHCNT) */
|
|
+#define I2CFHCNT_ADJUST(n) (((n) - 8) < 6 ? 6 : ((n) - 8))
|
|
+/* I2C fast mode low count register(I2CFLCNT) */
|
|
+#define I2CFLCNT_ADJUST(n) (((n) - 1) < 8 ? 8 : ((n) - 1))
|
|
+
|
|
+struct i2c_ingenic {
|
|
+ void __iomem *iomem;
|
|
+ struct device *dev;
|
|
+ int irq;
|
|
+ struct clk *clk;
|
|
+ struct i2c_adapter adap;
|
|
+
|
|
+ enum msg_end_type w_end_type;
|
|
+ enum msg_end_type r_end_type;
|
|
+ unsigned char *rbuf;
|
|
+ unsigned char *wbuf;
|
|
+ unsigned int rd_len;
|
|
+ int len;
|
|
+
|
|
+ struct completion complete;
|
|
+
|
|
+ int debug;
|
|
+ unsigned int rate;
|
|
+
|
|
+ unsigned int timeout_ms; /*ms*/
|
|
+ unsigned int speed_hz; /*hz*/
|
|
+ unsigned int id; /*chip id*/
|
|
+};
|
|
+
|
|
+static inline unsigned short i2c_readl(struct i2c_ingenic *i2c,
|
|
+ unsigned short offset);
|
|
+#ifdef I2C_DEBUG
|
|
+static void i2c_ingenic_dump_regs(struct i2c_ingenic *i2c)
|
|
+{
|
|
+ struct i2c_ingenic *i2c_id = i2c;
|
|
+
|
|
+#define PRINT_REG_WITH_ID(reg_name, id) \
|
|
+ dev_info(&(i2c->adap.dev),"--"#reg_name " 0x%08x\n",i2c_readl(id, reg_name))
|
|
+
|
|
+ PRINT_REG_WITH_ID(I2C_CTRL, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_INTST, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_INTM, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_RAW_INTR_STAT, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_RXTL, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_TXTL, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_STA, i2c_id);
|
|
+ PRINT_REG_WITH_ID(0x78, i2c_id);
|
|
+ return;
|
|
+
|
|
+ PRINT_REG_WITH_ID(I2C_CTRL, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_TAR, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_SAR, i2c_id);
|
|
+ // PRINT_REG_WITH_ID(I2C_DC, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_SHCNT, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_SLCNT, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_FHCNT, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_FLCNT, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_INTST, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_INTM, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_RXTL, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_TXTL, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_CINTR, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_CRXUF, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_CRXOF, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_CTXOF, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_CRXREQ, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_CTXABRT, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_CRXDONE, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_CACT, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_CSTP, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_CSTT, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_CGC, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_ENB, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_STA, i2c_id);
|
|
+ /*debug trans & recive fifo count */
|
|
+ PRINT_REG_WITH_ID(0x74, i2c_id);
|
|
+ PRINT_REG_WITH_ID(0x78, i2c_id);
|
|
+
|
|
+ PRINT_REG_WITH_ID(I2C_TXABRT, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_DMACR, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_DMATDLR, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_DMARDLR, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_SDASU, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_ACKGC, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_ENSTA, i2c_id);
|
|
+ PRINT_REG_WITH_ID(I2C_SDAHD, i2c_id);
|
|
+#undef PRINT_REG_WITH_ID
|
|
+}
|
|
+#else /* I2C_DEBUG */
|
|
+static void i2c_ingenic_dump_regs(struct i2c_ingenic *i2c) {}
|
|
+#endif /* !I2C_DEBUG */
|
|
+
|
|
+static inline unsigned short i2c_readl(struct i2c_ingenic *i2c,
|
|
+ unsigned short offset)
|
|
+{
|
|
+ return readl(i2c->iomem + offset);
|
|
+}
|
|
+
|
|
+static inline void i2c_writel(struct i2c_ingenic *i2c, unsigned short offset,
|
|
+ unsigned short value)
|
|
+{
|
|
+ writel(value, i2c->iomem + offset);
|
|
+}
|
|
+
|
|
+static int i2c_ingenic_enable(struct i2c_ingenic *i2c, int enable)
|
|
+{
|
|
+ int timeout = TIMEOUT;
|
|
+
|
|
+ i2c_writel(i2c, I2C_ENB, enable);
|
|
+ while (((i2c_readl(i2c, I2C_ENSTA) & I2C_ENB_I2CENB) == !enable) && (--timeout > 0))
|
|
+ msleep(1);
|
|
+
|
|
+ if (timeout)
|
|
+ return 0;
|
|
+
|
|
+ dev_err(&(i2c->adap.dev), "%s i2c%d failed\n", enable?"enable":"disable", i2c->adap.nr);
|
|
+ return -ETIMEDOUT;
|
|
+}
|
|
+
|
|
+static void i2c_ingenic_reset(struct i2c_ingenic *i2c)
|
|
+{
|
|
+ i2c_readl(i2c, I2C_CTXABRT);
|
|
+ i2c_readl(i2c, I2C_INTST);
|
|
+
|
|
+ i2c_ingenic_enable(i2c, 0);
|
|
+ udelay(10); /* Don't know why to wait for 10us */
|
|
+ i2c_ingenic_enable(i2c, 1);
|
|
+}
|
|
+
|
|
+/* function: send read command
|
|
+ * return: 0, successful
|
|
+ * 1, txfifo valid entry is more than receive fifo, before send read command,
|
|
+ * must be read.
|
|
+ * 2, txfifo count is 0 or rxfifo count is 0.
|
|
+ * */
|
|
+static inline unsigned int i2c_send_rcmd(struct i2c_ingenic *i2c)
|
|
+{
|
|
+ unsigned int tx_count, rx_count, count, tx_valid, rx_valid;
|
|
+
|
|
+ tx_valid = i2c_readl(i2c, I2C_TXFLR);
|
|
+ rx_valid = i2c_readl(i2c, I2C_RXFLR);
|
|
+ tx_count = I2C_FIFO_LEN - tx_valid;
|
|
+ rx_count = I2C_FIFO_LEN - rx_valid;
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if(i2c->debug > DEBUG_INFO)
|
|
+ dev_info(&(i2c->adap.dev),
|
|
+ "%s, tx_valid = %d, rx_valid = %d,"
|
|
+ " tx_count = %d, rx_count = %d\n",
|
|
+ __func__, tx_valid, rx_valid, tx_count, rx_count);
|
|
+#endif
|
|
+
|
|
+ if (tx_valid > rx_count) {
|
|
+ dev_warn(&(i2c->adap.dev),
|
|
+ "\n\n###Warrning: I2C transfer fifo valid entry is more receive fifo, "
|
|
+ "before send read cmd, please read data from "
|
|
+ "the read fifo.\n\n");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ if (!tx_count || !rx_count) {
|
|
+ dev_warn(&(i2c->adap.dev),
|
|
+ "\n\n###Warrning: I2C receive fifo or transfer fifo is full, "
|
|
+ "before send read cmd, please read data from "
|
|
+ "the read fifo or wait some time.\n\n");
|
|
+ return 2;
|
|
+ }
|
|
+
|
|
+ count = min3(i2c->rd_len, tx_count, rx_count);
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if (i2c->debug > DEBUG_INFO)
|
|
+ dev_info(&(i2c->adap.dev),
|
|
+ "%s, Before send read cmd, "
|
|
+ "need_send = %d, left_send = %d\n",
|
|
+ __func__, count ,i2c->rd_len);
|
|
+#endif
|
|
+
|
|
+ i2c->rd_len -= count;
|
|
+
|
|
+ if (!i2c->rd_len) {
|
|
+ while (count > 1) {
|
|
+ i2c_writel(i2c, I2C_DC, I2C_DC_READ);
|
|
+ count--;
|
|
+ }
|
|
+ if (i2c->r_end_type == MSG_END_STOP) {
|
|
+ i2c_writel(i2c, I2C_DC, I2C_DC_READ | I2C_DC_STP);
|
|
+ } else {
|
|
+ i2c_writel(i2c, I2C_DC, I2C_DC_READ);
|
|
+ }
|
|
+ } else {
|
|
+ while (count > 0) {
|
|
+ i2c_writel(i2c, I2C_DC, I2C_DC_READ);
|
|
+ count--;
|
|
+ }
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if(i2c->debug > DEBUG_INFO)
|
|
+ dev_info(&(i2c->adap.dev),
|
|
+ "%s, After send read cmd, "
|
|
+ "left_send = %d\n",
|
|
+ __func__, i2c->rd_len);
|
|
+#endif
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+static ssize_t enable_debug(struct device *dev, struct device_attribute *attr,
|
|
+ const char *buf, size_t size)
|
|
+{
|
|
+ int rc;
|
|
+ unsigned long enable;
|
|
+ struct i2c_ingenic *i2c = (struct i2c_ingenic *)dev_get_drvdata(dev);
|
|
+
|
|
+ rc = kstrtol(buf, 0, &enable);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ if ((enable >= 0) && (enable <= DEBUG_INFO + 1))
|
|
+ i2c->debug = enable;
|
|
+ else
|
|
+ goto err;
|
|
+
|
|
+ return size;
|
|
+err:
|
|
+ pr_err("Please input correct number(enable >= 0 && enable <= 5)"
|
|
+ " to disable or enable debug info print\n");
|
|
+ return -EAGAIN;
|
|
+}
|
|
+
|
|
+static struct device_attribute attributes[] = {
|
|
+ __ATTR(debug_info, 0200, NULL, enable_debug),
|
|
+};
|
|
+
|
|
+static int create_debug_sysfs_interface(struct device *dev)
|
|
+{
|
|
+ int i;
|
|
+ for (i = 0; i < ARRAY_SIZE(attributes); i++)
|
|
+ if (device_create_file(dev, attributes + i))
|
|
+ goto err;
|
|
+ return 0;
|
|
+
|
|
+err:
|
|
+ for( ; i >= 0; i--)
|
|
+ device_remove_file(dev, attributes + i);
|
|
+ return -1;
|
|
+}
|
|
+#endif
|
|
+
|
|
+static irqreturn_t i2c_ingenic_irq(int irqno, void *dev_id)
|
|
+{
|
|
+ unsigned short tmp, intst, intmsk;
|
|
+ struct i2c_ingenic *i2c = dev_id;
|
|
+
|
|
+ intst = i2c_readl(i2c, I2C_INTST);
|
|
+ intmsk = i2c_readl(i2c, I2C_INTM);
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if (i2c->debug > DEBUG_INFO)
|
|
+ dev_info(&(i2c->adap.dev), "--I2C irq register INTST:0x%08x\n", intst);
|
|
+#endif
|
|
+
|
|
+ if ((intst & I2C_INTST_TXABT) && (intmsk & I2C_INTM_MTXABT)) {
|
|
+ dev_err(&(i2c->adap.dev),
|
|
+ "%s %d, I2C transfer error, ABORT interrupt\n",
|
|
+ __func__, __LINE__);
|
|
+ goto END_TRSF_IRQ_HND;
|
|
+ }
|
|
+
|
|
+ if ((intst & I2C_INTST_ISTP) && (intmsk & I2C_INTM_MISTP)) {
|
|
+ i2c_readl(i2c, I2C_CSTP); /* clear STP bit */
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if (i2c->debug > DEBUG_INFO) {
|
|
+ dev_info(&(i2c->adap.dev),
|
|
+ "%s, Now stop condition has occurred,"
|
|
+ "and left data length is %d\n",
|
|
+ __func__, i2c->len);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ if (i2c->len == 0)
|
|
+ goto END_TRSF_IRQ_HND;
|
|
+ }
|
|
+
|
|
+ if ((intmsk & I2C_INTM_MTXEMP) && (intst & I2C_INTST_TXEMP)) {
|
|
+ if (!i2c->len) {
|
|
+ if (i2c->w_end_type == MSG_END_REPEAT_START) {
|
|
+ goto END_TRSF_IRQ_HND;
|
|
+ } else {
|
|
+ tmp = i2c_readl(i2c, I2C_INTM);
|
|
+ tmp &= ~I2C_INTM_MTXEMP;
|
|
+ i2c_writel(i2c, I2C_INTM, tmp);
|
|
+ }
|
|
+ } else {
|
|
+ while ((i2c->len > 0) &&
|
|
+ (i2c_readl(i2c, I2C_STA) & I2C_STA_TFNF)) {
|
|
+ tmp = *i2c->wbuf++;
|
|
+ if (i2c->len == 1) {
|
|
+ if (i2c->w_end_type == MSG_END_STOP)
|
|
+ tmp |= I2C_DC_STP;
|
|
+ }
|
|
+
|
|
+ i2c_writel(i2c, I2C_DC, tmp);
|
|
+ i2c->len -= 1;
|
|
+ }
|
|
+
|
|
+ if (i2c->len == 0) {
|
|
+ i2c_writel(i2c, I2C_TXTL, 0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((intst & I2C_INTST_RXFL) && (intmsk & I2C_INTM_MRXFL)) {
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if (i2c->debug >= DEBUG_INFO) {
|
|
+ dev_info(&(i2c->adap.dev),
|
|
+ "%s, Before read I2C_DC, "
|
|
+ "left_send_cmd = %d, left_read_cnt = %d,"
|
|
+ " rx_valid = %d, tx_valid = %d\n",
|
|
+ __func__, i2c->rd_len, i2c->len,
|
|
+ i2c_readl(i2c, I2C_RXFLR), i2c_readl(i2c, I2C_TXFLR));
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ while ((i2c_readl(i2c, I2C_STA) & I2C_STA_RFNE) &&
|
|
+ (i2c->len > 0)) {
|
|
+ tmp = i2c_readl(i2c, I2C_DC) & 0xff;
|
|
+ *i2c->rbuf++ = tmp;
|
|
+ i2c->len--;
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if (i2c->debug >= DEBUG_INFO)
|
|
+ dev_info(&(i2c->adap.dev),
|
|
+ "%s, After read I2C_DC, "
|
|
+ "left_read_cnt = %d,"
|
|
+ " rx_valid = %d, tx_valid = %d\n",
|
|
+ __func__, i2c->len,
|
|
+ i2c_readl(i2c, I2C_RXFLR), i2c_readl(i2c, I2C_TXFLR));
|
|
+#endif
|
|
+
|
|
+ if (i2c->len == 0) {
|
|
+ goto END_RECE_IRQ_HND;
|
|
+ }
|
|
+
|
|
+ if (i2c->len <= I2C_FIFO_LEN) {
|
|
+ i2c_writel(i2c, I2C_RXTL, i2c->len - 1);
|
|
+ }
|
|
+
|
|
+ if (i2c_send_rcmd(i2c)) {
|
|
+ dev_err(&(i2c->adap.dev),
|
|
+ "%s %d, I2C controller has BUG,"
|
|
+ " RXFLR or TXFLR can not clear\n",
|
|
+ __func__, __LINE__);
|
|
+ BUG();
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if (i2c->debug > DEBUG_INFO)
|
|
+ dev_info(&(i2c->adap.dev),
|
|
+ "%s, After send read command, "
|
|
+ "left_send_cmd = %d, "
|
|
+ "left_read_cnt = %d\n",
|
|
+ __func__, i2c->rd_len, i2c->len);
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ if ((intst & I2C_INTST_RXOF) && (intmsk & I2C_INTM_MRXOF)) {
|
|
+ dev_err(&(i2c->adap.dev),
|
|
+ "%s %d, I2C transfer error, RXFIFO over full\n",
|
|
+ __func__, __LINE__);
|
|
+ i2c_readl(i2c, I2C_CRXOF); /* clear RXOF bit */
|
|
+ }
|
|
+
|
|
+ if ((intst & I2C_INTST_TXOF) && (intmsk & I2C_INTM_MTXOF)) {
|
|
+ dev_err(&(i2c->adap.dev),
|
|
+ "%s %d, I2C transfer error, TXFIFO over full\n",
|
|
+ __func__, __LINE__);
|
|
+ i2c_readl(i2c, I2C_CTXOF); /* clear TXOF bit */
|
|
+ goto END_TRSF_IRQ_HND;
|
|
+ }
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+END_RECE_IRQ_HND:
|
|
+END_TRSF_IRQ_HND:
|
|
+ i2c_writel(i2c, I2C_INTM, 0);
|
|
+ complete(&i2c->complete);
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static void txabrt(struct i2c_ingenic *i2c, int src)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ dev_err(&(i2c->adap.dev), "--I2C txabrt:\n");
|
|
+ for (i = 0; i < 16; i++) {
|
|
+ if (src & (0x1 << i))
|
|
+ dev_info(&(i2c->adap.dev), "--I2C TXABRT[%d]=%s\n", i,
|
|
+ abrt_src[i]);
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline int xfer_read(struct i2c_ingenic *i2c, unsigned char *buf, int len,
|
|
+ enum msg_end_type end_type)
|
|
+{
|
|
+ int ret = 0;
|
|
+ long timeout;
|
|
+ unsigned short tmp;
|
|
+ unsigned int wait_complete_timeout_ms;
|
|
+
|
|
+ wait_complete_timeout_ms =
|
|
+ len * 1000 * 9 * 2 / i2c->rate + i2c->timeout_ms;
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if (i2c->debug > DEBUG_WARN)
|
|
+ dev_info(&(i2c->adap.dev),
|
|
+ "%s, Begin read msg, want to read length is %d\n",
|
|
+ __func__, len);
|
|
+ memset(buf, 0, len);
|
|
+#endif
|
|
+
|
|
+ i2c->rd_len = len;
|
|
+ i2c->len = len;
|
|
+ i2c->rbuf = buf;
|
|
+ i2c->r_end_type = end_type;
|
|
+
|
|
+ i2c_readl(i2c, I2C_CSTP); /* clear STP bit */
|
|
+ i2c_readl(i2c, I2C_CTXOF); /* clear TXOF bit */
|
|
+ i2c_readl(i2c, I2C_CTXABRT); /* clear TXABRT bit */
|
|
+
|
|
+ if (len <= I2C_FIFO_LEN) {
|
|
+ i2c_writel(i2c, I2C_RXTL, len - 1);
|
|
+ } else {
|
|
+ i2c_writel(i2c, I2C_RXTL, RX_LEVEL);
|
|
+ }
|
|
+
|
|
+ while (i2c_readl(i2c, I2C_STA) & I2C_STA_RFNE) {
|
|
+ i2c_readl(i2c, I2C_DC);
|
|
+ }
|
|
+ if (i2c_send_rcmd(i2c))
|
|
+ BUG();
|
|
+
|
|
+ tmp = I2C_INTM_MRXFL | I2C_INTM_MTXABT;
|
|
+ if (end_type == MSG_END_STOP)
|
|
+ tmp |= I2C_INTM_MISTP;
|
|
+ i2c_writel(i2c, I2C_INTM, tmp);
|
|
+
|
|
+
|
|
+ timeout = wait_for_completion_timeout(&i2c->complete,
|
|
+ msecs_to_jiffies
|
|
+ (wait_complete_timeout_ms));
|
|
+ if (!timeout) {
|
|
+ dev_err(&(i2c->adap.dev), "--I2C irq read timeout\n");
|
|
+ i2c_ingenic_dump_regs(i2c);
|
|
+ ret = -ETIMEDOUT;
|
|
+ }
|
|
+
|
|
+ tmp = i2c_readl(i2c, I2C_TXABRT);
|
|
+ if (tmp) {
|
|
+ txabrt(i2c, tmp);
|
|
+ if (tmp > 0x1 && tmp < 0x10)
|
|
+ ret = -ENXIO;
|
|
+ else
|
|
+ ret = -EIO;
|
|
+ // ABRT_GCALL_READ
|
|
+ if (tmp & (1 << 5)) {
|
|
+ ret = -EAGAIN;
|
|
+ }
|
|
+ i2c_readl(i2c, I2C_CTXABRT);
|
|
+ }
|
|
+
|
|
+ if (ret < 0)
|
|
+ i2c_ingenic_reset(i2c);
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if (i2c->debug > DEBUG_WARN)
|
|
+ dev_info(&(i2c->adap.dev),
|
|
+ "%s, Reading msg over\n", __func__);
|
|
+#endif
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static inline int xfer_write(struct i2c_ingenic *i2c, unsigned char *buf, int len,
|
|
+ enum msg_end_type end_type)
|
|
+{
|
|
+ int ret = 0;
|
|
+ long timeout = TIMEOUT;
|
|
+ unsigned short reg_tmp;
|
|
+ unsigned int wait_complete_timeout_ms;
|
|
+
|
|
+ wait_complete_timeout_ms =
|
|
+ len * 1000 * 9 * 2 / i2c->rate + i2c->timeout_ms;
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if (i2c->debug > DEBUG_WARN)
|
|
+ dev_info(&(i2c->adap.dev),
|
|
+ "%s, Begin write msg, want to write length is %d\n",
|
|
+ __func__, len);
|
|
+#endif
|
|
+ i2c->wbuf = buf;
|
|
+ i2c->len = len;
|
|
+
|
|
+ i2c_writel(i2c, I2C_TXTL, TX_LEVEL);
|
|
+
|
|
+ i2c_readl(i2c, I2C_CSTP); /* clear STP bit */
|
|
+ i2c_readl(i2c, I2C_CTXOF); /* clear TXOF bit */
|
|
+ i2c_readl(i2c, I2C_CTXABRT); /* clear TXABRT bit */
|
|
+
|
|
+ i2c->w_end_type = end_type;
|
|
+ while ((i2c_readl(i2c, I2C_STA) & I2C_STA_TFNF) && (i2c->len > 0)) {
|
|
+ reg_tmp = *i2c->wbuf++;
|
|
+ if (i2c->len == 1) {
|
|
+ if (end_type == MSG_END_STOP) {
|
|
+ reg_tmp |= I2C_DC_STP;
|
|
+ }
|
|
+ }
|
|
+ i2c_writel(i2c, I2C_DC, reg_tmp);
|
|
+
|
|
+ i2c->len -= 1;
|
|
+ }
|
|
+
|
|
+ if (i2c->len == 0) {
|
|
+ i2c_writel(i2c, I2C_TXTL, 0);
|
|
+ }
|
|
+
|
|
+ reg_tmp = I2C_INTM_MTXEMP | I2C_INTM_MTXABT | I2C_INTM_MTXOF;
|
|
+ if (end_type == MSG_END_STOP)
|
|
+ reg_tmp |= I2C_INTM_MISTP;
|
|
+
|
|
+ i2c_writel(i2c, I2C_INTM, reg_tmp);
|
|
+
|
|
+ timeout = wait_for_completion_timeout(&i2c->complete,
|
|
+ msecs_to_jiffies
|
|
+ (wait_complete_timeout_ms));
|
|
+
|
|
+ if (!timeout) {
|
|
+ dev_err(&(i2c->adap.dev), "--I2C pio write wait timeout\n");
|
|
+ i2c_ingenic_dump_regs(i2c);
|
|
+ ret = -ETIMEDOUT;
|
|
+ }
|
|
+
|
|
+ reg_tmp = i2c_readl(i2c, I2C_TXABRT);
|
|
+ if (reg_tmp) {
|
|
+ txabrt(i2c, reg_tmp);
|
|
+ if (reg_tmp > 0x1 && reg_tmp < 0x10)
|
|
+ ret = -ENXIO;
|
|
+ else
|
|
+ ret = -EIO;
|
|
+ //after I2C_TXABRT_ABRT_XDATA_NOACK error,this required core to resend
|
|
+ if (reg_tmp & 8) {
|
|
+ ret = -EAGAIN;
|
|
+ }
|
|
+ i2c_readl(i2c, I2C_CTXABRT);
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if (i2c->debug > DEBUG_WARN)
|
|
+ dev_info(&(i2c->adap.dev),
|
|
+ "%s, Write msg over\n", __func__);
|
|
+#endif
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int i2c_disable_clk(struct i2c_ingenic *i2c)
|
|
+{
|
|
+ int timeout = 10;
|
|
+ int tmp = i2c_readl(i2c, I2C_STA);
|
|
+
|
|
+ while ((tmp & I2C_STA_MSTACT) && (--timeout > 0)) {
|
|
+ udelay(90);
|
|
+ tmp = i2c_readl(i2c, I2C_STA);
|
|
+ }
|
|
+ if (timeout > 0) {
|
|
+ clk_disable_unprepare(i2c->clk);
|
|
+ return 0;
|
|
+ } else {
|
|
+ dev_err(&(i2c->adap.dev),
|
|
+ "--I2C disable clk timeout, I2C_STA = 0x%x\n", tmp);
|
|
+ i2c_ingenic_reset(i2c);
|
|
+ clk_disable_unprepare(i2c->clk);
|
|
+ return -ETIMEDOUT;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int i2c_ingenic_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int count)
|
|
+{
|
|
+ int i, ret = 0;
|
|
+ struct i2c_ingenic *i2c = adap->algo_data;
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if (i2c->debug > DEBUG_WARN) {
|
|
+ dev_info(&(i2c->adap.dev),
|
|
+ "\n\n\n%s, Begin master xfer, want to transfer msg count is %d\n",
|
|
+ __func__, count);
|
|
+ }
|
|
+#endif
|
|
+ clk_prepare_enable(i2c->clk);
|
|
+
|
|
+ i2c_writel(i2c, I2C_TAR, msg->addr);
|
|
+
|
|
+ for (i = 0; i < count; i++, msg++) {
|
|
+ enum msg_end_type end_type = MSG_END_STOP;
|
|
+ if (i < (count - 1)) {
|
|
+ if (msg[i + 1].flags & I2C_M_NOSTART) {
|
|
+ end_type = MSG_END_CONTINUE; /* have no STOP and START */
|
|
+ } else {
|
|
+ end_type = MSG_END_REPEAT_START; /* have no STOP but have RESTART */
|
|
+ }
|
|
+ }
|
|
+
|
|
+ reinit_completion(&i2c->complete);
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if (i2c->debug > DEBUG_WARN)
|
|
+ dev_info(&(i2c->adap.dev),
|
|
+ "%s, Now transfer msg: %d\n", __func__, i);
|
|
+#endif
|
|
+ if (msg->flags & I2C_M_RD) {
|
|
+ ret = xfer_read(i2c, msg->buf, msg->len, end_type);
|
|
+ } else {
|
|
+ ret = xfer_write(i2c, msg->buf, msg->len, end_type);
|
|
+ }
|
|
+ if (ret < 0) {
|
|
+ i2c_ingenic_reset(i2c);
|
|
+ clk_disable_unprepare(i2c->clk);
|
|
+ goto ERR;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (i2c_disable_clk(i2c)) {
|
|
+ ret = -ETIMEDOUT;
|
|
+ goto ERR;
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ if (i2c->debug > DEBUG_WARN)
|
|
+ dev_info(&(i2c->adap.dev),
|
|
+ "%s, Transfer msg over\n\n\n", __func__);
|
|
+#endif
|
|
+
|
|
+ERR:
|
|
+ return ret ? : i;
|
|
+}
|
|
+
|
|
+static u32 i2c_ingenic_functionality(struct i2c_adapter *adap)
|
|
+{
|
|
+ unsigned int ret;
|
|
+ ret = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct i2c_algorithm i2c_ingenic_algorithm = {
|
|
+ .master_xfer = i2c_ingenic_xfer,
|
|
+ .functionality = i2c_ingenic_functionality,
|
|
+};
|
|
+
|
|
+static int i2c_set_speed(struct i2c_ingenic *i2c, int rate)
|
|
+{
|
|
+ long dev_clk = clk_get_rate(i2c->clk);
|
|
+ long cnt_high = 0; /* HIGH period count of the SCL clock */
|
|
+ long cnt_low = 0; /* LOW period count of the SCL clock */
|
|
+ long setup_time = 0;
|
|
+ long hold_time = 0;
|
|
+ unsigned short tmp;
|
|
+
|
|
+ i2c->rate = rate;
|
|
+ if (i2c_ingenic_enable(i2c, 0))
|
|
+ dev_info(&(i2c->adap.dev), "i2c not disable\n");
|
|
+ if (rate <= 100000) {
|
|
+ tmp = 0x43 | (1 << 5); /* standard speed mode */
|
|
+ i2c_writel(i2c, I2C_CTRL, tmp);
|
|
+ } else {
|
|
+ tmp = 0x45 | (1 << 5); /* fast speed mode */
|
|
+ i2c_writel(i2c, I2C_CTRL, tmp);
|
|
+ }
|
|
+
|
|
+ /* high
|
|
+ * ____ ____ ____ ____
|
|
+ * clk __| | |___| |____| |____| |___
|
|
+ * | | |
|
|
+ * | | |
|
|
+ * |_|_| _________ ____
|
|
+ * data __/ | |\___/ \____/ \____
|
|
+ * setup->| |<|
|
|
+ * ->| |<-hold
|
|
+ */
|
|
+
|
|
+ //setup_time = (10 000 000/(rate*4)) + 1;
|
|
+ setup_time = (dev_clk / (rate * 4));
|
|
+ if (setup_time > 1)
|
|
+ setup_time -= 1;
|
|
+ //hold_time = (10000000/(rate*4)) - 1;
|
|
+ hold_time = (dev_clk / (rate * 4));
|
|
+
|
|
+ /* high
|
|
+ * ____ ____
|
|
+ * clk __| |___| |____
|
|
+ * low
|
|
+ * |<--period--->|
|
|
+ *
|
|
+ */
|
|
+ cnt_high = dev_clk / (rate * 2);
|
|
+ cnt_low = dev_clk / (rate * 2);
|
|
+
|
|
+ /*dev_info(&(i2c->adap.dev), "set:%ld hold:%ld dev=%ld h=%ld l=%ld\n",
|
|
+ setup_time, hold_time, dev_clk, cnt_high, cnt_low);*/
|
|
+ if (setup_time > 255)
|
|
+ setup_time = 255;
|
|
+ if (setup_time <= 0)
|
|
+ setup_time = 1;
|
|
+ if (hold_time > 0xFFFF)
|
|
+ hold_time = 0xFFFF;
|
|
+
|
|
+ if (rate <= 100000) {
|
|
+ i2c_writel(i2c, I2C_SHCNT, I2CSHCNT_ADJUST(cnt_high));
|
|
+ i2c_writel(i2c, I2C_SLCNT, I2CSLCNT_ADJUST(cnt_low));
|
|
+ } else {
|
|
+ i2c_writel(i2c, I2C_FHCNT, I2CFHCNT_ADJUST(cnt_high));
|
|
+ i2c_writel(i2c, I2C_FLCNT, I2CFLCNT_ADJUST(cnt_low));
|
|
+ }
|
|
+
|
|
+ i2c_writel(i2c, I2C_SDASU, setup_time & 0xff);
|
|
+ i2c_writel(i2c, I2C_SDAHD, hold_time);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifndef CONFIG_I2C_INGENICV10_WAIT_MS
|
|
+#define DEF_INGENIC_I2C_WAIT_TIMEOUT_MS (1000)
|
|
+#else
|
|
+#define DEF_INGENIC_I2C_WAIT_TIMEOUT_MS (CONFIG_I2C_INGENICV10_WAIT_MS)
|
|
+#endif
|
|
+
|
|
+#define DEF_INGENIC_I2C_SPEED_HZ (400000)
|
|
+static void ingenic_i2c_parse_dt(struct device_node *np, struct i2c_ingenic *i2c)
|
|
+{
|
|
+ u32 timeout, speed;
|
|
+ int id;
|
|
+
|
|
+ if (!of_property_read_u32(np, "timeout", &timeout))
|
|
+ i2c->timeout_ms = timeout;
|
|
+ else
|
|
+ i2c->timeout_ms = DEF_INGENIC_I2C_WAIT_TIMEOUT_MS;
|
|
+
|
|
+ if (!of_property_read_u32(np, "clock-frequency", &speed))
|
|
+ i2c->speed_hz = speed;
|
|
+ else
|
|
+ i2c->speed_hz = DEF_INGENIC_I2C_SPEED_HZ;
|
|
+
|
|
+ if ((id = of_alias_get_id(np, "i2c")) >= 0)
|
|
+ i2c->id = id;
|
|
+ else
|
|
+ i2c->id = -1;
|
|
+}
|
|
+
|
|
+
|
|
+static int i2c_ingenic_probe(struct platform_device *pdev)
|
|
+{
|
|
+ int ret = 0;
|
|
+ struct i2c_ingenic *i2c;
|
|
+ struct resource *res;
|
|
+ unsigned int reg_tmp;
|
|
+ char name[20];
|
|
+ int i2c_id;
|
|
+
|
|
+ i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
|
|
+ if (!i2c)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ i2c->dev = &pdev->dev;
|
|
+ if (!pdev->dev.of_node) {
|
|
+ i2c->timeout_ms = DEF_INGENIC_I2C_WAIT_TIMEOUT_MS;
|
|
+ i2c->speed_hz = DEF_INGENIC_I2C_SPEED_HZ;
|
|
+ i2c->id = pdev->id;
|
|
+ res = platform_get_resource(pdev, IORESOURCE_BUS, 0);
|
|
+ if (res)
|
|
+ i2c->speed_hz = res->start * 1000;
|
|
+ } else
|
|
+ ingenic_i2c_parse_dt(pdev->dev.of_node, i2c);
|
|
+
|
|
+ i2c->adap.owner = THIS_MODULE;
|
|
+ i2c->adap.algo = &i2c_ingenic_algorithm;
|
|
+ i2c->adap.retries = 5;
|
|
+ i2c->adap.timeout = 5;
|
|
+ i2c->adap.algo_data = i2c;
|
|
+ i2c->adap.dev.parent = &pdev->dev;
|
|
+ i2c->adap.nr = i2c->id;
|
|
+
|
|
+ i2c->adap.dev.of_node = pdev->dev.of_node;
|
|
+
|
|
+ i2c_id = i2c->id;
|
|
+ if (i2c_id >= 0)
|
|
+ sprintf(i2c->adap.name, "i2c%u", i2c->id);
|
|
+ else
|
|
+ sprintf(i2c->adap.name, "%s", dev_name(&pdev->dev));
|
|
+
|
|
+ sprintf(name, "gate_%s", i2c->adap.name);
|
|
+
|
|
+ i2c->clk = devm_clk_get(&pdev->dev, name);
|
|
+ if (!i2c->clk)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if(clk_prepare(i2c->clk) < 0) {
|
|
+ dev_err(&pdev->dev, "failed to prepare for clk %s\n", __clk_get_name(i2c->clk));
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+ if (!res)
|
|
+ return -ENOENT;
|
|
+
|
|
+ i2c->iomem = devm_ioremap_resource(&pdev->dev, res);
|
|
+ if (IS_ERR(i2c->iomem))
|
|
+ return PTR_ERR(i2c->iomem);
|
|
+
|
|
+ i2c->irq = platform_get_irq(pdev, 0);
|
|
+ if (i2c->irq < 0)
|
|
+ return i2c->irq;
|
|
+
|
|
+ ret = devm_request_irq(&pdev->dev, i2c->irq,
|
|
+ i2c_ingenic_irq, 0, i2c->adap.name, i2c);
|
|
+ if (ret)
|
|
+ return -ENODEV;
|
|
+
|
|
+
|
|
+ clk_enable(i2c->clk);
|
|
+
|
|
+ i2c_set_speed(i2c, i2c->speed_hz);
|
|
+
|
|
+#if 0
|
|
+ reg_tmp = i2c_readl(i2c, I2C_DC);
|
|
+ reg_tmp &= ~I2C_DC_STP;
|
|
+ i2c_writel(i2c, I2C_DC, reg_tmp);
|
|
+#endif
|
|
+
|
|
+ reg_tmp = i2c_readl(i2c, I2C_CTRL);
|
|
+#if defined(CONFIG_I2C_NON_RESTART_MODE)
|
|
+ reg_tmp &= ~I2C_CTRL_REST;
|
|
+#else
|
|
+ reg_tmp |= I2C_CTRL_REST;
|
|
+#endif
|
|
+ i2c_writel(i2c, I2C_CTRL, reg_tmp);
|
|
+
|
|
+ // for jgao WHY?
|
|
+ // i2c_writel(i2c, I2C_FLT, 0xF); /*set filter*/
|
|
+
|
|
+ i2c_writel(i2c, I2C_INTM, 0x0);
|
|
+
|
|
+ platform_set_drvdata(pdev, i2c);
|
|
+
|
|
+ i2c_ingenic_enable(i2c, 1);
|
|
+
|
|
+ clk_disable_unprepare(i2c->clk);
|
|
+
|
|
+ init_completion(&i2c->complete);
|
|
+
|
|
+ ret = i2c_add_numbered_adapter(&i2c->adap);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&pdev->dev, KERN_INFO"I2C: Failed to add bus\n");
|
|
+ clk_disable_unprepare(i2c->clk);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_I2C_DEBUG_INFO
|
|
+ ret = create_debug_sysfs_interface(&pdev->dev);
|
|
+ if (ret < 0)
|
|
+ dev_err(&pdev->dev, "create debug sysfs interface failed\n");
|
|
+#endif
|
|
+ dev_info(&pdev->dev, "register i2c%d success.\n", i2c->id);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int i2c_ingenic_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct i2c_ingenic *i2c = platform_get_drvdata(pdev);
|
|
+ clk_disable_unprepare(i2c->clk);
|
|
+ i2c_del_adapter(&i2c->adap);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct of_device_id ingenic_i2c_dt_match[] = {
|
|
+ { .compatible = "ingenic,i2c" },
|
|
+ { .compatible = "ingenic,x2000-i2c" },
|
|
+ { .compatible = "ingenic,t40-i2c" },
|
|
+ {},
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, ingenic_i2c_dt_match);
|
|
+
|
|
+static struct platform_driver i2c_ingenic_driver = {
|
|
+ .probe = i2c_ingenic_probe,
|
|
+ .remove = i2c_ingenic_remove,
|
|
+ .driver = {
|
|
+ .name = "ingenic-i2c",
|
|
+ .owner = THIS_MODULE,
|
|
+ .of_match_table = of_match_ptr(ingenic_i2c_dt_match),
|
|
+ },
|
|
+};
|
|
+
|
|
+static int __init i2c_ingenic_init(void)
|
|
+{
|
|
+ return platform_driver_register(&i2c_ingenic_driver);
|
|
+}
|
|
+module_init(i2c_ingenic_init);
|
|
+
|
|
+static void __exit i2c_ingenic_exit(void)
|
|
+{
|
|
+ platform_driver_unregister(&i2c_ingenic_driver);
|
|
+}
|
|
+module_exit(i2c_ingenic_exit);
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_DESCRIPTION("i2c driver for INGENIC SoCs");
|