linux spi驱动开发学习(一)-----spi子系统架构
一.spi设备(各定义在include/linux/spi.h)
-
struct spi_device {
-
struct device dev;
-
struct spi_master *master;
-
u32 max_speed_hz;
-
u8 chip_select;
-
u8 mode;
-
u8 bits_per_word;
-
int irq;
-
void *controller_state;
-
void *controller_data;
-
char modalias[SPI_NAME_SIZE];
-
};
2.spi传输模式:
-
#define SPI_CPHA 0x01 //时钟相位
-
#define SPI_CPOL 0x02 //时钟继续
-
#define SPI_MODE_0 (0|0) //模式0
-
#define SPI_MODE_1 (0|SPI_CPHA) //模式1
-
#define SPI_MODE_2 (SPI_CPOL|0) //模式2
-
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA) //模式3
-
#define SPI_CS_HIGH 0x04 //片选高电平
-
#define SPI_LSB_FIRST 0x08 //LSB
-
#define SPI_3WIRE 0x10 //3线模式 SI和SO同一根线
-
#define SPI_LOOP 0x20 //回送模式
-
#define SPI_NO_CS 0x40 //单个设备占用一根SPI总线,所以没片选
-
#define SPI_READY 0x80 //从机拉低电平停止数据传输
3.spi设备的添加spi_new_device
-
struct spi_device *spi_new_device(struct spi_master *master,struct spi_board_info *chip)
-
{
-
struct spi_device *proxy;
-
int status;
-
-
proxy = spi_alloc_device(master);
-
if (!proxy)
-
return NULL;
-
WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
-
proxy->chip_select = chip->chip_select;
-
proxy->max_speed_hz = chip->max_speed_hz;
-
proxy->mode = chip->mode;
-
proxy->irq = chip->irq;
-
strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
-
proxy->dev.platform_data = (void *) chip->platform_data;
-
proxy->controller_data = chip->controller_data;
-
proxy->controller_state = NULL;
-
status = spi_add_device(proxy);
-
if (status < 0) {
-
spi_dev_put(proxy);
-
return NULL;
-
}
-
return proxy;
-
}
-
EXPORT_SYMBOL_GPL(spi_new_device);
3.1.分配spi设备
-
struct spi_device *spi_alloc_device(struct spi_master *master)
-
{
-
struct spi_device *spi;
-
struct device *dev = master->dev.parent;
-
-
if (!spi_master_get(master))
-
return NULL;
-
spi = kzalloc(sizeof *spi, GFP_KERNEL);
-
if (!spi) {
-
dev_err(dev, "cannot alloc spi_device\n");
-
spi_master_put(master);
-
return NULL;
-
}
-
spi->master = master;
-
spi->dev.parent = dev;
-
spi->dev.bus = &spi_bus_type;
-
spi->dev.release = spidev_release;
-
device_initialize(&spi->dev);
-
return spi;
-
}
-
EXPORT_SYMBOL_GPL(spi_alloc_device);
3.2 添加spi设备
-
int spi_add_device(struct spi_device *spi)
-
{
-
static DEFINE_MUTEX(spi_add_lock);
-
struct device *dev = spi->master->dev.parent;
-
struct device *d;
-
int status;
-
-
if (spi->chip_select >= spi->master->num_chipselect) {
-
dev_err(dev, "cs%d >= max %d\n",spi->chip_select,spi->master->num_chipselect);
-
return -EINVAL;
-
}
-
dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),spi->chip_select);
-
mutex_lock(&spi_add_lock);
-
d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev));
-
if (d != NULL) {
-
dev_err(dev, "chipselect %d already in use\n",spi->chip_select);
-
put_device(d);
-
status = -EBUSY;
-
goto done;
-
}
-
status = spi_setup(spi);
-
if (status < 0) {
-
dev_err(dev, "can't setup %s, status %d\n",dev_name(&spi->dev), status);
-
goto done;
-
}
-
status = device_add(&spi->dev);
-
if (status < 0)
-
dev_err(dev, "can't add %s, status %d\n",dev_name(&spi->dev), status);
-
else
-
dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));
-
done:
-
mutex_unlock(&spi_add_lock);
-
return status;
-
}
-
EXPORT_SYMBOL_GPL(spi_add_device);
3.2.1 spi setup方法
-
int spi_setup(struct spi_device *spi)
-
{
-
unsigned bad_bits;
-
int status;
-
bad_bits = spi->mode & ~spi->master->mode_bits;
-
if (bad_bits) {
-
dev_err(&spi->dev, "setup: unsupported mode bits %x\n",bad_bits);
-
return -EINVAL;
-
}
-
if (!spi->bits_per_word)
-
spi->bits_per_word = 8;
-
-
status = spi->master->setup(spi);
-
-
dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s""%u bits/w, %u Hz max --> %d\n",
-
(int) (spi->mode & (SPI_CPOL | SPI_CPHA)),(spi->mode & SPI_CS_HIGH) ? "cs_high, " : "",
-
(spi->mode & SPI_LSB_FIRST) ? "lsb, " : "",(spi->mode & SPI_3WIRE) ? "3wire, " : "",
-
(spi->mode & SPI_LOOP) ? "loopback, " : "",spi->bits_per_word, spi->max_speed_hz,status);
-
return status;
-
}
-
EXPORT_SYMBOL_GPL(spi_setup);
二.spi板级设备
1.板级设备结构体
-
struct spi_board_info {
-
char modalias[SPI_NAME_SIZE];
-
const void *platform_data;
-
void *controller_data;
-
int irq;
-
u32 max_speed_hz;
-
u16 bus_num;
-
u16 chip_select;
-
u8 mode;
-
};
2.板级设备注册(静态注册,一般在板级初始化函数中调用)
-
int __init spi_register_board_info(struct spi_board_info const *info, unsigned n)
-
{
-
struct boardinfo *bi;
-
int i;
-
-
bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);
-
if (!bi)
-
return -ENOMEM;
-
for (i = 0; i < n; i++, bi++, info++) {
-
struct spi_master *master;
-
memcpy(&bi->board_info, info, sizeof(*info));
-
mutex_lock(&board_lock);
-
list_add_tail(&bi->list, &board_list);
-
list_for_each_entry(master, &spi_master_list, list)
-
spi_match_master_to_boardinfo(master, &bi->board_info);
-
mutex_unlock(&board_lock);
-
}
-
return 0;
-
}
2.1.spi板级设备与spi主机匹配
-
static void spi_match_master_to_boardinfo(struct spi_master *master,struct spi_board_info *bi)
-
{
-
struct spi_device *dev;
-
if (master->bus_num != bi->bus_num)
-
return;
-
dev = spi_new_device(master, bi);
-
if (!dev)
-
dev_err(master->dev.parent, "can't create new device for %s\n",bi->modalias);
-
}
三.spi设备驱动
1.spi设备驱动结构体
-
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);
-
int (*suspend)(struct spi_device *spi, pm_message_t mesg);
-
int (*resume)(struct spi_device *spi);
-
struct device_driver driver;
-
};
2.spi设备驱动注册
-
int spi_register_driver(struct spi_driver *sdrv)
-
{
-
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);
-
}
-
EXPORT_SYMBOL_GPL(spi_register_driver);
这里的probe方法会在设备与驱动匹配的时候给调用
参看really_probe函数的部分代码
-
if (dev->bus->probe) {
-
ret = dev->bus->probe(dev);
-
if (ret)
-
goto probe_failed;
-
}
-
else if (drv->probe) {
-
ret = drv->probe(dev);
-
if (ret)
-
goto probe_failed;
-
}
2.1 spi_drv_probe
-
static int spi_drv_probe(struct device *dev)
-
{
-
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
-
return sdrv->probe(to_spi_device(dev));
-
}
3.spi设备驱动注销
-
static inline void spi_unregister_driver(struct spi_driver *sdrv)
-
{
-
if (sdrv)
-
driver_unregister(&sdrv->driver);
-
}
四.spi主机
1.spi主机结构体
-
struct spi_master {
-
struct device dev;
-
struct list_head list;
-
s16 bus_num;
-
u16 num_chipselect;
-
u16 dma_alignment;
-
u16 mode_bits;
-
u16 flags;
-
spinlock_t bus_lock_spinlock;
-
struct mutex bus_lock_mutex;
-
bool bus_lock_flag;
-
int (*setup)(struct spi_device *spi);
-
int (*transfer)(struct spi_device *spi,struct spi_message *mesg);
-
void (*cleanup)(struct spi_device *spi);
-
};
1.2.flags标志
-
#define SPI_MASTER_HALF_DUPLEX BIT(0) //半双工
-
#define SPI_MASTER_NO_RX BIT(1) //不读
-
#define SPI_MASTER_NO_TX BIT(2) //不写
2.spi主机初始化spi_alloc_master
-
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
-
{
-
struct spi_master *master;
-
if (!dev)
-
return NULL;
-
master = kzalloc(size + sizeof *master, GFP_KERNEL);
-
if (!master)
-
return NULL;
-
device_initialize(&master->dev);
-
master->dev.class = &spi_master_class;
-
master->dev.parent = get_device(dev);
-
spi_master_set_devdata(master, &master[1]);
-
return master;
-
}
-
EXPORT_SYMBOL_GPL(spi_alloc_master);
3.注册spi主机
-
int spi_register_master(struct spi_master *master)
-
{
-
static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
-
struct device *dev = master->dev.parent;
-
struct boardinfo *bi;
-
int status = -ENODEV;
-
int dynamic = 0;
-
-
if (!dev)
-
return -ENODEV;
-
if (master->num_chipselect == 0)
-
return -EINVAL;
-
if (master->bus_num < 0) {
-
master->bus_num = atomic_dec_return(&dyn_bus_id);
-
dynamic = 1;
-
}
-
spin_lock_init(&master->bus_lock_spinlock);
-
mutex_init(&master->bus_lock_mutex);
-
-
master->bus_lock_flag = 0;
-
dev_set_name(&master->dev, "spi%u", master->bus_num);
-
status = device_add(&master->dev);
-
if (status < 0)
-
goto done;
-
dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),dynamic ? " (dynamic)" : "");
-
mutex_lock(&board_lock);
-
list_add_tail(&master->list, &spi_master_list);
-
list_for_each_entry(bi, &board_list, list)
-
spi_match_master_to_boardinfo(master, &bi->board_info);
-
-
mutex_unlock(&board_lock);
-
status = 0;
-
of_register_spi_devices(master);
-
done:
-
return status;
-
}
-
EXPORT_SYMBOL_GPL(spi_register_master);
3.1 匹配spi主机和板级spi设备
-
static void spi_match_master_to_boardinfo(struct spi_master *master,struct spi_board_info *bi)
-
{
-
struct spi_device *dev;
-
if (master->bus_num != bi->bus_num)
-
return;
-
dev = spi_new_device(master, bi);
-
if (!dev)
-
dev_err(master->dev.parent, "can't create new device for %s\n",bi->modalias);
-
}
在注册板级设备或主机设备的时候都会添加
spi板级设备添加进board_list链表,spi主机设备添加进spi_master_list链表
不管是先注册spi板级设备还是先注册spi主机设备
都会调用list_for_each_entry遍历对应的要匹配的设备的链表,查找是否有匹配的例子
若找到都会调用spi_match_master_to_boardinfo函数添加spi设备
4.注销spi主机
-
void spi_unregister_master(struct spi_master *master)
-
{
-
int dummy;
-
mutex_lock(&board_lock);
-
list_del(&master->list);
-
mutex_unlock(&board_lock);
-
dummy = device_for_each_child(&master->dev, NULL, __unregister);
-
device_unregister(&master->dev);
-
}
-
EXPORT_SYMBOL_GPL(spi_unregister_master);
4.1 注销挂载该总线上的spi子设备
-
static int __unregister(struct device *dev, void *null)
-
{
-
spi_unregister_device(to_spi_device(dev));
-
return 0;
-
}
5.spi主机设备类
-
static struct class spi_master_class = {
-
.name = "spi_master",
-
.owner = THIS_MODULE,
-
.dev_release = spi_master_release,
-
};
五.spi总线
1.spi总线结构体
-
struct bus_type spi_bus_type = {
-
.name = "spi",
-
.dev_attrs = spi_dev_attrs,
-
.match = spi_match_device,
-
.uevent = spi_uevent,
-
.suspend = spi_suspend,
-
.resume = spi_resume,
-
};
-
EXPORT_SYMBOL_GPL(spi_bus_type);
2.设备匹配方法spi_match_device
前面的匹配方法是spi板级设备与spi主机设备的匹配方法,匹配的结果是添加新spi设备spi_new_device
这里的匹配是spi设备和spi驱动的匹配,匹配的结果是会调用spi驱动的设备驱动文件probe方法,既spi_drv_probe
-
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);
-
-
if (of_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;
-
}
2.1 of_driver_match_device
-
static inline int of_driver_match_device(const struct device *dev,const struct device_driver *drv)
-
{
-
return of_match_device(drv->of_match_table, dev) != NULL;
-
}
2.1.1 of_match_device
-
const struct of_device_id *of_match_device(const struct of_device_id *matches,const struct device *dev)
-
{
-
if ((!matches) || (!dev->of_node))
-
return NULL;
-
return of_match_node(matches, dev->of_node);
-
}
-
EXPORT_SYMBOL(of_match_device);
2.1.1.1 of_match_node //drv->of_match_table,dev->of_node
-
const struct of_device_id *of_match_node(const struct of_device_id *matches,const struct device_node *node)
-
{
-
while (matches->name[0] || matches->type[0] || matches->compatible[0]) {
-
int match = 1;
-
if (matches->name[0])
-
match &= node->name && !strcmp(matches->name, node->name);
-
if (matches->type[0])
-
match &= node->type && !strcmp(matches->type, node->type);
-
if (matches->compatible[0])
-
match &= of_device_is_compatible(node,matches->compatible);
-
if (match)
-
return matches;
-
matches++;
-
}
-
return NULL;
-
}
-
EXPORT_SYMBOL(of_match_node);
2.2 spi_match_id
-
static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,const struct spi_device *sdev)
-
{
-
while (id->name[0]) {
-
if (!strcmp(sdev->modalias, id->name))
-
return id;
-
id++;
-
}
-
return NULL;
-
}
六 spi消息和spi传输
1.spi消息结构体
-
struct spi_message {
-
struct list_head transfers;
-
struct spi_device *spi;
-
unsigned is_dma_mapped:1;
-
void (*complete)(void *context);
-
void *context;
-
unsigned actual_length;
-
int status;
-
struct list_head queue;
-
void *state;
-
};
2.初始化spi消息
-
static inline void spi_message_init(struct spi_message *m)
-
{
-
memset(m, 0, sizeof *m);
-
INIT_LIST_HEAD(&m->transfers);
-
}
3.添加传输事务到spi传输链表
-
static inline void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
-
{
-
list_add_tail(&t->transfer_list, &m->transfers);
-
}
4.spi传输结构体
-
struct spi_transfer {
-
const void *tx_buf;
-
void *rx_buf;
-
unsigned len;
-
dma_addr_t tx_dma;
-
dma_addr_t rx_dma;
-
unsigned cs_change:1;
-
u8 bits_per_word;
-
u16 delay_usecs;
-
u32 speed_hz;
-
struct list_head transfer_list;
-
};
七.spi子系统的初始化spi_init
-
static int __init spi_init(void)
-
{
-
int status;
-
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
-
if (!buf) {
-
status = -ENOMEM;
-
goto err0;
-
}
-
status = bus_register(&spi_bus_type);
-
if (status < 0)
-
goto err1;
-
status = class_register(&spi_master_class);
-
if (status < 0)
-
goto err2;
-
return 0;
-
err2:
-
bus_unregister(&spi_bus_type);
-
err1:
-
kfree(buf);
-
buf = NULL;
-
err0:
-
return status;
-
}
-
postcore_initcall(spi_init);
八.spi子系统的API
1.spi读 spi_read
-
static inline int spi_read(struct spi_device *spi, u8 *buf, size_t len)
-
{
-
struct spi_transfer t = {
-
.rx_buf = buf,
-
.len = len,
-
};
-
struct spi_message m;
-
spi_message_init(&m);
-
spi_message_add_tail(&t, &m);
-
return spi_sync(spi, &m);
-
}
2.spi写 spi_write
-
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);
-
}
spi的读写操作都是初始化一个spi_transfer传输结构体,并将其添加进spi消息传输事务链表中
然后通过spi_sync来同步读写操作,接着看下spi_sync的具体代码
2.1 spi_sync
-
int spi_sync(struct spi_device *spi, struct spi_message *message)
-
{
-
return __spi_sync(spi, message, 0);
-
}
-
EXPORT_SYMBOL_GPL(spi_sync);
2.1.1 __spi_sync函数
-
static int __spi_sync(struct spi_device *spi, struct spi_message *message,int bus_locked)
-
{
-
DECLARE_COMPLETION_ONSTACK(done);
-
int status;
-
struct spi_master *master = spi->master;
-
-
message->complete = spi_complete;
-
message->context = &done;
-
if (!bus_locked)
-
mutex_lock(&master->bus_lock_mutex);
-
status = spi_async_locked(spi, message);
-
if (!bus_locked)
-
mutex_unlock(&master->bus_lock_mutex);
-
if (status == 0) {
-
wait_for_completion(&done);
-
status = message->status;
-
}
-
message->context = NULL;
-
return status;
-
}
2.1.1.1 spi_async_locked 函数
-
int spi_async_locked(struct spi_device *spi, struct spi_message *message)
-
{
-
struct spi_master *master = spi->master;
-
int ret;
-
unsigned long flags;
-
spin_lock_irqsave(&master->bus_lock_spinlock, flags);
-
ret = __spi_async(spi, message);
-
spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
-
return ret;
-
}
-
EXPORT_SYMBOL_GPL(spi_async_locked);
2.1.1.1.1 __spi_async函数(异步)
-
static int __spi_async(struct spi_device *spi, struct spi_message *message)
-
{
-
struct spi_master *master = spi->master;
-
-
if ((master->flags & SPI_MASTER_HALF_DUPLEX)|| (spi->mode & SPI_3WIRE)) {
-
struct spi_transfer *xfer;
-
unsigned flags = master->flags;
-
list_for_each_entry(xfer, &message->transfers, transfer_list) {
-
if (xfer->rx_buf && xfer->tx_buf)
-
return -EINVAL;
-
if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)
-
return -EINVAL;
-
if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)
-
return -EINVAL;
-
}
-
}
-
message->spi = spi;
-
message->status = -EINPROGRESS;
-
return master->transfer(spi, message);
-
}
3.spi先写后读 spi_write_then_read
-
int spi_write_then_read(struct spi_device *spi,const u8 *txbuf, unsigned n_tx,u8 *rxbuf, unsigned n_rx)
-
{
-
static DEFINE_MUTEX(lock);
-
int status;
-
struct spi_message message;
-
struct spi_transfer x[2];
-
u8 *local_buf;
-
-
if ((n_tx + n_rx) > SPI_BUFSIZ)
-
return -EINVAL;
-
spi_message_init(&message);
-
memset(x, 0, sizeof x);
-
if (n_tx) {
-
x[0].len = n_tx;
-
spi_message_add_tail(&x[0], &message);
-
}
-
if (n_rx) {
-
x[1].len = n_rx;
-
spi_message_add_tail(&x[1], &message);
-
}
-
if (!mutex_trylock(&lock)) {
-
local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
-
if (!local_buf)
-
return -ENOMEM;
-
} else
-
local_buf = buf;
-
memcpy(local_buf, txbuf, n_tx);
-
x[0].tx_buf = local_buf;
-
x[1].rx_buf = local_buf + n_tx;
-
status = spi_sync(spi, &message);
-
if (status == 0)
-
memcpy(rxbuf, x[1].rx_buf, n_rx);
-
if (x[0].tx_buf == buf)
-
mutex_unlock(&lock);
-
else
-
kfree(local_buf);
-
return status;
-
}
-
EXPORT_SYMBOL_GPL(spi_write_then_read);
4.spi写8位后读8位数据 spi_w8r8
-
static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)
-
{
-
ssize_t status;
-
u8 result;
-
status = spi_write_then_read(spi, &cmd, 1, &result, 1);
-
return (status < 0) ? status : result;
-
}
5.spi写8位数据后读16位数据 spi_w8r16
-
static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd)
-
{
-
ssize_t status;
-
u16 result;
-
status = spi_write_then_read(spi, &cmd, 1, (u8 *) &result, 2);
-
return (status < 0) ? status : result;
-
}
这里的API是内核空间使用的接口,设备驱动程序调用这些API直接操作spi的读写操作,来完成任务
阅读(1127) | 评论(0) | 转发(0) |