Linux-SPI驱动

序言

SPI 具体通信协议本文不涉及,本文主要介绍在 Linux 中 SPI 的驱动框架。

SPI 有四条线:SCLK、MOSI、MISO、SS/CS

  • SCLK:时钟线
  • MOSI:主机数据输出从机数据输入
  • MISO:主机数据输入从机数据输出
  • SS/CS:片选信号,由主机控制

SPI 通信时,SPI 的片选信号可以作为硬件片选与软件片选。

  • 硬件片选:如果选择使用硬件片选的方式,则在数据传输时,CS 片选信号的电平硬件上会拉低。当数据传输结束后,硬件上CS片选信号的电平拉高。
  • 软件片选:软件片选即 SPI 通信过程中, CS 片选信号需要在软件上做处理,也就需要嵌入式开发者在数据传输前手动(程序中)拉低 CS 片选信号的电平,在数据传输结束后,也需要软件拉高 CS 片选信号的电平。

SPI 驱动框架

Linux SPI 驱动可分为:SPI 总线驱动和 SPI 设备驱动。

  • SPI 总线驱动:主要包含 SPI 硬件体系结构中适配器(SPI 控制器)的控制,用于产生 SPI 读写时序。
  • SPI 设备驱动:通过 SPI 主机驱动与 CPU 交换数据。

SPI 设备驱动涉及字符设备驱动、SPI 核心和 SPI 主机驱动。

  • 字符设备驱动:创建字符设备与应用程序访问数据。

  • SPI核心层:提供SPI控制器驱动和设备驱动的注册方法、注销方法、SPI通信硬件无关接口函数。

  • SPI主机驱动:主要包含SPI硬件体系结构中适配器(spi控制器)的控制,用于产生SPI 读写时序。

SPI 总线驱动

SPI 总线驱动主要是 SOC 的 SPI 控制器驱动,SPI 控制器驱动负责与硬件的 SPI 控制器进行交互,完成硬件层面的初始化、配置和操作。这一部分一般由芯片厂提供,例如 IMX6ULL 的 SPI 控制器驱动已经由 NXP 公司编写好了,这一部分只需要了解内部如何调用即可。

主要实现:

  • 申请并初始化 spi_master 结构体,同 I2C 的 i2c_adapter
  • 接着设置 transfer 函数,同 I2C 中的 i2c_algorithm 结构体中的 master_xfer 函数一样数据传输函数。
  • 最后通过 spi_register_master 函数向 Linux 内核注册设置好的 spi_master 结构体。

spi_master 结构体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// include/linux/spi/spi.h

struct spi_master {
struct device dev;

struct list_head list;

/* other than negative (== assign one dynamically), bus_num is fully
* board-specific. usually that simplifies to being SOC-specific.
* example: one SOC has three SPI controllers, numbered 0..2,
* and one board's schematics might show it using SPI-2. software
* would normally use bus_num=2 for that controller.
*/
s16 bus_num;

/* chipselects will be integral to many controllers; some others
* might use board-specific GPIOs.
*/
u16 num_chipselect;

/* some SPI controllers pose alignment requirements on DMAable
* buffers; let protocol drivers know about these requirements.
*/
u16 dma_alignment;

/* spi_device.mode flags understood by this controller driver */
u16 mode_bits;

/* bitmask of supported bits_per_word for transfers */
u32 bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))

/* limits on transfer speed */
u32 min_speed_hz;
u32 max_speed_hz;

/* other constraints relevant to this driver */
u16 flags;
#define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
#define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */
#define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */
#define SPI_MASTER_MUST_RX BIT(3) /* requires rx */
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */

/*
* on some hardware transfer / message size may be constrained
* the limit may depend on device transfer settings
*/
size_t (*max_transfer_size)(struct spi_device *spi);
size_t (*max_message_size)(struct spi_device *spi);

/* I/O mutex */
struct mutex io_mutex;

/* lock and mutex for SPI bus locking */
spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex;

/* flag indicating that the SPI bus is locked for exclusive use */
bool bus_lock_flag;

/* Setup mode and clock, etc (spi driver may call many times).
*
* IMPORTANT: this may be called when transfers to another
* device are active. DO NOT UPDATE SHARED REGISTERS in ways
* which could break those transfers.
*/
int (*setup)(struct spi_device *spi);

