linux驱动 8. SPI驱动

0.简介

SPI也是嵌入式设备经常用到的一种总线,它采用主从架构,也可以全双工通讯。今天我们来分析一下SPI在linux中的驱动。
software:linux 4.14.98
hardware: imx8

和I2C驱动有些类似。SPI的驱动大致可以分为三部分。
1.SPI设备驱动部分
2.SPI核心层部分
3.SPI控制器驱动部分
摘录网络上面一张图,如下图所示
linux驱动 8. SPI驱动_第1张图片

1.SPI控制器驱动

在这里先分析一下SPI控制器驱动给,下面看代码。
FILE:drivers/spi/spi-imx.c

static SIMPLE_DEV_PM_OPS(imx_spi_pm, spi_imx_suspend, spi_imx_resume);
#define IMX_SPI_PM       (&imx_spi_pm)
#else
#define IMX_SPI_PM       NULL
#endif

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_imx_dt_ids定义如下:

static const struct of_device_id spi_imx_dt_ids[] = {
        { .compatible = "fsl,imx1-cspi", .data = &imx1_cspi_devtype_data, },
        { .compatible = "fsl,imx21-cspi", .data = &imx21_cspi_devtype_data, },
        { .compatible = "fsl,imx27-cspi", .data = &imx27_cspi_devtype_data, },
        { .compatible = "fsl,imx31-cspi", .data = &imx31_cspi_devtype_data, },
        { .compatible = "fsl,imx35-cspi", .data = &imx35_cspi_devtype_data, },
        { .compatible = "fsl,imx51-ecspi", .data = &imx51_ecspi_devtype_data, },
        { .compatible = "fsl,imx53-ecspi", .data = &imx53_ecspi_devtype_data, },
        { .compatible = "fsl,imx6ul-ecspi", .data = &imx6ul_ecspi_devtype_data, },
        { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, spi_imx_dt_ids);

spi_imx_devtype 定义如下:

static struct platform_device_id spi_imx_devtype[] = {
        {
                .name = "imx1-cspi",
                .driver_data = (kernel_ulong_t) &imx1_cspi_devtype_data,
        }, {
                .name = "imx21-cspi",
                .driver_data = (kernel_ulong_t) &imx21_cspi_devtype_data,
        }, {
                .name = "imx27-cspi",
                .driver_data = (kernel_ulong_t) &imx27_cspi_devtype_data,
        }, {
                .name = "imx31-cspi",
                .driver_data = (kernel_ulong_t) &imx31_cspi_devtype_data,
        }, {
                .name = "imx35-cspi",
                .driver_data = (kernel_ulong_t) &imx35_cspi_devtype_data,
        }, {
                .name = "imx51-ecspi",
                .driver_data = (kernel_ulong_t) &imx51_ecspi_devtype_data,
        }, {
                .name = "imx53-ecspi",
                .driver_data = (kernel_ulong_t) &imx53_ecspi_devtype_data,
        }, {
                .name = "imx6ul-ecspi",
                .driver_data = (kernel_ulong_t) &imx6ul_ecspi_devtype_data,
        }, {
                /* sentinel */
        }
};

在这里我们重点看一下spi_imx_probe函数

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;
        int i, ret, irq, spi_drctl, num_cs;
        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;

        if (!np && !mxc_platform_info) {
                dev_err(&pdev->dev, "can't get the platform data\n");
                return -EINVAL;
        }

        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;

        ret = of_property_read_u32(np, "fsl,spi-rdy-drctl", &spi_drctl);
        if ((ret < 0) || (spi_drctl >= 0x3)) {
                /* '11' is reserved */
                spi_drctl = 0;
        }

        platform_set_drvdata(pdev, master);

        master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
        master->bus_num = np ? -1 : pdev->id;

        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_imx = spi_master_get_devdata(master);
        spi_imx->bitbang.master = master;
        spi_imx->dev = &pdev->dev;
        spi_imx->slave_mode = slave_mode;

        spi_imx->devtype_data = devtype_data;

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

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

                for (i = 0; i < master->num_chipselect; i++) {
                        int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
                        if (!gpio_is_valid(cs_gpio) && mxc_platform_info)
                                cs_gpio = mxc_platform_info->chipselect[i];

                        master->cs_gpios[i] = cs_gpio;
                        if (!gpio_is_valid(cs_gpio))
                                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_master_put;
                        }
                }
        }

        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 \
                                             | SPI_NO_CS;
        if (is_imx35_cspi(spi_imx) || is_imx51_ecspi(spi_imx) ||
            is_imx53_ecspi(spi_imx))
                spi_imx->bitbang.master->mode_bits |= SPI_LOOP | SPI_READY;

        spi_imx->spi_drctl = spi_drctl;

        init_completion(&spi_imx->xfer_done);

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        spi_imx->base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(spi_imx->base)) {
                ret = PTR_ERR(spi_imx->base);
                goto out_master_put;
        }
        spi_imx->base_phys = res->start;

        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                ret = irq;
                goto out_master_put;
        }

        ret = devm_request_irq(&pdev->dev, irq, spi_imx_isr, 0,
                               dev_name(&pdev->dev), spi_imx);
        if (ret) {
                dev_err(&pdev->dev, "can't get irq%d: %d\n", irq, ret);
                goto out_master_put;
        }

        spi_imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
        if (IS_ERR(spi_imx->clk_ipg)) {
                ret = PTR_ERR(spi_imx->clk_ipg);
                goto out_master_put;
        }

        spi_imx->clk_per = devm_clk_get(&pdev->dev, "per");
        if (IS_ERR(spi_imx->clk_per)) {
                ret = PTR_ERR(spi_imx->clk_per);
                goto out_master_put;
        }

        ret = clk_prepare_enable(spi_imx->clk_per);
        if (ret)
                goto out_master_put;

        ret = clk_prepare_enable(spi_imx->clk_ipg);
        if (ret)
                goto out_put_per;

        spi_imx->spi_clk = clk_get_rate(spi_imx->clk_per);
        /*
         * Only validated on i.mx35 and i.mx6 now, can remove the constraint
         * if validated on other chips.
         */
        if (spi_imx->devtype_data->has_dmamode) {
                ret = spi_imx_sdma_init(&pdev->dev, spi_imx, master);
                if (ret == -EPROBE_DEFER)
                        goto out_clk_put;

                if (ret < 0)
                        dev_err(&pdev->dev, "dma setup error %d, use pio\n",
                                ret);
        }

        spi_imx->devtype_data->reset(spi_imx);

        spi_imx->devtype_data->intctrl(spi_imx, 0);

        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;
        }

        dev_info(&pdev->dev, "probed\n");

        clk_disable_unprepare(spi_imx->clk_ipg);
        clk_disable_unprepare(spi_imx->clk_per);
        return ret;

out_clk_put:
        clk_disable_unprepare(spi_imx->clk_ipg);
out_put_per:
        clk_disable_unprepare(spi_imx->clk_per);
out_master_put:
        spi_master_put(master);

        return ret;
}

在这个probe里面比较重要的是*struct spi_master master,这个变量。这个是核心层识别的控制器对象。后面的工作大部分都围绕着它展开。
我们追踪下 函数spi_bitbang_start(&spi_imx->bitbang),其定义如下:

/*----------------------------------------------------------------------*/

/**
 * spi_bitbang_start - start up a polled/bitbanging SPI master driver
 * @bitbang: driver handle
 *
 * Caller should have zero-initialized all parts of the structure, and then
 * provided callbacks for chip selection and I/O loops.  If the master has
 * a transfer method, its final step should call spi_bitbang_transfer; or,
 * that's the default if the transfer routine is not initialized.  It should
 * also set up the bus number and number of chipselects.
 *
 * For i/o loops, provide callbacks either per-word (for bitbanging, or for
 * hardware that basically exposes a shift register) or per-spi_transfer
 * (which takes better advantage of hardware like fifos or DMA engines).
 *
 * Drivers using per-word I/O loops should use (or call) spi_bitbang_setup,
 * spi_bitbang_cleanup and spi_bitbang_setup_transfer to handle those spi
 * master methods.  Those methods are the defaults if the bitbang->txrx_bufs
 * routine isn't initialized.
 *
 * This routine registers the spi_master, which will process requests in a
 * dedicated task, keeping IRQs unblocked most of the time.  To stop
 * processing those requests, call spi_bitbang_stop().
 *
 * On success, this routine will take a reference to master. The caller is
 * responsible for calling spi_bitbang_stop() to decrement the reference and
 * spi_master_put() as counterpart of spi_alloc_master() to prevent a memory
 * leak.
 */
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;

        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
         */
        ret = spi_register_master(spi_master_get(master));
        if (ret)
                spi_master_put(master);

        return 0;
}
EXPORT_SYMBOL_GPL(spi_bitbang_start);

在spi_bitbang_start中会调用 spi_register_master(spi_master_get(master)),将master注册到核心层。下面我们说一下核心层。

2.核心层

承接上一节,下面先说一下核心层的spi_register_master函数,定义如下:

#define spi_register_master(_ctlr)      spi_register_controller(_ctlr)
....................................
/**
 * spi_register_controller - register SPI master or slave controller
 * @ctlr: initialized master, originally from spi_alloc_master() or
 *      spi_alloc_slave()
 * Context: can sleep
 *
 * SPI controllers connect to their drivers using some non-SPI bus,
 * such as the platform bus.  The final stage of probe() in that code
 * includes calling spi_register_controller() to hook up to this SPI bus glue.
 *
 * SPI controllers use board specific (often SOC specific) bus numbers,
 * and board-specific addressing for SPI devices combines those numbers
 * with chip select numbers.  Since SPI does not directly support dynamic
 * device identification, boards need configuration tables telling which
 * chip is at which address.
 *
 * This must be called from context that can sleep.  It returns zero on
 * success, else a negative error code (dropping the controller's refcount).
 * After a successful return, the caller is responsible for calling
 * spi_unregister_controller().
 *
 * Return: zero on success, else a negative error code.
 */
int spi_register_controller(struct spi_controller *ctlr)
{
        struct device           *dev = ctlr->dev.parent;
        struct boardinfo        *bi;
        int                     status = -ENODEV;
        int                     id, first_dynamic;

        if (!dev)
                return -ENODEV;

        if (!spi_controller_is_slave(ctlr)) {
                status = of_spi_register_master(ctlr);
                if (status)
                        return status;
        }

        /* 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;
        }
        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);
        if (!ctlr->max_dma_len)
                ctlr->max_dma_len = INT_MAX;

        /* register the device, then userspace will see it.
         * registration fails if the bus ID is in use.
         */
        dev_set_name(&ctlr->dev, "spi%u", ctlr->bus_num);
        status = device_add(&ctlr->dev);
        if (status < 0) {
                /* free bus id */
                mutex_lock(&board_lock);
                idr_remove(&spi_master_idr, ctlr->bus_num);
                mutex_unlock(&board_lock);
                goto done;
        }
        dev_dbg(dev, "registered %s %s\n",
                        spi_controller_is_slave(ctlr) ? "slave" : "master",
                        dev_name(&ctlr->dev));

        /* 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);
                if (status) {
                        device_del(&ctlr->dev);
                        /* free bus id */
                        mutex_lock(&board_lock);
                        idr_remove(&spi_master_idr, ctlr->bus_num);
                        mutex_unlock(&board_lock);
                        goto done;
                }
        }
        /* add statistics */
        spin_lock_init(&ctlr->statistics.lock);

        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);

        /* Register devices from the device tree and ACPI */
        of_register_spi_devices(ctlr);
        acpi_register_spi_devices(ctlr);
done:
        return status;
}
EXPORT_SYMBOL_GPL(spi_register_controller);

