/*++ drivers/i2c/busses/wmt_i2c_bus-4.c Copyright (c) 2013 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 . WonderMedia Technologies, Inc. 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. --*/ /* Include your headers here*/ #include #include #include /* #include */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __KERNEL__ #ifdef DEBUG #define DPRINTK printk #else #define DPRINTK(x...) #endif #else #define DPRINTK printf #endif #define MAX_BUS_READY_CNT 50 /* jiffy*/ #define MAX_TX_TIMEOUT 500 /* ms*/ #define MAX_RX_TIMEOUT 500 /* ms*/ #define USE_UBOOT_PARA struct wmt_i2c_s { struct i2c_regs_s *regs; int irq_no ; enum i2c_mode_e i2c_mode ; int volatile isr_nack ; int volatile isr_byte_end ; int volatile isr_timeout ; int volatile isr_int_pending ; }; static int i2c_wmt_wait_bus_not_busy(void); extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); static unsigned int speed_mode = 0; unsigned int wmt_i2c4_speed_mode = 0; static unsigned int is_master = 1;/*master:1, slave:0*/ unsigned int wmt_i2c4_is_master = 1; static unsigned int wmt_i2c4_power_state = 0;/*0:power on, 1:suspend, 2:shutdown*/ EXPORT_SYMBOL(wmt_i2c4_is_master); /**/ /* variable*/ /*-------------------------------------------------*/ static volatile struct wmt_i2c_s i2c ; DECLARE_WAIT_QUEUE_HEAD(i2c4_wait); /* spinlock_t i2c_wmt_irqlock = SPIN_LOCK_UNLOCKED; */ static DEFINE_SPINLOCK(i2c_wmt_irqlock); static struct list_head wmt_i2c_fifohead; /* static spinlock_t i2c_fifolock = SPIN_LOCK_UNLOCKED; */ static DEFINE_SPINLOCK(i2c_fifolock); static int i2c_wmt_read_buf( unsigned int slave_addr, char *buf, unsigned int length, int restart, int last ); static int i2c_wmt_write_buf( unsigned int slave_addr, char *buf, unsigned int length, int restart, int last ); static void i2c_gpio_init(void) { GPIO_CTRL_GP10_I2S_BYTE_VAL &= ~BIT4; GPIO_CTRL_GP11_I2S_BYTE_VAL &= ~BIT2; PULL_EN_GP10_I2S_BYTE_VAL |= BIT4; PULL_EN_GP11_I2S_BYTE_VAL |= BIT2; PULL_CTRL_GP10_I2S_BYTE_VAL |= BIT4; PULL_CTRL_GP11_I2S_BYTE_VAL |= BIT2; PIN_SHARING_SEL_4BYTE_VAL |= BIT21 | BIT22 | BIT13 | BIT14; } static void i2c_wmt_set_mode(enum i2c_mode_e mode /*!<; //[IN] mode */) { if (is_master == 0) return; i2c.i2c_mode = mode ; if (i2c.i2c_mode == I2C_STANDARD_MODE) { DPRINTK("I2C: set standard mode \n"); i2c.regs->tr_reg = I2C_TR_STD_VALUE ; /* 0x8041*/ } else if (i2c.i2c_mode == I2C_FAST_MODE) { DPRINTK("I2C: set fast mode \n"); i2c.regs->tr_reg = I2C_TR_FAST_VALUE ; /* 0x8011*/ } } static int i2c_send_request( struct i2c_msg *msg, int msg_num, int non_block, void (*callback)(void *data), void *data ) { struct wmt_i2cbusfifo *i2c_fifo_head; struct i2c_msg *pmsg = NULL; int ret = 0; int restart = 0; int last = 0; unsigned long flags; int slave_addr = msg[0].addr; if (slave_addr == WMT_I2C_API_I2C_ADDR) return ret ; if (wmt_i2c4_power_state == 2) { printk("I2C4 has been shutdown\n"); return -EIO; } i2c.isr_nack = 0 ; i2c.isr_byte_end = 0 ; i2c.isr_timeout = 0 ; if (non_block == 0) i2c.isr_int_pending = 0; i2c_fifo_head = kzalloc(sizeof(struct wmt_i2cbusfifo), GFP_ATOMIC); INIT_LIST_HEAD(&i2c_fifo_head->busfifohead); pmsg = &msg[0]; i2c_fifo_head->msg = pmsg; i2c_fifo_head->msg_num = msg_num; spin_lock_irqsave(&i2c_fifolock, flags); if (list_empty(&wmt_i2c_fifohead)) { i2c_wmt_wait_bus_not_busy(); pmsg = &msg[0]; i2c_fifo_head->xfer_length = 1; i2c_fifo_head->xfer_msgnum = 0; i2c_fifo_head->restart = 0; i2c_fifo_head->non_block = non_block; if (non_block == 1) { i2c_fifo_head->callback = callback; i2c_fifo_head->data = data; } else { i2c_fifo_head->callback = 0; i2c_fifo_head->data = 0; } list_add_tail(&i2c_fifo_head->busfifohead, &wmt_i2c_fifohead); if (pmsg->flags & I2C_M_RD) { i2c_fifo_head->xfer_length = 1; ret = i2c_wmt_read_buf(pmsg->addr, pmsg->buf, pmsg->len, restart, last); } else { i2c_fifo_head->xfer_length = 1; if (pmsg->flags & I2C_M_NOSTART) i2c_fifo_head->restart = 1; else i2c_fifo_head->restart = 0; ret = i2c_wmt_write_buf(pmsg->addr, pmsg->buf, pmsg->len, restart, last); } } else { i2c_fifo_head->xfer_length = 0; i2c_fifo_head->xfer_msgnum = 0; i2c_fifo_head->restart = 0; i2c_fifo_head->non_block = non_block; if (non_block == 1) { i2c_fifo_head->callback = callback; i2c_fifo_head->data = data; } else { i2c_fifo_head->callback = 0; i2c_fifo_head->data = 0; } list_add_tail(&i2c_fifo_head->busfifohead, &wmt_i2c_fifohead); } spin_unlock_irqrestore(&i2c_fifolock, flags); if (non_block == 0) { wait_event(i2c4_wait, i2c.isr_int_pending); ret = msg_num; if (i2c.isr_nack == 1) { DPRINTK("i2c_err : write NACK error (rx) \n\r") ; ret = -EIO ; } if (i2c.isr_timeout == 1) { DPRINTK("i2c_err : write SCL timeout error (rx)\n\r") ; ret = -ETIMEDOUT ; } } return ret; } static int i2c_wmt_read_buf( unsigned int slave_addr, char *buf, unsigned int length, int restart, int last ) { unsigned short tcr_value; int ret = 0; DPRINTK("[%s]:length = %d , slave_addr = %x\n", __func__, length , slave_addr); if (length <=0) return -1; i2c.isr_nack = 0 ; i2c.isr_byte_end = 0 ; i2c.isr_timeout = 0 ; /*i2c.isr_int_pending = 0;*/ i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ if (length <=0) return -1; i2c.isr_nack = 0 ; i2c.isr_byte_end = 0 ; i2c.isr_timeout = 0 ; /*i2c.isr_int_pending = 0;*/ tcr_value = 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)) ; if (length == 1) i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ i2c.regs->tcr_reg = tcr_value ; return ret; } static int i2c_wmt_write_buf( unsigned int slave_addr, char *buf, unsigned int length, int restart, int last ) { unsigned short tcr_value ; unsigned int xfer_length ; int ret = 0 ; DPRINTK("[%s]length = %d , slave_addr = %x\n", __func__, length , slave_addr); if (slave_addr == WMT_I2C_API_I2C_ADDR) return ret ; if (is_master == 0) return -ENXIO; /* special case allow length:0, for i2c_smbus_xfer*/ /**/ 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.isr_int_pending = 0;*/ i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ if (length == 0) i2c.regs->cdr_reg = 0 ; else i2c.regs->cdr_reg = (unsigned short)(buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; tcr_value = 0 ; 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)) ; i2c.regs->tcr_reg = tcr_value ; ret = 0 ; return ret; } static int i2c_wmt_read_msg( unsigned int slave_addr, /*!<; //[IN] Salve address */ char *buf, /*!<; //[OUT] Pointer to data */ unsigned int length, /*!<; //Data length */ int restart, /*!<; //Need to restart after a complete read */ int last /*!<; //Last read */ ) { unsigned short tcr_value ; unsigned int xfer_length ; int is_timeout ; int ret = 0 ; int wait_event_result = 0 ; if (is_master == 0) return -ENXIO; if (length <= 0) return -1 ; xfer_length = 0 ; if (restart == 0) ret = i2c_wmt_wait_bus_not_busy() ; if (ret < 0) return ret ; i2c.isr_nack = 0 ; i2c.isr_byte_end = 0 ; i2c.isr_timeout = 0 ; i2c.isr_int_pending = 0; i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ if (restart == 0) i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ tcr_value = 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)) ; } if (length == 1) i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ i2c.regs->tcr_reg = tcr_value ; /*repeat start case*/ if (restart == 1) i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ ret = 0 ; for (; ;) { is_timeout = 0 ; wait_event_result = wait_event_interruptible_timeout(i2c4_wait, i2c.isr_int_pending , (MAX_RX_TIMEOUT * HZ / 1000)) ; if (likely(wait_event_result > 0)) { DPRINTK("I2C: wait interrupted (rx) \n"); ret = 0 ; } else if (likely(i2c.isr_int_pending == 0)) { DPRINTK("I2C: wait timeout (rx) \n"); is_timeout = 1 ; ret = -ETIMEDOUT ; } /**/ /* fail case*/ /**/ if (i2c.isr_nack == 1) { DPRINTK("i2c_err : write NACK error (rx) \n\r") ; ret = -EIO ; break ; } if (i2c.isr_timeout == 1) { DPRINTK("i2c_err : write SCL timeout error (rx)\n\r") ; msleep(10); ret = -ETIMEDOUT ; break ; } if (is_timeout == 1) { DPRINTK("i2c_err: write software timeout error (rx) \n\r") ; ret = -ETIMEDOUT ; break ; } /**/ /* pass case*/ /**/ if (i2c.isr_byte_end == 1) { buf[xfer_length] = (i2c.regs->cdr_reg >> 8) ; ++xfer_length ; DPRINTK("i2c_test: received BYTE_END\n\r"); } i2c.isr_int_pending = 0; 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->cr_reg |= (I2C_CR_TX_NEXT_NO_ACK | I2C_CR_CPU_RDY); DPRINTK("i2c_test: set CPU_RDY & TX_ACK. next data is last.\r\n"); } else { i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; DPRINTK("i2c_test: more data to read. only set CPU_RDY. \r\n"); } } else if (length == xfer_length) { /* end rx xfer*/ if (last == 1) { /* stop case*/ DPRINTK("i2c_test: read completed \r\n"); break ; } else { /* restart case*/ /* ??? how to handle the restart after read ?*/ DPRINTK("i2c_test: RX ReStart Case \r\n") ; break ; } } else { DPRINTK("i2c_err : read known error\n\r") ; ret = -EIO ; break ; } } DPRINTK("i2c_test: read sequence completed\n\r"); return ret ; } static int i2c_wmt_write_msg( unsigned int slave_addr, /*!<; //[IN] Salve address */ char *buf, /*!<; //[OUT] Pointer to data */ unsigned int length, /*!<; //Data length */ int restart, /*!<; //Need to restart after a complete write */ int last /*!<; //Last read */ ) { unsigned short tcr_value ; unsigned int xfer_length ; int is_timeout ; int ret = 0 ; int wait_event_result ; DPRINTK("length = %d , slave_addr = %x\n", length , slave_addr); if (slave_addr == WMT_I2C_API_I2C_ADDR) return ret ; if (is_master == 0) return -ENXIO; /* special case allow length:0, for i2c_smbus_xfer*/ /**/ if (length < 0) return -1 ; xfer_length = 0 ; /* for array index and also for checking counting*/ if (restart == 0) ret = i2c_wmt_wait_bus_not_busy() ; if (ret < 0) return ret ; i2c.isr_nack = 0 ; i2c.isr_byte_end = 0 ; i2c.isr_timeout = 0 ; i2c.isr_int_pending = 0; /**/ /* special case allow length:0, for i2c_smbus_xfer*/ /**/ if (length == 0) i2c.regs->cdr_reg = 0 ; else i2c.regs->cdr_reg = (unsigned short)(buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; if (restart == 0) { i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ } /**/ /* I2C: Set transfer mode [standard/fast]*/ /**/ tcr_value = 0 ; 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)) ; i2c.regs->tcr_reg = tcr_value ; if (restart == 1) i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; ret = 0 ; for (; ;) { is_timeout = 0 ; /**/ /* I2C: Wait for interrupt. if ( i2c.isr_int_pending == 1 ) ==> an interrupt exsits.*/ /**/ wait_event_result = wait_event_interruptible_timeout(i2c4_wait, i2c.isr_int_pending , (MAX_TX_TIMEOUT * HZ / 1000)) ; if (likely(wait_event_result > 0)) { DPRINTK("I2C: wait interrupted (tx)\n"); ret = 0 ; } else if (likely(i2c.isr_int_pending == 0)) { DPRINTK("I2C: wait timeout (tx) \n"); is_timeout = 1 ; ret = -ETIMEDOUT ; } /**/ /* fail case*/ /**/ if (i2c.isr_nack == 1) { DPRINTK("i2c_err : write NACK error (tx) \n\r") ; ret = -EIO ; break ; } if (i2c.isr_timeout == 1) { DPRINTK("i2c_err : write SCL timeout error (tx)\n\r") ; msleep(10); ret = -ETIMEDOUT ; break ; } if (is_timeout == 1) { DPRINTK("i2c_err : write software timeout error (tx)\n\r") ; ret = -ETIMEDOUT ; break ; } /**/ /* pass case*/ /**/ if (i2c.isr_byte_end == 1) { DPRINTK("i2c: isr end byte (tx)\n\r") ; ++xfer_length ; } i2c.isr_int_pending = 0 ; i2c.isr_nack = 0 ; i2c.isr_byte_end = 0 ; i2c.isr_timeout = 0 ; if ((i2c.regs->csr_reg & I2C_CSR_RCV_ACK_MASK) == I2C_CSR_RCV_NOT_ACK) { DPRINTK("i2c_err : write RCV NACK error\n\r") ; ret = -EIO ; break ; } /**/ /* special case allow length:0, for i2c_smbus_xfer*/ /**/ if (length == 0) { i2c.regs->cr_reg = (I2C_CR_TX_END|I2C_CR_CPU_RDY|I2C_CR_ENABLE) ; break ; } if (length > xfer_length) { i2c.regs->cdr_reg = (unsigned short) (buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; i2c.regs->cr_reg = (I2C_CR_CPU_RDY | I2C_CR_ENABLE) ; DPRINTK("i2c_test: write register data \n\r") ; } else if (length == xfer_length) { /* end tx xfer*/ if (last == 1) { /* stop case*/ i2c.regs->cr_reg = (I2C_CR_TX_END|I2C_CR_CPU_RDY|I2C_CR_ENABLE) ; DPRINTK("i2c_test: finish write \n\r") ; break ; } else { /* restart case*/ /* handle the restart for first write then the next is read*/ i2c.regs->cr_reg = (I2C_CR_ENABLE) ; DPRINTK("i2c_test: tx restart Case \n\r") ; break ; } } else { DPRINTK("i2c_err : write unknown error\n\r") ; ret = -EIO ; break ; } } ; DPRINTK("i2c_test: write sequence completed\n\r"); return ret ; } static int i2c_wmt_wait_bus_not_busy(void) { int ret ; int cnt ; ret = 0 ; cnt = 0 ; while (1) { if ((REG16_VAL(I2C4_CSR_ADDR) & I2C_STATUS_MASK) == I2C_READY) { ret = 0; break ; } cnt++ ; if (cnt > MAX_BUS_READY_CNT) { ret = (-EBUSY) ; printk("i2c_err : wait but not ready time-out\n\r") ; cnt = 0; break; } } return ret ; } static void i2c_wmt_reset(void) { unsigned short tmp ; if (is_master == 0) return; /**/ /* software initial*/ /**/ i2c.regs = (struct i2c_regs_s *)I2C4_BASE_ADDR ; i2c.irq_no = IRQ_I2C4 ; if (speed_mode == 0) i2c.i2c_mode = I2C_STANDARD_MODE ; else i2c.i2c_mode = I2C_FAST_MODE ; i2c.isr_nack = 0 ; i2c.isr_byte_end = 0 ; i2c.isr_timeout = 0 ; i2c.isr_int_pending = 0; /**/ /* hardware initial*/ /**/ i2c.regs->cr_reg = 0 ; i2c.regs->div_reg = APB_96M_I2C_DIV ; i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ i2c.regs->imr_reg = I2C_IMR_ALL_ENABLE ; /* 0x0007*/ i2c.regs->cr_reg = I2C_CR_ENABLE ; tmp = i2c.regs->csr_reg ; /* read clear*/ i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ if (i2c.i2c_mode == I2C_STANDARD_MODE) i2c.regs->tr_reg = I2C_TR_STD_VALUE ; /* 0x8041*/ else if (i2c.i2c_mode == I2C_FAST_MODE) i2c.regs->tr_reg = I2C_TR_FAST_VALUE ; /* 0x8011*/ DPRINTK("Resetting I2C Controller Unit\n"); return ; } static int wmt_i2c_transfer_msg(struct wmt_i2cbusfifo *fifo_head) { int xfer_length = fifo_head->xfer_length; int xfer_msgnum = fifo_head->xfer_msgnum; struct i2c_msg *pmsg = &fifo_head->msg[xfer_msgnum]; int restart = fifo_head->restart; unsigned short tcr_value; unsigned short slave_addr = pmsg->addr; int length = pmsg->len; int ret = 0; if (pmsg->flags & I2C_M_RD) { if (restart == 0) i2c_wmt_wait_bus_not_busy(); i2c.isr_nack = 0 ; i2c.isr_byte_end = 0 ; i2c.isr_timeout = 0 ; /*i2c.isr_int_pending = 0;*/ i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ i2c.regs->cr_reg &= ~(I2C_CR_TX_NEXT_NO_ACK); /*clear NEXT_NO_ACK*/ if (restart == 0) i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ tcr_value = 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)) ; } if (length == 1) i2c.regs->cr_reg |= I2C_CR_TX_NEXT_NO_ACK; /*only 8-bit to read*/ i2c.regs->tcr_reg = tcr_value ; /*repeat start case*/ if (restart == 1) i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ } else { if (restart == 0) i2c_wmt_wait_bus_not_busy(); i2c.isr_nack = 0 ; i2c.isr_byte_end = 0 ; i2c.isr_timeout = 0 ; /*i2c.isr_int_pending = 0;*/ /**/ /* special case allow length:0, for i2c_smbus_xfer*/ /**/ if (length == 0) i2c.regs->cdr_reg = 0 ; else i2c.regs->cdr_reg = (unsigned short)(pmsg->buf[xfer_length] & I2C_CDR_DATA_WRITE_MASK) ; if (restart == 0) { i2c.regs->cr_reg &= ~(I2C_CR_TX_END); /*clear Tx end*/ i2c.regs->cr_reg |= (I2C_CR_CPU_RDY); /*release SCL*/ } /**/ /* I2C: Set transfer mode [standard/fast]*/ /**/ tcr_value = 0 ; 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)) ; i2c.regs->tcr_reg = tcr_value ; if (restart == 1) i2c.regs->cr_reg |= I2C_CR_CPU_RDY ; } return ret; } static irqreturn_t i2c_wmt_handler( int this_irq, /*!<; //[IN] IRQ number */ void *dev_id /*!<; //[IN] Pointer to device ID */ ) { int wakeup ; unsigned short isr_status ; unsigned short tmp ; unsigned long flags; struct wmt_i2cbusfifo *fifo_head; int xfer_length = 0; int xfer_msgnum = 0; struct i2c_msg *pmsg; volatile unsigned short csr_reg; spin_lock_irqsave(&i2c_wmt_irqlock, flags); isr_status = i2c.regs->isr_reg ; csr_reg = i2c.regs->csr_reg; wakeup = 0 ; fifo_head = list_first_entry(&wmt_i2c_fifohead, struct wmt_i2cbusfifo, busfifohead); if (isr_status & I2C_ISR_NACK_ADDR) { DPRINTK("[%s]:i2c NACK\n", __func__); /*spin_lock(&i2c_fifolock);*/ list_del(&fifo_head->busfifohead);/*del request*/ kfree(fifo_head); /*spin_unlock(&i2c_fifolock);*/ xfer_length = 0; i2c.regs->isr_reg = I2C_ISR_NACK_ADDR_WRITE_CLEAR ; tmp = i2c.regs->csr_reg ; /* read clear*/ i2c.isr_nack = 1 ; wakeup = 1 ; } if ((isr_status & I2C_ISR_BYTE_END && ((csr_reg & I2C_CSR_RCV_ACK_MASK) == I2C_CSR_RCV_NOT_ACK))) { /* printk("data rcv nack\n"); */ list_del(&fifo_head->busfifohead);/*del request*/ kfree(fifo_head); xfer_length = 0; i2c.regs->isr_reg = I2C_ISR_BYTE_END_WRITE_CLEAR ; i2c.isr_nack = 1 ; wakeup = 1 ; } else if (isr_status & I2C_ISR_BYTE_END) { i2c.regs->isr_reg = I2C_ISR_BYTE_END_WRITE_CLEAR ; i2c.isr_byte_end = 1 ; xfer_length = fifo_head->xfer_length; xfer_msgnum = fifo_head->xfer_msgnum; pmsg = &fifo_head->msg[xfer_msgnum]; /*read case*/ if (pmsg->flags & I2C_M_RD) { pmsg->buf[xfer_length - 1] = (i2c.regs->cdr_reg >> 8) ; /*the last data in current msg?*/ if (xfer_length == pmsg->len - 1) { /*last msg of the current request?*/ /*spin_lock(&i2c_fifolock);*/ if (pmsg->flags & I2C_M_NOSTART) { ++fifo_head->xfer_length; fifo_head->restart = 1; /* ++fifo_head->xfer_msgnum; */ i2c.regs->cr_reg |= I2C_CR_CPU_RDY; } else { ++fifo_head->xfer_length; fifo_head->restart = 0; /* ++fifo_head->xfer_msgnum; */ i2c.regs->cr_reg |= (I2C_CR_CPU_RDY | I2C_CR_TX_NEXT_NO_ACK); } /*spin_unlock(&i2c_fifolock);*/ } else if (xfer_length == pmsg->len) {/*next msg*/ if (xfer_msgnum < fifo_head->msg_num - 1) { /*spin_lock(&i2c_fifolock);*/ fifo_head->xfer_length = 0; ++fifo_head->xfer_msgnum; wmt_i2c_transfer_msg(fifo_head); ++fifo_head->xfer_length; /*spin_unlock(&i2c_fifolock);*/ } else { /*data of this msg has been transfered*/ /*spin_lock(&i2c_fifolock);*/ list_del(&fifo_head->busfifohead);/*del request*/ /*next request exist?*/ if (list_empty(&wmt_i2c_fifohead)) {/*no more reqeust*/ if (fifo_head->non_block == 0) { wakeup = 1; } else { fifo_head->callback(fifo_head->data); } kfree(fifo_head); } else { /*more request*/ if (fifo_head->non_block == 0) { wakeup = 1; } else { fifo_head->callback(fifo_head->data); } kfree(fifo_head); fifo_head = list_first_entry(&wmt_i2c_fifohead, struct wmt_i2cbusfifo, busfifohead); /* if (fifo_head->non_block == 0) wakeup = 1; */ fifo_head->xfer_length = 0; wmt_i2c_transfer_msg(fifo_head); ++fifo_head->xfer_length; /* if (fifo_head->non_block == 0) { printk("2 : non callback\n"); wakeup = 1; } else { printk("2 :callback\n"); fifo_head->callback(fifo_head->data); } */ } /*spin_unlock(&i2c_fifolock);*/ } } else {/*next data*/ /*spin_lock(&i2c_fifolock);*/ ++fifo_head->xfer_length; /*spin_unlock(&i2c_fifolock);*/ i2c.regs->cr_reg |= I2C_CR_CPU_RDY; } } else { /*write case*/ /*the last data in current msg?*/ if (xfer_length == pmsg->len) { /*last msg of the current request?*/ if (xfer_msgnum < fifo_head->msg_num - 1) { /*spin_lock(&i2c_fifolock);*/ if (pmsg->flags & I2C_M_NOSTART) { ++fifo_head->xfer_length; fifo_head->restart = 1; } else { ++fifo_head->xfer_length; fifo_head->restart = 0; i2c.regs->cr_reg &= ~(I2C_CR_TX_END); udelay(2); i2c.regs->cr_reg |= (I2C_CR_TX_END); } /*access next msg*/ fifo_head->xfer_length = 0; ++fifo_head->xfer_msgnum; wmt_i2c_transfer_msg(fifo_head); ++fifo_head->xfer_length; /*spin_unlock(&i2c_fifolock);*/ } else {/*this request finish*/ /*spin_lock(&i2c_fifolock);*/ /*next request exist?*/ list_del(&fifo_head->busfifohead);/*del request*/ if (list_empty(&wmt_i2c_fifohead)) { /*kfree(fifo_head);*/ /* if (fifo_head->non_block == 0) wakeup = 1; */ i2c.regs->cr_reg &= ~(I2C_CR_TX_END); udelay(2); i2c.regs->cr_reg |= (I2C_CR_TX_END); if (fifo_head->non_block == 0) { wakeup = 1; } else { fifo_head->callback(fifo_head->data); } kfree(fifo_head); } else { i2c.regs->cr_reg &= ~(I2C_CR_TX_END); udelay(2); i2c.regs->cr_reg |= (I2C_CR_TX_END); if (fifo_head->non_block == 0) { wakeup = 1; } else { fifo_head->callback(fifo_head->data); } kfree(fifo_head); fifo_head = list_first_entry(&wmt_i2c_fifohead, struct wmt_i2cbusfifo, busfifohead); /* if (fifo_head->non_block == 0) wakeup = 1; */ /*next msg*/ fifo_head->xfer_length = 0; ++fifo_head->xfer_msgnum; wmt_i2c_transfer_msg(fifo_head); ++fifo_head->xfer_length; /* if (fifo_head->non_block == 0) { printk("4:non callback\n"); wakeup = 1; } else { printk("4:callback\n"); fifo_head->callback(fifo_head->data); } */ } /*spin_unlock(&i2c_fifolock);*/ } } else {/*next data*/ i2c.regs->cdr_reg = (unsigned short) (pmsg->buf[fifo_head->xfer_length] & I2C_CDR_DATA_WRITE_MASK); /*spin_lock(&i2c_fifolock);*/ ++fifo_head->xfer_length; /*spin_unlock(&i2c_fifolock);*/ i2c.regs->cr_reg |= (I2C_CR_CPU_RDY | I2C_CR_ENABLE); } } } if (isr_status & I2C_ISR_SCL_TIME_OUT) { DPRINTK("[%s]SCL timeout\n", __func__); #if 0 i2c.regs->cr_reg |= BIT7;/*reset status*/ /*spin_lock(&i2c_fifolock);*/ list_del(&fifo_head->busfifohead);/*del request*/ /*spin_unlock(&i2c_fifolock);*/ xfer_length = 0; i2c.regs->isr_reg = I2C_ISR_SCL_TIME_OUT_WRITE_CLEAR | I2C_ISR_BYTE_END_WRITE_CLEAR; i2c.isr_timeout = 1 ; wakeup = 1; #endif i2c.regs->isr_reg = I2C_ISR_SCL_TIME_OUT_WRITE_CLEAR ; } if (wakeup) { /*spin_lock_irqsave(&i2c_wmt_irqlock, flags);*/ i2c.isr_int_pending = 1; /*spin_unlock_irqrestore(&i2c_wmt_irqlock, flags);*/ wake_up(&i2c4_wait); } else DPRINTK("i2c_err : unknown I2C ISR Handle 0x%4.4X" , isr_status) ; spin_unlock_irqrestore(&i2c_wmt_irqlock, flags); return IRQ_HANDLED; } static int i2c_wmt_resource_init(void) { if (is_master == 0) return 0; if (request_irq(i2c.irq_no , &i2c_wmt_handler, IRQF_DISABLED, "i2c", 0) < 0) { DPRINTK(KERN_INFO "I2C: Failed to register I2C irq %i\n", i2c.irq_no); return -ENODEV; } return 0; } static void i2c_wmt_resource_release(void) { if (is_master == 0) return; free_irq(i2c.irq_no, 0); } static struct i2c_algo_wmt_data i2c_wmt_data = { write_msg: i2c_wmt_write_msg, read_msg: i2c_wmt_read_msg, send_request: i2c_send_request, wait_bus_not_busy: i2c_wmt_wait_bus_not_busy, reset: i2c_wmt_reset, set_mode: i2c_wmt_set_mode, udelay: I2C_ALGO_UDELAY, timeout: I2C_ALGO_TIMEOUT, }; static struct i2c_adapter i2c_wmt_ops = { .owner = THIS_MODULE, /* .id = I2C_ALGO_WMT, */ .algo_data = &i2c_wmt_data, .name = "wmt_i2c4_adapter", .retries = I2C_ADAPTER_RETRIES, .nr = 4, }; #ifdef CONFIG_PM static struct i2c_regs_s wmt_i2c_reg ; static void i2c_shutdown(void) { printk("i2c4 shutdown\n"); wmt_i2c4_power_state = 2; while (!list_empty(&wmt_i2c_fifohead)) msleep(1); while (1) {/*wait busy clear*/ if ((REG16_VAL(I2C4_CSR_ADDR) & I2C_STATUS_MASK) == I2C_READY) break ; msleep(1); } return; } static int i2c_suspend(void) { printk("i2c4 suspend\n"); wmt_i2c_reg.imr_reg = i2c.regs->imr_reg; wmt_i2c_reg.tr_reg = i2c.regs->tr_reg; wmt_i2c_reg.div_reg = i2c.regs->div_reg; return 0; } static void i2c_resume(void) { printk("i2c4 resume\n"); /* GPIO_CTRL_GP17_I2C_BYTE_VAL &= ~(BIT2 | BIT3); PULL_EN_GP17_I2C_BYTE_VAL |= (BIT2 | BIT3); PULL_CTRL_GP17_I2C_BYTE_VAL |= (BIT2 | BIT3); */ i2c_gpio_init(); auto_pll_divisor(DEV_I2C4, CLK_ENABLE, 0, 0); auto_pll_divisor(DEV_I2C4, SET_DIV, 2, 20);/*20M Hz*/ i2c.regs->cr_reg = 0 ; i2c.regs->div_reg = wmt_i2c_reg.div_reg; i2c.regs->imr_reg = wmt_i2c_reg.imr_reg; i2c.regs->tr_reg = wmt_i2c_reg.tr_reg ; i2c.regs->cr_reg = 0x001 ; } #else #define i2c_suspend NULL #define i2c_resume NULL #define i2c_shutdown NULL #endif extern int wmt_i2c_add_bus(struct i2c_adapter *); extern int wmt_i2c_del_bus(struct i2c_adapter *); #ifdef CONFIG_PM static struct syscore_ops wmt_i2c_syscore_ops = { .suspend = i2c_suspend, .resume = i2c_resume, .shutdown = i2c_shutdown, }; #endif static int __init i2c_adap_wmt_init(void) { unsigned short tmp ; char varname[] = "wmt.i2c.param"; #ifdef CONFIG_I2C_SLAVE_WMT char varname1[] = "wmt.bus.i2c.slave_port"; #endif unsigned char buf[80]; int ret; unsigned int port_num; int idx = 0; int varlen = 80; /*since i2c4 is share pin, it turn on while uboot parameter define i2c4 mode*/ int i2c_turn_on = 0; unsigned int pllb_freq = 0; unsigned int tr_val = 0; #ifdef CONFIG_I2C_SLAVE_WMT #ifdef USE_UBOOT_PARA ret = wmt_getsyspara(varname1, buf, &varlen); #else ret = 1; #endif is_master = 1; if (ret == 0) { ret = sscanf(buf, "%x", &port_num); while (ret) { if (port_num != 4) is_master = 1; else { is_master = 0; break; } idx += ret; ret = sscanf(buf + idx, ",%x", &port_num); } } else is_master = 1; #endif wmt_i2c4_is_master = is_master; if (is_master == 1) { #ifdef USE_UBOOT_PARA ret = wmt_getsyspara(varname, buf, &varlen); #else ret = 1; #endif if (ret == 0) { ret = sscanf(buf, "%x:%x", &port_num, &speed_mode); idx += 3; while (ret) { if (ret < 2) speed_mode = 0; else { if (port_num != 4) speed_mode = 0; else { i2c_turn_on = 1; break; } } ret = sscanf(buf + idx, ",%x:%x", &port_num, &speed_mode); idx += 4; } } if (i2c_turn_on == 0) { printk("I2C4 turn off\n"); return -EIO; } if (speed_mode > 1) speed_mode = 0; wmt_i2c4_speed_mode = speed_mode; /**/ /* software initial*/ /**/ i2c.regs = (struct i2c_regs_s *)I2C4_BASE_ADDR ; i2c.irq_no = IRQ_I2C4 ; printk("PORT 4 speed_mode = %d\n", speed_mode); if (speed_mode == 0) i2c.i2c_mode = I2C_STANDARD_MODE ; else if (speed_mode == 1) i2c.i2c_mode = I2C_FAST_MODE ; i2c.isr_nack = 0 ; i2c.isr_byte_end = 0 ; i2c.isr_timeout = 0 ; i2c.isr_int_pending = 0; /**/ /* hardware initial*/ /**/ auto_pll_divisor(DEV_I2C4, CLK_ENABLE, 0, 0); pllb_freq = auto_pll_divisor(DEV_I2C4, SET_DIV, 2, 20);/*20M Hz*/ i2c_gpio_init(); printk("pllb_freq = %d\n", pllb_freq); if ((pllb_freq%(1000*2*100)) != 0) tr_val = pllb_freq/(1000*2*100) + 1; else tr_val = pllb_freq/(1000*2*100); i2c.regs->cr_reg = 0 ; i2c.regs->div_reg = APB_96M_I2C_DIV ; i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ i2c.regs->imr_reg = I2C_IMR_ALL_ENABLE ; /* 0x0007*/ i2c.regs->cr_reg = I2C_CR_ENABLE ; tmp = i2c.regs->csr_reg ; /* read clear*/ i2c.regs->isr_reg = I2C_ISR_ALL_WRITE_CLEAR ; /* 0x0007*/ if (i2c.i2c_mode == I2C_STANDARD_MODE) i2c.regs->tr_reg = 0xff00|tr_val; else if (i2c.i2c_mode == I2C_FAST_MODE) { tr_val /= 4; i2c.regs->tr_reg = 0xff00|tr_val ; } } if (i2c_wmt_resource_init() == 0) { if (wmt_i2c_add_bus(&i2c_wmt_ops) < 0) { i2c_wmt_resource_release(); printk(KERN_INFO "i2c: Failed to add bus\n"); return -ENODEV; } } else return -ENODEV; INIT_LIST_HEAD(&wmt_i2c_fifohead); #ifdef CONFIG_PM register_syscore_ops(&wmt_i2c_syscore_ops); #endif wmt_i2c4_power_state = 0; printk(KERN_INFO "i2c: successfully added bus\n"); #ifdef I2C_REG_TEST printk("i2c.regs->cr_reg= 0x%08x\n\r", i2c.regs->cr_reg); printk("i2c.regs->tcr_reg= 0x%08x\n\r", i2c.regs->tcr_reg); printk("i2c.regs->csr_reg= 0x%08x\n\r", i2c.regs->csr_reg); printk("i2c.regs->isr_reg= 0x%08x\n\r", i2c.regs->isr_reg); printk("i2c.regs->imr_reg= 0x%08x\n\r", i2c.regs->imr_reg); printk("i2c.regs->cdr_reg= 0x%08x\n\r", i2c.regs->cdr_reg); printk("i2c.regs->tr_reg= 0x%08x\n\r", i2c.regs->tr_reg); printk("i2c.regs->div_reg= 0x%08x\n\r", i2c.regs->div_reg); #endif return 0; } subsys_initcall(i2c_adap_wmt_init); static void i2c_adap_wmt_exit(void) { wmt_i2c_del_bus(&i2c_wmt_ops); i2c_wmt_resource_release(); printk(KERN_INFO "i2c: successfully removed bus\n"); } MODULE_AUTHOR("WonderMedia Technologies, Inc."); MODULE_DESCRIPTION("WMT I2C Adapter Driver"); MODULE_LICENSE("GPL"); module_exit(i2c_adap_wmt_exit);