sdk-hwV1.3/lichee/linux-4.9/drivers/i2c/busses/i2c-sunxi-ng.c

3409 lines
93 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2020 - 2023 Allwinner Technology Co.,Ltd. All rights reserved. */
/*
* 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
*
* 2021.8.15 Lewis <liuyu@allwinnertech.com>
* Optimization the probe function and all the function involved code
*
* 2021.10.13 Lewis <liuyu@allwinnertec.com>
* Refactored twi xfer code
*/
#include <sunxi-log.h>
#include <sunxi-common.h>
#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/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/clk/sunxi.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/dmapool.h>
#include <linux/dma/sunxi-dma.h>
#include <asm/uaccess.h>
#include <linux/time.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/regulator/consumer.h>
#if defined(CONFIG_AW_TWI_DELAYINIT) || defined(CONFIG_SUNXI_I2C_DELAYINIT)
#include <linux/rpmsg.h>
#include <linux/aw_rpmsg.h>
#include <linux/rpbuf.h>
#endif /* CONFIG_AW_TWI_DELAYINIT */
#define SUNXI_TWI_VERSION "2.6.5"
/* TWI Register Offset */
/* 31:8bit reserved,7-1bit for slave addr,0 bit for GCE */
#define TWI_ADDR (0x00)
/* 31:8bit reserved,7-0bit for second addr in 10bit addr */
#define TWI_XADDR (0x04)
/* 31:8bit reserved, 7-0bit send or receive data byte */
#define TWI_DATA (0x08)
/* INT_EN,BUS_EN,M_STA,INT_FLAG,A_ACK */
#define TWI_CNTR (0x0C)
/* 28 interrupt types + 0xF8 normal type = 29 */
#define TWI_STAT (0x10)
/* 31:7bit reserved,6-3bit,CLK_M,2-0bit CLK_N */
#define TWI_CCR (0x14)
/* 31:1bit reserved;0bit,write 1 to clear 0. */
#define TWI_SRST (0x18)
/* 31:2bit reserved,1:0 bit data byte follow read command */
#define TWI_EFR (0x1C)
/* 31:6bits reserved 5:0bit for sda&scl control */
#define TWI_LCR (0x20)
/* 23:16 VER_BIG 7:0:VER_SMALL */
#define TWI_VERSION (0xFC)
#if IS_ENABLED(CONFIG_I2C_SLAVE)
/* TWI_ADDR */
#define TWI_ADDR_SHIFT (0x1)
#define TWI_ADDR_10_BIT_SHIFT (0x8)
#define TWI_ADDR_10_BIT_WIDTH (0x3)
#define TWI_ADDR_10_BIT_MASK (0x78)
#define TWI_SLAVE_7_BIT_MASK (0x7f)
#define TWI_SLAVE_10_BIT_MASK (0xff)
#endif
/* TWI_DATA */
#define TWI_DATA_MASK (0xff << 0)
/* TWI_CNTR */
#define CLK_COUNT_MODE (0x1 << 0)
/* set 1 to send A_ACK,then low level on SDA */
#define A_ACK (0x1 << 2)
/* INT_FLAG,interrupt status flag: set '1' when interrupt coming */
#define INT_FLAG (0x1 << 3)
/* M_STP,Automatic clear 0 */
#define M_STP (0x1 << 4)
/* M_STA,atutomatic clear 0 */
#define M_STA (0x1 << 5)
/* BUS_EN, master mode should be set 1 */
#define BUS_EN (0x1 << 6)
/* INT_EN, set 1 to enable interrupt */
#define INT_EN (0x1 << 7) /* INT_EN */
/* 31:8 bit reserved */
/* TWI_STAT */
/*
* -------------------------------------------------------------------
* Code Status
* 00h Bus error
* 08h START condition transmitted
* 10h Repeated START condition transmitted
* 18h Address + Write bit transmitted, ACK received
* 20h Address + Write bit transmitted, ACK not received
* 28h Data byte transmitted in master mode, ACK received
* 30h Data byte transmitted in master mode, ACK not received
* 38h Arbitration lost in address or data byte
* 40h Address + Read bit transmitted, ACK received
* 48h Address + Read bit transmitted, ACK not received
* 50h Data byte received in master mode, ACK transmitted
* 58h Data byte received in master mode, not ACK transmitted
* 60h Slave address + Write bit received, ACK transmitted
* 68h Arbitration lost in address as master,
* slave address + Write bit received, ACK transmitted
* 70h General Call address received, ACK transmitted
* 78h Arbitration lost in address as master,
* General Call address received, ACK transmitted
* 80h Data byte received after slave address received, ACK transmitted
* 88h Data byte received after slave address received, not ACK transmitted
* 90h Data byte received after General Call received, ACK transmitted
* 98h Data byte received after General Call received, not ACK transmitted
* A0h STOP or repeated START condition received in slave mode
* A8h Slave address + Read bit received, ACK transmitted
* B0h Arbitration lost in address as master,
* slave address + Read bit received, ACK transmitted
* B8h Data byte transmitted in slave mode, ACK received
* C0h Data byte transmitted in slave mode, ACK not received
* C8h Last byte transmitted in slave mode, ACK received
* D0h Second Address byte + Write bit transmitted, ACK received
* D8h Second Address byte + Write bit transmitted, ACK not received
* F8h No relevant status information or no interrupt
*--------------------------------------------------------------------------
*/
#define TWI_STAT_MASK (0xff)
/* 7:0 bits use only,default is 0xF8 */
#define TWI_STAT_BUS_ERR (0x00) /* BUS ERROR */
/* timeout when sending 9th scl clk */
#define TWI_STAT_TIMEOUT_9CLK (0x01)
/* start can't send out */
#define TWI_STAT_TX_NSTA (0x02) /* defined by us not spec */
/* Master mode use only */
#define TWI_STAT_TX_STA (0x08) /* START condition transmitted */
/* Repeated START condition transmitted */
#define TWI_STAT_TX_RESTA (0x10)
/* Address+Write bit transmitted, ACK received */
#define TWI_STAT_TX_AW_ACK (0x18)
/* Address+Write bit transmitted, ACK not received */
#define TWI_STAT_TX_AW_NAK (0x20)
/* data byte transmitted in master mode,ack received */
#define TWI_STAT_TXD_ACK (0x28)
/* data byte transmitted in master mode ,ack not received */
#define TWI_STAT_TXD_NAK (0x30)
/* arbitration lost in address or data byte */
#define TWI_STAT_ARBLOST (0x38)
/* Address+Read bit transmitted, ACK received */
#define TWI_STAT_TX_AR_ACK (0x40)
/* Address+Read bit transmitted, ACK not received */
#define TWI_STAT_TX_AR_NAK (0x48)
/* data byte received in master mode ,ack transmitted */
#define TWI_STAT_RXD_ACK (0x50)
/* date byte received in master mode,not ack transmitted */
#define TWI_STAT_RXD_NAK (0x58)
/* Slave mode use only */
/* Slave address+Write bit received, ACK transmitted */
#define TWI_STAT_RXWS_ACK (0x60)
#define TWI_STAT_ARBLOST_RXWS_ACK (0x68)
/* General Call address received, ACK transmitted */
#define TWI_STAT_RXGCAS_ACK (0x70)
#define TWI_STAT_ARBLOST_RXGCAS_ACK (0x78)
#define TWI_STAT_RXDS_ACK (0x80)
#define TWI_STAT_RXDS_NAK (0x88)
#define TWI_STAT_RXDGCAS_ACK (0x90)
#define TWI_STAT_RXDGCAS_NAK (0x98)
#define TWI_STAT_RXSTPS_RXRESTAS (0xA0)
#define TWI_STAT_RXRS_ACK (0xA8)
#define TWI_STAT_ARBLOST_SLAR_ACK (0xB0)
#define TWI_STAT_SLV_TXD_ACK (0xB8)
#define TWI_STAT_SLV_TXD_NACK (0xC0)
#define TWI_STAT_SLV_TX_LAST_ACK (0xC8)
/* 10bit Address, second part of address */
/* Second Address byte+Write bit transmitted,ACK received */
#define TWI_STAT_TX_SAW_ACK (0xD0)
/* Second Address byte+Write bit transmitted,ACK not received */
#define TWI_STAT_TX_SAW_NAK (0xD8)
/* No relevant status information,INT_FLAG = 0 */
#define TWI_STAT_IDLE (0xF8)
/* status erro */
#define TWI_STAT_ERROR (0xF9)
/* TWI_CCR */
/*
* 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;standard mode:100KHz or fast mode:400KHz
*/
#define TWI_CLK_DUTY (0x1 << 7) /* 7bit */
#define TWI_CLK_DUTY_30 (0x1 << 8) /* 8bit */
#define TWI_CLK_DIV_M_OFFSET 3
#define TWI_CLK_DIV_M (0xf << TWI_CLK_DIV_M_OFFSET) /* 6:3bit */
#define TWI_CLK_DIV_N_OFFSET 0
#define TWI_CLK_DIV_N (0x7 << TWI_CLK_DIV_N_OFFSET) /* 2:0bit */
/* TWI_SRST */
/* write 1 to clear 0, when complete soft reset clear 0 */
#define TWI_SOFT_RST (0x1 << 0)
/* TWI_EFR */
/* default -- 0x0 */
/* 00:no,01: 1byte, 10:2 bytes, 11: 3bytes */
#define TWI_EFR_MASK (0x3 << 0)
#define NO_DATA_WROTE (0x0 << 0)
#define BYTE_DATA_WROTE (0x1 << 0)
#define BYTES_DATA1_WROTE (0x2 << 0)
#define BYTES_DATA2_WROTE (0x3 << 0)
/* TWI_LCR */
#define SCL_STATE (0x1 << 5)
#define SDA_STATE (0x1 << 4)
#define SCL_CTL (0x1 << 3)
#define SCL_CTL_EN (0x1 << 2)
#define SDA_CTL (0x1 << 1)
#define SDA_CTL_EN (0x1 << 0)
#define TWI_DRV_CTRL (0x200)
#define TWI_DRV_CFG (0x204)
#define TWI_DRV_SLV (0x208)
#define TWI_DRV_FMT (0x20C)
#define TWI_DRV_BUS_CTRL (0x210)
#define TWI_DRV_INT_CTRL (0x214)
#define TWI_DRV_DMA_CFG (0x218)
#define TWI_DRV_FIFO_CON (0x21C)
#define TWI_DRV_SEND_FIFO_ACC (0x300)
#define TWI_DRV_RECV_FIFO_ACC (0x304)
/* TWI_DRV_CTRL */
/* 0:module disable; 1:module enable; only use in TWI master Mode */
#define TWI_DRV_EN (0x01 << 0)
/* 0:normal; 1:reset */
#define SOFT_RESET (0x01 << 1)
#define TIMEOUT_N_OFFSET 8
#define TIMEOUT_N (0xff << TIMEOUT_N_OFFSET)
#define TWI_DRV_STAT_OFFSET 16
#define TWI_DRV_STAT_MASK (0xff << TWI_DRV_STAT_OFFSET)
#define TRAN_RESULT (0x07 << 24)
/* 0:send slave_id + W; 1:do not send slave_id + W */
#define READ_TRAN_MODE (0x01 << 28)
/* 0:restart; 1:STOP + START */
#define RESTART_MODE (0x01 << 29)
/* 0:transmission idle; 1:start transmission */
#define START_TRAN (0x01 << 31)
/* TWI_DRV_CFG */
#define PACKET_CNT_OFFSET 0
#define PACKET_CNT (0xffff << PACKET_CNT_OFFSET)
#define PACKET_INTERVAL_OFFSET 16
#define PACKET_INTERVAL (0xffff << PACKET_INTERVAL_OFFSET)
/* TWI_DRV_SLV */
#define SLV_ID_X_OFFSET 0
#define SLV_ID_X (0xff << SLV_ID_X_OFFSET)
#define CMD (0x01 << 8)
#define SLV_ID_OFFSET 9
#define SLV_ID (0x7f << SLV_ID_OFFSET)
/* TWI_DRV_FMT */
/* how many bytes be sent/received as data */
#define DATA_BYTE_OFFSET 0
#define DATA_BYTE (0xffff << DATA_BYTE_OFFSET)
/* how many btyes be sent as slave device reg address */
#define ADDR_BYTE_OFFSET 16
#define ADDR_BYTE (0xff << ADDR_BYTE_OFFSET)
/* TWI_DRV_BUS_CTRL */
/* SDA manual output en */
#define SDA_MOE (0x01 << 0)
/* SCL manual output en */
#define SCL_MOE (0x01 << 1)
/* SDA manual output value */
#define SDA_MOV (0x01 << 2)
/* SCL manual output value */
#define SCL_MOV (0x01 << 3)
/* SDA current status */
#define SDA_STA (0x01 << 6)
/* SCL current status */
#define SCL_STA (0x01 << 7)
#define TWI_DRV_CLK_M_OFFSET 8
#define TWI_DRV_CLK_M (0x0f << TWI_DRV_CLK_M_OFFSET)
#define TWI_DRV_CLK_N_OFFSET 12
#define TWI_DRV_CLK_N (0x07 << TWI_DRV_CLK_N_OFFSET)
#define TWI_DRV_CLK_DUTY (0x01 << 15)
#define TWI_DRV_COUNT_MODE (0x01 << 16)
#define TWI_DRV_CLK_DUTY_30 (0x01 << 17)
/* TWI_DRV_INT_CTRL */
#define TRAN_COM_PD (0x1 << 0)
#define TRAN_ERR_PD (0x1 << 1)
#define TX_REQ_PD (0x1 << 2)
#define RX_REQ_PD (0x1 << 3)
#define TRAN_COM_INT_EN (0x1 << 16)
#define TRAN_ERR_INT_EN (0x1 << 17)
#define TX_REQ_INT_EN (0x1 << 18)
#define RX_REQ_INT_EN (0x1 << 19)
#define TWI_DRV_INT_EN_MASK (0x0f << 16)
#define TWI_DRV_INT_STA_MASK (0x0f << 0)
/* TWI_DRV_DMA_CFG */
#define TX_TRIG_OFFSET 0
#define TX_TRIG (0x3f << TX_TRIG_OFFSET)
#define DMA_TX_EN (0x01 << 8)
#define RX_TRIG_OFFSET 16
#define RX_TRIG (0x3f << RX_TRIG_OFFSET)
#define DMA_RX_EN (0x01 << 24)
#define TWI_DRQEN_MASK (DMA_TX_EN | DMA_RX_EN)
/* TWI_DRV_FIFO_CON */
/* the number of data in SEND_FIFO */
#define SEND_FIFO_CONTENT_OFFSET 0
#define SEND_FIFO_CONTENT (0x3f << SEND_FIFO_CONTENT_OFFSET)
/* Set this bit to clear SEND_FIFO pointer, and this bit cleared automatically */
#define SEND_FIFO_CLEAR (0x01 << 5)
#define RECV_FIFO_CONTENT_OFFSET 16
#define RECV_FIFO_CONTENT (0x3f << RECV_FIFO_CONTENT_OFFSET)
#define RECV_FIFO_CLEAR (0x01 << 22)
/* TWI_DRV_SEND_FIFO_ACC */
#define SEND_DATA_FIFO (0xff << 0)
/* TWI_DRV_RECV_FIFO_ACC */
#define RECV_DATA_FIFO (0xff << 0)
/* end of twi regiter offset */
#define LOOP_TIMEOUT 1024
#define TWI_FREQ_100K 100000
#define TWI_FREQ_200K 200000
#define TWI_FREQ_400K 400000
#define AUTOSUSPEND_TIMEOUT 5000
#define HEXADECIMAL (0x10)
#define REG_INTERVAL (0x04)
#define REG_CL (0x0c)
#define DMA_THRESHOLD 32
#define MAX_FIFO 32
#define DMA_TIMEOUT 1000
#define TWI_READ true
#define TWI_WRITE false
#define TWI_ATOMIC_TIMEOUT_US 2000
#define TWI_DRV_IRQ 1
#define TWI_ENGINE_IRQ 0
#define TWI_DEBUG
unsigned int debug_chan_flags; /* Represents a global variable that allows dynamic printing */
#if defined(TWI_DEBUG)
#define TWI_DBG(twi, fmt...) { \
if ((0x1 << twi->bus_num) & debug_chan_flags) { \
sunxi_debug(twi->dev, fmt); \
}}
#else
#define TWI_DBG(twi, fmt...)
#endif /* endif defined(TWI_DEBUG) */
/* twi transfer status twi->status */
enum SUNXI_TWI_XFER_STATUS {
/* For master mode */
SUNXI_TWI_XFER_STATUS_ERROR = -1,
SUNXI_TWI_XFER_STATUS_IDLE = 0,
SUNXI_TWI_XFER_STATUS_RUNNING,
SUNXI_TWI_XFER_STATUS_SHUTDOWN,
#if IS_ENABLED(CONFIG_I2C_SLAVE)
/* For slave mode */
SUNXI_TWI_XFER_STATUS_SLAVE_IDLE,
SUNXI_TWI_XFER_STATUS_SLAVE_SADDR,
SUNXI_TWI_XFER_STATUS_SLAVE_WDATA,
SUNXI_TWI_XFER_STATUS_SLAVE_RDATA,
SUNXI_TWI_XFER_STATUS_SLAVE_ERROR,
#endif
};
struct sunxi_twi_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_twi_hw_data {
bool has_clk_duty_30; /* duty cycle 30% of Clock as Master */
bool slave_func_fixed; /* fixed the sda/sck issue under slave mode */
};
struct sunxi_twi {
/* twi framework datai */
struct i2c_adapter adap;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
struct i2c_client *slave;
#endif
struct platform_device *pdev;
struct device *dev;
struct i2c_msg *msg;
/* the total num of msg */
unsigned int msg_num;
/* the current msg index -> msg[msg_idx] */
unsigned int msg_idx;
/* the current msg's buf data index -> msg->buf[buf_idx] */
unsigned int buf_idx;
/* for twi core bus lock */
struct mutex bus_lock;
/* dts data */
struct resource *res;
void __iomem *base_addr;
struct clk *bus_clk;
struct reset_control *reset;
unsigned int bus_freq;
struct regulator *regulator;
char regulator_id[16];
struct pinctrl *pctrl;
int irq;
int irq_flag;
unsigned int twi_drv_used;
unsigned int no_suspend;
unsigned int pkt_interval;
struct sunxi_twi_dma *dma_tx;
struct sunxi_twi_dma *dma_rx;
struct sunxi_twi_dma *dma_using;
u8 *dma_buf;
u32 vol; /* the twi io voltage */
/* other data */
int bus_num;
enum SUNXI_TWI_XFER_STATUS status; /* error, idle, running, shutdown */
unsigned int debug_state; /* log the twi machine state */
const struct sunxi_twi_hw_data *data;
spinlock_t lock; /* syn */
wait_queue_head_t wait;
struct completion cmd_complete;
unsigned int reg1[16]; /* store the twi engined mode resigter status */
unsigned int reg2[16]; /* store the twi drv mode regiter status */
#if defined(CONFIG_AW_TWI_DELAYINIT) || defined(CONFIG_SUNXI_I2C_DELAYINIT)
const char *rproc_ser_name;
char rproc_device_name[16];
bool delay_init_done;
#endif /* CONFIG_AW_TWI_DELAYINIT */
};
#ifdef TWI_DEBUG
static void sunxi_twi_dump_reg(struct sunxi_twi *twi, 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)(twi->res->start + offset + i));
cnt += sprintf(buf + cnt, "%08x ",
readl(twi->base_addr + offset + i));
if (i % HEXADECIMAL == REG_CL) {
TWI_DBG(twi, "%s\n", buf);
cnt = 0;
}
}
}
#else
static void sunxi_twi_dump_reg(struct sunxi_twi *twi, u32 offset, u32 len) { }
#endif
/* clear the interrupt flag,
* the twi bus xfer status (register TWI_STAT) will changed as following
*/
static void sunxi_twi_engine_clear_irq(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + TWI_CNTR);
/* start and stop bit should be 0 */
reg_val |= INT_FLAG;
reg_val &= ~(M_STA | M_STP);
writel(reg_val, base_addr + TWI_CNTR);
}
/* only when get the last data, we will clear the flag when stop */
static inline void
sunxi_twi_engine_get_byte(void __iomem *base_addr, unsigned char *buffer)
{
*buffer = (unsigned char)(TWI_DATA_MASK & readl(base_addr + TWI_DATA));
}
/* write data and clear irq flag to trigger send flow */
static void
sunxi_twi_engine_put_byte(struct sunxi_twi *twi, const unsigned char *buffer)
{
unsigned int reg_val;
reg_val = *buffer & TWI_DATA_MASK;
writel(reg_val, twi->base_addr + TWI_DATA);
TWI_DBG(twi, "engine-mode: data 0x%x xfered\n", reg_val);
}
static void sunxi_twi_engine_enable_irq(void __iomem *base_addr)
{
unsigned int reg_val = readl(base_addr + TWI_CNTR);
/*
* 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 |= INT_EN;
reg_val &= ~(INT_FLAG | M_STA | M_STP);
writel(reg_val, base_addr + TWI_CNTR);
}
static void sunxi_twi_engine_disable_irq(void __iomem *base_addr)
{
unsigned int reg_val = readl(base_addr + TWI_CNTR);
reg_val &= ~INT_EN;
reg_val &= ~(INT_FLAG | M_STA | M_STP);
writel(reg_val, base_addr + TWI_CNTR);
}
static void sunxi_twi_bus_enable(struct sunxi_twi *twi)
{
unsigned int reg_val;
if (twi->twi_drv_used) {
reg_val = readl(twi->base_addr + TWI_DRV_CTRL);
reg_val |= TWI_DRV_EN;
writel(reg_val, twi->base_addr + TWI_DRV_CTRL);
} else {
reg_val = readl(twi->base_addr + TWI_CNTR);
reg_val |= BUS_EN;
writel(reg_val, twi->base_addr + TWI_CNTR);
}
}
static void sunxi_twi_bus_disable(struct sunxi_twi *twi)
{
unsigned int reg_val;
if (twi->twi_drv_used) {
reg_val = readl(twi->base_addr + TWI_DRV_CTRL);
reg_val &= ~TWI_DRV_EN;
writel(reg_val, twi->base_addr + TWI_DRV_CTRL);
} else {
reg_val = readl(twi->base_addr + TWI_CNTR);
reg_val &= ~BUS_EN;
writel(reg_val, twi->base_addr + TWI_CNTR);
}
}
/* trigger start signal, the start bit will be cleared automatically */
static void sunxi_twi_engine_set_start(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + TWI_CNTR);
reg_val |= M_STA;
reg_val &= ~(INT_FLAG);
writel(reg_val, base_addr + TWI_CNTR);
}
/* trigger stop signal and clear int flag
* the stop bit will be cleared automatically
*/
static void sunxi_twi_engine_set_stop(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + TWI_CNTR);
reg_val |= M_STP;
reg_val &= ~INT_FLAG;
writel(reg_val, base_addr + TWI_CNTR);
}
/* get stop bit status, poll if stop signal is sent */
static inline unsigned int sunxi_twi_engine_get_stop(void __iomem *base_addr)
{
unsigned int reg_val = readl(base_addr + TWI_CNTR);
return reg_val & M_STP;
}
/* when sending ack or nack, it will send ack automatically */
static inline void sunxi_twi_engine_enable_ack(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + TWI_CNTR);
reg_val |= A_ACK;
writel(reg_val, base_addr + TWI_CNTR);
}
static inline void sunxi_twi_engine_disable_ack(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + TWI_CNTR);
reg_val &= ~A_ACK;
writel(reg_val, base_addr + TWI_CNTR);
}
/* check the engine-mode or drv-mode irq coming or not with irq enable or not
* return TWI_ENGINE_IRQ on engine-mode interrupt is coming with irq is enabled
* return TWI_DRV_IRQ on drv-mode interrupt is coming with irq is enabled
* otherwise return the error num
*/
static unsigned int sunxi_twi_check_irq(struct sunxi_twi *twi)
{
u32 status;
if (twi->twi_drv_used) {
status = readl(twi->base_addr + TWI_DRV_INT_CTRL);
if ((status & TWI_DRV_INT_EN_MASK) && (status & TWI_DRV_INT_STA_MASK))
return TWI_DRV_IRQ;
} else {
status = readl(twi->base_addr + TWI_CNTR);
if ((status & INT_EN) && (status & INT_FLAG))
return TWI_ENGINE_IRQ;
}
return -EINVAL;
}
/* get the twi controller current xfer status */
static unsigned int sunxi_twi_get_xfer_sta(struct sunxi_twi *twi)
{
u32 status;
if (twi->twi_drv_used) {
status = readl(twi->base_addr + TWI_DRV_CTRL) & TWI_DRV_STAT_MASK;
status >>= TWI_DRV_STAT_OFFSET;
} else {
status = readl(twi->base_addr + TWI_STAT) & TWI_STAT_MASK;
}
return status;
}
/* set twi controller clock
* clk_n: clock divider factor n
* clk_m: clock divider factor m
*/
static void sunxi_twi_set_clock(struct sunxi_twi *twi, u8 clk_m, u8 clk_n)
{
u32 clk_n_mask, clk_n_offset, clk_m_offset, clk_m_mask;
u32 reg, duty, duty_30, reg_val;
/* @IP-TODO
* drv-mode set clkm and clkn bit to adjust frequency, finally the
* TWI_DRV_BUS_CTRL register will not be changed, the value of the TWI_CCR
* register will represent the current drv-mode operating frequency
*/
if (twi->twi_drv_used) {
reg = TWI_DRV_BUS_CTRL;
clk_n_mask = TWI_DRV_CLK_N;
clk_n_offset = TWI_DRV_CLK_N_OFFSET;
clk_m_mask = TWI_DRV_CLK_M;
clk_m_offset = TWI_DRV_CLK_M_OFFSET;
duty = TWI_DRV_CLK_DUTY;
duty_30 = TWI_DRV_CLK_DUTY_30;
} else {
reg = TWI_CCR;
clk_n_mask = TWI_CLK_DIV_N;
clk_n_offset = TWI_CLK_DIV_N_OFFSET;
clk_m_mask = TWI_CLK_DIV_M;
clk_m_offset = TWI_CLK_DIV_M_OFFSET;
duty = TWI_CLK_DUTY;
duty_30 = TWI_CLK_DUTY_30;
}
reg_val = readl(twi->base_addr + reg);
reg_val &= ~(clk_m_mask | clk_n_mask);
reg_val |= ((clk_m << clk_m_offset) & clk_m_mask);
reg_val |= ((clk_n << clk_n_offset) & clk_n_mask);
switch (twi->bus_freq) {
case TWI_FREQ_100K:
/* Clock duty 50% */
reg_val &= ~(duty);
break;
case TWI_FREQ_200K:
/* Clock duty 40% */
reg_val |= duty;
break;
case TWI_FREQ_400K:
if (twi->data->has_clk_duty_30)
/* Clock duty 30% */
reg_val |= duty_30;
else
/* Clock duty 40% */
reg_val |= duty;
break;
default:
sunxi_err(twi->dev, "unsupport duty with bus freq %d\n", twi->bus_freq);
}
writel(reg_val, twi->base_addr + reg);
}
static int sunxi_twi_set_frequency(struct sunxi_twi *twi)
{
u8 clk_m = 0, clk_n = 0, _2_pow_clk_n = 1;
unsigned int clk_in, clk_src, divider, clk_real;
clk_in = clk_get_rate(twi->bus_clk);
if (clk_in == 0) {
sunxi_err(twi->dev, "get clock freq failed\n");
return -EINVAL;
}
TWI_DBG(twi, "get clock freq is %u\n", clk_in);
clk_src = clk_in / 10;
divider = clk_src / twi->bus_freq;
if (!divider) {
clk_m = 1;
goto twi_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) */
clk_real = clk_src / (clk_m + 1) / _2_pow_clk_n;
if (clk_real <= twi->bus_freq)
goto twi_set_clk;
else
clk_m++;
}
clk_n++;
_2_pow_clk_n *= 2; /* mutilple by 2 */
}
twi_set_clk:
sunxi_twi_set_clock(twi, clk_m, clk_n);
return 0;
}
/*
* twi controller soft_reset can only clear flag bit inside of ip, include the
* state machine parameters, counters, various flags, fifo, fifo-cnt.
*
* But the internal configurations or external register configurations of ip
* will not be changed.
*/
static void sunxi_twi_soft_reset(struct sunxi_twi *twi)
{
u32 reg_val, reg, mask;
int ret;
if (twi->twi_drv_used) {
reg = TWI_DRV_CTRL;
mask = SOFT_RESET;
} else {
reg = TWI_SRST;
mask = TWI_SOFT_RST;
}
reg_val = readl(twi->base_addr + reg);
reg_val |= mask;
writel(reg_val, twi->base_addr + reg);
if (twi->twi_drv_used) {
/*
* @IP-TODO
* drv-mode soft_reset bit will not clear automatically, write 0 to unreset.
* The reset only takes one or two CPU clk cycle.
*/
udelay(5);
reg_val &= (~mask);
writel(reg_val, twi->base_addr + reg);
} else {
/* Hardware will auto clear this bit when soft reset
* Before return, driver must wait reset opertion complete */
ret = readl_poll_timeout_atomic(twi->base_addr + reg, reg_val, !(reg_val & mask), 5, 1000);
if (ret) {
sunxi_err(twi->dev, "timeout for waiting soft reset (0x%x)\n", reg_val);
return ;
}
}
}
#if IS_ENABLED(CONFIG_I2C_SLAVE)
static void sunxi_twi_slave_reset(struct sunxi_twi *twi)
{
sunxi_twi_engine_clear_irq(twi->base_addr);
sunxi_twi_engine_disable_ack(twi->base_addr);
sunxi_twi_soft_reset(twi);
sunxi_twi_engine_enable_ack(twi->base_addr);
twi->status = SUNXI_TWI_XFER_STATUS_SLAVE_IDLE;
}
#endif
/* iset the data byte number follow read command control */
static void sunxi_twi_engine_set_efr(void __iomem *base_addr, u32 efr)
{
u32 reg_val;
reg_val = readl(base_addr + TWI_EFR);
reg_val &= ~TWI_EFR_MASK;
efr &= TWI_EFR_MASK;
reg_val |= efr;
writel(reg_val, base_addr + TWI_EFR);
}
static int sunxi_twi_engine_stop(struct sunxi_twi *twi)
{
int err;
void __iomem *base_addr = twi->base_addr;
/* After setting the M_STP position to 1 in TWI_CNTR, the hardware
* will automatically send a stop signal and clear the M_STP bit.
* The clearing action may occur before reading the register again.
* Therefore, no register read-back check is performed when sending
* the stop signal.
*/
sunxi_twi_engine_set_stop(base_addr);
TWI_DBG(twi, "Send stop finish\n");
/* twi bus xfer status will chaned after irq flag cleared */
sunxi_twi_engine_clear_irq(base_addr);
err = wait_field_equ((twi->base_addr + TWI_STAT), TWI_STAT_MASK,
TWI_STAT_IDLE, TWI_ATOMIC_TIMEOUT_US);
if (err) {
sunxi_err(twi->dev, "engine-mode: bus state: 0x%0x, isn't idle\n",
sunxi_twi_get_xfer_sta(twi));
return -EINVAL;
}
TWI_DBG(twi, "engine-mode: stop signal xfered");
return 0;
}
static void sunxi_twi_set_clk_count_mode(struct sunxi_twi *twi, u8 mode)
{
u32 reg_val, reg_ctrl, reg;
if (twi->twi_drv_used) {
reg = TWI_DRV_BUS_CTRL;
reg_ctrl = TWI_DRV_COUNT_MODE;
/* @IP-TODO
* drv-mode set TWI_DRV_BUS_CTRL register will not be changed.
* do not write the value that may destory the normal configure.
* Cause this issue, clock stretching cannot be used under drv-mode.
*/
return ;
} else {
reg = TWI_CNTR;
reg_ctrl = CLK_COUNT_MODE;
}
reg_val = readl(twi->base_addr + reg);
if (mode)
reg_val |= reg_ctrl;
else
reg_val &= ~reg_ctrl;
writel(reg_val, twi->base_addr + reg);
}
static void sunxi_twi_scl_control_enable(struct sunxi_twi *twi)
{
u32 reg_val, reg_ctrl, reg;
if (twi->twi_drv_used) {
reg = TWI_DRV_BUS_CTRL;
reg_ctrl = SCL_MOE;
} else {
reg = TWI_LCR;
reg_ctrl = SCL_CTL_EN;
}
reg_val = readl(twi->base_addr + reg);
reg_val |= reg_ctrl;
writel(reg_val, twi->base_addr + reg);
}
static void sunxi_twi_scl_control_disable(struct sunxi_twi *twi)
{
u32 reg_val, reg_ctrl, reg;
if (twi->twi_drv_used) {
reg = TWI_DRV_BUS_CTRL;
reg_ctrl = SCL_MOE;
} else {
reg = TWI_LCR;
reg_ctrl = SCL_CTL_EN;
}
reg_val = readl(twi->base_addr + reg);
reg_val &= ~(reg_ctrl);
writel(reg_val, twi->base_addr + reg);
}
static int sunxi_twi_get_scl(struct i2c_adapter *adap)
{
struct sunxi_twi *twi = (struct sunxi_twi *)adap->algo_data;
if (twi->twi_drv_used)
return !!(readl(twi->base_addr + TWI_DRV_BUS_CTRL) & SCL_STA);
else
return !!(readl(twi->base_addr + TWI_LCR) & SCL_STATE);
}
static void sunxi_twi_set_scl(struct i2c_adapter *adap, int val)
{
struct sunxi_twi *twi = (struct sunxi_twi *)adap->algo_data;
u32 reg_val, status, reg;
sunxi_twi_scl_control_enable(twi);
if (twi->twi_drv_used) {
reg = TWI_DRV_BUS_CTRL;
status = SCL_MOV;
} else {
reg = TWI_LCR;
status = SCL_CTL;
}
reg_val = readl(twi->base_addr + reg);
TWI_DBG(twi, "set scl, val:%x, val:%d\n", reg_val, val);
if (val)
reg_val |= status;
else
reg_val &= ~(status);
writel(reg_val, twi->base_addr + reg);
sunxi_twi_scl_control_disable(twi);
}
static int sunxi_twi_get_sda(struct i2c_adapter *adap)
{
struct sunxi_twi *twi = (struct sunxi_twi *)adap->algo_data;
if (twi->twi_drv_used)
return !!(readl(twi->base_addr + TWI_DRV_BUS_CTRL) & SDA_STA);
else
return !!(readl(twi->base_addr + TWI_LCR) & SDA_STATE);
}
static int sunxi_twi_get_bus_free(struct i2c_adapter *adap)
{
return sunxi_twi_get_sda(adap);
}
/* get the irq enabled */
static unsigned int sunxi_twi_drv_get_irq(void __iomem *base_addr)
{
unsigned int sta, irq;
irq = (readl(base_addr + TWI_DRV_INT_CTRL) & TWI_DRV_INT_EN_MASK) >> 16;
sta = readl(base_addr + TWI_DRV_INT_CTRL) & TWI_DRV_INT_STA_MASK;
return irq & sta;
}
static void sunxi_twi_drv_clear_irq(void __iomem *base_addr, u32 bitmap)
{
unsigned int reg_val = readl(base_addr + TWI_DRV_INT_CTRL);
reg_val |= (bitmap & TWI_DRV_INT_STA_MASK);
writel(reg_val, base_addr + TWI_DRV_INT_CTRL);
}
static void sunxi_twi_drv_enable_irq(void __iomem *base_addr, u32 bitmap)
{
u32 reg_val = readl(base_addr + TWI_DRV_INT_CTRL);
reg_val |= bitmap;
reg_val &= ~TWI_DRV_INT_STA_MASK;
writel(reg_val, base_addr + TWI_DRV_INT_CTRL);
}
static void sunxi_twi_drv_disable_irq(void __iomem *base_addr, u32 bitmap)
{
u32 reg_val = readl(base_addr + TWI_DRV_INT_CTRL);
reg_val &= ~bitmap;
reg_val &= ~TWI_DRV_INT_STA_MASK;
writel(reg_val, base_addr + TWI_DRV_INT_CTRL);
}
static void sunxi_twi_drv_enable_dma_irq(void __iomem *base_addr, u32 bitmap)
{
u32 reg_val = readl(base_addr + TWI_DRV_DMA_CFG);
bitmap &= TWI_DRQEN_MASK;
reg_val |= bitmap;
writel(reg_val, base_addr + TWI_DRV_DMA_CFG);
}
static void sunxi_twi_drv_disable_dma_irq(void __iomem *base_addr, u32 bitmap)
{
u32 reg_val = readl(base_addr + TWI_DRV_DMA_CFG);
bitmap &= TWI_DRQEN_MASK;
reg_val &= ~bitmap;
writel(reg_val, base_addr + TWI_DRV_DMA_CFG);
}
/*
* The engine-mode bus_en bit is automatically set to 1 after the drv-mode START_TRAN bit set to 1.
* (because drv-mode rely on engine-mode)
*/
static void sunxi_twi_drv_start_xfer(struct sunxi_twi *twi)
{
u32 reg_val = readl(twi->base_addr + TWI_DRV_CTRL);
reg_val |= START_TRAN;
writel(reg_val, twi->base_addr + TWI_DRV_CTRL);
TWI_DBG(twi, "drv-mode: start signal xfered\n");
}
static void sunxi_twi_drv_set_tx_trig(void __iomem *base_addr, u32 trig)
{
u32 reg_val = readl(base_addr + TWI_DRV_DMA_CFG);
reg_val &= ~TX_TRIG;
reg_val |= (trig << TX_TRIG_OFFSET);
writel(reg_val, base_addr + TWI_DRV_DMA_CFG);
}
/* When one of the following conditions is met:
* 1. The number of data (in bytes) in RECV_FIFO reaches RX_TRIG;
* 2. Packet read done and RECV_FIFO not empty.
* If RX_REQ is enabled, the rx-pending-bit will be set to 1 and the interrupt will be triggered;
* If RX_REQ is disabled, the rx-pending-bit will be set to 1 but the interrupt will NOT be triggered.
*/
static void sunxi_twi_drv_set_rx_trig(void __iomem *base_addr, u32 trig)
{
u32 reg_val;
reg_val = readl(base_addr + TWI_DRV_DMA_CFG);
reg_val &= ~RX_TRIG;
reg_val |= (trig << RX_TRIG_OFFSET);
writel(reg_val, base_addr + TWI_DRV_DMA_CFG);
}
/* bytes be send as slave device reg address */
static void sunxi_twi_drv_set_addr_byte(void __iomem *base_addr, u32 len)
{
u32 reg_val = readl(base_addr + TWI_DRV_FMT);
reg_val &= ~ADDR_BYTE;
reg_val |= (len << ADDR_BYTE_OFFSET);
writel(reg_val, base_addr + TWI_DRV_FMT);
}
/* bytes be send/received as data */
static void sunxi_twi_drv_set_data_byte(void __iomem *base_addr, u32 len)
{
u32 val = readl(base_addr + TWI_DRV_FMT);
val &= ~DATA_BYTE;
val |= (len << DATA_BYTE_OFFSET);
writel(val, base_addr + TWI_DRV_FMT);
}
/* interval between each packet in 32*Fscl cycles */
/*
static void sunxi_twi_drv_set_packet_interval(void __iomem *base_addr, u32 val)
{
u32 reg_val = readl(base_addr + TWI_DRV_CFG);
reg_val &= ~PACKET_INTERVAL;
reg_val |= (val << PACKET_INTERVAL_OFFSET);
writel(reg_val, base_addr + TWI_DRV_CFG);
}
*/
/* FIFO data be transmitted as PACKET_CNT packets in current format */
static void sunxi_twi_drv_set_packet_cnt(void __iomem *base_addr, u32 val)
{
u32 reg_val = readl(base_addr + TWI_DRV_CFG);
reg_val &= ~PACKET_CNT;
reg_val |= (val << PACKET_CNT_OFFSET);
writel(reg_val, base_addr + TWI_DRV_CFG);
}
/* do not send slave_id +W */
static void sunxi_twi_drv_enable_read_mode(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + TWI_DRV_CTRL);
reg_val |= READ_TRAN_MODE;
writel(reg_val, base_addr + TWI_DRV_CTRL);
}
/* send slave_id + W */
static void sunxi_twi_drv_disable_read_mode(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + TWI_DRV_CTRL);
reg_val &= ~READ_TRAN_MODE;
writel(reg_val, base_addr + TWI_DRV_CTRL);
}
static void
sunxi_twi_drv_set_slave_addr(struct sunxi_twi *twi, struct i2c_msg *msgs)
{
unsigned int reg_val = 0;
/* read, default value is write */
if (msgs->flags & I2C_M_RD)
reg_val |= CMD;
else
reg_val &= ~CMD;
if (msgs->flags & I2C_M_TEN) {
/* SLV_ID | CMD | SLV_ID_X */
reg_val |= ((0x78 | ((msgs->addr >> 8) & 0x03)) << 9);
reg_val |= (msgs->addr & 0xff);
TWI_DBG(twi, "drv-mode: first 10bit(0x%x) xfered\n", msgs->addr);
} else {
reg_val |= ((msgs->addr & 0x7f) << 9);
TWI_DBG(twi, "drv-mode: 7bits(0x%x) + r/w xfered", msgs->addr);
}
writel(reg_val, twi->base_addr + TWI_DRV_SLV);
}
static void sunxi_twi_drv_clear_txfifo(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + TWI_DRV_FIFO_CON);
reg_val |= SEND_FIFO_CLEAR;
writel(reg_val, base_addr + TWI_DRV_FIFO_CON);
}
static void sunxi_twi_drv_clear_rxfifo(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + TWI_DRV_FIFO_CON);
reg_val |= RECV_FIFO_CLEAR;
writel(reg_val, base_addr + TWI_DRV_FIFO_CON);
}
static int sunxi_twi_drv_send_msg(struct sunxi_twi *twi, struct i2c_msg *msg)
{
u16 i;
u32 reg_val, reg_val_again;
int ret;
TWI_DBG(twi, "drv-mode: tx msg len is %d\n", msg->len);
for (i = 0; i < msg->len; i++) {
ret = readl_poll_timeout_atomic(twi->base_addr + TWI_DRV_FIFO_CON,
reg_val, !((reg_val & SEND_FIFO_CONTENT) >= MAX_FIFO),
5, 100);
if (ret) {
sunxi_err(twi->dev, "drv-mode: SEND FIFO overflow, timeout\n");
return -EINVAL;
}
writeb(msg->buf[i], twi->base_addr + TWI_DRV_SEND_FIFO_ACC);
reg_val_again = readl(twi->base_addr + TWI_DRV_FIFO_CON);
TWI_DBG(twi, "drv-mode: write Byte[%u]=0x%x,tx fifo len=%d\n",
i, msg->buf[i], (reg_val_again & SEND_FIFO_CONTENT));
}
return 0;
}
static u32 sunxi_twi_drv_recv_msg(struct sunxi_twi *twi, struct i2c_msg *msg)
{
u16 i;
u32 reg_val;
int ret;
TWI_DBG(twi, "drv-mode: rx msg len is %d\n", msg->len);
for (i = 0; i < msg->len; i++) {
ret = readl_poll_timeout_atomic(twi->base_addr + TWI_DRV_FIFO_CON,
reg_val, ((reg_val & RECV_FIFO_CONTENT) >> RECV_FIFO_CONTENT_OFFSET),
5, 100);
if (ret) {
sunxi_err(twi->dev, "drv-mode: rerceive fifo empty. timeout\n");
return -EINVAL;
}
msg->buf[i] = readb(twi->base_addr + TWI_DRV_RECV_FIFO_ACC);
TWI_DBG(twi, "drv-mode: readb: Byte[%d] = 0x%x\n",
i, msg->buf[i]);
}
return 0;
}
static void sunxi_twi_dma_callback(void *arg)
{
struct sunxi_twi *twi = (struct sunxi_twi *)arg;
if (twi->dma_tx) {
TWI_DBG(twi, "drv-mode: dma write data end\n");
} else {
TWI_DBG(twi, "drv-mode: dma read data end\n");
}
complete(&twi->cmd_complete);
}
/* request dma channel */
static int sunxi_twi_dma_request(struct sunxi_twi *twi, dma_addr_t phy_addr)
{
struct sunxi_twi_dma *dma_tx, *dma_rx;
dma_cap_mask_t mask_tx, mask_rx;
struct dma_slave_config dma_sconfig;
int err;
dma_tx = devm_kzalloc(twi->dev, sizeof(*dma_tx), GFP_KERNEL);
dma_rx = devm_kzalloc(twi->dev, sizeof(*dma_rx), GFP_KERNEL);
if (IS_ERR_OR_NULL(dma_tx) || IS_ERR_OR_NULL(dma_rx)) {
sunxi_err(twi->dev, "dma kzalloc failed\n");
return -EINVAL;
}
dma_cap_zero(mask_tx);
dma_cap_set(DMA_SLAVE, mask_tx);
dma_tx->chan = dma_request_channel(mask_tx, NULL, NULL);
if (IS_ERR(dma_tx->chan)) {
sunxi_err(twi->dev, "can't request DMA tx channel\n");
err = PTR_ERR(dma_tx->chan);
goto err0;
}
dma_sconfig.dst_addr = phy_addr + TWI_DRV_SEND_FIFO_ACC;
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;
dma_sconfig.direction = DMA_MEM_TO_DEV;
#ifndef DRQDST_TWI0_TX
sunxi_err(twi->dev, "[twi%d] can't susport DMA for TX\n", twi->bus_num);
#else
dma_sconfig.slave_id = sunxi_slave_id(DRQDST_TWI0_TX + twi->bus_num,
DRQSRC_SDRAM);
#endif
err = dmaengine_slave_config(dma_tx->chan, &dma_sconfig);
if (err < 0) {
sunxi_err(twi->dev, "can't configure tx channel\n");
goto err1;
}
twi->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 (IS_ERR(dma_rx->chan)) {
sunxi_err(twi->dev, "can't request DMA rx channel\n");
goto err1;
}
dma_sconfig.src_addr = phy_addr + TWI_DRV_RECV_FIFO_ACC;
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;
dma_sconfig.direction = DMA_DEV_TO_MEM;
#ifndef DRQSRC_TWI0_RX
sunxi_err(twi->dev, "[twi%d] can't susport DMA for RX\n", twi->bus_num);
#else
dma_sconfig.slave_id = sunxi_slave_id(DRQSRC_SDRAM,
DRQSRC_TWI0_RX + twi->bus_num);
#endif
err = dmaengine_slave_config(dma_rx->chan, &dma_sconfig);
if (err < 0) {
sunxi_err(twi->dev, "can't configure rx channel\n");
goto err2;
}
twi->dma_rx = dma_rx;
init_completion(&twi->cmd_complete);
TWI_DBG(twi, "using %s (tx) and %s (rx) for DMA transfers\n",
dma_chan_name(twi->dma_tx->chan), dma_chan_name(twi->dma_rx->chan));
return 0;
err2:
dma_release_channel(twi->dma_rx->chan);
err1:
dma_release_channel(twi->dma_tx->chan);
err0:
return err;
}
static void sunxi_twi_dma_release(struct sunxi_twi *twi)
{
if (twi->dma_tx) {
twi->dma_tx->dma_buf = 0;
twi->dma_tx->dma_len = 0;
dma_release_channel(twi->dma_tx->chan);
twi->dma_tx->chan = NULL;
twi->dma_tx = NULL;
}
if (twi->dma_rx) {
twi->dma_rx->dma_buf = 0;
twi->dma_rx->dma_len = 0;
dma_release_channel(twi->dma_rx->chan);
twi->dma_rx->chan = NULL;
twi->dma_rx = NULL;
}
}
static int sunxi_twi_dma_init(struct sunxi_twi *twi, bool read)
{
unsigned long time_left;
struct sunxi_twi_dma *dma;
struct device *chan_dev;
struct dma_async_tx_descriptor *dma_desc;
int ret;
if (read) {
twi->dma_using = twi->dma_rx;
twi->dma_using->dma_transfer_dir = DMA_DEV_TO_MEM;
twi->dma_using->dma_data_dir = DMA_FROM_DEVICE;
twi->dma_using->dma_len = twi->msg->len;
} else {
twi->dma_using = twi->dma_tx;
twi->dma_using->dma_transfer_dir = DMA_MEM_TO_DEV;
twi->dma_using->dma_data_dir = DMA_TO_DEVICE;
twi->dma_using->dma_len = twi->msg->len;
}
dma = twi->dma_using;
chan_dev = dma->chan->device->dev;
dma->dma_buf = dma_map_single(chan_dev, twi->msg->buf,
dma->dma_len, dma->dma_data_dir);
if (dma_mapping_error(chan_dev, dma->dma_buf)) {
sunxi_err(twi->dev, "DMA mapping failed\n");
ret = -EINVAL;
goto err0;
}
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) {
sunxi_err(twi->dev, "Not able to get desc for DMA xfer\n");
ret = -EINVAL;
goto err0;
}
dma_desc->callback = sunxi_twi_dma_callback;
dma_desc->callback_param = twi;
if (dma_submit_error(dmaengine_submit(dma_desc))) {
sunxi_err(twi->dev, "DMA submit failed\n");
ret = -EINVAL;
goto err0;
}
reinit_completion(&twi->cmd_complete);
dma_async_issue_pending(dma->chan);
TWI_DBG(twi, "dma issue pending\n");
time_left = wait_for_completion_timeout(
&twi->cmd_complete,
msecs_to_jiffies(DMA_TIMEOUT));
TWI_DBG(twi, "[twi%d] time_left = %lu\n", twi->bus_num, time_left);
return 0;
err0:
return ret;
}
static int sunxi_twi_drv_dma_deinit(struct sunxi_twi *twi, struct sunxi_twi_dma **_info)
{
struct device *chan_dev = (*_info)->chan->device->dev;
dma_unmap_single(chan_dev, (*_info)->dma_buf, (*_info)->dma_len, (*_info)->dma_data_dir);
return 0;
}
static int sunxi_twi_dma_tx_config(struct sunxi_twi *twi)
{
int err;
sunxi_twi_drv_set_tx_trig(twi->base_addr, MAX_FIFO / 2);
sunxi_twi_drv_enable_dma_irq(twi->base_addr, DMA_TX_EN);
err = sunxi_twi_dma_init(twi, TWI_WRITE);
if (err) {
sunxi_err(twi->dev, "dma_tx xfer init failed\n");
goto err;
}
sunxi_twi_drv_start_xfer(twi);
return 0;
err:
sunxi_twi_dma_release(twi);
sunxi_twi_drv_disable_dma_irq(twi->base_addr, DMA_TX_EN);
twi->dma_tx = NULL;
return err;
}
static int sunxi_twi_dma_rx_config(struct sunxi_twi *twi)
{
int err;
sunxi_twi_drv_set_rx_trig(twi->base_addr, MAX_FIFO / 2);
sunxi_twi_drv_enable_dma_irq(twi->base_addr, DMA_RX_EN);
err = sunxi_twi_dma_init(twi, TWI_READ);
if (err) {
sunxi_err(twi->dev, "dma_rx xfer init failed\n");
goto err;
}
sunxi_twi_drv_start_xfer(twi);
return 0;
err:
sunxi_twi_dma_release(twi);
sunxi_twi_drv_disable_dma_irq(twi->base_addr, DMA_RX_EN);
twi->dma_rx = NULL;
return err;
}
/* 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
**/
static void sunxi_twi_engine_addr_byte(struct sunxi_twi *twi)
{
unsigned char addr = 0;
unsigned char tmp = 0;
if (twi->msg[twi->msg_idx].flags & I2C_M_TEN) {
/* 0111_10xx,ten bits address--9:8bits */
tmp = 0x78 | (((twi->msg[twi->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 = (twi->msg[twi->msg_idx].addr & 0x7f) << 1;
}
/* read, default value is write */
if (twi->msg[twi->msg_idx].flags & I2C_M_RD)
addr |= 1;
if (twi->msg[twi->msg_idx].flags & I2C_M_TEN) {
TWI_DBG(twi, "first part of 10bits = 0x%x\n", addr);
} else {
TWI_DBG(twi, "engine-mode: 7bits+r/w = 0x%x xfered\n", addr);
}
/* send 7bits+r/w or the first part of 10bits */
sunxi_twi_engine_put_byte(twi, &addr);
}
static int sunxi_twi_bus_barrier(struct i2c_adapter *adap)
{
int i, ret;
struct sunxi_twi *twi = (struct sunxi_twi *)adap->algo_data;
for (i = 0; i < LOOP_TIMEOUT; i++) {
if (sunxi_twi_get_bus_free(adap))
return 0;
udelay(1);
}
ret = i2c_recover_bus(adap);
sunxi_twi_soft_reset(twi);
return ret;
}
/* return 0 on success, otherwise return the negative error num */
static int sunxi_twi_drv_wait_complete(struct sunxi_twi *twi)
{
unsigned long timeout;
timeout = wait_event_timeout(twi->wait, twi->status != SUNXI_TWI_XFER_STATUS_RUNNING,
twi->adap.timeout);
if (timeout == 0) {
sunxi_err(twi->dev, "drv-mode: xfer timeout (dev addr:0x%x)\n",
twi->msg->addr);
sunxi_twi_dump_reg(twi, 0x200, 0x20);
return -ETIME;
}
if (twi->status == SUNXI_TWI_XFER_STATUS_ERROR) {
sunxi_err(twi->dev, "drv-mode: xfer failed (dev addr:0x%x)\n",
twi->msg->addr);
sunxi_twi_dump_reg(twi, 0x200, 0x20);
return -EINVAL;
} else if (twi->status == SUNXI_TWI_XFER_STATUS_IDLE) {
TWI_DBG(twi, "drv-mode: xfer complete\n");
} else {
sunxi_err(twi->dev, "drv-mode: result err\n");
return -EINVAL;
}
return 0;
}
/* return the xfer msgs num or the negative error num */
static int sunxi_twi_engine_wait_complete(struct sunxi_twi *twi)
{
unsigned long timeout;
int ret;
timeout = wait_event_timeout(twi->wait, twi->status != SUNXI_TWI_XFER_STATUS_RUNNING,
twi->adap.timeout);
if (timeout == 0) {
sunxi_err(twi->dev, "engine-mode: xfer timeout(dev addr:0x%x)\n",
twi->msg->addr);
sunxi_twi_dump_reg(twi, 0x00, 0x20);
return -ETIME;
}
if (twi->status == SUNXI_TWI_XFER_STATUS_ERROR) {
sunxi_err(twi->dev, "engine-mode: xfer failed(dev addr:0x%x)\n",
twi->msg->addr);
sunxi_twi_dump_reg(twi, 0x00, 0x20);
ret = -EINVAL;
} else if (twi->status == SUNXI_TWI_XFER_STATUS_IDLE) {
if (twi->msg_idx != twi->msg_num) {
sunxi_err(twi->dev, "engine-mode: xfer incomplete(dev addr:0x%x\n",
twi->msg->addr);
ret = -EINVAL;
} else {
TWI_DBG(twi, "engine-mode: xfer complete\n");
ret = twi->msg_idx;
}
} else {
sunxi_err(twi->dev, "engine-mode: result err\n");
ret = -EINVAL;
}
return ret;
}
static int sunxi_twi_drv_core_process(struct sunxi_twi *twi)
{
void __iomem *base_addr = twi->base_addr;
unsigned int irq, err_sta;
irq = sunxi_twi_drv_get_irq(base_addr);
sunxi_twi_drv_clear_irq(twi->base_addr, irq);
TWI_DBG(twi, "drv-mode: the enabled irq 0x%x coming\n", irq);
if ((irq & TRAN_COM_PD) && (irq & RX_REQ_PD)) { /* the last RX_IRQ and COMPLETE_IRQ "simultaneous trigger" */
if (sunxi_twi_drv_recv_msg(twi, twi->msg))
twi->status = SUNXI_TWI_XFER_STATUS_ERROR;
else
twi->status = SUNXI_TWI_XFER_STATUS_IDLE;
sunxi_twi_drv_disable_irq(twi->base_addr, TWI_DRV_INT_EN_MASK);
wake_up(&twi->wait);
return 0;
} else if (irq & TRAN_COM_PD) { /* read or write packages is complete */
/*
* current read opration not end, go on and get data by cpu or dma
* cpu read tiggered by RX_REQ_PD (RECV_FIFO not empty)
* dma read tiggered by DMA_RX_Req(RECV_FIFO not empty)
* */
if (twi->msg->flags & I2C_M_RD) {
if (twi->msg->len <= MAX_FIFO) /* cpu read */
return 0; /* current read opration not end, go on and get data by next RX_REQ_PD */
}
/* dma read end or write end */
twi->status = SUNXI_TWI_XFER_STATUS_IDLE; /* current write operation is end */
sunxi_twi_drv_disable_irq(twi->base_addr, TWI_DRV_INT_EN_MASK);
wake_up(&twi->wait);
return 0;
} else if (irq & RX_REQ_PD) { /* current read operation is end */
if (sunxi_twi_drv_recv_msg(twi, twi->msg))
twi->status = SUNXI_TWI_XFER_STATUS_ERROR;
else
twi->status = SUNXI_TWI_XFER_STATUS_IDLE;
sunxi_twi_drv_disable_irq(twi->base_addr, TWI_DRV_INT_EN_MASK);
wake_up(&twi->wait);
return 0;
} else if (irq & TRAN_ERR_PD) { /* current read/write packages trasnfer err */
sunxi_twi_drv_disable_irq(twi->base_addr, TWI_DRV_INT_EN_MASK);
/* The correct way to get the bus status in drv-mode is:
* err_sta = sunxi_twi_get_xfer_sta(twi)
* however, for the current TWI controller, when using drv-mode,
* the bus status is also obtained through the interrupt status register of engine-mode.
* therefore, we temporarily use the engine-mode interrupt status register
* to get the real bus status. this way is:
* err_sta = readl(twi->base_addr + TWI_STAT) & TWI_STAT_MASK
*/
err_sta = readl(twi->base_addr + TWI_STAT) & TWI_STAT_MASK;
switch (err_sta) {
case 0x00:
sunxi_err(twi->dev, "drv-mode: bus error\n");
break;
case 0x01:
sunxi_err(twi->dev, "drv-mode: Timeout when sending 9th SCL clk\n");
break;
case 0x20:
sunxi_err(twi->dev, "drv-mode: Address + Write bit transmitted,"
"ACK not received\n");
break;
case 0x30:
sunxi_err(twi->dev, "drv-mode: Data byte transmitted in master mode,"
"ACK not received\n");
break;
case 0x38:
sunxi_err(twi->dev, "drv-mode: Arbitration lost in address, or data byte\n");
break;
case 0x48:
sunxi_err(twi->dev, "drv-mode: Address + Read bit transmitted,"
"ACK not received\n");
break;
case 0x58:
sunxi_err(twi->dev, "drv-mode: Data byte received in master mode,"
"ACK not received\n");
break;
default:
sunxi_err(twi->dev, "drv-mode: unknown error\n");
break;
}
sunxi_err(twi->dev, "drv mode: TWI BUS error state is 0x%x\n", err_sta);
twi->status = SUNXI_TWI_XFER_STATUS_ERROR;
wake_up(&twi->wait);
return -err_sta;
} else {
sunxi_err(twi->dev, "Unknown irq 0x%x", irq);
twi->status = SUNXI_TWI_XFER_STATUS_ERROR;
sunxi_twi_drv_disable_irq(twi->base_addr, TWI_DRV_INT_EN_MASK);
wake_up(&twi->wait);
return -ENXIO;
}
}
static int sunxi_twi_engine_core_process(struct sunxi_twi *twi)
{
unsigned long flags;
unsigned char state;
unsigned char tmp;
void __iomem *base_addr = twi->base_addr;
sunxi_twi_engine_disable_irq(base_addr);
state = sunxi_twi_get_xfer_sta(twi);
TWI_DBG(twi, "engine-mode: [slave address:(0x%x),irq state:(0x%x)]\n",
twi->msg->addr, state);
spin_lock_irqsave(&twi->lock, flags);
switch (state) {
case 0xf8: /* On reset or stop the bus is idle, use only at poll method */
goto out_break;
case 0x08: /* A START condition has been transmitted */
case 0x10: /* A repeated start condition has been transmitted */
sunxi_twi_engine_addr_byte(twi);/* send slave address */
break; /* break and goto to normal function, */
case 0x18: /* slave_addr + write has been transmitted; ACK received */
/* send second part of 10 bits addr, the remaining 8 bits of address */
if (twi->msg[twi->msg_idx].flags & I2C_M_TEN) {
tmp = twi->msg[twi->msg_idx].addr & 0xff;
sunxi_twi_engine_put_byte(twi, &tmp);
break;
}
/* for 7 bit addr, then directly send data byte--case 0xd0: */
/* fall through */
case 0xd0: /* SLA+W has transmitted,ACK received! */
case 0x28: /* then continue send data or current xfer end */
/* send register address and the write data */
if (twi->buf_idx < twi->msg[twi->msg_idx].len) {
sunxi_twi_engine_put_byte(twi,
&(twi->msg[twi->msg_idx].buf[twi->buf_idx]));
twi->buf_idx++;
} else { /* the other msg */
twi->msg_idx++;
twi->buf_idx = 0;
/* all the write msgs xfer success, then wakeup */
if (twi->msg_idx == twi->msg_num) {
goto out_success;
} else if (twi->msg_idx < twi->msg_num) {
/* for restart pattern, read spec, two msgs */
sunxi_twi_engine_set_start(twi->base_addr);
TWI_DBG(twi, "Send restart finish\n");
} else {
goto out_failed;
}
}
break;
/* SLA+R has been transmitted; ACK has been received, is ready to receive
* with Restart, needn't to send second part of 10 bits addr
*/
case 0x40:
/* refer-"TWI-SPEC v2.1" */
/* enable A_ACK need it(receive data len) more than 1. */
if (twi->msg[twi->msg_idx].len > 1)
sunxi_twi_engine_enable_ack(base_addr);/* then jump to case 0x50 */
break;
case 0x50: /* Data bytes has been received; ACK has been transmitted */
/* receive first data byte */
if (twi->buf_idx < twi->msg[twi->msg_idx].len) {
/* get data then clear flag,then next data coming */
sunxi_twi_engine_get_byte(base_addr,
&twi->msg[twi->msg_idx].buf[twi->buf_idx]);
/* more than 2 bytes, the last byte need not to send ACK */
if ((twi->buf_idx + 2) == twi->msg[twi->msg_idx].len)
sunxi_twi_engine_disable_ack(base_addr);
twi->buf_idx++;
break;
} else { /* err process, the last byte should be @case 0x58 */
goto out_failed;
}
case 0x58: /* Data byte has been received; NOT ACK has been transmitted */
/* received the last byte */
if (twi->buf_idx == (twi->msg[twi->msg_idx].len - 1)) {
sunxi_twi_engine_get_byte(base_addr,
&twi->msg[twi->msg_idx].buf[twi->buf_idx]);
twi->msg_idx++;
twi->buf_idx = 0;
/* all the read mags xfer succeed,wakeup the thread */
if (twi->msg_idx == twi->msg_num) {
goto out_success;
} else if (twi->msg_idx < twi->msg_num) { /* repeat start */
sunxi_twi_engine_set_start(twi->base_addr);
TWI_DBG(twi, "Send restart finish\n");
break;
} else {
goto out_failed;
}
} else {
goto out_failed;
}
case 0xd8:
sunxi_err(twi->dev, "second addr has transmitted, ACK not received!");
goto out_failed;
case 0x20:
sunxi_err(twi->dev, "SLA+W has been transmitted; ACK not received\n");
goto out_failed;
case 0x30:
sunxi_err(twi->dev, "DATA byte transmitted, ACK not receive\n");
goto out_failed;
case 0x38:
sunxi_err(twi->dev, "Arbitration lost in SLA+W, SLA+R or data bytes\n");
goto out_failed;
case 0x48:
sunxi_err(twi->dev, "Address + Read bit transmitted, ACK not received\n");
goto out_failed;
case 0x00:
sunxi_err(twi->dev, "Bus error\n");
goto out_failed;
default:
goto out_failed;
}
/* just for debug */
twi->debug_state = state;
/* this time xfer is't completed,return and enable irq
* then wait new interrupt coming to continue xfer
*/
out_break:
sunxi_twi_engine_clear_irq(base_addr);
sunxi_twi_engine_enable_irq(base_addr);
spin_unlock_irqrestore(&twi->lock, flags);
return 0;
/* xfer failed, then send stop and wakeup */
out_failed:
sunxi_err(twi->dev, "engine mode: TWI BUS error state is 0x%x\n", state);
if (sunxi_twi_engine_stop(twi))
sunxi_err(twi->dev, "STOP failed!\n");
twi->msg_idx = -state;
twi->status = SUNXI_TWI_XFER_STATUS_ERROR;
twi->debug_state = state;
spin_unlock_irqrestore(&twi->lock, flags);
wake_up(&twi->wait);
return -state;
/* xfer success, then send wtop and wakeup */
out_success:
if (sunxi_twi_engine_stop(twi))
sunxi_err(twi->dev, "STOP failed!\n");
twi->status = SUNXI_TWI_XFER_STATUS_IDLE;
twi->debug_state = state;
spin_unlock_irqrestore(&twi->lock, flags);
wake_up(&twi->wait);
return 0;
}
#if IS_ENABLED(CONFIG_I2C_SLAVE)
static int sunxi_twi_slave_core_process(struct sunxi_twi *twi)
{
unsigned long flags;
unsigned char state;
void __iomem *base_addr = twi->base_addr;
unsigned char value;
u32 reg_val;
int timeout = 0x7ffff;
state = sunxi_twi_get_xfer_sta(twi);
TWI_DBG(twi, "slave-mode: addr(0x%x) irq(0x%x)\n", twi->slave->addr, state);
spin_lock_irqsave(&twi->lock, flags);
if (!twi->data->slave_func_fixed) {
if (state == TWI_STAT_RXRS_ACK || state == TWI_STAT_SLV_TXD_ACK) {
/* wait scl into second half cycle otherwise the 9th ack clk may shorter than normal */
while (sunxi_twi_get_scl(&twi->adap) && --timeout)
;
if (timeout <= 0)
sunxi_err(twi->dev, "slave-mode: wait scl low timeout!\n");
reg_val = readl(twi->base_addr + TWI_LCR);
reg_val &= ~SCL_CTL;
writel(reg_val, twi->base_addr + TWI_LCR);
sunxi_twi_scl_control_enable(twi);
}
}
switch (state) {
case 0x60: /* Slave address + Write bit received, ACK transmitted */
i2c_slave_event(twi->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
twi->status = SUNXI_TWI_XFER_STATUS_SLAVE_SADDR;
break;
case 0x80: /* Data byte received after slave address received, ACK transmitted */
if (twi->status == SUNXI_TWI_XFER_STATUS_SLAVE_SADDR) {
sunxi_twi_engine_get_byte(base_addr, &value);
i2c_slave_event(twi->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
twi->status = SUNXI_TWI_XFER_STATUS_SLAVE_WDATA;
} else if (twi->status == SUNXI_TWI_XFER_STATUS_SLAVE_WDATA) {
sunxi_twi_engine_get_byte(base_addr, &value);
i2c_slave_event(twi->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
} else {
twi->status = SUNXI_TWI_XFER_STATUS_SLAVE_ERROR;
}
break;
case 0xa0: /* STOP or repeated START condition received in slave mode */
i2c_slave_event(twi->slave, I2C_SLAVE_STOP, &value);
twi->status = SUNXI_TWI_XFER_STATUS_SLAVE_IDLE;
break;
case 0xa8: /* Slave address + Read bit received, ACK transmitted */
i2c_slave_event(twi->slave, I2C_SLAVE_READ_REQUESTED, &value);
sunxi_twi_engine_put_byte(twi, &value);
twi->status = SUNXI_TWI_XFER_STATUS_SLAVE_RDATA;
break;
case 0xb8: /* Data byte transmitted in slave mode, ACK received */
i2c_slave_event(twi->slave, I2C_SLAVE_READ_PROCESSED, &value);
sunxi_twi_engine_put_byte(twi, &value);
twi->status = SUNXI_TWI_XFER_STATUS_SLAVE_RDATA;
break;
case 0xc0: /* Data byte transmitted in slave mode, ACK not received */
i2c_slave_event(twi->slave, I2C_SLAVE_READ_REQUESTED, &value);
sunxi_twi_engine_put_byte(twi, &value);
i2c_slave_event(twi->slave, I2C_SLAVE_STOP, &value);
twi->status = SUNXI_TWI_XFER_STATUS_SLAVE_IDLE;
break;
default:
sunxi_err(twi->dev, "slave-mode: addr 0x%02x error irq(0x%x) status(0x%x), need reset\n",
twi->slave->addr, state, twi->status);
twi->status = SUNXI_TWI_XFER_STATUS_SLAVE_ERROR;
break;
}
/* just for debug */
twi->debug_state = state;
if (twi->status == SUNXI_TWI_XFER_STATUS_SLAVE_ERROR)
sunxi_twi_slave_reset(twi);
sunxi_twi_engine_clear_irq(base_addr);
if (!twi->data->slave_func_fixed) {
if (state == TWI_STAT_RXRS_ACK || state == TWI_STAT_SLV_TXD_ACK) {
/* delay 1us to fix the sda/scl reversal at same time issue */
udelay(1);
sunxi_twi_scl_control_disable(twi);
}
}
spin_unlock_irqrestore(&twi->lock, flags);
return 0;
}
#endif
static irqreturn_t sunxi_twi_handler(int this_irq, void *dev_id)
{
struct sunxi_twi *twi = (struct sunxi_twi *)dev_id;
u32 ret = sunxi_twi_check_irq(twi);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
if (twi->slave) {
if (ret == TWI_ENGINE_IRQ) {
sunxi_twi_slave_core_process(twi);
return IRQ_HANDLED;
} else {
sunxi_err(twi->dev, "slave-mode: wrong irq, check irq number!!\n");
return IRQ_NONE;
}
} else
#endif /* CONFIG_I2C_SLAVE */
{
if (ret == TWI_ENGINE_IRQ) {
sunxi_twi_engine_core_process(twi);
} else if (ret == TWI_DRV_IRQ) {
sunxi_twi_drv_core_process(twi);
} else {
sunxi_err(twi->dev, "master-mode: wrong irq, check irq number!!\n");
return IRQ_NONE;
}
}
return IRQ_HANDLED;
}
/* twi controller tx xfer function
* return the xfered msgs num on success,or the negative error num on failed
*/
static int sunxi_twi_drv_tx_one_msg(struct sunxi_twi *twi, struct i2c_msg *msg)
{
unsigned long flags;
int ret;
TWI_DBG(twi, "drv-mode: one-msg write slave_addr=0x%x, data_len=0x%x\n",
msg->addr, msg->len);
spin_lock_irqsave(&twi->lock, flags);
twi->msg = msg;
twi->buf_idx = 0;
spin_unlock_irqrestore(&twi->lock, flags);
sunxi_twi_drv_disable_read_mode(twi->base_addr);
sunxi_twi_drv_set_slave_addr(twi, msg);
if (msg->len == 1) {
sunxi_twi_drv_set_addr_byte(twi->base_addr, 0);
sunxi_twi_drv_set_data_byte(twi->base_addr, msg->len);
} else {
sunxi_twi_drv_set_addr_byte(twi->base_addr, 1);
sunxi_twi_drv_set_data_byte(twi->base_addr, msg->len - 1);
}
sunxi_twi_drv_set_packet_cnt(twi->base_addr, 1);
if (msg->len > MAX_FIFO) {
TWI_DBG(twi, "drv-mode: dma write\n");
ret = sunxi_twi_dma_tx_config(twi);
if (ret) {
sunxi_err(twi->dev, "dma_tx config failed\n");
goto err_dma;
}
} else {
TWI_DBG(twi, "drv-mode: cpu write\n");
sunxi_twi_drv_set_tx_trig(twi->base_addr, msg->len);
/*
* if now fifo can't store all the msg data buf
* enable the TX_REQ_INT_EN, and wait TX_REQ_INT,
* then continue send the remaining data to fifo
*/
if (sunxi_twi_drv_send_msg(twi, twi->msg))
sunxi_twi_drv_enable_irq(twi->base_addr, TX_REQ_INT_EN);
sunxi_twi_drv_start_xfer(twi);
}
sunxi_twi_drv_enable_irq(twi->base_addr, TRAN_ERR_INT_EN | TRAN_COM_INT_EN);
ret = sunxi_twi_drv_wait_complete(twi);
if (twi->dma_tx && (msg->len > MAX_FIFO)) {
/* check ret value only when use dma xfer */
if (ret < 0)
dmaengine_terminate_sync(twi->dma_tx->chan);
sunxi_twi_drv_dma_deinit(twi, &twi->dma_tx);
sunxi_twi_drv_disable_dma_irq(twi->base_addr, DMA_TX_EN);
}
sunxi_twi_drv_disable_irq(twi->base_addr, TWI_DRV_INT_EN_MASK);
return ret;
err_dma:
sunxi_twi_drv_disable_dma_irq(twi->base_addr, DMA_TX_EN);
sunxi_twi_drv_disable_irq(twi->base_addr, TWI_DRV_INT_EN_MASK);
return ret;
}
/* read xfer msg function
* msg : the msg queue
* num : the number of msg, usually is one or two:
*
* num = 1 : some twi slave device support one msg to read, need't reg_addr
*
* num = 2 : normally twi reead need two msg, msg example is as following :
* msg[0]->flag express write, msg[0]->buf store the reg data,
* msg[1]->flag express read, msg[1]-<buf store the data recived
* return 0 on succes.
*/
static int
sunxi_twi_drv_rx_msgs(struct sunxi_twi *twi, struct i2c_msg *msgs, int num)
{
struct i2c_msg *wmsg, *rmsg;
int ret;
if (num == 1) {
wmsg = NULL;
rmsg = msgs;
TWI_DBG(twi, "drv-mode: one-msg read slave_addr=0x%x, data_len=0x%x\n",
rmsg->addr, rmsg->len);
sunxi_twi_drv_enable_read_mode(twi->base_addr);
sunxi_twi_drv_set_addr_byte(twi->base_addr, 0);
} else if (num == 2) {
wmsg = msgs;
rmsg = msgs + 1;
TWI_DBG(twi, "drv-mode: two-msg read slave_addr=0x%x, data_len=0x%x\n",
rmsg->addr, rmsg->len);
if (wmsg->addr != rmsg->addr) {
sunxi_err(twi->dev, "drv-mode: two msg's addr must be the same\n");
return -EINVAL;
}
sunxi_twi_drv_disable_read_mode(twi->base_addr);
sunxi_twi_drv_set_addr_byte(twi->base_addr, wmsg->len);
} else {
sunxi_err(twi->dev, "twi read xfer can not transfer %d msgs once", num);
return -EINVAL;
}
twi->msg = rmsg;
sunxi_twi_drv_set_slave_addr(twi, rmsg);
sunxi_twi_drv_set_packet_cnt(twi->base_addr, 1);
sunxi_twi_drv_set_data_byte(twi->base_addr, rmsg->len);
if (num == 2) /* if (wmsg) */
sunxi_twi_drv_send_msg(twi, wmsg);
if (rmsg->len > MAX_FIFO) {
TWI_DBG(twi, "drv-mode: rx msgs by dma\n");
ret = sunxi_twi_dma_rx_config(twi);
if (ret) {
sunxi_err(twi->dev, "dma_rx config failed\n");
goto err_dma;
}
} else {
TWI_DBG(twi, "drv-mode: rx msgs by cpu\n");
/* set the rx_trigger_level max to avoid the RX_REQ com before COM_REQ */
sunxi_twi_drv_set_rx_trig(twi->base_addr, MAX_FIFO);
sunxi_twi_drv_start_xfer(twi);
}
if (rmsg->len > MAX_FIFO)
sunxi_twi_drv_enable_irq(twi->base_addr, TRAN_ERR_INT_EN | TRAN_COM_INT_EN);
else
sunxi_twi_drv_enable_irq(twi->base_addr, TRAN_ERR_INT_EN | TRAN_COM_INT_EN | RX_REQ_INT_EN);
ret = sunxi_twi_drv_wait_complete(twi);
if (twi->dma_rx && (rmsg->len > MAX_FIFO)) {
/* check ret value only when use dma xfer */
if (ret < 0)
dmaengine_terminate_sync(twi->dma_rx->chan);
sunxi_twi_drv_dma_deinit(twi, &twi->dma_rx);
sunxi_twi_drv_disable_dma_irq(twi->base_addr, DMA_RX_EN);
}
sunxi_twi_drv_disable_irq(twi->base_addr, TWI_DRV_INT_EN_MASK);
return ret;
err_dma:
sunxi_twi_drv_disable_dma_irq(twi->base_addr, DMA_RX_EN);
sunxi_twi_drv_disable_irq(twi->base_addr, TWI_DRV_INT_EN_MASK);
return ret;
}
/**
* @twi: struct of sunxi_twi
* @msgs: One or more messages to execute before STOP is issued to terminate
* the operation,and each message begins with a START.
* @num: the Number of messages to be executed.
*
* return the number of xfered or the negative error num
*/
static int
sunxi_twi_drv_xfer(struct sunxi_twi *twi, struct i2c_msg *msgs, int num)
{
int i = 0;
unsigned long flags;
spin_lock_irqsave(&twi->lock, flags);
twi->status = SUNXI_TWI_XFER_STATUS_RUNNING; /* xfer means that the twi is in the running state */
spin_unlock_irqrestore(&twi->lock, flags);
sunxi_twi_drv_clear_irq(twi->base_addr, TWI_DRV_INT_STA_MASK);
sunxi_twi_drv_disable_irq(twi->base_addr, TWI_DRV_INT_STA_MASK);
sunxi_twi_drv_disable_dma_irq(twi->base_addr, DMA_TX_EN | DMA_RX_EN);
sunxi_twi_drv_clear_txfifo(twi->base_addr);
sunxi_twi_drv_clear_rxfifo(twi->base_addr);
while (i < num) {
TWI_DBG(twi, "drv-mode: addr: 0x%x, flag:%x, len:%d\n",
msgs[i].addr, msgs[i].flags, msgs[i].len);
if (msgs[i].flags & I2C_M_RD) {
/* one msg read */
if (sunxi_twi_drv_rx_msgs(twi, &msgs[i], 1))
return -EINVAL;
i++;
} else if (i + 1 < num && msgs[i + 1].flags & I2C_M_RD) {
/* two msgs read */
if (sunxi_twi_drv_rx_msgs(twi, &msgs[i], 2))
return -EINVAL;
i += 2;
} else {
/* one msg write */
if (sunxi_twi_drv_tx_one_msg(twi, &msgs[i]))
return -EINVAL;
i++;
}
}
return i;
}
static int
sunxi_twi_engine_xfer(struct sunxi_twi *twi, struct i2c_msg *msgs, int num)
{
unsigned long flags;
void __iomem *base_addr = twi->base_addr;
sunxi_twi_engine_disable_ack(base_addr);
sunxi_twi_engine_set_efr(base_addr, NO_DATA_WROTE);
sunxi_twi_engine_clear_irq(base_addr);
/* may conflict with xfer_complete */
spin_lock_irqsave(&twi->lock, flags);
twi->msg = msgs;
twi->msg_num = num;
twi->msg_idx = 0;
twi->status = SUNXI_TWI_XFER_STATUS_RUNNING;
spin_unlock_irqrestore(&twi->lock, flags);
sunxi_twi_engine_enable_irq(base_addr);
/* then send START signal, and needn't clear int flag */
/* After setting the M_STA position to 1 in TWI_CNTR, the hardware
* will automatically send a start signal and clear the M_STA bit.
* The clearing action may occur before reading the register again.
* Therefore, no register read-back check is performed when sending
* the start signal.
*/
sunxi_twi_engine_set_start(twi->base_addr);
TWI_DBG(twi, "Send start finish\n");
spin_lock_irqsave(&twi->lock, flags);
spin_unlock_irqrestore(&twi->lock, flags);
/*
* sleep and wait, timeput = 5*HZ
* then do the transfer at function : sunxi_twi_engine_core_process
*/
return sunxi_twi_engine_wait_complete(twi);
}
static int
sunxi_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct sunxi_twi *twi = (struct sunxi_twi *)adap->algo_data;
int ret;
if (twi->status == SUNXI_TWI_XFER_STATUS_SHUTDOWN) {
sunxi_err(twi->dev, "twi bus is shutdown and shouldn't be working anymore\n");
return -EBUSY;
}
#if defined(CONFIG_AW_TWI_DELAYINIT) || defined(CONFIG_SUNXI_I2C_DELAYINIT)
if (!twi->delay_init_done) {
sunxi_err(twi->dev, "twi wait rpmsg and shouldn't be working anymore\n");
return -EBUSY;
}
#endif
#if IS_ENABLED(CONFIG_I2C_SLAVE)
if (twi->slave) {
sunxi_err(twi->dev, "twi bus is in slave mode and shouldn't be using in master anymore\n");
return -EBUSY;
}
#endif
if (IS_ERR_OR_NULL(msgs) || (num <= 0)) {
sunxi_err(twi->dev, "invalid argument\n");
return -EINVAL;
}
twi->msg = NULL;
/* then the sunxi_twi_runtime_reseme() call back */
ret = pm_runtime_get_sync(twi->dev);
if (ret < 0)
goto out;
sunxi_twi_soft_reset(twi);
ret = sunxi_twi_bus_barrier(&twi->adap);
if (ret) {
sunxi_err(twi->dev, "twi bus barrier failed, sda is still low!\n");
goto out;
}
if (twi->twi_drv_used)
ret = sunxi_twi_drv_xfer(twi, msgs, num);
else
ret = sunxi_twi_engine_xfer(twi, msgs, num);
out:
pm_runtime_mark_last_busy(twi->dev);
/* asnyc release to ensure other module all suspend */
pm_runtime_put_autosuspend(twi->dev);
twi->msg = NULL;
return ret;
}
static unsigned int sunxi_twi_functionality(struct i2c_adapter *adap)
{
unsigned int flag = I2C_FUNC_I2C|I2C_FUNC_10BIT_ADDR|I2C_FUNC_SMBUS_EMUL;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
flag |= I2C_FUNC_SLAVE;
#endif
return flag;
}
#if IS_ENABLED(CONFIG_I2C_SLAVE)
static int sunxi_twi_slave_init(struct sunxi_twi *twi)
{
struct i2c_client *slave = twi->slave;
unsigned char addr;
if (slave->flags & I2C_M_TEN) {
addr = TWI_ADDR_10_BIT_MASK |
(((slave->addr) >> TWI_ADDR_10_BIT_SHIFT) & TWI_ADDR_10_BIT_WIDTH);
writel(addr << TWI_ADDR_SHIFT, twi->base_addr + TWI_ADDR);
addr = (slave->addr) & TWI_SLAVE_10_BIT_MASK;
writel(addr, twi->base_addr + TWI_XADDR);
} else {
addr = (slave->addr) & TWI_SLAVE_7_BIT_MASK;
writel(addr << TWI_ADDR_SHIFT, twi->base_addr + TWI_ADDR);
}
sunxi_twi_engine_enable_ack(twi->base_addr);
sunxi_twi_engine_enable_irq(twi->base_addr);
twi->status = SUNXI_TWI_XFER_STATUS_SLAVE_IDLE;
return 0;
}
static void sunxi_twi_slave_exit(struct sunxi_twi *twi)
{
sunxi_twi_engine_disable_irq(twi->base_addr);
sunxi_twi_engine_disable_ack(twi->base_addr);
writel(0, twi->base_addr + TWI_ADDR);
writel(0, twi->base_addr + TWI_XADDR);
}
static int sunxi_twi_reg_slave(struct i2c_client *slave)
{
struct sunxi_twi *twi;
int err;
twi = i2c_get_adapdata(slave->adapter);
if (twi->twi_drv_used)
return -EINVAL;
if (twi->slave)
return -EBUSY;
/* Keep device active for slave address detection logic */
err = pm_runtime_get_sync(twi->dev);
if (err < 0)
return err;
twi->slave = slave;
sunxi_twi_soft_reset(twi);
err = sunxi_twi_slave_init(twi);
if (err)
goto err0;
sunxi_info(twi->dev, "slave mode: enter addr(0x%x)\n", slave->addr);
return 0;
err0:
twi->slave = NULL;
pm_runtime_mark_last_busy(twi->dev);
pm_runtime_put_autosuspend(twi->dev);
return err;
}
static int sunxi_twi_unreg_slave(struct i2c_client *slave)
{
struct sunxi_twi *twi = i2c_get_adapdata(slave->adapter);
WARN_ON(!twi->slave);
sunxi_info(twi->dev, "slave mode: exit addr(0x%x)\n", slave->addr);
sunxi_twi_slave_exit(twi);
twi->slave = NULL;
sunxi_twi_soft_reset(twi);
pm_runtime_mark_last_busy(twi->dev);
pm_runtime_put_autosuspend(twi->dev);
return 0;
}
#endif
static const struct i2c_algorithm sunxi_twi_algorithm = {
.master_xfer = sunxi_twi_xfer,
.functionality = sunxi_twi_functionality,
#if IS_ENABLED(CONFIG_I2C_SLAVE)
.reg_slave = sunxi_twi_reg_slave,
.unreg_slave = sunxi_twi_unreg_slave,
#endif
};
static struct i2c_bus_recovery_info sunxi_twi_bus_recovery = {
.get_scl = sunxi_twi_get_scl,
.set_scl = sunxi_twi_set_scl,
.get_sda = sunxi_twi_get_sda,
.recover_bus = i2c_generic_scl_recovery,
};
static void sunxi_twi_adap_lock_bus(struct i2c_adapter *adapter, unsigned int flags)
{
struct sunxi_twi *twi = (struct sunxi_twi *)adapter->algo_data;
mutex_lock(&twi->bus_lock);
}
static int sunxi_twi_adap_trylock_bus(struct i2c_adapter *adapter, unsigned int flags)
{
struct sunxi_twi *twi = (struct sunxi_twi *)adapter->algo_data;
int ret;
ret = mutex_trylock(&twi->bus_lock);
if (ret)
sunxi_err(twi->dev, "trylock_bus failed!\n");
return ret;
}
static void sunxi_twi_adap_unlock_bus(struct i2c_adapter *adapter, unsigned int flags)
{
struct sunxi_twi *twi = (struct sunxi_twi *)adapter->algo_data;
mutex_unlock(&twi->bus_lock);
}
static const struct i2c_lock_operations sunxi_twi_adap_lock_ops = {
.lock_bus = sunxi_twi_adap_lock_bus,
.trylock_bus = sunxi_twi_adap_trylock_bus,
.unlock_bus = sunxi_twi_adap_unlock_bus,
};
static int sunxi_twi_regulator_request(struct sunxi_twi *twi)
{
#ifdef CONFIG_SUNXI_REGULATOR_DT
if (twi->regulator)
return 0;
twi->regulator = regulator_get_optional(twi->dev, "twi");
if (IS_ERR(twi->regulator)) {
sunxi_err(twi->dev, "regulator not found(isn't configured in dts)!\n");
return -EPROBE_DEFER;
}
#else
/* Consider "n*" as nocare. Support "none", "nocare", "null", "" etc. */
if ((twi->regulator_id[0] == 'n') || (twi->regulator_id[0] == 0))
return 0;
twi->regulator = regulator_get(NULL, twi->regulator_id);
if (IS_ERR(twi->regulator)) {
sunxi_err(twi->dev, "get regulator %s failed!\n", twi->regulator_id);
return -1;
}
#endif
return 0;
}
static int sunxi_twi_regulator_release(struct sunxi_twi *twi)
{
if (!twi->regulator)
return 0;
regulator_put(twi->regulator);
twi->regulator = NULL;
return 0;
}
static int sunxi_twi_regulator_enable(struct sunxi_twi *twi)
{
if (!twi->regulator || IS_ERR(twi->regulator))
return 0;
/* set output voltage to the dts config */
if (twi->vol)
regulator_set_voltage(twi->regulator, twi->vol, twi->vol);
if (regulator_enable(twi->regulator)) {
sunxi_err(twi->dev, "enable regulator failed!\n");
return -EINVAL;
}
return 0;
}
static int sunxi_twi_regulator_disable(struct sunxi_twi *twi)
{
if (!twi->regulator)
return 0;
if (regulator_is_enabled(twi->regulator))
regulator_disable(twi->regulator);
return 0;
}
static int sunxi_twi_clk_request(struct sunxi_twi *twi)
{
twi->bus_clk = of_clk_get(twi->dev->of_node, 0);
if (IS_ERR(twi->bus_clk)) {
sunxi_err(twi->dev, "request clock failed\n");
return -EINVAL;
}
return 0;
}
static int sunxi_twi_resource_get(struct device_node *np, struct sunxi_twi *twi)
{
int err;
#ifndef CONFIG_SUNXI_REGULATOR_DT
const char *str_vcc_twi;
#endif
twi->bus_num = of_alias_get_id(np, "twi");
if (twi->bus_num < 0) {
sunxi_err(twi->dev, "TWI failed to get alias id\n");
return -EINVAL;
}
twi->pdev->id = twi->bus_num;
twi->res = platform_get_resource(twi->pdev, IORESOURCE_MEM, 0);
if (!twi->res) {
sunxi_err(twi->dev, "failed to get MEM res\n");
return -ENXIO;
}
twi->base_addr = devm_ioremap_resource(twi->dev, twi->res);
if (IS_ERR(twi->base_addr)) {
sunxi_err(twi->dev, "unable to ioremap\n");
return PTR_ERR(twi->base_addr);
}
err = sunxi_twi_clk_request(twi);
if (err) {
sunxi_err(twi->dev, "request twi clk failed!\n");
return err;
}
twi->pctrl = devm_pinctrl_get(twi->dev);
if (IS_ERR(twi->pctrl)) {
sunxi_err(twi->dev, "pinctrl_get failed\n");
return PTR_ERR(twi->pctrl);
}
twi->no_suspend = 0;
of_property_read_u32(np, "no_suspend", &twi->no_suspend);
twi->irq_flag = (twi->no_suspend) ? IRQF_NO_SUSPEND : 0;
twi->irq = platform_get_irq(twi->pdev, 0);
if (twi->irq < 0)
return twi->irq;
twi->vol = 0;
of_property_read_u32(np, "twi_vol", &twi->vol);
#ifndef CONFIG_SUNXI_REGULATOR_DT
err = of_property_read_string(np, "twi_regulator", &str_vcc_twi);
if (err)
sunxi_err(twi->dev, "warning: failed to get regulator id\n");
else if (strlen(str_vcc_twi) >= sizeof(twi->regulator_id))
sunxi_err(twi->dev, "illegal regulator id\n");
else {
strcpy(twi->regulator_id, str_vcc_twi);
sunxi_info(twi->dev, "twi_regulator: %s\n", twi->regulator_id);
}
#endif
err = sunxi_twi_regulator_request(twi);
if (err) {
sunxi_err(twi->dev, "request regulator failed!\n");
}
err = of_property_read_u32(np, "clock-frequency", &twi->bus_freq);
if (err) {
sunxi_err(twi->dev, "failed to get clock frequency\n");
goto err0;
}
twi->pkt_interval = 0;
of_property_read_u32(np, "twi_pkt_interval", &twi->pkt_interval);
twi->twi_drv_used = 0;
of_property_read_u32(np, "twi_drv_used", &twi->twi_drv_used);
if (twi->twi_drv_used) {
err = sunxi_twi_dma_request(twi, (dma_addr_t)twi->res->start);
if (err)
sunxi_info(twi->dev, "dma channel request failed, "
"or no dma comfiguration information in dts\n");
}
return 0;
err0:
sunxi_twi_regulator_release(twi);
return err;
}
static void sunxi_twi_resource_put(struct sunxi_twi *twi)
{
sunxi_twi_regulator_release(twi);
}
static int sunxi_twi_select_pin_state(struct sunxi_twi *twi, char *name)
{
int ret;
struct pinctrl_state *pctrl_state;
pctrl_state = pinctrl_lookup_state(twi->pctrl, name);
if (IS_ERR(pctrl_state)) {
sunxi_err(twi->dev, "pinctrl_lookup_state(%s) failed! return %p\n",
name, pctrl_state);
return -EINVAL;
}
ret = pinctrl_select_state(twi->pctrl, pctrl_state);
if (ret < 0)
sunxi_err(twi->dev, "pinctrl select state(%s) failed! return %d\n",
name, ret);
return ret;
}
static int sunxi_twi_clk_init(struct sunxi_twi *twi)
{
int err;
/*
* twi will be used in the uboot stage, so it must be reset before
* configuring the module registers to clean up the configuration
* information of the uboot stage
*/
sunxi_periph_reset_assert(twi->bus_clk);
sunxi_periph_reset_deassert(twi->bus_clk);
if (clk_prepare_enable(twi->bus_clk)) {
sunxi_err(twi->dev, "enable apb_twi clock failed!\n");
return -EINVAL;
}
/* twi ctroller module clock is controllerd by self */
err = sunxi_twi_set_frequency(twi);
if (err) {
sunxi_err(twi->dev, "set frequency failed!\n");
goto err;
}
/* set 1 to support twi clock stretching feature */
sunxi_twi_set_clk_count_mode(twi, 1);
return 0;
err:
clk_disable_unprepare(twi->bus_clk);
return err;
}
static void sunxi_twi_clk_exit(struct sunxi_twi *twi)
{
/* disable clk */
if (!IS_ERR_OR_NULL(twi->bus_clk) && __clk_is_enabled(twi->bus_clk)) {
clk_disable_unprepare(twi->bus_clk);
}
sunxi_periph_reset_assert(twi->bus_clk);
}
static int sunxi_twi_hw_init(struct sunxi_twi *twi)
{
int err;
err = sunxi_twi_regulator_enable(twi);
if (err) {
sunxi_err(twi->dev, "enable regulator failed!\n");
goto err0;
}
err = sunxi_twi_select_pin_state(twi, PINCTRL_STATE_DEFAULT);
if (err) {
sunxi_err(twi->dev, "request twi gpio failed!\n");
goto err1;
}
err = sunxi_twi_clk_init(twi);
if (err) {
sunxi_err(twi->dev, "init twi clock failed!\n");
goto err2;
}
sunxi_twi_soft_reset(twi);
sunxi_twi_bus_enable(twi);
return 0;
err2:
sunxi_twi_select_pin_state(twi, PINCTRL_STATE_SLEEP);
err1:
sunxi_twi_regulator_disable(twi);
err0:
return err;
}
static void sunxi_twi_hw_exit(struct sunxi_twi *twi)
{
sunxi_twi_bus_disable(twi);
sunxi_twi_clk_exit(twi);
sunxi_twi_select_pin_state(twi, PINCTRL_STATE_SLEEP);
sunxi_twi_regulator_disable(twi);
}
#if defined(CONFIG_AW_TWI_DELAYINIT) || defined(CONFIG_SUNXI_I2C_DELAYINIT)
static int sunxi_twi_rpmsg_callback(void *dev, void *data, int len)
{
struct sunxi_twi *twi = dev;
int err = 0;
if (!data || !strncmp(data, "return", len)) {
err = devm_request_irq(twi->dev, twi->irq, sunxi_twi_handler,
twi->irq_flag, dev_name(twi->dev), twi);
if (err) {
sunxi_err(twi->dev, "request irq failed!\n");
return err;
}
/*
* After the RTOS uses TWI, the state of the pin is still DEFAULT on the RTOS side.
* When the pin state is set to DEFAULT on the linux side, it will exit directly.
* Therefore, it is necessary to first set the state of the pin to SLEEP.
*/
sunxi_twi_select_pin_state(twi, PINCTRL_STATE_SLEEP);
sunxi_twi_hw_init(twi);
twi->delay_init_done = true;
/*
* when rv return the control, reopen the autosuspend funtion.
*/
pm_runtime_use_autosuspend(twi->dev);
} else if (!strncmp(data, "get", len)) {
/*
* when rv get the control, disable the autosuspend funtion,
* avoid the pm runtime state error.
*/
pm_runtime_dont_use_autosuspend(twi->dev);
if (twi->irq)
devm_free_irq(twi->dev, twi->irq, twi);
sunxi_twi_hw_exit(twi);
twi->delay_init_done = false;
}
return err;
}
#endif /* CONFIG_AW_TWI_DELAYINIT */
static ssize_t sunxi_twi_info_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sunxi_twi *twi = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE,
"twi->bus_num = %d\n"
"twi->name = %s\n"
"twi->irq = %d\n"
"twi->freqency = %u\n",
twi->bus_num,
dev_name(twi->dev),
twi->irq,
twi->bus_freq);
}
static ssize_t sunxi_twi_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sunxi_twi *twi = dev_get_drvdata(dev);
if (twi == NULL)
return scnprintf(buf, PAGE_SIZE, "%s\n", "sunxi_twi is NULL!");
return scnprintf(buf, PAGE_SIZE,
"twi->bus_num = %d\n"
"twi->status = [%d]\n"
"twi->msg_num = %u, ->msg_idx = %u, ->buf_idx = %u\n"
"twi->bus_freq = %u\n"
"twi->irq = %d\n"
"twi->debug_state = %u\n"
"twi->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",
twi->bus_num, twi->status,
twi->msg_num, twi->msg_idx, twi->buf_idx,
twi->bus_freq, twi->irq, twi->debug_state,
twi->base_addr,
TWI_ADDR, readl(twi->base_addr + TWI_ADDR),
TWI_XADDR, readl(twi->base_addr + TWI_XADDR),
TWI_DATA, readl(twi->base_addr + TWI_DATA),
TWI_CNTR, readl(twi->base_addr + TWI_CNTR),
TWI_STAT, readl(twi->base_addr + TWI_STAT),
TWI_CCR, readl(twi->base_addr + TWI_CCR),
TWI_SRST, readl(twi->base_addr + TWI_SRST),
TWI_EFR, readl(twi->base_addr + TWI_EFR),
TWI_LCR, readl(twi->base_addr + TWI_LCR));
}
static ssize_t sunxi_twi_freq_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct sunxi_twi *twi = dev_get_drvdata(dev);
unsigned int freq;
int err;
if (twi->status != SUNXI_TWI_XFER_STATUS_IDLE) {
sunxi_err(twi->dev, "Bus busy. Try it later.\n");
return -EBUSY;
}
err = kstrtouint(buf, 10, &freq);
if (err) {
sunxi_err(twi->dev, "String conversion failed!\n");
return -ERANGE;
}
if (freq == twi->bus_freq) {
sunxi_info(twi->dev, "Clock frequncy already is %u\n", freq);
return count;
}
switch (freq) {
case TWI_FREQ_100K:
case TWI_FREQ_200K:
case TWI_FREQ_400K:
sunxi_info(twi->dev, "Change clock frequncy from %u to %u.\n", twi->bus_freq, freq);
sunxi_twi_adap_lock_bus(&twi->adap, I2C_LOCK_SEGMENT);
twi->bus_freq = freq;
err = sunxi_twi_set_frequency(twi);
if (err) {
sunxi_err(twi->dev, "Set frequency failed!\n");
sunxi_twi_adap_unlock_bus(&twi->adap, I2C_LOCK_SEGMENT);
return err;
}
sunxi_twi_adap_unlock_bus(&twi->adap, I2C_LOCK_SEGMENT);
break;
default:
sunxi_err(twi->dev, "Invalid input %u. Should be 100000/200000/400000.\n", freq);
break;
}
return count;
}
static ssize_t sunxi_twi_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sunxi_twi *twi = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "Current mode is %s\n", twi->twi_drv_used ? "1: drv" : "0: engine");
}
static ssize_t sunxi_twi_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct sunxi_twi *twi = dev_get_drvdata(dev);
static char const *twi_mode[] = { "engine", "drv" };
unsigned int set_twi_drv_used;
int err;
if (twi->status != SUNXI_TWI_XFER_STATUS_IDLE) {
sunxi_err(twi->dev, "Bus busy. Try it later.\n");
return -EBUSY;
}
#if IS_ENABLED(CONFIG_I2C_SLAVE)
if (twi->slave) {
sunxi_err(twi->dev, "operation is not support in slave mode\n");
return count;
}
#endif
err = kstrtouint(buf, 10, &set_twi_drv_used);
if (err) {
sunxi_err(twi->dev, "String conversion failed!\n");
return -ERANGE;
}
if (set_twi_drv_used != 0 && set_twi_drv_used != 1) {
sunxi_err(twi->dev, "Invalid input %u. Should be 0 or 1.\n", set_twi_drv_used);
return -EINVAL;
}
if (set_twi_drv_used == twi->twi_drv_used) {
sunxi_info(twi->dev, "Twi already in %s mode\n", twi_mode[set_twi_drv_used]);
} else {
sunxi_info(twi->dev, "Change twi mode from %s to %s\n", twi_mode[twi->twi_drv_used], twi_mode[set_twi_drv_used]);
sunxi_twi_adap_lock_bus(&twi->adap, I2C_LOCK_SEGMENT);
sunxi_twi_hw_exit(twi);
twi->twi_drv_used = set_twi_drv_used;
sunxi_twi_hw_init(twi);
sunxi_twi_adap_unlock_bus(&twi->adap, I2C_LOCK_SEGMENT);
}
return count;
}
#if defined(TWI_DEBUG)
/*
* If you want a twi to allow dynamic printing, just set the bit corresponding to debug_chan_flags to 1;
*
* | twi | ... | twi7 | twi6 | twi5 | twi4 | twi3 | twi2 | twi1 | twi0 |
* | bits | ... | bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
*
* If debug_chan_flags = 0x30( bit4 = 1 and bit5 = 1), indicates that twi4 and twi5 are allowed to print
* dynamically; When you want add twi6 dynamic printing, need to set bit6 to 1, so you should echo 0x70
* to debug_chan sysfs node!
*/
static ssize_t sunxi_debug_chan_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "Current debug_chan_flags = 0x%x\n", debug_chan_flags);
}
static ssize_t sunxi_debug_chan_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct sunxi_twi *twi = dev_get_drvdata(dev);
unsigned int set_debug_chan_flags;
int err;
err = kstrtouint(buf, 16, &set_debug_chan_flags);
if (err) {
sunxi_err(twi->dev, "String conversion failed!\n");
return -ERANGE;
}
debug_chan_flags = set_debug_chan_flags;
return count;
}
#endif
#if IS_ENABLED(CONFIG_I2C_SLAVE)
static ssize_t sunxi_twi_slave_reset_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct sunxi_twi *twi = dev_get_drvdata(dev);
sunxi_info(twi->dev, "twi controller soft reset\n");
sunxi_twi_engine_disable_irq(twi->base_addr);
disable_irq(twi->irq);
sunxi_twi_slave_reset(twi);
enable_irq(twi->irq);
sunxi_twi_engine_enable_irq(twi->base_addr);
return count;
}
#endif
static struct device_attribute sunxi_twi_debug_attr[] = {
__ATTR(info, S_IRUGO, sunxi_twi_info_show, NULL),
__ATTR(status, S_IRUGO, sunxi_twi_status_show, NULL),
__ATTR(freq, S_IWUSR, NULL, sunxi_twi_freq_store),
__ATTR(twi_mode, S_IRUGO|S_IWUSR, sunxi_twi_mode_show, sunxi_twi_mode_store),
#if defined(TWI_DEBUG)
__ATTR(debug_chan, S_IRUGO|S_IWUSR, sunxi_debug_chan_show, sunxi_debug_chan_store),
#endif
#if IS_ENABLED(CONFIG_I2C_SLAVE)
__ATTR(slave_reset, S_IWUSR, NULL, sunxi_twi_slave_reset_store),
#endif
};
static void sunxi_twi_create_sysfs(struct platform_device *_pdev)
{
int i;
for (i = 0; i < ARRAY_SIZE(sunxi_twi_debug_attr); i++)
device_create_file(&_pdev->dev, &sunxi_twi_debug_attr[i]);
}
static void sunxi_twi_remove_sysfs(struct platform_device *_pdev)
{
int i;
for (i = 0; i < ARRAY_SIZE(sunxi_twi_debug_attr); i++)
device_remove_file(&_pdev->dev, &sunxi_twi_debug_attr[i]);
}
static int sunxi_twi_probe(struct platform_device *pdev)
{
struct sunxi_twi *twi;
int err;
twi = devm_kzalloc(&pdev->dev, sizeof(*twi), GFP_KERNEL);
if (IS_ERR_OR_NULL(twi))
return -ENOMEM; /* Do not print prompts after kzalloc errors */
twi->pdev = pdev;
twi->dev = &pdev->dev;
twi->data = of_device_get_match_data(&pdev->dev);
if (!twi->data) {
sunxi_err(twi->dev, "TWI failed to get device data\n");
return -ENODEV;
}
/* get dts resource */
err = sunxi_twi_resource_get(pdev->dev.of_node, twi);
if (err) {
sunxi_err(twi->dev, "TWI failed to get resource\n");
goto err0;
}
/* twi controller hardware init */
#if defined(CONFIG_AW_TWI_DELAYINIT) || defined(CONFIG_SUNXI_I2C_DELAYINIT)
twi->delay_init_done = true;
err = of_property_read_string(pdev->dev.of_node, "rproc-name", &twi->rproc_ser_name);
if (!err) {
/*
* After the RTOS part uses TWI, it sends a signal to Linux, and then Linux calls the
* sunxi_twi_rpmsg_callback() function to initialize TWI resources normally, and all
* subsequent operations on TWI are completed on the Linux side.
*/
twi->delay_init_done = false;
sprintf(twi->rproc_device_name, "twi%d", twi->bus_num);
rpmsg_notify_add(twi->rproc_ser_name, twi->rproc_device_name, sunxi_twi_rpmsg_callback, twi);
} else
#endif /* CONFIG_AW_TWI_DELAYINIT */
{
err = devm_request_irq(twi->dev, twi->irq, sunxi_twi_handler,
twi->irq_flag, dev_name(twi->dev), twi);
if (err) {
sunxi_err(twi->dev, "request irq failed!\n");
return err;
}
err = sunxi_twi_hw_init(twi);
if (err) {
sunxi_err(twi->dev, "hw init failed! try again!!\n");
goto err1;
}
}
spin_lock_init(&twi->lock);
init_waitqueue_head(&twi->wait);
sunxi_twi_create_sysfs(pdev);
pm_runtime_set_active(twi->dev);
if (twi->no_suspend)
pm_runtime_get_noresume(twi->dev);
pm_runtime_set_autosuspend_delay(twi->dev, AUTOSUSPEND_TIMEOUT);
pm_runtime_use_autosuspend(twi->dev);
pm_runtime_enable(twi->dev);
if (!twi->no_suspend) {
err = pm_runtime_get_sync(twi->dev);
if (err < 0)
goto err2;
}
scnprintf(twi->adap.name, sizeof(twi->adap.name), "SUNXI TWI(%pa)", &twi->res->start);
mutex_init(&twi->bus_lock);
twi->status = SUNXI_TWI_XFER_STATUS_IDLE;
twi->adap.owner = THIS_MODULE;
twi->adap.nr = twi->bus_num;
twi->adap.retries = 3;
twi->adap.timeout = 3 * HZ;
twi->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
twi->adap.algo = &sunxi_twi_algorithm;
twi->adap.bus_recovery_info = &sunxi_twi_bus_recovery;
twi->adap.lock_ops = &sunxi_twi_adap_lock_ops;
twi->adap.algo_data = twi;
twi->adap.dev.parent = &pdev->dev;
twi->adap.dev.of_node = pdev->dev.of_node;
platform_set_drvdata(pdev, twi);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_set_adapdata(&twi->adap, twi);
#endif
/*
* register twi adapter should be the ending of probe
* before register all the resouce twi controller need be ready
* (twi_xfer may occur at any time when register)
*/
err = i2c_add_numbered_adapter(&twi->adap);
if (err) {
sunxi_err(twi->dev, "failed to add adapter\n");
goto err3;
}
if (!twi->no_suspend) {
pm_runtime_mark_last_busy(twi->dev);
pm_runtime_put_autosuspend(twi->dev);
}
debug_chan_flags = 0x0; /* Initially set to disallow dynamic printing */
sunxi_info(twi->dev, "v%s probe success\n", SUNXI_TWI_VERSION);
return 0;
err3:
pm_runtime_put_noidle(twi->dev);
err2:
pm_runtime_disable(twi->dev);
pm_runtime_dont_use_autosuspend(twi->dev);
pm_runtime_set_suspended(twi->dev);
sunxi_twi_remove_sysfs(pdev);
sunxi_twi_remove_sysfs(pdev);
#if defined(CONFIG_AW_TWI_DELAYINIT) || defined(CONFIG_SUNXI_I2C_DELAYINIT)
if (twi->delay_init_done)
#endif /* CONFIG_AW_TWI_DELAYINIT */
sunxi_twi_hw_exit(twi);
err1:
sunxi_twi_resource_put(twi);
err0:
return err;
}
static int sunxi_twi_remove(struct platform_device *pdev)
{
struct sunxi_twi *twi = platform_get_drvdata(pdev);
i2c_del_adapter(&twi->adap);
platform_set_drvdata(pdev, NULL);
pm_runtime_put_noidle(twi->dev);
pm_runtime_disable(twi->dev);
pm_runtime_dont_use_autosuspend(twi->dev);
pm_runtime_set_suspended(twi->dev);
sunxi_twi_remove_sysfs(pdev);
#if defined(CONFIG_AW_TWI_DELAYINIT) || defined(CONFIG_SUNXI_I2C_DELAYINIT)
if (twi->rproc_ser_name)
rpmsg_notify_del(twi->rproc_ser_name, twi->rproc_device_name);
if (twi->delay_init_done)
#endif /* CONFIG_AW_TWI_DELAYINIT */
sunxi_twi_hw_exit(twi);
/*
* dma_release_channel() uses mutex_lock, it takes 7000us to release dma.
* Considering the performance impact, it is more appropriate to release when remove
*/
sunxi_twi_dma_release(twi);
sunxi_twi_resource_put(twi);
TWI_DBG(twi, "remove\n");
return 0;
}
static void sunxi_twi_shutdown(struct platform_device *pdev)
{
struct sunxi_twi *twi = platform_get_drvdata(pdev);
unsigned long flags, timeout;
if (twi->no_suspend) {
sunxi_info(twi->dev, "pmu use this twi, not do anything in shutdown!\n");
return;
}
if (twi->status == SUNXI_TWI_XFER_STATUS_IDLE)
sunxi_info(twi->dev, "xfer completed, shutdown twi directly\n");
else {
timeout = wait_event_timeout(twi->wait, twi->status != SUNXI_TWI_XFER_STATUS_RUNNING, twi->adap.timeout);
if (timeout == 0)
sunxi_err(twi->dev, "shutdown twi timeout, should be reinitialized when used in new stage\n");
}
spin_lock_irqsave(&twi->lock, flags);
twi->status = SUNXI_TWI_XFER_STATUS_SHUTDOWN;
spin_unlock_irqrestore(&twi->lock, flags);
sunxi_info(twi->dev, "shutdown finish\n");
return;
}
#if IS_ENABLED(CONFIG_PM)
static int sunxi_twi_runtime_suspend(struct device *dev)
{
struct sunxi_twi *twi = dev_get_drvdata(dev);
#if defined(CONFIG_AW_TWI_DELAYINIT) || defined(CONFIG_SUNXI_I2C_DELAYINIT)
if (!twi->delay_init_done)
return 0;
#endif /* CONFIG_AW_TWI_DELAYINIT */
sunxi_twi_bus_disable(twi);
sunxi_twi_clk_exit(twi);
sunxi_twi_select_pin_state(twi, PINCTRL_STATE_SLEEP);
TWI_DBG(twi, "runtime suspend finish\n");
return 0;
}
static int sunxi_twi_runtime_resume(struct device *dev)
{
struct sunxi_twi *twi = dev_get_drvdata(dev);
int err;
#if defined(CONFIG_AW_TWI_DELAYINIT) || defined(CONFIG_SUNXI_I2C_DELAYINIT)
if (!twi->delay_init_done)
return 0;
#endif /* CONFIG_AW_TWI_DELAYINIT */
err = sunxi_twi_select_pin_state(twi, PINCTRL_STATE_DEFAULT);
if (err) {
sunxi_err(twi->dev, "request twi gpio failed!\n");
return err;
}
err = sunxi_twi_clk_init(twi);
if (err) {
sunxi_err(twi->dev, "init twi clock failed!\n");
return err;
}
sunxi_twi_soft_reset(twi);
sunxi_twi_bus_enable(twi);
TWI_DBG(twi, "runtime resume finish\n");
return 0;
}
/* fake suspend_noirq function, does nothing and is intended to pair with resume_noirq */
static int sunxi_twi_suspend_noirq(struct device *dev)
{
return 0;
}
/* To address the issue of requiring reinitialization of the twi used by PMU in scenarios without CPUs */
static int sunxi_twi_resume_noirq(struct device *dev)
{
struct sunxi_twi *twi = dev_get_drvdata(dev);
/*
* cpus + normal standby : the twi used by pmu dont close it regulator
* cpus + supper stadby : the twi used by pmu dont close it regulator
* without cpus + normal standby : the twi used by pmu dont close it regulator
* without cpus + supper stadby : the twi used by pmu will close it regulator
*
* To address the need for reinitializing the TWI used by the PMU during system wake-up
* when there is no CPU, and potential issues with other modules modifying the APB bus
* clock, the hardware resource initialization is applied to the TWI used by the PMU
* during wake-up.
*/
if (twi->no_suspend) {
sunxi_info(twi->dev, "resume noirq\n");
return sunxi_twi_hw_init(twi);
}
return 0;
}
static int sunxi_twi_suspend_late(struct device *dev)
{
struct sunxi_twi *twi = dev_get_drvdata(dev);
unsigned long timeout;
/* the twi used by pmu should't suspend to keep pmu tranfer success when system standby */
if (twi->no_suspend) {
TWI_DBG(twi, "doesn't need to suspend\n");
return 0;
}
/* all the twis except used by pmu must wait for the last xfer complete before suspend */
if (twi->status == SUNXI_TWI_XFER_STATUS_RUNNING) {
timeout = wait_event_timeout(twi->wait, twi->status != SUNXI_TWI_XFER_STATUS_RUNNING,
twi->adap.timeout);
if (timeout == 0)
sunxi_info(twi->dev, "suspend_noirq twi timeout\n");
}
sunxi_twi_regulator_disable(twi);
sunxi_info(twi->dev, "suspend late\n");
return pm_runtime_force_suspend(dev);
}
static int sunxi_twi_resume_early(struct device *dev)
{
struct sunxi_twi *twi = dev_get_drvdata(dev);
int ret;
/*
* When using AXP as the wake-up source, the TWI used by the PMU will be utilized to
* read the PMU's registers. In this case, this TWI does not require any configuration.
* Otherwise, ongoing TWI communication may be interrupted, leading to a failure in
* system wake-up.
*/
if (twi->no_suspend)
return 0;
ret = sunxi_twi_regulator_enable(twi);
if (ret) {
sunxi_err(twi->dev, "enable regulator failed!\n");
return ret;
}
/*
* dev->power.needs_force_resume not change in sunxi_twi_suspend_late, so this is a
* fake resume, sunxi_twi_runtime_resume really be invoked before twi xfer
*/
ret = pm_runtime_force_resume(dev);
sunxi_info(twi->dev, "resume early\n");
return ret;
}
static const struct dev_pm_ops sunxi_twi_dev_pm_ops = {
.suspend_noirq = sunxi_twi_suspend_noirq,
.resume_noirq = sunxi_twi_resume_noirq,
.suspend_late = sunxi_twi_suspend_late,
.resume_early = sunxi_twi_resume_early,
.runtime_suspend = sunxi_twi_runtime_suspend,
.runtime_resume = sunxi_twi_runtime_resume,
};
#define SUNXI_TWI_DEV_PM_OPS (&sunxi_twi_dev_pm_ops)
#else
#define SUNXI_TWI_DEV_PM_OPS NULL
#endif
static struct sunxi_twi_hw_data sunxi_twi_v100_data = {
.has_clk_duty_30 = false,
.slave_func_fixed = false,
};
static struct sunxi_twi_hw_data sunxi_twi_v101_data = {
.has_clk_duty_30 = true,
.slave_func_fixed = false,
};
static const struct of_device_id sunxi_twi_match[] = {
/* compatible for old name format */
{ .compatible = "allwinner,sun8i-twi", .data = &sunxi_twi_v100_data },
{ .compatible = "allwinner,sun20i-twi", .data = &sunxi_twi_v100_data },
{ .compatible = "allwinner,sun50i-twi", .data = &sunxi_twi_v100_data },
{ .compatible = "allwinner,sunxi-twi-v100", .data = &sunxi_twi_v100_data },
/* For 1885/1890 */
{ .compatible = "allwinner,sunxi-twi-v101", .data = &sunxi_twi_v101_data },
{},
};
MODULE_DEVICE_TABLE(of, sunxi_twi_match);
static struct platform_driver sunxi_twi_driver = {
.probe = sunxi_twi_probe,
.remove = sunxi_twi_remove,
.shutdown = sunxi_twi_shutdown,
.driver = {
.name = "sunxi-twi",
.pm = SUNXI_TWI_DEV_PM_OPS,
.of_match_table = sunxi_twi_match,
},
};
static int __init sunxi_twi_adap_init(void)
{
return platform_driver_register(&sunxi_twi_driver);
}
static void __exit sunxi_twi_adap_exit(void)
{
platform_driver_unregister(&sunxi_twi_driver);
}
#if IS_ENABLED(CONFIG_AW_RPROC_FAST_BOOT)
subsys_initcall(sunxi_twi_adap_init);
#else
subsys_initcall_sync(sunxi_twi_adap_init);
#endif /* CONFIG_AW_TWI_DELAYINIT */
module_exit(sunxi_twi_adap_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:twi-sunxi");
MODULE_VERSION(SUNXI_TWI_VERSION);
MODULE_DESCRIPTION("SUNXI TWI Bus Driver");
MODULE_AUTHOR("pannan");
MODULE_AUTHOR("shaosidi <shaosidi@allwinnertech.com>");
MODULE_AUTHOR("chenmingxi <chenmingxi@allwinnertech.com>");