The use of punctual atom Linux alpha development board -- SPI driver

Meishan swordsman 2022-06-23 18:13:32 阅读数:455

usepunctualatomlinuxalpha

SPI Controller drive

The host controller drives :SOC Of spi Peripheral driver , It is the number compiled by the semiconductor manufacturer , by spi-imx.c When spi After the device and driver of the controller are matched ,spi_imx_probe The function will execute .

SPI The core of control drive is spi_master The construction of ,spi_master

spi_master->transfer
spi_master-> transfer_one_message 6ULL The host controller uses this function ,

Device drivers : Concrete SPI Chip driver

Emc20608

SPI_Driver It's very important , The point is to apply for or define a spi_driver Then initialize spi_driver Member variables of ,

Drive and device tree

SPI Four pin pinctrl Electrical attribute assignment of :SCLK、NS、MOSI、MISO
 Insert picture description here
 Insert picture description here
@ hinder 0 Indicates which hardware chip selection signal is received !

The reference document is document Under the devicetree Design documents under .

 Insert picture description here
1、 Modify the device tree , add to IO Related information
ECSPI3_SCLK -> UART2_RXD
ECSPI3_MOSI -> UART2_CTS
ECSPI3_SS0 -> UART2_TXD
ECSPI3_MISO -> UART2_RTS

Chip selection signal is not used as hardware chip selection , But as an ordinary GPIO, We control the chip selection pin by ourselves in the program .

2、 stay ECSPI3 Create under node icm20608 Child node

3、 It needs to be initialized icm20608 chip 、 Then read the original data from it ! This process involves how to use linux Internal SPI drive API Read and write ICM20608
Two important structures are used :spi_transfer and spi_message
spi_transfer It is used to build the content of receiving and sending data .

structure spi_transfer, Then package it to spi_message Inside , Need to use spi_message_init initialization spi_message, And then use spi_message_add_tail take spi_transfer Add to spi_message Inside , End use spi_sync and spi_async To send the .

Driver writing

Get the parent node of the device tree :
 Insert picture description here
 Insert picture description here

SPI Driver code

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "icm20608.h"

Equipment structure

#define ICM20608_CNT 1
#define ICM20608_NAME "icm20608"
/* Equipment structure */
struct icm20608_dev {

struct cdev cdev;
struct class *class;/* class : To automatically create nodes */
struct device *device;/* equipment : To automatically create nodes */
dev_t devid; // Device number 
int major; // The main equipment, 
int minor; // Secondary device number 
void *private_data;
int cs_gpio;
struct device_node *nd;
};
struct icm20608_dev icm20608;

file_operation Operation function

static int icm20608_open(struct inode *inode, struct file *filp)
{

/* Set up private data */
//filp->private_data = &icm20608;
return 0;
}
static ssize_t icm20608_read(struct file *filp, __user char *buf, size_t count,loff_t *ppos)
{

return 0;
}
static int icm20608_release(struct inode *inode, struct file *filp)
{

//struct icm20608_dev *dev = (struct icm20608_dev *)filp->private_data;
return 0;
}
/** * Operation set of character device */
const struct file_operations icm20608_fops = {

.owner = THIS_MODULE,
.open = icm20608_open,
.read = icm20608_read,
.release = icm20608_release,
};

Read and write registers


/*spi Read register */
static int icm20608_read_regs(struct icm20608_dev *dev,u8 reg,void *buf,int len)
{

int ret = 0;
unsigned char txdata[len];
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = (struct spi_device*)dev->private_data;
// The selection is down 
gpio_set_value(dev->cs_gpio,0);
// structure spitransfer
t= kzalloc(sizeof(struct spi_transfer),GFP_KERNEL);
/* First step : Send the address of the register to be read */
txdata[0] = reg|0x80;// Read the register address , The highest bit of the register address should be set to 1
t->tx_buf = txdata;
t->len = 1;
spi_message_init(&m);
spi_message_add_tail(t,&m);
ret = spi_sync(spi,&m);
// The second step Reading data 
txdata[0] = 0xff; // invalid 
t->rx_buf = buf;
t->len = len;
spi_message_init(&m);
spi_message_add_tail(t,&m);
ret = spi_sync(spi,&m);
// Free memory 
kfree(t);
gpio_set_value(dev->cs_gpio,1);
return ret;
}
/*spi Write register */
static int icm20608_write_regs(struct icm20608_dev *dev,u8 reg,u8 *buf,int len)
{

int ret = 0;
unsigned char txdata[len];
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = (struct spi_device*)dev->private_data;
// The selection is down 
gpio_set_value(dev->cs_gpio,0);
// structure spitransfer
t = kzalloc(sizeof(struct spi_transfer),GFP_KERNEL);
/* First step : Send the address of the register to be read */
txdata[0] = reg&~0x80;// Read the register address , The highest bit of the register address should be set to 1
t->tx_buf = txdata;
t->len = 1;
spi_message_init(&m);
spi_message_add_tail(t,&m);
ret = spi_sync(spi,&m);
// The second step Reading data 
t->rx_buf = buf;
t->len = len;
spi_message_init(&m);
spi_message_add_tail(t,&m);
ret = spi_sync(spi,&m);
// Free memory 
kfree(t);
gpio_set_value(dev->cs_gpio,1);
return ret;
}
/* * @description : Read icm20608 Specify the register value , Read a register * @param – dev : icm20608 equipment * @param – reg : Register to read * @return : Read register value */
static unsigned char icm20608_read_onereg(struct icm20608_dev *dev,u8 reg)
{

u8 data = 0;
icm20608_read_regs(dev, reg, &data, 1);
return data;
}
/* * @description : towards icm20608 The specified register writes the specified value , Write a register * @param – dev : icm20608 equipment * @param – reg : Register to write * @param – data : Value to write * @return : nothing */
static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg,u8 value)
{

u8 buf = value;
icm20608_write_regs(dev, reg, &buf, 1);
}

