diff options
Diffstat (limited to 'board/wmt/wmt_i2c_3.c')
-rwxr-xr-x | board/wmt/wmt_i2c_3.c | 636 |
1 files changed, 636 insertions, 0 deletions
diff --git a/board/wmt/wmt_i2c_3.c b/board/wmt/wmt_i2c_3.c new file mode 100755 index 0000000..e1bd79a --- /dev/null +++ b/board/wmt/wmt_i2c_3.c @@ -0,0 +1,636 @@ +/*++ +Copyright (c) 2010 WonderMedia Technologies, Inc. + +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, see http://www.gnu.org/licenses/>. + +WonderMedia Technologies, Inc. +10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ +/* + * (C) Copyright 2001, 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * 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 + * + * This has been changed substantially by Gerald Van Baren, Custom IDEAS, + * vanbaren@cideas.com. It was heavily influenced by LiMon, written by + * Neil Russell. + */ + +#include <common.h> +#include <i2c.h> +#include "include/i2c.h" +#include "include/wmt_clk.h" +#include <malloc.h> + +#if defined(CONFIG_HARD_I2C) + +/* #define DEBUG_I2C */ + + +/*----------------------------------------------------------------------- + * Definitions + */ + +#define RETRIES 0 + + +#define I2C_ACK 0 /* PD_SDA level to ack a byte */ +#define I2C_NOACK 1 /* PD_SDA level to noack a byte */ + + +#ifdef DEBUG_I2C +#define PRINTD(fmt, args...) do { \ + DECLARE_GLOBAL_DATA_PTR; \ + if (gd->have_console) \ + printf(fmt , ##args); \ + } while (0) +#else +#define PRINTD(fmt, args...) +#endif + +static struct i2c_s i2c ; +static int retry_time = 3; + +/*----------------------------------------------------------------------- + * Local functions + */ + +static int i2c_do_xfer(struct i2c_msg_s msgs[], int num); + +#if 0 +static int write_byte(uchar byte); +#endif + +static int i2c_read_msg(unsigned short slave_addr, unsigned char *buf, + unsigned short length, int restart, int last); +static int i2c_write_msg(unsigned short slave_addr, unsigned char *buf, + unsigned short length, int restart, int last); +#if 0 +static uchar read_byte(int); +#endif + +static enum i2c_mode_s i2c_xfer_mode = I2C_STANDARD_MODE; + +extern int auto_pll_divisor(enum dev_id dev, enum clk_cmd cmd, int unit, int freq); +extern int wmt_delayus(int us); + +#define GPIO_CTRL_I2C (*(volatile char *)0xD8110057) +#define GPIO_PAD_EN_I2C (*(volatile char *)0xD8110497) +#define GPIO_PAD_PU_I2C (*(volatile char *)0xD81104D7) + +/* [Rx00] GPIO Enable Control Register for I2C */ +#define GPIO_I2C3_SDA 0x00000002 +#define GPIO_I2C3_SCL 0x00000001 +/* [Rx600] GPIO Pull up/down Control Register for I2C */ +#define GPIO_I2C3_SCL_PULL_EN 0x00000001 +#define GPIO_I2C3_SDA_PULL_EN 0x00000002 +#define GPIO_I2C3_SCL_PULL_UP 0x00000001 +#define GPIO_I2C3_SDA_PULL_UP 0x00000002 + +static int i2c_wait_bus_not_busy(void) +{ + unsigned int timeout = 30000 ; + + while (1) { + if ((i2c.regs->IICSR & I2C_STATUS_MASK) == I2C_READY) + break ; + --timeout ; + if (timeout == 0) + break ; + } + if (timeout == 0) { + PRINTD("i2c_err : wait ready timeout error\n\r") ; + return -1 ; + } + return 0 ; +} + + +#if 0 +/*----------------------------------------------------------------------- + * Send 8 bits and look for an acknowledgement. + */ +static int write_byte(uchar data) +{ + unsigned int length = 1; + int ret ; + unsigned char *buf[1]; + struct i2c_msg_s wr[1]; + + *buf = 0x00; + + wr[0].addr = data ; + wr[0].flags = I2C_M_WR ; + wr[0].len = length ; + wr[0].buf = (unsigned char *)buf ; + ret = i2c_transfer(wr, 1); + + if (ret != 1) { + PRINTD("%d, %s, write fail with address=0x%X\n", __LINE__, __func__, address); + free(buf); + return -1; + } + + PRINTD("i2c_write: chip %02X addr %02X alen %d buffer %p len %d\n", + chip, addr, alen, buffer, len); + free(buf); + + return 0 ; +} + + +/*----------------------------------------------------------------------- + * if ack == I2C_ACK, ACK the byte so can continue reading, else + * send I2C_NOACK to end the read. + */ +static uchar read_byte(int ack) +{ + return 1; +} +#endif + +/*=====================================================================*/ +/* Public Functions */ +/*=====================================================================*/ + +/*----------------------------------------------------------------------- + * Initialization + */ +void i2c3_init(int speed, int slaveaddr) +{ + unsigned short tmp ; + + auto_pll_divisor(DEV_I2C3, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_I2C3, SET_DIV, 2, 20); + /**/ + /* software initial*/ + i2c.regs = (struct I2C_REG *)BA_I2C3; + i2c.irq_no = 15; + /*set i2c master transfer mode*/ + if (i2c_xfer_mode == I2C_STANDARD_MODE) + i2c.i2c_mode = I2C_STANDARD_MODE ; + else if (i2c_xfer_mode == I2C_FAST_MODE) + i2c.i2c_mode = I2C_FAST_MODE ; + else if (i2c_xfer_mode == I2C_HS_MODE) + i2c.i2c_mode = I2C_HS_MODE ; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + /* Set I2C/GPIO pinmux to IIC funciton*/ + /* Set bit[0-3] to zero*/ + GPIO_CTRL_I2C &= ~(GPIO_I2C3_SCL | GPIO_I2C3_SDA); + GPIO_PAD_EN_I2C |= (GPIO_I2C3_SCL_PULL_EN | + GPIO_I2C3_SDA_PULL_EN); + GPIO_PAD_PU_I2C |= (GPIO_I2C3_SCL_PULL_UP | + GPIO_I2C3_SDA_PULL_UP); + + /* Ensure I2C clock is enabled*/ + /*set i2c master register */ + i2c.regs->IICCR = 0; + i2c.regs->IICDIV = 12; /* 12MHz input clk directly*/ + i2c.regs->IICISR = I2C_ISR_ALL_WRITE_CLEAR; + i2c.regs->IICIMR = I2C_IMR_ALL_ENABLE; + i2c.regs->IICCR = I2C_CR_ENABLE; + tmp = i2c.regs->IICSR; /* Read clear "received ACK bit"*/ + i2c.regs->IICISR = I2C_ISR_ALL_WRITE_CLEAR; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + i2c.regs->IICTR = I2C_TR_STD_VALUE ; /* 0x8064*/ + else if (i2c.i2c_mode == I2C_FAST_MODE) + i2c.regs->IICTR = I2C_TR_FAST_VALUE ; /* 0x8019*/ + + return ; +} + +/*===========================================================================*/ +/* i2c_transfer*/ +/**/ +/* return:*/ +/*===========================================================================*/ +int i2c3_transfer(struct i2c_msg_s msgs[], int num) +{ + int ret ; + int retries ; + int i; + int delay = 1000; + + retries = retry_time ; + for (i = retries ; i > 0; i--) { + ret = i2c_do_xfer(msgs, num); + if (ret > 0) + return ret ; + + PRINTD("%s:i2c_test: retrying transmission\n\r", __func__); + delay = 1000; + while (delay == 0) + delay--; + } + wmt_delayus(100) ; + + PRINTD("i2c_err : retried %i times\n\r", retries); + return -1 ; + +} + +static int i2c_do_xfer(struct i2c_msg_s msgs[], int num) +{ + struct i2c_msg_s *pmsg = NULL; + int i; + int ret = 0 ; + + ret = i2c_wait_bus_not_busy(); + if (ret < 0) + return ret ; + + for (i = 0; ret >= 0 && i < num; i++) { + int last = ((i + 1) == num); + int restart = (i != 0) ; + pmsg = &msgs[i]; + + if (pmsg->flags & I2C_M_RD) /* READ*/ + ret = i2c_read_msg(pmsg->addr, pmsg->buf, pmsg->len, restart, last); + else /* Write*/ + ret = i2c_write_msg(pmsg->addr, pmsg->buf, pmsg->len, restart, last); + } + + if (ret < 0) + return ret; + else + return i; +} +/*----------------------------------------------------------------------- + * Probe to see if a chip is present. Also good for checking for the + * completion of EEPROM writes since the chip stops responding until + * the write completes (typically 10mSec). + */ +int i2c3_probe(uchar addr) +{ + int rc; + + /* perform 1 byte read transaction */ + /* + rc = write_byte(addr); + */ + rc = 1; + + return rc ? 1 : 0; +} + +/*===========================================================================*/ +/* i2c_irq_handler*/ +/**/ +/* return: NULL*/ +/*===========================================================================*/ +static void i2c_irq_handler(void) +{ + unsigned short isr_status ; + + isr_status = i2c.regs->IICISR ; + + if (isr_status & I2C_ISR_NACK_ADDR) { + unsigned short tmp ; + i2c.regs->IICISR = I2C_ISR_NACK_ADDR_WRITE_CLEAR ; + tmp = i2c.regs->IICSR ; /* read clear*/ + i2c.isr_nack = 1 ; + return ; + } + + if (isr_status & I2C_ISR_BYTE_END) { + i2c.regs->IICISR = I2C_ISR_BYTE_END_WRITE_CLEAR ; + i2c.isr_byte_end = 1 ; + return ; + } + + if (isr_status & I2C_ISR_SCL_TIME_OUT) { + i2c.regs->IICISR = I2C_ISR_SCL_TIME_OUT_WRITE_CLEAR ; + i2c.isr_timeout = 1 ; + return ; + } else { + PRINTD("i2c_err : unknown I2C ISR Handle 0x%4.4X" , isr_status) ; + return ; + } +} + +/* + * i2c_write_msg + * return: 0 success + * -1 fail + */ +static int i2c_write_msg(unsigned short slave_addr, + unsigned char *buf, + unsigned short length, + int restart, + int last) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + unsigned int timeout ; + if (length == 0) + return -1 ; + xfer_length = 0 ; /* for array index and also for checking counting*/ + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + + i2c.regs->IICDR = (unsigned short)(buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else { + tcr_value = (unsigned short)(I2C_TCR_HS_MODE|I2C_TCR_MASTER_WRITE |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + i2c.regs->IICDIV = HS_MASTER_CODE; + } + + /* SET TRANSFER MODE*/ + i2c.regs->IICTCR = tcr_value ; + + /*repeat start case*/ + if (restart == 1) + i2c.regs->IICCR |= I2C_CR_CPU_RDY ; + + while (1) { + timeout = 500000 ; + while (1) { + i2c_irq_handler(); + if ((i2c.isr_nack == 1) || (i2c.isr_byte_end == 1) || (i2c.isr_timeout == 1)) + break ; + --timeout ; + if (timeout == 0) + break ; + } + /* fail case*/ + if (timeout == 0) { + PRINTD("[%s]i2c_err : wrire software timeout error (tx)\n\r", __func__) ; + return -1 ; + } + if (i2c.isr_nack == 1) { + PRINTD("i2c_err : write NACK error (tx) \n\r") ; + return -1 ; + } + if (i2c.isr_timeout == 1) { + PRINTD("%s:i2c_err : write SCL timeout error (tx)\n\r", __func__) ; + return -1 ; + } + + /* pass case*/ + if (i2c.isr_byte_end == 1) + ++xfer_length ; + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + + if ((i2c.regs->IICSR & I2C_CSR_RCV_ACK_MASK) == I2C_CSR_RCV_NOT_ACK) { + PRINTD("i2c_err : write RCV NACK error\n\r") ; + return -1 ; + } + + + if (length > xfer_length) { + i2c.regs->IICDR = (unsigned short) (buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; + i2c.regs->IICCR = (I2C_CR_CPU_RDY | I2C_CR_ENABLE) ; + } else if (length == xfer_length) { /* end tx xfer*/ + if (last == 1) { /* stop case*/ + //i2c.regs->IICCR = (I2C_CR_TX_END|I2C_CR_CPU_RDY|I2C_CR_ENABLE) ; + wmt_delayus(2);/*2 us*/ + i2c.regs->IICCR |= (I2C_CR_TX_END) ; + break ; + } else { /* restart case*/ + /* handle the restart for first write then the next is read*/ + i2c.regs->IICCR = (I2C_CR_ENABLE) ; + break ; + } + } else { + PRINTD("i2c_err : write unknown error\n\r") ; + return -1 ; + } + } + i2c.regs->IICCR &= ~(I2C_CR_TX_END|I2C_CR_CPU_RDY) ; + return 0 ; +} + +/* + * i2c_read_msg + * return: 0 success + * -1 fail + */ +static int i2c_read_msg(unsigned short slave_addr, + unsigned char *buf, + unsigned short length, + int restart, + int last) +{ + unsigned short tcr_value ; + unsigned int xfer_length ; + unsigned int timeout ; + + if (length == 0) + return -1 ; + xfer_length = 0 ; + + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + + if (i2c.i2c_mode == I2C_STANDARD_MODE) + tcr_value = (unsigned short)(I2C_TCR_STANDARD_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else if (i2c.i2c_mode == I2C_FAST_MODE) + tcr_value = (unsigned short)(I2C_TCR_FAST_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + else + tcr_value = (unsigned short)(I2C_TCR_HS_MODE|I2C_TCR_MASTER_READ |\ + (slave_addr & I2C_TCR_SLAVE_ADDR_MASK)) ; + + i2c.regs->IICTCR = tcr_value; + + /*repeat start case*/ + if (restart == 1) + i2c.regs->IICCR |= I2C_CR_CPU_RDY ; + + if (length == 1) + i2c.regs->IICCR |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ + + while (1) { + timeout = 500000 ; + while (1) { + i2c_irq_handler(); + if ((i2c.isr_nack == 1) || (i2c.isr_byte_end == 1) || (i2c.isr_timeout == 1)) + break ; + --timeout ; + if (timeout == 0) + break ; + } + /* fail case*/ + if (i2c.isr_nack == 1) { + PRINTD("i2c_err : write NACK error (rx) \n\r") ; + return -1 ; + } + if (i2c.isr_timeout == 1) { + PRINTD("%s, i2c_err : write SCL timeout error (rx)\n\r", __func__) ; + return -1 ; + } + if (timeout == 0) { + PRINTD("[%s]i2c_err: write software timeout error (rx) \n\r", __func__) ; + return -1 ; + } + /* pass case*/ + if (i2c.isr_byte_end == 1) { + buf[xfer_length] = (i2c.regs->IICDR >> 8) ; + ++xfer_length ; + } + i2c.isr_nack = 0 ; + i2c.isr_byte_end = 0 ; + i2c.isr_timeout = 0 ; + + if (length > xfer_length) { + if ((length - 1) == xfer_length) /* next read is the last one*/ + i2c.regs->IICCR |= (I2C_CR_TX_NEXT_NO_ACK | I2C_CR_CPU_RDY); + else + i2c.regs->IICCR |= I2C_CR_CPU_RDY ; + } else if (length == xfer_length) { /* end rx xfer*/ + if (last == 1) /* stop case*/ + break ; + else /* restart case*/ + /* ??? how to handle the restart after read ?*/ + break ; + } else { + PRINTD("i2c_err : read known error\n\r") ; + return -1 ; + } + } + PRINTD("i2c_test: read sequence completed\n\r"); + i2c.regs->IICCR &= ~(I2C_CR_TX_NEXT_NO_ACK | I2C_CR_CPU_RDY); + return 0 ; +} + +/*----------------------------------------------------------------------- + * Read bytes + */ +int i2c3_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int ret; + unsigned char *reg_idx; + struct i2c_msg_s wr[1] ; + struct i2c_msg_s rd[1] ; + int i = 0; + + reg_idx = calloc(alen, sizeof(unsigned char *)); + for (i = 0; i < alen; ++i) + reg_idx[i] = (addr & (0xff << i*8)) >> i*8; + + wr[0].addr = chip ; + wr[0].flags = I2C_M_WR ; + wr[0].len = alen ; + wr[0].buf = reg_idx ; + + rd[0].addr = chip ; + rd[0].flags = I2C_M_RD ; + rd[0].len = len ; + rd[0].buf = buffer ; + + if (alen > 0) + ret = i2c3_transfer(wr, 1); + ret = i2c3_transfer(rd, 1); + + PRINTD("i2c_read: chip %02X addr %02X alen %d buffer %p len %d\n", + chip, addr, alen, buffer, len); + + if (ret != 1) { + PRINTD("[i2c_register_read] read fail \n"); + free(reg_idx); + return -1; + } + + free(reg_idx); + return 0 ; + +} + +/*----------------------------------------------------------------------- + * Write bytes + */ +int i2c3_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + unsigned int length = len + alen; + int ret ; + unsigned char *buf; + struct i2c_msg_s wr[1]; + unsigned int i = 0; + buf = calloc(length, sizeof(unsigned char *)); + + for (i = 0; i < length; ++i) { + if (i < alen) + *(buf + alen - i - 1) = (unsigned char) ((addr >> (i * 8)) & 0xFF); + else + *(buf + i) = *(buffer + i - alen); + } + wr[0].addr = chip ; + wr[0].flags = I2C_M_WR ; + wr[0].len = length ; + wr[0].buf = buf ; + ret = i2c3_transfer(wr, 1); + + if (ret != 1) { + PRINTD("%d, %s, write fail with address=0x%X\n", __LINE__, __func__, address); + free(buf); + return -1; + } + + PRINTD("i2c_write: chip %02X addr %02X alen %d buffer %p len %d\n", + chip, addr, alen, buffer, len); + free(buf); + + return 0 ; +} + +/*----------------------------------------------------------------------- + * Read a register + */ +uchar i2c3_reg_read(uchar i2c_addr, uchar reg) +{ + uchar buf; + + i2c3_read(i2c_addr, reg, 1, &buf, 1); + + return buf; +} + +/*----------------------------------------------------------------------- + * Write a register + */ +void i2c3_reg_write(uchar i2c_addr, uchar reg, uchar val) +{ + i2c3_write(i2c_addr, reg, 1, &val, 1); +} + + +#endif /* CONFIG_SOFT_I2C */ |