驱动编程简单教程——PTC512(ADC芯片驱动)为例

1.简介

上几周硬件找了一个ADC芯片的替代料,需要我这边写一个驱动做测试。以前都是配置与修改设备树,最多查看或小改一下驱动,没有写过驱动,所有记录一下思路。大致思路就是先看原理图,再看芯片手册,知道要写驱动的芯片张怎么样,怎么工作的。

2.查看原理图

在这里插入图片描述

3.查看芯片手册

在这里插入图片描述

3.1 引脚定义

参考输入等相关引脚
在这里插入图片描述
ADC通道相关引脚
在这里插入图片描述
SPI相关引脚
在这里插入图片描述
GPIO相关功能引脚
在这里插入图片描述
电源等相关引脚
在这里插入图片描述

3.2 内部架构与相关原理

在这里插入图片描述
在这里插入图片描述

3.3 芯片寄存器的操作(重点)

我们操作一个设备一般就是操作其设备相关的寄存器

3.3.1 手动模式

在这里插入图片描述
在这里插入图片描述
SPI一次传输16bit的数据,如果设置成手动模式,相关设置如下

0001 (选择手动模式) 1(开启低字段编码) 0001(选择的通道) 1(开启2Vref量程) 0(关闭休眠模式) 0(关闭GPIO功能) 0000(GPIO0)

则驱动用SPI往此ADC芯片传0001 1000 1100 0000,就设置相关寄存器工作
在这里插入图片描述
选择的通道数据,需要延迟3个数据

3.3.2 Auto1模式

在这里插入图片描述
在这里插入图片描述
设置通道寄存器
在这里插入图片描述
在这里插入图片描述

1000 0000 0000 0000 进入auto1 program sequence,下一帧编程通道
1010 1010 0101 0101 扫描 0 2 4 6 9 11 13 15 通道

在这里插入图片描述

3.3.3 Auto2模式

在这里插入图片描述
在这里插入图片描述
设置通道寄存器
在这里插入图片描述
在这里插入图片描述

3.3.4选择模式下继续操作

在这里插入图片描述
还有GPIO和告警的设置,但因为项目没有用到,驱动没有去编写,所有不做分析

4.开始编写驱动

#include <linux/module.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/kthread.h>

#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/fs.h>
#include <linux/input.h>

#define SPIADC_DEBUG (1)
#ifdef SPIADC_DEBUG
	#define spiadc_debug(fmt...)	printk(KERN_INFO "spiadc: "fmt)
#else
	#define spiadc_debug(fmt...)	do{}while(0)
#endif

#define FSU_SPI_DRIVER_VERSION          ("1.01.001")

/** 
  * 复位所有寄存器
  */
#define TPC5120_RESET_CHIP 	        (uint16_t)0x4200

/** 
  * 模式控制寄存器
  */
#define TPC5120_MANNUAL_MODE_REG    (uint16_t)0x0001
#define TPC5120_AUTO1_MODE_REG      (uint16_t)0x0002
#define TPC5120_AUTO2_MODE_REG 	    (uint16_t)0x0003

/** 
  * 自动模式程序寄存器
  */
#define TPC5120_AUTO1_PROG_REG      (uint16_t)0x0008
#define TPC5120_AUTO2_PROG_REG      (uint16_t)0x0009

/** 
  *  MANNUAL模式寄存器
  */ 
#define ManuLSBEN                   ((uint16_t)0x0800)
#define ManuCHnl				    ((uint16_t)0x0080)
#define ManuRangSel                 ((uint16_t)0x0040)
#define ManuPD 						((uint16_t)0x0020)
#define ManuGPIOEn					((uint16_t)0x0010)
#define ManuGPIOData				((uint16_t)0x0001)

/** 
  * AUTO1模式寄存器 
  */ 
#define Auto1LSBEN                  ((uint16_t)0x0800)       
#define Auto1CHnlRst				((uint16_t)0x0400)
#define Auto1RangSel                ((uint16_t)0x0040)       
#define Auto1PD 					((uint16_t)0x0020)
#define Auto1GPIOEn					((uint16_t)0x0010)
#define Auto1GPIOData				((uint16_t)0x0001)

/** 
  * AUTO2模式寄存器 
  */ 
#define Auto2LSBEN                  ((uint16_t)0x0800)       
#define Auto2CHnlRst				((uint16_t)0x0400)
#define Auto2RangSel                ((uint16_t)0x0040)       
#define Auto2PD 					((uint16_t)0x0020)
#define Auto2GPIOEn					((uint16_t)0x0010)
#define Auto2GPIOData				((uint16_t)0x0001)

typedef enum 
{
	Manual,
	Auto1,
	Auto2,
}TPC5120_mode_type;

typedef enum 
{
	lsd_code_disable,
	lsd_code_enabled,
}TPC5120_lsd_code;

typedef enum 
{
	power_down_disable,
	power_down_enabled,
}TPC5120_power_down;

typedef enum 
{
	chan_no_reset,
	chan_reset,
}TPC5120_chan_type;

typedef enum 
{
	range_v,
	range_2v,
}TPC5120_range_type;

typedef enum 
{
	gpio_disable,
	gpio_enabled,
}TPC5120_GPIO_type;

typedef enum 
{
	gpio_0 = 0x0,
	gpio_1 = 0x2,
	gpio_2 = 0x4,
	gpio_3 = 0x8,
}TPC5120_GPIO_number;

typedef struct 
{
	u8 value_valid;//数据更新标志
	u8 chan;
	u16 reg_value; //寄存器读取的数值
	u32 adc_value; //转换处理出来的数值

}fsu_spi_adc;

typedef struct
{
	u8 adc_valid;      //adc修改标志
	u8 adc_mode;       //adc工作模式
	u16 adc_ch_scan;   //adc扫描通道
	u32 task_sleep_time;//线程休眠时间

	int vref;
	int max;

	struct device *dev;
    struct spi_device *spi;
	struct kobject *kobj_spi_adcs;
	struct task_struct *spi_adc_task;//线程

	fsu_spi_adc f_spi_adc_data[16]; //各通道数据
}fsu_spi_dev;

/*
* 函数名: show_fsu_spi_data
* 功能:显示adc数据
*/
static ssize_t show_fsu_spi_data(struct device *dev, struct device_attribute *attr, char *buf)
{
	int i,len = 0;
	fsu_spi_dev *fsd = dev_get_drvdata(dev);

    if (NULL == fsd)
    {
       	printk(KERN_ERR ": fsu spi device empty!!!\n");
       	return 0;
    }

    len = sprintf(buf, "ChNo        RegisterValue        Value        Status \n");
	for(i = 0; i < 16; i++)
	{
		len += sprintf(buf + len, "%-16d    %-8d     %-4d          %-8s\n", \
        		fsd->f_spi_adc_data[i].chan, fsd->f_spi_adc_data[i].reg_value, \
				fsd->f_spi_adc_data[i].adc_value, fsd->f_spi_adc_data[i].value_valid ? "normal" : "fault");
	}

	return len;
}

/*
* 函数名: show_spi_driver_ver
* 功能:显示adc驱动版本
*/
static ssize_t show_spi_driver_ver(struct device *dev, struct device_attribute *attr, char *buf)
{
	fsu_spi_dev *fsd = dev_get_drvdata(dev);
	if (NULL == fsd)
	{
    	printk(KERN_ERR ": fsu spi device empty!!!\n");
    	return 0;
	}

	return ( sprintf(buf, "%s\n", FSU_SPI_DRIVER_VERSION) );
}

/*
* 函数名: show_spi_adc_chan
* 功能:显示adc 通道号
*/
static ssize_t show_spi_adc_chan(struct device *dev, struct device_attribute *attr, char *buf)
{
	fsu_spi_dev *fsd = dev_get_drvdata(dev);
	if (NULL == fsd)
	{
    	printk(KERN_ERR ": fsu spi device empty!!!\n");
    	return 0;
	}

	spiadc_debug("spi adc chan:%x\n", fsd->adc_ch_scan);
	return (sprintf(buf, "spi adc scan channl = %x \n", fsd->adc_ch_scan));
}

/*
* 函数名: set_spi_adc_chan
* 功能:设置adc扫描通道
*/
static ssize_t set_spi_adc_chan(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	u32 u32Data;
	
	fsu_spi_dev *fsd = dev_get_drvdata(dev);
	if (NULL == fsd)
	{
    	printk(KERN_ERR ": fsu spi device empty!!!\n");
    	return 0;
	}

	spiadc_debug("set buf:%s\n", buf);
	if (kstrtou32(buf, 10, &u32Data))
	{
		printk(KERN_ERR "spi adc scan channl para error!\n");
		return -EINVAL;
	}
	spiadc_debug("set Data:%d\n", u32Data);
	if(u32Data >= 0)
	{
		fsd->adc_ch_scan = u32Data & 0x0000ffff;
		fsd->adc_valid = 1;
	}
	else
	{
		spiadc_debug("spi adc scan channl input error!\n");
		fsd->adc_valid = 0;
	}

	return count;
}

/*
* 函数名: show_spi_adc_mode
* 功能:显示adc工作模式
*/
static ssize_t show_spi_adc_mode(struct device *dev, struct device_attribute *attr, char *buf)
{
	fsu_spi_dev *fsd = dev_get_drvdata(dev);
	if (NULL == fsd)
	{
    	printk(KERN_ERR ": fsu spi device empty!!!\n");
    	return 0;
	}

	spiadc_debug("spi sdc mode:%x\n", fsd->adc_mode);
	return (sprintf(buf, "Manual = 0; Auto1 = 1; Auto2 = 2;\nThe current mode = %d\n", fsd->adc_mode));
}

/*
* 函数名: set_spi_adc_mode
* 功能:设置adc工作模式
*/
static ssize_t set_spi_adc_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	u32 u32Data;
	
	fsu_spi_dev *fsd = dev_get_drvdata(dev);
	if (NULL == fsd)
	{
    	printk(KERN_ERR ": fsu spi device empty!!!\n");
    	return 0;
	}

	spiadc_debug("set buf:%s\n", buf);
	if (kstrtou32(buf, 10, &u32Data))
	{
		printk(KERN_ERR "spi adc mode para error!\n");
		return -EINVAL;
	}
	spiadc_debug("set Data:%d\n", u32Data);
	if(u32Data < 3)
	{
		fsd->adc_mode = u32Data;
		fsd->adc_valid = 1;
	}
	else
	{
		spiadc_debug("spi adc mode input error!\n");
		fsd->adc_valid = 0;
	}

	return count;
}

/*
* 函数名: show_spi_adc_sleep_time
* 功能:显示adc数据更新读取时间
*/
static ssize_t show_spi_adc_sleep_time(struct device *dev, struct device_attribute *attr, char *buf)
{	
	fsu_spi_dev *fsd = dev_get_drvdata(dev);
	if (NULL == fsd)
	{
    	printk(KERN_ERR ": fsu spi device empty!!!\n");
    	return 0;
	}

	spiadc_debug("task_sleep_time:%d\n", fsd->task_sleep_time);
	return (sprintf(buf, "%d\n", fsd->task_sleep_time));
}

/*
* 函数名: set_spi_adc_sleep_time
* 功能:设置adc数据更新读取时间
*/
static ssize_t set_spi_adc_sleep_time(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	u32 u32Data;

	fsu_spi_dev *fsd = dev_get_drvdata(dev);
	if (NULL == fsd)
	{
    	printk(KERN_ERR ": fsu spi device empty!!!\n");
    	return 0;
	}

	spiadc_debug("set buf:%s\n", buf);
	if (kstrtou32(buf, 10, &u32Data))
	{
		printk(KERN_ERR "spi adc time para error!\n");
		return -EINVAL;
	}

	if(u32Data > 0)
	{
		spiadc_debug("set Data:%d\n", u32Data);
		fsd->task_sleep_time = u32Data;
		fsd->adc_valid = 1;
	}
	else
	{
		spiadc_debug("spi adc time input error!\n");
		fsd->adc_valid = 0;
	}

	return count;
}

