add rk30 i2c drvier

This commit is contained in:
kfx
2012-02-07 18:06:57 +08:00
parent e21b7af41b
commit 8e579254f1
9 changed files with 1396 additions and 1 deletions

View File

@@ -39,9 +39,51 @@
static struct platform_device *devices[] __initdata = {
};
// i2c
#ifdef CONFIG_I2C0_RK30
static struct i2c_board_info __initdata i2c0_info[] = {
};
#endif
#ifdef CONFIG_I2C1_RK30
static struct i2c_board_info __initdata i2c1_info[] = {
};
#endif
#ifdef CONFIG_I2C2_RK30
static struct i2c_board_info __initdata i2c2_info[] = {
};
#endif
#ifdef CONFIG_I2C3_RK30
static struct i2c_board_info __initdata i2c3_info[] = {
};
#endif
#ifdef CONFIG_I2C4_RK30
static struct i2c_board_info __initdata i2c4_info[] = {
};
#endif
static void __init rk30_i2c_register_board_info(void)
{
#ifdef CONFIG_I2C0_RK30
i2c_register_board_info(0, i2c0_info, ARRAY_SIZE(i2c0_info));
#endif
#ifdef CONFIG_I2C1_RK30
i2c_register_board_info(1, i2c1_info, ARRAY_SIZE(i2c1_info));
#endif
#ifdef CONFIG_I2C2_RK30
i2c_register_board_info(2, i2c2_info, ARRAY_SIZE(i2c2_info));
#endif
#ifdef CONFIG_I2C3_RK30
i2c_register_board_info(3, i2c3_info, ARRAY_SIZE(i2c3_info));
#endif
#ifdef CONFIG_I2C4_RK30
i2c_register_board_info(4, i2c4_info, ARRAY_SIZE(i2c4_info));
#endif
}
//end of i2c
static void __init machine_rk30_board_init(void)
{
platform_add_devices(devices, ARRAY_SIZE(devices));
rk30_i2c_register_board_info();
platform_add_devices(devices, ARRAY_SIZE(devices));
}
static void __init rk30_reserve(void)

View File

