Files
gk-sdk/source/gmp/drv/lsadc/adc.c
T

448 lines
12 KiB
C

/*
* Copyright (c) XMEDIA. All rights reserved.
*/
#include "osal.h"
#include <linux/types.h>
#include "adc.h"
#ifndef NULL
#define NULL ((xmedia_void *)0)
#endif
#define lsadc_print(fmt, ...) osal_printk("Func:%s, Line:%d, "fmt"\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
//#define USE_LSADC_CHANNEL_0
//#define ENABLE_ADC_IRQ
#define LSADC_IRQ_ID 37
#define LSADC_BASE_ADDR 0x120a0000
#define LSADC_ADDR_OFFSET(x) (LSADC_BASE_ADDR+(x))
#define LSADC_CRG_ADDR 0x120101BC
#define LSADC_CONFIG 0x00
#define LSADC_GLITCH 0x04
#define LSADC_TIMESCAN 0x08
#define LSADC_PD_TD 0x0C
#define LSADC_INTEN 0x10
#define LSADC_INTSTATUS 0x14
#define LSADC_INTCLR 0x18
#define LSADC_START 0x1C
#define LSADC_STOP 0x20
#define LSADC_ACTBIT 0x24
#define LSADC_CHNDATA 0x2C
#define LSADC_ENABLE 0x3C
#define LSADC_MAX_CHN_NUM 3
#define LSADC_NUM_BITS 10
#define LSADC_ALL_CHN_INT_MASK 0xF
#define LSADC_ACTIVE_BIT (0xfff)
#define LSADC_ACTIVE_BIT_MASK (0xFFF)
#define LSADC_DATA_DELTA (0xc)
#define LSADC_DATA_DELTA_MASK (0xF)
#define LSADC_GLITCH_SAMPLE (0x30)
#define LSADC_TIME_SCAN (0x200)
#define LSADC_GLITCH_TIME_US ((LSADC_GLITCH_SAMPLE + 1) * LSADC_TIME_SCAN * 25 / 100)
volatile xmedia_void* lsadc_reg = NULL;
xmedia_s32 lsadc_irq = LSADC_IRQ_ID;
#ifdef ENABLE_ADC_IRQ
#define INVALID_CDC_VALUE (0)
typedef struct {
xmedia_u32 adc_value;
} lsadc_cdc_info;
lsadc_cdc_info g_cdc_info[LSADC_MAX_CHN_NUM];
static osal_spinlock_t g_lsadc_spin_lock;
#endif
#define LSADC_SPIN_LOCK_FLAG xmedia_ulong
#define LSADC_VIR_ADDR(x) ((uintptr_t)(lsadc_reg) + ((x)-LSADC_BASE_ADDR))
#define lsadc_writel(v, x) osal_writel(v, LSADC_VIR_ADDR(LSADC_ADDR_OFFSET(x)))
#define lsadc_readl(x) osal_readl(LSADC_VIR_ADDR(LSADC_ADDR_OFFSET(x)))
static inline xmedia_void lsadc_reg_write(xmedia_ulong value, xmedia_ulong mask,
xmedia_ulong addr)
{
xmedia_ulong t;
t = lsadc_readl(addr);
t &= ~mask;
t |= value & mask;
lsadc_writel(t, addr);
}
static xmedia_void write_reg32(xmedia_u32 value, xmedia_u32 mask, const xmedia_void* addr)
{
xmedia_u32 t;
t = osal_readl((xmedia_void*)addr);
t &= ~mask;
t |= value & mask;
osal_writel(t, (xmedia_void*)addr);
}
static xmedia_void lsadc_enable_clock(xmedia_void)
{
xmedia_void *lsadc_crg_addr;
lsadc_crg_addr = osal_ioremap(LSADC_CRG_ADDR, (xmedia_ulong)0x4);
write_reg32(0x0 << 2, 0x1 << 2, lsadc_crg_addr); // (bit2)ls_adc_srst_req = 0
osal_udelay(200);
write_reg32(0x1 << 3, 0x1 << 3, lsadc_crg_addr); // (bit3)ls_adc_cken = 1
osal_udelay(100);
osal_iounmap((xmedia_void*)lsadc_crg_addr);
}
static xmedia_void lsadc_disable_clock(xmedia_void)
{
xmedia_void *lsadc_crg_addr;
lsadc_crg_addr = osal_ioremap(LSADC_CRG_ADDR, (xmedia_ulong)0x4);
write_reg32(0x0 << 3, 0x1 << 3, lsadc_crg_addr); // (bit3)ls_adc_cken = 0
osal_udelay(100);
write_reg32(0x1 << 2, 0x1 << 2, lsadc_crg_addr); // (bit2)ls_adc_srst_req = 1
osal_udelay(100);
osal_iounmap((xmedia_void*)lsadc_crg_addr);
}
static xmedia_s32 lsadc_open(xmedia_void* private_data)
{
lsadc_enable_clock();
lsadc_reg_write(1 << 15, 1 << 15, LSADC_CONFIG); // exit reset state
lsadc_reg_write(1 << 9, 1 << 9 , LSADC_ENABLE); // enable lsadc
return 0;
}
static xmedia_s32 lsadc_release(xmedia_void* private_data)
{
lsadc_reg_write(0 << 15, 1 << 15, LSADC_CONFIG); // enter reset state
lsadc_reg_write(0 << 9, 1 << 9 , LSADC_ENABLE); // disable lsadc
lsadc_disable_clock();
return 0;
}
/* 0: single scanning mode
* 1: continuous scanning mode
* The filter glitch is already eanble, only the continuous mode has a filter glitch, and the single mode is invalid.
*/
static xmedia_s32 lsadc_model_select(xmedia_s32 value)
{
xmedia_u32 val;
val = (xmedia_u32)value;
if ((val != 0) && (val != 1)) {
lsadc_print("error value:%x\n", val);
return -1;
}
lsadc_reg_write(val << 13, 1 << 13, LSADC_CONFIG);
if (val == 1) {
lsadc_reg_write(LSADC_ACTIVE_BIT, LSADC_ACTIVE_BIT_MASK, LSADC_ACTBIT); // [11:0]
lsadc_reg_write(LSADC_DATA_DELTA << 20, LSADC_DATA_DELTA_MASK << 20, LSADC_CONFIG); // [23:20]
lsadc_writel(LSADC_GLITCH_SAMPLE, LSADC_GLITCH); //glitch_sample, must > 0
lsadc_writel(LSADC_TIME_SCAN, LSADC_TIMESCAN); //time_scan, must > 20
/* set filter glitch function, 0:enable, 1:bypass, attention to LSADC_CTRL10(0x120a0028),
* lsadc will filter the lsadc_zero value in this reg;defaule value is 0
*/
lsadc_reg_write(0 << 17, 1 << 17, LSADC_CONFIG);
} else {
lsadc_reg_write(1 << 17, 1 << 17, LSADC_CONFIG); //set glitch bypass
}
return 0;
}
static xmedia_s32 lsadc_chn_valid(xmedia_s32 chn, xmedia_s32 enable)
{
xmedia_ulong value;
value = enable ? 1 : 0;
switch (chn)
{
#ifdef USE_LSADC_CHANNEL_0
case 0:
lsadc_reg_write(value << 8, 1 << 8, LSADC_CONFIG);
break;
#endif
case 1:
lsadc_reg_write(value << 9, 1 << 9, LSADC_CONFIG);
break;
case 2:
lsadc_reg_write(value << 10, 1 << 10, LSADC_CONFIG);
break;
default:
lsadc_print("error chn:%d\n", chn);
return -1;
}
return 0;
}
static xmedia_s32 lsadc_start(xmedia_void)
{
#ifdef ENABLE_ADC_IRQ
LSADC_SPIN_LOCK_FLAG flag;
#endif
xmedia_s32 model_sel = 0;
xmedia_s32 deglitch_bypass = 0;
xmedia_s32 config = lsadc_readl(LSADC_CONFIG);
xmedia_s32 chn_num = 0;
xmedia_s32 sleep_time_ms = 0;
if ( (config & (1 << 13)) != 0 ) {
model_sel = 1;
}
if ( (config & (1 << 17)) != 0 ) {
deglitch_bypass = 1;
}
if (1 == model_sel) {
xmedia_s32 lsadc_active_bit = lsadc_readl(LSADC_ACTBIT) & 0xFFF;
lsadc_print("config=%#x lsadc_active_bit=%#x\n", config, lsadc_active_bit);
#ifdef USE_LSADC_CHANNEL_0
if ( (config & (1 << 8)) != 0 ) {
chn_num++;
}
#endif
if ( (config & (1 << 9)) != 0 ) {
chn_num++;
}
if ( (config & (1 << 10)) != 0 ) {
chn_num++;
}
if (0 == deglitch_bypass) {
xmedia_s32 sleep_time_us = LSADC_GLITCH_TIME_US * chn_num;
sleep_time_ms = sleep_time_us / 1000;
if (0 != (sleep_time_us % 1000))
sleep_time_ms += 1;
} else {
sleep_time_ms = 1;
lsadc_print("bypass in continuous scan mode! sleep_time_ms=%d\n", sleep_time_ms);
}
} else {
sleep_time_ms = 1;
}
#ifdef ENABLE_ADC_IRQ
osal_memset(g_cdc_info, 0, sizeof(g_cdc_info));
osal_spin_lock_irqsave(&g_lsadc_spin_lock, &flag);
lsadc_reg_write(1, 1, LSADC_INTEN); //inten enable
lsadc_reg_write(LSADC_ALL_CHN_INT_MASK, LSADC_ALL_CHN_INT_MASK, LSADC_INTCLR); //clr all intflag
osal_spin_unlock_irqrestore(&g_lsadc_spin_lock, &flag);
#endif
lsadc_reg_write(1, 1, LSADC_START); //start
osal_msleep(sleep_time_ms); // wait for scan & calculate equ_vaule.
return 0;
}
static xmedia_s32 lsadc_stop(xmedia_void)
{
#ifdef ENABLE_ADC_IRQ
LSADC_SPIN_LOCK_FLAG flag;
#endif
lsadc_reg_write(1, 1, LSADC_STOP); //stop
#ifdef ENABLE_ADC_IRQ
osal_spin_lock_irqsave(&g_lsadc_spin_lock, &flag);
osal_memset(g_cdc_info, 0, sizeof(g_cdc_info));
lsadc_reg_write(0, 1, LSADC_INTEN); //inten disable
osal_spin_unlock_irqrestore(&g_lsadc_spin_lock, &flag);
#endif
return 0;
}
static xmedia_s32 lsadc_get_chn_value(xmedia_s32 chn)
{
xmedia_u32 unchn;
#ifdef USE_LSADC_CHANNEL_0
if (chn < 0 || chn >= LSADC_MAX_CHN_NUM) {
#else
if (chn <= 0 || chn >= LSADC_MAX_CHN_NUM) {
#endif
lsadc_print("error chn:%d\n", chn);
return -1;
}
unchn = (xmedia_u32)chn;
#ifdef ENABLE_ADC_IRQ
return g_cdc_info[unchn].adc_value;
#else
return lsadc_readl(LSADC_CHNDATA + (unchn << 2));
#endif
}
#ifdef ENABLE_ADC_IRQ
static xmedia_s32 lsadc_irq_proc(xmedia_s32 irq, xmedia_void* devId)
{
xmedia_u32 intstate;
xmedia_s32 chn_value;
xmedia_u32 chn;
LSADC_SPIN_LOCK_FLAG flag;
xmedia_u32 int_flag;
osal_spin_lock_irqsave(&g_lsadc_spin_lock, &flag);
intstate = lsadc_readl(LSADC_INTSTATUS);
for (chn = 0; chn < LSADC_MAX_CHN_NUM; chn++) {
int_flag = 1 << chn;
if (intstate & (int_flag)) {
chn_value = lsadc_readl(LSADC_CHNDATA + (chn << 2));
g_cdc_info[chn].adc_value = chn_value;
lsadc_reg_write(int_flag, int_flag, LSADC_INTCLR);//clr intflag
}
}
osal_spin_unlock_irqrestore(&g_lsadc_spin_lock, &flag);
return OSAL_IRQ_HANDLED;
}
#endif
static xmedia_slong lsadc_ioctl (xmedia_u32 cmd, xmedia_ulong arg, xmedia_void* private_data)
{
xmedia_s32 ret = -1;
xmedia_s32 param = 0;
switch (cmd) {
case LSADC_IOC_MODEL_SEL:
{
param = *(xmedia_s32*)(uintptr_t)arg;
ret = lsadc_model_select(param);
break;
}
case LSADC_IOC_CHN_ENABLE:
{
param = *(xmedia_s32*)(uintptr_t)arg;
ret = lsadc_chn_valid(param, 1);
break;
}
case LSADC_IOC_CHN_DISABLE:
{
param = *(xmedia_s32*)(uintptr_t)arg;
ret = lsadc_chn_valid(param, 0);
break;
}
case LSADC_IOC_START:
{
ret = lsadc_start();
break;
}
case LSADC_IOC_STOP:
{
ret = lsadc_stop();
break;
}
case LSADC_IOC_GET_CHNVAL:
{
param = *(xmedia_s32*)(uintptr_t)arg;
ret = lsadc_get_chn_value(param);
break;
}
default:
lsadc_print("error cmd:%08x\n", cmd);
ret = -1;
}
return ret;
}
static struct osal_fileops g_lsadc_fops =
{
.open = lsadc_open,
.release = lsadc_release,
.unlocked_ioctl = lsadc_ioctl,
};
static osal_dev_t* g_lsadc_dev;
xmedia_s32 lsadc_init(xmedia_void)
{
xmedia_s32 ret = 0;
if (!lsadc_reg) {
lsadc_reg = (volatile xmedia_void*)osal_ioremap(LSADC_BASE_ADDR, 0x100);
if (!lsadc_reg) {
lsadc_print("lsadc ioremap error.\n");
return -1;
}
}
#ifdef ENABLE_ADC_IRQ
lsadc_print("lsadc_irq=%d.\n", lsadc_irq);
if (lsadc_irq <= 0) {
lsadc_irq = LSADC_IRQ_ID;
lsadc_print("lsadc_irq=LSADC_IRQ_ID(%d).\n", lsadc_irq);
}
osal_spin_lock_init(&g_lsadc_spin_lock);
ret = osal_request_irq(lsadc_irq, lsadc_irq_proc, XMEDIA_NULL, "lsadc", &g_lsadc_fops);
if (ret != 0) {
lsadc_print("lsadc request irq error.\n");
osal_iounmap((xmedia_void*)lsadc_reg);
return -1;
}
#endif
g_lsadc_dev = osal_createdev("lsadc");
g_lsadc_dev->minor = 255;
g_lsadc_dev->fops = &g_lsadc_fops;
ret = osal_registerdevice(g_lsadc_dev);
if (ret != 0) {
osal_destroydev(g_lsadc_dev);
lsadc_print("lsadc register device error.\n");
osal_free_irq(lsadc_irq, &g_lsadc_fops);
osal_iounmap((xmedia_void*)lsadc_reg);
}
osal_printk("load xm_adc.ko OK!\n");
return ret;
}
xmedia_void lsadc_exit(xmedia_void)
{
#ifdef ENABLE_ADC_IRQ
osal_free_irq(lsadc_irq, &g_lsadc_fops);
osal_spin_lock_destory(&g_lsadc_spin_lock);
#endif
osal_deregisterdevice(g_lsadc_dev);
osal_destroydev(g_lsadc_dev);
lsadc_disable_clock();
osal_printk("unload xm_adc.ko OK!\n");
}