Hi Henrik, On Sun, Nov 25, 2012 at 12:38:46PM +0100, Henrik Nordström wrote: > A basic basic driver for the I2C controller found in Allwinner > sunXi (A10 & A13) SoCs. > > Signed-off-by: Henrik Nordstrom <hen...@henriknordstrom.net> > Signed-off-by: Stefan Roese <s...@denx.de> > --- > arch/arm/cpu/armv7/sunxi/clock.c | 15 ++ > arch/arm/include/asm/arch-sunxi/i2c.h | 185 ++++++++++++++++++++++ > drivers/i2c/Makefile | 1 + > drivers/i2c/sunxi_i2c.c | 278 > +++++++++++++++++++++++++++++++++ > include/configs/sunxi-common.h | 8 + > 5 files changed, 487 insertions(+), 0 deletions(-) > create mode 100644 arch/arm/include/asm/arch-sunxi/i2c.h > create mode 100644 drivers/i2c/sunxi_i2c.c > > diff --git a/arch/arm/cpu/armv7/sunxi/clock.c > b/arch/arm/cpu/armv7/sunxi/clock.c > index 424acfc..b9bbb7d 100644 > --- a/arch/arm/cpu/armv7/sunxi/clock.c > +++ b/arch/arm/cpu/armv7/sunxi/clock.c > @@ -42,6 +42,7 @@ int clock_init(void) > return 0; > } > > +
No need to add extra newline here. > /* Return PLL5 frequency in Hz > * Note: Assumes PLL5 reference is 24MHz clock > */ > @@ -55,3 +56,17 @@ unsigned int clock_get_pll5(void) > int p = 1 << ((rval >> 16) & 3); > return 24000000 * n * k / p; > } > + > +int clock_twi_onoff(int port, int state) > +{ > + struct sunxi_ccm_reg *const ccm = > + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; > + > + if (port > 2) > + return -1; > + > + /* set the apb1 clock gate for twi */ > + sr32(&ccm->apb1_gate, 0 + port, 1, state); > + > + return 0; > +} > diff --git a/arch/arm/include/asm/arch-sunxi/i2c.h > b/arch/arm/include/asm/arch-sunxi/i2c.h > new file mode 100644 > index 0000000..9a6e168 > --- /dev/null > +++ b/arch/arm/include/asm/arch-sunxi/i2c.h > @@ -0,0 +1,185 @@ > +/* > + * (C) Copyright 2012 Henrik Nordstrom <hen...@henriknordstrom.net> > + * > + * Based on sun4i linux kernle i2c.h > + * (C) Copyright 2007-2012 > + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> > + * Tom Cubie <tangla...@allwinnertech.com> > + * Victor Wei <weizih...@allwinnertech.com> > + * > + * See file CREDITS for list of people who contributed to this > + * project. > + * > + * 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. > + * > + * 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. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, > + * MA 02111-1307 USA > + */ Add one newline here like in all other .h files. > +#ifndef _SUNXI_I2C_H_ > +#define _SUNXI_I2C_H_ > + > +struct i2c { > + u32 saddr; /* 31:8bit res,7-1bit for slave addr,0 bit for GCE */ > + u32 xsaddr; /* 31:8bit res,7-0bit for second addr in 10bit addr */ > + u32 data; /* 31:8bit res, 7-0bit send or receive data byte */ > + u32 ctl; /* INT_EN,BUS_EN,M_STA,INT_FLAG,A_ACK */ > + u32 status; /* 28 interrupt types + 0xF8 normal type = 29 */ > + u32 clkr; /* 31:7bit res,6-3bit,CLK_M,2-0bit CLK_N */ > + u32 reset; /* 31:1bit res;0bit,write 1 to clear 0. */ > + u32 efr; /* 31:2bit res,1:0 bit data byte follow read comand */ > + u32 lctl; /* 31:6bits res 5:0bit for sda&scl control */ > +}; > + > +/* TWI address register */ > +#define TWI_GCE_EN (0x1 << 0) /* gen call addr enable slave mode */ > +#define TWI_ADDR_MASK (0x7f << 1) /* 7:1bits */ > +#define TWI_XADDR_MASK 0xff /* 7:0bits for extend slave > address */ > + > +#define TWI_DATA_MASK 0xff /* 7:0bits for send or received > */ > + > +/* TWI Control Register Bit Fields */ > +/* 1:0 bits reserved */ > +/* set 1 to send A_ACK,then low level on SDA */ > +#define TWI_CTL_ACK (0x1 << 2) > +/* INT_FLAG,interrupt status flag: set '1' when interrupt coming */ > +#define TWI_CTL_INTFLG (0x1 << 3) > +#define TWI_CTL_STP (0x1 << 4) /* M_STP,Automatic clear 0 */ > +#define TWI_CTL_STA (0x1 << 5) /* M_STA,atutomatic clear 0 */ > +#define TWI_CTL_BUSEN (0x1 << 6) /* BUS_EN, mastr mode should be > set 1 */ > +#define TWI_CTL_INTEN (0x1 << 7) /* INT_EN */ > +/* 31:8 bit reserved */ > + > +/* > + * TWI Clock Register Bit Fields & Masks,default value:0x0000_0000 > + * 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_DIV_M (0xF << 3) /* 6:3bit */ > +#define TWI_CLK_DIV_N (0x7 << 0) /* 2:0bit */ > +#define TWI_CLK_DIV(N, M) ((((N) & 0xF) << 3) | (((M) & 0x7) << 0)) > + > +/* TWI Soft Reset Register Bit Fields & Masks */ > +/* write 1 to clear 0, when complete soft reset clear 0 */ > +#define TWI_SRST_SRST (0x1 << 0) > + > +/* TWI Enhance Feature Register Bit Fields & Masks */ > +/* default -- 0x0 */ > +/* 00:no,01: 1byte, 10:2 bytes, 11: 3bytes */ > +#define TWI_EFR_MASK (0x3 << 0) > +#define TWI_EFR_WARC_0 (0x0 << 0) > +#define TWI_EFR_WARC_1 (0x1 << 0) > +#define TWI_EFR_WARC_2 (0x2 << 0) > +#define TWI_EFR_WARC_3 (0x3 << 0) > + > +/* twi line control register -default value: 0x0000_003a */ > +/* SDA line state control enable ,1:enable;0:disable */ > +#define TWI_LCR_SDA_EN (0x01 << 0) > +/* SDA line state control bit, 1:high level;0:low level */ > +#define TWI_LCR_SDA_CTL (0x01 << 1) > +/* SCL line state control enable ,1:enable;0:disable */ > +#define TWI_LCR_SCL_EN (0x01 << 2) > +/* SCL line state control bit, 1:high level;0:low level */ > +#define TWI_LCR_SCL_CTL (0x01 << 3) > +/* current state of SDA,readonly bit */ > +#define TWI_LCR_SDA_STATE_MASK (0x01 << 4) > +/* current state of SCL,readonly bit */ > +#define TWI_LCR_SCL_STATE_MASK (0x01 << 5) > +/* 31:6bits reserved */ > +#define TWI_LCR_IDLE_STATUS 0x3a > + > +/* TWI Status Register Bit Fields & Masks */ > +#define TWI_STAT_MASK 0xff > +/* 7:0 bits use only,default is 0xF8 */ > +#define TWI_STAT_BUS_ERR 0x00 /* BUS ERROR */ > + > +/* 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 > +/* Second Address byte + Write bit transmitted, ACK received */ > +#define TWI_STAT_TX_2AW_ACK 0xD0 > +/* Second Address byte + Write bit transmitted, ACK received */ > +#define TWI_STAT_TX_2AW_NAK 0xD8 > +/* 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 > +/* > + * Arbitration lost in address as master, slave address + Write bit received, > + * ACK transmitted > + */ > +#define TWI_STAT_ARBLOST_RXWS_ACK 0x68 > +/* General Call address received, ACK transmitted */ > +#define TWI_STAT_RXGCAS_ACK 0x70 > +/* > + * Arbitration lost in address as master, General Call address received, > + * ACK transmitted > + */ > +#define TWI_STAT_ARBLOST_RXGCAS_ACK 0x78 > +/* Data byte received after slave address received, ACK transmitted */ > +#define TWI_STAT_RXDS_ACK 0x80 > +/* Data byte received after slave address received, not ACK transmitted */ > +#define TWI_STAT_RXDS_NAK 0x88 > +/* Data byte received after General Call received, ACK transmitted */ > +#define TWI_STAT_RXDGCAS_ACK 0x90 > +/* Data byte received after General Call received, not ACK transmitted */ > +#define TWI_STAT_RXDGCAS_NAK 0x98 > +/* STOP or repeated START condition received in slave */ > +#define TWI_STAT_RXSTPS_RXRESTAS 0xA0 > +/* Slave address + Read bit received, ACK transmitted */ > +#define TWI_STAT_RXRS_ACK 0xA8 > +/* > + * Arbitration lost in address as master, slave address + Read bit received, > + * ACK transmitted > + */ > +#define TWI_STAT_ARBLOST_SLAR_ACK 0xB0 > +/* Data byte transmitted in slave mode, ACK received */ > +#define TWI_STAT_TXDS_ACK 0xB8 > +/* Data byte transmitted in slave mode, ACK not received */ > +#define TWI_STAT_TXDS_NAK 0xC0 > +/* Last byte transmitted in slave mode, ACK received */ > +#define TWI_STAT_TXDSL_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 infomation,INT_FLAG = 0 */ > +#define TWI_STAT_IDLE 0xF8 > + > +#endif > diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile > index 5dbdbe3..9f929e6 100644 > --- a/drivers/i2c/Makefile > +++ b/drivers/i2c/Makefile > @@ -46,6 +46,7 @@ COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o > COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o > COBJS-$(CONFIG_SH_I2C) += sh_i2c.o > COBJS-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o > +COBJS-$(CONFIG_SUNXI_I2C) += sunxi_i2c.o > > COBJS := $(COBJS-y) > SRCS := $(COBJS:.o=.c) > diff --git a/drivers/i2c/sunxi_i2c.c b/drivers/i2c/sunxi_i2c.c > new file mode 100644 > index 0000000..6bf5309 > --- /dev/null > +++ b/drivers/i2c/sunxi_i2c.c > @@ -0,0 +1,278 @@ > +/* > + * Copyright (c) 2012 Henrik Nordstrom <hen...@henriknordstrom.net> > + * > + * See file CREDITS for list of people who contributed to this > + * project. > + * > + * 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. > + * > + * 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. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, > + * MA 02111-1307 USA > + */ > + > +#include <common.h> > +#include <i2c.h> > +#include <asm/io.h> > +#include <asm/arch/cpu.h> > +#include <asm/arch/i2c.h> > +#include <asm/arch/gpio.h> > +#include <asm/arch/clock.h> > + > +static struct i2c __attribute__ ((section(".data"))) *i2c_base = > + (struct i2c *)0x1c2ac00; > + > +void i2c_init(int speed, int slaveaddr) > +{ > + sunxi_gpio_set_cfgpin(SUNXI_GPB(0), 2); > + sunxi_gpio_set_cfgpin(SUNXI_GPB(1), 2); > + clock_twi_onoff(0, 1); > + > + /* Enable the i2c bus */ > + writel(TWI_CTL_BUSEN, &i2c_base->ctl); > + > + /* 400KHz operation M=2, N=1, 24MHz APB clock */ > + writel(TWI_CLK_DIV(2, 1), &i2c_base->clkr); > + writel(TWI_SRST_SRST, &i2c_base->reset); > + > + while ((readl(&i2c_base->reset) & TWI_SRST_SRST)) > + ; > +} > + > +int i2c_probe(uchar chip) > +{ > + return -1; > +} > + > +static int i2c_wait_ctl(int mask, int state) > +{ > + int timeout = 0x2ff; > + int value = state ? mask : 0; > + > + debug("i2c_wait_ctl(%x == %x), ctl=%x, status=%x\n", mask, value, > + i2c_base->ctl, i2c_base->status); > + > + while (((readl(&i2c_base->ctl) & mask) != value) && timeout-- > 0) > + ; > + > + debug("i2c_wait_ctl(), timeout=%d, ctl=%x, status=%x\n", timeout, > + i2c_base->ctl, i2c_base->status); > + > + if (timeout != 0) > + return 0; > + else > + return -1; > +} > + > +static void i2c_clear_irq(void) > +{ > + writel(readl(&i2c_base->ctl) & ~TWI_CTL_INTFLG, &i2c_base->ctl); > +} > + > +static int i2c_wait_irq(void) > +{ > + return i2c_wait_ctl(TWI_CTL_INTFLG, 1); > +} > + > +static int i2c_wait_status(int status) > +{ > + int timeout = 0x2ff; > + > + while (readl(&i2c_base->status) != status && timeout-- > 0) > + ; > + > + if (timeout != 0) > + return 0; > + else > + return -1; > +} > + > +static int i2c_wait_irq_status(int status) > +{ > + if (i2c_wait_irq() != 0) > + return -1; > + > + if (readl(&i2c_base->status) != status) > + return -1; > + > + return 0; > +} > + > +static int i2c_wait_bus_idle(void) > +{ > + int timeout = 0x2ff; > + > + while (readl(&i2c_base->lctl) != 0x3a && timeout-- > 0) > + ; > + > + if (timeout != 0) > + return 0; > + else > + return -1; > +} > + > +static int i2c_stop(void) > +{ > + u32 ctl; > + > + ctl = readl(&i2c_base->ctl) & 0xc0; > + ctl |= TWI_CTL_STP; > + > + writel(ctl, &i2c_base->ctl); > + > + /* dummy to delay one I/O operation to make sure it's started */ > + (void)readl(&i2c_base->ctl); > + > + if (i2c_wait_ctl(TWI_CTL_STP, 0) != 0) > + return -1; > + if (i2c_wait_status(TWI_STAT_IDLE)) > + return -1; > + if (i2c_wait_bus_idle() != 0) > + return -1; > + > + return 0; > +} > + > +static int i2c_send_data(u8 data, u8 status) > +{ > + debug("i2c_write(%02x, %x), ctl=%x, status=%x\n", data, status, > + i2c_base->ctl, i2c_base->status); > + > + writel(data, &i2c_base->data); > + i2c_clear_irq(); > + > + if (i2c_wait_irq_status(status) != 0) > + return -1; > + > + return 0; > +} > + > +static int i2c_start(int status) > +{ > + u32 ctl; > + > + debug("i2c_start(%x), ctl=%x, status=%x\n", status, i2c_base->ctl, > + i2c_base->status); > + /* Check that the controller is idle */ > + if (status == TWI_STAT_TX_STA > + && readl(&i2c_base->status) != TWI_STAT_IDLE) { > + return -1; > + } > + > + writel(0, &i2c_base->efr); > + > + /* Send start */ > + ctl = readl(&i2c_base->ctl); > + ctl |= TWI_CTL_STA; /* Set start bit */ > + ctl &= ~TWI_CTL_INTFLG; /* Clear int flag */ > + writel(ctl, &i2c_base->ctl); > + > + if (i2c_wait_ctl(TWI_CTL_STA, 0) != 0) > + return -1; > + if (i2c_wait_irq_status(status) != 0) > + return -1; > + > + return 0; > +} > + > +int i2c_do_read(uchar chip, uint addr, int alen, uchar *buffer, int len) > +{ > + u32 status; > + u32 ctl; > + > + if (i2c_start(TWI_STAT_TX_STA) != 0) > + return -1; > + > + /* Send chip address */ > + if (i2c_send_data(chip << 1 | 0, TWI_STAT_TX_AW_ACK) != 0) > + return -1; > + > + /* Send data address */ > + if (i2c_send_data(addr, TWI_STAT_TXD_ACK) != 0) > + return -1; > + > + /* Send restart for read */ > + if (i2c_start(TWI_STAT_TX_RESTA) != 0) > + return -1; > + > + /* Send chip address */ > + if (i2c_send_data(chip << 1 | 1, TWI_STAT_TX_AR_ACK) != 0) > + return -1; > + > + /* Set ACK mode */ > + ctl = readl(&i2c_base->ctl); > + ctl |= TWI_CTL_ACK; > + writel(ctl, &i2c_base->ctl); > + status = TWI_STAT_RXD_ACK; > + > + /* Read data */ > + while (len > 0) { > + if (len == 1) { > + /* Set NACK mode (last byte) */ > + ctl = readl(&i2c_base->ctl); > + ctl &= ~TWI_CTL_ACK; > + writel(ctl, &i2c_base->ctl); > + status = TWI_STAT_RXD_NAK; > + } > + > + i2c_clear_irq(); > + if (i2c_wait_irq_status(status) != 0) > + return -1; > + > + *buffer++ = readl(&i2c_base->data); > + len--; > + } > + > + return 0; > +} > + > +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) > +{ > + int rc = i2c_do_read(chip, addr, alen, buffer, len); > + > + i2c_stop(); > + > + return rc; > +} > + > +static int i2c_do_write(uchar chip, uint addr, int alen, uchar *buffer, > + int len) > +{ > + if (i2c_start(TWI_STAT_TX_STA) != 0) > + return -1; > + > + /* Send chip address */ > + if (i2c_send_data(chip << 1 | 0, TWI_STAT_TX_AW_ACK) != 0) > + return -1; > + > + /* Send data address */ > + if (i2c_send_data(addr, TWI_STAT_TXD_ACK) != 0) > + return -1; > + > + /* Send data */ > + while (len > 0) { > + if (i2c_send_data(*buffer++, TWI_STAT_TXD_ACK) != 0) > + return -1; > + len--; > + } > + > + return 0; > +} > + > +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) > +{ > + int rc = i2c_do_write(chip, addr, alen, buffer, len); > + > + i2c_stop(); > + > + return rc; > +} > diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h > index 8a026e0..c2d16fb 100644 > --- a/include/configs/sunxi-common.h > +++ b/include/configs/sunxi-common.h > @@ -194,4 +194,12 @@ > #undef CONFIG_CMD_NET > #undef CONFIG_CMD_NFS > > +/* I2C */ > +#define CONFIG_SPL_I2C_SUPPORT > +#define CONFIG_SYS_I2C_SPEED 400000 > +#define CONFIG_HARD_I2C > +#define CONFIG_SUNXI_I2C > +#define CONFIG_SYS_I2C_SLAVE 0x7f > +#define CONFIG_CMD_I2C Why don't you do it like this: #ifdef CONFIG_CMD_I2C #define CONFIG_SPL_I2C_SUPPORT #define CONFIG_SYS_I2C_SPEED 400000 #define CONFIG_HARD_I2C #define CONFIG_SUNXI_I2C #define CONFIG_SYS_I2C_SLAVE 0x7f #endif /* CONFIG_CMD_I2C */ That way you can simply turn on i2c support in board configuration file. If users don't want/need i2c they don't need to use it. > + > #endif /* __CONFIG_H */ > -- > 1.7.7.6 Luka _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot