/* * Copyright (c) XMEDIA. All rights reserved. */ #include "osal.h" #include #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"); }