/* bidirectional bulk transfers
*
* + The transfer() method may not sleep; its main role is
* just to add the message to the queue.
* + For now there's no remove-from-queue operation, or
* any other request management
* + To a given spi_device, message queueing is pure fifo
*
* + The master's main job is to process its message queue,
* selecting a chip then transferring data
* + If there are multiple spi_device children, the i/o queue
* arbitration algorithm is unspecified (round robin, fifo,
* priority, reservations, preemption, etc)
*
* + Chipselect stays active during the entire message
* (unless modified by spi_transfer.cs_change != 0).
* + The message transfers use clock and SPI mode parameters
* previously established by setup() for this device
*/
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);

/* called on release() to free memory provided by spi_master */
void (*cleanup)(struct spi_device *spi);

/*
* Used to enable core support for DMA handling, if can_dma()
* exists and returns true then the transfer will be mapped
* prior to transfer_one() being called. The driver should
* not modify or store xfer and dma_tx and dma_rx must be set
* while the device is prepared.
*/
bool (*can_dma)(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer);

/*
* These hooks are for drivers that want to use the generic
* master transfer queueing mechanism. If these are used, the
* transfer() function above must NOT be specified by the driver.
* Over time we expect SPI drivers to be phased over to this API.
*/
bool queued;
struct kthread_worker kworker;
struct task_struct *kworker_task;
struct kthread_work pump_messages;
spinlock_t queue_lock;
struct list_head queue;
struct spi_message *cur_msg;
bool idling;
bool busy;
bool running;
bool rt;
bool auto_runtime_pm;
bool cur_msg_prepared;
bool cur_msg_mapped;
struct completion xfer_completion;
size_t max_dma_len;

int (*prepare_transfer_hardware)(struct spi_master *master);
int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg);
int (*unprepare_transfer_hardware)(struct spi_master *master);
int (*prepare_message)(struct spi_master *master,
struct spi_message *message);
int (*unprepare_message)(struct spi_master *master,
struct spi_message *message);
int (*spi_flash_read)(struct spi_device *spi,
struct spi_flash_read_message *msg);
bool (*flash_read_supported)(struct spi_device *spi);

/*
* These hooks are for drivers that use a generic implementation
* of transfer_one_message() provied by the core.
*/
void (*set_cs)(struct spi_device *spi, bool enable);
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *transfer);
void (*handle_err)(struct spi_master *master,
struct spi_message *message);

/* gpio chip select */
int *cs_gpios;

/* statistics */
struct spi_statistics statistics;

/* DMA channels for use with core dmaengine helpers */
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;

/* dummy data for full duplex devices */
void *dummy_rx;
void *dummy_tx;

int (*fw_translate_cs)(struct spi_master *master, unsigned cs);
};

SPI 设备驱动

SPI 设备驱动主要是填充两个重要的结构体:spi_clientspi_driverspi_client 是描述设备信息的,spi_driver 是描述驱动内容的,类似于平台总线驱动中的 platform_deviceplatform_driver,如果采用设备树的话,则只需要编写 spi_driver 结构体的内容即可,因为设备树中的 SPI 节点会转换为 spi_client 的内容。

spi_client 结构体如下:

Linux 内核用 spi_driver 结构体表示 SPI 设备驱动,spi_driver 结构体如下:

1
2
3
4
5
6
7
8
// include/linux/spi/spi.h
struct spi_driver {
const struct spi_device_id *id_table;
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
struct device_driver driver;
};

向内核注册 spi_driver 结构体:使用一个宏定义来代替直接包含头文件的方式获取 THIS_MODULE,从而避免头文件链式依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// include/linux/spi/spi.h
// drivers/spi/spi.c

/* use a define to avoid include chaining to get THIS_MODULE */
#define spi_register_driver(driver) \
__spi_register_driver(THIS_MODULE, driver)

int __spi_register_driver(struct module *owner, struct spi_driver *sdrv)
{
sdrv->driver.owner = owner;
sdrv->driver.bus = &spi_bus_type;
if (sdrv->probe)
sdrv->driver.probe = spi_drv_probe;
if (sdrv->remove)
sdrv->driver.remove = spi_drv_remove;
if (sdrv->shutdown)
sdrv->driver.shutdown = spi_drv_shutdown;
return driver_register(&sdrv->driver);
}

具体注册 spi_driver 的注册流程同 platform_driver 一致。

SPI 设备和驱动匹配过程

SPI 设备和驱动的匹配过程是由 SPI 总线来完成的,这点和 platform、I2C 等驱动一样。

SPI 总线定义了一个 bus_type 结构体如下:

1
2
3
4
5
6
7
8
9
// drivers/spi/spi.c