static DEVICE_ATTR(fsu_spi_adc_data, S_IRUGO, show_fsu_spi_data, NULL);
static DEVICE_ATTR(fsu_spi_adc_ver, S_IRUGO, show_spi_driver_ver, NULL);
static DEVICE_ATTR(fsu_spi_adc_mode, S_IWUSR | S_IRUGO, show_spi_adc_mode, set_spi_adc_mode);
static DEVICE_ATTR(fsu_spi_adc_chan, S_IWUSR | S_IRUGO, show_spi_adc_chan, set_spi_adc_chan);
static DEVICE_ATTR(fsu_spi_adc_sleep_time, S_IWUSR | S_IRUGO, show_spi_adc_sleep_time, set_spi_adc_sleep_time);

static struct attribute *fsu_spiadc_attrs[] = {
    &dev_attr_fsu_spi_adc_data.attr,
	&dev_attr_fsu_spi_adc_ver.attr,
	&dev_attr_fsu_spi_adc_mode.attr,
	&dev_attr_fsu_spi_adc_chan.attr,
	&dev_attr_fsu_spi_adc_sleep_time.attr,
    NULL
};

static struct attribute_group fsu_spiadc_group = {
    .attrs = fsu_spiadc_attrs 
};

static int fsu_add_all_spi_data_node(struct device *dev)
{
	int ret;
	ret = sysfs_create_group(&dev->kobj, &fsu_spiadc_group);

    if (ret)
    {
       	dev_warn(dev, "Create fsu all spi data sysfs node 'fsu_spi_data' failed, ret=%d\n", ret);
    }

	return 0;
}

/*
* 函数名: spi_write_read
* 功能:写入TPC5120寄存器设置指令,并读取数据
*/
static u16 spi_write_read(fsu_spi_dev *adc, u16 cmd)
{
	u8 cmd_buf[2];
	u8 data_buf[2];
	u16 data = 0;
	cmd_buf[1] = cmd & 0x00FF;
	cmd_buf[0] = cmd >> 8;
	spi_write(adc->spi,cmd_buf,2);
	spi_read(adc->spi,data_buf,2);
	data = data_buf[0] <<8 |data_buf[1];
	spiadc_debug("==== spi_write_read cmd:%x%x data_buf:%x%x data:%x ====\n",cmd_buf[0],cmd_buf[1],data_buf[0],data_buf[1],data);
	return data;
}

/*
* 函数名: set_auto1_prog_cfg
* 参数:  chscan 扫描的通道号
* 功能:设置TPC521程序寄存器 设置指定读取的通道数据
* 例: chscan = 0xaa55 扫描 0 2 4 6 9 11 13 15通道
*/
static void set_auto1_prog_cfg(fsu_spi_dev *adc, u16 chscan)
{	
	u16 adc_cfg;
	adc_cfg=TPC5120_AUTO1_PROG_REG<<12;
	spi_write_read(adc, adc_cfg);   	// enters auto1 program sequence
	spi_write_read(adc, chscan);		// select channel to scan
}

/*
* 函数名: set_auto2_prog_cfg
* 参数: chscan 扫描的通道号
* 功能:设置TPC521程序寄存器 设置自动扫描通道的范围
* 例: chscan = 0x5 扫描 0~5通道
*/
static void set_auto2_prog_cfg(fsu_spi_dev *adc, u16 chscan)
{
	u16 adc_cfg;
	adc_cfg=(TPC5120_AUTO2_PROG_REG<<12)|((chscan&0x000f)<<6);
	spi_write_read(adc, adc_cfg);
}

