linuxSPI主机与设备驱动

  1. SPI主机驱动
  2. SPI外设驱动

SPI主机驱动

在linux中,用spi_controler结构体来描述一个SPI主机控制器驱动,其主要成员是主机控制器的序号、片选数量、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
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
struct spi_controller {
struct device dev;

struct list_head list;

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 */
u32 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_BPW_RANGE_MASK(min, max) GENMASK((max) - 1, (min) - 1)

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

/* other constraints relevant to this driver */
u16 flags;

...

/* flag indicating this is an SPI slave controller */
bool slave;

/*
* 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 */
...

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

...

/* bidirectional bulk transfers
*/
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);

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

...

/*
* These hooks are for drivers that want to use the generic
* controller 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.
*/
...
/*
* These hooks are for drivers that use a generic implementation
* of transfer_one_message() provied by the core.
*/
...
/* Optimized handlers for SPI memory-like operations. */
const struct spi_controller_mem_ops *mem_ops;

/* CS delays */
...

/* gpio chip select */
...
/* 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_controller *ctlr, unsigned cs);

/*

* Driver sets this field to indicate it is able to snapshot SPI
* transfers (needed e.g. for reading the time of POSIX clocks)
*/
bool ptp_sts_supported;

/* Interrupt enable state during PTP system timestamping */
unsigned long irq_flags;
};

struct spi_controller *spi_alloc_master(struct device *host,
unsigned int size);

int spi_register_controller(struct spi_controller *ctlr);
int devm_spi_register_controller(struct device *dev,
struct spi_controller *ctlr);
void spi_unregister_controller(struct spi_controller *ctlr);

在linux中,spi_driver结构体描述一个SPI外设驱动,这个外设驱动可以认为是spi_controller的客户端驱动,其继承device_driver。

1
2
3
4
5
6
7
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外设驱动中,当通过SPI总线进行数据传输时,使用了一套与CPU无关的统一接口。这套接口的第一个关键数据结构是spi_transfer,它描述了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
33
34
35
36
37
38
39
40
struct spi_transfer {
/* it's ok if tx_buf == rx_buf (right?)
* for MicroWire, one buffer must be null
* buffers must work with dma_*map_single() calls, unless
* spi_message.is_dma_mapped reports a pre-existing mapping
*/
const void *tx_buf;
void *rx_buf;
unsigned len;

dma_addr_t tx_dma;
dma_addr_t rx_dma;
struct sg_table tx_sg;
struct sg_table rx_sg;

unsigned cs_change:1;
unsigned tx_nbits:3;
unsigned rx_nbits:3;
#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
u8 bits_per_word;
u16 delay_usecs;
struct spi_delay delay;
struct spi_delay cs_change_delay;
struct spi_delay word_delay;
u32 speed_hz;

u32 effective_speed_hz;

unsigned int ptp_sts_word_pre;
unsigned int ptp_sts_word_post;

struct ptp_system_timestamp *ptp_sts;

bool timestamped_pre;
bool timestamped_post;

struct list_head transfer_list;
};

而一次完整的SPI传输流程可能不止包含一次spi_transfer,它可能包含一个或多个spi_transfer,这些spi_transfer最终通过spi_message组织到一起。

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
struct spi_message {
struct list_head transfers;

struct spi_device *spi;

unsigned is_dma_mapped:1;

/* REVISIT: we might want a flag affecting the behavior of the
* last transfer ... allowing things like "read 16 bit length L"
* immediately followed by "read L bytes". Basically imposing
* a specific message scheduling algorithm.
*
* Some controller drivers (message-at-a-time queue processing)
* could provide that as their default scheduling algorithm. But
* others (with multi-message pipelines) could need a flag to
* tell them about such special cases.
*/

/* completion is reported through a callback */
void (*complete)(void *context);
void *context;
unsigned frame_length;
unsigned actual_length;
int status;

/* for optional use by whatever driver currently owns the
* spi_message ... between calls to spi_async and then later
* complete(), that's the spi_controller controller driver.
*/
struct list_head queue;
void *state;

/* list of spi_res reources when the spi message is processed */
struct list_head resources;
};

通过spi_message_init()可以对spi_message进行初始化,一次典型的初始化spi_transfer、spi_message并进行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
static inline int
spi_write(struct spi_device *spi, const u8 *buf, size_t len)
{
struct spi_transfer t = {
.tx_buf = buf,
.len = len,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spi_sync(spi, &m);
}

static inline int
spi_read(struct spi_device *spi, u8 *buf, size_t len)
{
struct spi_transfer t = {
.tx_buf = buf,
.len = len,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spi_sync(spi, &m);
}

SPI主机控制器驱动位于drivers/spi/目录下,这些驱动的主体是实现了spi_controller的transfer()、transfer_one()、setup()这样的成员函数,当然,也可能是实现了spi_bitbang的txrx_bufs()、setup_transfer()、chipselect()这样的成员函数。

SPI外设驱动

SPI外设驱动遍布内核的drivers、sound的各个子目录下,SPI只是一种总线,spi_driver的作用只是将SPI外设挂接到该总线上,因此在spi_driver的probe()成员函数中,将注册SPI外设本身所属设备驱动的类型。

和platform_driver对应着platform_device一样,spi_driver对应着spi_device。如同platform_device需要在BSP的板级文件中添加板信息数据一样,spi_device同样需要。spi_device的板信息用spi_board_info结构体描述,如下所示:

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
struct spi_board_info {
/* the device name and module name are coupled, like platform_bus;
* "modalias" is normally the driver name.
*
* platform_data goes to spi_device.dev.platform_data,
* controller_data goes to spi_device.controller_data,
* device properties are copied and attached to spi_device,
* irq is copied too
*/
char modalias[SPI_NAME_SIZE];
const void *platform_data;
const struct property_entry *properties;
void *controller_data;
int irq;

/* slower signaling on noisy or low voltage boards */
u32 max_speed_hz;


/* bus_num is board specific and matches the bus_num of some
* spi_controller that will probably be registered later.
*
* chip_select reflects how this chip is wired to that master;
* it's less than num_chipselect.
*/
u16 bus_num;
u16 chip_select;

/* mode becomes spi_device.mode, and is essential for chips
* where the default of SPI_CS_HIGH = 0 is wrong.
*/
u32 mode;

/* ... may need additional spi_device chip config data here.
* avoid stuff protocol drivers can set; but include stuff
* needed to behave without being bound to a driver:
* - quirks like clock rate mattering when not selected
*/
};

在linux启动过程中,在机器的init_machine()函数中,会通过如下语句注册这些spi_board_info:

1
2
int
spi_register_board_info(struct spi_board_info const *info, unsigned n);

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 yxhlfx@163.com

文章标题:linuxSPI主机与设备驱动

本文作者:红尘追风

发布时间:2016-07-20, 16:44:42

原始链接:http://www.micernel.com/2016/07/20/linuxSPI%E4%B8%BB%E6%9C%BA%E4%B8%8E%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录