struct bus_type spi_bus_type = {
.name = "spi",
.dev_groups = spi_dev_groups,
.match = spi_match_device,
.uevent = spi_uevent,
};
EXPORT_SYMBOL_GPL(spi_bus_type); // 使得别的模块可调用该结构体

匹配函数为 spi_match_device

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
const struct spi_driver *sdrv = to_spi_driver(drv);

/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;

/* Then try ACPI */
if (acpi_driver_match_device(dev, drv))
return 1;

if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi);

return strcmp(spi->modalias, drv->name) == 0;
}
  • 首先 of_driver_match_device 函数用于完成设备树设备和驱动的匹配,比较设备节点的 compatible 属性和 of_device_id 结构体中的 compatible 属性是否相等。

  • 若上一步匹配失败,则进行 ACPI 形式的匹配。

  • 若上一半匹配也失败,则进行传统的无设备树的匹配方式,比较 spi_driver 结构体中的 spi_device_id 表和 spi_device 结构体中 modalias 成员变量是否有匹配的。

  • 最后则是比较 spi_device 结构体中 modalias 成员变量和 device_driver 中的 name 成员变量是否相等。

SPI 设备驱动代码编写

首先是 SPI 设备的信息,有两种方法:未使用设备树和使用设备树。

SPI 设备信息描述

未使用设备树

跟 I2C 类似,新建一个模块进行注册 spi_device 结构体。

使用设备树

使用设备树文件时,只需要在设备树文件中添加相应的 SPI 设备节点即可例如:添加 icm20608 设备,需要注意的是添加 SPI 设备节点有要求,形式如:别名:设备名@通道号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
pinctrl_ecspi3: ecspi3 {
fsl,pins = <
MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x000010B0
MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x000010B0
MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x000010B0
//MX6UL_PAD_UART2_TX_DATA__ECSPI3_SS0 0x000010B0
MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x000010B0
>;
};
};
};


&ecspi3 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi3>;
cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>; // 片选IO直接置0
status = "okay";


spidev: icm20608@0{ // 此处的命名有要求:设备名@通道号 0表示该设备使用 ECSPI 0通道
compatible = "invensense,icm20608";
interrupt-parent = <&gpio1>;
interrupts = <1 1>;
spi-max-frequency = <8000000>; // 最高频率8MHz
reg = <0>; // 这里表示该设备使用 ECSPI 0通道
};
};

MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 这个 IO 是 ICM20608 的片选信号,这里我们并没有将其复用为 ECSPI3的 SS0 信号,而是将其复用为了普通的 GPIO 。这里选择软件片选使用,NXP 的针对 IMX6U系列的 SPI 主机控制器驱动中,有实现 SPI 的软件片选!!

一定要使用 “cs-gpios” 属性来描述片选引脚,SPI主机驱动就会控制片选引脚,SPI主机控制器驱动实现了SPI的软件片选功能, 后面我们在编写SPI设备驱动时,软件上不用手动设置片选电平的高低。

I.MX6ULL SPI 主机控制器驱动:软件片选处理(片选信号)_spi软件片选-CSDN博客

drivers/spi/spi.c 中有函数将节点中的名为: cs-gpios 的 gpio 添加到 spi_master 结构体的 cs_gpios 成员中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
static int of_spi_register_master(struct spi_master *master)
{
int nb, i, *cs;
struct device_node *np = master->dev.of_node;

if (!np)
return 0;

nb = of_gpio_named_count(np, "cs-gpios");
master->num_chipselect = max_t(int, nb, master->num_chipselect);

/* Return error only for an incorrectly formed cs-gpios property */
if (nb == 0 || nb == -ENOENT)
return 0;
else if (nb < 0)
return nb;

cs = devm_kzalloc(&master->dev,
sizeof(int) * master->num_chipselect,
GFP_KERNEL);
master->cs_gpios = cs;

if (!master->cs_gpios)
return -ENOMEM;

for (i = 0; i < master->num_chipselect; i++)
cs[i] = -ENOENT;

for (i = 0; i < nb; i++)
cs[i] = of_get_named_gpio(np, "cs-gpios", i);

return 0;
}