/*
* 函数名: set_manual_mode_cfg
* 参数: lsbEn 是否开启第di10~0的编码
		 chan 通道号
         range 范围选择 0 0~Vref 1 0~2Vref
		 pd 休眠模式
		 ioEn GPIO功能
		 iodata 设置GPIO_x为输出模式
* 功能:设置TPC521模式寄存器 ADC为手动模式
*/
static void set_manual_mode_cfg(fsu_spi_dev *adc, u8 lsbEn, u16 chan, u8 range, u8 pd, u8 ioEn, u8 iodata)
{
	u16 adc_cfg;
	adc_cfg=(TPC5120_MANNUAL_MODE_REG<<12);
	if(lsbEn) adc_cfg|=ManuLSBEN;
	adc_cfg|=(chan&0x000f)*ManuCHnl;
	if(range) adc_cfg|=ManuRangSel;
	if(pd)    adc_cfg|=ManuPD;
	if(ioEn)  adc_cfg|=ManuGPIOEn;
	adc_cfg|=iodata*ManuGPIOData;
	spi_write_read(adc, adc_cfg);   // config mannual mode reg
}

/*
* 函数名: set_auto1_mode_cfg
* 参数: lsbEn 是否开启第di10~0的编码
         chanRst  通道是否复位
         range 范围选择 0 0~Vref 1 0~2Vref
		 pd 休眠模式
		 ioEn GPIO功能
		 iodata 设置GPIO_x为输出模式
* 功能:设置TPC521模式寄存器 ADC为自动1模式
*/
static void set_auto1_mode_cfg(fsu_spi_dev *adc, u8 lsbEn, u8 chanRst, u8 range, u8 pd, u8 ioEn, u8 iodata)
{
	u16 adc_cfg;
	adc_cfg=(TPC5120_AUTO1_MODE_REG <<12);
	if(lsbEn)   adc_cfg|=Auto1LSBEN;
	if(chanRst) adc_cfg|=Auto1CHnlRst;
	if(range)   adc_cfg|=Auto1RangSel;
	if(pd)      adc_cfg|=Auto1PD;
	if(ioEn)    adc_cfg|=Auto1GPIOEn;
	adc_cfg|=iodata*Auto1GPIOData;
	spi_write_read(adc, adc_cfg);  	// config auto1 mode reg
}

/*
* 函数名: set_auto2_mode
* 参数: lsbEn 是否开启第di10~0的编码
         chanRst  通道是否复位
         range 范围选择 0 0~Vref 1 0~2Vref
		 pd 休眠模式
		 ioEn GPIO功能
		 iodata 设置GPIO_x为输出模式
* 功能:设置TPC521模式寄存器 ADC为自动2模式
*/
static void set_auto2_mode_cfg(fsu_spi_dev *adc, u8 lsbEn, u8 chanRst, u8 range, u8 pd, u8 ioEn, u8 iodata)
{
	u16 adc_cfg;
	adc_cfg=TPC5120_AUTO2_MODE_REG<<12;
	if(lsbEn)   adc_cfg|=Auto2LSBEN;
	if(chanRst) adc_cfg|=Auto2CHnlRst;
	if(range)   adc_cfg|=Auto2RangSel;
	if(pd)      adc_cfg|=Auto2PD;
	if(ioEn)    adc_cfg|=Auto2GPIOEn;	
	adc_cfg|=iodata*Auto2GPIOData;
	spi_write_read(adc, adc_cfg);	 // config auto2 mode reg
}

/*
* 函数名: parse_adc_data
* 功能:解析spi读取到的数据,并更新
*/
static int parse_adc_data(fsu_spi_dev *adc, u16 spi_data)
{
	u16 adc_buf = spi_data;

	u8 adc_chan = adc_buf >> 12;
	u16 reg_value = adc_buf & 0x0fff;

	//换算数据
	u32  adc_value = 0;
	adc_value = (reg_value * adc->vref)/adc->max;

	int i;
	spiadc_debug("==== parse_adc_data chan:%x reg_value:%x adc_value:%d ====\n",adc_chan,reg_value,adc_value);
	for(i = 0; i < 16; i++)
	{
		if ( adc_chan == adc->f_spi_adc_data[i].chan)
		{
			if(reg_value != adc->f_spi_adc_data[i].reg_value)
			{
				adc->f_spi_adc_data[i].reg_value = reg_value;
				adc->f_spi_adc_data[i].adc_value = adc_value;
				//数据更新
				adc->f_spi_adc_data[i].value_valid = 1;
			}
			else
			{
				//数据没有更新
				adc->f_spi_adc_data[i].value_valid = 0;
			}
		}
		else
		{
			//数据没有更新
			adc->f_spi_adc_data[i].value_valid = 0;
		}
	}
}

