firmware/br-ext-chip-allwinner/board/v83x/kernel/patches/00000-drivers_i2c_busses_i2...

2582 lines
70 KiB
Diff
Raw Blame History

diff -drupN a/drivers/i2c/busses/i2c-sunxi.c b/drivers/i2c/busses/i2c-sunxi.c
--- a/drivers/i2c/busses/i2c-sunxi.c 1970-01-01 03:00:00.000000000 +0300
+++ b/drivers/i2c/busses/i2c-sunxi.c 2022-06-12 05:28:14.000000000 +0300
@@ -0,0 +1,2577 @@
+/*
+ * drivers/i2c/busses/i2c-sunxi.c
+ *
+ * Copyright (C) 2013 Allwinner.
+ * Pan Nan <pannan@reuuimllatech.com>
+ *
+ * SUNXI TWI Controller Driver
+ *
+ * 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.
+ *
+ * 2013.5.3 Mintow <duanmintao@allwinnertech.com>
+ * Adapt to all the new chip of Allwinner. Support sun8i/sun9i
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/clk/sunxi.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
+#include <asm/uaccess.h>
+#include <linux/time.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+
+#include "i2c-sunxi.h"
+#include <linux/regulator/consumer.h>
+
+/* For debug */
+#define I2C_ERR(fmt, arg...) pr_err("%s()%d - "fmt, __func__, __LINE__, ##arg)
+
+static u32 debug_mask = 1;
+#define dprintk(level_mask, fmt, arg...) \
+do { \
+ if (unlikely(debug_mask & level_mask)) \
+ pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg); \
+} while (0)
+
+#define SUNXI_I2C_OK 0
+#define SUNXI_I2C_FAIL -1
+#define SUNXI_I2C_RETRY -2
+#define SUNXI_I2C_SFAIL -3 /* start fail */
+#define SUNXI_I2C_TFAIL -4 /* stop fail */
+
+#define DMA_THRESHOLD 32
+#define MAX_FIFO 32
+#define DMA_TIMEOUT 1000
+
+static int twi_regulator_enable(struct sunxi_i2c_platform_data *pdata);
+static int twi_regulator_disable(struct sunxi_i2c_platform_data *pdata);
+
+/* I2C transfer status */
+enum {
+ I2C_XFER_IDLE = 0x1,
+ I2C_XFER_START = 0x2,
+ I2C_XFER_RUNNING = 0x4,
+};
+
+struct sunxi_i2c_dma {
+ struct dma_chan *chan;
+ dma_addr_t dma_buf;
+ unsigned int dma_len;
+ enum dma_transfer_direction dma_transfer_dir;
+ enum dma_data_direction dma_data_dir;
+};
+
+struct sunxi_i2c {
+ struct platform_device *pdev;
+ int bus_num;
+ unsigned int status; /* start, running, idle */
+ struct i2c_adapter adap;
+ struct device *dev;
+
+ spinlock_t lock; /* syn */
+ wait_queue_head_t wait;
+ struct completion cmd_complete;
+
+ struct i2c_msg *msg;
+ unsigned int msg_num;
+ unsigned int msg_idx;
+ unsigned int msg_ptr;
+
+ struct clk *mclk;
+
+ unsigned int bus_freq;
+ int irq;
+ unsigned int debug_state; /* log the twi machine state */
+
+ struct resource *res;
+ void __iomem *base_addr;
+
+ struct pinctrl *pctrl;
+ unsigned int twi_drv_used;
+ unsigned int no_suspend;
+ unsigned int pkt_interval;
+
+ unsigned char result;
+ struct sunxi_i2c_dma *dma_tx;
+ struct sunxi_i2c_dma *dma_rx;
+ struct sunxi_i2c_dma *dma_using;
+
+};
+
+#if (!defined(CONFIG_ARCH_SUN8IW16) && !defined(CONFIG_ARCH_SUN8IW19))
+void dump_reg(struct sunxi_i2c *i2c, u32 offset, u32 len)
+{
+ u32 i;
+ u8 buf[64], cnt = 0;
+
+ for (i = 0; i < len; i = i + REG_INTERVAL) {
+ if (i%HEXADECIMAL == 0)
+ cnt += sprintf(buf + cnt, "0x%08x: ",
+ (u32)(i2c->res->start + offset + i));
+
+ cnt += sprintf(buf + cnt, "%08x ",
+ readl(i2c->base_addr + offset + i));
+
+ if (i%HEXADECIMAL == REG_CL) {
+ pr_warn("%s\n", buf);
+ cnt = 0;
+ }
+ }
+}
+#endif
+
+/* clear the interrupt flag */
+static inline void twi_clear_irq_flag(void __iomem *base_addr)
+{
+ unsigned int reg_val = readl(base_addr + TWI_CTL_REG);
+ /* start and stop bit should be 0 */
+ reg_val |= TWI_CTL_INTFLG;
+ reg_val &= ~(TWI_CTL_STA | TWI_CTL_STP);
+ writel(reg_val, base_addr + TWI_CTL_REG);
+ /* read two more times to make sure that */
+ /* interrupt flag does really be cleared */
+ {
+ unsigned int temp;
+
+ temp = readl(base_addr + TWI_CTL_REG);
+ temp |= readl(base_addr + TWI_CTL_REG);
+ }
+}
+
+/* get data first, then clear flag */
+static inline void
+twi_get_byte(void __iomem *base_addr, unsigned char *buffer)
+{
+ *buffer = (unsigned char)(TWI_DATA_MASK &
+ readl(base_addr + TWI_DATA_REG));
+ twi_clear_irq_flag(base_addr);
+}
+
+/* only get data, we will clear the flag when stop */
+static inline void
+twi_get_last_byte(void __iomem *base_addr, unsigned char *buffer)
+{
+ *buffer = (unsigned char)(TWI_DATA_MASK &
+ readl(base_addr + TWI_DATA_REG));
+}
+
+/* write data and clear irq flag to trigger send flow */
+static inline void
+twi_put_byte(void __iomem *base_addr, const unsigned char *buffer)
+{
+ writel((unsigned int)*buffer, base_addr + TWI_DATA_REG);
+ twi_clear_irq_flag(base_addr);
+}
+
+static inline void twi_enable_irq(void __iomem *base_addr)
+{
+ unsigned int reg_val = readl(base_addr + TWI_CTL_REG);
+
+ /*
+ * 1 when enable irq for next operation, set intflag to 0 to prevent
+ * to clear it by a mistake (intflag bit is write-1-to-clear bit)
+ * 2 Similarly, mask START bit and STOP bit to prevent to set it
+ * twice by a mistake (START bit and STOP bit are self-clear-to-0 bits)
+ */
+ reg_val |= TWI_CTL_INTEN;
+ reg_val &= ~(TWI_CTL_STA | TWI_CTL_STP | TWI_CTL_INTFLG);
+ writel(reg_val, base_addr + TWI_CTL_REG);
+}
+
+static inline void twi_disable_irq(void __iomem *base_addr)
+{
+ unsigned int reg_val = readl(base_addr + TWI_CTL_REG);
+
+ reg_val &= ~TWI_CTL_INTEN;
+ reg_val &= ~(TWI_CTL_STA | TWI_CTL_STP | TWI_CTL_INTFLG);
+ writel(reg_val, base_addr + TWI_CTL_REG);
+}
+
+static inline void
+twi_disable(void __iomem *base_addr, unsigned int reg, unsigned int mask)
+{
+ unsigned int reg_val = readl(base_addr + reg);
+
+ reg_val &= ~mask;
+ writel(reg_val, base_addr + reg);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", reg,
+ readl(base_addr + reg));
+}
+
+static inline void
+twi_enable(void __iomem *base_addr, unsigned int reg, unsigned int mask)
+{
+ unsigned int reg_val = readl(base_addr + reg);
+
+ reg_val |= mask;
+ writel(reg_val, base_addr + reg);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", reg,
+ readl(base_addr + reg));
+}
+
+
+/* trigger start signal, the start bit will be cleared automatically */
+static inline void twi_set_start(void __iomem *base_addr)
+{
+ unsigned int reg_val = readl(base_addr + TWI_CTL_REG);
+
+ reg_val |= TWI_CTL_STA;
+ reg_val &= ~TWI_CTL_INTFLG;
+ writel(reg_val, base_addr + TWI_CTL_REG);
+}
+
+/* get start bit status, poll if start signal is sent */
+static inline unsigned int twi_get_start(void __iomem *base_addr)
+{
+ unsigned int reg_val = readl(base_addr + TWI_CTL_REG);
+
+ reg_val >>= 5;
+ return reg_val & 1;
+}
+
+/* trigger stop signal, the stop bit will be cleared automatically */
+static inline void twi_set_stop(void __iomem *base_addr)
+{
+ unsigned int reg_val = readl(base_addr + TWI_CTL_REG);
+
+ reg_val |= TWI_CTL_STP;
+ reg_val &= ~TWI_CTL_INTFLG;
+ writel(reg_val, base_addr + TWI_CTL_REG);
+}
+
+/* get stop bit status, poll if stop signal is sent */
+static inline unsigned int twi_get_stop(void __iomem *base_addr)
+{
+ unsigned int reg_val = readl(base_addr + TWI_CTL_REG);
+
+ reg_val >>= 4;
+ return reg_val & 1;
+}
+
+static inline void twi_disable_ack(void __iomem *base_addr)
+{
+ unsigned int reg_val = readl(base_addr + TWI_CTL_REG);
+
+ reg_val &= ~TWI_CTL_ACK;
+ reg_val &= ~TWI_CTL_INTFLG;
+ writel(reg_val, base_addr + TWI_CTL_REG);
+}
+
+/* when sending ack or nack, it will send ack automatically */
+static inline void twi_enable_ack(void __iomem *base_addr)
+{
+ unsigned int reg_val = readl(base_addr + TWI_CTL_REG);
+
+ reg_val |= TWI_CTL_ACK;
+ reg_val &= ~TWI_CTL_INTFLG;
+ writel(reg_val, base_addr + TWI_CTL_REG);
+}
+
+/* get the interrupt flag */
+static inline unsigned int twi_query_irq_flag(void __iomem *base_addr)
+{
+ unsigned int reg_val = readl(base_addr + TWI_CTL_REG);
+
+ return (reg_val & TWI_CTL_INTFLG);/* 0x 0000_1000 */
+}
+
+/* get interrupt status */
+static inline unsigned int twi_query_irq_status(void __iomem *base_addr)
+{
+ unsigned int reg_val = readl(base_addr + TWI_STAT_REG);
+
+ return (reg_val & TWI_STAT_MASK);
+}
+
+/* set twi clock
+ *
+ * clk_n: clock divider factor n
+ * clk_m: clock divider factor m
+ */
+static void twi_clk_write_reg(struct sunxi_i2c *i2c, unsigned int reg_clk,
+ unsigned int sclk_freq,
+ unsigned char clk_m, unsigned char clk_n,
+ unsigned int mask_clk_m, unsigned int mask_clk_n)
+{
+ unsigned int reg_val = readl(i2c->base_addr + reg_clk);
+#if defined(CONFIG_ARCH_SUN50IW10)
+ unsigned int duty;
+#endif
+ dprintk(DEBUG_INFO2, "[i2c%d] reg_clk = 0x%x, clk_m = %u, clk_n = %u,"
+ "mask_clk_m = %x, mask_clk_n = %x\n", i2c->bus_num,
+ reg_clk, clk_m, clk_n, mask_clk_m, mask_clk_n);
+ if (reg_clk == TWI_DRIVER_BUSC) {
+ reg_val &= ~(mask_clk_m | mask_clk_n);
+ reg_val |= ((clk_m | (clk_n << 4)) << 8);
+#if defined(CONFIG_ARCH_SUN50IW10)
+ duty = TWI_DRV_CLK_DUTY;
+ if (sclk_freq > STANDDARD_FREQ)
+ reg_val |= duty;
+ else
+ reg_val &= ~duty;
+#endif
+ writel(reg_val, i2c->base_addr + reg_clk);
+ dprintk(DEBUG_INFO2, "[i2c%d] reg: 0x%x value: 0x%x\n",
+ i2c->bus_num, reg_clk,
+ readl(i2c->base_addr + reg_clk));
+ } else {
+ reg_val &= ~(mask_clk_m | mask_clk_n);
+ reg_val |= ((clk_m << 3) | clk_n);
+#if defined(CONFIG_ARCH_SUN50IW10)
+ duty = TWI_CLK_DUTY;
+ if (sclk_freq > STANDDARD_FREQ)
+ reg_val |= duty;
+ else
+ reg_val &= ~duty;
+#endif
+ writel(reg_val, i2c->base_addr + reg_clk);
+ dprintk(DEBUG_INFO2, "[i2c%d] reg: 0x%x value: 0x%x\n",
+ i2c->bus_num, reg_clk,
+ readl(i2c->base_addr + reg_clk));
+ }
+}
+
+/*
+* Fin is APB CLOCK INPUT;
+* Fsample = F0 = Fin/2^CLK_N;
+* F1 = F0/(CLK_M+1);
+* Foscl = F1/10 = Fin/(2^CLK_N * (CLK_M+1)*10);
+* Foscl is clock SCL;100KHz or 400KHz
+*
+* clk_in: apb clk clock
+* sclk_freq: freqence to set in HZ
+*/
+static int twi_set_clock(struct sunxi_i2c *i2c, unsigned int reg_clk,
+ unsigned int clk_in, unsigned int sclk_freq,
+ unsigned int mask_clk_m, unsigned int mask_clk_n)
+{
+ /*
+ unsigned int clk_m = 0;
+ unsigned int clk_n = 0;
+ unsigned int _2_pow_clk_n = 1;
+ */
+ unsigned char clk_m = 0, clk_n = 0, _2_pow_clk_n = 1;
+ unsigned int src_clk = clk_in/10;
+ unsigned int divider = src_clk/sclk_freq; /* 400khz or 100khz */
+ unsigned int sclk_real = 0; /* the real clock frequency */
+
+ if (divider == 0) {
+ clk_m = 1;
+ goto set_clk;
+ }
+
+ /*
+ * search clk_n and clk_m,from large to small value so
+ * that can quickly find suitable m & n.
+ */
+ while (clk_n < 8) { /* 3bits max value is 8 */
+ /* (m+1)*2^n = divider -->m = divider/2^n -1 */
+ clk_m = (divider/_2_pow_clk_n) - 1;
+ /* clk_m = (divider >> (_2_pow_clk_n>>1))-1 */
+ while (clk_m < 16) { /* 4bits max value is 16 */
+ /* src_clk/((m+1)*2^n) */
+ sclk_real = src_clk/(clk_m + 1)/_2_pow_clk_n;
+ if (sclk_real <= sclk_freq)
+ goto set_clk;
+ else
+ clk_m++;
+ }
+ clk_n++;
+ _2_pow_clk_n *= 2; /* mutilple by 2 */
+ }
+
+set_clk:
+ twi_clk_write_reg(i2c, reg_clk, sclk_freq, clk_m, clk_n, mask_clk_m, mask_clk_n);
+ return 0;
+}
+
+/* soft reset twi */
+static inline void
+twi_soft_reset(void __iomem *base_addr, unsigned int reg, unsigned int mask)
+{
+ unsigned int reg_val = readl(base_addr + reg);
+
+ reg_val |= mask;
+ writel(reg_val, base_addr + reg);
+}
+
+/* Enhanced Feature Register */
+static inline void twi_set_efr(void __iomem *base_addr, unsigned int efr)
+{
+ unsigned int reg_val = readl(base_addr + TWI_EFR_REG);
+
+ reg_val &= ~TWI_EFR_MASK;
+ efr &= TWI_EFR_MASK;
+ reg_val |= efr;
+ writel(reg_val, base_addr + TWI_EFR_REG);
+}
+
+static int sunxi_i2c_xfer_complete(struct sunxi_i2c *i2c, int code);
+static int
+sunxi_i2c_do_xfer(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num);
+
+static int twi_select_gpio_state(struct pinctrl *pctrl, char *name, u32 no)
+{
+ int ret = 0;
+ struct pinctrl_state *pctrl_state = NULL;
+
+ pctrl_state = pinctrl_lookup_state(pctrl, name);
+ if (IS_ERR(pctrl_state)) {
+ I2C_ERR("TWI%d pinctrl_lookup_state(%s) failed! return %p\n",
+ no, name, pctrl_state);
+ return -1;
+ }
+
+ ret = pinctrl_select_state(pctrl, pctrl_state);
+ if (ret < 0)
+ I2C_ERR("TWI%d pinctrl_select_state(%s) failed! return %d\n",
+ no, name, ret);
+
+ return ret;
+}
+
+static int twi_request_gpio(struct sunxi_i2c *i2c)
+{
+ dprintk(DEBUG_INIT, "[i2c%d] init name: %s\n", i2c->bus_num,
+ i2c->adap.dev.parent->init_name);
+
+ i2c->pctrl = devm_pinctrl_get(i2c->adap.dev.parent);
+ if (IS_ERR(i2c->pctrl)) {
+ I2C_ERR("[i2c%d] pinctrl_get failed, return %ld\n",
+ i2c->bus_num, PTR_ERR(i2c->pctrl));
+ return -1;
+ }
+
+ return twi_select_gpio_state(i2c->pctrl, PINCTRL_STATE_DEFAULT,
+ i2c->bus_num);
+}
+
+static void twi_release_gpio(struct sunxi_i2c *i2c)
+{
+ devm_pinctrl_put(i2c->pctrl);
+}
+
+/* function */
+static int twi_start(void __iomem *base_addr, int bus_num)
+{
+ unsigned int timeout = 0xff;
+
+ twi_set_start(base_addr);
+ while ((twi_get_start(base_addr) == 1) && (--timeout))
+ ;
+ if (timeout == 0) {
+ I2C_ERR("[i2c%d] START can't sendout!\n", bus_num);
+ return SUNXI_I2C_FAIL;
+ }
+
+ return SUNXI_I2C_OK;
+}
+
+static int twi_restart(void __iomem *base_addr, int bus_num)
+{
+ unsigned int timeout = 0xff;
+
+ twi_set_start(base_addr);
+ twi_clear_irq_flag(base_addr);
+ while ((twi_get_start(base_addr) == 1) && (--timeout))
+ ;
+ if (timeout == 0) {
+ I2C_ERR("[i2c%d] Restart can't sendout!\n", bus_num);
+ return SUNXI_I2C_FAIL;
+ }
+
+ return SUNXI_I2C_OK;
+}
+
+static int twi_stop(void __iomem *base_addr, int bus_num)
+{
+ unsigned int timeout = 0xff;
+
+ twi_set_stop(base_addr);
+ twi_clear_irq_flag(base_addr);
+
+ twi_get_stop(base_addr);/* it must delay 1 nop to check stop bit */
+ while ((twi_get_stop(base_addr) == 1) && (--timeout))
+ ;
+ if (timeout == 0) {
+ I2C_ERR("[i2c%d] STOP can't sendout!\n", bus_num);
+ return SUNXI_I2C_TFAIL;
+ }
+
+ timeout = 0xff;
+ while ((readl(base_addr + TWI_STAT_REG) != TWI_STAT_IDLE)
+ && (--timeout))
+ ;
+ if (timeout == 0) {
+ I2C_ERR("[i2c%d] i2c state(0x%0x) isn't idle(0xf8)\n",
+ bus_num, readl(base_addr + TWI_STAT_REG));
+ return SUNXI_I2C_TFAIL;
+ }
+
+ timeout = 0xff;
+ while ((readl(base_addr + TWI_LCR_REG) != TWI_LCR_IDLE_STATUS
+ && readl(base_addr + TWI_LCR_REG) != TWI_LCR_NORM_STATUS)
+ && (--timeout))
+ ;
+
+ if (timeout == 0) {
+ I2C_ERR("[i2c%d] i2c lcr(0x%0x) isn't idle(0x3a)\n",
+ bus_num, readl(base_addr + TWI_LCR_REG));
+ return SUNXI_I2C_TFAIL;
+ }
+
+ return SUNXI_I2C_OK;
+}
+
+/* get SDA state */
+static unsigned int twi_get_sda(void __iomem *base_addr)
+{
+ unsigned int status = 0;
+
+ status = TWI_LCR_SDA_STATE_MASK & readl(base_addr + TWI_LCR_REG);
+ status >>= 4;
+ return (status&0x1);
+}
+
+/* set SCL level(high/low), only when SCL enable */
+static void twi_set_scl(void __iomem *base_addr, unsigned char hi_lo)
+{
+ unsigned int reg_val = readl(base_addr + TWI_LCR_REG);
+
+ reg_val &= ~TWI_LCR_SCL_CTL;
+ hi_lo &= 0x01;
+ reg_val |= (hi_lo<<3);
+ writel(reg_val, base_addr + TWI_LCR_REG);
+}
+
+/* enable SDA or SCL */
+static void twi_enable_lcr(void __iomem *base_addr, unsigned char sda_scl)
+{
+ unsigned int reg_val = readl(base_addr + TWI_LCR_REG);
+
+ sda_scl &= 0x01;
+ if (sda_scl)
+ reg_val |= TWI_LCR_SCL_EN;/* enable scl line control */
+ else
+ reg_val |= TWI_LCR_SDA_EN;/* enable sda line control */
+
+ writel(reg_val, base_addr + TWI_LCR_REG);
+}
+
+/* disable SDA or SCL */
+static void twi_disable_lcr(void __iomem *base_addr, unsigned char sda_scl)
+{
+ unsigned int reg_val = readl(base_addr + TWI_LCR_REG);
+
+ sda_scl &= 0x01;
+ if (sda_scl)
+ reg_val &= ~TWI_LCR_SCL_EN;/* disable scl line control */
+ else
+ reg_val &= ~TWI_LCR_SDA_EN;/* disable sda line control */
+
+ writel(reg_val, base_addr + TWI_LCR_REG);
+}
+
+/* send 9 clock to release sda */
+static int twi_send_clk_9pulse(void __iomem *base_addr, int bus_num)
+{
+ char twi_scl = 1, low = 0, high = 1, cycle = 0;
+ unsigned char status;
+
+ /* enable scl control */
+ twi_enable_lcr(base_addr, twi_scl);
+
+ while (cycle < 9) {
+ if (twi_get_sda(base_addr)
+ && twi_get_sda(base_addr)
+ && twi_get_sda(base_addr)) {
+ break;
+ }
+ /* twi_scl -> low */
+ twi_set_scl(base_addr, low);
+ udelay(1000);
+
+ /* twi_scl -> high */
+ twi_set_scl(base_addr, high);
+ udelay(1000);
+ cycle++;
+ }
+
+ if (twi_get_sda(base_addr)) {
+ twi_disable_lcr(base_addr, twi_scl);
+ status = SUNXI_I2C_OK;
+ } else {
+ I2C_ERR("[i2c%d] SDA is still Stuck Low, failed.\n", bus_num);
+ twi_disable_lcr(base_addr, twi_scl);
+ status = SUNXI_I2C_FAIL;
+ }
+
+ return status;
+}
+
+static int twi_regulator_request(struct sunxi_i2c_platform_data *pdata,
+ struct device *dev)
+{
+ struct regulator *regu = NULL;
+
+ if (pdata->regulator != NULL)
+ return 0;
+
+#ifdef CONFIG_SUNXI_REGULATOR_DT
+ regu = regulator_get(dev, "twi");
+ if (IS_ERR(regu)) {
+ I2C_ERR("[i2c%d] get supply failed!\n", pdata->bus_num);
+ return -EPROBE_DEFER;
+ }
+#else
+ /* Consider "n*" as nocare. Support "none", "nocare", "null", "" etc. */
+ if ((pdata->regulator_id[0] == 'n') || (pdata->regulator_id[0] == 0))
+ return 0;
+
+ regu = regulator_get(NULL, pdata->regulator_id);
+ if (IS_ERR(regu)) {
+ I2C_ERR("[i2c%d] get regulator %s failed!\n",
+ pdata->bus_num, pdata->regulator_id);
+ return -1;
+ }
+#endif
+ pdata->regulator = regu;
+
+ return 0;
+}
+
+static int twi_regulator_release(struct sunxi_i2c_platform_data *pdata)
+{
+ if (pdata->regulator == NULL)
+ return 0;
+
+ regulator_put(pdata->regulator);
+ pdata->regulator = NULL;
+
+ return 1;
+}
+
+static unsigned int twi_drv_query_irq_status(void __iomem *base_addr)
+{
+ unsigned int reg_val = readl(base_addr + TWI_DRIVER_INTC);
+
+ return (reg_val & TWI_DRV_STAT_MASK);
+}
+
+static void twi_drv_clear_irq_flag(u32 pending_bit, void __iomem *base_addr)
+{
+ u32 reg_val = readl(base_addr + TWI_DRIVER_INTC);
+
+ pending_bit &= TWI_DRV_STAT_MASK;
+ reg_val |= pending_bit;
+ writel(reg_val, base_addr + TWI_DRIVER_INTC);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_INTC,
+ readl(base_addr + TWI_DRIVER_INTC));
+}
+
+static void i2c_drv_clear_pending(void __iomem *base_addr)
+{
+ u32 reg_val = readl(base_addr + TWI_DRIVER_INTC);
+
+ reg_val |= TWI_DRV_STAT_MASK;
+ writel(reg_val, base_addr + TWI_DRIVER_INTC);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_INTC,
+ readl(base_addr + TWI_DRIVER_INTC));
+}
+
+/* start i2c transfer */
+static void i2c_start_xfer(void __iomem *base_addr)
+{
+ u32 reg_val = readl(base_addr + TWI_DRIVER_CTRL);
+
+ reg_val |= START_TRAN;
+ writel(reg_val, base_addr + TWI_DRIVER_CTRL);
+}
+
+/*
+ * send DMA RX Req when the data byte number in RECV_FIFO reaches RX_TRIG
+ * or Read Packet Tansmission completed with RECV_FIFO not empty
+ */
+static void i2c_set_rx_trig_level(u32 val, void __iomem *base_addr)
+{
+ u32 mask = TRIG_MASK;
+ u32 reg_val = readl(base_addr + TWI_DRIVER_DMAC);
+
+ val = (val & mask) << 16;
+ reg_val &= ~(mask << 16);
+ reg_val |= val;
+ writel(reg_val, base_addr + TWI_DRIVER_DMAC);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_DMAC,
+ readl(base_addr + TWI_DRIVER_DMAC));
+}
+
+/* bytes be send as slave device reg address */
+static void i2c_set_packet_addr_byte(u32 val, void __iomem *base_addr)
+{
+ u32 mask = ADDR_BYTE;
+ u32 reg_val = readl(base_addr + TWI_DRIVER_FMT);
+
+ reg_val &= ~mask;
+ val = (val << 16) & mask;
+ reg_val |= val;
+ writel(reg_val, base_addr + TWI_DRIVER_FMT);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_FMT,
+ readl(base_addr + TWI_DRIVER_FMT));
+}
+
+/* bytes be send/received as data */
+static void i2c_set_packet_data_byte(u32 val, void __iomem *base_addr)
+{
+ u32 mask = DATA_BYTE;
+ u32 reg_val = readl(base_addr + TWI_DRIVER_FMT);
+
+ reg_val &= ~mask;
+ val &= mask;
+ reg_val |= val;
+ writel(reg_val, base_addr + TWI_DRIVER_FMT);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_FMT,
+ readl(base_addr + TWI_DRIVER_FMT));
+
+}
+
+/* interval between each packet in 32*Fscl cycles */
+static void i2c_set_packet_interval(u32 val, void __iomem *base_addr)
+{
+ u32 mask = INTERVAL_MASK;
+ u32 reg_val = readl(base_addr + TWI_DRIVER_CFG);
+
+ reg_val &= ~mask;
+ val <<= 16;
+ val &= mask;
+ reg_val |= val;
+ writel(reg_val, base_addr + TWI_DRIVER_CFG);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_CFG,
+ readl(base_addr + TWI_DRIVER_CFG));
+}
+
+/* FIFO data be transmitted as PACKET_CNT packets in current format */
+static void i2c_set_packet_cnt(u32 val, void __iomem *base_addr)
+{
+ u32 mask = PACKET_MASK;
+ u32 reg_val = readl(base_addr + TWI_DRIVER_CFG);
+
+ reg_val &= ~mask;
+ val &= mask;
+ reg_val |= val;
+ writel(reg_val, base_addr + TWI_DRIVER_CFG);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_CFG,
+ readl(base_addr + TWI_DRIVER_CFG));
+}
+
+/* do not send slave_id +W */
+static void i2c_enable_read_tran_mode(void __iomem *base_addr)
+{
+ u32 mask = READ_TRAN;
+ u32 reg_val = readl(base_addr + TWI_DRIVER_CTRL);
+
+ reg_val |= mask;
+ writel(reg_val, base_addr + TWI_DRIVER_CTRL);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_CTRL,
+ readl(base_addr + TWI_DRIVER_CTRL));
+
+}
+
+/* send slave_id + W */
+static void i2c_disable_read_tran_mode(void __iomem *base_addr)
+{
+ u32 mask = READ_TRAN;
+ u32 reg_val = readl(base_addr + TWI_DRIVER_CTRL);
+
+ reg_val &= ~mask;
+ writel(reg_val, base_addr + TWI_DRIVER_CTRL);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_CTRL,
+ readl(base_addr + TWI_DRIVER_CTRL));
+
+}
+
+static void i2c_drv_enable_tran_irq(u32 bitmap, void __iomem *base_addr)
+{
+ u32 reg_val = readl(base_addr + TWI_DRIVER_INTC);
+
+ reg_val |= bitmap;
+ reg_val &= ~TWI_DRV_STAT_MASK;
+ writel(reg_val, base_addr + TWI_DRIVER_INTC);
+
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_INTC,
+ readl(base_addr + TWI_DRIVER_INTC));
+
+}
+
+static void i2c_drv_disable_tran_irq(u32 bitmap, void __iomem *base_addr)
+{
+ u32 reg_val = readl(base_addr + TWI_DRIVER_INTC);
+
+ reg_val &= ~bitmap;
+ reg_val &= ~TWI_DRV_STAT_MASK;
+ writel(reg_val, base_addr + TWI_DRIVER_INTC);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_INTC,
+ readl(base_addr + TWI_DRIVER_INTC));
+}
+
+static void i2c_drv_enable_dma_irq(u32 bitmap, void __iomem *base_addr)
+{
+ u32 reg_val = readl(base_addr + TWI_DRIVER_DMAC);
+
+ bitmap &= I2C_DRQEN_MASK;
+ reg_val |= bitmap;
+ writel(reg_val, base_addr + TWI_DRIVER_DMAC);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_DMAC,
+ readl(base_addr + TWI_DRIVER_DMAC));
+}
+
+static void i2c_drv_disable_dma_irq(u32 bitmap, void __iomem *base_addr)
+{
+ u32 reg_val = readl(base_addr + TWI_DRIVER_DMAC);
+
+ bitmap &= I2C_DRQEN_MASK;
+ reg_val &= ~bitmap;
+ writel(reg_val, base_addr + TWI_DRIVER_DMAC);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_DMAC,
+ readl(base_addr + TWI_DRIVER_DMAC));
+}
+
+static void
+sunxi_i2c_drv_slave_addr(struct sunxi_i2c *i2c, struct i2c_msg *msgs)
+{
+ unsigned int val = 0, cmd = 0;
+
+ /* read, default value is write */
+ if (msgs->flags & I2C_M_RD)
+ cmd = SLV_RD_CMD;
+
+ if (msgs->flags & I2C_M_TEN) {
+ /* SLV_ID | CMD | SLV_ID_X */
+ val = ((0x78 | ((msgs->addr >> 8) & 0x03)) << 9) | cmd
+ | (msgs->addr & 0xff);
+ dprintk(DEBUG_INFO2, "10bit addr\n");
+ } else {
+ val = ((msgs->addr & 0x7f) << 9) | cmd;
+ dprintk(DEBUG_INFO2, "7bit addr\n");
+ }
+
+ writel(val, i2c->base_addr + TWI_DRIVER_SLV);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_SLV,
+ readl(i2c->base_addr + TWI_DRIVER_SLV));
+
+}
+
+/* the number of data in SEND_FIFO */
+static int i2c_query_txfifo(void __iomem *base_addr)
+{
+ unsigned int reg_val;
+
+ reg_val = readl(base_addr + TWI_DRIVER_FIFOC) & SEND_FIFO_CONT;
+
+ return reg_val;
+}
+
+/* the number of data in RECV_FIFO */
+static int i2c_query_rxfifo(void __iomem *base_addr)
+{
+ unsigned int reg_val;
+
+ reg_val = readl(base_addr + TWI_DRIVER_FIFOC) & RECV_FIFO_CONT;
+ reg_val >>= 16;
+
+ return reg_val;
+}
+
+static void i2c_clear_txfifo(void __iomem *base_addr)
+{
+ unsigned int reg_val;
+
+ reg_val = readl(base_addr + TWI_DRIVER_FIFOC);
+ reg_val |= SEND_FIFO_CLEAR;
+ writel(reg_val, base_addr + TWI_DRIVER_FIFOC);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_FIFOC,
+ readl(base_addr + TWI_DRIVER_FIFOC));
+}
+
+static void i2c_clear_rxfifo(void __iomem *base_addr)
+{
+ unsigned int reg_val;
+
+ reg_val = readl(base_addr + TWI_DRIVER_FIFOC);
+ reg_val |= RECV_FIFO_CLEAR;
+ writel(reg_val, base_addr + TWI_DRIVER_FIFOC);
+ dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_FIFOC,
+ readl(base_addr + TWI_DRIVER_FIFOC));
+}
+
+static int i2c_sunxi_send_msgs(struct sunxi_i2c *i2c, struct i2c_msg *msgs)
+{
+ u16 i;
+ u8 time = 0xff;
+
+ dprintk(DEBUG_INFO, "[i2c%d] msgs->len = %d\n",
+ i2c->bus_num, msgs->len);
+
+ for (i = 0; i < msgs->len; i++) {
+ while ((i2c_query_txfifo(i2c->base_addr) >= MAX_FIFO) && time--)
+ ;
+ if (time) {
+ writeb(msgs->buf[i], i2c->base_addr + TWI_DRIVER_SENDF);
+ dprintk(DEBUG_INFO2, "[i2c%d] writeb: Byte[%u] = 0x%x,"
+ "fifo len = %d\n",
+ i2c->bus_num, i, msgs->buf[i],
+ i2c_query_txfifo(i2c->base_addr));
+ } else {
+ I2C_ERR("[i2c%d] SEND FIFO overflow. timeout\n",
+ i2c->bus_num);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int
+i2c_sunxi_recv_msgs(struct sunxi_i2c *i2c, struct i2c_msg *msgs)
+{
+ u16 i;
+ u8 time = 0xff;
+
+ dprintk(DEBUG_INFO, "[i2c%d] msgs->len = %d\n",
+ i2c->bus_num, msgs->len);
+
+ for (i = 0; i < msgs->len; i++) {
+ while (!i2c_query_rxfifo(i2c->base_addr) && time--)
+ ;
+ if (time) {
+ msgs->buf[i] = readb(i2c->base_addr + TWI_DRIVER_RECVF);
+ dprintk(DEBUG_INFO1, "[i2c%d] readb: Byte[%d] = 0x%x\n",
+ i2c->bus_num, i, msgs->buf[i]);
+ } else
+ return 0;
+ }
+ return msgs->len;
+}
+
+static int sunxi_i2c_drv_core_process(struct sunxi_i2c *i2c)
+{
+ void __iomem *base_addr = i2c->base_addr;
+ unsigned long flags = 0;
+ unsigned int status, code;
+
+ spin_lock_irqsave(&i2c->lock, flags);
+
+ status = twi_drv_query_irq_status(base_addr);
+ twi_drv_clear_irq_flag(status, base_addr);
+ dprintk(DEBUG_INFO, "[i2c%d] irq status = 0x%x\n",
+ i2c->bus_num, status);
+
+ if (status & TRAN_COM_PD) {
+ i2c_drv_disable_tran_irq(TRAN_COM_INT, i2c->base_addr);
+ i2c->result = RESULT_COMPLETE;
+ wake_up(&i2c->wait);
+ dprintk(DEBUG_INFO, "[i2c%d] packet transmission completed\n",
+ i2c->bus_num);
+
+ if ((status & RX_REQ_PD) && (i2c->msg->len < DMA_THRESHOLD)) {
+ i2c_sunxi_recv_msgs(i2c, i2c->msg);
+ }
+ }
+
+ if (status & TRAN_ERR_PD) {
+ i2c_drv_disable_tran_irq(TRAN_ERR_INT, i2c->base_addr);
+ code = readl(base_addr + TWI_DRIVER_CTRL);
+ code = (code & TWI_DRV_STA) >> 16;
+ switch (code) {
+ case 0x00:
+ I2C_ERR("[i2c%d] bus error\n", i2c->bus_num);
+ break;
+ case 0x01:
+ I2C_ERR("[i2c%d] Timeout when sending 9th SCL clk\n",
+ i2c->bus_num);
+ break;
+ case 0x20:
+ I2C_ERR("[i2c%d] Address + Write bit transmitted,"
+ "ACK not received\n", i2c->bus_num);
+ break;
+ case 0x30:
+ I2C_ERR("[i2c%d] Data byte transmitted in master mode,"
+ "ACK not received\n", i2c->bus_num);
+ break;
+ case 0x38:
+ I2C_ERR("[i2c%d] Arbitration lost in address"
+ "or data byte\n", i2c->bus_num);
+ break;
+ case 0x48:
+ I2C_ERR("[i2c%d] Address + Read bit transmitted,"
+ "ACK not received\\n", i2c->bus_num);
+ break;
+ case 0x58:
+ I2C_ERR("[i2c%d] Data byte received in master mode,"
+ "ACK not received\n", i2c->bus_num);
+ break;
+ default:
+ I2C_ERR("[i2c%d] unknown error\n",
+ i2c->bus_num);
+ break;
+ }
+ i2c->msg_idx = code;
+ i2c->result = RESULT_ERR;
+ dprintk(DEBUG_INFO, "[i2c%d] packet transmission failed\n",
+ i2c->bus_num);
+ wake_up(&i2c->wait);
+ spin_unlock_irqrestore(&i2c->lock, flags);
+ return code;
+ }
+
+ spin_unlock_irqrestore(&i2c->lock, flags);
+ return 0;
+}
+
+/* Functions for DMA support */
+static void sunxi_i2c_dma_request(struct sunxi_i2c *i2c,
+ dma_addr_t phy_addr)
+{
+ struct sunxi_i2c_dma *dma_tx, *dma_rx;
+ struct dma_slave_config dma_sconfig;
+ struct device *dev = &i2c->adap.dev;
+ dma_cap_mask_t mask_tx, mask_rx;
+ int ret;
+
+ dma_tx = devm_kzalloc(dev, sizeof(*dma_tx), GFP_KERNEL);
+ dma_rx = devm_kzalloc(dev, sizeof(*dma_rx), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(dma_tx) || IS_ERR_OR_NULL(dma_rx))
+ return;
+
+ dma_cap_zero(mask_tx);
+ dma_cap_set(DMA_SLAVE, mask_tx);
+ dma_tx->chan = dma_request_channel(mask_tx, NULL, NULL);
+
+ if (!dma_tx->chan) {
+ I2C_ERR("[i2c%d] can't request DMA tx channel\n", i2c->bus_num);
+ goto fail_al;
+ }
+
+ dma_sconfig.dst_addr = phy_addr + TWI_DRIVER_SENDF;
+ dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.src_maxburst = 16;
+ dma_sconfig.dst_maxburst = 16;
+#ifndef DRQDST_TWI0_TX
+ I2C_ERR("[i2c%d] can't susport DMA for TX\n", i2c->bus_num);
+#else
+ dma_sconfig.slave_id = sunxi_slave_id(DRQDST_TWI0_TX + i2c->bus_num,
+ DRQSRC_SDRAM);
+#endif
+ dma_sconfig.direction = DMA_MEM_TO_DEV;
+ ret = dmaengine_slave_config(dma_tx->chan, &dma_sconfig);
+ if (ret < 0) {
+ I2C_ERR("[i2c%d] can't configure tx channel\n", i2c->bus_num);
+ goto fail_tx;
+ }
+ i2c->dma_tx = dma_tx;
+
+ dma_cap_zero(mask_rx);
+ dma_cap_set(DMA_SLAVE, mask_rx);
+ dma_rx->chan = dma_request_channel(mask_rx, NULL, NULL);
+
+ if (!dma_rx->chan) {
+ I2C_ERR("[i2c%d] can't request DMA rx channel\n", i2c->bus_num);
+ goto fail_tx;
+ }
+
+ dma_sconfig.src_addr = phy_addr + TWI_DRIVER_RECVF;
+ dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.src_maxburst = 16;
+ dma_sconfig.dst_maxburst = 16;
+#ifndef DRQSRC_TWI0_RX
+ I2C_ERR("[i2c%d] can't susport DMA for RX\n", i2c->bus_num);
+#else
+ dma_sconfig.slave_id = sunxi_slave_id(DRQSRC_SDRAM,
+ DRQSRC_TWI0_RX + i2c->bus_num);
+#endif
+ dma_sconfig.direction = DMA_DEV_TO_MEM;
+ ret = dmaengine_slave_config(dma_rx->chan, &dma_sconfig);
+ if (ret < 0) {
+ I2C_ERR("[i2c%d] can't configure rx channel\n", i2c->bus_num);
+ goto fail_rx;
+ }
+ i2c->dma_rx = dma_rx;
+
+
+ init_completion(&i2c->cmd_complete);
+ dprintk(DEBUG_INIT, "[i2c%d] using %s (tx) and %s (rx)"
+ "for DMA transfers\n", i2c->bus_num,
+ dma_chan_name(i2c->dma_tx->chan),
+ dma_chan_name(i2c->dma_rx->chan));
+
+ return;
+
+fail_rx:
+ dma_release_channel(i2c->dma_rx->chan);
+fail_tx:
+ dma_release_channel(i2c->dma_tx->chan);
+fail_al:
+ devm_kfree(dev, dma_tx);
+ devm_kfree(dev, dma_rx);
+ dprintk(DEBUG_INIT, "[i2c%d] can't use DMA, using PIO instead\n",
+ i2c->bus_num);
+}
+
+static void sunxi_i2c_dma_callback(void *arg)
+{
+ struct sunxi_i2c *i2c = (struct sunxi_i2c *)arg;
+
+ if (i2c->dma_using == i2c->dma_tx)
+ dprintk(DEBUG_INFO, "[i2c%d] dma write data end\n",
+ i2c->bus_num);
+ else if (i2c->dma_using == i2c->dma_rx)
+ dprintk(DEBUG_INFO, "[i2c%d] dma read data end\n",
+ i2c->bus_num);
+ dma_unmap_single(i2c->dma_using->chan->device->dev,
+ i2c->dma_using->dma_buf,
+ i2c->dma_using->dma_len, i2c->dma_using->dma_data_dir);
+ complete(&i2c->cmd_complete);
+}
+
+static void sunxi_i2c_dma_free(struct sunxi_i2c_dma *dma)
+{
+ dma->dma_buf = 0;
+ dma->dma_len = 0;
+ dma_release_channel(dma->chan);
+ dma->chan = NULL;
+}
+
+static int i2c_sunxi_drv_complete(struct sunxi_i2c *i2c)
+{
+ unsigned long flags = 0;
+ unsigned long timeout = 0;
+
+ timeout = wait_event_timeout(i2c->wait, i2c->result, i2c->adap.timeout);
+ if (timeout == 0) {
+ I2C_ERR("[i2c%d] twi driver xfer timeout (dev addr:0x%x)\n",
+ i2c->bus_num, i2c->msg->addr);
+#if (!defined(CONFIG_ARCH_SUN8IW16) && !defined(CONFIG_ARCH_SUN8IW19))
+ dump_reg(i2c, 0x200, 0x20);
+#endif
+ i2c_drv_disable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT
+ | RX_REQ_INT | TX_REQ_INT, i2c->base_addr);
+ i2c_drv_disable_dma_irq(DMA_TX | DMA_RX, i2c->base_addr);
+ return -ETIME;
+ } else if (i2c->result == RESULT_ERR) {
+#if 0
+ I2C_ERR("[i2c%d] incomplete xfer"
+ "(status: 0x%x, dev addr: 0x%x)\n",
+ i2c->bus_num, i2c->msg_idx, i2c->msg->addr);
+#endif
+#if (!defined(CONFIG_ARCH_SUN8IW16) && !defined(CONFIG_ARCH_SUN8IW19))
+ dump_reg(i2c, 0x200, 0x20);
+#endif
+ i2c_drv_disable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT
+ | RX_REQ_INT | TX_REQ_INT, i2c->base_addr);
+ i2c_drv_disable_dma_irq(DMA_TX | DMA_RX, i2c->base_addr);
+ return -ECOMM;
+ }
+
+ dprintk(DEBUG_INFO, "[i2c%d] xfer complete\n", i2c->bus_num);
+
+ spin_lock_irqsave(&i2c->lock, flags);
+ i2c->result = 0;
+ spin_unlock_irqrestore(&i2c->lock, flags);
+
+ return 0;
+}
+
+static int i2c_sunxi_dma_xfer(struct sunxi_i2c *i2c, unsigned char *buf)
+{
+ unsigned long time_left;
+ struct sunxi_i2c_dma *dma = i2c->dma_using;
+ struct dma_async_tx_descriptor *dma_desc;
+ struct device *chan_dev = dma->chan->device->dev;
+
+ dma->dma_buf = dma_map_single(chan_dev, buf,
+ dma->dma_len, dma->dma_data_dir);
+ if (dma_mapping_error(chan_dev, dma->dma_buf)) {
+ I2C_ERR("DMA mapping failed\n");
+ goto err_map;
+ }
+ dma_desc = dmaengine_prep_slave_single(dma->chan, dma->dma_buf,
+ dma->dma_len, dma->dma_transfer_dir,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!dma_desc) {
+ I2C_ERR("Not able to get desc for DMA xfer\n");
+ goto err_desc;
+ }
+ dma_desc->callback = sunxi_i2c_dma_callback;
+ dma_desc->callback_param = i2c;
+ if (dma_submit_error(dmaengine_submit(dma_desc))) {
+ I2C_ERR("[i2c%d] DMA submit failed\n", i2c->bus_num);
+ goto err_submit;
+ }
+
+ reinit_completion(&i2c->cmd_complete);
+ dma_async_issue_pending(dma->chan);
+ dprintk(DEBUG_INFO1, "[i2c%d] dma issue pending\n", i2c->bus_num);
+
+ time_left = wait_for_completion_timeout(
+ &i2c->cmd_complete,
+ msecs_to_jiffies(DMA_TIMEOUT));
+ dprintk(DEBUG_INFO1, "[i2c%d] time_left = %lu\n",
+ i2c->bus_num, time_left);
+
+ return 0;
+
+err_submit:
+err_desc:
+ dma_unmap_single(chan_dev, dma->dma_buf,
+ dma->dma_len, dma->dma_data_dir);
+err_map:
+ return -EINVAL;
+}
+
+static int sunxi_i2c_drv_write(struct sunxi_i2c *i2c, struct i2c_msg *msgs)
+{
+
+ i2c->msg = msgs;
+
+ sunxi_i2c_drv_slave_addr(i2c, msgs);
+ if (msgs->len == 1) {
+ i2c_set_packet_addr_byte(0, i2c->base_addr);
+ i2c_set_packet_data_byte(msgs->len, i2c->base_addr);
+ } else {
+ i2c_set_packet_addr_byte(1, i2c->base_addr);
+ i2c_set_packet_data_byte(msgs->len - 1, i2c->base_addr);
+ }
+ i2c_set_packet_cnt(1, i2c->base_addr);
+
+ i2c_drv_clear_pending(i2c->base_addr);
+ i2c_drv_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, i2c->base_addr);
+ i2c_start_xfer(i2c->base_addr);
+
+ i2c_sunxi_send_msgs(i2c, msgs);
+
+ return 0;
+}
+
+static int sunxi_i2c_drv_dma_write(struct sunxi_i2c *i2c, struct i2c_msg *msgs)
+{
+ int ret = 0;
+
+ i2c->msg = msgs;
+
+ sunxi_i2c_drv_slave_addr(i2c, msgs);
+ i2c_set_packet_addr_byte(1, i2c->base_addr);
+ i2c_set_packet_data_byte(msgs->len - 1, i2c->base_addr);
+ i2c_set_packet_cnt(1, i2c->base_addr);
+
+ i2c_drv_clear_pending(i2c->base_addr);
+ i2c_drv_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, i2c->base_addr);
+ i2c_drv_enable_dma_irq(DMA_TX, i2c->base_addr);
+ i2c_start_xfer(i2c->base_addr);
+
+ i2c->dma_using = i2c->dma_tx;
+ i2c->dma_using->dma_transfer_dir = DMA_MEM_TO_DEV;
+ i2c->dma_using->dma_data_dir = DMA_TO_DEVICE;
+ i2c->dma_using->dma_len = msgs->len;
+
+ ret = i2c_sunxi_dma_xfer(i2c, msgs->buf);
+
+ return ret;
+}
+
+static int
+sunxi_i2c_drv_mulpk_write(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num)
+{
+ u16 i;
+
+ i2c->msg = msgs;
+
+ sunxi_i2c_drv_slave_addr(i2c, msgs);
+ i2c_set_packet_addr_byte(1, i2c->base_addr);
+ i2c_set_packet_data_byte(msgs->len - 1, i2c->base_addr);
+ i2c_set_packet_interval(i2c->pkt_interval, i2c->base_addr);
+ i2c_set_packet_cnt(num, i2c->base_addr);
+
+ i2c_drv_clear_pending(i2c->base_addr);
+ i2c_drv_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, i2c->base_addr);
+ i2c_start_xfer(i2c->base_addr);
+
+ for (i = 0; i < num; i++)
+ i2c_sunxi_send_msgs(i2c, msgs + i);
+
+ return 0;
+}
+
+static int
+sunxi_i2c_drv_dma_mulpk_write(struct sunxi_i2c *i2c, struct i2c_msg *msgs,
+ int num)
+{
+ u16 i;
+ int ret = 0;
+ unsigned char *buf;
+ unsigned int tlen = num * msgs->len;
+
+ i2c->msg = msgs;
+
+ sunxi_i2c_drv_slave_addr(i2c, msgs);
+ i2c_set_packet_addr_byte(1, i2c->base_addr);
+ i2c_set_packet_data_byte(msgs->len - 1, i2c->base_addr);
+ i2c_set_packet_interval(i2c->pkt_interval, i2c->base_addr);
+ i2c_set_packet_cnt(num, i2c->base_addr);
+
+ buf = kzalloc(tlen, GFP_KERNEL);
+ if (IS_ERR_OR_NULL(buf)) {
+ I2C_ERR("[i2c%d] kzalloc failed\n", i2c->bus_num);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < num; i++) {
+ memcpy(buf + (i * (msgs->len)), (msgs + i)->buf, msgs->len);
+ }
+
+ i2c_drv_clear_pending(i2c->base_addr);
+ i2c_drv_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, i2c->base_addr);
+ i2c_drv_enable_dma_irq(DMA_TX, i2c->base_addr);
+ i2c_start_xfer(i2c->base_addr);
+
+ i2c->dma_using = i2c->dma_tx;
+ i2c->dma_using->dma_transfer_dir = DMA_MEM_TO_DEV;
+ i2c->dma_using->dma_data_dir = DMA_TO_DEVICE;
+ i2c->dma_using->dma_len = tlen;
+
+ ret = i2c_sunxi_dma_xfer(i2c, buf);
+
+ kfree(buf);
+ buf = NULL;
+
+ return ret;
+}
+
+static int
+sunxi_i2c_drv_read(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num)
+{
+ struct i2c_msg *wmsgs, *rmsgs;
+
+ if (num == 1) {
+ wmsgs = NULL;
+ rmsgs = msgs;
+ } else if (num == 2) {
+ wmsgs = msgs;
+ rmsgs = msgs + 1;
+ }
+
+ i2c->msg = rmsgs;
+
+ sunxi_i2c_drv_slave_addr(i2c, rmsgs);
+ i2c_set_packet_cnt(1, i2c->base_addr);
+ i2c_set_packet_data_byte(rmsgs->len, i2c->base_addr);
+ if (rmsgs->len > MAX_FIFO)
+ i2c_set_rx_trig_level(MAX_FIFO, i2c->base_addr);
+ else
+ i2c_set_rx_trig_level(rmsgs->len, i2c->base_addr);
+ if (i2c_query_rxfifo(i2c->base_addr))
+ i2c_clear_rxfifo(i2c->base_addr);
+
+ i2c_drv_clear_pending(i2c->base_addr);
+ i2c_drv_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, i2c->base_addr);
+ i2c_start_xfer(i2c->base_addr);
+
+ if (wmsgs)
+ i2c_sunxi_send_msgs(i2c, wmsgs);
+
+ return 0;
+}
+
+static int
+sunxi_i2c_drv_dma_read(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num)
+{
+ int ret = 0;
+ struct i2c_msg *wmsgs, *rmsgs;
+
+ if (num == 1) {
+ wmsgs = NULL;
+ rmsgs = msgs;
+ } else if (num == 2) {
+ wmsgs = msgs;
+ rmsgs = msgs + 1;
+ }
+
+ i2c->msg = rmsgs;
+
+ sunxi_i2c_drv_slave_addr(i2c, rmsgs);
+ i2c_set_packet_data_byte(rmsgs->len, i2c->base_addr);
+ i2c_set_packet_cnt(1, i2c->base_addr);
+ i2c_set_rx_trig_level(MAX_FIFO/2, i2c->base_addr);
+ if (i2c_query_rxfifo(i2c->base_addr))
+ i2c_clear_rxfifo(i2c->base_addr);
+
+ i2c_drv_clear_pending(i2c->base_addr);
+ i2c_drv_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, i2c->base_addr);
+ i2c_drv_enable_dma_irq(DMA_RX, i2c->base_addr);
+ i2c_start_xfer(i2c->base_addr);
+ if (wmsgs)
+ i2c_sunxi_send_msgs(i2c, wmsgs);
+
+ i2c->dma_using = i2c->dma_rx;
+ i2c->dma_using->dma_transfer_dir = DMA_DEV_TO_MEM;
+ i2c->dma_using->dma_data_dir = DMA_FROM_DEVICE;
+ i2c->dma_using->dma_len = rmsgs->len;
+
+ ret = i2c_sunxi_dma_xfer(i2c, rmsgs->buf);
+
+ return ret;
+}
+
+/**
+ * sunxi_i2c_drv_do_xfer - twi driver transmission control
+ * @i2c: struct of sunxi_i2c
+ * @msgs: One or more messages to execute before STOP is issued to
+ * terminate the operation; each message begins with a START.
+ * @num: Number of messages to be executed.
+ *
+ * Returns negative errno, else the number of messages executed.
+ */
+static int
+sunxi_i2c_drv_do_xfer(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num)
+{
+ int ret;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&i2c->lock, flags);
+ i2c->result = 0;
+ spin_unlock_irqrestore(&i2c->lock, flags);
+
+ i2c_drv_clear_pending(i2c->base_addr);
+ i2c_drv_disable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT
+ | RX_REQ_INT | TX_REQ_INT, i2c->base_addr);
+ i2c_drv_disable_dma_irq(DMA_TX | DMA_RX, i2c->base_addr);
+ if (i2c_query_txfifo(i2c->base_addr))
+ i2c_clear_txfifo(i2c->base_addr);
+
+ if (num == 1) {
+ if (msgs->flags & I2C_M_RD) {
+ /* 1 msgs read */
+ i2c_enable_read_tran_mode(i2c->base_addr);
+ i2c_set_packet_addr_byte(0, i2c->base_addr);
+
+ if (i2c->dma_rx && (msgs->len >= DMA_THRESHOLD)) {
+ dprintk(DEBUG_INFO, "[i2c%d] master dma read\n",
+ i2c->bus_num);
+ ret = sunxi_i2c_drv_dma_read(i2c, msgs, num);
+ } else {
+ dprintk(DEBUG_INFO, "[i2c%d] master cpu read\n",
+ i2c->bus_num);
+ ret = sunxi_i2c_drv_read(i2c, msgs, num);
+ }
+ } else {
+ /* 1 msgs write */
+ i2c_disable_read_tran_mode(i2c->base_addr);
+
+ if (i2c->dma_tx && (msgs->len >= DMA_THRESHOLD)) {
+ dprintk(DEBUG_INFO,
+ "[i2c%d] master dma write\n",
+ i2c->bus_num);
+ ret = sunxi_i2c_drv_dma_write(i2c, msgs);
+ } else {
+ dprintk(DEBUG_INFO,
+ "[i2c%d] master cpu write\n",
+ i2c->bus_num);
+ ret = sunxi_i2c_drv_write(i2c, msgs);
+ }
+ }
+ } else if ((num == 2) && ((msgs + 1)->flags & I2C_M_RD)) {
+ /* 2 msgs read */
+ i2c_disable_read_tran_mode(i2c->base_addr);
+ i2c_set_packet_addr_byte(msgs->len, i2c->base_addr);
+
+ if (i2c->dma_rx && ((msgs + 1)->len >= DMA_THRESHOLD)) {
+ dprintk(DEBUG_INFO, "[i2c%d] master dma read\n",
+ i2c->bus_num);
+ ret = sunxi_i2c_drv_dma_read(i2c, msgs, num);
+ } else {
+ dprintk(DEBUG_INFO, "[i2c%d] master cpu read\n",
+ i2c->bus_num);
+ ret = sunxi_i2c_drv_read(i2c, msgs, num);
+ }
+ } else {
+ /* multiple write with the same format packet */
+ i2c_disable_read_tran_mode(i2c->base_addr);
+ i2c_set_packet_addr_byte(1, i2c->base_addr);
+
+ if (i2c->dma_tx && ((num * msgs->len) >= DMA_THRESHOLD)) {
+ dprintk(DEBUG_INFO,
+ "[i2c%d] master dma multiple packet write\n",
+ i2c->bus_num);
+ ret = sunxi_i2c_drv_dma_mulpk_write(i2c, msgs, num);
+ } else {
+ dprintk(DEBUG_INFO,
+ "[i2c%d] master cpu multiple packet write\n",
+ i2c->bus_num);
+ ret = sunxi_i2c_drv_mulpk_write(i2c, msgs, num);
+ }
+ }
+ if (ret)
+ return ret;
+
+ ret = i2c_sunxi_drv_complete(i2c);
+
+ if (ret)
+ return ret;
+ else
+ return num;
+}
+
+
+/*
+ ****************************************************************************
+ *
+ * FunctionName: sunxi_i2c_addr_byte
+ *
+ * Description:
+ * 7bits addr: 7-1bits addr+0 bit r/w
+ * 10bits addr: 1111_11xx_xxxx_xxxx-->1111_0xx_rw,xxxx_xxxx
+ * send the 7 bits addr,or the first part of 10 bits addr
+ * Parameters:
+ *
+ *
+ * Return value:
+ * <20><>
+ * Notes:
+ *
+ ****************************************************************************
+ */
+static void sunxi_i2c_addr_byte(struct sunxi_i2c *i2c)
+{
+ unsigned char addr = 0;
+ unsigned char tmp = 0;
+
+ if (i2c->msg[i2c->msg_idx].flags & I2C_M_TEN) {
+ /* 0111_10xx,ten bits address--9:8bits */
+ tmp = 0x78 | (((i2c->msg[i2c->msg_idx].addr)>>8) & 0x03);
+ addr = tmp << 1; /*1111_0xx0*/
+ /* how about the second part of ten bits addr? */
+ /* Answer: deal at twi_core_process() */
+ } else
+ /* 7-1bits addr, xxxx_xxx0 */
+ addr = (i2c->msg[i2c->msg_idx].addr & 0x7f) << 1;
+
+ /* read, default value is write */
+ if (i2c->msg[i2c->msg_idx].flags & I2C_M_RD)
+ addr |= 1;
+
+ if (i2c->msg[i2c->msg_idx].flags & I2C_M_TEN) {
+ dprintk(DEBUG_INFO1, "[i2c%d] first part of 10bits = 0x%x\n",
+ i2c->bus_num, addr);
+ } else
+ dprintk(DEBUG_INFO1, "[i2c%d] 7bits+r/w = 0x%x\n",
+ i2c->bus_num, addr);
+
+ /* send 7bits+r/w or the first part of 10bits */
+ twi_put_byte(i2c->base_addr, &addr);
+}
+
+
+static int sunxi_i2c_core_process(struct sunxi_i2c *i2c)
+{
+ void __iomem *base_addr = i2c->base_addr;
+ int ret = SUNXI_I2C_OK;
+ int err_code = 0;
+ unsigned char state = 0;
+ unsigned char tmp = 0;
+ unsigned long flags = 0;
+
+ state = twi_query_irq_status(base_addr);
+
+ spin_lock_irqsave(&i2c->lock, flags);
+ dprintk(DEBUG_INFO, "[i2c%d][slave address = (0x%x), state = (0x%x)]\n",
+ i2c->bus_num, i2c->msg->addr, state);
+ if (i2c->msg == NULL) {
+ I2C_ERR("[i2c%d] i2c message is NULL, err_code = 0xfe\n",
+ i2c->bus_num);
+ err_code = 0xfe;
+ goto msg_null;
+ }
+
+ switch (state) {
+ case 0xf8:
+ /* On reset or stop the bus is idle, use only at poll method */
+ err_code = 0xf8;
+ goto err_out;
+ case 0x08: /* A START condition has been transmitted */
+ case 0x10: /* A repeated start condition has been transmitted */
+ sunxi_i2c_addr_byte(i2c);/* send slave address */
+ break;
+ case 0xd8: /* second addr has transmitted, ACK not received! */
+ case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */
+ err_code = 0x20;
+ goto err_out;
+ case 0x18: /* SLA+W has been transmitted; ACK has been received */
+ /* if any, send second part of 10 bits addr */
+ if (i2c->msg[i2c->msg_idx].flags & I2C_M_TEN) {
+ /* the remaining 8 bits of address */
+ tmp = i2c->msg[i2c->msg_idx].addr & 0xff;
+ twi_put_byte(base_addr, &tmp); /* case 0xd0: */
+ break;
+ }
+ /* for 7 bit addr, then directly send data byte--case 0xd0: */
+ case 0xd0: /* second addr has transmitted,ACK received! */
+ case 0x28: /* Data byte in DATA REG has been transmitted; */
+ /* ACK has been received */
+ /* after send register address then START send write data */
+ if (i2c->msg_ptr < i2c->msg[i2c->msg_idx].len) {
+ twi_put_byte(base_addr,
+ &(i2c->msg[i2c->msg_idx].buf[i2c->msg_ptr]));
+ i2c->msg_ptr++;
+ break;
+ }
+
+ i2c->msg_idx++; /* the other msg */
+ i2c->msg_ptr = 0;
+ if (i2c->msg_idx == i2c->msg_num) {
+ err_code = SUNXI_I2C_OK;/* Success,wakeup */
+ goto ok_out;
+ } else if (i2c->msg_idx < i2c->msg_num) {
+ /* for restart pattern, read spec, two msgs */
+ ret = twi_restart(base_addr, i2c->bus_num);
+ if (ret == SUNXI_I2C_FAIL) {
+ I2C_ERR("[i2c%d] twi_regulator: %s\n",
+ i2c->bus_num,
+ ((struct sunxi_i2c_platform_data *)(i2c->adap.dev.parent->platform_data))->regulator_id);
+ err_code = SUNXI_I2C_SFAIL;
+ goto err_out;/* START can't sendout */
+ }
+ } else {
+ err_code = SUNXI_I2C_FAIL;
+ goto err_out;
+ }
+ break;
+ case 0x30: /* Data byte in I2CDAT has been transmitted; */
+ /* NOT ACK has been received */
+ err_code = 0x30; /*err,wakeup the thread*/
+ goto err_out;
+ case 0x38: /* Arbitration lost during SLA+W, SLA+R or data bytes */
+ err_code = 0x38; /*err,wakeup the thread*/
+ goto err_out;
+ case 0x40: /* SLA+R has been transmitted; ACK has been received */
+ /* with Restart,needn't to send second part of 10 bits addr */
+ /* refer-"I2C-SPEC v2.1" */
+ /* enable A_ACK need it(receive data len) more than 1. */
+ if (i2c->msg[i2c->msg_idx].len > 1) {
+ /* send register addr complete,then enable the A_ACK */
+ /* and get ready for receiving data */
+ twi_enable_ack(base_addr);
+ twi_clear_irq_flag(base_addr);/* jump to case 0x50 */
+ } else if (i2c->msg[i2c->msg_idx].len == 1) {
+ twi_clear_irq_flag(base_addr);/* jump to case 0x58 */
+ }
+ break;
+ case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */
+ err_code = 0x48; /*err,wakeup the thread*/
+ goto err_out;
+ case 0x50: /* Data bytes has been received; ACK has been transmitted */
+ /* receive first data byte */
+ if (i2c->msg_ptr < i2c->msg[i2c->msg_idx].len) {
+ /* more than 2 bytes, the last byte need not to send ACK */
+ if ((i2c->msg_ptr + 2) == i2c->msg[i2c->msg_idx].len)
+ /* last byte no ACK */
+ twi_disable_ack(base_addr);
+
+ /* get data then clear flag,then next data coming */
+ twi_get_byte(base_addr,
+ &i2c->msg[i2c->msg_idx].buf[i2c->msg_ptr]);
+ i2c->msg_ptr++;
+ break;
+ }
+ /* err process, the last byte should be @case 0x58 */
+ err_code = SUNXI_I2C_FAIL;/* err, wakeup */
+ goto err_out;
+ case 0x58:
+ /* Data byte has been received; NOT ACK has been transmitted */
+ /* received the last byte */
+ if (i2c->msg_ptr == i2c->msg[i2c->msg_idx].len - 1) {
+ twi_get_last_byte(base_addr,
+ &i2c->msg[i2c->msg_idx].buf[i2c->msg_ptr]);
+ i2c->msg_idx++;
+ i2c->msg_ptr = 0;
+ if (i2c->msg_idx == i2c->msg_num) {
+ /* succeed,wakeup the thread */
+ err_code = SUNXI_I2C_OK;
+ goto ok_out;
+ } else if (i2c->msg_idx < i2c->msg_num) {
+ /* repeat start */
+ ret = twi_restart(base_addr, i2c->bus_num);
+ if (ret == SUNXI_I2C_FAIL) {/* START fail */
+ I2C_ERR("[i2c%d] twi_regulator: %s\n",
+ i2c->bus_num,
+ ((struct sunxi_i2c_platform_data *)(i2c->adap.dev.parent->platform_data))->regulator_id);
+ err_code = SUNXI_I2C_SFAIL;
+ goto err_out;
+ }
+ break;
+ }
+ } else {
+ err_code = 0x58;
+ goto err_out;
+ }
+ case 0x00: /* Bus error during master or slave mode due to illegal level condition */
+ err_code = 0xff;
+ goto err_out;
+ default:
+ err_code = state;
+ goto err_out;
+ }
+ i2c->debug_state = state;/* just for debug */
+ spin_unlock_irqrestore(&i2c->lock, flags);
+ return ret;
+
+ok_out:
+err_out:
+ if (twi_stop(base_addr, i2c->bus_num) == SUNXI_I2C_TFAIL)
+ I2C_ERR("[i2c%d] STOP failed!\n", i2c->bus_num);
+
+
+msg_null:
+ ret = sunxi_i2c_xfer_complete(i2c, err_code);/* wake up */
+ i2c->debug_state = state;/* just for debug */
+ spin_unlock_irqrestore(&i2c->lock, flags);
+ return ret;
+}
+
+static irqreturn_t sunxi_i2c_handler(int this_irq, void *dev_id)
+{
+ struct sunxi_i2c *i2c = (struct sunxi_i2c *)dev_id;
+
+ if (i2c->twi_drv_used)
+ sunxi_i2c_drv_core_process(i2c);
+ else {
+ if (!twi_query_irq_flag(i2c->base_addr)) {
+ I2C_ERR("unknown interrupt!\n");
+ return IRQ_NONE;
+ }
+
+ /* disable irq */
+ twi_disable_irq(i2c->base_addr);
+
+ /* twi core process */
+ sunxi_i2c_core_process(i2c);
+
+ /*
+ * enable irq only when twi is transferring,
+ * otherwise disable irq
+ */
+ if (i2c->status != I2C_XFER_IDLE)
+ twi_enable_irq(i2c->base_addr);
+ }
+ return IRQ_HANDLED;
+}
+
+static int sunxi_i2c_xfer_complete(struct sunxi_i2c *i2c, int code)
+{
+ int ret = SUNXI_I2C_OK;
+
+ i2c->msg = NULL;
+ i2c->msg_num = 0;
+ i2c->msg_ptr = 0;
+ i2c->status = I2C_XFER_IDLE;
+
+ /* i2c->msg_idx store the information */
+ if (code == SUNXI_I2C_FAIL) {
+ I2C_ERR("[i2c%d] Maybe Logic Error, debug it!\n", i2c->bus_num);
+ i2c->msg_idx = code;
+ ret = SUNXI_I2C_FAIL;
+ i2c->result = RESULT_ERR;
+ } else if (code != SUNXI_I2C_OK) {
+ i2c->msg_idx = code;
+ ret = SUNXI_I2C_FAIL;
+ i2c->result = RESULT_COMPLETE;
+ }
+
+ wake_up(&i2c->wait);
+
+ return ret;
+}
+
+static int
+sunxi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct sunxi_i2c *i2c = (struct sunxi_i2c *)adap->algo_data;
+ int ret = SUNXI_I2C_FAIL;
+ unsigned char i = 0;
+ unsigned long diff_time;
+ struct timeval start, end;
+ u16 n, m;
+
+ if (IS_ERR_OR_NULL(msgs) || (num <= 0)) {
+ I2C_ERR("[i2c%d] invalid argument\n", i2c->bus_num);
+ return -EINVAL;
+ }
+
+ dprintk(DEBUG_INFO, "[i2c%d] num = %d\n", i2c->bus_num, num);
+
+ if (debug_mask & DEBUG_INFO2) {
+ for (n = 0; n < num; n++) {
+ printk("num: %d, data: ", n);
+ if ((msgs + n)->buf) {
+ for (m = 0; m < (msgs + n)->len; m++)
+ printk("%02x ", *((msgs + n)->buf + m));
+ printk("\n");
+ } else
+ printk("null\n");
+ }
+ }
+
+ do_gettimeofday(&start);
+ ret = pm_runtime_get_sync(i2c->dev);
+ if (ret < 0)
+ goto out;
+ if (i2c->twi_drv_used) {
+ dprintk(DEBUG_INFO1, "[i2c%d] twi driver xfer\n", i2c->bus_num);
+ ret = sunxi_i2c_drv_do_xfer(i2c, msgs, num);
+
+ } else {
+ dprintk(DEBUG_INFO1, "[i2c%d] twi engine xfer\n", i2c->bus_num);
+ for (i = 1; i <= adap->retries; i++) {
+ ret = sunxi_i2c_do_xfer(i2c, msgs, num);
+
+ if (ret != SUNXI_I2C_RETRY)
+ goto out;
+
+ dprintk(DEBUG_INFO,
+ "[i2c%d] Retrying transmission %d\n",
+ i2c->adap.nr, i);
+ udelay(100);
+ }
+
+ ret = -EREMOTEIO;
+ }
+
+out:
+ pm_runtime_mark_last_busy(i2c->dev);
+ pm_runtime_put_autosuspend(i2c->dev);
+
+ do_gettimeofday(&end);
+ diff_time = 1000000 * (end.tv_sec - start.tv_sec) + end.tv_usec
+ - start.tv_usec;
+ dprintk(DEBUG_INFO2, "[i2c%d] diff_time = %ld us\n", i2c->bus_num,
+ diff_time);
+
+ return ret;
+}
+
+static int
+sunxi_i2c_do_xfer(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num)
+{
+ unsigned long timeout = 0;
+ int ret = SUNXI_I2C_FAIL;
+ unsigned long flags = 0;
+
+ twi_soft_reset(i2c->base_addr, TWI_SRST_REG, TWI_SRST_SRST);
+ udelay(100);
+
+ /* test the bus is free,already protect by the semaphore at DEV layer */
+ while (twi_query_irq_status(i2c->base_addr) != TWI_STAT_IDLE &&
+ twi_query_irq_status(i2c->base_addr) != TWI_STAT_BUS_ERR &&
+ twi_query_irq_status(i2c->base_addr) != TWI_STAT_ARBLOST_SLAR_ACK) {
+ dprintk(DEBUG_INFO, "[i2c%d] bus is busy, status = %x\n",
+ i2c->bus_num, twi_query_irq_status(i2c->base_addr));
+ if (twi_send_clk_9pulse(i2c->base_addr, i2c->bus_num) != SUNXI_I2C_OK) {
+ ret = SUNXI_I2C_RETRY;
+ goto out;
+ } else
+ break;
+ }
+
+ /* may conflict with xfer_complete */
+ spin_lock_irqsave(&i2c->lock, flags);
+ i2c->msg = msgs;
+ i2c->msg_num = num;
+ i2c->msg_ptr = 0;
+ i2c->msg_idx = 0;
+ i2c->status = I2C_XFER_START;
+ twi_enable_irq(i2c->base_addr); /* enable irq */
+ twi_disable_ack(i2c->base_addr); /* disabe ACK */
+ /* set the special function register,default:0. */
+ twi_set_efr(i2c->base_addr, 0);
+ spin_unlock_irqrestore(&i2c->lock, flags);
+
+ /* START signal, needn't clear int flag */
+ ret = twi_start(i2c->base_addr, i2c->bus_num);
+ if (ret == SUNXI_I2C_FAIL) {
+ I2C_ERR("[i2c%d] twi_regulator: %s\n",
+ i2c->bus_num,
+ ((struct sunxi_i2c_platform_data *)(i2c->adap.dev.parent->platform_data))->regulator_id);
+ twi_soft_reset(i2c->base_addr, TWI_SRST_REG, TWI_SRST_SRST);
+ twi_disable_irq(i2c->base_addr); /* disable irq */
+ i2c->status = I2C_XFER_IDLE;
+ ret = SUNXI_I2C_RETRY;
+ goto out;
+ }
+
+ i2c->status = I2C_XFER_RUNNING;
+ /* sleep and wait,do the transfer at interrupt handler,timeout = 5*HZ */
+ timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0,
+ i2c->adap.timeout);
+ /* return code,if(msg_idx == num) succeed */
+ ret = i2c->msg_idx;
+ if (timeout == 0) {
+ I2C_ERR("[i2c%d] xfer timeout (dev addr:0x%x)\n",
+ i2c->bus_num, msgs->addr);
+ spin_lock_irqsave(&i2c->lock, flags);
+ i2c->msg = NULL;
+ spin_unlock_irqrestore(&i2c->lock, flags);
+ ret = -ETIME;
+ } else if (ret != num) {
+ // I2C_ERR("[i2c%d] incomplete xfer (status: 0x%x, dev addr: 0x%x)\n", i2c->bus_num, ret, msgs->addr);
+ ret = -ECOMM;
+ }
+out:
+ return ret;
+}
+
+static unsigned int sunxi_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C|I2C_FUNC_10BIT_ADDR|I2C_FUNC_SMBUS_EMUL;
+}
+
+
+static const struct i2c_algorithm sunxi_i2c_algorithm = {
+ .master_xfer = sunxi_i2c_xfer,
+ .functionality = sunxi_i2c_functionality,
+};
+
+static int sunxi_i2c_clk_init(struct sunxi_i2c *i2c)
+{
+ unsigned int apb_clk = 0;
+
+ if (clk_prepare_enable(i2c->mclk)) {
+ I2C_ERR("[i2c%d] enable apb_twi clock failed!\n", i2c->bus_num);
+ return -1;
+ }
+
+ sunxi_periph_reset_assert(i2c->mclk);
+ sunxi_periph_reset_deassert(i2c->mclk);
+
+ /* set twi module clock */
+ apb_clk = clk_get_rate(i2c->mclk);
+ if (apb_clk == 0) {
+ I2C_ERR("[i2c%d] get i2c source clock frequency failed!\n",
+ i2c->bus_num);
+ return -1;
+ }
+
+ /* enable twi engine or twi driver */
+ if (i2c->twi_drv_used) {
+ twi_set_clock(i2c, TWI_DRIVER_BUSC, 24000000, i2c->bus_freq,
+ TWI_DRV_CLK_M, TWI_DRV_CLK_N);
+ dprintk(DEBUG_INFO1, "[i2c%d] set twi driver clock\n",
+ i2c->bus_num);
+ twi_enable(i2c->base_addr, TWI_DRIVER_CTRL, TWI_DRV_EN);
+ } else {
+#ifdef CONFIG_EVB_PLATFORM
+ twi_set_clock(i2c, TWI_CLK_REG, apb_clk, i2c->bus_freq,
+ TWI_CLK_DIV_M, TWI_CLK_DIV_N);
+#else
+ twi_set_clock(i2c, TWI_CLK_REG, 24000000, i2c->bus_freq,
+ TWI_CLK_DIV_M, TWI_CLK_DIV_N);
+#endif
+ dprintk(DEBUG_INFO1, "[i2c%d] set twi engine clock\n",
+ i2c->bus_num);
+ twi_enable(i2c->base_addr, TWI_CTL_REG, TWI_CTL_BUSEN);
+ }
+
+ return 0;
+}
+
+static int sunxi_i2c_clk_exit(struct sunxi_i2c *i2c)
+{
+ /* disable twi bus */
+ if (i2c->twi_drv_used) {
+ twi_disable(i2c->base_addr, TWI_DRIVER_CTRL, TWI_DRV_EN);
+ } else
+ twi_disable(i2c->base_addr, TWI_CTL_REG, TWI_CTL_BUSEN);
+
+ /* disable clk */
+ if (!IS_ERR_OR_NULL(i2c->mclk))
+ clk_disable_unprepare(i2c->mclk);
+ else {
+ clk_disable_unprepare(i2c->mclk);
+ I2C_ERR("[i2c%d] i2c mclk handle is invalid, just return!\n",
+ i2c->bus_num);
+ return -1;
+ }
+ return 0;
+}
+
+static int sunxi_i2c_hw_init(struct sunxi_i2c *i2c,
+ struct sunxi_i2c_platform_data *pdata, struct device *dev)
+{
+ int ret = 0;
+
+ ret = twi_regulator_request(pdata, dev);
+ if (ret < 0) {
+ I2C_ERR("[i2c%d] request regulator failed!\n", i2c->bus_num);
+ return ret;
+ }
+
+ ret = twi_regulator_enable(pdata);
+ if (ret < 0) {
+ I2C_ERR("[i2c%d] enable regulator failed!\n", i2c->bus_num);
+ return ret;
+ }
+
+ ret = twi_request_gpio(i2c);
+ if (ret < 0) {
+ I2C_ERR("[i2c%d] request i2c gpio failed!\n", i2c->bus_num);
+ return ret;
+ }
+
+ if (sunxi_i2c_clk_init(i2c)) {
+ I2C_ERR("[i2c%d] init i2c clock failed!\n", i2c->bus_num);
+ return -1;
+ }
+
+ if (!(i2c->twi_drv_used))
+ twi_soft_reset(i2c->base_addr, TWI_SRST_REG, TWI_SRST_SRST);
+
+ return ret;
+}
+
+static void sunxi_i2c_hw_exit(struct sunxi_i2c *i2c,
+ struct sunxi_i2c_platform_data *pdata)
+{
+ if (sunxi_i2c_clk_exit(i2c)) {
+ I2C_ERR("[i2c%d] exit i2c clock failed!\n", i2c->bus_num);
+ return;
+ }
+ twi_release_gpio(i2c);
+
+ twi_regulator_disable(pdata);
+
+ twi_regulator_release(pdata);
+}
+
+static void sunxi_i2c_release(struct device *dev)
+{
+ dprintk(DEBUG_INIT, "release\n");
+}
+
+#if (!defined(CONFIG_ARCH_SUN8IW16) && !defined(CONFIG_ARCH_SUN8IW19))
+static ssize_t sunxi_i2c_info_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = container_of(dev,
+ struct platform_device, dev);
+ struct sunxi_i2c_platform_data *pdata = dev->platform_data;
+
+ return snprintf(buf, PAGE_SIZE,
+ "pdev->id = %d\n"
+ "pdev->name = %s\n"
+ "pdev->num_resources = %u\n"
+ "pdev->resource.mem = [%pa, %pa]\n"
+ "pdev->resource.irq = %pa\n"
+ "pdev->dev.platform_data.bus_num = %d\n"
+ "pdev->dev.platform_data.freqency = %u\n"
+ "pdev->dev.platform_data.regulator= 0x%p\n"
+ "pdev->dev.platform_data.regulator_id = %s\n",
+ pdev->id, pdev->name, pdev->num_resources,
+ &pdev->resource[0].start, &pdev->resource[0].end,
+ &pdev->resource[1].start, pdata->bus_num, pdata->frequency,
+ pdata->regulator, pdata->regulator_id);
+}
+static struct device_attribute sunxi_i2c_info_attr =
+ __ATTR(info, S_IRUGO, sunxi_i2c_info_show, NULL);
+
+static ssize_t sunxi_i2c_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sunxi_i2c *i2c = dev_get_drvdata(dev);
+ static char const *i2c_status[] = {"Unknown", "Idle", "Start",
+ "Unknown", "Running"};
+
+ if (i2c == NULL)
+ return snprintf(buf, PAGE_SIZE, "%s\n", "sunxi_i2c is NULL!");
+
+ return snprintf(buf, PAGE_SIZE,
+ "i2c->bus_num = %d\n"
+ "i2c->status = [%u] %s\n"
+ "i2c->msg_num = %u, ->msg_idx = %u, ->msg_ptr = %u\n"
+ "i2c->bus_freq = %u\n"
+ "i2c->irq = %d\n"
+ "i2c->debug_state = %u\n"
+ "i2c->base_addr = 0x%p, the TWI control register:\n"
+ "[ADDR] 0x%02x = 0x%08x, [XADDR] 0x%02x = 0x%08x\n"
+ "[DATA] 0x%02x = 0x%08x, [CNTR] 0x%02x = 0x%08x\n"
+ "[STAT] 0x%02x = 0x%08x, [CCR] 0x%02x = 0x%08x\n"
+ "[SRST] 0x%02x = 0x%08x, [EFR] 0x%02x = 0x%08x\n"
+ "[LCR] 0x%02x = 0x%08x\n",
+ i2c->bus_num, i2c->status, i2c_status[i2c->status],
+ i2c->msg_num, i2c->msg_idx, i2c->msg_ptr,
+ i2c->bus_freq, i2c->irq, i2c->debug_state,
+ i2c->base_addr,
+ TWI_ADDR_REG, readl(i2c->base_addr + TWI_ADDR_REG),
+ TWI_XADDR_REG, readl(i2c->base_addr + TWI_XADDR_REG),
+ TWI_DATA_REG, readl(i2c->base_addr + TWI_DATA_REG),
+ TWI_CTL_REG, readl(i2c->base_addr + TWI_CTL_REG),
+ TWI_STAT_REG, readl(i2c->base_addr + TWI_STAT_REG),
+ TWI_CLK_REG, readl(i2c->base_addr + TWI_CLK_REG),
+ TWI_SRST_REG, readl(i2c->base_addr + TWI_SRST_REG),
+ TWI_EFR_REG, readl(i2c->base_addr + TWI_EFR_REG),
+ TWI_LCR_REG, readl(i2c->base_addr + TWI_LCR_REG));
+}
+static struct device_attribute sunxi_i2c_status_attr =
+ __ATTR(status, S_IRUGO, sunxi_i2c_status_show, NULL);
+
+static void sunxi_i2c_sysfs(struct platform_device *_pdev)
+{
+ device_create_file(&_pdev->dev, &sunxi_i2c_info_attr);
+ device_create_file(&_pdev->dev, &sunxi_i2c_status_attr);
+}
+#endif
+
+static int sunxi_i2c_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct sunxi_i2c *i2c = NULL;
+ struct resource *mem_res = NULL;
+ struct sunxi_i2c_platform_data *pdata = NULL;
+ int ret, irq;
+ unsigned int int_flag = 0;
+#ifndef CONFIG_SUNXI_REGULATOR_DT
+ const char *str_vcc_twi;
+#endif
+ dma_addr_t phy_addr;
+
+ if (np == NULL) {
+ I2C_ERR("I2C failed to get of node\n");
+ return -ENODEV;
+ }
+
+ i2c = kzalloc(sizeof(struct sunxi_i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ pdata = kzalloc(sizeof(struct sunxi_i2c_platform_data), GFP_KERNEL);
+ if (pdata == NULL) {
+ kfree(i2c);
+ return -ENOMEM;
+ }
+ i2c->dev = &pdev->dev;
+ pdev->dev.platform_data = pdata;
+ pdev->dev.driver_data = i2c;
+
+ pdev->id = of_alias_get_id(np, "twi");
+ if (pdev->id < 0) {
+ I2C_ERR("I2C failed to get alias id\n");
+ ret = -EINVAL;
+ goto emem;
+ }
+ pdata->bus_num = pdev->id;
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (mem_res == NULL) {
+ I2C_ERR("[i2c%d] failed to get MEM res\n", pdev->id);
+ ret = -ENXIO;
+ goto emem;
+ }
+
+ if (!request_mem_region(mem_res->start, resource_size(mem_res),
+ mem_res->name)) {
+ I2C_ERR("[i2c%d] failed to request mem region\n", pdev->id);
+ ret = -EINVAL;
+ goto emem;
+ }
+
+ i2c->base_addr = ioremap(mem_res->start, resource_size(mem_res));
+ if (!i2c->base_addr) {
+ ret = -EIO;
+ goto eiomap;
+ }
+ i2c->res = mem_res;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ I2C_ERR("[i2c%d] failed to get irq\n", pdev->id);
+ ret = -EINVAL;
+ goto eiomap;
+ }
+
+ ret = of_property_read_u32(np, "clock-frequency", &pdata->frequency);
+ if (ret) {
+ I2C_ERR("[i2c%d] failed to get clock frequency\n", pdev->id);
+ ret = -EINVAL;
+ goto eiomap;
+ }
+
+#ifndef CONFIG_SUNXI_REGULATOR_DT
+ ret = of_property_read_string(np, "twi_regulator", &str_vcc_twi);
+ if (ret)
+ I2C_ERR("[i2c%d] warning: failed to get regulator id\n",
+ pdev->id);
+ else if (strlen(str_vcc_twi) >= sizeof(pdata->regulator_id))
+ I2C_ERR("[i2c%d] illegal regulator id\n", pdev->id);
+ else {
+ strcpy(pdata->regulator_id, str_vcc_twi);
+ pr_info("[i2c%d] twi_regulator: %s\n", pdev->id,
+ pdata->regulator_id);
+ }
+#endif
+ pdev->dev.release = sunxi_i2c_release;
+ i2c->adap.owner = THIS_MODULE;
+ i2c->adap.nr = pdata->bus_num;
+ i2c->adap.retries = 3;
+ i2c->adap.timeout = 5*HZ;
+ i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ i2c->bus_freq = pdata->frequency;
+ i2c->irq = irq;
+ i2c->bus_num = pdata->bus_num;
+ i2c->status = I2C_XFER_IDLE;
+ snprintf(i2c->adap.name, sizeof(i2c->adap.name),
+ SUNXI_TWI_DEV_NAME"%u", i2c->adap.nr);
+ pdev->dev.init_name = i2c->adap.name;
+
+ spin_lock_init(&i2c->lock);
+ init_waitqueue_head(&i2c->wait);
+
+ i2c->mclk = of_clk_get(np, 0);
+ if (IS_ERR_OR_NULL(i2c->mclk)) {
+ I2C_ERR("[i2c%d] request TWI clock failed\n", i2c->bus_num);
+ ret = -EIO;
+ goto eremap;
+ }
+
+ i2c->adap.algo = &sunxi_i2c_algorithm;
+
+#ifndef CONFIG_SUNXI_ARISC
+#if (!defined(CONFIG_ARCH_SUN50IW9) && !defined(CONFIG_ARCH_SUN8IW19) \
+ && !defined(CONFIG_ARCH_SUN50IW10))
+ /* SUNXI_ARISC will only use twi0, enable gic interrupt when suspend */
+ if (i2c->adap.nr == 0)
+ int_flag |= IRQF_NO_SUSPEND;
+#endif
+#endif
+ if (of_property_read_u32(np, "no_suspend", &i2c->no_suspend))
+ i2c->no_suspend = 0;
+ else
+ int_flag |= IRQF_NO_SUSPEND;
+
+ ret = request_irq(irq, sunxi_i2c_handler, int_flag,
+ i2c->adap.name, i2c);
+ if (ret) {
+ I2C_ERR("[i2c%d] requeset irq failed!\n", i2c->bus_num);
+ goto ereqirq;
+ }
+
+ i2c->adap.algo_data = i2c;
+ i2c->adap.dev.parent = &pdev->dev;
+ i2c->adap.dev.of_node = pdev->dev.of_node;
+
+ if (of_property_read_u32(np, "twi_drv_used", &i2c->twi_drv_used))
+ i2c->twi_drv_used = 0;
+ dprintk(DEBUG_INIT, "[i2c%d] twi_drv_used = %d\n", i2c->bus_num,
+ i2c->twi_drv_used);
+ if (of_property_read_u32(np, "twi_pkt_interval", &i2c->pkt_interval))
+ i2c->pkt_interval = 0;
+ dprintk(DEBUG_INIT, "[i2c%d] twi_pkt_interval = %d\n",
+ i2c->bus_num, i2c->pkt_interval);
+
+ ret = sunxi_i2c_hw_init(i2c, pdata, &pdev->dev);
+ if (ret != 0) {
+ I2C_ERR("[i2c%d] hw init failed!\n", i2c->bus_num);
+ if (ret == -EPROBE_DEFER) {
+ I2C_ERR("[i2c%d] regulator init try again!\n",
+ i2c->bus_num);
+ } else
+ ret = -EINVAL;
+ goto ehwinit;
+ }
+
+ pm_runtime_set_active(i2c->dev);
+ if (i2c->no_suspend) {
+ pm_runtime_get_noresume(i2c->dev);
+ }
+ pm_runtime_set_autosuspend_delay(i2c->dev, AUTOSUSPEND_TIMEOUT);
+ pm_runtime_use_autosuspend(i2c->dev);
+ pm_runtime_enable(i2c->dev);
+
+ ret = i2c_add_numbered_adapter(&i2c->adap);
+ if (ret < 0) {
+ I2C_ERR("[i2c%d] failed to add adapter\n", i2c->bus_num);
+ pm_runtime_set_suspended(i2c->dev);
+ pm_runtime_disable(i2c->dev);
+ goto eadapt;
+ }
+
+ phy_addr = (dma_addr_t)mem_res->start;
+
+ /* Init DMA config if supported */
+ if (i2c->twi_drv_used)
+ sunxi_i2c_dma_request(i2c, phy_addr);
+
+ i2c->pdev = pdev;
+
+ platform_set_drvdata(pdev, i2c);
+
+#if (!defined(CONFIG_ARCH_SUN8IW16) && !defined(CONFIG_ARCH_SUN8IW19))
+ sunxi_i2c_sysfs(pdev);
+#endif
+
+ return 0;
+
+eadapt:
+ clk_disable_unprepare(i2c->mclk);
+
+ehwinit:
+ free_irq(irq, i2c);
+
+ereqirq:
+ iounmap(i2c->base_addr);
+
+eremap:
+ if (!IS_ERR_OR_NULL(i2c->mclk)) {
+ clk_put(i2c->mclk);
+ i2c->mclk = NULL;
+ }
+
+eiomap:
+ release_mem_region(mem_res->start, resource_size(mem_res));
+
+emem:
+ kfree(pdata);
+ kfree(i2c);
+
+ return ret;
+}
+
+static int sunxi_i2c_remove(struct platform_device *pdev)
+{
+ struct sunxi_i2c *i2c = platform_get_drvdata(pdev);
+
+ dprintk(DEBUG_INIT, "[i2c%d] remove\n", i2c->bus_num);
+
+ if (i2c->dma_tx)
+ sunxi_i2c_dma_free(i2c->dma_tx);
+ if (i2c->dma_rx)
+ sunxi_i2c_dma_free(i2c->dma_rx);
+
+ platform_set_drvdata(pdev, NULL);
+ i2c_del_adapter(&i2c->adap);
+ free_irq(i2c->irq, i2c);
+ pm_runtime_set_suspended(i2c->dev);
+ pm_runtime_disable(i2c->dev);
+ /* disable clock and release gpio */
+ sunxi_i2c_hw_exit(i2c, pdev->dev.platform_data);
+ if (!IS_ERR_OR_NULL(i2c->mclk)) {
+ clk_put(i2c->mclk);
+ i2c->mclk = NULL;
+ } else {
+ I2C_ERR("i2c mclk handle is invalid, just return!\n");
+ return -1;
+ }
+
+ iounmap(i2c->base_addr);
+ release_mem_region(i2c->res->start, resource_size(i2c->res));
+ kfree(i2c);
+ kfree(pdev->dev.platform_data);
+
+ return 0;
+}
+
+static int twi_regulator_enable(struct sunxi_i2c_platform_data *pdata)
+{
+ if (pdata->regulator == NULL)
+ return 0;
+
+ if (regulator_enable(pdata->regulator) != 0) {
+ I2C_ERR("[i2c%d] enable regulator %s failed!\n",
+ pdata->bus_num, pdata->regulator_id);
+ return -1;
+ }
+ return 0;
+}
+
+static int twi_regulator_disable(struct sunxi_i2c_platform_data *pdata)
+{
+ if (pdata->regulator == NULL)
+ return 0;
+
+ if (regulator_disable(pdata->regulator) != 0) {
+ I2C_ERR("[i2c%d] disable regulator %s failed!\n",
+ pdata->bus_num, pdata->regulator_id);
+ return -1;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int sunxi_i2c_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sunxi_i2c *i2c = platform_get_drvdata(pdev);
+
+#ifndef CONFIG_SUNXI_ARISC
+#if (!defined(SONFIG_ARCH_SUN50IW9) && !defined(CONFIG_ARCH_SUN8IW19) \
+ && !defined(CONFIG_ARCH_SUN50IW10))
+ /* SUNXI_ARISC will only use twi0 */
+ if (i2c->adap.nr == 0)
+ return 0;
+#endif
+#endif
+
+ if (sunxi_i2c_clk_exit(i2c)) {
+ I2C_ERR("[i2c%d] suspend failed for clk\n", i2c->bus_num);
+ return -1;
+ }
+
+ twi_select_gpio_state(i2c->pctrl, PINCTRL_STATE_SLEEP, i2c->bus_num);
+ twi_regulator_disable(dev->platform_data);
+ dprintk(DEBUG_SUSPEND, "[i2c%d] runtime suspend finish\n", i2c->bus_num);
+
+ return 0;
+}
+
+static int sunxi_i2c_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sunxi_i2c *i2c = platform_get_drvdata(pdev);
+
+#ifndef CONFIG_SUNXI_ARISC
+#if (!defined(CONFIG_ARCH_SUN50IW9) && !defined(CONFIG_ARCH_SUN8IW19) \
+ && !defined(CONFIG_ARCH_SUN50IW10))
+ /* SUNXI_ARISC will only use twi0 */
+ if (i2c->adap.nr == 0)
+ return 0;
+#endif
+#endif
+
+ if (twi_regulator_enable(dev->platform_data)) {
+ return -1;
+ }
+
+ twi_select_gpio_state(i2c->pctrl, PINCTRL_STATE_DEFAULT, i2c->bus_num);
+
+ if (sunxi_i2c_clk_init(i2c)) {
+ I2C_ERR("[i2c%d] init clk failed..\n", i2c->bus_num);
+ return -1;
+ }
+
+ if (!(i2c->twi_drv_used))
+ twi_soft_reset(i2c->base_addr, TWI_SRST_REG, TWI_SRST_SRST);
+
+ dprintk(DEBUG_SUSPEND, "[i2c%d] runtime resume finish\n", i2c->bus_num);
+
+ return 0;
+}
+
+static int sunxi_i2c_suspend_noirq(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sunxi_i2c *i2c = platform_get_drvdata(pdev);
+
+ if (i2c->twi_drv_used)
+ twi_disable(i2c->base_addr, TWI_DRIVER_CTRL, TWI_DRV_EN);
+ if (i2c->no_suspend) {
+ dprintk(DEBUG_SUSPEND, "[i2c%d] doesn't need to suspend\n",
+ i2c->bus_num);
+ return 0;
+ }
+
+ return pm_runtime_force_suspend(dev);
+
+}
+
+static int sunxi_i2c_resume_noirq(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sunxi_i2c *i2c = platform_get_drvdata(pdev);
+
+ if (i2c->twi_drv_used) {
+ twi_set_clock(i2c, TWI_DRIVER_BUSC, 24000000, i2c->bus_freq,
+ TWI_DRV_CLK_M, TWI_DRV_CLK_N);
+ twi_enable(i2c->base_addr, TWI_DRIVER_CTRL, TWI_DRV_EN);
+ }
+ if (i2c->no_suspend) {
+ dprintk(DEBUG_SUSPEND, "[i2c%d] doesn't need to resume\n",
+ i2c->bus_num);
+ return 0;
+ }
+
+ return pm_runtime_force_resume(dev);
+
+}
+
+static const struct dev_pm_ops sunxi_i2c_dev_pm_ops = {
+ .suspend_noirq = sunxi_i2c_suspend_noirq,
+ .resume_noirq = sunxi_i2c_resume_noirq,
+ .runtime_suspend = sunxi_i2c_runtime_suspend,
+ .runtime_resume = sunxi_i2c_runtime_resume,
+};
+
+#define SUNXI_I2C_DEV_PM_OPS (&sunxi_i2c_dev_pm_ops)
+#else
+#define SUNXI_I2C_DEV_PM_OPS NULL
+#endif
+
+static const struct of_device_id sunxi_i2c_match[] = {
+ { .compatible = "allwinner,sun8i-twi", },
+ { .compatible = "allwinner,sun50i-twi", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sunxi_i2c_match);
+
+static struct platform_driver sunxi_i2c_driver = {
+ .probe = sunxi_i2c_probe,
+ .remove = sunxi_i2c_remove,
+ .driver = {
+ .name = SUNXI_TWI_DEV_NAME,
+ .owner = THIS_MODULE,
+ .pm = SUNXI_I2C_DEV_PM_OPS,
+ .of_match_table = sunxi_i2c_match,
+ },
+};
+
+static int __init sunxi_i2c_adap_init(void)
+{
+ dprintk(DEBUG_INIT, "init\n");
+
+ return platform_driver_register(&sunxi_i2c_driver);
+}
+
+static void __exit sunxi_i2c_adap_exit(void)
+{
+ dprintk(DEBUG_INIT, "exit\n");
+
+ platform_driver_unregister(&sunxi_i2c_driver);
+}
+
+#ifdef CONFIG_SUNXI_REGULATOR_DT
+subsys_initcall(sunxi_i2c_adap_init);
+#else
+fs_initcall(sunxi_i2c_adap_init);
+#endif
+
+module_exit(sunxi_i2c_adap_exit);
+module_param_named(debug, debug_mask, int, 0664);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:i2c-sunxi");
+MODULE_DESCRIPTION("SUNXI I2C Bus Driver");
+MODULE_AUTHOR("pannan");