IMX6ULL 的 ECSPI 主机驱动文件为 drivers/spi/spi-imx.c,有以下两个函数实现了软件片选 IO 的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
static int spi_imx_probe(struct platform_device *pdev)
{
// 省略...
master->dev.of_node = pdev->dev.of_node;
ret = spi_bitbang_start(&spi_imx->bitbang);
if (ret) {
dev_err(&pdev->dev, "bitbang start failed with %d\n", ret);
goto out_clk_put;
}

if (!master->cs_gpios) {
dev_err(&pdev->dev, "No CS GPIOs available\n");
ret = -EINVAL;
goto out_clk_put;
}

for (i = 0; i < master->num_chipselect; i++) {
if (!gpio_is_valid(master->cs_gpios[i]))
continue;

ret = devm_gpio_request(&pdev->dev, master->cs_gpios[i],
DRIVER_NAME);
if (ret) {
dev_err(&pdev->dev, "Can't get CS GPIO %i\n",
master->cs_gpios[i]);
goto out_clk_put;
}
}

dev_info(&pdev->dev, "probed\n");
// 省略...
}

static void spi_imx_chipselect(struct spi_device *spi, int is_active)
{
int active = is_active != BITBANG_CS_INACTIVE;
int dev_is_lowactive = !(spi->mode & SPI_CS_HIGH);

if (!gpio_is_valid(spi->cs_gpio))
return;

gpio_set_value(spi->cs_gpio, dev_is_lowactive ^ active);
}

SPI driver 驱动

注册 driver 驱动

注册 spi_driver 结构体驱动文件通用结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/string.h>

struct xxx_dev {
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
struct device_node *nd; /* 设备节点 */
int major; /* 主设备号 */
void *private_data; /* 私有数据 */
};

static struct xxx_dev xxx_dev_t;

static int xxx_spi_driver_probe(struct spi_device *spi)
{
/* 注册字符设备操作 */

// 初始化spi_device结构体
spi->mode = SPI_MODE_0; /* 设置模式 MODE0,CPOL=0,CPHA=0 */
spi_setup(spi);
xxx_dev_t.private_data = spi; /* 设置私有数据 */

return 0;
}
static int xxx_spi_driver_remove(struct spi_device *spi)
{
/* 注销字符设备操作 */

printk("xxx_spi_driver_remove ok\n");
return 0;
}

/* 设备树compatible匹配表 */
static const struct of_device_id of_match_table[] = {
{.compatible = "xxx"},
{},
};

/* 无设备树的匹配ID表 */
static const struct spi_device_id id_table[] = {
{"xxx"},
{},
};

/* 定义spi总线设备结构体 */
static struct spi_driver xxx_spi_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "xxx",
.of_match_table = of_match_table,
},
.probe = xxx_spi_driver_probe,
.remove = xxx_spi_driver_remove,
.id_table = id_table,
};


/* 驱动入口函数 */
static int __init xxx_spi_driver_init(void)
{
int ret;
// 注册 spi 驱动
ret = spi_register_driver(&xxx_spi_driver);

printk("xxx_spi_driver_init ok\n");
return ret;
}

/* 驱动出口函数 */
static void __exit xxx_spi_driver_exit(void)
{
// 将前面注册的spi_driver也从Linux内核中注销掉
spi_unregister_driver(&xxx_spi_driver);
printk("xxx_spi_driver_exit ok\n");
}

module_init(xxx_spi_driver_init);
module_exit(xxx_spi_driver_exit);
MODULE_LICENSE("GPL");

注册字符设备

匹配成功时注册字符设备:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <linux/fs.h> 				/*注册设备节点的文件结构体*/
#include <linux/cdev.h> // 对字符设备结构cdev 以及一系列的操作函数的定义。包含了cdev 结构及相关函数的定义。
#include <linux/device.h> //包含了device、class 等结构的定义

static ssize_t xxx_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{

return 0;
}

static int xxx_open (struct inode *node, struct file *file)
{

return 0;
}

static int xxx_close (struct inode *node, struct file *file)
{

return 0;
}

static const struct file_operations xxx_fops = {
.owner = THIS_MODULE,
.open = xxx_open,
.release = xxx_close,
.read = xxx_read,
};

