2211 lines
59 KiB
C
2211 lines
59 KiB
C
#include <hal_log.h>
|
|
#include <stdlib.h>
|
|
#include <hal_clk.h>
|
|
#include <hal_reset.h>
|
|
#include <hal_gpio.h>
|
|
#include <sunxi_hal_twi.h>
|
|
#include <hal_dma.h>
|
|
#include <hal_cache.h>
|
|
#include <sunxi_hal_regulator.h>
|
|
#include <interrupt.h>
|
|
|
|
#ifdef CONFIG_RTTKERNEL
|
|
#include <hal_cfg.h>
|
|
#include <script.h>
|
|
#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;
|
|
}
|
|
}
|