ICM20608 initialization


void icm20608reg_init(void)
{

u8 value = 0;
icm20608_write_onereg(&icm20608, ICM20_PWR_MGMT_1, 0x80);
mdelay(50);
icm20608_write_onereg(&icm20608, ICM20_PWR_MGMT_1, 0x01);
mdelay(50);
value = icm20608_read_onereg(&icm20608, ICM20_WHO_AM_I);
printk("ICM20608 ID = %#X\r\n", value);
}

After the matching drive succeeds probe function

static int icm20608_probe(struct spi_device *spi)
{

int ret = 0;
/* Build character device driver framework */
/*2、 Register character device */
printk("icm20608_init ok\r\n");
icm20608.major = 0;// Set to 0, Indicates the equipment number applied by the system 
if(icm20608.major) // Given the main equipment number 
{

icm20608.devid = MKDEV(icm20608.major,0);
ret = register_chrdev_region(icm20608.devid,ICM20608_CNT,ICM20608_NAME);
}else{

ret = alloc_chrdev_region(&icm20608.devid,0,ICM20608_CNT,ICM20608_NAME);
icm20608.major = MAJOR(icm20608.devid);
icm20608.minor = MINOR(icm20608.devid);
}
if(ret < 0){

printk("icm20608 chrdev_region err\r\n");
return -1;
}
printk("icm20608 majorid = %d,minorid = %d\r\n",icm20608.major,icm20608.minor);
/*3、 Register character device */
icm20608.cdev.owner = THIS_MODULE;
cdev_init(&icm20608.cdev, &icm20608_fops);
cdev_add(&icm20608.cdev,icm20608.devid,ICM20608_CNT);
/* 4、 Automatically create device nodes */
icm20608.class = class_create(THIS_MODULE, ICM20608_NAME);
if (IS_ERR(icm20608.class)) {

return PTR_ERR(icm20608.class);
}
/* 5、 Create device */
icm20608.device = device_create(icm20608.class, NULL, icm20608.devid, NULL, ICM20608_NAME);
if (IS_ERR(icm20608.device)) {

return PTR_ERR(icm20608.device);
}
// Get the software chip selection pin 
icm20608.nd = of_get_parent(spi->dev.of_node);
icm20608.cs_gpio = of_get_named_gpio(icm20608.nd,"cs-gpio",0);
if (icm20608.cs_gpio<0)
{

printk("can't get gpio\r\n");
return -1;
}
ret = gpio_request(icm20608.cs_gpio,"cs");
if (ret <0)
{

printk("gpio_request failed\n");
return -1;
}
ret = gpio_direction_output(icm20608.cs_gpio,1);// Default high level 
/* initialization spi_device*/
spi->mode = SPI_MODE_0;
spi_setup(spi);
icm20608.private_data = spi; // Set private data as spi
/* initialization icm20608*/
icm20608reg_init();
return 0;
}

spi_driver initialization

static int icm20608_remove(struct spi_device *spi)
{

/* Delete character device */
cdev_del(&icm20608.cdev);
/* Cancel device number */
unregister_chrdev_region(icm20608.devid,ICM20608_CNT);
/* First destroy the equipment and then destroy the class */
device_destroy(icm20608.class,icm20608.devid);
/* Destroy class */
class_destroy(icm20608.class);
/* Release the selection */
gpio_free(icm20608.cs_gpio);
return 0;
}
/* Traditional matching table */
static struct spi_device_id icm20608_id[] ={

{
"alientek,icm20608",0},
{
}
};
/* Device tree matching table */
static struct of_device_id icm20608_of_match[] ={

{
.compatible = "alientek,icm20608"},
{
}
};
/*spi driver*/
static struct spi_driver icm20608_driver = {

.probe = icm20608_probe,
.remove = icm20608_remove,
.driver = {

.name = "icm20608",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(icm20608_of_match),
},
.id_table = icm20608_id,// Traditional device matching !!!
};

Driver loading and unloading

static int __init icm20608_init(void)
{

int ret;
ret = spi_register_driver(&icm20608_driver);
return ret;
}
static void __exit icm20608_exit(void)
{

spi_unregister_driver(&icm20608_driver);
}
// Module loading function 
module_init(icm20608_init);
// Module uninstall 
module_exit(icm20608_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("qhy");
版权声明:本文为[Meishan swordsman]所创,转载请带上原文链接,感谢。 https://javamana.com/2022/174/202206231654540855.html