实验目的
由于LS2K0300久久派开发板4.19内核还没有现成可用的ADC驱动,但是龙芯官方的5.10内核已经提供了ADC驱动,想要在4.19内核使用ADC就要参考5.10内核移植驱动,本次实验主要是关于ADC驱动的移植和使用
驱动移植
主要的驱动代码主要有3个:loongson-2k300-adc.c、loongson-2k300-adc-core.c、loongson-2k300-adc-core.h,位于drivers/iio/adc
目录下
loongson-2k300-adc.c
这个是platform_driver代码部分
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/delay.h>
#include "loongson-2k300-adc-core.h"
#define ADC_DATA_MASK 0x0FFF
#define ADC_MAX_VOLTAGE 1800
#define ADC_SCALE 4096
struct ls2k300_adc_data {
void __iomem *base;
spinlock_t read_raw_lock; // 添加读取的自旋锁 扫描模式,单次读取时使用
};
// 这里是有节点可以读取的关键
#define LS2K300_CHANNEL(num) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = num, \
.address = ADC_DR, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = num, \
.scan_type = { \
.sign = 's', \
.realbits = 12, \
.storagebits = 16, \
.shift = 12, \
.endianness = IIO_CPU, \
}, \
}
static const struct iio_chan_spec ls2k300_adc_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(8),
LS2K300_CHANNEL(0),
LS2K300_CHANNEL(1),
LS2K300_CHANNEL(2),
LS2K300_CHANNEL(3),
LS2K300_CHANNEL(4),
LS2K300_CHANNEL(5),
LS2K300_CHANNEL(6),
LS2K300_CHANNEL(7),
// 注入通道
// { .type = IIO_VOLTAGE, .differential = 1, .channel = 0, .address = ADC_JDR1 },
// { .type = IIO_VOLTAGE, .differential = 1, .channel = 1, .address = ADC_JDR2 },
// { .type = IIO_VOLTAGE, .differential = 1, .channel = 2, .address = ADC_JDR3 },
// { .type = IIO_VOLTAGE, .differential = 1, .channel = 3, .address = ADC_JDR4 },
};
// sysfs 下的节点读取时的具体调用函数
static int ls2k300_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct ls2k300_adc_data *data = iio_priv(indio_dev);
unsigned int reg_offset;
unsigned long flags;
switch (mask) {
case IIO_CHAN_INFO_RAW:
// 针对 in_voltagex_raw 节点
if (chan->differential) {
reg_offset = chan->address;
} else {
reg_offset = chan->address;
}
// 由于规则通道组只有一个通道,所以这里需要用锁来限定一段时间只能读取一个通道的值。
spin_lock_irqsave(&data->read_raw_lock, flags);
ADC_RegularChannelConfig(data->base, chan->channel, 1, ADC_SampleTime_64Cycles);
adc_software_start_conv_trigger(data->base, ENABLE, 0);
adc_eoc_check_conv_end(data->base);
*val = ioread16(data->base + reg_offset) & ADC_DATA_MASK;
spin_unlock_irqrestore(&data->read_raw_lock, flags);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
// 针对 in_voltagex_scale 节点
*val = ADC_MAX_VOLTAGE;
*val2 = ADC_SCALE;
// 返回的值就是 val1 / val2 的那个小数值
// scale * raw 就是对应的电压值,单位mV
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
}
static const struct iio_info ls2k300_adc_info = {
.read_raw = ls2k300_adc_read_raw,
};
static int ls2k300_adc_probe(struct platform_device *pdev)
{
adc_init_info adc_init_struct;
struct iio_dev *indio_dev;
struct ls2k300_adc_data *data;
struct resource *res;
int ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&pdev->dev, "devm_iio_device_alloc failed! probe failed!\n");
return -ENOMEM;
}
data = iio_priv(indio_dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->base)) {
dev_err(&pdev->dev, "devm_ioremap_resource failed! probe failed! (base: %.llx)\n", (unsigned long long)data->base);
return PTR_ERR(data->base);
}
// 配置 ADC CR1 和 CR2 等寄存器,具体配置根据需要调整
// 不使用 DMA, 扫描模式, 单次,规则序列转化数为1, 也就是每次都等待读取完毕。
// EOC中断和JEOC中断没开
adc_struct_init(&adc_init_struct);
adc_init_struct.ADC_Mode = ADC_Mode_Independent;
adc_init_struct.ADC_ScanConvMode = ENABLE;
adc_init_struct.ADC_ContinuousConvMode = DISABLE;
adc_init_struct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
adc_init_struct.ADC_DataAlign = ADC_DataAlign_Right;
adc_init_struct.ADC_NbrOfChannel = 1; //规则通道数量 为 1,这是一个一个的触发 扫描模式的单次扫描
adc_init_struct.ADC_ClkDivider = 1; //Loongson Feature
adc_init_struct.ADC_JTrigMod = 0; //Loongson Feature
adc_init_struct.ADC_ADCEdge = 0; //Loongson Feature
adc_init_struct.ADC_DiffMod = 0; //Loongson Feature
adc_init(data->base, &adc_init_struct);
adc_dev_enable(data->base, ENABLE);
// iio 框架信息注册
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &ls2k300_adc_info;
indio_dev->name = pdev->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ls2k300_adc_channels;
indio_dev->num_channels = ARRAY_SIZE(ls2k300_adc_channels);
spin_lock_init(&data->read_raw_lock); // 初始化自旋锁
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(&pdev->dev, "iio_device_register failed! probe failed! (ret: %d)\n", ret);
return ret;
}
platform_set_drvdata(pdev, indio_dev);
dev_info(&pdev->dev, "ADC Device registered successfully\n");
return 0;
}
static int ls2k300_adc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
iio_device_unregister(indio_dev);
return 0;
}
static const struct of_device_id ls2k300_adc_of_match[] = {
{ .compatible = "ls2k300-adc", },
{ }
};
MODULE_DEVICE_TABLE(of, ls2k300_adc_of_match);
static struct platform_driver ls2k300_adc_driver = {
.driver = {
.name = "ls2k300_adc",
.of_match_table = ls2k300_adc_of_match,
},
.probe = ls2k300_adc_probe,
.remove = ls2k300_adc_remove,
};
module_platform_driver(ls2k300_adc_driver);
MODULE_AUTHOR("oujintao [email protected]");
MODULE_DESCRIPTION("2k300 ADC IIO driver");
MODULE_LICENSE("GPL");
loongson-2k300-adc-core.c
这个是关于ADC的操作部分驱动代码:主要是读写寄存器
#include <linux/module.h>
#include <linux/delay.h>
#include "loongson-2k300-adc-core.h"
#define ADC_DATA_MASK 0x0FFF
/*******************************************************************************
* Function Name : adc_init
* Description : Initializes the ADCx peripheral according to the specified parameters
* in the ADC_InitStruct.
* Input : - ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.
* - ADC_InitStruct: pointer to an adc_init_info structure that
* contains the configuration information for the specified
* ADC peripheral.
* Output : None
* Return : None
******************************************************************************/
void adc_init(adc_reg_map* ADCx, adc_init_info* ADC_InitStruct)
{
unsigned int temp_val_1 = 0;
unsigned int temp_val_2 = 0;
/*---------------------------- ADCx CR1 Configuration -----------------*/
/* Get the ADCx CR1 value */
temp_val_1 = ADCx->CR1;
/* Clear DUALMOD and SCAN bits */
temp_val_1 &= CR1_CLEAR_MASK;
/* Configure ADCx: Dual mode and scan conversion mode */
/* Set DUALMOD bits according to ADC_Mode value */
/* Set SCAN bit according to ADC_ScanConvMode value */
temp_val_1 |= (unsigned int)(ADC_InitStruct->ADC_Mode | ((unsigned int)ADC_InitStruct->ADC_ScanConvMode << 8) |
((unsigned int)((ADC_InitStruct->ADC_ClkDivider)&0x3f) << 24) |
((unsigned int)ADC_InitStruct->ADC_DiffMod << 20) |
((unsigned int)ADC_InitStruct->ADC_OutPhaseSel << 30)); //yg
temp_val_1 |= 1 << CR1_EOC_IE_OFFSET;
temp_val_1 ^= 1 << CR1_EOC_IE_OFFSET;
temp_val_1 |= ADC_InitStruct->ADC_Int_EOC << CR1_EOC_IE_OFFSET;
temp_val_1 |= 1 << CR1_J_EOC_IE_OFFSET;
temp_val_1 ^= 1 << CR1_J_EOC_IE_OFFSET;
temp_val_1 |= ADC_InitStruct->ADC_Int_JEOC << CR1_J_EOC_IE_OFFSET;
/* Write to ADCx CR1 */
ADCx->CR1 = temp_val_1;
/*---------------------------- ADCx CR2 Configuration -----------------*/
/* Get the ADCx CR2 value */
temp_val_1 = ADCx->CR2;
/* Clear CONT, ALIGN and EXTSEL bits */
temp_val_1 &= CR2_CLEAR_MASK;
/* Configure ADCx: external trigger event and continuous conversion mode */
/* Set ALIGN bit according to ADC_DataAlign value */
/* Set EXTSEL bits according to ADC_ExternalTrigConv value */
/* Set CONT bit according to ADC_ContinuousConvMode value */
temp_val_1 |= (unsigned int)(ADC_InitStruct->ADC_DataAlign | ADC_InitStruct->ADC_ExternalTrigConv |
((unsigned int)ADC_InitStruct->ADC_ContinuousConvMode << 1) |
((unsigned int)ADC_InitStruct->ADC_JTrigMod << 24) | //yg
((unsigned int)(((ADC_InitStruct->ADC_ClkDivider)>>6)&0xf) << 26) |
((unsigned int)ADC_InitStruct->ADC_ADCEdge << 30) |
((unsigned int)ADC_InitStruct->ADC_ClkMask << 31)) ; //yg
/* Write to ADCx CR2 */
ADCx->CR2 = temp_val_1;
/*---------------------------- ADCx SQR1 Configuration -----------------*/
/* Get the ADCx SQR1 value */
temp_val_1 = ADCx->SQR1;
/* Clear L bits */
temp_val_1 &= SQR1_CLEAR_MASK;
/* Configure ADCx: regular channel sequence length */
/* Set L bits according to ADC_NbrOfChannel value */
temp_val_2 |= (ADC_InitStruct->ADC_NbrOfChannel - 1);
temp_val_1 |= ((unsigned int)temp_val_2 << 20);
/* Write to ADCx SQR1 */
ADCx->SQR1 = temp_val_1;
}
/*******************************************************************************
* Function Name : adc_struct_init
* Description : Fills each ADC_InitStruct member with its default value.
* Input : ADC_InitStruct : pointer to an adc_init_info structure
* which will be initialized.
* Output : None
* Return : None
*******************************************************************************/
void adc_struct_init(adc_init_info* ADC_InitStruct)
{
/* Reset ADC init structure parameters values */
/* Initialize the ADC_Mode member */
ADC_InitStruct->ADC_Mode = ADC_Mode_Independent;
/* initialize the ADC_ScanConvMode member */
ADC_InitStruct->ADC_ScanConvMode = DISABLE;
/* Initialize the ADC_ContinuousConvMode member */
ADC_InitStruct->ADC_ContinuousConvMode = DISABLE;
/* Initialize the ADC_ExternalTrigConv member */
ADC_InitStruct->ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
/* Initialize the ADC_DataAlign member */
ADC_InitStruct->ADC_DataAlign = ADC_DataAlign_Right;
/* Initialize the ADC_NbrOfChannel member */
ADC_InitStruct->ADC_NbrOfChannel = 1;
/* Initialize the ADC_ClkDivider member */
ADC_InitStruct->ADC_ClkDivider = 0xff;
/* Initialize the ADC_JTrigMod member */
ADC_InitStruct->ADC_JTrigMod = 0;
/* Initialize the ADC_ADCEdge member */
ADC_InitStruct->ADC_ADCEdge = 0;
/* Initialize the ADC_DIFFMOD member */
ADC_InitStruct->ADC_DiffMod = 0;
/* Initialize the ADC_OutPhaseSel member */
ADC_InitStruct->ADC_OutPhaseSel = 0;
/* Initialize the ADC_ClkMask member */
ADC_InitStruct->ADC_ClkMask = 0;
/* Initialize CR1 EOC disable */
ADC_InitStruct->ADC_Int_EOC = DISABLE;
/* Initialize CR1 EOC disable */
ADC_InitStruct->ADC_Int_JEOC = DISABLE;
}
void ADC_RegularChannelConfig(adc_reg_map* ADCx, unsigned char ADC_Channel, unsigned char Rank, unsigned char ADC_SampleTime)
{
unsigned int temp_val_1 = 0, temp_val_2 = 0;
/* if ADC_Channel_10 ... ADC_Channel_17 is selected */
if (ADC_Channel > ADC_Channel_9) {
/* Get the old register value */
temp_val_1 = ADCx->SMPR1;
/* Calculate the mask to clear */
temp_val_2 = SMPR1_SMP_MASK << (3 * (ADC_Channel - 10));
/* Clear the old discontinuous mode channel count */
temp_val_1 &= ~temp_val_2;
/* Calculate the mask to set */
temp_val_2 = (unsigned int)ADC_SampleTime << (3 * (ADC_Channel - 10));
/* Set the discontinuous mode channel count */
temp_val_1 |= temp_val_2;
/* Store the new register value */
ADCx->SMPR1 = temp_val_1;
} else { /* ADC_Channel include in ADC_Channel_[0..9] */
/* Get the old register value */
temp_val_1 = ADCx->SMPR2;
/* Calculate the mask to clear */
temp_val_2 = SMPR2_SMP_MASK << (3 * ADC_Channel);
/* Clear the old discontinuous mode channel count */
temp_val_1 &= ~temp_val_2;
/* Calculate the mask to set */
temp_val_2 = (unsigned int)ADC_SampleTime << (3 * ADC_Channel);
/* Set the discontinuous mode channel count */
temp_val_1 |= temp_val_2;
/* Store the new register value */
ADCx->SMPR2 = temp_val_1;
}
/* For Rank 1 to 6 */
if (Rank < 7) {
/* Get the old register value */
temp_val_1 = ADCx->SQR3;
/* Calculate the mask to clear */
temp_val_2 = SQR3_SQ_MASK << (5 * (Rank - 1));
/* Clear the old SQx bits for the selected rank */
temp_val_1 &= ~temp_val_2;
/* Calculate the mask to set */
temp_val_2 = (unsigned int)ADC_Channel << (5 * (Rank - 1));
/* Set the SQx bits for the selected rank */
temp_val_1 |= temp_val_2;
/* Store the new register value */
ADCx->SQR3 = temp_val_1;
} else if (Rank < 13) { /* For Rank 7 to 12 */
/* Get the old register value */
temp_val_1 = ADCx->SQR2;
/* Calculate the mask to clear */
temp_val_2 = SQR2_SQ_MASK << (5 * (Rank - 7));
/* Clear the old SQx bits for the selected rank */
temp_val_1 &= ~temp_val_2;
/* Calculate the mask to set */
temp_val_2 = (unsigned int)ADC_Channel << (5 * (Rank - 7));
/* Set the SQx bits for the selected rank */
temp_val_1 |= temp_val_2;
/* Store the new register value */
ADCx->SQR2 = temp_val_1;
} else { /* For Rank 13 to 16 */
/* Get the old register value */
temp_val_1 = ADCx->SQR1;
/* Calculate the mask to clear */
temp_val_2 = SQR1_SQ_MASK << (5 * (Rank - 13));
/* Clear the old SQx bits for the selected rank */
temp_val_1 &= ~temp_val_2;
/* Calculate the mask to set */
temp_val_2 = (unsigned int)ADC_Channel << (5 * (Rank - 13));
/* Set the SQx bits for the selected rank */
temp_val_1 |= temp_val_2;
/* Store the new register value */
ADCx->SQR1 = temp_val_1;
}
}
/*******************************************************************************
* Function Name : adc_dev_enable
* Description : Enables or disables the specified ADC peripheral.
* Input : - ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.
* - state: new state of the ADCx peripheral. This parameter
* can be: ENABLE or DISABLE.
* Output : None
* Return : None
*******************************************************************************/
void adc_dev_enable(adc_reg_map* ADCx, FunctionalState state)
{
unsigned int temp;
if (state == ENABLE) {
temp = ADCx->CR2;
temp |= 1 << CR2_ADON_OFFSET;
temp ^= 1 << CR2_ADON_OFFSET;
temp |= (state << CR2_ADON_OFFSET);
ADCx->CR2 = temp;
// 触发复位校准和AD校准
ADCx->CR2 = temp;
temp = ADCx->CR2;
temp |= (state << 2) | (state << 3);
ADCx->CR2 = temp;
#if 0
while (1) {
temp = ADCx->CR2;
temp &= 1 << 2;
if (!temp)
break;
}
#else
ndelay(1000000);
#endif
} else {
temp = ADCx->CR2;
temp |= 1 << CR2_ADON_OFFSET;
temp ^= 1 << CR2_ADON_OFFSET;
ADCx->CR2 = temp;
}
}
/*******************************************************************************
* Function Name : adc_software_start_conv_trigger
* Description : Enables or disables the selected ADC software start conversion .
* Input : - ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.
* - state: new state of the selected ADC software start conversion.
* This parameter can be: ENABLE or DISABLE.
* Output : None
* Return : None
*******************************************************************************/
void adc_software_start_conv_trigger(adc_reg_map* ADCx, FunctionalState state, unsigned char J_channel)
{
unsigned int temp;
unsigned char EOC_offset;
unsigned char start_offset;
unsigned char exttrig_offset;
EOC_offset = J_channel ? SR_J_EOC_OFFSET : SR_EOC_OFFSET;
start_offset = J_channel ? CR2_EXTTRIG_SW_J_START_OFFSET : CR2_EXTTRIG_SW_START_OFFSET;
exttrig_offset = J_channel ? CR2_J_EXTTRIG_OFFSET : CR2_EXTTRIG_OFFSET;
if (state == ENABLE) {
temp = ADCx->CR2;
temp |= 1 << exttrig_offset;
ADCx->CR2 = temp;
// 先把 EOC 清除
temp = ADCx->SR;
temp |= 1 << EOC_offset;
temp ^= 1 << EOC_offset;
ADCx->SR = temp;
ADCx->CR2 |= 1 << start_offset;
} else {
/* Disable the selected ADC conversion on external event and stop the selected ADC conversion */
temp = ADCx->CR2;
temp |= 1 << start_offset;
temp ^= 1 << start_offset;
ADCx->CR2 = temp;
}
}
void adc_eoc_check_conv_end(adc_reg_map* ADCx)
{
int max_loop;
unsigned int temp;
max_loop = 0;
while (1) {
temp = ADCx->SR;
temp &= 1 << 1;
ndelay(10);
++max_loop;
if (temp == 2)
break;
if (max_loop == 10000) {
pr_info("adc_eoc_check_conv_end eoc wait timeout\n");
break;
}
}
}
loongson-2k300-adc-core.h
头文件部分:主要是定义一些ADC相关的寄存器宏和结构体
#ifndef __LOONGSON_2K300_ADC_CORE_H__
#define __LOONGSON_2K300_ADC_CORE_H__
typedef enum FunctionalState {
DISABLE = 0,
ENABLE,
} FunctionalState;
// ADC register offsets
#define ADC_SR 0x00
#define ADC_CR1 0x04
#define ADC_CR2 0x08
#define ADC_SMPR1 0x0C
#define ADC_SMPR2 0x10
#define ADC_JOFR1 0x14
#define ADC_JOFR2 0x18
#define ADC_JOFR3 0x1C
#define ADC_JOFR4 0x20
#define ADC_HTR 0x24
#define ADC_LTR 0x28
#define ADC_SQR1 0x2C
#define ADC_SQR2 0x30
#define ADC_SQR3 0x34
#define ADC_JSQR 0x38
#define ADC_JDR1 0x3C
#define ADC_JDR2 0x40
#define ADC_JDR3 0x44
#define ADC_JDR4 0x48
#define ADC_DR 0x4C
typedef struct
{
volatile unsigned int SR;
volatile unsigned int CR1;
volatile unsigned int CR2;
volatile unsigned int SMPR1;
volatile unsigned int SMPR2;
volatile unsigned int JOFR1;
volatile unsigned int JOFR2;
volatile unsigned int JOFR3;
volatile unsigned int JOFR4;
volatile unsigned int HTR;
volatile unsigned int LTR;
volatile unsigned int SQR1;
volatile unsigned int SQR2;
volatile unsigned int SQR3;
volatile unsigned int JSQR;
volatile unsigned int JDR1;
volatile unsigned int JDR2;
volatile unsigned int JDR3;
volatile unsigned int JDR4;
volatile unsigned int DR;
} adc_reg_map;
/******************** (C) COPYRIGHT 2008 STMicroelectronics ********************
* File Name : stm32f10x_adc.h
* Author : MCD Application Team
* Version : V2.0.3
* Date : 09/22/2008
* Description : This file contains all the functions prototypes for the
* ADC firmware library.
********************************************************************************
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*******************************************************************************/
/* Define to prevent recursive inclusion -------------------------------------*/
#define ADC_RCG (12)
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* ADC DISCNUM mask */
// #define CR1_DISCNUM_Reset ((unsigned int)0xFFFF1FFF)
// /* ADC DISCEN mask */
// #define CR1_DISCEN_Set ((unsigned int)0x00000800)
// #define CR1_DISCEN_Reset ((unsigned int)0xFFFFF7FF)
// /* ADC JAUTO mask */
// #define CR1_JAUTO_Set ((unsigned int)0x00000400)
// #define CR1_JAUTO_Reset ((unsigned int)0xFFFFFBFF)
// /* ADC JDISCEN mask */
// #define CR1_JDISCEN_Set ((unsigned int)0x00001000)
// #define CR1_JDISCEN_Reset ((unsigned int)0xFFFFEFFF)
// /* ADC AWDCH mask */
// #define CR1_AWDCH_Reset ((unsigned int)0xFFFFFFE0)
// /* ADC Analog watchdog enable mode mask */
// #define CR1_AWDMode_Reset ((unsigned int)0xFF3FFDFF)
// /* CR1 register Mask */
#define CR1_CLEAR_MASK ((unsigned int)0xFFF0FEFF)
//
#define SR_EOC_OFFSET 0x1
#define SR_J_EOC_OFFSET 0x2
//
#define CR1_EOC_IE_OFFSET 0x5
#define CR1_J_EOC_IE_OFFSET 0x7
/* ADC ADON mask */
#define CR2_ADON_OFFSET 0x0
/* ADC reset */
// #define CR2_ADC_Reset ((unsigned int)0x80000000)
// /* ADC DMA mask */
// #define CR2_DMA_Set ((unsigned int)0x00000100)
// #define CR2_DMA_Reset ((unsigned int)0xFFFFFEFF)
// /* ADC RSTCAL mask */
// #define CR2_RSTCAL_Set ((unsigned int)0x00000008)
// /* ADC CAL mask */
// #define CR2_CAL_Set ((unsigned int)0x00000004)
// /* ADC SWSTART mask */
// #define CR2_SWSTART_Set ((unsigned int)0x00400000)
// /* ADC EXTTRIG mask */
// #define CR2_EXTTRIG_Set ((unsigned int)0x00100000)
// #define CR2_EXTTRIG_Reset ((unsigned int)0xFFEFFFFF)
/* ADC Software start mask */
#define CR2_EXTTRIG_SW_START_OFFSET 22
#define CR2_EXTTRIG_SW_J_START_OFFSET 21
#define CR2_EXTTRIG_OFFSET 20
#define CR2_J_EXTTRIG_OFFSET 15
// #define CR2_EXTTRIG_SWSTART_Set ((unsigned int)0x00500000)
// #define CR2_EXTTRIG_SWSTART_Reset ((unsigned int)0xFFAFFFFF)
/* ADC JEXTSEL mask */
// #define CR2_JEXTSEL_Reset ((unsigned int)0xFFFF8FFF)
// /* ADC JEXTTRIG mask */
// #define CR2_JEXTTRIG_Set ((unsigned int)0x00008000)
// #define CR2_JEXTTRIG_Reset ((unsigned int)0xFFFF7FFF)
// /* ADC JSWSTART mask */
// #define CR2_JSWSTART_Set ((unsigned int)0x00200000)
// /* ADC injected software start mask */
// #define CR2_JEXTTRIG_JSWSTART_Set ((unsigned int)0x00208000)
// #define CR2_JEXTTRIG_JSWSTART_Reset ((unsigned int)0xFFDF7FFF)
// /* ADC TSPD mask */
// #define CR2_TSVREFE_Set ((unsigned int)0x00800000)
// #define CR2_TSVREFE_Reset ((unsigned int)0xFF7FFFFF)
// /* CR2 register Mask */
#define CR2_CLEAR_MASK ((unsigned int)0xFFF1F7FD)
// /* ADC SQx mask */
#define SQR3_SQ_MASK 0x1F
#define SQR2_SQ_MASK 0x1F
#define SQR1_SQ_MASK 0x1F
// /* SQR1 register Mask */
#define SQR1_CLEAR_MASK ((unsigned int)0xFF0FFFFF)
// /* ADC JSQx mask */
// #define JSQR_JSQ_Set ((unsigned int)0x0000001F)
// /* ADC JL mask */
// #define JSQR_JL_Set ((unsigned int)0x00300000)
// #define JSQR_JL_Reset ((unsigned int)0xFFCFFFFF)
// /* ADC SMPx mask */
#define SMPR1_SMP_MASK 0x7
#define SMPR2_SMP_MASK 0x7
// /* ADC JDRx registers offset */
// #define JDR_Offset ((unsigned char)0x28)
// #define DISABLE 0
// #define ENABLE 1
/* Includes ------------------------------------------------------------------*/
// #include "../i2c/ls2k0300_map.h"
/* Exported types ------------------------------------------------------------*/
/* ADC Init structure definition */
typedef struct
{
unsigned int ADC_Mode;
int ADC_ScanConvMode; //CR1 scan
int ADC_ContinuousConvMode;//CR2 cont
unsigned int ADC_ExternalTrigConv;//CR2 extsel
unsigned int ADC_DataAlign;//CR2 align
unsigned char ADC_NbrOfChannel;//SQR1 l
unsigned short ADC_ClkDivider;//CR1 clkdiv
unsigned char ADC_JTrigMod;//CR2 jtrigmod
unsigned char ADC_ADCEdge;//CR2 adcedge
unsigned char ADC_DiffMod;//CR1 diffmod
unsigned char ADC_OutPhaseSel;//CR1 ops
unsigned char ADC_ClkMask;//CR2 clkmask
unsigned char ADC_Int_EOC; // CR1 EOC enable ?
unsigned char ADC_Int_JEOC; // CR1 JEOC enable ?
}adc_init_info;
/* Exported constants --------------------------------------------------------*/
/* ADC dual mode -------------------------------------------------------------*/
#define ADC_Mode_Independent ((unsigned int)0x00000000)
#define ADC_Mode_RegInjecSimult ((unsigned int)0x00010000)
#define ADC_Mode_RegSimult_AlterTrig ((unsigned int)0x00020000)
#define ADC_Mode_InjecSimult_FastInterl ((unsigned int)0x00030000)
#define ADC_Mode_InjecSimult_SlowInterl ((unsigned int)0x00040000)
#define ADC_Mode_InjecSimult ((unsigned int)0x00050000)
#define ADC_Mode_RegSimult ((unsigned int)0x00060000)
#define ADC_Mode_FastInterl ((unsigned int)0x00070000)
#define ADC_Mode_SlowInterl ((unsigned int)0x00080000)
#define ADC_Mode_AlterTrig ((unsigned int)0x00090000)
#define IS_ADC_MODE(MODE) (((MODE) == ADC_Mode_Independent) || \
((MODE) == ADC_Mode_RegInjecSimult) || \
((MODE) == ADC_Mode_RegSimult_AlterTrig) || \
((MODE) == ADC_Mode_InjecSimult_FastInterl) || \
((MODE) == ADC_Mode_InjecSimult_SlowInterl) || \
((MODE) == ADC_Mode_InjecSimult) || \
((MODE) == ADC_Mode_RegSimult) || \
((MODE) == ADC_Mode_FastInterl) || \
((MODE) == ADC_Mode_SlowInterl) || \
((MODE) == ADC_Mode_AlterTrig))
/* ADC extrenal trigger sources for regular channels conversion --------------*/
/* for ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T1_CC1 ((unsigned int)0x00000000)
#define ADC_ExternalTrigConv_T1_CC2 ((unsigned int)0x00020000)
#define ADC_ExternalTrigConv_T2_CC2 ((unsigned int)0x00060000)
#define ADC_ExternalTrigConv_T3_TRGO ((unsigned int)0x00080000)
#define ADC_ExternalTrigConv_T4_CC4 ((unsigned int)0x000A0000)
#define ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO ((unsigned int)0x000C0000)
/* for ADC1, ADC2 and ADC3 */
#define ADC_ExternalTrigConv_T1_CC3 ((unsigned int)0x00040000)
#define ADC_ExternalTrigConv_None ((unsigned int)0x000E0000)
/* for ADC3 */
#define ADC_ExternalTrigConv_T3_CC1 ((unsigned int)0x00000000)
#define ADC_ExternalTrigConv_T2_CC3 ((unsigned int)0x00020000)
#define ADC_ExternalTrigConv_T8_CC1 ((unsigned int)0x00060000)
#define ADC_ExternalTrigConv_T8_TRGO ((unsigned int)0x00080000)
#define ADC_ExternalTrigConv_T5_CC1 ((unsigned int)0x000A0000)
#define ADC_ExternalTrigConv_T5_CC3 ((unsigned int)0x000C0000)
#define IS_ADC_EXT_TRIG(REGTRIG) (((REGTRIG) == ADC_ExternalTrigConv_T1_CC1) || \
((REGTRIG) == ADC_ExternalTrigConv_T1_CC2) || \
((REGTRIG) == ADC_ExternalTrigConv_T1_CC3) || \
((REGTRIG) == ADC_ExternalTrigConv_T2_CC2) || \
((REGTRIG) == ADC_ExternalTrigConv_T3_TRGO) || \
((REGTRIG) == ADC_ExternalTrigConv_T4_CC4) || \
((REGTRIG) == ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO) || \
((REGTRIG) == ADC_ExternalTrigConv_None) || \
((REGTRIG) == ADC_ExternalTrigConv_T3_CC1) || \
((REGTRIG) == ADC_ExternalTrigConv_T2_CC3) || \
((REGTRIG) == ADC_ExternalTrigConv_T8_CC1) || \
((REGTRIG) == ADC_ExternalTrigConv_T8_TRGO) || \
((REGTRIG) == ADC_ExternalTrigConv_T5_CC1) || \
((REGTRIG) == ADC_ExternalTrigConv_T5_CC3))
/* ADC data align ------------------------------------------------------------*/
#define ADC_DataAlign_Right ((unsigned int)0x00000000)
#define ADC_DataAlign_Left ((unsigned int)0x00000800)
#define IS_ADC_DATA_ALIGN(ALIGN) (((ALIGN) == ADC_DataAlign_Right) || \
((ALIGN) == ADC_DataAlign_Left))
/* ADC channels --------------------------------------------------------------*/
#define ADC_Channel_0 ((unsigned char)0x00)
#define ADC_Channel_1 ((unsigned char)0x01)
#define ADC_Channel_2 ((unsigned char)0x02)
#define ADC_Channel_3 ((unsigned char)0x03)
#define ADC_Channel_4 ((unsigned char)0x08)
#define ADC_Channel_5 ((unsigned char)0x09)
#define ADC_Channel_6 ((unsigned char)0x0a)
#define ADC_Channel_7 ((unsigned char)0x0b)
#define ADC_Channel_8 ((unsigned char)0x18)
#define ADC_Channel_9 ((unsigned char)0x19)
#define ADC_Channel_10 ((unsigned char)0x1A)
#define ADC_Channel_11 ((unsigned char)0x1B)
#define ADC_Channel_12 ((unsigned char)0x1C)
#define ADC_Channel_13 ((unsigned char)0x1D)
#define ADC_Channel_14 ((unsigned char)0x1E)
#define ADC_Channel_15 ((unsigned char)0x1F)
#define ADC_Channel_16 ((unsigned char)0x10)
#define ADC_Channel_17 ((unsigned char)0x11)
#define IS_ADC_CHANNEL(CHANNEL) (((CHANNEL) == ADC_Channel_0) || ((CHANNEL) == ADC_Channel_1) || \
((CHANNEL) == ADC_Channel_2) || ((CHANNEL) == ADC_Channel_3) || \
((CHANNEL) == ADC_Channel_4) || ((CHANNEL) == ADC_Channel_5) || \
((CHANNEL) == ADC_Channel_6) || ((CHANNEL) == ADC_Channel_7) || \
((CHANNEL) == ADC_Channel_8) || ((CHANNEL) == ADC_Channel_9) || \
((CHANNEL) == ADC_Channel_10) || ((CHANNEL) == ADC_Channel_11) || \
((CHANNEL) == ADC_Channel_12) || ((CHANNEL) == ADC_Channel_13) || \
((CHANNEL) == ADC_Channel_14) || ((CHANNEL) == ADC_Channel_15) || \
((CHANNEL) == ADC_Channel_16) || ((CHANNEL) == ADC_Channel_17))
/* ADC sampling times --------------------------------------------------------*/
#define ADC_SampleTime_1Cycles ((unsigned char)0x00)
#define ADC_SampleTime_2Cycles ((unsigned char)0x01)
#define ADC_SampleTime_4Cycles ((unsigned char)0x02)
#define ADC_SampleTime_8Cycles ((unsigned char)0x03)
#define ADC_SampleTime_16Cycles ((unsigned char)0x04)
#define ADC_SampleTime_32Cycles ((unsigned char)0x05)
#define ADC_SampleTime_64Cycles ((unsigned char)0x06)
#define ADC_SampleTime_128Cycles ((unsigned char)0x07)
#define IS_ADC_SAMPLE_TIME(TIME) (((TIME) == ADC_SampleTime_1Cycles5) || \
((TIME) == ADC_SampleTime_2Cycles5) || \
((TIME) == ADC_SampleTime_4Cycles5) || \
((TIME) == ADC_SampleTime_8Cycles5) || \
((TIME) == ADC_SampleTime_16Cycles5) || \
((TIME) == ADC_SampleTime_32Cycles5) || \
((TIME) == ADC_SampleTime_64Cycles5) || \
((TIME) == ADC_SampleTime_128Cycles5))
/* ADC extrenal trigger sources for injected channels conversion -------------*/
/* For ADC1 and ADC2 */
#define ADC_ExternalTrigInjecConv_T2_TRGO ((unsigned int)0x00002000)
#define ADC_ExternalTrigInjecConv_T2_CC1 ((unsigned int)0x00003000)
#define ADC_ExternalTrigInjecConv_T3_CC4 ((unsigned int)0x00004000)
#define ADC_ExternalTrigInjecConv_T4_TRGO ((unsigned int)0x00005000)
#define ADC_ExternalTrigInjecConv_Ext_IT15_TIM8_CC4 ((unsigned int)0x00006000)
/* For ADC1, ADC2 and ADC3 */
#define ADC_ExternalTrigInjecConv_T1_TRGO ((unsigned int)0x00000000)
#define ADC_ExternalTrigInjecConv_T1_CC4 ((unsigned int)0x00001000)
#define ADC_ExternalTrigInjecConv_None ((unsigned int)0x00007000)
/* For ADC3 */
#define ADC_ExternalTrigInjecConv_T4_CC3 ((unsigned int)0x00002000)
#define ADC_ExternalTrigInjecConv_T8_CC2 ((unsigned int)0x00003000)
#define ADC_ExternalTrigInjecConv_T8_CC4 ((unsigned int)0x00004000)
#define ADC_ExternalTrigInjecConv_T5_TRGO ((unsigned int)0x00005000)
#define ADC_ExternalTrigInjecConv_T5_CC4 ((unsigned int)0x00006000)
#define IS_ADC_EXT_INJEC_TRIG(INJTRIG) (((INJTRIG) == ADC_ExternalTrigInjecConv_T1_TRGO) || \
((INJTRIG) == ADC_ExternalTrigInjecConv_T1_CC4) || \
((INJTRIG) == ADC_ExternalTrigInjecConv_T2_TRGO) || \
((INJTRIG) == ADC_ExternalTrigInjecConv_T2_CC1) || \
((INJTRIG) == ADC_ExternalTrigInjecConv_T3_CC4) || \
((INJTRIG) == ADC_ExternalTrigInjecConv_T4_TRGO) || \
((INJTRIG) == ADC_ExternalTrigInjecConv_Ext_IT15_TIM8_CC4) || \
((INJTRIG) == ADC_ExternalTrigInjecConv_None) || \
((INJTRIG) == ADC_ExternalTrigInjecConv_T4_CC3) || \
((INJTRIG) == ADC_ExternalTrigInjecConv_T8_CC2) || \
((INJTRIG) == ADC_ExternalTrigInjecConv_T8_CC4) || \
((INJTRIG) == ADC_ExternalTrigInjecConv_T5_TRGO) || \
((INJTRIG) == ADC_ExternalTrigInjecConv_T5_CC4))
/* ADC injected channel selection --------------------------------------------*/
#define ADC_InjectedChannel_1 ((unsigned char)0x14)
#define ADC_InjectedChannel_2 ((unsigned char)0x18)
#define ADC_InjectedChannel_3 ((unsigned char)0x1C)
#define ADC_InjectedChannel_4 ((unsigned char)0x20)
#define IS_ADC_INJECTED_CHANNEL(CHANNEL) (((CHANNEL) == ADC_InjectedChannel_1) || \
((CHANNEL) == ADC_InjectedChannel_2) || \
((CHANNEL) == ADC_InjectedChannel_3) || \
((CHANNEL) == ADC_InjectedChannel_4))
/* ADC analog watchdog selection ---------------------------------------------*/
#define ADC_AnalogWatchdog_SingleRegEnable ((unsigned int)0x00800200)
#define ADC_AnalogWatchdog_SingleInjecEnable ((unsigned int)0x00400200)
#define ADC_AnalogWatchdog_SingleRegOrInjecEnable ((unsigned int)0x00C00200)
#define ADC_AnalogWatchdog_AllRegEnable ((unsigned int)0x00800000)
#define ADC_AnalogWatchdog_AllInjecEnable ((unsigned int)0x00400000)
#define ADC_AnalogWatchdog_AllRegAllInjecEnable ((unsigned int)0x00C00000)
#define ADC_AnalogWatchdog_None ((unsigned int)0x00000000)
#define IS_ADC_ANALOG_WATCHDOG(WATCHDOG) (((WATCHDOG) == ADC_AnalogWatchdog_SingleRegEnable) || \
((WATCHDOG) == ADC_AnalogWatchdog_SingleInjecEnable) || \
((WATCHDOG) == ADC_AnalogWatchdog_SingleRegOrInjecEnable) || \
((WATCHDOG) == ADC_AnalogWatchdog_AllRegEnable) || \
((WATCHDOG) == ADC_AnalogWatchdog_AllInjecEnable) || \
((WATCHDOG) == ADC_AnalogWatchdog_AllRegAllInjecEnable) || \
((WATCHDOG) == ADC_AnalogWatchdog_None))
/* ADC interrupts definition -------------------------------------------------*/
#define ADC_IT_EOC ((unsigned short)0x0220)
#define ADC_IT_AWD ((unsigned short)0x0140)
#define ADC_IT_JEOC ((unsigned short)0x0480)
#define IS_ADC_IT(IT) ((((IT) & (unsigned short)0xF81F) == 0x00) && ((IT) != 0x00))
#define IS_ADC_GET_IT(IT) (((IT) == ADC_IT_EOC) || ((IT) == ADC_IT_AWD) || \
((IT) == ADC_IT_JEOC))
/* ADC flags definition ------------------------------------------------------*/
#define ADC_FLAG_AWD ((unsigned char)0x01)
#define ADC_FLAG_EOC ((unsigned char)0x02)
#define ADC_FLAG_JEOC ((unsigned char)0x04)
#define ADC_FLAG_JSTRT ((unsigned char)0x08)
#define ADC_FLAG_STRT ((unsigned char)0x10)
#define IS_ADC_CLEAR_FLAG(FLAG) ((((FLAG) & (unsigned char)0xE0) == 0x00) && ((FLAG) != 0x00))
#define IS_ADC_GET_FLAG(FLAG) (((FLAG) == ADC_FLAG_AWD) || ((FLAG) == ADC_FLAG_EOC) || \
((FLAG) == ADC_FLAG_JEOC) || ((FLAG)== ADC_FLAG_JSTRT) || \
((FLAG) == ADC_FLAG_STRT))
/* ADC thresholds ------------------------------------------------------------*/
#define IS_ADC_THRESHOLD(THRESHOLD) ((THRESHOLD) <= 0xFFF)
/* ADC injected offset -------------------------------------------------------*/
#define IS_ADC_OFFSET(OFFSET) ((OFFSET) <= 0xFFF)
/* ADC injected length -------------------------------------------------------*/
#define IS_ADC_INJECTED_LENGTH(LENGTH) (((LENGTH) >= 0x1) && ((LENGTH) <= 0x4))
/* ADC injected rank ---------------------------------------------------------*/
#define IS_ADC_INJECTED_RANK(RANK) (((RANK) >= 0x1) && ((RANK) <= 0x4))
/* ADC regular length --------------------------------------------------------*/
#define IS_ADC_REGULAR_LENGTH(LENGTH) (((LENGTH) >= 0x1) && ((LENGTH) <= 0x10))
/* ADC regular rank ----------------------------------------------------------*/
#define IS_ADC_REGULAR_RANK(RANK) (((RANK) >= 0x1) && ((RANK) <= 0x10))
/* ADC regular discontinuous mode number -------------------------------------*/
#define IS_ADC_REGULAR_DISC_NUMBER(NUMBER) (((NUMBER) >= 0x1) && ((NUMBER) <= 0x8))
//Loongson Feature
#define RCH0 ADC_Channel_0
#define RCH1 ADC_Channel_1
#define RCH2 ADC_Channel_2
#define RCH3 ADC_Channel_3
#define RCH4 ADC_Channel_4
#define RCH5 ADC_Channel_5
#define RCH6 ADC_Channel_6
#define RCH7 ADC_Channel_7
//Loongson Feature
#define JCH0 ADC_Channel_0
#define JCH1 ADC_Channel_1
#define JCH2 ADC_Channel_2
#define JCH3 ADC_Channel_3
void adc_init(adc_reg_map* ADCx, adc_init_info* ADC_InitStruct);
void adc_struct_init(adc_init_info* ADC_InitStruct);
void ADC_RegularChannelConfig(adc_reg_map* ADCx, unsigned char ADC_Channel, unsigned char Rank, unsigned char ADC_SampleTime);
void adc_dev_enable(adc_reg_map* ADCx, FunctionalState state);
void adc_software_start_conv_trigger(adc_reg_map* ADCx, FunctionalState state, unsigned char J_channel);
void adc_eoc_check_conv_end(adc_reg_map* ADCx);
#endif /* __LOONGSON_2K300_ADC_CORE_H__ */
Kconfig
需要在drivers/iio/adc
下的Kconfig加入以下代码
config LS_2K300_ADC
tristate "ls2k300 driver"
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
loongson 2k300 ADC driver
Makefile
需要在drivers/iio/adc
下的Makefile加入以下代码
obj-$(CONFIG_LS_2K300_ADC) += loongson-2k300-adc.o loongson-2k300-adc-core.o
loongson_2k0300.dtsi
dtsi文件下加入以下代码
adc: adc@0x1611c000 {
compatible = "ls2k300-adc";
reg = <0 0x1611c000 0 0x50>;
status = "disabled";
num-channels = <0>;
};
loongson_2k0300_pai_99.dts
dts设备树下加入adc节点
&adc {
status = "okay";
};
.config
在内核根目录下的配置文件加入以下开关
CONFIG_IIO=y
CONFIG_LS_2K300_ADC=y
修改完毕后重新编译内核,将内核部署到开发板/boot
目录下,重启开发板发现/sys/bus/iio/devices/iio/device0
目录下已经生成了相应的in_voltageX_raw
节点
接口
久久派上集成8路12位ADC采样接口, 支持单端采样, 也支持差分采样, 参考电压Vref为1.8V
PIN | 信号定义 | 备注 |
---|---|---|
1 | P3V3 | 3.3V 电源输出 |
2 | GND0 | 地线 |
3 | ADC 通道 4 | 0/4 通道可以独立采样, 也可以作为差分采样 |
4 | ADC 通道 0 | 0/4 通道可以独立采样, 也可以作为差分采样 |
5 | ADC 通道 1 | 1/5 通道可以独立采样, 也可以作为差分采样 |
6 | ADC 通道 5 | 1/5 通道可以独立采样, 也可以作为差分采样 |
7 | ADC 通道 2 | 2/6 通道可以独立采样, 也可以作为差分采样 |
8 | ADC 通道 6 | 2/6 通道可以独立采样, 也可以作为差分采样 |
9 | ADC 通道 3 | 3/7 通道可以独立采样, 也可以作为差分采样 |
10 | ADC 通道 7 | 3/7 通道可以独立采样, 也可以作为差分采样 |
原理图如下,J1针脚处可见ADC并不是按顺序分布的
测试
以读取ADC0通道为例,测试命令如下(其中raw为ADC原始数据,scale为单位分辨率所占的电压值,两者相乘可得到实际电压值)
raw=$(cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw)
scale=$(cat /sys/bus/iio/devices/iio\:device0/in_voltage_scale)
echo "vol_raw : $raw,vol_scale:$scale"
使用电位器调节到1.8V左右,和读取的数值较为接近:0.439453125 * 4095 / 1000约等于1.8V
参考
linux-5.10: linux for loongson2 (gitee.com)
标签:temp,龙芯,int,unsigned,LS2K0300,ADC,驱动,adc,define From: https://blog.csdn.net/HeavenMo/article/details/140253618