SmartAudio/lichee/linux-4.9/drivers/i2c/busses/i2c-sunxi.c

2533 lines
67 KiB
C
Executable File
Raw Blame History

/*
* 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"
/* For debug */
#define I2C_EXIT() pr_debug("%s()%d - %s\n", __func__, __LINE__, "Exit")
#define I2C_ENTER() pr_debug("%s()%d - %s\n", __func__, __LINE__, "Enter ...")
#define I2C_ERR(fmt, arg...) pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg)
#ifndef CONFIG_SUNXI_I2C_PRINT_TRANSFER_INFO
static int bus_transfer_dbg = -1;
module_param_named(transfer_debug, bus_transfer_dbg,
int, S_IRUGO | S_IWUSR | S_IWGRP);
#endif
#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 2000
static u32 debug_mask;
#define dprintk(level_mask, fmt, arg...) \
do { \
if (unlikely(debug_mask & level_mask)) \
pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg); \
} while (0)
static int twi_used_mask;
/* 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 */
void __iomem *base_addr;
struct pinctrl *pctrl;
bool twi_drv_used;
unsigned char result;
struct sunxi_i2c_dma *dma_tx;
struct sunxi_i2c_dma *dma_rx;
struct sunxi_i2c_dma *dma_using;
};
static void dump_reg(void __iomem *base_addr, u32 offset, u32 len)
{
u32 i, j;
for (i = 0; i < len; i += 0x10) {
pr_warn("0x%04x: ", offset + i);
for (j = 0; j < 0x10; j += 0x04)
pr_warn("0x%08x ", readl(base_addr + offset + i + j));
pr_warn("\n");
}
}
/* 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_INFO, "[i2c0] reg: 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_INFO, "[i2c0] reg: 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 clk_m, unsigned int clk_n,
unsigned int mask_clk_m, unsigned int mask_clk_n)
{
unsigned int reg_val = readl(i2c->base_addr + reg_clk);
dprintk(DEBUG_INFO, "[i2c%d] reg_clk = 0x%x, clk_m = %u, clk_n = %u,"
"mask_clk_m = %u, mask_clk_n = %u\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 << 3)) << 8);
writel(reg_val, i2c->base_addr + reg_clk);
dprintk(DEBUG_INFO, "[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);
writel(reg_val, i2c->base_addr + reg_clk);
dprintk(DEBUG_INFO, "[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_req: 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_req,
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 int src_clk = clk_in/10;
unsigned int divider = src_clk/sclk_req; /* 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_req)
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, 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_chan_is_enable(int _ch)
{
return twi_used_mask & SUNXI_TWI_CHAN_MASK(_ch);
}
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_INFO, "Pinctrl init %d ... [%s]\n",
i2c->bus_num, i2c->adap.dev.parent->init_name);
if (!twi_chan_is_enable(i2c->bus_num))
return -1;
i2c->pctrl = devm_pinctrl_get(i2c->adap.dev.parent);
if (IS_ERR(i2c->pctrl)) {
I2C_ERR("TWI%d devm_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 int 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 int 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 int 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)
{
int twi_scl = 1;
int low = 0;
int high = 1;
int 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 regulator *regu = NULL;
/* 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;
}
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_INFO, "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_INFO, "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_INFO, "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_INFO, "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_INFO, "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 &= INTERVAL_MASK;
reg_val |= val;
writel(reg_val, base_addr + TWI_DRIVER_CFG);
dprintk(DEBUG_INFO, "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_INFO, "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_INFO, "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_INFO, "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);
bitmap &= TWI_DRV_INT_MASK;
reg_val |= bitmap;
writel(reg_val, base_addr + TWI_DRIVER_INTC);
dprintk(DEBUG_INFO, "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);
bitmap &= TWI_DRV_INT_MASK;
reg_val &= ~bitmap;
writel(reg_val, base_addr + TWI_DRIVER_INTC);
dprintk(DEBUG_INFO, "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_INFO, "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_INFO, "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_CMD;
if (msgs->flags & I2C_M_TEN) {
/* SLV_ID | CMD | SLV_ID_X */
val = ((0x78 | ((msgs->addr >> 8) & 0x03)) << 9) | cmd
| (msgs->addr & 0xff);
} else {
val = ((msgs->addr & 0x7f) << 9) | cmd;
}
writel(val, i2c->base_addr + TWI_DRIVER_SLV);
dprintk(DEBUG_INFO, "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_INFO, "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_INFO, "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)
{
unsigned int i;
unsigned char 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--)
;
dprintk(DEBUG_INFO, "[i2c%d] time = %d\n", i2c->bus_num, time);
if (time) {
writeb(msgs->buf[i], i2c->base_addr + TWI_DRIVER_SENDF);
dprintk(DEBUG_INFO, "[i2c%d] writeb: Byte[0] = 0x%x,"
"fifo len = %d\n",
i2c->bus_num, msgs->buf[i],
i2c_query_txfifo(i2c->base_addr));
} else {
dprintk(DEBUG_INFO,
"[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)
{
unsigned int i;
unsigned char time = 0xff;
dprintk(DEBUG_INFO, "[i2c%d] recving data\n", i2c->bus_num);
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_INFO, "[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;
unsigned int fifolen;
spin_lock_irqsave(&i2c->lock, flags);
status = twi_drv_query_irq_status(base_addr);
dprintk(DEBUG_INFO, "[i2c%d] irq status = 0x%x\n",
i2c->bus_num, status);
twi_drv_clear_irq_flag(status, base_addr);
if ((status & RX_REQ_PD) && (i2c->msg->len < DMA_THRESHOLD)) {
dprintk(DEBUG_INFO, "[i2c%d] i2c->msg->len = %d\n",
i2c->bus_num, i2c->msg->len);
fifolen = (readl(base_addr + TWI_DRIVER_FIFOC)
& RECV_FIFO_CONT) >> 16;
dprintk(DEBUG_INFO, "[i2c%d] fifo len = %d\n",
i2c->bus_num, fifolen);
if (i2c_sunxi_recv_msgs(i2c, i2c->msg))
i2c_drv_disable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT
| RX_REQ_INT, i2c->base_addr);
fifolen = (readl(base_addr + TWI_DRIVER_FIFOC)
& RECV_FIFO_CONT) >> 16;
dprintk(DEBUG_INFO, "[i2c%d] fifo len = %d\n",
i2c->bus_num, fifolen);
}
if (status & TRAN_COM_PD) {
dprintk(DEBUG_INFO, "[i2c%d] packet transmission completed\n",
i2c->bus_num);
dprintk(DEBUG_INFO, "[i2c%d] fifo len = %d\n", i2c->bus_num,
i2c_query_txfifo(i2c->base_addr));
i2c->result = RESULT_COMPLETE;
wake_up(&i2c->wait);
spin_unlock_irqrestore(&i2c->lock, flags);
}
if (status & TRAN_ERR_PD) {
dprintk(DEBUG_INFO, "[i2c%d] packet transmission failed\n",
i2c->bus_num);
i2c->result = RESULT_ERR;
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;
wake_up(&i2c->wait);
spin_unlock_irqrestore(&i2c->lock, flags);
return code;
}
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 (!dma_tx || !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;
#if defined(CONFIG_ARCH_SUN8IW18)
dma_sconfig.slave_id = sunxi_slave_id(SUNXI_TWI_DRQ_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;
#if defined(CONFIG_ARCH_SUN8IW18)
dma_sconfig.slave_id = sunxi_slave_id(DRQDST_SDRAM,
SUNXI_TWI_DRQ_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);
dprintk(DEBUG_INFO, "[i2c%d] callback complete\n", i2c->bus_num);
}
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 timeout = 0;
timeout = wait_event_timeout(i2c->wait, i2c->result, 2*HZ);
if (timeout == 0) {
I2C_ERR("[i2c%d] twi driver xfer timeout (dev addr:0x%x)\n",
i2c->bus_num, i2c->msg->addr);
dump_reg(i2c->base_addr, 0x200, 0x40);
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) {
I2C_ERR("[i2c%d] incomplete xfer"
"(status: 0x%x, dev addr: 0x%x)\n",
i2c->bus_num, i2c->msg_idx, i2c->msg->addr);
dump_reg(i2c->base_addr, 0x200, 0x40);
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);
i2c->result = 0;
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_INFO, "[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_INFO, "[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)
{
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;
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)
{
int i;
sunxi_i2c_drv_slave_addr(i2c, msgs);
i2c_set_packet_data_byte(msgs->len - 1, i2c->base_addr);
i2c_set_packet_interval(0x30, 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)
{
int i, ret = 0;
unsigned char *buf;
unsigned int tlen = num * msgs->len;
sunxi_i2c_drv_slave_addr(i2c, msgs);
i2c_set_packet_data_byte(msgs->len - 1, i2c->base_addr);
i2c_set_packet_interval(0x30, i2c->base_addr);
i2c_set_packet_cnt(num, i2c->base_addr);
buf = kzalloc(tlen, GFP_KERNEL);
if (!buf) {
I2C_ERR("[i2c%d] kzalloc failed\n", i2c->bus_num);
return -ENOMEM;
}
for (i = 0; i < num; i++) {
memcpy(buf, msgs->buf, msgs->len);
msgs++;
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);
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 | RX_REQ_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;
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(1, 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;
#ifdef CONFIG_SUNXI_I2C_PRINT_TRANSFER_INFO
if (i2c->bus_num == CONFIG_SUNXI_I2C_PRINT_TRANSFER_INFO_WITH_BUS_NUM) {
if (i2c->msg[i2c->msg_idx].flags & I2C_M_TEN)
dprintk(DEBUG_INFO,
"[i2c%d] first part of 10bits = 0x%x\n",
i2c->bus_num, addr);
dprintk(DEBUG_INFO, "[i2c%d] 7bits+r/w = 0x%x\n",
i2c->bus_num, addr);
}
#else
if (unlikely(bus_transfer_dbg != -1)) {
if (i2c->bus_num == bus_transfer_dbg) {
if (i2c->msg[i2c->msg_idx].flags & I2C_M_TEN)
dprintk(DEBUG_INFO,
"[i2c%d] first part of 10bits = 0x%x\n",
i2c->bus_num, addr);
dprintk(DEBUG_INFO, "[i2c%d] 7bits+r/w = 0x%x\n",
i2c->bus_num, addr);
}
}
#endif
/* 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);
#ifdef CONFIG_SUNXI_I2C_PRINT_TRANSFER_INFO
if (i2c->bus_num == CONFIG_SUNXI_I2C_PRINT_TRANSFER_INFO_WITH_BUS_NUM)
dprintk(DEBUG_INFO, "[i2c%d][slave address = (0x%x),"
"state = (0x%x)]\n",
i2c->bus_num, i2c->msg->addr, state);
#else
if (unlikely(bus_transfer_dbg != -1)) {
if (i2c->bus_num == bus_transfer_dbg)
dprintk(DEBUG_INFO,
"[i2c%d][slave address = (0x%x),"
"state = (0x%x)]\n",
i2c->bus_num, i2c->msg->addr, state);
}
#endif
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;
dprintk(DEBUG_INFO, "[i2c%d] run to interrupt\n", i2c->bus_num);
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;
} else if (code != SUNXI_I2C_OK) {
i2c->msg_idx = code;
ret = SUNXI_I2C_FAIL;
}
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;
int i = 0;
unsigned long diff_time;
struct timeval start, end;
do_gettimeofday(&start);
dprintk(DEBUG_INFO, "[i2c%d] num = %d\n", i2c->bus_num, num);
for (i = 0; i < num; i++) {
dprintk(DEBUG_INFO, "[i2c%d] msgs[%d].addr = 0x%x\n",
i2c->bus_num, i, msgs[i].addr);
dprintk(DEBUG_INFO, "[i2c%d] msgs[%d].flags = 0x%x\n",
i2c->bus_num, i, msgs[i].flags);
dprintk(DEBUG_INFO, "[i2c%d] msgs[%d].len = %u\n",
i2c->bus_num, i, msgs[i].len);
}
ret = pm_runtime_get_sync(i2c->dev);
if (ret < 0)
goto out;
if (i2c->twi_drv_used) {
dprintk(DEBUG_INFO, "[i2c%d] twi driver xfer\n", i2c->bus_num);
ret = sunxi_i2c_drv_do_xfer(i2c, msgs, num);
} else {
dprintk(DEBUG_INFO, "[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;
}
if (msgs->flags & I2C_M_RD)
dprintk(DEBUG_INFO, "[i2c%d] msgs->buf[0]=0x%x\n",
i2c->bus_num, (msgs->buf)[0]);
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_INFO, "[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_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_INFO, "[i2c%d] set twi driver clock\n",
i2c->bus_num);
twi_enable(i2c->base_addr, TWI_DRIVER_CTRL, TWI_DRV_EN);
dprintk(DEBUG_INFO, "[i2c%d] twi driver enable \n",
i2c->bus_num);
} 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_INFO, "[i2c%d] set twi engine clock\n",
i2c->bus_num);
twi_enable(i2c->base_addr, TWI_CTL_REG, TWI_CTL_BUSEN);
dprintk(DEBUG_INFO, "[i2c%d] twi engine enable \n",
i2c->bus_num);
}
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);
dprintk(DEBUG_INFO, "[i2c%d] disable twi driver\n",
i2c->bus_num);
} 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)
{
int ret = 0;
ret = twi_regulator_request(pdata);
if (ret < 0) {
I2C_ERR("[i2c%d] request regulator failed!\n", i2c->bus_num);
return -1;
}
ret = twi_request_gpio(i2c);
if (ret < 0) {
I2C_ERR("[i2c%d] request i2c gpio failed!\n", i2c->bus_num);
return -1;
}
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_release(pdata);
}
static void sunxi_i2c_release(struct device *dev)
{
I2C_ENTER();
}
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);
}
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 long int_flag = 0;
const char *str_vcc_twi;
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;
}
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;
}
ret = of_property_read_string(np, "twi_regulator",
&str_vcc_twi);
if (ret)
I2C_ERR("[i2c%d] 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);
}
}
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
/* SUNXI_ARISC will only use twi0, enable gic interrupt when suspend */
if (i2c->adap.nr == 0)
int_flag |= IRQF_NO_SUSPEND;
#endif
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;
twi_used_mask |= SUNXI_TWI_CHAN_MASK(pdev->id);
#if defined(CONFIG_ARCH_SUN8IW18)
i2c->twi_drv_used = of_property_read_bool(np, "twi_drv_used");
dprintk(DEBUG_INIT, "[i2c%d] twi_drv_used = %d\n", i2c->bus_num,
i2c->twi_drv_used);
#endif
if (sunxi_i2c_hw_init(i2c, pdata)) {
ret = -EIO;
goto ehwinit;
}
pm_runtime_enable(i2c->dev);
pm_runtime_set_autosuspend_delay(i2c->dev, AUTOSUSPEND_TIMEOUT);
pm_runtime_use_autosuspend(i2c->dev);
pm_runtime_set_active(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);
sunxi_i2c_sysfs(pdev);
dprintk(DEBUG_INIT, "I2C: %s: sunxi I2C adapter\n",
dev_name(&i2c->adap.dev));
dprintk(DEBUG_INFO, "TWI_CTL 0x%p: 0x%08x\n", i2c->base_addr + 0x0c,
readl(i2c->base_addr + 0x0c));
dprintk(DEBUG_INFO, "TWI_STAT 0x%p: 0x%08x\n", i2c->base_addr + 0x10,
readl(i2c->base_addr + 0x10));
dprintk(DEBUG_INFO, "TWI_CLK 0x%p: 0x%08x\n", i2c->base_addr + 0x14,
readl(i2c->base_addr + 0x14));
dprintk(DEBUG_INFO, "TWI_SRST 0x%p: 0x%08x\n", i2c->base_addr + 0x18,
readl(i2c->base_addr + 0x18));
dprintk(DEBUG_INFO, "TWI_EFR 0x%p: 0x%08x\n", i2c->base_addr + 0x1c,
readl(i2c->base_addr + 0x1c));
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);
kfree(i2c);
kfree(pdev->dev.platform_data);
I2C_EXIT();
return 0;
}
#ifdef CONFIG_PM
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] enable regulator %s failed!\n",
pdata->bus_num, pdata->regulator_id);
return -1;
}
return 0;
}
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
/* SUNXI_ARISC will only use twi0 */
if (i2c->adap.nr == 0)
return 0;
#endif
if (sunxi_i2c_clk_exit(i2c)) {
I2C_ERR("[i2c%d] suspend failed..\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] 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
/* SUNXI_ARISC will only use twi0 */
if (i2c->adap.nr == 0)
return 0;
#endif
twi_regulator_enable(dev->platform_data);
twi_select_gpio_state(i2c->pctrl, PINCTRL_STATE_DEFAULT, i2c->bus_num);
if (sunxi_i2c_clk_init(i2c)) {
I2C_ERR("[i2c%d] resume 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] 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 (!pm_runtime_status_suspended(dev)) {
sunxi_i2c_runtime_suspend(dev);
dprintk(DEBUG_SUSPEND, "[i2c%d] finish\n", i2c->bus_num);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_enable(dev);
}
return 0;
}
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 (pm_runtime_active(dev)) {
sunxi_i2c_runtime_resume(dev);
dprintk(DEBUG_SUSPEND, "[i2c%d] finish\n", i2c->bus_num);
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
}
return 0;
}
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, "Sunxi I2C adapt init\n");
return platform_driver_register(&sunxi_i2c_driver);
}
static void __exit sunxi_i2c_adap_exit(void)
{
I2C_ENTER();
platform_driver_unregister(&sunxi_i2c_driver);
}
fs_initcall(sunxi_i2c_adap_init);
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");