目录
目录
3. devm_spi_register_controller
3.4 spi_controller_initialize_queue(ctlr->transfer没有定义)
3.5 spi_match_controller_to_boardinfo
3.5.1 spi_match_controller_to_boardinfo
3.6 of_register_spi_devices(从设备树中注册spi device设备)
1. spi_master的编写流程
SPI控制器驱动在Linux中用struct spi_master定义,也就是struct spi_controller。
struct spi_controller {
struct device dev;
struct list_head list;
....
}
#define spi_master spi_controller
struct spi_master *master;
static int xxx_spi_probe(struct platform_device *pdev)
{
/* 1. 分配master */
struct spi_master *master;
master = spi_alloc_master(&pdev->dev, sizeof(*xxx_spi));
/* 2. 设置master */
master->prepare_transfer_hardware = tsm_prepare_transfer_hardware;
master->prepare_message = tsm_prepare_message;
master->transfer_one = tsm_transfer_one;
master->unprepare_transfer_hardware = tsm_unprepare_transfer_hardware;
master->set_cs = tsm_spi_chipselect;
master->setup = tsm_spi_setup;
master->cleanup = tsm_spi_cleanup;
master->mode_bits = SPI_CPOL | SPI_CPHA;
/* Set to default valid value */
master->bits_per_word_mask = SPI_BPW_MASK(8);
master->max_speed_hz = tsm_spi->clk_rate;
tsm_spi->speed_hz = master->max_speed_hz;
.....
/* 3. 注册master */
ret = devm_spi_register_controller(&pdev->dev, master);
}
2. spi_alloc_master
spi_alloc_master->__spi_alloc_controller
struct spi_controller *__spi_alloc_controller(struct device *dev,
unsigned int size, bool slave)
{
struct spi_controller *ctlr;
if (!dev)
return NULL;
ctlr = kzalloc(size + sizeof(*ctlr), GFP_KERNEL);
if (!ctlr)
return NULL;
device_initialize(&ctlr->dev);
ctlr->bus_num = -1;
ctlr->num_chipselect = 1;
ctlr->slave = slave;
if (IS_ENABLED(CONFIG_SPI_SLAVE) && slave)
ctlr->dev.class = &spi_slave_class;
else
ctlr->dev.class = &spi_master_class;
ctlr->dev.parent = dev;
pm_suspend_ignore_children(&ctlr->dev, true);
spi_controller_set_devdata(ctlr, &ctlr[1]);
return ctlr;
}
简单来说,就是分配一个struct spi_controller结构体,设置其bus_num、num_chipselect默认值分别为-1,1。bus_num为-1,表示该control在spi bus上的编号动态分配,如果需要修改为自己需要的值,后面可以修改,也可以在设备树中修改。num_chipselect表示该control上的从设备的数量。
3. devm_spi_register_controller
devm_spi_register_controller->spi_register_controller
3.1 of_spi_register_master
int spi_register_controller(struct spi_controller *ctlr)
{
.........
if (!dev)
return -ENODEV;
if (!spi_controller_is_slave(ctlr)) {
status = of_spi_register_master(ctlr);
if (status)
return status;
}
...........
}
static int of_spi_register_master(struct spi_controller *ctlr)
{
int nb, i, *cs;
struct device_node *np = ctlr->dev.of_node;
if (!np)
return 0;
nb = of_gpio_named_count(np, "cs-gpios");
ctlr->num_chipselect = max_t(int, nb, ctlr->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(&ctlr->dev, sizeof(int) * ctlr->num_chipselect,
GFP_KERNEL);
ctlr->cs_gpios = cs;
if (!ctlr->cs_gpios)
return -ENOMEM;
for (i = 0; i < ctlr->num_chipselect; i++)
cs[i] = -ENOENT;
for (i = 0; i < nb; i++)
cs[i] = of_get_named_gpio(np, "cs-gpios", i);
return 0;
}
该函数主要是实现对num_chipselect字段的重新幅值。
3.2 bus_num的申请
int spi_register_controller(struct spi_controller *ctlr)
{
/* even if it's just one always-selected device, there must
* be at least one chipselect
*/
if (ctlr->num_chipselect == 0)
return -EINVAL;
if (ctlr->bus_num >= 0) {
/* devices with a fixed bus num must check-in with the num */
mutex_lock(&board_lock);
id = idr_alloc(&spi_master_idr, ctlr, ctlr->bus_num,
ctlr->bus_num + 1, GFP_KERNEL);
mutex_unlock(&board_lock);
if (WARN(id < 0, "couldn't get idr"))
return id == -ENOSPC ? -EBUSY : id;
ctlr->bus_num = id;
} else if (ctlr->dev.of_node) {
/* allocate dynamic bus number using Linux idr */
id = of_alias_get_id(ctlr->dev.of_node, "spi");
if (id >= 0) {
ctlr->bus_num = id;
mutex_lock(&board_lock);
id = idr_alloc(&spi_master_idr, ctlr, ctlr->bus_num,
ctlr->bus_num + 1, GFP_KERNEL);
mutex_unlock(&board_lock);
if (WARN(id < 0, "couldn't get idr"))
return id == -ENOSPC ? -EBUSY : id;
}
}
if (ctlr->bus_num < 0) {
first_dynamic = of_alias_get_highest_id("spi");
if (first_dynamic < 0)
first_dynamic = 0;
else
first_dynamic++;
mutex_lock(&board_lock);
id = idr_alloc(&spi_master_idr, ctlr, first_dynamic,
0, GFP_KERNEL);
mutex_unlock(&board_lock);
if (WARN(id < 0, "couldn't get idr"))
return id;
ctlr->bus_num = id;
}
...........
}
bus_num大于0,检查是否有效以及是否已经申请过了。若bus_num小于0,先从设备树中找,找不到,则动态分配。
3.3 初始化其他一些字段
int spi_register_controller(struct spi_controller *ctlr)
{
INIT_LIST_HEAD(&ctlr->queue);
spin_lock_init(&ctlr->queue_lock);
spin_lock_init(&ctlr->bus_lock_spinlock);
mutex_init(&ctlr->bus_lock_mutex);
mutex_init(&ctlr->io_mutex);
ctlr->bus_lock_flag = 0;
init_completion(&ctlr->xfer_completion);
...........
}
3.4 spi_controller_initialize_queue(ctlr->transfer没有定义)
int spi_register_controller(struct spi_controller *ctlr)
{
/* If we're using a queued driver, start the queue */
if (ctlr->transfer)
dev_info(dev, "controller is unqueued, this is deprecated\n");
else {
status = spi_controller_initialize_queue(ctlr);
...........
}
如果没有定义ctlr->transfer的话,则调用spi_controller_initialize_queue函数。
static int spi_controller_initialize_queue(struct spi_controller *ctlr)
{
int ret;
ctlr->transfer = spi_queued_transfer;
if (!ctlr->transfer_one_message)
ctlr->transfer_one_message = spi_transfer_one_message;
/* Initialize and start queue */
ret = spi_init_queue(ctlr);
if (ret) {
dev_err(&ctlr->dev, "problem initializing queue\n");
goto err_init_queue;
}
ctlr->queued = true;
ret = spi_start_queue(ctlr);
......
}
(1)配置spi_master(spi_control)里面关于传输的函数。
(2)初始化queue,开始queue。
master需要填充哪些参数,这个我们后面再谈。
3.5 spi_match_controller_to_boardinfo
int spi_register_controller(struct spi_controller *ctlr)
{
mutex_lock(&board_lock);
list_add_tail(&ctlr->list, &spi_controller_list);
list_for_each_entry(bi, &board_list, list)
spi_match_controller_to_boardinfo(ctlr, &bi->board_info);
mutex_unlock(&board_lock);
...........
}
(1)将该control加入到spi_controller_list链表中。
(2)调用spi_match_controller_to_boardinfo注册board_list中的spi device设置。
3.5.1 spi_match_controller_to_boardinfo
static void spi_match_controller_to_boardinfo(struct spi_controller *ctlr,
struct spi_board_info *bi)
{
struct spi_device *dev;
if (ctlr->bus_num != bi->bus_num)
return;
dev = spi_new_device(ctlr, bi);
if (!dev)
dev_err(ctlr->dev.parent, "can't create new device for %s\n",
bi->modalias);
}
3.5.2 spi_register_board_info
int spi_register_board_info(struct spi_board_info const *info, unsigned n)
{
struct boardinfo *bi;
int i;
if (!n)
return 0;
bi = kcalloc(n, sizeof(*bi), GFP_KERNEL);
if (!bi)
return -ENOMEM;
for (i = 0; i < n; i++, bi++, info++) {
struct spi_controller *ctlr;
memcpy(&bi->board_info, info, sizeof(*info));
if (info->properties) {
bi->board_info.properties =
property_entries_dup(info->properties);
if (IS_ERR(bi->board_info.properties))
return PTR_ERR(bi->board_info.properties);
}
mutex_lock(&board_lock);
list_add_tail(&bi->list, &board_list);
list_for_each_entry(ctlr, &spi_controller_list, list)
spi_match_controller_to_boardinfo(ctlr,
&bi->board_info);
mutex_unlock(&board_lock);
}
return 0;
}
3.6 of_register_spi_devices(从设备树中注册spi device设备)
int spi_register_controller(struct spi_controller *ctlr)
{
...........
/* Register devices from the device tree and ACPI */
of_register_spi_devices(ctlr);
acpi_register_spi_devices(ctlr);
...........
}
static struct spi_device *
of_register_spi_device(struct spi_controller *ctlr, struct device_node *nc)
{
struct spi_device *spi;
int rc;
/* Alloc an spi_device */
spi = spi_alloc_device(ctlr);
if (!spi) {
dev_err(&ctlr->dev, "spi_device alloc error for %pOF\n", nc);
rc = -ENOMEM;
goto err_out;
}
/* Select device driver */
rc = of_modalias_node(nc, spi->modalias,
sizeof(spi->modalias));
if (rc < 0) {
dev_err(&ctlr->dev, "cannot find modalias for %pOF\n", nc);
goto err_out;
}
rc = of_spi_parse_dt(ctlr, spi, nc);
if (rc)
goto err_out;
/* Store a pointer to the node in the device structure */
of_node_get(nc);
spi->dev.of_node = nc;
spi->dev.fwnode = of_fwnode_handle(nc);
/* Register the new device */
rc = spi_add_device(spi);
......
}
3.6.1 spi_alloc_device
3.6.2 of_modalias_node
int of_modalias_node(struct device_node *node, char *modalias, int len)
{
const char *compatible, *p;
int cplen;
compatible = of_get_property(node, "compatible", &cplen);
if (!compatible || strlen(compatible) > cplen)
return -ENODEV;
p = strchr(compatible, ',');
strlcpy(modalias, p ? p + 1 : compatible, len);
return 0;
}
of_modalias_node会把设备树compatible字段,后面的值赋值给spi->modalias。例如下面的代码,spi->modalias会被赋值为w25q64。
&uspi0 {
status = "okay";
//max-freq = <48000000>;
w25q64@00 {
......
compatible = "winbond,w25q64";
.....
};
};
3.6.2 of_spi_parse_dt
static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
struct device_node *nc)
{
u32 value;
int rc;
/* Mode (clock phase/polarity/etc.) */
if (of_property_read_bool(nc, "spi-cpha"))
spi->mode |= SPI_CPHA;
if (of_property_read_bool(nc, "spi-cpol"))
spi->mode |= SPI_CPOL;
if (of_property_read_bool(nc, "spi-cs-high"))
spi->mode |= SPI_CS_HIGH;
if (of_property_read_bool(nc, "spi-3wire"))
spi->mode |= SPI_3WIRE;
if (of_property_read_bool(nc, "spi-lsb-first"))
spi->mode |= SPI_LSB_FIRST;
/* Device DUAL/QUAD mode */
if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
switch (value) {
case 1:
break;
case 2:
spi->mode |= SPI_TX_DUAL;
break;
case 4:
spi->mode |= SPI_TX_QUAD;
break;
default:
dev_warn(&ctlr->dev,
"spi-tx-bus-width %d not supported\n",
value);
break;
}
}
if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {
switch (value) {
case 1:
break;
case 2:
spi->mode |= SPI_RX_DUAL;
break;
case 4:
spi->mode |= SPI_RX_QUAD;
break;
default:
dev_warn(&ctlr->dev,
"spi-rx-bus-width %d not supported\n",
value);
break;
}
}
if (spi_controller_is_slave(ctlr)) {
if (strcmp(nc->name, "slave")) {
dev_err(&ctlr->dev, "%pOF is not called 'slave'\n",
nc);
return -EINVAL;
}
return 0;
}
/* Device address */
rc = of_property_read_u32(nc, "reg", &value);
if (rc) {
dev_err(&ctlr->dev, "%pOF has no valid 'reg' property (%d)\n",
nc, rc);
return rc;
}
spi->chip_select = value;
/* Device speed */
rc = of_property_read_u32(nc, "spi-max-frequency", &value);
if (rc) {
dev_err(&ctlr->dev,
"%pOF has no valid 'spi-max-frequency' property (%d)\n", nc, rc);
return rc;
}
spi->max_speed_hz = value;
return 0;
}
如果使用设备树添加spi device,那么你必须在设备树中添加一些值。其他好理解,reg表示该spi control中该设备的片选编号。