//驱动运行线程
static int fsu_spi_adc_sample(void *arg)
{
	spiadc_debug("==== fsu_spi_adc_sample start ====\n");
	fsu_spi_dev *fspiadc = NULL;
	u16 spi_data;
	int i;
	fspiadc = (fsu_spi_dev *)arg;

	while(1)
	{
		msleep(fspiadc->task_sleep_time);
		if(kthread_should_stop()) 
		{	
			break;
		}

		if(1 == fspiadc->adc_valid)
		{
			fspiadc->adc_valid = 0;
			//复位所有寄存器
			spi_write_read(fspiadc,TPC5120_RESET_CHIP);
			//配置寄存器
			switch(fspiadc->adc_mode)
			{
				case Manual:
					//设置模式寄存器
					set_manual_mode_cfg(fspiadc,lsd_code_enabled,fspiadc->adc_ch_scan,range_2v,power_down_disable,gpio_disable,gpio_0);
				break;

				case Auto1:
					//设置模式寄存器
					set_auto1_mode_cfg(fspiadc,lsd_code_enabled,chan_reset,range_2v,power_down_disable,gpio_disable,gpio_0);
					//设置通道寄存器
					set_auto1_prog_cfg(fspiadc,fspiadc->adc_ch_scan);
				break;

				case Auto2:
					//设置模式寄存器
					set_auto2_mode_cfg(fspiadc,lsd_code_enabled,chan_reset,range_2v,power_down_disable,gpio_disable,gpio_0);
					//设置通道寄存器
					set_auto2_prog_cfg(fspiadc,fspiadc->adc_ch_scan);
				break;
			}
		}

		for(i = 0; i < 16; i++)
		{
			spi_data = spi_write_read(fspiadc,0);
			//解析数据
			parse_adc_data(fspiadc,spi_data);
		}
	}
	spiadc_debug("==== fsu_spi_adc_sample end ====\n");
	return 0;
}

static int fsu_spi_adc_init(fsu_spi_dev *spi_dev)
{
	spiadc_debug("==== fsu_spi_adc_init start ====\n");
	if (NULL == spi_dev)
	{
		return -1;
	}

	fsu_spi_dev *fspiadc = spi_dev;
	int i;
	fspiadc->adc_valid = 1;
	fspiadc->adc_mode = 2;
	fspiadc->adc_ch_scan = 0xb;
	fspiadc->task_sleep_time = 5000;

	for( i = 0;i < 16; i++)
	{
		fspiadc->f_spi_adc_data[i].value_valid = 0;
		fspiadc->f_spi_adc_data[i].chan = i;
		fspiadc->f_spi_adc_data[i].reg_value = 0;
		fspiadc->f_spi_adc_data[i].adc_value = 0;
	}
	spiadc_debug("==== fsu_spi_adc_init end ====\n");
	return 0;
}

static int fsu_spi_adc_parse_dt(fsu_spi_dev *spiadc)
{
    struct device_node *node = spiadc->dev->of_node;

    if (of_property_read_u32(node, "vref", &spiadc->vref))
    {
        dev_err(spiadc->dev, "Could not get 'vref'!!");
        return -1;
    }

    if (spiadc->vref <= 0)
    {
        spiadc->vref = 5000;
        dev_warn(spiadc->dev, "Invalid 'vref', using default %d\n", spiadc->vref);
    }

    if (of_property_read_u32(node, "max", &spiadc->max))
    {
        dev_err(spiadc->dev, "Could not get 'max'!!");
        return -1;
    }

    if (spiadc->max <= 0)
    {
        spiadc->max = 4096;
        dev_warn(spiadc->dev, "Invalid 'max', using default %d\n", spiadc->max);      
    }

	spiadc_debug("spi adc vref:%d, max:%d\n",spiadc->vref, spiadc->max);
	
    return 0;
}