我们追踪一下函数spi_match_controller_to_boardinfo(ctlr, &bi->board_info),这个会将match的设备添加到总线上去。下面看其定义:


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);
}

继续追踪spi_new_device(ctlr, bi),定义如下:

/**
 * spi_new_device - instantiate one new SPI device
 * @ctlr: Controller to which device is connected
 * @chip: Describes the SPI device
 * Context: can sleep
 *
 * On typical mainboards, this is purely internal; and it's not needed
 * after board init creates the hard-wired devices.  Some development
 * platforms may not be able to use spi_register_board_info though, and
 * this is exported so that for example a USB or parport based adapter
 * driver could add devices (which it would learn about out-of-band).
 *
 * Return: the new device, or NULL.
 */
struct spi_device *spi_new_device(struct spi_controller *ctlr,
                                  struct spi_board_info *chip)
{
        struct spi_device       *proxy;
        int                     status;

        /* NOTE:  caller did any chip->bus_num checks necessary.
         *
         * Also, unless we change the return value convention to use
         * error-or-pointer (not NULL-or-pointer), troubleshootability
         * suggests syslogged diagnostics are best here (ugh).
         */

        proxy = spi_alloc_device(ctlr);
        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;

        if (chip->properties) {
                status = device_add_properties(&proxy->dev, chip->properties);
                if (status) {
                        dev_err(&ctlr->dev,
                                "failed to add properties to '%s': %d\n",
                                chip->modalias, status);
                        goto err_dev_put;
                }
        }

        status = spi_add_device(proxy);
        if (status < 0)
                goto err_remove_props;

        return proxy;

err_remove_props:
        if (chip->properties)
                device_remove_properties(&proxy->dev);
err_dev_put:
        spi_dev_put(proxy);
        return NULL;
}
EXPORT_SYMBOL_GPL(spi_new_device);

最终调用 spi_add_device()函数进行添加。

核心层的文件是:drivers/spi/spi.c
在这里我们看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;

        if (IS_ENABLED(CONFIG_SPI_SLAVE)) {
                status = class_register(&spi_slave_class);
                if (status < 0)
                        goto err3;
        }

        if (IS_ENABLED(CONFIG_OF_DYNAMIC))
                WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
        if (IS_ENABLED(CONFIG_ACPI))
                WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier));

        return 0;

err3:
        class_unregister(&spi_master_class);
err2:
        bus_unregister(&spi_bus_type);
err1:
        kfree(buf);
        buf = NULL;
err0:
        return status;
}

注意函数bus_register(&spi_bus_type),这个就是注册SPI总线。
spi_bus_type定义如下:

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);

还有函数class_register(&spi_master_class),这个是注册一个spi master 类,注册到核心层的spi控制器都属于这个class。核心层还有很多其他的函数,在这里我们就分析这么多,以后有时间在详解其他的。接下来我们说一下SPI设备驱动。

3. SPI设备驱动

SPI设备驱动我们以linux中提供的通用的为例。函数实现在文件:drivers/spi/spidev.c
我们先来看一下init函数,定义如下:

static int __init spidev_init(void)
{
        int status;

        /* Claim our 256 reserved device numbers.  Then register a class
         * that will key udev/mdev to add/remove /dev nodes.  Last, register
         * the driver which manages those device numbers.
         */
        BUILD_BUG_ON(N_SPI_MINORS > 256);
        status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
        if (status < 0)
                return status;

        spidev_class = class_create(THIS_MODULE, "spidev");
        if (IS_ERR(spidev_class)) {
                unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
                return PTR_ERR(spidev_class);
        }

        status = spi_register_driver(&spidev_spi_driver);
        if (status < 0) {
                class_destroy(spidev_class);
                unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
        }
        return status;
}
module_init(spidev_init);

