rt-thread/bsp/allwinner/libraries/sunxi-hal/hal/source/gpadc/hal_gpadc.c

495 lines
14 KiB
C
Raw Normal View History

/* 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 <hal_interrupt.h>
#include <hal_clk.h>
#include <sunxi_hal_gpadc.h>
#include <hal_reset.h>
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);