文章目录
一、spi_controller 结构体框图
二、spi_imx_probe()函数
此函数是spi主机控制器的平台设备驱动的probe成员
static struct platform_driver spi_imx_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = spi_imx_dt_ids,
.pm = IMX_SPI_PM,
},
.id_table = spi_imx_devtype,
.probe = spi_imx_probe,
.remove = spi_imx_remove,
};
module_platform_driver(spi_imx_driver);
- 主要内容
获取设备树节点信息,初始化spi时钟、dma…
保存spi寄存器起始地址,填充spi控制器回调函数
drivers/spi/spi-imx.c
static int spi_imx_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id =
of_match_device(spi_imx_dt_ids, &pdev->dev);
struct spi_imx_master *mxc_platform_info =
dev_get_platdata(&pdev->dev);
struct spi_master *master;
struct spi_imx_data *spi_imx;
struct resource *res;
const struct spi_imx_devtype_data *devtype_data = of_id ? of_id->data :
(struct spi_imx_devtype_data *)pdev->id_entry->driver_data;
bool slave_mode;
...
// 判断当前spi主控制器节点工作在主模式还是从模式
// 若是设置了这个属性,那么bool值返回1,为从模式
// 让后更具主从模式进行不同的分配内存操作
slave_mode = devtype_data->has_slavemode &&
of_property_read_bool(np, "spi-slave");
if (slave_mode)
master = spi_alloc_slave(&pdev->dev,
sizeof(struct spi_imx_data));
else
master = spi_alloc_master(&pdev->dev,
sizeof(struct spi_imx_data));
if (!master)
return -ENOMEM;
...
// 读取此属性,获取片选信号的数量,保存在num_cs
ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);
if (ret < 0) {
if (mxc_platform_info) {
num_cs = mxc_platform_info->num_chipselect;
master->num_chipselect = num_cs;
}
} else {
// 读取成功则赋值
master->num_chipselect = num_cs;
}
// 获取 spi_controller->device->spi_imx_data
spi_imx = spi_master_get_devdata(master);
// bitbang结构体的存在是为了让我们能够用普通gpio来模拟spi的时序
// 简单了解一下
spi_imx->bitbang.master = master;
spi_imx->dev = &pdev->dev;
spi_imx->slave_mode = slave_mode;
spi_imx->devtype_data = devtype_data;
// 记录每个片选信号所使用的gpio引脚
master->cs_gpios = devm_kzalloc(&master->dev,
sizeof(int) * master->num_chipselect, GFP_KERNEL);
spi_imx->bitbang.chipselect = spi_imx_chipselect;
spi_imx->bitbang.setup_transfer = spi_imx_setupxfer;
spi_imx->bitbang.txrx_bufs = spi_imx_transfer;
spi_imx->bitbang.master->setup = spi_imx_setup;
spi_imx->bitbang.master->cleanup = spi_imx_cleanup;
spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;
spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;
spi_imx->bitbang.master->slave_abort = spi_imx_slave_abort;
spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
...
init_completion(&spi_imx->xfer_done);
// 获取spi控制器对应的寄存器组的基地址
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spi_imx->base = devm_ioremap_resource(&pdev->dev, res);
...
// 详见下
ret = spi_bitbang_start(&spi_imx->bitbang);
...
}
spi_bitbang_start()函数
drivers/spi/spi-bitbang.c
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
struct spi_master *master = bitbang->master;
int ret;
if (!master || !bitbang->chipselect)
return -EINVAL;
mutex_init(&bitbang->lock);
if (!master->mode_bits)
master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;
if (master->transfer || master->transfer_one_message)
return -EINVAL;
// 填充的函数都和bitbang结构体相关,都是用来模拟spi时序
master->prepare_transfer_hardware = spi_bitbang_prepare_hardware;
master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware;
master->transfer_one = spi_bitbang_transfer_one;
master->set_cs = spi_bitbang_set_cs;
if (!bitbang->txrx_bufs) {
bitbang->use_dma = 0;
bitbang->txrx_bufs = spi_bitbang_bufs;
if (!master->setup) {
if (!bitbang->setup_transfer)
bitbang->setup_transfer =
spi_bitbang_setup_transfer;
master->setup = spi_bitbang_setup;
master->cleanup = spi_bitbang_cleanup;
}
}
/* driver may get busy before register() returns, especially
* if someone registered boardinfo for devices
*/
// 将spi主控制器注册到linux系统
ret = spi_register_master(spi_master_get(master));
if (ret)
spi_master_put(master);
return ret;
}
EXPORT_SYMBOL_GPL(spi_bitbang_start);
三、核心函数
1、spi_setup()函数
设置spi设备的片选信号、传输单位、最大传输速率…
drivers/spi/spi.c
int spi_setup(struct spi_device *spi)
{
unsigned bad_bits, ugly_bits;
int status;
...
// 对spi的传输单位进行设置,只能是8位或者16位
status = __spi_validate_bits_per_word(spi->controller,
spi->bits_per_word);
...
// 用spi控制器的最大传输速率来限制spi设备的最大传输速率
spi->max_speed_hz = spi->controller->max_speed_hz;
...
if (spi->controller->setup)//详见下
status = spi->controller->setup(spi);
...
return status;
}
2、spi_imx_setup()函数
drivers/spi/spi-imx.c
static int spi_imx_setup(struct spi_device *spi)
{
dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n", __func__,
spi->mode, spi->bits_per_word, spi->max_speed_hz);
// 若当前spi设备不支持片选信号模式,直接返回
if (spi->mode & SPI_NO_CS)
return 0;
// 若gpio有效,设置为输出模式
if (gpio_is_valid(spi->cs_gpio))
gpio_direction_output(spi->cs_gpio,
spi->mode & SPI_CS_HIGH ? 0 : 1);
// 详见下
spi_imx_chipselect(spi, BITBANG_CS_INACTIVE);
return 0;
}
spi_imx_chipselect()函数
drivers/spi/spi-imx.c
static void spi_imx_chipselect(struct spi_device *spi, int is_active)
{
int active = is_active != BITBANG_CS_INACTIVE;
// 判断spi的片选信号是否低有效
int dev_is_lowactive = !(spi->mode & SPI_CS_HIGH);
if (spi->mode & SPI_NO_CS)
return;
if (!gpio_is_valid(spi->cs_gpio))
return;
// ^是异或运算符,相同为0相异为1,低电平有效则设置为低电平
gpio_set_value(spi->cs_gpio, dev_is_lowactive ^ active);
}
3、spi_message_init()函数
include/linux/spi/spi.h
初始化一个spi信息
static inline void spi_message_init(struct spi_message *m)
{
memset(m, 0, sizeof *m);
// 初始化m中的两个链表节点成员
spi_message_init_no_memset(m);
}
spi_message结构体
include/linux/spi/spi.h
struct spi_message {
//
struct list_head transfers;
// 表示此spi消息属于哪一个spi设备
struct spi_device *spi;
...
// 当此spi消息传输完成回调使用的
void (*complete)(void *context);
void *context;
unsigned frame_length;
unsigned actual_length;
int status;
...
// 配合内核线程进行数据的异步传输
struct list_head queue;
void *state;
/* list of spi_res reources when the spi message is processed */
// 记录spi消息相关的资源
struct list_head resources;
};
spi_message_init_no_memset()函数
初始化两个链表节点
static inline void spi_message_init_no_memset(struct spi_message *m)
{
INIT_LIST_HEAD(&m->transfers);
INIT_LIST_HEAD(&m->resources);
}
4、spi_message_add_tail()函数
include/linux/spi/spi.h
把一个一个spi具体的消息存放到spi_message来保存
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{
// 把参数1链表节点加入到参数2的末尾
list_add_tail(&t->transfer_list, &m->transfers);
}
spi_transfer结构体
include/linux/spi/spi.h
此结构体是spi传输数据的最基本的单位,多个spi_transfer结构体可以组成一个spi_message结构体
struct spi_transfer {
// 指向想要发送的spi消息的buf
const void *tx_buf;
// 指向用来接收spi消息的buf
void *rx_buf;
unsigned len;
...
u32 speed_hz;
struct list_head transfer_list;
};
5、spi_sync()函数
drivers/spi/spi.c
同步传输数据,阻塞当前线程
详见下一讲
int spi_sync(struct spi_device *spi, struct spi_message *message)
6、spi_async()函数
drivers/spi/spi.c
异步传输数据,不会阻塞当前线程
详见下一讲
int spi_async(struct spi_device *spi, struct spi_message *message)