static int fsu_spi_adc_probe(struct spi_device *spi)
{
	spiadc_debug("==== fsu_spi_adc_probe start ====\n");
    int ret;
	fsu_spi_dev *spiadc;

	/*
		整个内核空间的调用链上只有4KB或8KB的栈,相对于应用程序来说是非常小,如果需要大内存的空间,需要使用专门的函数进行动态分配kmalloc
	*/
	/* allocate device handle */
    spiadc = devm_kzalloc(&spi->dev, sizeof(*spiadc), GFP_KERNEL);
    if (NULL == spiadc)
    {
        return -ENOMEM;
    }

	/* init adc */
    spiadc->dev = &spi->dev;
    spiadc->spi = spi;

	spiadc_debug("==== spi dev chip_select:%d, max_speed_hz:%d, mode:%d ====\n", spi->chip_select, spi->max_speed_hz, spi->mode);

    /* setup spi */
    spi->bits_per_word = 8;
    ret = spi_setup(spi);
    if (ret < 0)
    {
        dev_err(&spi->dev, "Setup spi failed, ret = %d\n", ret);
		devm_kfree(&spi->dev, spiadc);
		return ret;
    }

	/* set drv data */
    dev_set_drvdata(&spi->dev, spiadc);

	/* parse dt */ //解析设备树
	fsu_spi_adc_parse_dt(spiadc);

	//初始化默认配置
	fsu_spi_adc_init(spiadc);

	//生成设备节点
	fsu_add_all_spi_data_node(spiadc->dev);

	//开启线程
	spiadc->spi_adc_task = kthread_create(fsu_spi_adc_sample, spiadc, "spi_adc_task");
	if(IS_ERR(spiadc->spi_adc_task))
	{
		spiadc_debug("creat spi_adc_task erro \n");
		spiadc->spi_adc_task = NULL;
	}
	else
	{
		//等待线程
		wake_up_process(spiadc->spi_adc_task);
	}
	spiadc_debug("==== fsu_spi_adc_probe end ====\n");
    return 0;
}


static int fsu_spi_adc_remove(struct spi_device *spi)
{
    fsu_spi_dev *fsd = dev_get_drvdata(&spi->dev);

	if(fsd->spi_adc_task) 
	{
		kthread_stop(fsd->spi_adc_task);
		fsd->spi_adc_task = NULL;
	}
    sysfs_remove_group(&spi->dev.kobj, &fsu_spiadc_group);
    devm_kfree(&spi->dev, fsd);	

    return 0;
}

static const struct of_device_id fsu_spiadc_match_ids[] = {
    { .compatible = "znv,fsu-spiadc", .data = NULL},
    {},
};

MODULE_DEVICE_TABLE(of, fsu_spiadc_match_ids);

static struct spi_driver fsu_spiadc_driver = {
	.probe		= fsu_spi_adc_probe,
	.remove		= fsu_spi_adc_remove,
	.driver		= {
    	.name		= FSU_SPI_ADC_NAME,
    	.owner		= THIS_MODULE,
    	.of_match_table = fsu_spiadc_match_ids,
    },
};

//驱动入口函数
static int __init fsu_spiadc_init(void)
{
    spi_register_driver(&fsu_spiadc_driver);
    return 0;
}
//驱动卸载函数
static void __exit fsu_spiadc_exit(void)
{
    spi_unregister_driver(&fsu_spiadc_driver);
}

//驱动程序的入口
module_init(fsu_spiadc_init); //运行insmod指令的时候,就会调用
//驱动程序的出口
module_exit(fsu_spiadc_exit); //运行rmmod指令的时候,会被调用

//驱动的描述
MODULE_AUTHOR("kamin");		//作者
MODULE_DESCRIPTION("ADC Driver Mode");	//模块功能说明
MODULE_LICENSE("GPL");	//许可证:驱动遵循GPL协议

  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值