rt-thread/bsp/allwinner/libraries/sunxi-hal/hal/source/tpadc/hal_tpadc.c

715 lines
17 KiB
C

/*
* ===========================================================================================
*
* Filename: hal_tpadc.c
*
* Description: tpadc hal layer code
*
* Version: Melis3.0
* Create: 2020-1-14
* Revision: none
* Compiler: GCC:version 9.2.1 20170904 (release),ARM/embedded-7-branch revision 255204
*
* Author: liuyu@allwinnertech.com
* Organization: SWC-BPD
* Last Modified: 2021-1-14
*
* ===========================================================================================
*/
#include <hal_interrupt.h>
#include <hal_clk.h>
#include <hal_reset.h>
#include "sunxi_hal_tpadc.h"
hal_tpadc_t hal_tpadc;
static void sunxi_flush_fifo(unsigned long reg_base)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_INT_FIFOC);
reg_val = SET_BITS(TP_FIFO_FLUSH, TP_FIFO_FLUSH_WIDTH,reg_val, 0x1);
writel(reg_val, reg_base + TP_INT_FIFOC);
}
static void sunxi_clear_fifo_status(unsigned long reg_base)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_INT_FIFOS);
reg_val = SET_BITS(FIFO_OVERRUN_PENDING, FIFO_OVERRUN_PENDIN_WIDTH,
reg_val, 0x0);
reg_val = SET_BITS(FIFO_DATA_PENDING, FIFO_DATA_PENDING_WIDTH,
reg_val, 0x0);
writel(reg_val, reg_base + TP_INT_FIFOS);
}
static void sunxi_set_acqiure_time(unsigned long reg_base, uint32_t val)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL0);
reg_val = SET_BITS(TACQ, TACQ_WIDTH, reg_val, val);
writel(reg_val, reg_base + TP_CTRL0);
}
static void sunxi_set_frequency_divider(unsigned long reg_base, uint32_t val)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL0);
reg_val = SET_BITS(FS_DIV, FS_DIV_WIDTH, reg_val, val);
writel(reg_val, reg_base + TP_CTRL0);
}
static void sunxi_set_clk_divider(unsigned long reg_base, uint32_t val)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL0);
reg_val = SET_BITS(ADC_CLK_DIVIDER, ADC_CLK_DIVIDER_WIDTH, reg_val, val);
writel(reg_val, reg_base + TP_CTRL0);
}
static void sunxi_select_delay_mode(unsigned long reg_base, uint32_t val)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL0);
reg_val = SET_BITS(ADC_FIRST_DLY_MODE, ADC_FIRST_DLY_MODE_WIDTH,reg_val, val);
writel(reg_val, reg_base + TP_CTRL0);
}
static void sunxi_set_dealy_time(unsigned long reg_base, uint32_t val)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL0);
reg_val = SET_BITS(ADC_FIRST_DLY, ADC_FIRST_DLY_WIDTH,reg_val, val);
writel(reg_val, reg_base + TP_CTRL0);
}
static void sunxi_clk_init(unsigned long reg_base)
{
sunxi_set_acqiure_time(reg_base, 0x02); //2us
sunxi_set_frequency_divider(reg_base, 0xb);
sunxi_set_clk_divider(reg_base, 0x3);
sunxi_select_delay_mode(reg_base, 0x1);
sunxi_set_dealy_time(reg_base, 0xf);
}
static uint32_t sunxi_tpadc_adc_ch_select(unsigned long reg_base, tp_channel_id id)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL1);
switch (id) {
case TP_CH_0:
reg_val |= TP_CH0_SELECT;
break;
case TP_CH_1:
reg_val |= TP_CH1_SELECT;
break;
case TP_CH_2:
reg_val |= TP_CH2_SELECT;
break;
case TP_CH_3:
reg_val |= TP_CH3_SELECT;
break;
default:
TPADC_ERR("%s, invalid channel id!", __func__);
return TPADC_ERROR;
}
writel(reg_val, reg_base + TP_CTRL1);
return 0;
}
/* 0:TAPADC 1: ADC*/
static void sunxi_tpadc_mode_select(unsigned long reg_base, uint32_t val)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL1);
reg_val = SET_BITS(TP_MODE_SELECT, TP_MODE_SELECT_WIDTH,reg_val, val);
writel(reg_val, reg_base + TP_CTRL1);
}
static void sunxi_tpadc_enable(unsigned long reg_base, uint32_t val)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL1);
reg_val = SET_BITS(TP_EN, TP_EN_WIDTH, reg_val, val);
writel(reg_val, reg_base + TP_CTRL1);
}
static void sunxi_set_up_debou_time(unsigned long reg_base, uint32_t val)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL1);
reg_val = SET_BITS(TP_DEBOUNCE, TP_DEBOUNCE_WIDTH, reg_val, val);
writel(reg_val, reg_base + TP_CTRL1);
}
static void sunxi_set_pressure_thresholed(unsigned long reg_base, uint32_t val)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL2);
reg_val = SET_BITS(PRE_MEA, PRE_MEA_WIDTH, reg_val, val);
writel(reg_val, reg_base + TP_CTRL2);
}
static void sunxi_pressure_enable(unsigned long reg_base)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL2);
reg_val = SET_BITS(PRE_MEA_EN, PRE_MEA_EN_WIDTH, reg_val, 0x1);
writel(reg_val, reg_base + TP_CTRL2);
}
/* 0: x_data y_data x_data y_data */
static void sunxi_tp_mode_fifo_select(unsigned long reg_base, uint32_t val)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL2);
reg_val = SET_BITS(TP_FIFO_MODE, TP_FIFO_MODE_WIDTH, reg_val, val);
writel(reg_val, reg_base + TP_CTRL2);
}
static void sunxi_set_sensitivity(unsigned long reg_base, uint32_t val)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL2);
reg_val = SET_BITS(TP_SENSITIVE, TP_SENSITIVE_WIDTH, reg_val, val);
writel(reg_val, reg_base + TP_CTRL2);
}
static void sunxi_set_filter_type(unsigned long reg_base, uint32_t val)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL3);
reg_val = SET_BITS(FILTER_TYPE, FILTER_TYPE_WIDTH, reg_val, val);
writel(reg_val, reg_base + TP_CTRL3);
}
static void sunxi_filter_enable(unsigned long reg_base)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_CTRL3);
reg_val = SET_BITS(FILTER_EN, FILTER_EN_WIDTH, reg_val, 0x1);
writel(reg_val, reg_base + TP_CTRL3);
}
static void sunxi_downirq_enable(unsigned long reg_base)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_INT_FIFOC);
reg_val = SET_BITS(TP_DOWN_IRQ_EN, TP_DOWN_IRQ_ENWIDTH, reg_val, 0x1);
writel(reg_val, reg_base + TP_INT_FIFOC);
}
static void sunxi_upirq_enable(unsigned long reg_base)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_INT_FIFOC);
reg_val = SET_BITS(TP_UP_IRQ_EN, TP_UP_IRQ_EN_WIDTH, reg_val, 0x1);
writel(reg_val, reg_base + TP_INT_FIFOC);
}
static void sunxi_drq_enable(unsigned long reg_base)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_INT_FIFOC);
reg_val = SET_BITS(TP_DATA_DRQ_EN, TP_DATA_DRQ_EN_WIDTH, reg_val, 0x1);
writel(reg_val, reg_base + TP_INT_FIFOC);
}
static void sunxi_set_trig_level(unsigned long reg_base, uint32_t val)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_INT_FIFOC);
reg_val = SET_BITS(TP_FIFO_TRIG, TP_FIFO_TRIG_WIDTH, reg_val, val);
writel(reg_val, reg_base + TP_INT_FIFOC);
}
static void sunxi_xydata_change_enable(unsigned long reg_base, uint32_t val)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_INT_FIFOC);
reg_val = SET_BITS(TP_DATA_XY_CHANGE, TP_DATA_XY_CHANGE_WIDTH, reg_val, val);
writel(reg_val, reg_base + TP_INT_FIFOC);
}
static void sunxi_irq_enable(unsigned long reg_base)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_INT_FIFOC);
reg_val = SET_BITS(TP_DATA_IRQ_EN, TP_DATA_IRQ_EN_WIDTH, reg_val, 0x1);
writel(reg_val, reg_base + TP_INT_FIFOC);
}
static void sunxi_irq_disable(unsigned long reg_base)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_INT_FIFOC);
reg_val = SET_BITS(TP_DATA_IRQ_EN, TP_DATA_IRQ_EN_WIDTH, reg_val, 0x0);
writel(reg_val, reg_base + TP_INT_FIFOC);
}
static void sunxi_overrun_enable(unsigned long reg_base)
{
uint32_t reg_val;
reg_val = readl(reg_base + TP_INT_FIFOC);
reg_val = SET_BITS(TP_FIFO_OVERRUN_IRQ, TP_FIFO_OVERRUN_IRQ_WIDTH, reg_val, 0x1);
writel(reg_val, reg_base + TP_INT_FIFOC);
}
static u32 sunxi_tpadc_irq_status(unsigned long reg_base)
{
return readl(reg_base + TP_INT_FIFOS);
}
static void sunxi_tpadc_clear_pending(unsigned long reg_base)
{
int reg;
reg = readl(reg_base + TP_INT_FIFOS);
writel(reg, reg_base + TP_INT_FIFOS);
}
static u32 sunxi_tpadc_ch_select(unsigned long reg_base, tp_channel_id id)
{
u32 reg_val;
reg_val = readl(reg_base + TP_CTRL1);
switch (id) {
case TP_CH_0:
reg_val |= TP_CH0_SELECT;
break;
case TP_CH_1:
reg_val |= TP_CH1_SELECT;
break;
case TP_CH_2:
reg_val |= TP_CH2_SELECT;
break;
case TP_CH_3:
reg_val |= TP_CH3_SELECT;
break;
default:
TPADC_ERR("%s, invalid channel id!", __func__);
return -1;
}
writel(reg_val, reg_base + TP_CTRL1);
return 0;
}
static int sunxi_tpadc_ch_deselect(unsigned long reg_base, tp_channel_id id)
{
u32 reg_val;
reg_val = readl(reg_base + TP_CTRL1);
switch (id) {
case TP_CH_0:
reg_val &= ~TP_CH0_SELECT;
break;
case TP_CH_1:
reg_val &= ~TP_CH1_SELECT;
break;
case TP_CH_2:
reg_val &= ~TP_CH2_SELECT;
break;
case TP_CH_3:
reg_val &= ~TP_CH3_SELECT;
break;
default:
TPADC_ERR("%s, invalid channel id!", __func__);
return -1;
}
writel(reg_val, reg_base + TP_CTRL1);
return 0;
}
static u32 hal_tpadc_data_read(unsigned long reg_base)
{
hal_tpadc_t *tpadc = &hal_tpadc;
return readl(reg_base + TP_DATA);
}
static int hal_tpadc_callback(uint32_t data, data_flag_t flag)
{
TPADC_INFO("tpadc : tpadc mode interrupt, data_flag is %ld", flag);
return 0;
}
static int hal_tpadc_adc_callback(uint32_t data, tp_channel_id channel)
{
TPADC_INFO("tpadc : auxiliary adc mode interrupt");
return 0;
}
static void sunxi_tpadc_setup(hal_tpadc_t *tpadc)
{
u32 i;
tpadc->reg_base = (unsigned long)TPADC_BASE;
tpadc->irq_num = SUNXI_TPADC_IRQ;
tpadc->rst_clk_id = (hal_reset_id_t)RST_BUS_TPADC;
tpadc->bus_clk_id = (hal_clk_id_t)CLK_BUS_TPADC;
tpadc->mod_clk_id = (hal_clk_id_t)CLK_TPADC;
tpadc->callback = hal_tpadc_callback;
for(i=0; i<TP_CH_MAX; i++)
tpadc->adc_callback[i] = hal_tpadc_adc_callback;
}
static hal_tpadc_status_t hal_tpadc_clk_init(hal_tpadc_t *tpadc)
{
hal_clk_type_t clk_type = HAL_SUNXI_CCU;
hal_clk_id_t bus_clk_id = tpadc->bus_clk_id;
hal_clk_id_t mod_clk_id = tpadc->mod_clk_id;
hal_reset_type_t reset_type = HAL_SUNXI_RESET;
hal_reset_id_t tpadc_reset_id = tpadc->rst_clk_id;
struct reset_control *reset;
reset = hal_reset_control_get(reset_type, tpadc_reset_id);
if (hal_reset_control_deassert(reset))
{
TPADC_ERR("tpadc reset deassert failed!");
return TPADC_ERROR;
}
hal_reset_control_put(reset);
tpadc->mod_clk = hal_clock_get(clk_type, mod_clk_id);
if (hal_clock_enable(tpadc->mod_clk))
{
TPADC_ERR("tpadc mod clk enable mclk failed!");
return TPADC_ERROR;
}
tpadc->bus_clk = hal_clock_get(clk_type, bus_clk_id);
if (hal_clock_enable(tpadc->bus_clk))
{
TPADC_ERR("tpadc bus clk enable mclk failed!");
return TPADC_ERROR;
}
return TPADC_OK;
}
static hal_tpadc_status_t hal_tpadc_clk_exit(hal_tpadc_t *tpadc)
{
hal_clock_disable(tpadc->bus_clk);
hal_clock_put(tpadc->bus_clk);
hal_clock_disable(tpadc->mod_clk);
hal_clock_put(tpadc->mod_clk);
return TPADC_OK;
}
/* the rtpadc tpadc mode interface */
static irqreturn_t tpadc_handler(int irq, void *dev)
{
hal_tpadc_t *tpadc = (hal_tpadc_t *)dev;
u32 reg_val;
u32 x_data, y_data;
u32 i;
reg_val = sunxi_tpadc_irq_status(tpadc->reg_base);
if (reg_val & TP_DATAPEND) {
x_data = hal_tpadc_data_read(tpadc->reg_base);
tpadc->callback(x_data, DATA_X);
y_data = hal_tpadc_data_read(tpadc->reg_base);
tpadc->callback(y_data, DATA_Y);
}
if (reg_val & TP_UPPEND) {
tpadc->callback(0, DATA_UP);
}
if (reg_val & TP_DOWNPEND) {
TPADC_INFO("touch down ");
}
sunxi_tpadc_clear_pending(tpadc->reg_base);
return TPADC_OK;
}
hal_tpadc_status_t hal_tpadc_register_callback(tpadc_usercallback_t user_callback)
{
hal_tpadc_t *tpadc = &hal_tpadc;
if (user_callback == NULL)
return TPADC_ERROR;
else
tpadc->callback = user_callback;
return TPADC_OK;
}
hal_tpadc_status_t hal_tpadc_init(void)
{
hal_tpadc_t *tpadc = &hal_tpadc;
u32 clk_in;
sunxi_tpadc_setup(tpadc);
if (hal_tpadc_clk_init(tpadc))
{
TPADC_ERR("tpadc init clk error");
return TPADC_ERROR;
}
/* clear status */
sunxi_flush_fifo(tpadc->reg_base);
sunxi_clear_fifo_status(tpadc->reg_base);
sunxi_clk_init(tpadc->reg_base);
sunxi_tpadc_mode_select(tpadc->reg_base, 0);
sunxi_tpadc_enable(tpadc->reg_base, 1);
sunxi_set_up_debou_time(tpadc->reg_base, 0x00);
sunxi_set_pressure_thresholed(tpadc->reg_base, 0x800fff);
sunxi_pressure_enable(tpadc->reg_base);
sunxi_tp_mode_fifo_select(tpadc->reg_base, 0x00);
sunxi_set_sensitivity(tpadc->reg_base, 0xf);
sunxi_set_filter_type(tpadc->reg_base, 0x1);
sunxi_filter_enable(tpadc->reg_base);
sunxi_downirq_enable(tpadc->reg_base);
sunxi_upirq_enable(tpadc->reg_base);
sunxi_drq_enable(tpadc->reg_base);
sunxi_set_trig_level(tpadc->reg_base, 0x1);
sunxi_xydata_change_enable(tpadc->reg_base, 0);
sunxi_irq_enable(tpadc->reg_base);
sunxi_overrun_enable(tpadc->reg_base);
sunxi_tpadc_clear_pending(tpadc->reg_base);
if (request_irq(tpadc->irq_num, tpadc_handler, 0, "tpadc", tpadc))
{
TPADC_ERR("tpadc request irq(%d) failed \n", tpadc->irq_num);
return TPADC_ERROR;
}
enable_irq(tpadc->irq_num);
TPADC_INFO("tpadc init success");
return TPADC_OK;
}
hal_tpadc_status_t hal_tpadc_exit(void)
{
hal_tpadc_t *tpadc = &hal_tpadc;
sunxi_irq_disable(tpadc->reg_base);
free_irq(tpadc->irq_num, tpadc);
hal_tpadc_clk_exit(tpadc);
return TPADC_OK;
}
hal_tpadc_status_t hal_tpadc_resume(void)
{
hal_tpadc_t *tpadc = &hal_tpadc;
hal_tpadc_clk_init(tpadc);
sunxi_tpadc_enable(tpadc->reg_base, 1);
return TPADC_OK;
}
hal_tpadc_status_t hal_tpadc_suspend(void)
{
hal_tpadc_t *tpadc = &hal_tpadc;
sunxi_tpadc_enable(tpadc->reg_base, 0);
hal_tpadc_clk_exit(tpadc);
return TPADC_OK;
}
/* the rtpadc adc mode interface*/
static irqreturn_t tpadc_adc_handler(int irq, void *dev)
{
hal_tpadc_t *tpadc = (hal_tpadc_t *)dev;
u32 reg_val;
u32 data;
reg_val = sunxi_tpadc_irq_status(tpadc->reg_base);
if (reg_val & TP_DATAPEND) {
reg_val = readl(tpadc->reg_base + TP_CTRL1);
if (reg_val & TP_CH0_SELECT)
{
data = hal_tpadc_data_read(tpadc->reg_base);
if (tpadc->adc_callback[0])
tpadc->adc_callback[0](data, TP_CH0_SELECT);
}
if (reg_val & TP_CH1_SELECT)
{
data = hal_tpadc_data_read(tpadc->reg_base);
if (tpadc->adc_callback[1])
tpadc->adc_callback[1](data, TP_CH1_SELECT);
}
if (reg_val & TP_CH2_SELECT)
{
data = hal_tpadc_data_read(tpadc->reg_base);
if (tpadc->adc_callback[2])
tpadc->adc_callback[2](data, TP_CH2_SELECT);
}
if (reg_val & TP_CH3_SELECT)
{
data = hal_tpadc_data_read(tpadc->reg_base);
if (tpadc->adc_callback[3])
tpadc->adc_callback[3](data, TP_CH3_SELECT);
}
if (reg_val & 0x0)
{
TPADC_ERR("no channel init, you need init 1~4 channel first");
return -IRQ_HANDLED;
}
}
sunxi_tpadc_clear_pending(tpadc->reg_base);
return IRQ_NONE;
}
hal_tpadc_status_t hal_tpadc_adc_register_callback(tp_channel_id channel, tpadc_adc_usercallback_t user_adc_callback)
{
hal_tpadc_t *tpadc = &hal_tpadc;
if (user_adc_callback == NULL)
return TPADC_ERROR;
else
tpadc->adc_callback[channel] = user_adc_callback;
return TPADC_OK;
}
hal_tpadc_status_t hal_tpadc_adc_init(void)
{
hal_tpadc_t *tpadc = &hal_tpadc;
u32 clk_in;
sunxi_tpadc_setup(tpadc);
if (hal_tpadc_clk_init(tpadc))
{
TPADC_ERR("tpadc init clk error");
return TPADC_ERROR;
}
/* clear status */
sunxi_flush_fifo(tpadc->reg_base);
sunxi_clear_fifo_status(tpadc->reg_base);
sunxi_clk_init(tpadc->reg_base);
sunxi_tpadc_mode_select(tpadc->reg_base, 1);
sunxi_tpadc_enable(tpadc->reg_base, 1);
sunxi_set_filter_type(tpadc->reg_base, 0x1);
sunxi_filter_enable(tpadc->reg_base);
sunxi_downirq_enable(tpadc->reg_base);
sunxi_upirq_enable(tpadc->reg_base);
sunxi_set_trig_level(tpadc->reg_base, 0x3);
sunxi_irq_enable(tpadc->reg_base);
sunxi_tpadc_clear_pending(tpadc->reg_base);
if (request_irq(tpadc->irq_num, tpadc_adc_handler, 0, "tpadc", tpadc))
{
TPADC_ERR("tpadc request irq(%d) failed \n", tpadc->irq_num);
return TPADC_ERROR;
}
enable_irq(tpadc->irq_num);
TPADC_INFO("tpadc init success");
return TPADC_OK;
}
hal_tpadc_status_t hal_tpadc_adc_channel_init(tp_channel_id channel)
{
hal_tpadc_t *tpadc = &hal_tpadc;
if (sunxi_tpadc_adc_ch_select(tpadc->reg_base, channel))
{
TPADC_ERR("TPADC adc mode channel select error");
return -1;
}
return 0;
}
hal_tpadc_status_t hal_tpadc_adc_exit()
{
hal_tpadc_t *tpadc = &hal_tpadc;
sunxi_irq_disable(tpadc->reg_base);
free_irq(tpadc->irq_num, tpadc);
hal_clock_disable(tpadc->bus_clk);
hal_clock_put(tpadc->bus_clk);
hal_clock_disable(tpadc->mod_clk);
hal_clock_put(tpadc->mod_clk);
return TPADC_OK;
}