#include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_RTTKERNEL #include #include #endif static const uint32_t hal_twi_address[] = { SUNXI_TWI0_PBASE, SUNXI_TWI1_PBASE, SUNXI_TWI2_PBASE, SUNXI_TWI3_PBASE, #if !(defined(CONFIG_ARCH_SUN8IW20) || defined(CONFIG_SOC_SUN20IW1)) SUNXI_S_TWI0_PBASE, #endif }; static const uint32_t hal_twi_irq_num[] = { SUNXI_IRQ_TWI0, SUNXI_IRQ_TWI1, SUNXI_IRQ_TWI2, SUNXI_IRQ_TWI3, #if !(defined(CONFIG_ARCH_SUN8IW20) || defined(CONFIG_SOC_SUN20IW1)) SUNXI_IRQ_S_TWI0, #endif }; #if !(defined(CONFIG_ARCH_SUN8IW20) || defined(CONFIG_SOC_SUN20IW1)) static const hal_clk_id_t hal_twi_pclk[] = { HAL_CLK_BUS_APB2, HAL_CLK_BUS_APB2, HAL_CLK_BUS_APB2, HAL_CLK_BUS_APB2, HAL_CLK_BUS_APB2, }; static const hal_clk_id_t hal_twi_mclk[] = { HAL_CLK_PERIPH_TWI0, HAL_CLK_PERIPH_TWI1, HAL_CLK_PERIPH_TWI2, HAL_CLK_PERIPH_TWI3, HAL_CLK_PERIPH_TWI4, }; #endif static const enum REGULATOR_TYPE_ENUM twi_regulator_type = AXP2101_REGULATOR; static const enum REGULATOR_ID_ENUM twi_regulator_id[] = { AXP2101_ID_ALDO2, AXP2101_ID_ALDO2, AXP2101_ID_MAX, AXP2101_ID_MAX, AXP2101_ID_MAX, }; static const int twi_vol[] = { 3300000, 3300000, -1, -1, -1, }; static hal_twi_t hal_twi[TWI_MASTER_MAX]; /* set twi clock * * clk_n: clock divider factor n * clk_m: clock divider factor m */ static void twi_clk_write_reg(hal_twi_t *twi, unsigned int reg_clk, unsigned int clk_m, unsigned int clk_n, unsigned int mask_clk_m, unsigned int mask_clk_n) { const unsigned long base_addr = twi->base_addr; unsigned int reg_val = readl(base_addr + reg_clk); if (reg_clk == TWI_DRIVER_BUSC) { reg_val &= ~(mask_clk_m | mask_clk_n); reg_val |= ((clk_m | (clk_n << 4)) << 8); writel(reg_val, base_addr + reg_clk); } else { reg_val &= ~(mask_clk_m | mask_clk_n); reg_val |= ((clk_m << 3) | clk_n); writel(reg_val, base_addr + reg_clk); } } /* * 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;100KHz or 400KHz * * clk_in: apb clk clock * sclk_req: freqence to set in HZ */ static int twi_set_clock(hal_twi_t *twi, unsigned int reg_clk, unsigned int clk_in, unsigned int sclk_req, unsigned int mask_clk_m, unsigned int mask_clk_n) { unsigned int clk_m = 0; unsigned int clk_n = 0; unsigned int _2_pow_clk_n = 1; unsigned int src_clk = clk_in / 10; unsigned int divider = src_clk / sclk_req; /* 400khz or 100khz */ unsigned int sclk_real = 0; /* the real clock frequency */ if (divider == 0) { clk_m = 1; goto set_clk; } /* * search clk_n and clk_m,from large to small value so * that can quickly find suitable m & n. */ while (clk_n < 8) /* 3bits max value is 8 */ { /* (m+1)*2^n = divider -->m = divider/2^n -1 */ clk_m = (divider / _2_pow_clk_n) - 1; /* clk_m = (divider >> (_2_pow_clk_n>>1))-1 */ while (clk_m < 16) /* 4bits max value is 16 */ { /* src_clk/((m+1)*2^n) */ sclk_real = src_clk / (clk_m + 1) / _2_pow_clk_n; if (sclk_real <= sclk_req) { goto set_clk; } else { clk_m++; } } clk_n++; _2_pow_clk_n *= 2; /* mutilple by 2 */ } set_clk: twi_clk_write_reg(twi, reg_clk, clk_m, clk_n, mask_clk_m, mask_clk_n); return 0; } /*************************************** TWI ENGINE XFER REG CONTROL begin****************************/ /* clear the interrupt flag */ static inline void twi_clear_irq_flag(const unsigned long base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); /* start and stop bit should be 0 */ reg_val |= TWI_CTL_INTFLG; reg_val &= ~(TWI_CTL_STA | TWI_CTL_STP); writel(reg_val, base_addr + TWI_CTL_REG); /* read two more times to make sure that */ /* interrupt flag does really be cleared */ { unsigned int temp; temp = readl(base_addr + TWI_CTL_REG); temp |= readl(base_addr + TWI_CTL_REG); } } /* get data first, then clear flag */ static inline void twi_get_byte(const unsigned long base_addr, unsigned char *buffer) { *buffer = (unsigned char)(TWI_DATA_MASK & readl(base_addr + TWI_DATA_REG)); twi_clear_irq_flag(base_addr); } /* only get data, we will clear the flag when stop */ static inline void twi_get_last_byte(const unsigned long base_addr, unsigned char *buffer) { *buffer = (unsigned char)(TWI_DATA_MASK & readl(base_addr + TWI_DATA_REG)); } /* write data and clear irq flag to trigger send flow */ static inline void twi_put_byte(const unsigned long base_addr, const unsigned char *buffer) { writel((unsigned int)*buffer, base_addr + TWI_DATA_REG); twi_clear_irq_flag(base_addr); } static inline void twi_enable_irq(const unsigned long base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); /* * 1 when enable irq for next operation, set intflag to 0 to prevent * to clear it by a mistake (intflag bit is write-1-to-clear bit) * 2 Similarly, mask START bit and STOP bit to prevent to set it * twice by a mistake (START bit and STOP bit are self-clear-to-0 bits) */ reg_val |= TWI_CTL_INTEN; reg_val &= ~(TWI_CTL_STA | TWI_CTL_STP | TWI_CTL_INTFLG); writel(reg_val, base_addr + TWI_CTL_REG); } static inline void twi_disable_irq(const unsigned long base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val &= ~TWI_CTL_INTEN; reg_val &= ~(TWI_CTL_STA | TWI_CTL_STP | TWI_CTL_INTFLG); writel(reg_val, base_addr + TWI_CTL_REG); } static inline void twi_disable(const unsigned long base_addr, unsigned int reg, unsigned int mask) { unsigned int reg_val = readl(base_addr + reg); reg_val &= ~mask; writel(reg_val, base_addr + reg); TWI_INFO("offset: 0x%x value: 0x%lx", reg, readl(base_addr + reg)); } static inline void twi_enable(const unsigned long base_addr, unsigned int reg, unsigned int mask) { unsigned int reg_val = readl(base_addr + reg); reg_val |= mask; writel(reg_val, base_addr + reg); TWI_INFO("offset: 0x%x value: 0x%lx", reg, readl(base_addr + reg)); } /* trigger start signal, the start bit will be cleared automatically */ static inline void twi_set_start(const unsigned long base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val |= TWI_CTL_STA; reg_val &= ~TWI_CTL_INTFLG; writel(reg_val, base_addr + TWI_CTL_REG); } /* get start bit status, poll if start signal is sent */ static inline unsigned int twi_get_start(const unsigned long base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val >>= 5; return reg_val & 1; } /* trigger stop signal, the stop bit will be cleared automatically */ static inline void twi_set_stop(const unsigned long base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val |= TWI_CTL_STP; reg_val &= ~TWI_CTL_INTFLG; writel(reg_val, base_addr + TWI_CTL_REG); } /* get stop bit status, poll if stop signal is sent */ static inline unsigned int twi_get_stop(const unsigned long base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val >>= 4; return reg_val & 1; } static inline void twi_disable_ack(const unsigned long base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val &= ~TWI_CTL_ACK; reg_val &= ~TWI_CTL_INTFLG; writel(reg_val, base_addr + TWI_CTL_REG); } /* when sending ack or nack, it will send ack automatically */ static inline void twi_enable_ack(const unsigned long base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val |= TWI_CTL_ACK; reg_val &= ~TWI_CTL_INTFLG; writel(reg_val, base_addr + TWI_CTL_REG); } /* get the interrupt flag */ static inline unsigned int twi_query_irq_flag(const unsigned long base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); return (reg_val & TWI_CTL_INTFLG);/* 0x 0000_1000 */ } /* get interrupt status */ static inline unsigned int twi_query_irq_status(const unsigned long base_addr) { unsigned int reg_val = readl(base_addr + TWI_STAT_REG); return (reg_val & TWI_STAT_MASK); } /* Enhanced Feature Register */ static inline void twi_set_efr(const unsigned long base_addr, unsigned int efr) { unsigned int reg_val = readl(base_addr + TWI_EFR_REG); reg_val &= ~TWI_EFR_MASK; efr &= TWI_EFR_MASK; reg_val |= efr; writel(reg_val, base_addr + TWI_EFR_REG); } /* function */ static int twi_start(const unsigned long base_addr, int port) { unsigned int timeout = 0xff; twi_set_start(base_addr); while ((twi_get_start(base_addr) == 1) && (--timeout)) ; if (timeout == 0) { TWI_ERR("[twi%d] START can't sendout!", port); return SUNXI_TWI_FAIL; } return SUNXI_TWI_OK; } static int twi_restart(const unsigned long base_addr, int port) { unsigned int timeout = 0xff; twi_set_start(base_addr); twi_clear_irq_flag(base_addr); while ((twi_get_start(base_addr) == 1) && (--timeout)) ; if (timeout == 0) { TWI_ERR("[twi%d] Restart can't sendout!", port); return SUNXI_TWI_FAIL; } return SUNXI_TWI_OK; } static int twi_stop(const unsigned long base_addr, int port) { unsigned int timeout = 0xff; twi_set_stop(base_addr); //unsigned int reg_val = readl(base_addr + TWI_CTL_REG); twi_clear_irq_flag(base_addr); twi_get_stop(base_addr);/* it must delay 1 nop to check stop bit */ while ((twi_get_stop(base_addr) == 1) && (--timeout)) ; if (timeout == 0) { TWI_ERR("[twi%d] STOP can't sendout!", port); return SUNXI_TWI_TFAIL; } //twi_clear_irq_flag(base_addr); timeout = 0xff; while ((readl(base_addr + TWI_STAT_REG) != TWI_STAT_IDLE) && (--timeout)) ; if (timeout == 0) { TWI_ERR("[twi%d] twi state(0x%0lx) isn't idle(0xf8)", port, readl(base_addr + TWI_STAT_REG)); return SUNXI_TWI_TFAIL; } timeout = 0xff; while ((readl(base_addr + TWI_LCR_REG) != TWI_LCR_IDLE_STATUS && readl(base_addr + TWI_LCR_REG) != TWI_LCR_NORM_STATUS) && (--timeout)) ; if (timeout == 0) { TWI_ERR("[twi%d] twi lcr(0x%0lx) isn't idle(0x3a)", port, readl(base_addr + TWI_LCR_REG)); return SUNXI_TWI_TFAIL; } //twi_clear_irq_flag(base_addr); TWI_INFO("twi stop end"); return SUNXI_TWI_OK; } /* get SDA state */ static unsigned int twi_get_sda(const unsigned long base_addr) { unsigned int status = 0; status = TWI_LCR_SDA_STATE_MASK & readl(base_addr + TWI_LCR_REG); status >>= 4; return (status & 0x1); } /* set SCL level(high/low), only when SCL enable */ static void twi_set_scl(const unsigned long base_addr, unsigned int hi_lo) { unsigned int reg_val = readl(base_addr + TWI_LCR_REG); reg_val &= ~TWI_LCR_SCL_CTL; hi_lo &= 0x01; reg_val |= (hi_lo << 3); writel(reg_val, base_addr + TWI_LCR_REG); } /* enable SDA or SCL */ static void twi_enable_lcr(const unsigned long base_addr, unsigned int sda_scl) { unsigned int reg_val = readl(base_addr + TWI_LCR_REG); sda_scl &= 0x01; if (sda_scl) { reg_val |= TWI_LCR_SCL_EN; /* enable scl line control */ } else { reg_val |= TWI_LCR_SDA_EN; /* enable sda line control */ } writel(reg_val, base_addr + TWI_LCR_REG); } /* disable SDA or SCL */ static void twi_disable_lcr(const unsigned long base_addr, unsigned int sda_scl) { unsigned int reg_val = readl(base_addr + TWI_LCR_REG); sda_scl &= 0x01; if (sda_scl) { reg_val &= ~TWI_LCR_SCL_EN; /* disable scl line control */ } else { reg_val &= ~TWI_LCR_SDA_EN; /* disable sda line control */ } writel(reg_val, base_addr + TWI_LCR_REG); } /* send 9 clock to release sda */ static int twi_send_clk_9pulse(const unsigned long base_addr, int port) { int twi_scl = 1; int low = 0; int high = 1; int cycle = 0; int num = 0; unsigned char status; /* enable scl control */ twi_enable_lcr(base_addr, twi_scl); while (cycle < 9) { if (twi_get_sda(base_addr) && twi_get_sda(base_addr) && twi_get_sda(base_addr)) { break; } /* twi_scl -> low */ twi_set_scl(base_addr, low); for (num = 1000; num > 0; num--); /* twi_scl -> high */ twi_set_scl(base_addr, high); for (num = 1000; num > 0; num--); cycle++; } if (twi_get_sda(base_addr)) { twi_disable_lcr(base_addr, twi_scl); status = SUNXI_TWI_OK; } else { TWI_ERR("[twi%d] SDA is still Stuck Low, failed.", port); twi_disable_lcr(base_addr, twi_scl); status = SUNXI_TWI_FAIL; } return status; } /*************************************** TWI DRV XFER REG CONTROL begin****************************/ #if 0 /* set twi clock * * clk_n: clock divider factor n * clk_m: clock divider factor m */ static void twi_clk_write_reg(const uint32_t base_addr, uint32_t sclk_freq, uint8_t clk_m, uint8_t clk_n) { uint32_t reg_val = readl(base_addr + TWI_DRIVER_BUSC); #if defined(CONFIG_ARCH_SUN50IW10) uint32_t duty; #endif TWI_INFO("reg_clk = 0x%x, clk_m = %u, clk_n = %u," "mask_clk_m = %x, mask_clk_n = %x", reg_clk, clk_m, clk_n, mask_clk_m, mask_clk_n); reg_val &= ~(TWI_DRV_CLK_M | TWI_DRV_CLK_N); reg_val |= ((clk_m | (clk_n << 4)) << 8); #if defined(CONFIG_ARCH_SUN50IW10) duty = TWI_DRV_CLK_DUTY; if (sclk_freq > STANDDARD_FREQ) { reg_val |= duty; } else { reg_val &= ~duty; } #endif writel(reg_val, base_addr + TWI_DRIVER_BUSC); } /* * 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;100KHz or 400KHz * * clk_in: apb clk clock * sclk_freq: freqence to set in HZ */ static int32_t twi_set_clock(twi_port_t twi_port, uint32_t clk_in, uint32_t sclk_freq) { const uint32_t base_addr = g_twi_regbase[twi->port]; uint8_t clk_m = 0, clk_n = 0, _2_pow_clk_n = 1; uint32_t src_clk = clk_in / 10; uint32_t divider = src_clk / sclk_freq; /* 400khz or 100khz */ uint32_t sclk_real = 0; /* the real clock frequency */ if (divider == 0) { clk_m = 1; goto set_clk; } /* * search clk_n and clk_m,from large to small value so * that can quickly find suitable m & n. */ while (clk_n < 8) /* 3bits max value is 8 */ { /* (m+1)*2^n = divider -->m = divider/2^n -1 */ clk_m = (divider / _2_pow_clk_n) - 1; /* clk_m = (divider >> (_2_pow_clk_n>>1))-1 */ while (clk_m < 16) /* 4bits max value is 16 */ { /* src_clk/((m+1)*2^n) */ sclk_real = src_clk / (clk_m + 1) / _2_pow_clk_n; if (sclk_real <= sclk_freq) { goto set_clk; } else { clk_m++; } } clk_n++; _2_pow_clk_n *= 2; /* mutilple by 2 */ } set_clk: twi_clk_write_reg(base_addr, sclk_freq, clk_m, clk_n); return 0; } #endif static uint32_t twi_drv_query_irq_status(const unsigned long base_addr) { uint32_t reg_val = readl(base_addr + TWI_DRIVER_INTC); return (reg_val & TWI_DRV_STAT_MASK); } static void twi_drv_clear_irq_flag(uint32_t pending_bit, const unsigned long base_addr) { uint32_t reg_val = readl(base_addr + TWI_DRIVER_INTC); pending_bit &= TWI_DRV_STAT_MASK; reg_val |= pending_bit; writel(reg_val, base_addr + TWI_DRIVER_INTC); } static void twi_clear_pending(const unsigned long base_addr) { uint32_t reg_val = readl(base_addr + TWI_DRIVER_INTC); reg_val |= TWI_DRV_STAT_MASK; writel(reg_val, base_addr + TWI_DRIVER_INTC); } /* start TWI transfer */ static void twi_start_xfer(const unsigned long base_addr) { uint32_t reg_val = readl(base_addr + TWI_DRIVER_CTRL); reg_val |= START_TRAN; writel(reg_val, base_addr + TWI_DRIVER_CTRL); } /* * send DMA RX Req when the data byte number in RECV_FIFO reaches RX_TRIG * or Read Packet Tansmission completed with RECV_FIFO not empty */ static void twi_set_rx_trig_level(uint32_t val, const unsigned long base_addr) { uint32_t mask = TRIG_MASK; uint32_t reg_val = readl(base_addr + TWI_DRIVER_DMAC); val = (val & mask) << 16; reg_val &= ~(mask << 16); reg_val |= val; writel(reg_val, base_addr + TWI_DRIVER_DMAC); } /* bytes be send as slave device reg address */ static void twi_set_packet_addr_byte(uint32_t val, const unsigned long base_addr) { uint32_t mask = ADDR_BYTE; uint32_t reg_val = readl(base_addr + TWI_DRIVER_FMT); reg_val &= ~mask; val = (val << 16) & mask; reg_val |= val; writel(reg_val, base_addr + TWI_DRIVER_FMT); } /* bytes be send/received as data */ static void twi_set_packet_data_byte(uint32_t val, const unsigned long base_addr) { uint32_t mask = DATA_BYTE; uint32_t reg_val = readl(base_addr + TWI_DRIVER_FMT); reg_val &= ~mask; val &= mask; reg_val |= val; writel(reg_val, base_addr + TWI_DRIVER_FMT); } #if 0 /* interval between each packet in 32*Fscl cycles */ static void twi_set_packet_interval(uint32_t val, const uint32_t base_addr) { uint32_t mask = INTERVAL_MASK; uint32_t reg_val = readl(base_addr + TWI_DRIVER_CFG); reg_val &= ~mask; val <<= 16; val &= mask; reg_val |= val; writel(reg_val, base_addr + TWI_DRIVER_CFG); } #endif /* FIFO data be transmitted as PACKET_CNT packets in current format */ static void twi_set_packet_cnt(uint32_t val, const unsigned long base_addr) { uint32_t mask = PACKET_MASK; uint32_t reg_val = readl(base_addr + TWI_DRIVER_CFG); reg_val &= ~mask; val &= mask; reg_val |= val; writel(reg_val, base_addr + TWI_DRIVER_CFG); } /* do not send slave_id +W */ static void twi_enable_read_tran_mode(const unsigned long base_addr) { uint32_t mask = READ_TRAN; uint32_t reg_val = readl(base_addr + TWI_DRIVER_CTRL); reg_val |= mask; writel(reg_val, base_addr + TWI_DRIVER_CTRL); } /* send slave_id + W */ static void twi_disable_read_tran_mode(const unsigned long base_addr) { uint32_t mask = READ_TRAN; uint32_t reg_val = readl(base_addr + TWI_DRIVER_CTRL); reg_val &= ~mask; writel(reg_val, base_addr + TWI_DRIVER_CTRL); } static inline void twi_soft_reset(const unsigned long base_addr, unsigned int reg, unsigned int mask) { unsigned int reg_val = readl(base_addr + reg); reg_val |= mask; writel(reg_val, base_addr + reg); } static void twi_enable_tran_irq(uint32_t bitmap, const unsigned long base_addr) { uint32_t reg_val = readl(base_addr + TWI_DRIVER_INTC); reg_val |= bitmap; reg_val &= ~TWI_DRV_STAT_MASK; writel(reg_val, base_addr + TWI_DRIVER_INTC); } static void twi_disable_tran_irq(uint32_t bitmap, const unsigned long base_addr) { uint32_t reg_val = readl(base_addr + TWI_DRIVER_INTC); reg_val &= ~bitmap; reg_val &= ~TWI_DRV_STAT_MASK; writel(reg_val, base_addr + TWI_DRIVER_INTC); } static void twi_enable_dma_irq(uint32_t bitmap, const unsigned long base_addr) { uint32_t reg_val = readl(base_addr + TWI_DRIVER_DMAC); bitmap &= TWI_DRQEN_MASK; reg_val |= bitmap; writel(reg_val, base_addr + TWI_DRIVER_DMAC); } static void twi_disable_dma_irq(uint32_t bitmap, const unsigned long base_addr) { uint32_t reg_val = readl(base_addr + TWI_DRIVER_DMAC); bitmap &= TWI_DRQEN_MASK; reg_val &= ~bitmap; writel(reg_val, base_addr + TWI_DRIVER_DMAC); } static void twi_slave_addr(const unsigned long base_addr, twi_msg_t *msgs) { uint32_t val = 0, cmd = 0; /* read, default value is write */ if (msgs->flags & TWI_M_RD) { cmd = SLV_RD_CMD; } if (msgs->flags & TWI_M_TEN) { /* SLV_ID | CMD | SLV_ID_X */ val = ((0x78 | ((msgs->addr >> 8) & 0x03)) << 9) | cmd | (msgs->addr & 0xff); } else { val = ((msgs->addr & 0x7f) << 9) | cmd; } writel(val, base_addr + TWI_DRIVER_SLV); } /* the number of data in SEND_FIFO */ static int32_t twi_query_txfifo(const unsigned long base_addr) { uint32_t reg_val; reg_val = readl(base_addr + TWI_DRIVER_FIFOC) & SEND_FIFO_CONT; return reg_val; } /* the number of data in RECV_FIFO */ static int32_t twi_query_rxfifo(const unsigned long base_addr) { uint32_t reg_val; reg_val = readl(base_addr + TWI_DRIVER_FIFOC) & RECV_FIFO_CONT; reg_val >>= 16; return reg_val; } static void twi_clear_txfifo(const unsigned long base_addr) { uint32_t reg_val; reg_val = readl(base_addr + TWI_DRIVER_FIFOC); reg_val |= SEND_FIFO_CLEAR; writel(reg_val, base_addr + TWI_DRIVER_FIFOC); } static void twi_clear_rxfifo(const unsigned long base_addr) { uint32_t reg_val; reg_val = readl(base_addr + TWI_DRIVER_FIFOC); reg_val |= RECV_FIFO_CLEAR; writel(reg_val, base_addr + TWI_DRIVER_FIFOC); } static int twi_send_msgs(hal_twi_t *twi, twi_msg_t *msgs) { uint16_t i; uint8_t time = 0xff; TWI_INFO("twi[%d] msgs->len = %d", twi->port, msgs->len); for (i = 0; i < msgs->len; i++) { while ((twi_query_txfifo(twi->base_addr) >= MAX_FIFO) && time--) ; if (time) { hal_writeb(msgs->buf[i], twi->base_addr + TWI_DRIVER_SENDF); } else { TWI_ERR("[twi%d] SEND FIFO overflow. timeout", twi->port); return SUNXI_TWI_FAIL; } } return SUNXI_TWI_OK; } static uint32_t twi_recv_msgs(hal_twi_t *twi, twi_msg_t *msgs) { uint16_t i; uint8_t time = 0xff; TWI_INFO("twi[%d] msgs->len = %d", twi->port, msgs->len); for (i = 0; i < msgs->len; i++) { while (!twi_query_rxfifo(twi->base_addr) && time--) ; if (time) { msgs->buf[i] = hal_readb(twi->base_addr + TWI_DRIVER_RECVF); } else { return 0; } } return msgs->len; } /************************ TWI DRV XFER REG CONTROL end*************************/ static void twi_dma_callback(void *para) { hal_twi_t *twi = (hal_twi_t *)para; int hal_sem_ret; hal_sem_ret = hal_sem_post(twi->dma_complete); if (hal_sem_ret != 0) { TWI_ERR("[twi%d] twi dma driver xfer timeout (dev addr:0x%x)\n", twi->port, twi->msgs->addr); return; } } static int twi_dma_xfer(hal_twi_t *twi, char *buf, int len, enum dma_transfer_direction dir) { struct sunxi_dma_chan *dma_chan = twi->dma_chan; struct dma_slave_config slave_config; int hal_sem_ret; hal_dcache_clean((unsigned long)buf, len); hal_dma_callback_install(dma_chan, twi_dma_callback, twi); if (dir == DMA_MEM_TO_DEV) { slave_config.direction = DMA_MEM_TO_DEV; slave_config.src_addr = (unsigned long)buf; slave_config.dst_addr = twi->base_addr + TWI_DRIVER_SENDF; slave_config.slave_id = sunxi_slave_id(DRQDST_TWI0_TX + twi->port, DRQSRC_SDRAM); } else { slave_config.direction = DMA_DEV_TO_MEM; slave_config.src_addr = twi->base_addr + TWI_DRIVER_RECVF; slave_config.dst_addr = (unsigned long)buf; slave_config.slave_id = sunxi_slave_id(DRQDST_SDRAM, DRQSRC_TWI0_RX + twi->port); } slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; slave_config.src_maxburst = DMA_SLAVE_BURST_16; slave_config.dst_maxburst = DMA_SLAVE_BURST_16; hal_dma_slave_config(dma_chan, &slave_config); if (dir == DMA_MEM_TO_DEV) { hal_dma_prep_device(dma_chan, slave_config.dst_addr, slave_config.src_addr, len, DMA_MEM_TO_DEV); } else { hal_dma_prep_device(dma_chan, slave_config.dst_addr, slave_config.src_addr, len, DMA_DEV_TO_MEM); } //hal_dma_cyclic_callback_install(dma_chan, twi_dma_callback, twi); hal_dma_start(dma_chan); hal_sem_ret = hal_sem_timedwait(twi->dma_complete, twi->timeout * 100); if (hal_sem_ret != 0) { TWI_ERR("[twi%d] twi dma driver xfer timeout (dev addr:0x%x)\n", twi->port, twi->msgs->addr); return -1; } TWI_INFO("[twi%d] twi driver dma xfer success.\n", twi->port); TWI_ERR("[twi%d] twi driver dma xfer success.\n", twi->port); return 0; } static int twi_write(hal_twi_t *twi, twi_msg_t *msgs) { twi->msgs = msgs; twi_slave_addr(twi->base_addr, msgs); if (msgs->len == 1) { twi_set_packet_addr_byte(0, twi->base_addr); twi_set_packet_data_byte(msgs->len, twi->base_addr); } else { twi_set_packet_addr_byte(1, twi->base_addr); twi_set_packet_data_byte(msgs->len - 1, twi->base_addr); } twi_set_packet_cnt(1, twi->base_addr); twi_clear_pending(twi->base_addr); twi_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, twi->base_addr); twi_start_xfer(twi->base_addr); return twi_send_msgs(twi, msgs); } static int32_t twi_dma_write(hal_twi_t *twi, twi_msg_t *msgs) { int32_t ret = 0; const uint32_t base_addr = twi->base_addr; twi->msgs = msgs; twi_slave_addr(base_addr, msgs); twi_set_packet_addr_byte(1, base_addr); twi_set_packet_data_byte(msgs->len - 1, base_addr); twi_set_packet_cnt(1, base_addr); twi_clear_pending(base_addr); twi_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, base_addr); twi_enable_dma_irq(DMA_TX, base_addr); twi_start_xfer(base_addr); ret = twi_dma_xfer(twi, msgs->buf, msgs->len, DMA_MEM_TO_DEV); return ret; } static int twi_read(hal_twi_t *twi, twi_msg_t *msgs, int32_t num) { twi_msg_t *wmsgs = NULL, *rmsgs = NULL; if (num == 1) { wmsgs = NULL; rmsgs = msgs; } else if (num == 2) { wmsgs = msgs; rmsgs = msgs + 1; } else { TWI_ERR("msg num err"); return -1; } TWI_INFO("rmsgs->len : %d", rmsgs->len); twi->msgs = rmsgs; twi_slave_addr(twi->base_addr, rmsgs); twi_set_packet_cnt(1, twi->base_addr); twi_set_packet_data_byte(rmsgs->len, twi->base_addr); if (rmsgs->len > MAX_FIFO) { twi_set_rx_trig_level(MAX_FIFO, twi->base_addr); } else { twi_set_rx_trig_level(rmsgs->len, twi->base_addr); } if (twi_query_rxfifo(twi->base_addr)) { twi_clear_rxfifo(twi->base_addr); } twi_clear_pending(twi->base_addr); twi_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, twi->base_addr); twi_start_xfer(twi->base_addr); if (wmsgs) { return twi_send_msgs(twi, wmsgs); } return 0; } static int32_t twi_dma_read(hal_twi_t *twi, twi_msg_t *msgs, int32_t num) { int32_t ret = 0; twi_msg_t *wmsgs, *rmsgs; const uint32_t base_addr = twi->base_addr; if (num == 1) { wmsgs = NULL; rmsgs = msgs; } else if (num == 2) { wmsgs = msgs; rmsgs = msgs + 1; } twi->msgs = rmsgs; twi_slave_addr(base_addr, rmsgs); twi_set_packet_data_byte(rmsgs->len, base_addr); twi_set_packet_cnt(1, base_addr); twi_set_rx_trig_level(MAX_FIFO / 2, base_addr); if (twi_query_rxfifo(base_addr)) { twi_clear_rxfifo(base_addr); } twi_clear_pending(base_addr); twi_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, base_addr); twi_enable_dma_irq(DMA_RX, base_addr); twi_start_xfer(base_addr); if (wmsgs) { twi_send_msgs(twi, wmsgs); } ret = twi_dma_xfer(twi, rmsgs->buf, rmsgs->len, DMA_DEV_TO_MEM); return ret; } /*************************************** TWI DRV XFER REG CONTROL end****************************/ static int hal_twi_drv_complete(hal_twi_t *twi) { int hal_sem_ret; hal_sem_ret = hal_sem_timedwait(twi->hal_sem, twi->timeout * 100); if (hal_sem_ret < 0) { TWI_ERR("[twi%d] twi driver xfer timeout (dev addr:0x%x)", twi->port, twi->msgs->addr); //dump_reg(twi, 0x200, 0x20); twi_disable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT | RX_REQ_INT | TX_REQ_INT, twi->base_addr); twi_disable_dma_irq(DMA_TX | DMA_RX, twi->base_addr); return SUNXI_TWI_FAIL; } else if (twi->result == RESULT_ERR) { TWI_ERR("[twi%d]twi drv xfer incomplete xfer" "(status: 0x%lx, dev addr: 0x%x)", twi->port, twi->msgs_idx, twi->msgs->addr); twi_disable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT | RX_REQ_INT | TX_REQ_INT, twi->base_addr); twi_disable_dma_irq(DMA_TX | DMA_RX, twi->base_addr); return SUNXI_TWI_FAIL; } TWI_INFO("twi drv xfer complete"); // twin_lock_irqsave(&twi->lock, flags); twi->result = RESULT_COMPLETE; // twin_unlock_irqrestore(&twi->lock, flags); return SUNXI_TWI_OK; } static int hal_twi_engine_complete(hal_twi_t *twi, int code) { int ret = SUNXI_TWI_OK; int hal_sem_ret; twi->msgs = NULL; twi->msgs_num = 0; twi->msgs_ptr = 0; twi->status = TWI_XFER_IDLE; /* twi->msgs_idx store the information */ if (code == SUNXI_TWI_FAIL) { TWI_ERR("[twi%d] Maybe Logic Error, debug it!", twi->port); twi->msgs_idx = code; ret = SUNXI_TWI_FAIL; twi->result = RESULT_ERR; } else if (code != SUNXI_TWI_OK) { twi->msgs_idx = code; ret = SUNXI_TWI_FAIL; twi->result = RESULT_ERR; } hal_sem_ret = hal_sem_post(twi->hal_sem); if (hal_sem_ret != 0) { ret = SUNXI_TWI_FAIL; TWI_ERR(" evdev give hal_semaphore err"); } TWI_INFO("code=%d, complete", twi->msgs_idx); return ret; } /* **************************************************************************** * * FunctionName: hal_i2c_addr_byte * * Description: * 7bits addr: 7-1bits addr+0 bit r/w * 10bits addr: 1111_11xx_xxxx_xxxx-->1111_0xx_rw,xxxx_xxxx * send the 7 bits addr,or the first part of 10 bits addr * Parameters: * * * Return value: * ?? * Notes: * **************************************************************************** */ static void hal_twi_addr_byte(hal_twi_t *twi) { unsigned char addr = 0; unsigned char tmp = 0; if (twi->msgs[twi->msgs_idx].flags & TWI_M_TEN) { /* 0111_10xx,ten bits address--9:8bits */ tmp = 0x78 | (((twi->msgs[twi->msgs_idx].addr) >> 8) & 0x03); addr = tmp << 1; /*1111_0xx0*/ /* how about the second part of ten bits addr? */ /* Answer: deal at twi_core_process() */ } else { addr = (twi->msgs[twi->msgs_idx].addr & 0x7f) << 1; }/* 7-1bits addr, xxxx_xxx0 */ /* read, default value is write */ if (twi->msgs[twi->msgs_idx].flags & TWI_M_RD) { addr |= 1; } if (twi->msgs[twi->msgs_idx].flags & TWI_M_TEN) { TWI_INFO("[twi%d] first part of 10bits = 0x%x", twi->port, addr); } else { TWI_INFO("[twi%d] 7bits+r/w = 0x%x", twi->port, addr); } /* send 7bits+r/w or the first part of 10bits */ twi_put_byte(twi->base_addr, &addr); } static int hal_twi_core_process(hal_twi_t *twi) { const uint32_t base_addr = twi->base_addr; int ret = SUNXI_TWI_OK; int err_code = 0; unsigned char state = 0; unsigned char tmp = 0; state = twi_query_irq_status(base_addr); //twin_lock_irqsave(&twi->lock, flags); TWI_INFO("[twi%d][slave address = (0x%x), state = (0x%x)]", twi->port, twi->msgs->addr, state); if (twi->msgs == NULL) { TWI_ERR("[twi%d] twi message is NULL, err_code = 0xfe", twi->port); err_code = 0xfe; goto msg_null; } switch (state) { case 0xf8: /* On reset or stop the bus is idle, use only at poll method */ err_code = 0xf8; goto err_out; case 0x08: /* A START condition has been transmitted */ case 0x10: /* A repeated start condition has been transmitted */ hal_twi_addr_byte(twi);/* send slave address */ break; case 0xd8: /* second addr has transmitted, ACK not received! */ case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */ err_code = 0x20; goto err_out; case 0x18: /* SLA+W has been transmitted; ACK has been received */ /* if any, send second part of 10 bits addr */ if (twi->msgs[twi->msgs_idx].flags & TWI_M_TEN) { /* the remaining 8 bits of address */ tmp = twi->msgs[twi->msgs_idx].addr & 0xff; twi_put_byte(base_addr, &tmp); /* case 0xd0: */ break; } goto send_data; /* for 7 bit addr, then directly send data byte--case 0xd0: */ case 0xd0: /* second addr has transmitted,ACK received! */ case 0x28: /* Data byte in DATA REG has been transmitted; */ /* ACK has been received */ /* after send register address then START send write data */ send_data: if (twi->msgs_ptr < twi->msgs[twi->msgs_idx].len) { twi_put_byte(base_addr, &(twi->msgs[twi->msgs_idx].buf[twi->msgs_ptr])); twi->msgs_ptr++; break; } twi->msgs_idx++; /* the other msg */ twi->msgs_ptr = 0; if (twi->msgs_idx == twi->msgs_num) { err_code = SUNXI_TWI_OK;/* Success,wakeup */ goto ok_out; } else if (twi->msgs_idx < twi->msgs_num) { /* for restart pattern, read spec, two msgs */ ret = twi_restart(base_addr, twi->port); if (ret == SUNXI_TWI_FAIL) { TWI_ERR("[twi%d] twi restart fail", twi->port); err_code = SUNXI_TWI_FAIL; goto err_out;/* START can't sendout */ } } else { err_code = SUNXI_TWI_FAIL; goto err_out; } break; case 0x30: /* Data byte in TWIDAT has been transmitted; */ /* NOT ACK has been received */ err_code = 0x30; /*err,wakeup the thread*/ goto err_out; case 0x38: /* Arbitration lost during SLA+W, SLA+R or data bytes */ err_code = 0x38; /*err,wakeup the thread*/ goto err_out; case 0x40: /* SLA+R has been transmitted; ACK has been received */ /* with Restart,needn't to send second part of 10 bits addr */ /* refer-"TWI-SPEC v2.1" */ /* enable A_ACK need it(receive data len) more than 1. */ if (twi->msgs[twi->msgs_idx].len > 1) { /* send register addr complete,then enable the A_ACK */ /* and get ready for receiving data */ twi_enable_ack(base_addr); twi_clear_irq_flag(base_addr);/* jump to case 0x50 */ } else if (twi->msgs[twi->msgs_idx].len == 1) { twi_clear_irq_flag(base_addr);/* jump to case 0x58 */ } break; case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */ err_code = 0x48; /*err,wakeup the thread*/ goto err_out; case 0x50: /* Data bytes has been received; ACK has been transmitted */ /* receive first data byte */ if (twi->msgs_ptr < twi->msgs[twi->msgs_idx].len) { /* more than 2 bytes, the last byte need not to send ACK */ if ((twi->msgs_ptr + 2) == twi->msgs[twi->msgs_idx].len) /* last byte no ACK */ { twi_disable_ack(base_addr); } /* get data then clear flag,then next data coming */ twi_get_byte(base_addr, &twi->msgs[twi->msgs_idx].buf[twi->msgs_ptr]); twi->msgs_ptr++; break; } /* err process, the last byte should be @case 0x58 */ err_code = SUNXI_TWI_FAIL;/* err, wakeup */ goto err_out; case 0x58: /* Data byte has been received; NOT ACK has been transmitted */ /* received the last byte */ if (twi->msgs_ptr == twi->msgs[twi->msgs_idx].len - 1) { twi_get_last_byte(base_addr, &twi->msgs[twi->msgs_idx].buf[twi->msgs_ptr]); twi->msgs_idx++; twi->msgs_ptr = 0; if (twi->msgs_idx == twi->msgs_num) { /* succeed,wakeup the thread */ err_code = SUNXI_TWI_OK; goto ok_out; } else if (twi->msgs_idx < twi->msgs_num) { /* repeat start */ ret = twi_restart(base_addr, twi->port); if (ret == SUNXI_TWI_FAIL) /* START fail */ { TWI_ERR("[twi%d] twi restart fail", twi->port); err_code = SUNXI_TWI_FAIL; goto err_out; } break; } } else { err_code = 0x58; goto err_out; } break; case 0x00: /* Bus error during master or slave mode due to illegal level condition */ err_code = 0xff; goto err_out; default: err_code = state; goto err_out; } //twin_unlock_irqrestore(&twi->lock, flags); return ret; ok_out: err_out: if (twi_stop(base_addr, twi->port) == SUNXI_TWI_FAIL) { TWI_ERR("[twi%d] STOP failed!", twi->port); } msg_null: ret = hal_twi_engine_complete(twi, err_code);/* wake up */ //twin_unlock_irqrestore(&twi->lock, flags); return ret; } static int hal_twi_drv_core_process(hal_twi_t *twi) { int ret = SUNXI_TWI_OK; uint32_t status, code = 0; int hal_sem_ret, taskwoken = 0; // twin_lock_irqsave(&twi->lock, flags); status = twi_drv_query_irq_status(twi->base_addr); twi_drv_clear_irq_flag(status, twi->base_addr); if (status & TRAN_COM_PD) { TWI_INFO("twi drv complete"); twi_disable_tran_irq(TRAN_COM_INT, twi->base_addr); twi->result = RESULT_COMPLETE; if ((status & RX_REQ_PD) && (twi->msgs->len < DMA_THRESHOLD)) { twi_recv_msgs(twi, twi->msgs); } goto ok_out; } if (status & TRAN_ERR_PD) { TWI_ERR("twi drv error"); twi_disable_tran_irq(TRAN_ERR_INT, twi->base_addr); code = readl(twi->base_addr + TWI_DRIVER_CTRL); code = (code & TWI_DRV_STA) >> 16; TWI_ERR("err code : %0lx", code); switch (code) { case 0x00: TWI_ERR("[twi%d] bus error", twi->port); break; case 0x01: TWI_ERR("[twi%d] Timeout when sending 9th SCL clk", twi->port); break; case 0x20: TWI_ERR("[twi%d] Address + Write bit transmitted," "ACK not received", twi->port); break; case 0x30: TWI_ERR("[twi%d] Data byte transmitted in master mode," "ACK not received", twi->port); break; case 0x38: TWI_ERR("[twi%d] Arbitration lost in address" "or data byte", twi->port); break; case 0x48: TWI_ERR("[twi%d] Address + Read bit transmitted," "ACK not received", twi->port); break; case 0x58: TWI_ERR("[twi%d] Data byte received in master mode," "ACK not received", twi->port); break; default: TWI_ERR("[twi%d] unknown error", twi->port); break; } goto err_out; } err_out: twi->msgs_idx = code; twi->result = RESULT_ERR; TWI_ERR("packet transmission failed , status : 0x%0lx", code); ok_out: //wake up hal_sem_ret = hal_sem_post(twi->hal_sem); if (hal_sem_ret != 0) { ret = SUNXI_TWI_FAIL; TWI_ERR(" evdev give hal_semaphore err"); } return ret; // twin_unlock_irqrestore(&twi->lock, flags); } static irqreturn_t hal_twi_handler(int irq, void *dev) { hal_twi_t *twi = (hal_twi_t *)dev; if (twi->twi_drv_used) { hal_twi_drv_core_process(twi); } else { if (!twi_query_irq_flag(twi->base_addr)) { TWI_ERR("unknown interrupt!"); return 0; } /* disable irq */ twi_disable_irq(twi->base_addr); /* twi core process */ hal_twi_core_process(twi); /* * enable irq only when twi is transferring, * otherwise disable irq */ if (twi->status != TWI_XFER_IDLE) { twi_enable_irq(twi->base_addr); } } return 0; } /** * twi_do_xfer - twi driver transmission control */ static int hal_twi_drv_do_xfer(hal_twi_t *twi, struct twi_msg *msgs, int num) { // uint64_t flags = 0; int ret = -1; // twin_lock_irqsave(&twi->lock, flags); // twi->result = 0; // twin_unlock_irqrestore(&twi->lock, flags); twi_clear_pending(twi->base_addr); twi_disable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT | RX_REQ_INT | TX_REQ_INT, twi->base_addr); twi_disable_dma_irq(DMA_TX | DMA_RX, twi->base_addr); if (twi_query_txfifo(twi->base_addr)) { twi_clear_txfifo(twi->base_addr); } if (num == 1) { if (msgs->flags & TWI_M_RD) { TWI_INFO("1 msgs read "); /* 1 msgs read */ twi_enable_read_tran_mode(twi->base_addr); twi_set_packet_addr_byte(0, twi->base_addr); if (twi->dma_chan && (msgs->len >= DMA_THRESHOLD)) { TWI_INFO("twi[%d] master dma read", twi->port); ret = twi_dma_read(twi, msgs, num); } else { TWI_INFO("twi[%d] master cpu read", twi->port); ret = twi_read(twi, msgs, num); } } else { /* 1 msgs write */ twi_disable_read_tran_mode(twi->base_addr); if (twi->dma_chan && (msgs->len >= DMA_THRESHOLD)) { TWI_INFO("twi[%d] master dma write\n", twi->port); ret = twi_dma_write(twi, msgs); } else { TWI_INFO("twi[%d] master cpu write\n", twi->port); ret = twi_write(twi, msgs); } } } else if ((num == 2) && ((msgs + 1)->flags & TWI_M_RD)) { /* 2 msgs read */ TWI_INFO("2 msgs read"); twi_disable_read_tran_mode(twi->base_addr); twi_set_packet_addr_byte(msgs->len, twi->base_addr); if (twi->dma_chan && ((msgs + 1)->len >= DMA_THRESHOLD)) { TWI_INFO("twi[%d] master dma read\n", twi->port); ret = twi_dma_read(twi, msgs, num); } else { TWI_INFO("twi[%d] master cpu read\n", twi->port); ret = twi_read(twi, msgs, num); } } if (ret) { return ret; } return hal_twi_drv_complete(twi); } static int hal_twi_engine_do_xfer(hal_twi_t *twi, twi_msg_t *msgs, int num) { int ret; const uint32_t base_addr = twi->base_addr; int hal_sem_ret; twi_soft_reset(base_addr, TWI_SRST_REG, TWI_SRST_SRST); //udelay(100); /* test the bus is free,already protect by the hal_semaphore at DEV layer */ while (twi_query_irq_status(base_addr) != TWI_STAT_IDLE && twi_query_irq_status(base_addr) != TWI_STAT_BUS_ERR && twi_query_irq_status(base_addr) != TWI_STAT_ARBLOST_SLAR_ACK) { TWI_ERR("[twi%d] bus is busy, status = %x", twi->port, twi_query_irq_status(base_addr)); if (twi_send_clk_9pulse(base_addr, twi->port) != SUNXI_TWI_OK) { ret = SUNXI_TWI_RETRY; goto out; } else { break; } } /* may conflict with xfer_complete */ //twin_lock_irqsave(&twi->lock, flags); twi->msgs = msgs; twi->msgs_num = num; twi->msgs_ptr = 0; twi->msgs_idx = 0; twi_enable_irq(base_addr); /* enable irq */ twi_disable_ack(base_addr); /* disabe ACK */ /* set the special function register,default:0. */ twi_set_efr(base_addr, 0); //twin_unlock_irqrestore(&twi->lock, flags); /* START signal, needn't clear int flag */ twi->status = TWI_XFER_START; ret = twi_start(base_addr, twi->port); if (ret == SUNXI_TWI_FAIL) { TWI_ERR("[twi%d] twi start fail", twi->port); twi_soft_reset(base_addr, TWI_SRST_REG, TWI_SRST_SRST); twi_disable_irq(base_addr); /* disable irq */ ret = SUNXI_TWI_RETRY; goto out; } twi->status = TWI_XFER_RUNNING; /* sleep and wait,do the transfer at interrupt handler,timeout = 5*HZ */ hal_sem_ret = hal_sem_timedwait(twi->hal_sem, twi->timeout * 100); /* return code,if(msgs_idx == num) succeed */ ret = twi->msgs_idx; if (hal_sem_ret != 0) { TWI_ERR("[twi%d] xfer timeout (dev addr:0x%x)", twi->port, msgs->addr); //twin_lock_irqsave(&twi->lock, flags); twi->msgs = NULL; //twin_unlock_irqrestore(&twi->lock, flags); ret = SUNXI_TWI_FAIL; } else if (ret != num) { TWI_ERR("[twi%d] incomplete xfer (status: 0x%x, dev addr: 0x%x)", twi->port, ret, msgs->addr); ret = SUNXI_TWI_FAIL; } TWI_INFO("hal_twi_engine_do_xfer end"); out: return ret; } twi_status_t hal_twi_xfer(twi_port_t port, twi_msg_t *msgs, int32_t num) { hal_twi_t *twi = &hal_twi[port]; int ret; if ((msgs == NULL) || (num <= 0)) { TWI_ERR("[twi%d] invalid argument", port); return TWI_STATUS_INVALID_PARAMETER; } if (twi->twi_drv_used) { TWI_INFO("[twi%d] twi driver xfer", twi->port); ret = hal_twi_drv_do_xfer(twi, msgs, num); if (ret < 0) { return TWI_STATUS_ERROR; } } else { TWI_INFO("[twi%d] twi engine xfer", twi->port); ret = hal_twi_engine_do_xfer(twi, msgs, num); if (ret < 0) { return TWI_STATUS_ERROR; } } return ret == num ? TWI_STATUS_OK : TWI_STATUS_ERROR; } static twi_status_t hal_twi_sys_pinctrl_init(hal_twi_t *twi) { #ifdef CONFIG_RTTKERNEL user_gpio_set_t gpio_cfg[2] = {0}; int count, i; char twi_name[16]; int ret = TWI_STATUS_OK; sprintf(twi_name, "twi%d", twi->port); count = Hal_Cfg_GetGPIOSecKeyCount(twi_name); if (!count) { TWI_ERR("[twi%d] not support in sys_config\n", twi->port); return TWI_STATUS_ERROR; } Hal_Cfg_GetGPIOSecData(twi_name, gpio_cfg, count); for (i = 0; i < count; i++) { twi->pin[i] = (gpio_cfg[i].port - 1) * 32 + gpio_cfg[i].port_num; twi->pinmux = gpio_cfg[i].mul_sel; ret = hal_gpio_pinmux_set_function(twi->pin[i], twi->pinmux); if (ret){ TWI_ERR("[twi%d] PIN%u set function failed! return %d\n", twi->port, twi->pin[i], ret); return TWI_STATUS_ERROR; } ret = hal_gpio_set_driving_level(twi->pin[i], gpio_cfg[i].drv_level); if (ret) { TWI_ERR("[twi%d] PIN%u set driving level failed! return %d\n", twi->port, gpio_cfg[i].drv_level, ret); return TWI_STATUS_ERROR; } if (gpio_cfg[i].pull) if (hal_gpio_set_pull(twi->pin[i], gpio_cfg[i].pull)) return TWI_STATUS_ERROR; } return TWI_STATUS_OK; #endif TWI_ERR("twi[%d] not support sys_config format \n", twi->port); return TWI_STATUS_ERROR; } static twi_status_t hal_twi_pinctrl_init(hal_twi_t *twi) { uint8_t i; switch (twi->port) { case TWI_MASTER_0: twi->pin[0] = TWI0_SCK; twi->pin[1] = TWI0_SDA; twi->pinmux = TWI0_PIN_MUXSEL; break; case TWI_MASTER_1: twi->pin[0] = TWI1_SCK; twi->pin[1] = TWI1_SDA; twi->pinmux = TWI1_PIN_MUXSEL; break; case TWI_MASTER_2: twi->pin[0] = TWI2_SCK; twi->pin[1] = TWI2_SDA; twi->pinmux = TWI2_PIN_MUXSEL; break; case TWI_MASTER_3: twi->pin[0] = TWI3_SCK; twi->pin[1] = TWI3_SDA; twi->pinmux = TWI3_PIN_MUXSEL; break; case S_TWI_MASTER_0: #if !(defined(CONFIG_ARCH_SUN8IW20) || defined(CONFIG_SOC_SUN20IW1)) twi->pin[0] = S_TWI0_SCK; twi->pin[1] = S_TWI0_SDA; twi->pinmux = S_TWI0_PIN_MUXSEL; #endif break; } for (i = 0; i < TWI_PIN_NUM; i++) { if (hal_gpio_pinmux_set_function(twi->pin[i], twi->pinmux)) { TWI_ERR("[twi%d] PIN set function failed!", twi->port); return TWI_STATUS_ERROR; } if (hal_gpio_set_driving_level(twi->pin[i], TWI_DRIVE_STATE)) { TWI_ERR("[twi%d] PIN set driving failed!", twi->port); return TWI_STATUS_ERROR; } if (hal_gpio_set_pull(twi->pin[i], TWI_PULL_STATE)) { TWI_ERR("[twi%d] PIN set driving failed!", twi->port); return TWI_STATUS_ERROR; } } return TWI_STATUS_OK; } static twi_status_t hal_twi_pinctrl_exit(hal_twi_t *twi) { uint8_t i; for (i = 0; i < TWI_PIN_NUM; i++) { if (hal_gpio_pinmux_set_function(twi->pin[i], TWI_DISABLE_PIN_MUXSEL)) { TWI_ERR("[twi%d] PIN exit function failed!", twi->port); return TWI_STATUS_ERROR; } } return TWI_STATUS_OK; } static twi_status_t hal_twi_regulator_init(hal_twi_t *twi) { enum REGULATOR_TYPE_ENUM regulator_type = twi_regulator_type; enum REGULATOR_ID_ENUM regulator_id = twi_regulator_id[twi->port]; int tar_vol = twi_vol[twi->port]; int ret; /* if (regulator_id == AXP2101_ID_MAX) { TWI_INFO("[twi%d] needn't to set regulator", twi->port); return TWI_STATUS_OK; } hal_regulator_get(REGULATOR_GET(regulator_type, regulator_id), &twi->regulator); ret = hal_regulator_set_voltage(&twi->regulator, tar_vol); if (ret) { TWI_ERR("twi%d set voltage failed", twi->port); return TWI_STATUS_ERROR; } ret = hal_regulator_enable(&twi->regulator); if (ret) { TWI_ERR("twi%d enabled regulator failed", twi->port); return TWI_STATUS_ERROR; } */ return TWI_STATUS_OK; } static twi_status_t hal_twi_regulator_exit(hal_twi_t *twi) { int ret; enum REGULATOR_ID_ENUM regulator_id = twi_regulator_id[twi->port]; if (regulator_id == AXP2101_ID_MAX) { TWI_INFO("[twi%d] needn't to exit regulator", twi->port); return TWI_STATUS_OK; } ret = hal_regulator_disable(&twi->regulator); if (ret) { TWI_ERR("twi%d disable regulator failed\n", twi->port); return TWI_STATUS_ERROR; } return TWI_STATUS_OK; } static twi_status_t hal_twi_clk_init(hal_twi_t *twi) { unsigned long rate; /* if (hal_clk_set_parent(twi->mclk, twi->pclk)) { TWI_ERR("[twi%d] clk set parent failed!",twi->port); return TWI_STATUS_ERROR; } */ #if !(defined(CONFIG_ARCH_SUN8IW20) || defined(CONFIG_SOC_SUN20IW1)) rate = hal_clk_get_rate(twi->pclk); if (hal_clock_enable(twi->mclk)) { TWI_ERR("[twi%d] clk enable mclk failed!", twi->port); return TWI_STATUS_ERROR; } #else rate =24000000; hal_clk_type_t clk_type = HAL_SUNXI_CCU; hal_clk_id_t twi_clk_id; hal_clk_t clk; hal_reset_type_t reset_type = HAL_SUNXI_RESET; hal_reset_id_t twi_reset_id; struct reset_control *reset; switch (twi->port) { case 0: twi_clk_id = SUNXI_CLK_TWI(0); twi_reset_id = SUNXI_CLK_RST_TWI(0); break; case 1: twi_clk_id = SUNXI_CLK_TWI(1); twi_reset_id = SUNXI_CLK_RST_TWI(1); break; case 2: twi_clk_id = SUNXI_CLK_TWI(2); twi_reset_id = SUNXI_CLK_RST_TWI(2); break; case 3: twi_clk_id = SUNXI_CLK_TWI(3); twi_reset_id = SUNXI_CLK_RST_TWI(3); break; default: TWI_ERR("twi%d is invalid\n", twi->port); return TWI_STATUS_ERROR; } reset = hal_reset_control_get(reset_type, twi_reset_id); if (!reset) { TWI_ERR("twi reset control get error"); return TWI_STATUS_ERROR; } hal_reset_control_deassert(reset); hal_reset_control_put(reset); clk = hal_clock_get(clk_type, twi_clk_id); if (!clk) { TWI_ERR("twi clock get error "); return TWI_STATUS_ERROR; } hal_clock_enable(clk); #endif if (twi->twi_drv_used) { twi_set_clock(twi, TWI_DRIVER_BUSC, 24000000, twi->freq, TWI_DRV_CLK_M, TWI_DRV_CLK_N); twi_enable(twi->base_addr, TWI_DRIVER_CTRL, TWI_DRV_EN); } else { twi_set_clock(twi, TWI_CLK_REG, rate, twi->freq, TWI_CLK_DIV_M, TWI_CLK_DIV_N); twi_enable(twi->base_addr, TWI_CTL_REG, TWI_CTL_BUSEN); } return TWI_STATUS_OK; } static void hal_twi_clk_exit(hal_twi_t *twi) { /* disable twi bus */ twi_disable(twi->base_addr, TWI_DRIVER_CTRL, TWI_DRV_EN); //hal_clock_disable(twi->mclk); } twi_status_t hal_twi_init(twi_port_t port) { int ret; unsigned long rate; hal_twi_t *twi = &hal_twi[port]; if (twi->already_init) //if twi has been inited, return ok { return TWI_STATUS_OK; } twi->port = port; twi->base_addr = hal_twi_address[twi->port]; twi->irqnum = hal_twi_irq_num[twi->port]; #if !(defined(CONFIG_ARCH_SUN8IW20) || defined(CONFIG_SOC_SUN20IW1)) twi->pclk = hal_twi_pclk[port]; twi->mclk = hal_twi_mclk[port]; #endif twi->freq = TWI_FREQUENCY_400K; #if 1 twi->twi_drv_used = ENGINE_XFER; #else twi->twi_drv_used = TWI_DRV_XFER; #endif twi->status = TWI_XFER_IDLE; twi->timeout = 5; twi->flags = 0; twi->hal_sem = hal_sem_create(0); if (twi->hal_sem == NULL) { TWI_ERR("[twi%d] creating hal semaphore failed.", port); goto errsem; } if (twi->twi_drv_used) { twi->dma_complete = hal_sem_create(0); if (twi->dma_complete == NULL) { TWI_ERR("[twi%d] creating dma semaphore failed.\n", port); goto errsem_dma; } } #if !(defined(CONFIG_ARCH_SUN8IW18P1) || defined(CONFIG_SOC_SUN20IW1)) if (hal_twi_regulator_init(twi)) { TWI_ERR("[twi%d] regulator init error", port); goto errregu; } #endif if (hal_twi_clk_init(twi)) { TWI_ERR("[twi%d] clk init error", port); goto errclk; } if (hal_twi_sys_pinctrl_init(twi)) { if (hal_twi_pinctrl_init(twi)) { TWI_ERR("[twi%d] pinctrl init error", port); goto errpin; } } if (request_irq(twi->irqnum, hal_twi_handler, 0, "twi-ctl", twi) < 0) { TWI_ERR("[twi%d] request irq error", twi->port); goto errirq; } enable_irq(twi->irqnum); if (twi->twi_drv_used) { hal_dma_chan_request(&twi->dma_chan); } twi->already_init++; return TWI_STATUS_OK; errirq: free_irq(twi->irqnum, twi); errpin: hal_twi_pinctrl_exit(twi); errclk: hal_twi_clk_exit(twi); #if !(defined(CONFIG_ARCH_SUN8IW18P1) || defined(CONFIG_SOC_SUN20IW1)) errregu: hal_twi_regulator_exit(twi); #endif errsem_dma: if (twi->twi_drv_used) { hal_sem_delete(twi->dma_complete); } errsem: hal_sem_delete(twi->hal_sem); return TWI_STATUS_ERROR; } twi_status_t hal_twi_uninit(twi_port_t port) { hal_twi_t *twi = &hal_twi[port]; if (twi->already_init > 0) { twi->already_init--; if (twi->already_init == 0) { disable_irq(twi->irqnum); free_irq(twi->irqnum, twi); hal_twi_pinctrl_exit(twi); if (twi->twi_drv_used) { hal_dma_chan_free(twi->dma_chan); } hal_twi_clk_exit(twi); #if !defined(CONFIG_ARCH_SUN8IW18P1) hal_twi_regulator_exit(twi); #endif hal_sem_delete(twi->hal_sem); } } return TWI_STATUS_OK; } twi_status_t hal_twi_write(twi_port_t port, unsigned long pos, const void *buf, uint32_t size) { twi_msg_t msg; uint8_t num = 1, i = 0; uint8_t *msg_buf; uint8_t *buf1 = (uint8_t *)buf; hal_twi_t *twi = &hal_twi[port]; msg_buf = (uint8_t *)malloc(sizeof(uint8_t) * (size + 1)); if (!msg_buf) { return TWI_STATUS_ERROR; } memset(msg_buf, 0x00, sizeof(uint8_t) * (size + 1)); msg_buf[0] = (int8_t)pos; for (i = 0; i < size; i++) { msg_buf[i + 1] = buf1[i]; } msg.addr = twi->slave_addr; msg.flags = twi->flags & TWI_M_TEN; msg.flags &= ~(TWI_M_RD); msg.len = (size + 1); msg.buf = msg_buf; return hal_twi_xfer(port, &msg, num); } twi_status_t hal_twi_read(twi_port_t port, unsigned long pos, void *buf, uint32_t size) { twi_msg_t msg[2]; uint8_t num = 2, index = 0; hal_twi_t *twi = &hal_twi[port]; index = (uint8_t)pos; msg[0].addr = twi->slave_addr; msg[0].flags = twi->flags & TWI_M_TEN; msg[0].flags &= ~(TWI_M_RD); msg[0].len = 1; msg[0].buf = &index; msg[1].addr = twi->slave_addr; msg[1].flags = twi->flags & TWI_M_TEN; msg[1].flags |= TWI_M_RD; msg[1].len = size; msg[1].buf = buf; return hal_twi_xfer(port, msg, num); } twi_status_t hal_twi_control(twi_port_t port, hal_twi_transfer_cmd_t cmd, void *args) { hal_twi_t *twi = &hal_twi[port]; twi_msg_t *msg; uint16_t *arg; switch (cmd) { case I2C_SLAVE: case I2C_SLAVE_FORCE: arg = (uint16_t *)args; if (*arg > 0x7ff) { return TWI_STATUS_INVALID_PARAMETER; } else if (twi->status != TWI_XFER_IDLE) { return TWI_STATUS_ERROR_BUSY; } twi->slave_addr = *arg; return TWI_STATUS_OK; case I2C_TENBIT: arg = (uint16_t *)args; if (*arg) { twi->flags |= TWI_M_TEN; } else { twi->flags &= ~TWI_M_TEN; } return TWI_STATUS_OK; case I2C_RDWR: msg = (twi_msg_t *)args; return hal_twi_xfer(port, msg, 1); default: return TWI_STATUS_INVALID_PARAMETER; } }