blob: 67a682790089d91610673f5752b98c08154cf934 [file] [log] [blame]
/*
* Copyright C 2011 by Amlogic, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* Remark: migrate from trunk by Hisun Bao 2011.07.29
*
*/
#include <config.h>
#include <common.h>
#include <asm/arch/io.h>
#include <asm/saradc.h>
#include <asm/cpu_id.h>
#include <asm/arch/secure_apb.h>
#ifdef CONFIG_OF_LIBFDT
#include <libfdt.h>
#endif
//#define ENABLE_DYNAMIC_POWER
#define AML_ADC_SAMPLE_DEBUG 0
#define FDT_DEFAULT_ADDRESS 0x01000000
#define P_SAR_ADC_REG0(base) (base + (0x0))
#define P_SAR_ADC_CHAN_LIST(base) (base + (0x1))
#define P_SAR_ADC_AVG_CNTL(base) (base + (0x2))
#define P_SAR_ADC_REG3(base) (base + (0x3))
#define P_SAR_ADC_DELAY(base) (base + (0x4))
#define P_SAR_ADC_LAST_RD(base) (base + (0x5))
#define P_SAR_ADC_FIFO_RD(base) (base + (0x6))
#define P_SAR_ADC_AUX_SW(base) (base + (0x7))
#define P_SAR_ADC_CHAN_10_SW(base) (base + (0x8))
#define P_SAR_ADC_DETECT_IDLE_SW(base) (base + (0x9))
#define P_SAR_ADC_DELTA_10(base) (base + (0xa))
#define P_SAR_ADC_REG11(base) (base + (0xb))
#define P_SAR_ADC_REG13(base) (base + (0xd))
#define SARADC_STATE_IDLE 0
#define SARADC_STATE_BUSY 1
#define SARADC_STATE_SUSPEND 2
#define FLAG_INITIALIZED (1<<28)
//#define FLAG_BUSY (1<<29)
#define FLAG_BUSY_KERNEL (1<<14) /* for kernel:SARADC_DELAY 14bit */
#define FLAG_BUSY_KERNEL_BIT 14
#define FLAG_BUSY_BL30 (1<<15) /* for bl30:SARADC_DELAY 15bit*/
typedef struct {
unsigned char channel;
unsigned char adc_type; /*1:12bit;0:10bit*/
u32 __iomem *saradc_clk_addr;
u32 __iomem *saradc_base_addr;
}saradc_info;
static saradc_info saradc_dev;
static __inline__ void aml_set_reg32_bits(volatile unsigned int *_reg, const uint32_t _value, const uint32_t _start, const uint32_t _len)
{
writel(( (readl((volatile unsigned int *)_reg) & ~((( 1L << (_len) )-1) << (_start))) | ((unsigned)((_value)&((1L<<(_len))-1)) << (_start))), (volatile void *)_reg );
}
static __inline__ uint32_t aml_get_reg32_bits(volatile unsigned int *_reg, const uint32_t _start, const uint32_t _len)
{
return ( (readl((volatile unsigned int *)_reg) >> (_start)) & (( 1L << (_len) ) - 1) );
}
static __inline__ void aml_write_reg32( volatile unsigned int *_reg, const uint32_t _value)
{
writel( _value,(volatile unsigned int *)_reg );
};
static __inline__ uint32_t aml_read_reg32(volatile unsigned int *_reg)
{
return readl((volatile unsigned int *)_reg);
};
saradc_info *saradc_dev_get(void)
{
return &saradc_dev;
}
/*
* description: used to enable and disable the clock of the SARADC
* onoff: 1: enable ; 0: disable
*/
void saradc_clock_switch( int onoff)
{
saradc_info *saradc = saradc_dev_get();
if (onoff) {
if (get_cpu_id().family_id >= MESON_CPU_MAJOR_ID_GXBB) /*GXBB's clock from the clock tree.*/
aml_set_reg32_bits(saradc->saradc_clk_addr,1,8,1);
else
aml_set_reg32_bits(P_SAR_ADC_REG3(saradc->saradc_base_addr),1,30,1);
} else {
if (get_cpu_id().family_id >= MESON_CPU_MAJOR_ID_GXBB) /*GXBB's clock from the clock tree.*/
aml_set_reg32_bits(saradc->saradc_clk_addr,0,8,1);
else
aml_set_reg32_bits(P_SAR_ADC_REG3(saradc->saradc_base_addr),0,30,1);
}
}
/*
* description: used to set the DIV of the clock
*/
void saradc_clock_set(unsigned char val)
{
saradc_info *saradc = saradc_dev_get();
if (get_cpu_id().family_id >= MESON_CPU_MAJOR_ID_GXBB) {
aml_set_reg32_bits(saradc->saradc_clk_addr, 0, 9, 2); /*bit9-bit10: select clk source*/
aml_set_reg32_bits(saradc->saradc_clk_addr, (val & 0xff), 0, 8);/*bit0-bit7: set clk div*/
} else {
aml_set_reg32_bits(P_SAR_ADC_REG3(saradc->saradc_base_addr), (val & 0x3f), 10, 6); /*bit10-bit15: set clk div*/
}
}
static inline void saradc_power_control(int on)
{
saradc_info *saradc = saradc_dev_get();
if (on) {
aml_set_reg32_bits(P_SAR_ADC_REG11(saradc->saradc_base_addr),1,13,1);
aml_set_reg32_bits(P_SAR_ADC_REG11(saradc->saradc_base_addr),3,5,2);
aml_set_reg32_bits(P_SAR_ADC_REG3(saradc->saradc_base_addr),1,21,1);
udelay(5);
saradc_clock_switch(1);
} else {
saradc_clock_switch(0);
/*aml_set_reg32_bits(PP_SAR_ADC_REG11(saradc->saradc_base_addr),0,13,1);*//* disable bandgap */
/*aml_set_reg32_bits(P_SAR_ADC_REG11(saradc->saradc_base_addr),0,5,2);*/
}
}
void saradc_hw_init(void)
{
saradc_info *saradc = saradc_dev_get();
if (get_cpu_id().family_id <= MESON_CPU_MAJOR_ID_GXTVBB)
saradc->adc_type = 0;
else
saradc->adc_type = 1;
aml_write_reg32(P_SAR_ADC_REG0(saradc->saradc_base_addr), 0x84004040);
aml_write_reg32(P_SAR_ADC_CHAN_LIST(saradc->saradc_base_addr), 0);
/* REG2: all chanel set to 8-samples & median averaging mode */
aml_write_reg32(P_SAR_ADC_AVG_CNTL(saradc->saradc_base_addr), 0);
aml_write_reg32(P_SAR_ADC_REG3(saradc->saradc_base_addr), 0x9388000a);
aml_write_reg32(P_SAR_ADC_DELAY(saradc->saradc_base_addr), 0x10a000a);
aml_write_reg32(P_SAR_ADC_AUX_SW(saradc->saradc_base_addr), 0x3eb1a0c);
aml_write_reg32(P_SAR_ADC_CHAN_10_SW(saradc->saradc_base_addr), 0x8c000c);
aml_write_reg32(P_SAR_ADC_DETECT_IDLE_SW(saradc->saradc_base_addr), 0xc000c);
if (saradc->adc_type)
aml_set_reg32_bits(P_SAR_ADC_REG3(saradc->saradc_base_addr),0x1,27,1);
/*select VDDA as ref voltage, otherwise select calibration voltage*/
if (get_cpu_id().family_id >= MESON_CPU_MAJOR_ID_TXLX)
aml_set_reg32_bits(P_SAR_ADC_REG11(saradc->saradc_base_addr),0x1,0,1);
saradc_clock_set(20);
#if AML_ADC_SAMPLE_DEBUG
printf("ADCREG reg0 =%x\n", aml_read_reg32(P_SAR_ADC_REG0(saradc->saradc_base_addr)));
printf("ADCREG ch list =%x\n",aml_read_reg32(P_SAR_ADC_CHAN_LIST(saradc->saradc_base_addr)));
printf("ADCREG avg =%x\n", aml_read_reg32(P_SAR_ADC_AVG_CNTL(saradc->saradc_base_addr)));
printf("ADCREG reg3 =%x\n", aml_read_reg32(P_SAR_ADC_REG3(saradc->saradc_base_addr)));
printf("ADCREG ch72 sw =%x\n",aml_read_reg32(P_SAR_ADC_AUX_SW(saradc->saradc_base_addr)));
printf("ADCREG ch10 sw =%x\n",aml_read_reg32(P_SAR_ADC_CHAN_10_SW(saradc->saradc_base_addr)));
printf("ADCREG detect&idle=%x\n",aml_read_reg32(P_SAR_ADC_DETECT_IDLE_SW(saradc->saradc_base_addr)));
printf("ADCREG GXBB_CLK_REG=%x\n",aml_read_reg32(saradc->saradc_clk_addr));
#endif //AML_ADC_SAMPLE_DEBUG
}
int saradc_probe(void)
{
int ret;
int len;
int parent_offset;
char *fdt_addr;
u32 *reg_addr;
int saradc_fdt_ready = 0;
unsigned long temp_addr;
saradc_info *saradc = saradc_dev_get();
#ifdef CONFIG_OF_LIBFDT
#ifdef CONFIG_DTB_MEM_ADDR
fdt_addr = (char *)CONFIG_DTB_MEM_ADDR;
#else
fdt_addr = (char *)FDT_DEFAULT_ADDRESS;
#endif
if ((ret = fdt_check_header((const void *)fdt_addr)) < 0) {
printf("saradc: check dts: %s, load default parameters\n",
fdt_strerror(ret));
}else{
saradc_fdt_ready = 1;
}
#endif
if (saradc_fdt_ready) {
#ifdef CONFIG_OF_LIBFDT
parent_offset = fdt_path_offset(fdt_addr, "/saradc");
if (parent_offset < 0) {
printf("saradc: not find the node /saradc: %s\n",fdt_strerror(parent_offset));
return -1;
}
reg_addr = (u32 *)fdt_getprop(fdt_addr, parent_offset, "reg", &len);
if (NULL == reg_addr) {
printf("saradc: failed to get /saradc\n");
return -1;
}
/*
* To avoid error "-Werror=int-to-pointer" when this code is compiled,
* and use the variable of type 'unsigned long' to save the address, then cast 'unsigned long' to 'pointer' type."
*/
temp_addr = fdt32_to_cpu(reg_addr[5]); /*big-endian to little-endian*/
saradc->saradc_clk_addr = (u32 __iomem *)(temp_addr & 0xffffffff);
temp_addr = fdt32_to_cpu(reg_addr[1]); /*big-endian to little-endian*/
saradc->saradc_base_addr = (u32 __iomem *)(temp_addr & 0xffffffff);
#endif
} else {
saradc->saradc_clk_addr = (u32 __iomem *)AO_SAR_CLK;
saradc->saradc_base_addr = (u32 __iomem *)AO_SAR_ADC_REG0;
}
saradc_hw_init();
return 0;
}
static void saradc_internal_cal_12bit(void)
{
int val[5]/*, nominal[5] = {0, 1024, 2048, 3072, 4096}*/;
int i;
int abs_val = 4096;
unsigned int abs_num = 0;
unsigned int abs_tmp = 0;
saradc_info *saradc = saradc_dev_get();
/* set CAL_CNTL: 3/4 VDD*/
aml_set_reg32_bits(P_SAR_ADC_REG3(saradc->saradc_base_addr),3,23,3);
for (i = 0; i < 64; i++) {
aml_set_reg32_bits(P_SAR_ADC_REG13(saradc->saradc_base_addr),i,8,6);
udelay(5);
val[0] = get_adc_sample_gxbb_12bit(7);
abs_tmp = abs(2764 - val[0]);
if (abs_tmp < abs_val) {
abs_val = abs_tmp;
abs_num = i;
}
}
aml_set_reg32_bits(P_SAR_ADC_REG13(saradc->saradc_base_addr),abs_num,8,6);
}
//#define NOT_USE_SINA_DETECT 1
//#define CLK_DIV 19
/*
* description: used to get sampling value
* ch: set channel
* use_10bit_num: set the bits of the sampling value(1:10bit; 0:12bit)
*/
int get_adc_sample_gxbb_early(int ch, int use_10bit_num)
{
int value, count, sum;
//unsigned long flags;
//int adc_state = SARADC_STATE_BUSY;
saradc_info *saradc = saradc_dev_get();
count = 0;
while (aml_read_reg32(P_SAR_ADC_DELAY(saradc->saradc_base_addr)) & FLAG_BUSY_BL30) {
udelay(100);
if (++count > 100) {
printf("bl30 busy error\n");
value = -1;
goto end1;
}
}
saradc_clock_switch(0);
saradc_clock_set(0xa0);
saradc_clock_switch(1);
aml_set_reg32_bits(P_SAR_ADC_DELAY(saradc->saradc_base_addr),1,FLAG_BUSY_KERNEL_BIT,1);
aml_set_reg32_bits(P_SAR_ADC_REG3(saradc->saradc_base_addr),1,29,1);
aml_write_reg32(P_SAR_ADC_CHAN_LIST(saradc->saradc_base_addr), ch);
aml_write_reg32(P_SAR_ADC_DETECT_IDLE_SW(saradc->saradc_base_addr), (0xc000c | (ch<<23) | (ch<<7)));
aml_set_reg32_bits(P_SAR_ADC_REG0(saradc->saradc_base_addr), 1,0,1);
aml_set_reg32_bits(P_SAR_ADC_REG0(saradc->saradc_base_addr), 1,2,1);
count = 0;
do {
udelay(10);
if (!(aml_read_reg32(P_SAR_ADC_REG0(saradc->saradc_base_addr)) & 0x70000000))
break;
else if (++count > 10000) {
printf("busy error=%x\n", aml_read_reg32(P_SAR_ADC_REG0(saradc->saradc_base_addr)));
value = -1;
goto end;
}
} while (1);
count = 0;
sum = 0;
while (aml_get_reg32_bits(P_SAR_ADC_REG0(saradc->saradc_base_addr),21,5) && (count < 32)) {
if (aml_get_reg32_bits(P_SAR_ADC_REG0(saradc->saradc_base_addr),26,1))
printf("fifo_count, but fifo empty\n");
value = aml_read_reg32(P_SAR_ADC_FIFO_RD(saradc->saradc_base_addr));
if (((value>>12) & 0x07) == ch) {
if (use_10bit_num) {
//printf("10bit val~\n");
if (saradc->adc_type) {
value &= 0xffc;
value >>= 2;
} else
value &= 0x3ff;
} else {
//printf("12bit val~\n");
value &= 0xfff;
}
sum += value;
count++;
} else
printf("chanel error\n");
}
if (!aml_get_reg32_bits(P_SAR_ADC_REG0(saradc->saradc_base_addr),26,1))
printf("fifo_count=0, but fifo not empty\n");
if (!count) {
value = -1;
goto end;
}
value = sum / count;
//printf("before cal: %d, count=%d\n", value, count);
//value = saradc_get_cal_value(adc, value);
end:
aml_set_reg32_bits(P_SAR_ADC_REG0(saradc->saradc_base_addr),1,14,1);
aml_set_reg32_bits(P_SAR_ADC_REG0(saradc->saradc_base_addr),0,0,1);
saradc_clock_switch(0);
saradc_clock_set(20);
saradc_clock_switch(1);
end1:
aml_set_reg32_bits(P_SAR_ADC_REG3(saradc->saradc_base_addr),0,29,1);
//adc_state = SARADC_STATE_IDLE;
aml_set_reg32_bits(P_SAR_ADC_DELAY(saradc->saradc_base_addr),0,FLAG_BUSY_KERNEL_BIT,1);
return value;
}
int get_adc_sample_gxbb(int ch)
{
int val;
val = get_adc_sample_gxbb_early(ch, 1);
return val;
}
int get_adc_sample_gxbb_12bit(int ch)
{
int val;
val = get_adc_sample_gxbb_early(ch, 0);
return val;
}
int saradc_enable(void)
{
saradc_probe();
saradc_power_control(1);
udelay(10);
saradc_internal_cal_12bit();
return 0;
}
int saradc_disable(void)
{
saradc_power_control(0);
return 0;
}