static int xxx_spi_driver_probe(struct spi_device *spi)
{
// 1.分配设备号
if(xxx_dev_t.major)
{
xxx_dev_t.devid = MKDEV(xxx_dev_t.major, 0);
register_chrdev_region(xxx_dev_t.devid, 1, xxx_NAME);
}
else
{
alloc_chrdev_region(&xxx_dev_t.devid, 0, 1, xxx_NAME);
xxx_dev_t.major = MAJOR(xxx_dev_t.devid);
}

// 2.初始化cdev
cdev_init(&xxx_dev_t.cdev, &xxx_fops);

// 3.添加设备号到cdev
cdev_add(&xxx_dev_t.cdev, xxx_dev_t.devid, 1);

// 4.创建类
xxx_dev_t.class = class_create(THIS_MODULE, xxx_NAME);
if (IS_ERR(xxx_dev_t.device))
return PTR_ERR(xxx_dev_t.device);

// 5.类下创建设备
xxx_dev_t.device = device_create(xxx_dev_t.class, NULL, xxx_dev_t.devid, NULL, xxx_NAME);
if (IS_ERR(xxx_dev_t.device))
return PTR_ERR(xxx_dev_t.device);


return 0;
}
static int xxx_spi_driver_remove(struct spi_device *spi)
{
// 1.注销设备号
unregister_chrdev_region(xxx_dev_t.devid, 1);
// 2.删除设备
cdev_del(&xxx_dev_t.cdev);
// 3.注销设备节点
device_destroy(xxx_dev_t.class, xxx_dev_t.devid);
// 4.删除类
class_destroy(xxx_dev_t.class);
printk("xxx_spi_driver_remove ok\n");
return 0;
}

SPI 设备数据收发和处理

定义一个 xxx_dev 结构体用来存放一些创建设备需要的变量和获取到的信息,读取和写入函数主要是填充 spi_transfer 结构体,接着添加到 spi_message 队列去,使用 spi_sync 进行发送。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/*
* @description : 读取 xxx 设备多个寄存器数据
* @param – dev : xxx 设备
* @param – reg : 要读取的寄存器首地址
* @param – val : 读取到的数据
* @param – len : 要读取的数据长度
* @return : 操作结果
*/
static int xxx_read_reg(struct xxx_dev *dev, uint8_t reg, void *val, uint8_t len)
{
int ret;
uint8_t tx_data[1];
uint8_t *rx_data;
struct spi_transfer *t;
struct spi_message msg;

struct spi_device *spi = (struct spi_device *)dev->private_data;

/* 分配内存 */
t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
if(!t)
{
printk("kzalloc error\n");
kfree(t);
return -ENOMEM;;
}

rx_data = kzalloc(sizeof(uint8_t) + len, GFP_KERNEL);
if(!rx_data)
{
printk("kzalloc error\n");
kfree(rx_data);
return -ENOMEM;;
}

/* 发送要读取的寄存地址和接收读取的数据 */
tx_data[0] = ; /* 首位1还是0看芯片手册 */

t->tx_buf = tx_data; /* 要发送的数据:寄存器地址 */
t->rx_buf = rx_data; /* 要接收的数据 */
t->len = len + 1; /* t->len=发送的长度+读取的长度 */

spi_message_init(&msg); /* 初始化spi_message */
spi_message_add_tail(t, &msg); /* 将spi_transfer 添加到spi_message 队列 */
ret = spi_sync(spi, &msg); /* 同步传输 */

// SPI接收数据时,第一个字节是寄存器的地址,跳过一个字节即可。
memcpy(val, rx_data + 1, len);

/* 释放内存 */
kfree(t);
kfree(rx_data);

return ret;
}

/*
* @description : 向 xxx 设备多个寄存器写入数据
* @param – dev : 要写入的设备结构体
* @param – reg : 要写入的寄存器首地址
* @param – val : 要写入的数据缓冲区
* @param – len : 要写入的数据长度
* @return : 操作结果
*/
static int xxx_write_reg(struct xxx_dev *dev, uint8_t reg, uint8_t *buf, uint8_t len)
{
int ret;
uint8_t *tx_data;
struct spi_transfer *t;
struct spi_message msg;

struct spi_device *spi = (struct spi_device *)dev->private_data;

/* 分配内存 */
t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
if(!t)
{
printk("kzalloc error\n");
kfree(t);
return -ENOMEM;;
}

tx_data = kzalloc(sizeof(uint8_t) + len, GFP_KERNEL);
if(!t)
{
printk("kzalloc error\n");
kfree(tx_data);
return -ENOMEM;;
}

/* 发送要读取的寄存地址和写入的数据 */
*tx_data = reg & ~(0x80); /* 写数据的时候寄存器地址bit8要清零 */
memcpy(tx_data + 1, buf, len); /* 把len 个寄存器拷贝到txdata 里 */

t->tx_buf = tx_data; /* 要发送的数据 */
t->len = len + 1; /* t->len=发送的长度+读取的长度 */

spi_message_init(&msg); /* 初始化spi_message */
spi_message_add_tail(t, &msg); /* 将spi_transfer 添加到spi_message 队列 */
ret = spi_sync(spi, &msg); /* 同步传输 */

/* 释放内存 */
kfree(t);
kfree(tx_data);

return ret;
}