/* Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved. * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in * the the People's Republic of China and other countries. * All Allwinner Technology Co.,Ltd. trademarks are used with permission. * DISCLAIMER * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT. * IF YOU NEED TO INTEGRATE THIRD PARTY¡¯S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.) * IN ALLWINNERS¡¯SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES. * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE. * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY¡¯S TECHNOLOGY. * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND, * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include hal_gpadc_t hal_gpadc; #if defined(CONFIG_SOC_SUN20IW1) static hal_gpadc_status_t hal_gpadc_clk_init(hal_gpadc_t *gpadc) { hal_clk_type_t clk_type = HAL_SUNXI_CCU; hal_clk_id_t gpadc_clk_id = gpadc->bus_clk; hal_clk_t mclk; hal_reset_type_t reset_type = HAL_SUNXI_RESET; hal_reset_id_t gpadc_reset_id = gpadc->rst_clk; struct reset_control *reset; mclk = hal_clock_get(clk_type, gpadc_clk_id); if(hal_clock_enable(mclk)) { GPADC_ERR("gpadc clk enable failed!\n"); return GPADC_ERROR; } gpadc->mbus_clk = mclk; reset = hal_reset_control_get(reset_type, gpadc_reset_id); if (hal_reset_control_deassert(reset)) { GPADC_ERR("gpadc reset deassert failed!\n"); return GPADC_ERROR; } hal_reset_control_put(reset); return GPADC_OK; } #else static hal_gpadc_status_t hal_gpadc_clk_init(hal_gpadc_t *gpadc) { #if !defined(CONFIG_ARCH_SUN8IW18P1) if (hal_clk_set_parent(gpadc->mclk, gpadc->pclk)) { GPADC_ERR("[gpadc] clk set parent failed!"); return GPADC_ERROR; } #endif if (hal_clock_enable(gpadc->mclk)) { GPADC_ERR("[gpadc] clk enable mclk failed!"); return GPADC_ERROR; } return GPADC_OK; } #endif static int gpadc_channel_check_valid(hal_gpadc_channel_t channal) { hal_gpadc_t *gpadc = &hal_gpadc; return channal < gpadc->channel_num ? 0 : -1 ; } static void gpadc_channel_select(hal_gpadc_channel_t channal) { uint32_t reg_val; hal_gpadc_t *gpadc = &hal_gpadc; reg_val = readl((unsigned long)(gpadc->reg_base) + GP_CS_EN_REG); reg_val |= (0x01 << channal); writel(reg_val, (unsigned long)(gpadc->reg_base) + GP_CS_EN_REG); } static void gpadc_channel_deselect(hal_gpadc_channel_t channal) { uint32_t reg_val; hal_gpadc_t *gpadc = &hal_gpadc; reg_val = readl((unsigned long)(gpadc->reg_base) + GP_CS_EN_REG); reg_val &= ~(0x01 << channal); writel(reg_val, (unsigned long)(gpadc->reg_base) + GP_CS_EN_REG); } static void gpadc_compare_select(hal_gpadc_channel_t channal) { uint32_t reg_val; hal_gpadc_t *gpadc = &hal_gpadc; reg_val = readl((unsigned long)(gpadc->reg_base) + GP_CS_EN_REG); reg_val |= (GP_CH0_CMP_EN << channal); writel(reg_val, (unsigned long)(gpadc->reg_base) + GP_CS_EN_REG); } static void gpadc_compare_deselect(hal_gpadc_channel_t channal) { uint32_t reg_val; hal_gpadc_t *gpadc = &hal_gpadc; reg_val = readl((unsigned long)(gpadc->reg_base) + GP_CTRL_REG); reg_val &= ~(GP_CH0_CMP_EN << channal); writel(reg_val, (unsigned long)(gpadc->reg_base) + GP_CTRL_REG); } static void gpadc_channel_enable_lowirq(hal_gpadc_channel_t channal) { uint32_t reg_val; hal_gpadc_t *gpadc = &hal_gpadc; reg_val = readl((unsigned long)(gpadc->reg_base) + GP_DATAL_INTC_REG); reg_val |= (0x01 << channal); writel(reg_val, (unsigned long)(gpadc->reg_base) + GP_DATAL_INTC_REG); } static void gpadc_channel_disable_lowirq(hal_gpadc_channel_t channal) { uint32_t reg_val; hal_gpadc_t *gpadc = &hal_gpadc; reg_val = readl((unsigned long)(gpadc->reg_base) + GP_DATAL_INTC_REG); reg_val &= ~(0x01 << channal); writel(reg_val, (unsigned long)(gpadc->reg_base) + GP_DATAL_INTC_REG); } static void gpadc_channel_compare_lowdata(hal_gpadc_channel_t channal, uint32_t low_uv) { uint32_t reg_val = 0, low = 0, unit = 0; hal_gpadc_t *gpadc = &hal_gpadc; /* analog voltage range 0~1.8v, 12bits sample rate, unit=1.8v/(2^12) */ unit = VOL_RANGE / 4096; /* 12bits sample rate */ low = low_uv / unit; if (low > VOL_VALUE_MASK) { low = VOL_VALUE_MASK; } reg_val = readl((unsigned long)(gpadc->reg_base) + GP_CH0_CMP_DATA_REG + 4 * channal); reg_val &= ~VOL_VALUE_MASK; reg_val |= (low & VOL_VALUE_MASK); writel(reg_val, (unsigned long)(gpadc->reg_base) + GP_CH0_CMP_DATA_REG + 4 * channal); } static void gpadc_channel_enable_highirq(hal_gpadc_channel_t channal) { uint32_t reg_val; hal_gpadc_t *gpadc = &hal_gpadc; reg_val = readl((unsigned long)(gpadc->reg_base) + GP_DATAH_INTC_REG); reg_val |= (1 << channal); writel(reg_val, (unsigned long)(gpadc->reg_base) + GP_DATAH_INTC_REG); } static void gpadc_channel_disable_highirq(hal_gpadc_channel_t channal) { uint32_t reg_val; hal_gpadc_t *gpadc = &hal_gpadc; reg_val = readl((unsigned long)(gpadc->reg_base) + GP_DATAH_INTC_REG); reg_val &= ~(1 << channal); writel(reg_val, (unsigned long)(gpadc->reg_base) + GP_DATAH_INTC_REG); } static void gpadc_channel_compare_highdata(hal_gpadc_channel_t channal, uint32_t hig_uv) { uint32_t reg_val = 0, hig_val = 0, unit_val = 0; hal_gpadc_t *gpadc = &hal_gpadc; /* anolog voltage range 0~1.8v, 12bits sample rate, unit=1.8v/(2^12) */ unit_val = VOL_RANGE / 4096; /* 12bits sample rate */ hig_val = hig_uv / unit_val; if (hig_val > VOL_VALUE_MASK) { hig_val = VOL_VALUE_MASK; } reg_val = readl((unsigned long)(gpadc->reg_base) + GP_CH0_CMP_DATA_REG + 4 * channal); reg_val &= ~(VOL_VALUE_MASK << 16); reg_val |= (hig_val & VOL_VALUE_MASK) << 16; writel(reg_val, (unsigned long)(gpadc->reg_base) + GP_CH0_CMP_DATA_REG + 4 * channal); } /* clk_in: source clock, round_clk: sample rate */ static void gpadc_sample_rate_set(uint32_t reg_base, uint32_t clk_in, uint32_t round_clk) { uint32_t div, reg_val; if (round_clk > clk_in) { GPADC_ERR("invalid round clk!"); } div = clk_in / round_clk - 1 ; reg_val = readl((unsigned long)(reg_base) + GP_SR_REG); reg_val &= ~GP_SR_CON; reg_val |= (div << 16); writel(reg_val, (unsigned long)(reg_base) + GP_SR_REG); } static void gpadc_calibration_enable(uint32_t reg_base) { uint32_t reg_val; reg_val = readl((unsigned long)(reg_base) + GP_CTRL_REG); reg_val |= GP_CALI_EN; writel(reg_val, (unsigned long)(reg_base) + GP_CTRL_REG); } static void gpadc_mode_select(uint32_t reg_base, enum gp_select_mode mode) { uint32_t reg_val; reg_val = readl((unsigned long)(reg_base) + GP_CTRL_REG); reg_val &= ~GP_MODE_SELECT; reg_val |= (mode << 18); writel(reg_val, (unsigned long)(reg_base) + GP_CTRL_REG); } /* enable gpadc function, true:enable, false:disable */ static void gpadc_enable(uint32_t reg_base) { uint32_t reg_val = 0; reg_val = readl((unsigned long)(reg_base) + GP_CTRL_REG); reg_val |= GP_ADC_EN; writel(reg_val, (unsigned long)(reg_base) + GP_CTRL_REG); } /* enable gpadc function, true:enable, false:disable */ static void gpadc_disable(uint32_t reg_base) { uint32_t reg_val = 0; reg_val = readl((unsigned long)(reg_base) + GP_CTRL_REG); reg_val &= ~GP_ADC_EN; writel(reg_val, (unsigned long)(reg_base) + GP_CTRL_REG); } static uint32_t gpadc_read_channel_irq_enable(uint32_t reg_base) { return readl((unsigned long)(reg_base) + GP_DATA_INTC_REG); } static uint32_t gpadc_read_channel_lowirq_enable(uint32_t reg_base) { return readl((unsigned long)(reg_base) + GP_DATAL_INTC_REG); } static uint32_t gpadc_read_channel_highirq_enable(uint32_t reg_base) { return readl((unsigned long)(reg_base) + GP_DATAH_INTC_REG); } static uint32_t gpadc_channel_irq_status(uint32_t reg_base) { return readl((unsigned long)(reg_base) + GP_DATA_INTS_REG); } static void gpadc_channel_clear_irq(uint32_t reg_base, uint32_t flags) { writel(flags, (unsigned long)(reg_base) + GP_DATA_INTS_REG); } static uint32_t gpadc_channel_lowirq_status(uint32_t reg_base) { return readl((unsigned long)(reg_base) + GP_DATAL_INTS_REG); } static void gpadc_channel_clear_lowirq(uint32_t reg_base, uint32_t flags) { writel(flags, (unsigned long)(reg_base) + GP_DATAL_INTS_REG); } static uint32_t gpadc_channel_highirq_status(uint32_t reg_base) { return readl((unsigned long)(reg_base) + GP_DATAH_INTS_REG); } static void gpadc_channel_clear_highirq(uint32_t reg_base, uint32_t flags) { writel(flags, (unsigned long)(reg_base) + GP_DATAH_INTS_REG); } static int gpadc_read_data(uint32_t reg_base, hal_gpadc_channel_t channal) { return readl((unsigned long)(reg_base) + GP_CH0_DATA_REG + 4 * channal) & GP_CH_DATA_MASK; } int hal_gpadc_callback(uint32_t data_type, uint32_t data) { GPADC_INFO("gpadc interrupt, data_type is %ld", data_type); return 0; } static irqreturn_t gpadc_handler(int irq, void *dev) { hal_gpadc_t *gpadc = (hal_gpadc_t *)dev; uint32_t reg_val, reg_low, reg_high; uint32_t reg_enable, reg_enable_low, reg_enable_high; uint32_t i, data = 0; reg_enable = gpadc_read_channel_irq_enable(gpadc->reg_base); reg_enable_low = gpadc_read_channel_lowirq_enable(gpadc->reg_base); reg_enable_high = gpadc_read_channel_highirq_enable(gpadc->reg_base); reg_val = gpadc_channel_irq_status(gpadc->reg_base); gpadc_channel_clear_irq(gpadc->reg_base, reg_val); reg_low = gpadc_channel_lowirq_status(gpadc->reg_base); gpadc_channel_clear_lowirq(gpadc->reg_base, reg_val); reg_high = gpadc_channel_highirq_status(gpadc->reg_base); gpadc_channel_clear_highirq(gpadc->reg_base, reg_val); for (i = 0; i < gpadc->channel_num; i++) { if (reg_low & (1 << i) & reg_enable_low) { data = gpadc_read_data(gpadc->reg_base, i); gpadc_channel_enable_highirq(i); if (gpadc->callback[i]) { gpadc->callback[i](GPADC_DOWN, data); } } if (reg_high & (1 << i) & reg_enable_high) { gpadc_channel_disable_highirq(i); gpadc->callback[i](GPADC_UP, data); } } return 0; } hal_gpadc_status_t hal_gpadc_register_callback(hal_gpadc_channel_t channal, gpadc_callback_t user_callback) { hal_gpadc_t *gpadc = &hal_gpadc; if (gpadc_channel_check_valid(channal)) { return GPADC_CHANNEL_ERROR; } if (user_callback == NULL) { return GPADC_ERROR; } gpadc->callback[channal] = user_callback; return GPADC_OK; } hal_gpadc_status_t hal_gpadc_channel_init(hal_gpadc_channel_t channal) { hal_gpadc_t *gpadc = &hal_gpadc; if (gpadc_channel_check_valid(channal)) { return GPADC_CHANNEL_ERROR; } gpadc_channel_select(channal); gpadc_compare_select(channal); gpadc_channel_enable_lowirq(channal); gpadc_channel_compare_lowdata(channal, COMPARE_LOWDATA); gpadc_channel_compare_highdata(channal, COMPARE_HIGDATA); return GPADC_OK; } hal_gpadc_status_t hal_gpadc_channel_exit(hal_gpadc_channel_t channal) { hal_gpadc_t *gpadc = &hal_gpadc; if (gpadc_channel_check_valid(channal)) { return GPADC_CHANNEL_ERROR; } gpadc_channel_deselect(channal); gpadc_compare_deselect(channal); gpadc_channel_disable_lowirq(channal); return GPADC_OK; } static void hal_gpadc_setup(hal_gpadc_t *gpadc) { uint8_t i; gpadc->reg_base = GPADC_BASE; gpadc->channel_num = CHANNEL_NUM; gpadc->irq_num = SUNXI_GPADC_IRQ; gpadc->sample_rate = DEFAULT_SR; #if defined(CONFIG_SOC_SUN20IW1) gpadc->bus_clk = CLK_BUS_GPADC; gpadc->rst_clk = RST_BUS_GPADC; #else gpadc->pclk = HAL_CLK_SRC_HOSC24M; gpadc->mclk = HAL_CLK_PERIPH_GPADC; #endif gpadc->mode = GP_CONTINUOUS_MODE; for (i = 0; i < gpadc->channel_num; i++) { gpadc->callback[i] = hal_gpadc_callback; } }; int hal_gpadc_init(void) { hal_gpadc_t *gpadc = &hal_gpadc; hal_gpadc_setup(gpadc); if (hal_gpadc_clk_init(gpadc)) { GPADC_ERR("gpadc init clk error"); return GPADC_CLK_ERROR; } GPADC_INFO("gpadc set sample rate"); gpadc_sample_rate_set(gpadc->reg_base, OSC_24MHZ, gpadc->sample_rate); if (request_irq(gpadc->irq_num, gpadc_handler, IRQF_NO_SUSPEND, "gpadc", gpadc)) { return GPADC_IRQ_ERROR; } enable_irq(gpadc->irq_num); GPADC_INFO("gpadc enable calibration"); gpadc_calibration_enable(gpadc->reg_base); gpadc_mode_select(gpadc->reg_base, gpadc->mode); gpadc_enable(gpadc->reg_base); return GPADC_OK; } hal_gpadc_status_t hal_gpadc_deinit(void) { disable_irq(hal_gpadc.irq_num); #if defined(CONFIG_SOC_SUN20IW1) hal_clock_disable(hal_gpadc.mbus_clk); #else hal_clock_disable(hal_gpadc.mclk); #endif return GPADC_OK; } //device_initcall(hal_gpadc_init);