static void __exit spidev_exit(void)
{
        spi_unregister_driver(&spidev_spi_driver);
        class_destroy(spidev_class);
        unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
module_exit(spidev_exit);

spidev_spi_driver定义如下:

static struct spi_driver spidev_spi_driver = {
        .driver = {
                .name =         "spidev",
                .of_match_table = of_match_ptr(spidev_dt_ids),
                .acpi_match_table = ACPI_PTR(spidev_acpi_ids),
        },
        .probe =        spidev_probe,
        .remove =       spidev_remove,

        /* NOTE:  suspend/resume methods are not necessary here.
         * We don't do anything except pass the requests to/from
         * the underlying controller.  The refrigerator handles
         * most issues; the controller driver handles the rest.
         */
};

spidev_fops 定义如下:

static const struct file_operations spidev_fops = {
        .owner =        THIS_MODULE,
        /* REVISIT switch to aio primitives, so that userspace
         * gets more complete API coverage.  It'll simplify things
         * too, except for the locking.
         */
        .write =        spidev_write,
        .read =         spidev_read,
        .unlocked_ioctl = spidev_ioctl,
        .compat_ioctl = spidev_compat_ioctl,
        .open =         spidev_open,
        .release =      spidev_release,
        .llseek =       no_llseek,
};

spidev_init()中,注册字符设备,并且在核心层注册SPI设备驱动。
我们再来看probe函数

static int spidev_probe(struct spi_device *spi)
{
        struct spidev_data      *spidev;
        int                     status;
        unsigned long           minor;

        /*
         * spidev should never be referenced in DT without a specific
         * compatible string, it is a Linux implementation thing
         * rather than a description of the hardware.
         */
        if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) {
                dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n");
                WARN_ON(spi->dev.of_node &&
                        !of_match_device(spidev_dt_ids, &spi->dev));
        }

        spidev_probe_acpi(spi);

        /* Allocate driver data */
        spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
        if (!spidev)
                return -ENOMEM;

        /* Initialize the driver data */
        spidev->spi = spi;
        spin_lock_init(&spidev->spi_lock);
        mutex_init(&spidev->buf_lock);

        INIT_LIST_HEAD(&spidev->device_entry);

        /* If we can allocate a minor number, hook up this device.
         * Reusing minors is fine so long as udev or mdev is working.
         */
        mutex_lock(&device_list_lock);
        minor = find_first_zero_bit(minors, N_SPI_MINORS);
        if (minor < N_SPI_MINORS) {
                struct device *dev;

                spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
                dev = device_create(spidev_class, &spi->dev, spidev->devt,
                                    spidev, "spidev%d.%d",
                                    spi->master->bus_num, spi->chip_select);
                status = PTR_ERR_OR_ZERO(dev);
        } else {
                dev_dbg(&spi->dev, "no minor number available!\n");
                status = -ENODEV;
        }
        if (status == 0) {
                set_bit(minor, minors);
                list_add(&spidev->device_entry, &device_list);
        }
        mutex_unlock(&device_list_lock);

        spidev->speed_hz = spi->max_speed_hz;

        if (status == 0)
                spi_set_drvdata(spi, spidev);
        else
                kfree(spidev);

        return status;
}

其中结构体struct spidev_data 定义如下:

struct spidev_data {
        dev_t                   devt;
        spinlock_t              spi_lock;
        struct spi_device       *spi;
        struct list_head        device_entry;

        /* TX/RX buffers are NULL unless this device is open (users > 0) */
        struct mutex            buf_lock;
        unsigned                users;
        u8                      *tx_buffer;
        u8                      *rx_buffer;
        u32                     speed_hz;
};

注意list_head变量,所有使用这个驱动的设备都会被做成一个链表。其中
dev = device_create(spidev_class, &spi->dev, spidev->devt,
spidev, “spidev%d.%d”,
spi->master->bus_num, spi->chip_select);
创建设备,并生成设备节点,设备节点在/dev目录下。
到此为止设备驱动也分析完了。

参考文章:
https://www.cnblogs.com/lknlfy/p/3265019.html

你可能感兴趣的:(Linux驱动)