Add I2C support for Allwinner SoCs
Subsystems (4)
v5 cover Add I2C support for Allwinner SoCs
Hi,
This patchset adds support for the I2C controller found on most of the Allwinner SoCs, especially the already supported A10 and A13, and the yet to come A31.
This driver leverages the Marvel mv64xxx i2c controller driver, that has an almost identical logic, with a slightly different register layout.
It has been tested on a A13-Olinuxino and an A10s-Olinuxino.
Thanks,
Maxime
Changes from v4:
* Don't expose the reg offset structure through the platform data
* Move the register offset structures to the driver and declare them static
* Default at marvell's register layout when using platform data, and switch between the Allwinner and the Marvell ones only when using DT.
* Remove the pull-ups in the device tree muxings
Changes from v3:
* Merged the driver in the Marvell mv64xxx i2c controller
Changes from v2:
* Slightly modified the switch comments again
* Removed the of_* calls in favor of platform_get_* functions
Changes from v1:
* Added comments to the switch statements to clarify when the fall through to the next case is made on purpose
* Use devm_ioremap_resource instead of of_iomap
* Moved the reset after enabling the clocks
* Added Emilio Lopez' patch to add the available i2c controllers to the cubieboard
Emilio L?pez (1):
ARM: sun4i: cubieboard: Enable the i2c controllers
Maxime Ripard (7):
i2c: mv64xxx: Add macros to access parts of registers
i2c: mv64xxx: make the registers offset configurable
i2c: mv64xxx: Add Allwinner sun4i compatible
ARM: sunxi: dt: Add i2c controller nodes to the DTSI
ARM: sun4i: dt: Add i2c muxing options
ARM: sun5i: dt: Add i2c muxing options
ARM: sun5i: olinuxino: Enable the i2c controllers
arch/arm/boot/dts/sun4i-a10-cubieboard.dts | 12 +++ arch/arm/boot/dts/sun4i-a10.dtsi | 48 ++++++++++++ arch/arm/boot/dts/sun5i-a13-olinuxino.dts | 18 +++++ arch/arm/boot/dts/sun5i-a13.dtsi | 48 ++++++++++++ drivers/i2c/busses/Kconfig | 3 +- drivers/i2c/busses/i2c-mv64xxx.c | 118 +++++++++++++++++++---------- 6 files changed, 206 insertions(+), 41 deletions(-)
Signature · 1 line
1.8.3
PATCH v5 1/8 i2c: mv64xxx: Add macros to access parts of registers
These macros make it more comprehensive to access to useful masked and shifted area of the various registers used.
--- drivers/i2c/busses/i2c-mv64xxx.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/i2c/busses/i2c-mv64xxx.c +6 -2
@@ -33,6 +33,10 @@#define MV64XXX_I2C_REG_EXT_SLAVE_ADDR 0x10#define MV64XXX_I2C_REG_SOFT_RESET 0x1c +#define MV64XXX_I2C_ADDR_ADDR (val) ((val & 0x7f) << 1) +#define MV64XXX_I2C_BAUD_DIV_N (val) (val & 0x7) +#define MV64XXX_I2C_BAUD_DIV_M (val) ((val & 0xf) << 3) +#define MV64XXX_I2C_REG_CONTROL_ACK 0x00000004#define MV64XXX_I2C_REG_CONTROL_IFLG 0x00000008#define MV64XXX_I2C_REG_CONTROL_STOP 0x00000010
@@ -133,7 +137,7 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data, drv_data->addr1 =0xf0 | (((u32 )msg->addr &0x300 ) >>7 ) | dir; drv_data->addr2 = (u32 )msg->addr &0xff ; }else { - drv_data->addr1 = ((u32 )msg->addr &0x7f ) <<1 | dir; + drv_data->addr1 =MV64XXX_I2C_ADDR_ADDR ((u32 )msg->addr) | dir; drv_data->addr2 =0 ; } }
@@ -151,7 +155,7 @@ static void mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data) {writel (0 , drv_data->reg_base +MV64XXX_I2C_REG_SOFT_RESET ); -writel ((((drv_data->freq_m &0xf ) <<3 ) | (drv_data->freq_n &0x7 )), +writel (MV64XXX_I2C_BAUD_DIV_M (drv_data->freq_m) |MV64XXX_I2C_BAUD_DIV_N (drv_data->freq_n), drv_data->reg_base +MV64XXX_I2C_REG_BAUD );writel (0 , drv_data->reg_base +MV64XXX_I2C_REG_SLAVE_ADDR );writel (0 , drv_data->reg_base +MV64XXX_I2C_REG_EXT_SLAVE_ADDR );
Signature · 1 line
1.8.3
PATCH v5 2/8 i2c: mv64xxx: make the registers offset configurable
In reply to Maxime RipardThe Allwinner i2c controller uses the same logic as the Marvell one, but with slightly different register offsets.
Introduce a structure that will be passed by either the pdata or associated to the compatible strings, and that holds the various registers that might be needed.
--- drivers/i2c/busses/i2c-mv64xxx.c | 101 ++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 39 deletions(-)
drivers/i2c/busses/i2c-mv64xxx.c +62 -39
@@ -19,20 +19,12 @@#include <linux/platform_device.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/of_i2c.h> #include <linux/clk.h> #include <linux/err.h> -/* Register defines */ -#define MV64XXX_I2C_REG_SLAVE_ADDR 0x00 -#define MV64XXX_I2C_REG_DATA 0x04 -#define MV64XXX_I2C_REG_CONTROL 0x08 -#define MV64XXX_I2C_REG_STATUS 0x0c -#define MV64XXX_I2C_REG_BAUD 0x0c -#define MV64XXX_I2C_REG_EXT_SLAVE_ADDR 0x10 -#define MV64XXX_I2C_REG_SOFT_RESET 0x1c -#define MV64XXX_I2C_ADDR_ADDR (val) ((val & 0x7f) << 1)#define MV64XXX_I2C_BAUD_DIV_N (val) (val & 0x7)#define MV64XXX_I2C_BAUD_DIV_M (val) ((val & 0xf) << 3)
@@ -89,6 +81,16 @@ enum { MV64XXX_I2C_ACTION_SEND_STOP, }; +struct mv64xxx_i2c_regs { +u8 addr; +u8 ext_addr; +u8 data; +u8 control; +u8 status; +u8 clock; +u8 soft_reset; +}; +struct mv64xxx_i2c_data {struct i2c_msg *msgs;int num_msgs;
@@ -98,6 +100,7 @@ struct mv64xxx_i2c_data {u32 aborting;u32 cntl_bits;void __iomem *reg_base; +struct mv64xxx_i2c_regs reg_offsets;u32 addr1;u32 addr2;u32 bytes_left;
@@ -116,6 +119,16 @@ struct mv64xxx_i2c_data {struct i2c_adapter adapter; }; +static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = { + .addr =0x00 , + .ext_addr =0x10 , + .data =0x04 , + .control =0x08 , + .status =0x0c , + .clock =0x0c , + .soft_reset =0x1c , +}; +static void mv64xxx_i2c_prepare_for_io (struct mv64xxx_i2c_data *drv_data,struct i2c_msg *msg)
@@ -154,13 +167,13 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,static void mv64xxx_i2c_hw_init (struct mv64xxx_i2c_data *drv_data) { -writel (0 , drv_data->reg_base +MV64XXX_I2C_REG_SOFT_RESET ); +writel (0 , drv_data->reg_base + drv_data->reg_offsets.soft_reset);writel (MV64XXX_I2C_BAUD_DIV_M (drv_data->freq_m) |MV64XXX_I2C_BAUD_DIV_N (drv_data->freq_n), - drv_data->reg_base +MV64XXX_I2C_REG_BAUD ); -writel (0 , drv_data->reg_base +MV64XXX_I2C_REG_SLAVE_ADDR ); -writel (0 , drv_data->reg_base +MV64XXX_I2C_REG_EXT_SLAVE_ADDR ); + drv_data->reg_base + drv_data->reg_offsets.clock); +writel (0 , drv_data->reg_base + drv_data->reg_offsets.addr); +writel (0 , drv_data->reg_base + drv_data->reg_offsets.ext_addr);writel (MV64XXX_I2C_REG_CONTROL_TWSIEN |MV64XXX_I2C_REG_CONTROL_STOP , - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control); drv_data->state =MV64XXX_I2C_STATE_IDLE ; }
@@ -282,7 +295,7 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) drv_data->cntl_bits |=MV64XXX_I2C_REG_CONTROL_START ;writel (drv_data->cntl_bits, - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control); drv_data->msgs++; drv_data->num_msgs--;
@@ -300,48 +313,48 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)case MV64XXX_I2C_ACTION_CONTINUE :writel (drv_data->cntl_bits, - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control);break ;case MV64XXX_I2C_ACTION_SEND_START :writel (drv_data->cntl_bits |MV64XXX_I2C_REG_CONTROL_START , - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control);break ;case MV64XXX_I2C_ACTION_SEND_ADDR_1 :writel (drv_data->addr1, - drv_data->reg_base +MV64XXX_I2C_REG_DATA ); + drv_data->reg_base + drv_data->reg_offsets.data);writel (drv_data->cntl_bits, - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control);break ;case MV64XXX_I2C_ACTION_SEND_ADDR_2 :writel (drv_data->addr2, - drv_data->reg_base +MV64XXX_I2C_REG_DATA ); + drv_data->reg_base + drv_data->reg_offsets.data);writel (drv_data->cntl_bits, - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control);break ;case MV64XXX_I2C_ACTION_SEND_DATA :writel (drv_data->msg->buf[drv_data->byte_posn++], - drv_data->reg_base +MV64XXX_I2C_REG_DATA ); + drv_data->reg_base + drv_data->reg_offsets.data);writel (drv_data->cntl_bits, - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control);break ;case MV64XXX_I2C_ACTION_RCV_DATA : drv_data->msg->buf[drv_data->byte_posn++] = -readl (drv_data->reg_base +MV64XXX_I2C_REG_DATA ); +readl (drv_data->reg_base + drv_data->reg_offsets.data);writel (drv_data->cntl_bits, - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control);break ;case MV64XXX_I2C_ACTION_RCV_DATA_STOP : drv_data->msg->buf[drv_data->byte_posn++] = -readl (drv_data->reg_base +MV64XXX_I2C_REG_DATA ); +readl (drv_data->reg_base + drv_data->reg_offsets.data); drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN ;writel (drv_data->cntl_bits |MV64XXX_I2C_REG_CONTROL_STOP , - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control); drv_data->block =0 ;wake_up (&drv_data->waitq);break ;
@@ -356,7 +369,7 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)case MV64XXX_I2C_ACTION_SEND_STOP : drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN ;writel (drv_data->cntl_bits |MV64XXX_I2C_REG_CONTROL_STOP , - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control); drv_data->block =0 ;wake_up (&drv_data->waitq);break ;
@@ -372,9 +385,9 @@ mv64xxx_i2c_intr(int irq, void *dev_id)irqreturn_t rc =IRQ_NONE ;spin_lock_irqsave (&drv_data->lock, flags); -while (readl (drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ) & +while (readl (drv_data->reg_base + drv_data->reg_offsets.control) &MV64XXX_I2C_REG_CONTROL_IFLG ) { - status =readl (drv_data->reg_base +MV64XXX_I2C_REG_STATUS ); + status =readl (drv_data->reg_base + drv_data->reg_offsets.status);mv64xxx_i2c_fsm (drv_data, status);mv64xxx_i2c_do_action (drv_data); rc =IRQ_HANDLED ;
@@ -495,6 +508,12 @@ static const struct i2c_algorithm mv64xxx_i2c_algo = {* ***************************************************************************** */ +static const struct of_device_id mv64xxx_i2c_of_match_table[] = { + { .compatible ="marvell,mv64xxx-i2c" , .data = &mv64xxx_i2c_regs_mv64xxx}, + {} +}; +MODULE_DEVICE_TABLE (of, mv64xxx_i2c_of_match_table); +#ifdef CONFIG_OF static int mv64xxx_calc_freq (const int tclk,const int n,const int m)
@@ -528,8 +547,10 @@ mv64xxx_find_baud_factors(const u32 req_freq, const u32 tclk, u32 *best_n,static int mv64xxx_of_config (struct mv64xxx_i2c_data *drv_data, -struct device_node *np) +struct device *dev) { +const struct of_device_id *device; +struct device_node *np = dev->of_node;u32 bus_freq, tclk;int rc =0 ;
@@ -558,6 +579,13 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,* So hard code the value to 1 second. */ drv_data->adapter.timeout =HZ ; + + device =of_match_device (mv64xxx_i2c_of_match_table, dev); +if (!device) +return -ENODEV ; + +memcpy (&drv_data->reg_offsets, device->data,sizeof (drv_data->reg_offsets)); + out:return rc;#endif
@@ -565,7 +593,7 @@ out:#else /* CONFIG_OF */ static int mv64xxx_of_config (struct mv64xxx_i2c_data *drv_data, -struct device_node *np) +struct device *dev) {return -ENODEV ; }
@@ -611,8 +639,9 @@ mv64xxx_i2c_probe(struct platform_device *pd) drv_data->freq_n = pdata->freq_n; drv_data->irq =platform_get_irq (pd,0 ); drv_data->adapter.timeout =msecs_to_jiffies (pdata->timeout); +memcpy (&drv_data->reg_offsets, &mv64xxx_i2c_regs_mv64xxx,sizeof (drv_data->reg_offsets)); }else if (pd->dev.of_node) { - rc =mv64xxx_of_config (drv_data, pd->dev.of_node); + rc =mv64xxx_of_config (drv_data, &pd->dev);if (rc) goto exit_clk; }
@@ -680,12 +709,6 @@ mv64xxx_i2c_remove(struct platform_device *dev)return 0 ; } -static const struct of_device_id mv64xxx_i2c_of_match_table[] = { - { .compatible ="marvell,mv64xxx-i2c" , }, - {} -}; -MODULE_DEVICE_TABLE (of, mv64xxx_i2c_of_match_table); -static struct platform_driver mv64xxx_i2c_driver = { .probe = mv64xxx_i2c_probe, .remove = mv64xxx_i2c_remove,
Signature · 1 line
1.8.3
PATCH v5 2/8 i2c: mv64xxx: make the registers offset configurable
Quoted 8 lines · Maxime Ripard
The Allwinner i2c controller uses the same logic as the Marvell one, but with slightly different register offsets.
Introduce a structure that will be passed by either the pdata or associated to the compatible strings, and that holds the various registers that might be needed.
Signed-off-by Maxime Ripard <[email protected]>
Hi Maxime
I tested on a Kirkwood QNAP. It has an RTC on the i2c bus. It still gives the correct time with your patches applied.
Andrew
Quoted 255 lines
---
drivers/i2c/busses/i2c-mv64xxx.c| 101 ++++++++++++++++++++++++---------------1 file changed, 62 insertions(+), 39 deletions(-)drivers/i2c/busses/i2c-mv64xxx.c +62
-39@@ -19,20 +19,12 @@#include <linux/platform_device.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/of_i2c.h> #include <linux/clk.h> #include <linux/err.h> -/* Register defines */ -#define MV64XXX_I2C_REG_SLAVE_ADDR 0x00 -#define MV64XXX_I2C_REG_DATA 0x04 -#define MV64XXX_I2C_REG_CONTROL 0x08 -#define MV64XXX_I2C_REG_STATUS 0x0c -#define MV64XXX_I2C_REG_BAUD 0x0c -#define MV64XXX_I2C_REG_EXT_SLAVE_ADDR 0x10 -#define MV64XXX_I2C_REG_SOFT_RESET 0x1c -#define MV64XXX_I2C_ADDR_ADDR (val) ((val & 0x7f) << 1)#define MV64XXX_I2C_BAUD_DIV_N (val) (val & 0x7)#define MV64XXX_I2C_BAUD_DIV_M (val) ((val & 0xf) << 3)@@ -89,6 +81,16 @@ enum { MV64XXX_I2C_ACTION_SEND_STOP, }; +struct mv64xxx_i2c_regs { +u8 addr; +u8 ext_addr; +u8 data; +u8 control; +u8 status; +u8 clock; +u8 soft_reset; +}; +struct mv64xxx_i2c_data {struct i2c_msg *msgs;int num_msgs;@@ -98,6 +100,7 @@ struct mv64xxx_i2c_data {u32 aborting;u32 cntl_bits;void __iomem *reg_base; +struct mv64xxx_i2c_regs reg_offsets;u32 addr1;u32 addr2;u32 bytes_left;@@ -116,6 +119,16 @@ struct mv64xxx_i2c_data {struct i2c_adapter adapter; }; +static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = { + .addr =0x00 , + .ext_addr =0x10 , + .data =0x04 , + .control =0x08 , + .status =0x0c , + .clock =0x0c , + .soft_reset =0x1c , +}; +static void mv64xxx_i2c_prepare_for_io (struct mv64xxx_i2c_data *drv_data,struct i2c_msg *msg)@@ -154,13 +167,13 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,static void mv64xxx_i2c_hw_init (struct mv64xxx_i2c_data *drv_data) { -writel (0 , drv_data->reg_base +MV64XXX_I2C_REG_SOFT_RESET ); +writel (0 , drv_data->reg_base + drv_data->reg_offsets.soft_reset);writel (MV64XXX_I2C_BAUD_DIV_M (drv_data->freq_m) |MV64XXX_I2C_BAUD_DIV_N (drv_data->freq_n), - drv_data->reg_base +MV64XXX_I2C_REG_BAUD ); -writel (0 , drv_data->reg_base +MV64XXX_I2C_REG_SLAVE_ADDR ); -writel (0 , drv_data->reg_base +MV64XXX_I2C_REG_EXT_SLAVE_ADDR ); + drv_data->reg_base + drv_data->reg_offsets.clock); +writel (0 , drv_data->reg_base + drv_data->reg_offsets.addr); +writel (0 , drv_data->reg_base + drv_data->reg_offsets.ext_addr);writel (MV64XXX_I2C_REG_CONTROL_TWSIEN |MV64XXX_I2C_REG_CONTROL_STOP , - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control); drv_data->state =MV64XXX_I2C_STATE_IDLE ; }@@ -282,7 +295,7 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) drv_data->cntl_bits |=MV64XXX_I2C_REG_CONTROL_START ;writel (drv_data->cntl_bits, - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control); drv_data->msgs++; drv_data->num_msgs--;@@ -300,48 +313,48 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)case MV64XXX_I2C_ACTION_CONTINUE :writel (drv_data->cntl_bits, - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control);break ;case MV64XXX_I2C_ACTION_SEND_START :writel (drv_data->cntl_bits |MV64XXX_I2C_REG_CONTROL_START , - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control);break ;case MV64XXX_I2C_ACTION_SEND_ADDR_1 :writel (drv_data->addr1, - drv_data->reg_base +MV64XXX_I2C_REG_DATA ); + drv_data->reg_base + drv_data->reg_offsets.data);writel (drv_data->cntl_bits, - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control);break ;case MV64XXX_I2C_ACTION_SEND_ADDR_2 :writel (drv_data->addr2, - drv_data->reg_base +MV64XXX_I2C_REG_DATA ); + drv_data->reg_base + drv_data->reg_offsets.data);writel (drv_data->cntl_bits, - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control);break ;case MV64XXX_I2C_ACTION_SEND_DATA :writel (drv_data->msg->buf[drv_data->byte_posn++], - drv_data->reg_base +MV64XXX_I2C_REG_DATA ); + drv_data->reg_base + drv_data->reg_offsets.data);writel (drv_data->cntl_bits, - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control);break ;case MV64XXX_I2C_ACTION_RCV_DATA : drv_data->msg->buf[drv_data->byte_posn++] = -readl (drv_data->reg_base +MV64XXX_I2C_REG_DATA ); +readl (drv_data->reg_base + drv_data->reg_offsets.data);writel (drv_data->cntl_bits, - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control);break ;case MV64XXX_I2C_ACTION_RCV_DATA_STOP : drv_data->msg->buf[drv_data->byte_posn++] = -readl (drv_data->reg_base +MV64XXX_I2C_REG_DATA ); +readl (drv_data->reg_base + drv_data->reg_offsets.data); drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN ;writel (drv_data->cntl_bits |MV64XXX_I2C_REG_CONTROL_STOP , - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control); drv_data->block =0 ;wake_up (&drv_data->waitq);break ;@@ -356,7 +369,7 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)case MV64XXX_I2C_ACTION_SEND_STOP : drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN ;writel (drv_data->cntl_bits |MV64XXX_I2C_REG_CONTROL_STOP , - drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ); + drv_data->reg_base + drv_data->reg_offsets.control); drv_data->block =0 ;wake_up (&drv_data->waitq);break ;@@ -372,9 +385,9 @@ mv64xxx_i2c_intr(int irq, void *dev_id)irqreturn_t rc =IRQ_NONE ;spin_lock_irqsave (&drv_data->lock, flags); -while (readl (drv_data->reg_base +MV64XXX_I2C_REG_CONTROL ) & +while (readl (drv_data->reg_base + drv_data->reg_offsets.control) &MV64XXX_I2C_REG_CONTROL_IFLG ) { - status =readl (drv_data->reg_base +MV64XXX_I2C_REG_STATUS ); + status =readl (drv_data->reg_base + drv_data->reg_offsets.status);mv64xxx_i2c_fsm (drv_data, status);mv64xxx_i2c_do_action (drv_data); rc =IRQ_HANDLED ;@@ -495,6 +508,12 @@ static const struct i2c_algorithm mv64xxx_i2c_algo = {* ***************************************************************************** */ +static const struct of_device_id mv64xxx_i2c_of_match_table[] = { + { .compatible ="marvell,mv64xxx-i2c" , .data = &mv64xxx_i2c_regs_mv64xxx}, + {} +}; +MODULE_DEVICE_TABLE (of, mv64xxx_i2c_of_match_table); +#ifdef CONFIG_OF static int mv64xxx_calc_freq (const int tclk,const int n,const int m)@@ -528,8 +547,10 @@ mv64xxx_find_baud_factors(const u32 req_freq, const u32 tclk, u32 *best_n,static int mv64xxx_of_config (struct mv64xxx_i2c_data *drv_data, -struct device_node *np) +struct device *dev) { +const struct of_device_id *device; +struct device_node *np = dev->of_node;u32 bus_freq, tclk;int rc =0 ;@@ -558,6 +579,13 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,* So hard code the value to 1 second. */ drv_data->adapter.timeout =HZ ; + + device =of_match_device (mv64xxx_i2c_of_match_table, dev); +if (!device) +return -ENODEV ; + +memcpy (&drv_data->reg_offsets, device->data,sizeof (drv_data->reg_offsets)); + out:return rc;#endif @@ -565,7 +593,7 @@ out:#else /* CONFIG_OF */ static int mv64xxx_of_config (struct mv64xxx_i2c_data *drv_data, -struct device_node *np) +struct device *dev) {return -ENODEV ; }@@ -611,8 +639,9 @@ mv64xxx_i2c_probe(struct platform_device *pd) drv_data->freq_n = pdata->freq_n; drv_data->irq =platform_get_irq (pd,0 ); drv_data->adapter.timeout =msecs_to_jiffies (pdata->timeout); +memcpy (&drv_data->reg_offsets, &mv64xxx_i2c_regs_mv64xxx,sizeof (drv_data->reg_offsets)); }else if (pd->dev.of_node) { - rc =mv64xxx_of_config (drv_data, pd->dev.of_node); + rc =mv64xxx_of_config (drv_data, &pd->dev);if (rc) goto exit_clk; }@@ -680,12 +709,6 @@ mv64xxx_i2c_remove(struct platform_device *dev)return 0 ; } -static const struct of_device_id mv64xxx_i2c_of_match_table[] = { - { .compatible ="marvell,mv64xxx-i2c" , }, - {} -}; -MODULE_DEVICE_TABLE (of, mv64xxx_i2c_of_match_table); -static struct platform_driver mv64xxx_i2c_driver = { .probe = mv64xxx_i2c_probe, .remove = mv64xxx_i2c_remove,
PATCH v5 3/8 i2c: mv64xxx: Add Allwinner sun4i compatible
In reply to Maxime RipardAdd the compatible string for the Allwinner A10 i2c controller and the associated register layout.
--- drivers/i2c/busses/Kconfig | 3 ++- drivers/i2c/busses/i2c-mv64xxx.c | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-)
drivers/i2c/busses/Kconfig +2 -1
@@ -507,10 +507,11 @@ config I2C_MPC config I2C_MV64XXX tristate "Marvell mv64xxx I2C Controller" - depends on (MV64X60 || PLAT_ORION) + depends on (MV64X60 || PLAT_ORION || ARCH_SUNXI) help If you say yes to this option, support will be included for the built-in I2C interface on the Marvell 64xxx line of host bridges. + This driver is also used for Allwinner SoCs I2C controllers. This driver can also be built as a module. If so, the module will be called i2c-mv64xxx.
drivers/i2c/busses/i2c-mv64xxx.c +11 -0
@@ -129,6 +129,16 @@ static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = { .soft_reset = 0x1c, }; +static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_sun4i = { + .addr =0x00 , + .ext_addr =0x04 , + .data =0x08 , + .control =0x0c , + .status =0x10 , + .clock =0x14 , + .soft_reset =0x18 , +}; +static void mv64xxx_i2c_prepare_for_io (struct mv64xxx_i2c_data *drv_data,struct i2c_msg *msg)
@@ -509,6 +519,7 @@ static const struct i2c_algorithm mv64xxx_i2c_algo = {***************************************************************************** */ static const struct of_device_id mv64xxx_i2c_of_match_table[] = { + { .compatible ="allwinner,sun4i-i2c" , .data = &mv64xxx_i2c_regs_sun4i}, { .compatible ="marvell,mv64xxx-i2c" , .data = &mv64xxx_i2c_regs_mv64xxx}, {} };
Signature · 1 line
1.8.3
PATCH v5 4/8 ARM: sunxi: dt: Add i2c controller nodes to the DTSI
In reply to Maxime RipardThe Allwinner A10 and A13 both have 3 i2c controller embedded. Add those to the common sunxi dtsi.
--- arch/arm/boot/dts/sun4i-a10.dtsi | 27 +++++++++++++++++++++++++++ arch/arm/boot/dts/sun5i-a13.dtsi | 27 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)
arch/arm/boot/dts/sun4i-a10.dtsi +27 -0
@@ -321,5 +321,32 @@ clocks = <&apb1_gates 23>; status = "disabled"; }; + + i2c0: i2c at 01c2ac00 { + compatible = "allwinner,sun4i-i2c"; + reg = <0x01c2ac00 0x400>; + interrupts = <7>; + clocks = <&apb1_gates 0>; + clock-frequency = <100000>; + status = "disabled"; + }; + + i2c1: i2c at 01c2b000 { + compatible = "allwinner,sun4i-i2c"; + reg = <0x01c2b000 0x400>; + interrupts = <8>; + clocks = <&apb1_gates 1>; + clock-frequency = <100000>; + status = "disabled"; + }; + + i2c2: i2c at 01c2b400 { + compatible = "allwinner,sun4i-i2c"; + reg = <0x01c2b400 0x400>; + interrupts = <9>; + clocks = <&apb1_gates 2>; + clock-frequency = <100000>; + status = "disabled"; + }; }; };
arch/arm/boot/dts/sun5i-a13.dtsi +27 -0
@@ -217,5 +217,32 @@ clocks = <&apb1_gates 19>; status = "disabled"; }; + + i2c0: i2c at 01c2ac00 { + compatible = "allwinner,sun4i-i2c"; + reg = <0x01c2ac00 0x400>; + interrupts = <7>; + clocks = <&apb1_gates 0>; + clock-frequency = <100000>; + status = "disabled"; + }; + + i2c1: i2c at 01c2b000 { + compatible = "allwinner,sun4i-i2c"; + reg = <0x01c2b000 0x400>; + interrupts = <8>; + clocks = <&apb1_gates 1>; + clock-frequency = <100000>; + status = "disabled"; + }; + + i2c2: i2c at 01c2b400 { + compatible = "allwinner,sun4i-i2c"; + reg = <0x01c2b400 0x400>; + interrupts = <9>; + clocks = <&apb1_gates 2>; + clock-frequency = <100000>; + status = "disabled"; + }; }; };
Signature · 1 line
1.8.3
PATCH v5 5/8 ARM: sun4i: dt: Add i2c muxing options
In reply to Maxime RipardThe i2c controller found on the Allwinner A10 has only one muxing option available for each controller. Add them to the dtsi
--- arch/arm/boot/dts/sun4i-a10.dtsi | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
arch/arm/boot/dts/sun4i-a10.dtsi +21 -0
@@ -228,6 +228,27 @@ allwinner,drive = <0>; allwinner,pull = <0>; }; + + i2c0_pins_a: i2c0 at 0 { + allwinner,pins = "PB0", "PB1"; + allwinner,function = "i2c0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + i2c1_pins_a: i2c1 at 0 { + allwinner,pins = "PB18", "PB19"; + allwinner,function = "i2c1"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + i2c2_pins_a: i2c2 at 0 { + allwinner,pins = "PB20", "PB21"; + allwinner,function = "i2c2"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; }; timer at 01c20c00 {
Signature · 1 line
1.8.3
PATCH v5 6/8 ARM: sun5i: dt: Add i2c muxing options
In reply to Maxime RipardThe i2c controller found on the Allwinner A13 has only one muxing option available for each controller. Add them to the dtsi
--- arch/arm/boot/dts/sun5i-a13.dtsi | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
arch/arm/boot/dts/sun5i-a13.dtsi +21 -0
@@ -184,6 +184,27 @@ allwinner,drive = <0>; allwinner,pull = <0>; }; + + i2c0_pins_a: i2c0 at 0 { + allwinner,pins = "PB0", "PB1"; + allwinner,function = "i2c0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + i2c1_pins_a: i2c1 at 0 { + allwinner,pins = "PB15", "PB16"; + allwinner,function = "i2c1"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + i2c2_pins_a: i2c2 at 0 { + allwinner,pins = "PB17", "PB18"; + allwinner,function = "i2c2"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; }; timer at 01c20c00 {
Signature · 1 line
1.8.3
PATCH v5 7/8 ARM: sun5i: olinuxino: Enable the i2c controllers
In reply to Maxime RipardThe A13-Olinuxino makes use of the 3 i2c controllers found on the Allwinner A13. Enable them in the device tree.
--- arch/arm/boot/dts/sun5i-a13-olinuxino.dts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
arch/arm/boot/dts/sun5i-a13-olinuxino.dts +18 -0
@@ -37,6 +37,24 @@ pinctrl-0 = <&uart1_pins_b>; status = "okay"; }; + + i2c0: i2c at 01c2ac00 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins_a>; + status = "okay"; + }; + + i2c1: i2c at 01c2b000 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins_a>; + status = "okay"; + }; + + i2c2: i2c at 01c2b400 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins_a>; + status = "okay"; + }; }; leds {
Signature · 1 line
1.8.3
PATCH v5 8/8 ARM: sun4i: cubieboard: Enable the i2c controllers
In reply to Maxime RipardFrom: Emilio L?pez <[email protected]>
The Cubieboard makes use of the first two i2c controllers found on the Allwinner A10; i2c-0 is used internally for the PMIC, while i2c-1 is exposed on the board headers. This patch enables them in the device tree.
--- arch/arm/boot/dts/sun4i-a10-cubieboard.dts | 12 ++++++++++++ 1 file changed, 12 insertions(+)
arch/arm/boot/dts/sun4i-a10-cubieboard.dts +12 -0
@@ -56,6 +56,18 @@ pinctrl-0 = <&uart0_pins_a>; status = "okay"; }; + + i2c0: i2c at 01c2ac00 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins_a>; + status = "okay"; + }; + + i2c1: i2c at 01c2b000 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins_a>; + status = "okay"; + }; }; leds {
Signature · 1 line
1.8.3
v5 cover Add I2C support for Allwinner SoCs
In reply to Maxime RipardQuoted 8 lines · Maxime Ripard
Hi,
This patchset adds support for the I2C controller found on most of the Allwinner SoCs, especially the already supported A10 and A13, and the yet to come A31.
This driver leverages the Marvel mv64xxx i2c controller driver, that has an almost identical logic, with a slightly different register layout.
\o/ Yay, that's more like it. Thanks for the rework!
Quoted 1 line
It has been tested on a A13-Olinuxino and an A10s-Olinuxino.
I'd love to get some Tested-by or other tags for Marvell hardware. Any volunteers?
Quoted 2 lines
Changes from v4:
* Don't expose the reg offset structure through the platform data
Commit message 2/9 still speaks about pdata, but I will fix this locally if all goes well with this series.
I will wait a bit more for tags and then pick up patches 1-3. I assume the rest goes via arm-soc, let me know if I should take them.
Regards,
Wolfram
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130614/94e58209/attachment-0001.sig>
v5 cover Add I2C support for Allwinner SoCs
Quoted 8 lines · Wolfram Sang
Quoted 6 lines · Maxime Ripard
This patchset adds support for the I2C controller found on most of the Allwinner SoCs, especially the already supported A10 and A13, and the yet to come A31.
This driver leverages the Marvel mv64xxx i2c controller driver, that has an almost identical logic, with a slightly different register layout.
Quoted 6 lines
\o/ Yay, that's more like it. Thanks for the rework!
Quoted 1 line
It has been tested on a A13-Olinuxino and an A10s-Olinuxino.
I'd love to get some Tested-by or other tags for Marvell hardware. Any volunteers?
on Marvell Dove, v3.10-rc5 with i2c/for-next merged in.
Sebastian
v5 cover Add I2C support for Allwinner SoCs
In reply to Wolfram SangHi Wolfram,
Quoted 10 lines · Wolfram Sang
Quoted 8 lines · Maxime Ripard
Hi,
This patchset adds support for the I2C controller found on most of the Allwinner SoCs, especially the already supported A10 and A13, and the yet to come A31.
This driver leverages the Marvel mv64xxx i2c controller driver, that has an almost identical logic, with a slightly different register layout.
Quoted 6 lines
\o/ Yay, that's more like it. Thanks for the rework!
Quoted 1 line
It has been tested on a A13-Olinuxino and an A10s-Olinuxino.
I'd love to get some Tested-by or other tags for Marvell hardware. Any volunteers?
Yes, I'd be more comfortable with Tested-by from Marvell users. I'll probably be able to get my hands on a Marvell board on monday, if no one tested it yet.
Quoted 8 lines
Quoted 2 lines
Changes from v4:
* Don't expose the reg offset structure through the platform dataCommit message 2/9 still speaks about pdata, but I will fix this locally if all goes well with this series.
I will wait a bit more for tags and then pick up patches 1-3. I assume the rest goes via arm-soc, let me know if I should take them.
Yes, I'll take it in my tree and push it to arm-soc afterwards.
Thanks!
Maxime
Signature · 3 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com
Wolfram Sang
Appliedv5 cover Add I2C support for Allwinner SoCs
Quoted 15 lines · Maxime Ripard
Hi Wolfram,
Quoted 12 lines · Wolfram Sang
Quoted 8 lines · Maxime Ripard
Hi,
This patchset adds support for the I2C controller found on most of the Allwinner SoCs, especially the already supported A10 and A13, and the yet to come A31.
This driver leverages the Marvel mv64xxx i2c controller driver, that has an almost identical logic, with a slightly different register layout.
\o/ Yay, that's more like it. Thanks for the rework!
Quoted 8 lines
Quoted 4 lines
Quoted 1 line
It has been tested on a A13-Olinuxino and an A10s-Olinuxino.
I'd love to get some Tested-by or other tags for Marvell hardware. Any volunteers?
Yes, I'd be more comfortable with Tested-by from Marvell users. I'll probably be able to get my hands on a Marvell board on monday, if no one tested it yet.
Applied to for-next, thanks, especially for respinning the driver! Also thanks to the testers! Great community work here \o/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130615/9a5121d9/attachment.sig>
v5 cover Add I2C support for Allwinner SoCs
Hi Wolfram,
Quoted 2 lines · Wolfram Sang
Applied to for-next, thanks, especially for respinning the driver! Also thanks to the testers! Great community work here \o/
Thanks, I applied the other patches to my sunxi/dt-for-3.11 branch.
Thanks a lot to Sebastian and Andrew for their patches !
Maxime
Signature · 3 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com