@@ -126,6 +126,200 @@ static void __init rk30_init_uart(void)
#endif
}
// i2c
#ifdef CONFIG_I2C0_RK30
static struct rk30_i2c_platform_data default_i2c0_data = {
.bus_num = 0,
.is_div_from_arm = 1,
#ifdef CONFIG_I2C0_CONTROLLER_RK29
.adap_type = I2C_RK29_ADAP,
#endif
#ifdef CONFIG_I2C0_CONTROLLER_RK30
.adap_type = I2C_RK30_ADAP,
#endif
};
static struct resource resources_i2c0[] = {
{
.start = IRQ_I2C0,
.end = IRQ_I2C0,
.flags = IORESOURCE_IRQ,
},
{
.start = RK30_I2C0_PHYS,
.end = RK30_I2C0_PHYS + RK30_I2C0_SIZE - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device device_i2c0 = {
.name = "i2c-rk30",
.id = 0,
.num_resources = ARRAY_SIZE(resources_i2c0),
.resource = resources_i2c0,
.dev = {
.platform_data = &default_i2c0_data,
},
};
#endif
#ifdef CONFIG_I2C1_RK30
static struct rk30_i2c_platform_data default_i2c1_data = {
.bus_num = 1,
.is_div_from_arm = 1,
#ifdef CONFIG_I2C1_CONTROLLER_RK29
.adap_type = I2C_RK29_ADAP,
#endif
#ifdef CONFIG_I2C1_CONTROLLER_RK30
.adap_type = I2C_RK30_ADAP,
#endif
};
static struct resource resources_i2c1[] = {
{
.start = IRQ_I2C1,
.end = IRQ_I2C1,
.flags = IORESOURCE_IRQ,
},
{
.start = RK30_I2C1_PHYS,
.end = RK30_I2C1_PHYS + RK30_I2C1_SIZE - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device device_i2c1 = {
.name = "i2c-rk30",
.id = 1,
.num_resources = ARRAY_SIZE(resources_i2c1),
.resource = resources_i2c1,
.dev = {
.platform_data = &default_i2c1_data,
},
};
#endif
#ifdef CONFIG_I2C2_RK30
static struct rk30_i2c_platform_data default_i2c2_data = {
.bus_num = 2,
.is_div_from_arm = 0,
#ifdef CONFIG_I2C2_CONTROLLER_RK29
.adap_type = I2C_RK29_ADAP,
#endif
#ifdef CONFIG_I2C2_CONTROLLER_RK30
.adap_type = I2C_RK30_ADAP,
#endif
};
static struct resource resources_i2c2[] = {
{
.start = IRQ_I2C2,
.end = IRQ_I2C2,
.flags = IORESOURCE_IRQ,
},
{
.start = RK30_I2C2_PHYS,
.end = RK30_I2C2_PHYS + RK30_I2C2_SIZE - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device device_i2c2 = {
.name = "i2c-rk30",
.id = 2,
.num_resources = ARRAY_SIZE(resources_i2c2),
.resource = resources_i2c2,
.dev = {
.platform_data = &default_i2c2_data,
},
};
#endif
#ifdef CONFIG_I2C3_RK30
static struct rk30_i2c_platform_data default_i2c3_data = {
.bus_num = 3,
.is_div_from_arm = 0,
#ifdef CONFIG_I2C3_CONTROLLER_RK29
.adap_type = I2C_RK29_ADAP,
#endif
#ifdef CONFIG_I2C3_CONTROLLER_RK30
.adap_type = I2C_RK30_ADAP,
#endif
};
static struct resource resources_i2c3[] = {
{
.start = IRQ_I2C3,
.end = IRQ_I2C3,
.flags = IORESOURCE_IRQ,
},
{
.start = RK30_I2C3_PHYS,
.end = RK30_I2C3_PHYS + RK30_I2C3_SIZE - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device device_i2c3 = {
.name = "i2c-rk30",
.id = 3,
.num_resources = ARRAY_SIZE(resources_i2c3),
.resource = resources_i2c3,
.dev = {
.platform_data = &default_i2c3_data,
},
};
#endif
#ifdef CONFIG_I2C4_RK30
static struct rk30_i2c_platform_data default_i2c4_data = {
.bus_num = 4,
.is_div_from_arm = 0,
#ifdef CONFIG_I2C4_CONTROLLER_RK29
.adap_type = I2C_RK29_ADAP,
#endif
#ifdef CONFIG_I2C4_CONTROLLER_RK30
.adap_type = I2C_RK30_ADAP,
#endif
};
static struct resource resources_i2c4[] = {
{
.start = IRQ_I2C4,
.end = IRQ_I2C4,
.flags = IORESOURCE_IRQ,
},
{
.start = RK30_I2C4_PHYS,
.end = RK30_I2C4_PHYS + RK30_I2C4_SIZE - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device device_i2c4 = {
.name = "i2c-rk30",
.id = 4,
.num_resources = ARRAY_SIZE(resources_i2c4),
.resource = resources_i2c4,
.dev = {
.platform_data = &default_i2c4_data,
},
};
#endif
static void __init rk30_init_i2c(void)
{
#ifdef CONFIG_I2C0_RK30
platform_device_register(&device_i2c0);
#endif
#ifdef CONFIG_I2C1_RK30
platform_device_register(&device_i2c1);
#endif
#ifdef CONFIG_I2C2_RK30
platform_device_register(&device_i2c2);
#endif
#ifdef CONFIG_I2C3_RK30
platform_device_register(&device_i2c3);
#endif
#ifdef CONFIG_I2C4_RK30
platform_device_register(&device_i2c4);
#endif
}
//end of i2c
#ifdef CONFIG_MTD_NAND_RK29XX
static struct resource resources_nand[] = {
{
@@ -146,6 +340,7 @@ static struct platform_device device_nand = {
static int __init rk30_init_devices(void)
{
rk30_init_uart();
rk30_init_i2c();
#ifdef CONFIG_MTD_NAND_RK29XX
platform_device_register(&device_nand);
#endif

View File

@@ -11,6 +11,19 @@
#define BOOT_MODE_OFFMODE_CHARGING 5
#define BOOT_MODE_REBOOT 6
#define BOOT_MODE_PANIC 7
struct rk30_i2c_platform_data {
char *name;
int bus_num;
#define I2C_RK29_ADAP 0
#define I2C_RK30_ADAP 1
int adap_type:1;
int is_div_from_arm:1;
u32 flags;
int (*io_init)(void);
int (*io_deinit)(void);
};
int board_boot_mode(void);
/* for USB detection */

View File

@@ -839,6 +839,87 @@ config SCx200_I2C
depends on SCx200_GPIO
select I2C_ALGOBIT
config I2C_RK30
tristate "RK I2C Adapter"
depends on PLAT_RK && !ARCH_RK29
default y
help
This supports I2C Adapter on RK Soc.
if I2C_RK30
comment "Now, there are five selectable I2C channels."
config I2C0_RK30
bool "I2C0 Channel Support"
default y
help
This supports the use of the I2C0 channel on RK Soc.
if I2C0_RK30
choice
prompt "I2C Controller Select"
config I2C0_CONTROLLER_RK29
bool "With RK29 I2C Controller"
config I2C0_CONTROLLER_RK30
bool "With RK30 I2C Controller"
endchoice
endif
config I2C1_RK30
bool "I2C1 Channel Support"
default y
help
This supports the use of the I2C1 channel on RK Soc.
if I2C1_RK30
choice
prompt "I2C Controller Select"
config I2C1_CONTROLLER_RK29
bool "With RK29 I2C Controller"
config I2C1_CONTROLLER_RK30
bool "With RK30 I2C Controller"
endchoice
endif
config I2C2_RK30
bool "I2C2 Channel Support"
default y
help
This supports the use of the I2C2 channel on RK Soc.
if I2C2_RK30
choice
prompt "I2C Controller Select"
config I2C2_CONTROLLER_RK29
bool "With RK29 I2C Controller"
config I2C2_CONTROLLER_RK30
bool "With RK30 I2C Controller"
endchoice
endif
config I2C3_RK30
bool "I2C3 Channel Support"
default y
help
This supports the use of the I2C3 channel on RK Soc.
if I2C3_RK30
choice
prompt "I2C Controller Select"
config I2C3_CONTROLLER_RK29
bool "With RK29 I2C Controller"
config I2C3_CONTROLLER_RK30
bool "With RK30 I2C Controller"
endchoice
endif
config I2C4_RK30
bool "I2C4 Channel Support"
default y
help
This supports the use of the I2C4 channel on RK Soc.
if I2C4_RK30
choice
prompt "I2C Controller Select"
config I2C4_CONTROLLER_RK29
bool "With RK29 I2C Controller"
config I2C4_CONTROLLER_RK30
bool "With RK30 I2C Controller"
endchoice
endif
endif
config I2C_RK29
tristate "RK29 i2c interface (I2C)"
depends on ARCH_RK29

View File

@@ -3,6 +3,7 @@
#
obj-$(CONFIG_I2C_RK29) += i2c-rk29.o
obj-$(CONFIG_I2C_DEV_RK29) += i2c-dev-rk29.o
obj-$(CONFIG_I2C_RK30) += i2c-rk30.o i2c-rk29-adapter.o i2c-rk30-adapter.o
obj-y += i2c-gpio.o
# ACPI drivers

View File

@@ -0,0 +1,619 @@
/* drivers/i2c/busses/i2c-rk29-adapter.c
*
* Copyright (C) 2012 ROCKCHIP, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "i2c-rk30.h"
/* master transmit */
#define I2C_MTXR (0x0000)
/* master receive */
#define I2C_MRXR (0x0004)
/* slave address */
#define I2C_SADDR (0x0010)
/* interrupt enable control */
#define I2C_IER (0x0014)
#define I2C_IER_ARBITR_LOSE (1<<7)
#define I2C_IER_MRX_NEED_ACK (1<<1)
#define I2C_IER_MTX_RCVD_ACK (1<<0)
#define IRQ_MST_ENABLE (I2C_IER_ARBITR_LOSE | \
I2C_IER_MRX_NEED_ACK | \
I2C_IER_MTX_RCVD_ACK)
#define IRQ_ALL_DISABLE (0x00)
/* interrupt status, write 0 to clear */
#define I2C_ISR (0x0018)
#define I2C_ISR_ARBITR_LOSE (1<<7)
#define I2C_ISR_MRX_NEED_ACK (1<<1)
#define I2C_ISR_MTX_RCVD_ACK (1<<0)
/* stop/start/resume command, write 1 to set */
#define I2C_LCMR (0x001c)
#define I2C_LCMR_RESUME (1<<2)
#define I2C_LCMR_STOP (1<<1)
#define I2C_LCMR_START (1<<0)
/* i2c core status */
#define I2C_LSR (0x0020)
#define I2C_LSR_RCV_NAK (1<<1)
#define I2C_LSR_RCV_ACK (~(1<<1))
#define I2C_LSR_BUSY (1<<0)
/* i2c config */
#define I2C_CONR (0x0024)
#define I2C_CONR_NAK (1<<4)
#define I2C_CONR_ACK (~(1<<4))
#define I2C_CONR_MTX_MODE (1<<3)
#define I2C_CONR_MRX_MODE (~(1<<3))
#define I2C_CONR_MPORT_ENABLE (1<<2)
#define I2C_CONR_MPORT_DISABLE (~(1<<2))
/* i2c core config */
#define I2C_OPR (0x0028)
#define I2C_OPR_RESET_STATUS (1<<7)
#define I2C_OPR_CORE_ENABLE (1<<6)
#define I2CCDVR_REM_BITS (0x03)
#define I2CCDVR_REM_MAX (1<<(I2CCDVR_REM_BITS))
#define I2CCDVR_EXP_BITS (0x03)
#define I2CCDVR_EXP_MAX (1<<(I2CCDVR_EXP_BITS))
#define RK29_I2C_START_TMO_COUNT 100 // msleep 1 * 100
#define ABNORMAL_STOP_DELAY 200 //unit us
static unsigned int abnormal_stop_addr[] = {
0x2d,
};
int i2c_suspended(struct i2c_adapter *adap)
{
return 1;
}
EXPORT_SYMBOL(i2c_suspended);
static inline void rk29_i2c_disable_ack(void __iomem *regs)
{
unsigned long conr = readl(regs + I2C_CONR);
conr |= I2C_CONR_NAK;
writel(conr, regs + I2C_CONR);
}
static inline void rk29_i2c_enable_ack(void __iomem *regs)
{
unsigned long conr = readl(regs + I2C_CONR);
conr &= I2C_CONR_ACK;
writel(conr, regs + I2C_CONR);
}
static inline void rk29_i2c_disable_mport(void __iomem *regs)
{
unsigned long conr = readl(regs + I2C_CONR);
conr &= I2C_CONR_MPORT_DISABLE;
writel(conr, regs + I2C_CONR);
}
static inline void rk29_i2c_enable_mport(void __iomem *regs)
{
unsigned long conr = readl(regs + I2C_CONR);
conr |= I2C_CONR_MPORT_ENABLE;
writel(conr, regs + I2C_CONR);
}
static inline void rk29_i2c_disable_irq(void __iomem *regs)
{
writel(IRQ_ALL_DISABLE, regs + I2C_IER);
}
static inline void rk29_i2c_enable_irq(void __iomem *regs)
{
writel(IRQ_MST_ENABLE, regs + I2C_IER);
}
/* scl = pclk/(5 *(rem+1) * 2^(exp+1)) */
static void rk29_i2c_calcdivisor(unsigned long pclk,
unsigned long scl_rate,
unsigned long *real_rate,
unsigned int *rem, unsigned int *exp)
{
unsigned int calc_rem = 0;
unsigned int calc_exp = 0;
for(calc_exp = 0; calc_exp < I2CCDVR_EXP_MAX; calc_exp++)
{
calc_rem = pclk / (5 * scl_rate * (1 <<(calc_exp +1)));
if(calc_rem < I2CCDVR_REM_MAX)
break;
}
if(calc_rem >= I2CCDVR_REM_MAX || calc_exp >= I2CCDVR_EXP_MAX)
{
calc_rem = I2CCDVR_REM_MAX - 1;
calc_exp = I2CCDVR_EXP_MAX - 1;
}
*rem = calc_rem;
*exp = calc_exp;
*real_rate = pclk/(5 * (calc_rem + 1) * (1 <<(calc_exp +1)));
return;
}
static void rk29_i2c_set_clk(struct rk30_i2c *i2c, unsigned long scl_rate)
{
unsigned int rem = 0, exp = 0;
unsigned long real_rate = 0, tmp;
unsigned long i2c_rate = 24000000;//clk_get_rate(i2c->clk);
if((scl_rate == i2c->scl_rate) && (i2c_rate == i2c->i2c_rate))
return;
i2c->i2c_rate = i2c_rate;
i2c->scl_rate = scl_rate;
rk29_i2c_calcdivisor(i2c->i2c_rate, i2c->scl_rate, &real_rate, &rem, &exp);
tmp = readl(i2c->regs + I2C_OPR);
tmp &= ~0x3f;
tmp |= exp;
tmp |= rem<<I2CCDVR_EXP_BITS;
writel(tmp, i2c->regs + I2C_OPR);
return;
}
static void rk29_i2c_init_hw(struct rk30_i2c *i2c)
{
unsigned long opr = readl(i2c->regs + I2C_OPR);
opr |= I2C_OPR_RESET_STATUS;
writel(opr, i2c->regs + I2C_OPR);
udelay(10);
opr = readl(i2c->regs + I2C_OPR);
opr &= ~I2C_OPR_RESET_STATUS;
writel(opr, i2c->regs + I2C_OPR);
rk29_i2c_set_clk(i2c, 100000);
rk29_i2c_disable_irq(i2c);
writel(0, i2c->regs + I2C_LCMR);
writel(0, i2c->regs + I2C_LCMR);
opr = readl(i2c->regs + I2C_OPR);
opr |= I2C_OPR_CORE_ENABLE;
writel(opr, i2c->regs + I2C_OPR);
udelay(i2c->tx_setup);
return;
}
static inline void rk29_i2c_master_complete(struct rk30_i2c *i2c, int ret)
{
i2c_dbg(i2c->dev, "master_complete %d\n", ret);
i2c->msg_ptr = 0;
i2c->msg = NULL;
i2c->msg_idx++;
i2c->msg_num = 0;
if (ret)
i2c->msg_idx = ret;
wake_up(&i2c->wait);
}
static void rk29_i2c_message_start(struct rk30_i2c *i2c,
struct i2c_msg *msg)
{
unsigned int addr = (msg->addr & 0x7f) << 1;
unsigned long stat, conr;
stat = 0;
i2c->addr = msg->addr & 0x7f;
if (msg->flags & I2C_M_RD)
addr |= 1;
if (msg->flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
rk29_i2c_enable_ack(i2c->regs);
i2c_dbg(i2c->dev, "START: set addr 0x%02x to DS\n", addr);
conr = readl(i2c->regs + I2C_CONR);
conr |= I2C_CONR_MTX_MODE;
writel(conr, i2c->regs + I2C_CONR);
writel(addr, i2c->regs + I2C_MTXR);
udelay(i2c->tx_setup);
writel(I2C_LCMR_START|I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
}
static inline void rk29_i2c_stop(struct rk30_i2c *i2c, int ret)
{
unsigned int n, i;
i2c_dbg(i2c->dev, "STOP\n");
udelay(i2c->tx_setup);
n = sizeof(abnormal_stop_addr)/sizeof(abnormal_stop_addr[0]);
for(i = 0; i < n; i++){
if(i2c->addr == abnormal_stop_addr[i])
udelay(ABNORMAL_STOP_DELAY);
}
writel(I2C_LCMR_STOP|I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
i2c->state = STATE_STOP;
rk29_i2c_master_complete(i2c, ret);
rk29_i2c_disable_irq(i2c->regs);
}
/* returns TRUE if the current message is the last in the set */
static inline int is_lastmsg(struct rk30_i2c *i2c)
{
return i2c->msg_idx >= (i2c->msg_num - 1);
}
/* returns TRUE if we this is the last byte in the current message */
static inline int is_msglast(struct rk30_i2c *i2c)
{
return i2c->msg_ptr == i2c->msg->len-1;
}
/* returns TRUE if we reached the end of the current message */
static inline int is_msgend(struct rk30_i2c *i2c)
{
return i2c->msg_ptr >= i2c->msg->len;
}
static int rk29_i2c_irq_nextbyte(struct rk30_i2c *i2c, unsigned long isr)
{
unsigned long lsr, conr;
unsigned char byte;
int ret = 0;
lsr = readl(i2c->regs + I2C_LSR);
switch (i2c->state) {
case STATE_IDLE:
dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__);
goto out;
case STATE_STOP:
dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__);
rk29_i2c_disable_irq(i2c->regs);
goto out;
case STATE_START:
if(!(isr & I2C_ISR_MTX_RCVD_ACK)){
writel(isr & ~I2C_ISR_MTX_RCVD_ACK, i2c->regs + I2C_ISR);
rk29_i2c_stop(i2c, -ENXIO);
dev_err(i2c->dev, "START: addr[0x%02x] ack was not received(isr)\n", i2c->addr);
goto out;
}
if ((lsr & I2C_LSR_RCV_NAK) &&
!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
writel(isr & ~I2C_ISR_MTX_RCVD_ACK, i2c->regs + I2C_ISR);
rk29_i2c_stop(i2c, -ENXIO);
dev_err(i2c->dev, "START: addr[0x%02x] ack was not received(lsr)\n", i2c->addr);
goto out;
}
if (i2c->msg->flags & I2C_M_RD)
i2c->state = STATE_READ;
else
i2c->state = STATE_WRITE;
/* terminate the transfer if there is nothing to do
* as this is used by the i2c probe to find devices. */
if (is_lastmsg(i2c) && i2c->msg->len == 0) {
writel(isr & ~I2C_ISR_MTX_RCVD_ACK, i2c->regs + I2C_ISR);
rk29_i2c_stop(i2c, 0);
goto out;
}
if (i2c->state == STATE_READ){
writel(isr & ~I2C_ISR_MTX_RCVD_ACK, i2c->regs + I2C_ISR);
goto prepare_read;
}
case STATE_WRITE:
if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
if (!(isr & I2C_ISR_MTX_RCVD_ACK)){
rk29_i2c_stop(i2c, -ECONNREFUSED);
dev_err(i2c->dev, "WRITE: addr[0x%02x] No Ack\n", i2c->addr);
goto out;
}
}
writel(isr & ~I2C_ISR_MTX_RCVD_ACK, i2c->regs + I2C_ISR);
retry_write:
if (!is_msgend(i2c)) {
byte = i2c->msg->buf[i2c->msg_ptr++];
conr = readl(i2c->regs + I2C_CONR);
conr |= I2C_CONR_MTX_MODE;
writel(conr, i2c->regs + I2C_CONR);
writel(byte, i2c->regs + I2C_MTXR);
writel(I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
} else if (!is_lastmsg(i2c)) {
/* we need to go to the next i2c message */
i2c_dbg(i2c->dev, "WRITE: Next Message\n");
i2c->msg_ptr = 0;
i2c->msg_idx++;
i2c->msg++;
/* check to see if we need to do another message */
if (i2c->msg->flags & I2C_M_NOSTART) {
if (i2c->msg->flags & I2C_M_RD) {
/* cannot do this, the controller
* forces us to send a new START
* when we change direction */
rk29_i2c_stop(i2c, -EINVAL);
}
goto retry_write;
} else {
/* send the new start */
rk29_i2c_message_start(i2c, i2c->msg);
i2c->state = STATE_START;
}
} else {
/* send stop */
rk29_i2c_stop(i2c, 0);
}
break;
case STATE_READ:
if(!(isr & I2C_ISR_MRX_NEED_ACK)){
rk29_i2c_stop(i2c, -ENXIO);
dev_err(i2c->dev, "READ: addr[0x%02x] not recv need ack interrupt\n", i2c->addr);
goto out;
}
writel(isr & ~I2C_ISR_MRX_NEED_ACK, i2c->regs + I2C_ISR);
byte = readl(i2c->regs + I2C_MRXR);
i2c_dbg(i2c->dev, "READ: byte = %d\n", byte);
i2c->msg->buf[i2c->msg_ptr++] = byte;
prepare_read:
if (is_msgend(i2c)) {
if (is_lastmsg(i2c)) {
/* last message, send stop and complete */
rk29_i2c_disable_ack(i2c);
i2c_dbg(i2c->dev, "READ: Send Stop\n");
rk29_i2c_stop(i2c, 0);
} else {
/* go to the next transfer */
i2c_dbg(i2c->dev, "READ: Next Transfer\n");
i2c->msg_ptr = 0;
i2c->msg_idx++;
i2c->msg++;
rk29_i2c_message_start(i2c, i2c->msg);
i2c->state = STATE_START;
}
}else{
conr = readl(i2c->regs + I2C_CONR);
conr &= I2C_CONR_MRX_MODE;
writel(conr, i2c->regs + I2C_CONR);
writel(I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
}
break;
}
out:
return ret;
}
static irqreturn_t rk29_i2c_irq(int irqno, void *dev_id)
{
struct rk30_i2c *i2c = dev_id;
unsigned long isr;
spin_lock(&i2c->lock);
udelay(i2c->tx_setup);
isr = readl(i2c->regs + I2C_ISR);
if (isr & I2C_ISR_ARBITR_LOSE) {
writel(isr & ~I2C_ISR_ARBITR_LOSE, i2c->regs + I2C_ISR);
dev_err(i2c->dev, "deal with arbitration loss\n");
}
if (i2c->state == STATE_IDLE) {
i2c_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");
goto out;
}
rk29_i2c_irq_nextbyte(i2c, isr);
out:
spin_unlock(&i2c->lock);
return IRQ_HANDLED;
}
/* rk29_i2c_set_master
*
* get the i2c bus for a master transaction
*/
static int rk29_i2c_set_master(struct rk30_i2c *i2c)
{
int tmo = RK29_I2C_START_TMO_COUNT;
unsigned long lsr;
while (tmo-- > 0) {
lsr = readl(i2c->regs + I2C_LSR);
if (!(lsr & I2C_LSR_BUSY))
return 0;
msleep(1);
}
return -ETIMEDOUT;
}
/* rk29_i2c_doxfer
*
* this starts an i2c transfer
*/
static int rk29_i2c_doxfer(struct rk30_i2c *i2c,
struct i2c_msg *msgs, int num)
{
unsigned long timeout;
int ret;
if (i2c->suspended)
return -EIO;
ret = rk29_i2c_set_master(i2c);
if (ret != 0) {
dev_err(i2c->dev, "addr[0x%02x] cannot get bus (error %d)\n", msgs[0].addr, ret);
ret = -EAGAIN;
goto out;
}
spin_lock_irq(&i2c->lock);
i2c->msg = msgs;
i2c->msg_num = num;
i2c->msg_ptr = 0;
i2c->msg_idx = 0;
i2c->state = STATE_START;
rk29_i2c_enable_irq(i2c);
rk29_i2c_message_start(i2c, msgs);
spin_unlock_irq(&i2c->lock);
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
ret = i2c->msg_idx;
if (timeout == 0)
i2c_dbg(i2c->dev, "addr[0x%02x] wait event timeout\n", msgs[0].addr);
else if (ret != num)
i2c_dbg(i2c->dev, "addr[0x%02x ]incomplete xfer (%d)\n", msgs[0].addr, ret);
msleep(1);
if((readl(i2c->regs + I2C_LSR) & I2C_LSR_BUSY) ||
readl(i2c->regs + I2C_LCMR) & I2C_LCMR_STOP ){
dev_warn(i2c->dev, "WARNING: STOP abnormal, addr[0x%02x] isr = 0x%x, lsr = 0x%x, lcmr = 0x%x\n",
msgs[0].addr,
readl(i2c->regs + I2C_ISR),
readl(i2c->regs + I2C_LSR),
readl(i2c->regs + I2C_LCMR)
);
}
out:
return ret;
}
/* rk29_i2c_xfer
*
* first port of call from the i2c bus code when an message needs
* transferring across the i2c bus.
*/
static int rk29_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct rk30_i2c *i2c = (struct rk30_i2c *)adap->algo_data;
int retry;
int ret;
unsigned long scl_rate;
if(msgs[0].scl_rate <= 400000 && msgs[0].scl_rate >= 10000)
scl_rate = msgs[0].scl_rate;
else if(msgs[0].scl_rate > 400000){
dev_warn(i2c->dev, "Warning: addr[0x%x] msg[0].scl_rate( = %dKhz) is too high!",
msgs[0].addr, msgs[0].scl_rate/1000);
scl_rate = 400000;
}
else{
dev_warn(i2c->dev, "Warning: addr[0x%x] msg[0].scl_rate( = %dKhz) is too low!",
msgs[0].addr, msgs[0].scl_rate/1000);
scl_rate = 10000;
}
if(i2c->is_div_from_arm[i2c->adap.nr])
wake_lock(&i2c->idlelock[i2c->adap.nr]);
rk29_i2c_set_clk(i2c, scl_rate);
rk29_i2c_enable_mport(i2c->regs);
udelay(i2c->tx_setup);
for (retry = 0; retry < adap->retries; retry++) {
ret = rk29_i2c_doxfer(i2c, msgs, num);
if (ret != -EAGAIN) {
return ret;
}
i2c_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
msleep(1);
}
rk29_i2c_disable_mport(i2c->regs);
if(i2c->is_div_from_arm[i2c->adap.nr])
wake_unlock(&i2c->idlelock[i2c->adap.nr]);
return -EREMOTEIO;
}
/* declare our i2c functionality */
static u32 rk29_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}
/* i2c bus registration info */
static const struct i2c_algorithm rk29_i2c_algorithm = {
.master_xfer = rk29_i2c_xfer,
.functionality = rk29_i2c_func,
};
int i2c_add_rk29_adapter(struct i2c_adapter *adap)
{
int ret = 0;
struct rk30_i2c *i2c = (struct rk30_i2c *)adap->algo_data;
adap->algo = &rk29_i2c_algorithm;
adap->retries = 3;
i2c->i2c_init_hw = &rk29_i2c_init_hw;
i2c->i2c_set_clk = &rk29_i2c_set_clk;
i2c->i2c_irq = &rk29_i2c_irq;
ret = i2c_add_numbered_adapter(adap);
return ret;
}

View File

@@ -0,0 +1,69 @@
/* drivers/i2c/busses/i2c-rk30-adapter.c
*
* Copyright (C) 2012 ROCKCHIP, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "i2c-rk30.h"
static void rk30_i2c_set_clk(struct rk30_i2c *i2c, unsigned long scl_rate)
{
return;
}
static void rk30_i2c_init_hw(struct rk30_i2c *i2c)
{
return;
}
static irqreturn_t rk30_i2c_irq(int irq, void *dev_id)
{
return IRQ_HANDLED;
}
static int rk30_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
return 0;
}
/* declare our i2c functionality */
static u32 rk30_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}
/* i2c bus registration info */
static const struct i2c_algorithm rk30_i2c_algorithm = {
.master_xfer = rk30_i2c_xfer,
.functionality = rk30_i2c_func,
};
int i2c_add_rk30_adapter(struct i2c_adapter *adap)
{
int ret = 0;
struct rk30_i2c *i2c = (struct rk30_i2c *)adap->algo_data;
adap->algo = &rk30_i2c_algorithm;
adap->retries = 3;
i2c->i2c_init_hw = &rk30_i2c_init_hw;
i2c->i2c_set_clk = &rk30_i2c_set_clk;
i2c->i2c_irq = &rk30_i2c_irq;
ret = i2c_add_numbered_adapter(adap);
return ret;
}

296
drivers/i2c/busses/i2c-rk30.c Executable file
View File

@@ -0,0 +1,296 @@
/* drivers/i2c/busses/i2c-rk30.c
*
* Copyright (C) 2012 ROCKCHIP, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "i2c-rk30.h"
#define TX_SETUP 1 //unit us
#ifdef CONFIG_CPU_FREQ
#define freq_to_i2c(_n) container_of(_n, struct rk30_i2c, freq_transition)
static int rk30_i2c_cpufreq_transition(struct notifier_block *nb,
unsigned long val, void *data)
{
struct rk30_i2c *i2c = freq_to_i2c(nb);
unsigned long flags;
int delta_f;
delta_f = clk_get_rate(i2c->clk) - i2c->i2c_rate;
if ((val == CPUFREQ_POSTCHANGE && delta_f < 0) ||
(val == CPUFREQ_PRECHANGE && delta_f > 0))
{
spin_lock_irqsave(&i2c->lock, flags);
i2c->i2c_set_clk(i2c, i2c->scl_rate);
spin_unlock_irqrestore(&i2c->lock, flags);
}
return 0;
}
static inline int rk30_i2c_register_cpufreq(struct rk30_i2c *i2c)
{
if (i2c->adap.nr != 0)
return 0;
i2c->freq_transition.notifier_call = rk30_i2c_cpufreq_transition;
return cpufreq_register_notifier(&i2c->freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
}
static inline void rk30_i2c_deregister_cpufreq(struct rk30_i2c *i2c)
{
if (i2c->adap.nr != 0)
return;
cpufreq_unregister_notifier(&i2c->freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
}
#else
static inline int rk30_i2c_register_cpufreq(struct rk30_i2c *i2c)
{
return 0;
}
static inline void rk30_i2c_deregister_cpufreq(struct rk30_i2c *i2c)
{
}
#endif
/* rk30_i2c_probe
*
* called by the bus driver when a suitable device is found
*/
static int rk30_i2c_probe(struct platform_device *pdev)
{
struct rk30_i2c *i2c = NULL;
struct rk30_i2c_platform_data *pdata = NULL;
struct resource *res;
char name[5];
int ret;
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "no platform data\n");
return -EINVAL;
}
if(pdata->io_init)
pdata->io_init();
i2c = kzalloc(sizeof(struct rk30_i2c), GFP_KERNEL);
if (!i2c) {
dev_err(&pdev->dev, "no memory for state\n");
return -ENOMEM;
}
strlcpy(i2c->adap.name, "rk30_i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->tx_setup = TX_SETUP;
spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);
/* find the clock and enable it */
i2c->dev = &pdev->dev;
i2c->clk = clk_get(&pdev->dev, "i2c");
if (IS_ERR(i2c->clk)) {
dev_err(&pdev->dev, "cannot get clock\n");
ret = -ENOENT;
goto err_noclk;
}
i2c_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
clk_enable(i2c->clk);
/* map the registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "cannot find IO resource\n");
ret = -ENOENT;
goto err_get_resource;
}
i2c->ioarea = request_mem_region(res->start, resource_size(res),
pdev->name);
if (i2c->ioarea == NULL) {
dev_err(&pdev->dev, "cannot request IO\n");
ret = -ENXIO;
goto err_ioarea;
}
i2c->regs = ioremap(res->start, resource_size(res));
if (i2c->regs == NULL) {
dev_err(&pdev->dev, "cannot map IO\n");
ret = -ENXIO;
goto err_ioremap;
}
i2c_dbg(&pdev->dev, "registers %p (%p, %p)\n",
i2c->regs, i2c->ioarea, res);
/* setup info block for the i2c core */
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.nr = pdata->bus_num;
if(pdata->adap_type == I2C_RK29_ADAP)
ret = i2c_add_rk29_adapter(&i2c->adap);
else // I2C_RK30_ADAP
ret = i2c_add_rk30_adapter(&i2c->adap);
if (ret < 0) {
dev_err(&pdev->dev, "failed to add adapter\n");
goto err_add_adapter;
}
/* find the IRQ for this unit (note, this relies on the init call to
* ensure no current IRQs pending
*/
i2c->irq = ret = platform_get_irq(pdev, 0);
if (ret <= 0) {
dev_err(&pdev->dev, "cannot find IRQ\n");
goto err_get_irq;
}
ret = request_irq(i2c->irq, i2c->i2c_irq, IRQF_DISABLED,
dev_name(&pdev->dev), i2c);
if (ret != 0) {
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
goto err_request_irq;
}
ret = rk30_i2c_register_cpufreq(i2c);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
goto err_register_cpufreq;
}
platform_set_drvdata(pdev, i2c);
sprintf(name, "%s%d", "i2c", i2c->adap.nr);
i2c->is_div_from_arm[i2c->adap.nr] = pdata->is_div_from_arm;
if(i2c->is_div_from_arm[i2c->adap.nr])
wake_lock_init(&i2c->idlelock[i2c->adap.nr], WAKE_LOCK_IDLE, name);
i2c->i2c_init_hw(i2c);
dev_info(&pdev->dev, "%s: RK30 I2C adapter\n", dev_name(&i2c->adap.dev));
return 0;
//err_none:
// rk30_i2c_deregister_cpufreq(i2c);
err_register_cpufreq:
free_irq(i2c->irq, i2c);
err_request_irq:
err_get_irq:
i2c_del_adapter(&i2c->adap);
err_add_adapter:
iounmap(i2c->regs);
err_ioremap:
kfree(i2c->ioarea);
err_ioarea:
release_resource(i2c->ioarea);
err_get_resource:
clk_put(i2c->clk);
err_noclk:
kfree(i2c);
return ret;
}
/* rk30_i2c_remove
*
* called when device is removed from the bus
*/
static int rk30_i2c_remove(struct platform_device *pdev)
{
struct rk30_i2c *i2c = platform_get_drvdata(pdev);
rk30_i2c_deregister_cpufreq(i2c);
free_irq(i2c->irq, i2c);
i2c_del_adapter(&i2c->adap);
iounmap(i2c->regs);
kfree(i2c->ioarea);
release_resource(i2c->ioarea);
clk_put(i2c->clk);
kfree(i2c);
return 0;
}
#ifdef CONFIG_PM
static int rk30_i2c_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct rk30_i2c *i2c = platform_get_drvdata(pdev);
i2c->suspended = 1;
return 0;
}
static int rk30_i2c_resume_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct rk30_i2c *i2c = platform_get_drvdata(pdev);
i2c->suspended = 0;
i2c->i2c_init_hw(i2c);
return 0;
}
static const struct dev_pm_ops rk30_i2c_dev_pm_ops = {
.suspend_noirq = rk30_i2c_suspend_noirq,
.resume_noirq = rk30_i2c_resume_noirq,
};
#define rk30_DEV_PM_OPS (&rk30_i2c_dev_pm_ops)
#else
#define rk30_DEV_PM_OPS NULL
#endif
MODULE_DEVICE_TABLE(platform, rk30_driver_ids);
static struct platform_driver rk30_i2c_driver = {
.probe = rk30_i2c_probe,
.remove = rk30_i2c_remove,
.driver = {
.owner = THIS_MODULE,
.name = "rk30_i2c",
.pm = rk30_DEV_PM_OPS,
},
};
static int __init i2c_adap_init(void)
{
return platform_driver_register(&rk30_i2c_driver);
}
subsys_initcall(i2c_adap_init);
static void __exit i2c_adap_exit(void)
{
platform_driver_unregister(&rk30_i2c_driver);
}
module_exit(i2c_adap_exit);
MODULE_DESCRIPTION("Driver for RK30 I2C Bus");
MODULE_AUTHOR("kfx, kfx@rock-chips.com");
MODULE_LICENSE("GPL");

79
drivers/i2c/busses/i2c-rk30.h Executable file
View File

@@ -0,0 +1,79 @@
#ifndef __RK30_I2C_H__
#define __RK30_I2C_H__
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/wakelock.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <mach/board.h>
#include <asm/irq.h>
#if 0
#define i2c_dbg(dev, format, arg...) \
dev_printk(KERN_INFO , dev , format , ## arg)
#else
#define i2c_dbg(dev, format, arg...)
#endif
enum rk30_i2c_state {
STATE_IDLE,
STATE_START,
STATE_READ,
STATE_WRITE,
STATE_STOP
};
struct rk30_i2c {
spinlock_t lock;
wait_queue_head_t wait;
unsigned int suspended:1;
struct i2c_msg *msg;
unsigned int msg_num;
unsigned int msg_idx;
unsigned int msg_ptr;
unsigned int tx_setup;
unsigned int irq;
enum rk30_i2c_state state;
unsigned long clkrate;
void __iomem *regs;
struct clk *clk;
struct device *dev;
struct resource *ioarea;
struct i2c_adapter adap;
unsigned long scl_rate;
unsigned long i2c_rate;
unsigned int addr;
struct wake_lock idlelock[5];
int is_div_from_arm[5];
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
void (*i2c_init_hw)(struct rk30_i2c *);
void (*i2c_set_clk)(struct rk30_i2c *, unsigned long);
irqreturn_t (*i2c_irq)(int, void *);
};
int i2c_add_rk29_adapter(struct i2c_adapter *);
int i2c_add_rk30_adapter(struct i2c_adapter *);
#endif