| /* |
| * 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 |
| * |
| * |
| */ |
| #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 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 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*/ |
| |
| static const char * const ch7_vol[] = { |
| "gnd", |
| "vdd/4", |
| "vdd/2", |
| "vdd*3/4", |
| "vdd", |
| }; |
| |
| typedef struct { |
| unsigned char channel; |
| unsigned char adc_type; /*1:12bit; 0:10bit*/ |
| u32 __iomem *clk_addr; |
| u32 __iomem *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) |
| aml_set_reg32_bits(saradc->clk_addr, 1, 8, 1); |
| else |
| aml_set_reg32_bits(P_SAR_ADC_REG3(saradc->base_addr), 1, 30, 1); |
| } else { |
| if (get_cpu_id().family_id >= MESON_CPU_MAJOR_ID_GXBB) |
| aml_set_reg32_bits(saradc->clk_addr, 0, 8, 1); |
| else |
| aml_set_reg32_bits(P_SAR_ADC_REG3(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) { |
| /*bit[0-7]: set clk div; bit[9-10]: select clk source*/ |
| aml_set_reg32_bits(saradc->clk_addr, 0, 9, 2); |
| aml_set_reg32_bits(saradc->clk_addr, (val & 0xff), 0, 8); |
| } else { |
| /*bit10-bit15: set clk div*/ |
| aml_set_reg32_bits(P_SAR_ADC_REG3(saradc->base_addr), |
| (val & 0x3f), 10, 6); |
| } |
| } |
| |
| 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->base_addr), 1, 13, 1); |
| if (get_cpu_id().family_id >= MESON_CPU_MAJOR_ID_G12A) |
| aml_set_reg32_bits(P_SAR_ADC_REG11(saradc->base_addr), 0, 5, 2); |
| else |
| aml_set_reg32_bits(P_SAR_ADC_REG11(saradc->base_addr), 3, 5, 2); |
| aml_set_reg32_bits(P_SAR_ADC_REG3(saradc->base_addr), 1, 21, 1); |
| |
| udelay(5); |
| saradc_clock_switch(1); |
| } else { |
| saradc_clock_switch(0); |
| aml_set_reg32_bits(P_SAR_ADC_REG3(saradc->base_addr), 0, 21, 1); |
| /*aml_set_reg32_bits(PP_SAR_ADC_REG11(saradc->base_addr),0,13,1);*/ |
| /*aml_set_reg32_bits(P_SAR_ADC_REG11(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->base_addr), 0x84004040); |
| aml_write_reg32(P_SAR_ADC_CHAN_LIST(saradc->base_addr), 0); |
| /* REG2: all chanel set to 8-samples & median averaging mode */ |
| aml_write_reg32(P_SAR_ADC_AVG_CNTL(saradc->base_addr), 0); |
| aml_write_reg32(P_SAR_ADC_REG3(saradc->base_addr), 0x9388000a); |
| aml_write_reg32(P_SAR_ADC_DELAY(saradc->base_addr), 0x10a000a); |
| aml_write_reg32(P_SAR_ADC_AUX_SW(saradc->base_addr), 0x3eb1a0c); |
| aml_write_reg32(P_SAR_ADC_CHAN_10_SW(saradc->base_addr), 0x8c000c); |
| aml_write_reg32(P_SAR_ADC_DETECT_IDLE_SW(saradc->base_addr), 0xc000c); |
| |
| if (saradc->adc_type) |
| aml_set_reg32_bits(P_SAR_ADC_REG3(saradc->base_addr), 0x1, 27, 1); |
| |
| saradc_clock_set(20); |
| } |
| |
| int saradc_probe(void) |
| { |
| int ret; |
| int len; |
| int elems; |
| 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." |
| */ |
| elems = len / sizeof(u32); |
| if (elems == 4) |
| saradc->clk_addr = (u32 __iomem *)AO_SAR_CLK; |
| else if (elems == 8) { |
| temp_addr = fdt32_to_cpu(reg_addr[5]); /*big-endian to little-endian*/ |
| saradc->clk_addr = (u32 __iomem *)(temp_addr & 0xffffffff); |
| } else { |
| printf("saradc: invalid size of property 'reg'\n"); |
| return -1; |
| } |
| temp_addr = fdt32_to_cpu(reg_addr[1]); /*big-endian to little-endian*/ |
| saradc->base_addr = (u32 __iomem *)(temp_addr & 0xffffffff); |
| #endif |
| } else { |
| saradc->clk_addr = (u32 __iomem *)AO_SAR_CLK; |
| saradc->base_addr = (u32 __iomem *)AO_SAR_ADC_REG0; |
| } |
| |
| saradc_hw_init(); |
| |
| return 0; |
| |
| } |
| |
| static void saradc_internal_cal_12bit(void) |
| { |
| /*reference voltage has been calibrated by BL2, there is nothing to do*/ |
| } |
| |
| int saradc_value_trim(int val) |
| { |
| int tmp; |
| |
| switch (get_cpu_id().family_id) { |
| case MESON_CPU_MAJOR_ID_GXL: |
| case MESON_CPU_MAJOR_ID_GXM: |
| case MESON_CPU_MAJOR_ID_TXL: |
| tmp = val * 3072 / 2764; |
| return (tmp < 1024) ? tmp : 1023; |
| break; |
| |
| default: |
| return val; |
| break; |
| } |
| } |
| |
| /* |
| * description: used to get sample value |
| * ch: set channel |
| * use_10bit_num: set the bits of the sample value(1:10bit; 0:12bit) |
| */ |
| int get_adc_sample_gxbb_early(int ch, int use_10bit_num) |
| { |
| int value, count, sum; |
| saradc_info *saradc = saradc_dev_get(); |
| |
| count = 0; |
| |
| while (aml_read_reg32(P_SAR_ADC_DELAY(saradc->base_addr)) & FLAG_BUSY_BL30) { |
| udelay(10); |
| if (++count > 1000) { |
| printf("bl30 busy error\n"); |
| value = -1; |
| goto end1; |
| } |
| } |
| |
| aml_set_reg32_bits(P_SAR_ADC_DELAY(saradc->base_addr), 1, |
| FLAG_BUSY_KERNEL_BIT, 1); |
| |
| saradc_clock_switch(0); |
| saradc_clock_set(0xa0); |
| saradc_clock_switch(1); |
| |
| aml_write_reg32(P_SAR_ADC_CHAN_LIST(saradc->base_addr), ch); |
| aml_write_reg32(P_SAR_ADC_DETECT_IDLE_SW(saradc->base_addr), |
| (0xc000c | (ch<<23) | (ch<<7))); |
| aml_set_reg32_bits(P_SAR_ADC_REG0(saradc->base_addr), 1, 0, 1); |
| aml_set_reg32_bits(P_SAR_ADC_REG0(saradc->base_addr), 1, 2, 1); |
| count = 0; |
| do { |
| udelay(10); |
| if (!(aml_read_reg32(P_SAR_ADC_REG0(saradc->base_addr)) & 0x70000000)) |
| break; |
| else if (++count > 10000) { |
| printf("busy error = %x\n", |
| aml_read_reg32(P_SAR_ADC_REG0(saradc->base_addr))); |
| value = -1; |
| goto end; |
| } |
| } while (1); |
| |
| count = 0; |
| sum = 0; |
| while (aml_get_reg32_bits(P_SAR_ADC_REG0(saradc->base_addr), 21, 5) && \ |
| (count < 32)) { |
| value = aml_read_reg32(P_SAR_ADC_FIFO_RD(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("channel error\n"); |
| } |
| |
| if (!count) { |
| value = -1; |
| goto end; |
| } |
| value = sum / count; |
| end: |
| aml_set_reg32_bits(P_SAR_ADC_REG0(saradc->base_addr), 1, 14, 1); |
| aml_set_reg32_bits(P_SAR_ADC_REG0(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_DELAY(saradc->base_addr), 0, |
| FLAG_BUSY_KERNEL_BIT, 1); |
| |
| return saradc_value_trim(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; |
| } |
| |
| void saradc_sample_test(void) |
| { |
| int i; |
| int val; |
| saradc_info *saradc = saradc_dev_get(); |
| |
| printf("ch7 sample test:\n"); |
| saradc_enable(); |
| for (i = 0; i < ARRAY_SIZE(ch7_vol); i++) { |
| aml_set_reg32_bits(P_SAR_ADC_REG3(saradc->base_addr), i, 23, 3); |
| udelay(10); |
| val = get_adc_sample_gxbb(7); |
| printf("%-7s : %d\n", ch7_vol[i], val); |
| } |
| saradc_disable(); |
| } |