SPI驱动分析之二

三、主控制器的驱动注册

文件spi_imx.c中

subsys_initcall(spi_imx_init);

static int __init spi_imx_init(void)
{
    return platform_driver_register(&spi_imx_driver);
}

static struct platform_driver spi_imx_driver = {
    .driver = {
           .name = DRIVER_NAME,
           .owner = THIS_MODULE,
           },
    .id_table = spi_imx_devtype,
    .probe = spi_imx_probe,
    .remove = __devexit_p(spi_imx_remove),
};


static int __devinit spi_imx_probe(struct platform_device *pdev)
{
    struct spi_imx_master *mxc_platform_info;
    struct spi_master *master;
    struct spi_imx_data *spi_imx;
    struct resource *res;
    int i, ret;

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

    master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data));
    if (!master)
        return -ENOMEM;

    platform_set_drvdata(pdev, master);

    master->bus_num = pdev->id;
    master->num_chipselect = mxc_platform_info->num_chipselect;

    spi_imx = spi_master_get_devdata(master);
    spi_imx->bitbang.master = spi_master_get(master);
    spi_imx->chipselect = mxc_platform_info->chipselect;

    for (i = 0; i < master->num_chipselect; i++) {
        if (spi_imx->chipselect[i] < 0)
            continue;
        ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME);
        if (ret) {
            while (i > 0) {
                i--;
                if (spi_imx->chipselect[i] >= 0)
                    gpio_free(spi_imx->chipselect[i]);
            }
            dev_err(&pdev->dev, "can't get cs gpios\n");
            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->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;

    init_completion(&spi_imx->xfer_done);

    spi_imx->devtype_data =
        spi_imx_devtype_data[pdev->id_entry->driver_data];

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(&pdev->dev, "can't get platform resource\n");
        ret = -ENOMEM;
        goto out_gpio_free;
    }

    if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
        dev_err(&pdev->dev, "request_mem_region failed\n");
        ret = -EBUSY;
        goto out_gpio_free;
    }

    spi_imx->base = ioremap(res->start, resource_size(res));
    if (!spi_imx->base) {
        ret = -EINVAL;
        goto out_release_mem;
    }

    spi_imx->irq = platform_get_irq(pdev, 0);
    if (spi_imx->irq < 0) {
        ret = -EINVAL;
        goto out_iounmap;
    }

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

    spi_imx->clk = clk_get(&pdev->dev, NULL);
    if (IS_ERR(spi_imx->clk)) {
        dev_err(&pdev->dev, "unable to get clock\n");
        ret = PTR_ERR(spi_imx->clk);
        goto out_free_irq;
    }

    clk_enable(spi_imx->clk);
    spi_imx->spi_clk = clk_get_rate(spi_imx->clk);

    spi_imx->devtype_data.reset(spi_imx);

    spi_imx->devtype_data.intctrl(spi_imx, 0);
    ret = spi_bitbang_start(&spi_imx->bitbang);
    if (ret) {
        dev_err(&pdev->dev, "bitbang start failed with %d\n", ret);
        goto out_clk_put;
    }
    clk_disable(spi_imx->clk);

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

    return ret;

out_clk_put:
    clk_disable(spi_imx->clk);
    clk_put(spi_imx->clk);
out_free_irq:
    free_irq(spi_imx->irq, spi_imx);
out_iounmap:
    iounmap(spi_imx->base);
out_release_mem:
    release_mem_region(res->start, resource_size(res));
out_gpio_free:
    for (i = 0; i < master->num_chipselect; i++)
        if (spi_imx->chipselect[i] >= 0)
            gpio_free(spi_imx->chipselect[i]);
out_master_put:
    spi_master_put(master);
    kfree(master);
    platform_set_drvdata(pdev, NULL);
    return ret;
}

spi_bitbang.c文件中

int spi_bitbang_start(struct spi_bitbang *bitbang)
{
    int    status;

    if (!bitbang->master || !bitbang->chipselect)
        return -EINVAL;

    INIT_WORK(&bitbang->work, bitbang_work);
    spin_lock_init(&bitbang->lock);
    INIT_LIST_HEAD(&bitbang->queue);

    if (!bitbang->master->mode_bits)
        bitbang->master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;

    if (!bitbang->master->transfer)
        bitbang->master->transfer = spi_bitbang_transfer;
    if (!bitbang->txrx_bufs) {
        bitbang->use_dma = 0;
        bitbang->txrx_bufs = spi_bitbang_bufs;
        if (!bitbang->master->setup) {
            if (!bitbang->setup_transfer)
                bitbang->setup_transfer =
                     spi_bitbang_setup_transfer;
            bitbang->master->setup = spi_bitbang_setup;
            bitbang->master->cleanup = spi_bitbang_cleanup;
        }
    } else if (!bitbang->master->setup)
        return -EINVAL;
    if (bitbang->master->transfer == spi_bitbang_transfer &&
            !bitbang->setup_transfer)
        return -EINVAL;

    /* this task is the only thing to touch the SPI bits */
    bitbang->busy = 0;
    bitbang->workqueue = create_singlethread_workqueue(
            dev_name(bitbang->master->dev.parent));
    if (bitbang->workqueue == NULL) {
        status = -EBUSY;
        goto err1;
    }

    /* driver may get busy before register() returns, especially
     * if someone registered boardinfo for devices
     */
    status = spi_register_master(bitbang->master);
    if (status < 0)
        goto err2;

    return status;

err2:
    destroy_workqueue(bitbang->workqueue);
err1:
    return status;
}
EXPORT_SYMBOL_GPL(spi_bitbang_start);

spi_bitbang.c文件中

/**
 * spi_bitbang_transfer - default submit to transfer queue
 */
int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)
{
    struct spi_bitbang    *bitbang;
    unsigned long        flags;
    int            status = 0;

    m->actual_length = 0;
    m->status = -EINPROGRESS;

    bitbang = spi_master_get_devdata(spi->master);

    spin_lock_irqsave(&bitbang->lock, flags);
    if (!spi->max_speed_hz)
        status = -ENETDOWN;
    else {
        list_add_tail(&m->queue, &bitbang->queue);
        queue_work(bitbang->workqueue, &bitbang->work);
    }
    spin_unlock_irqrestore(&bitbang->lock, flags);

    return status;
}
EXPORT_SYMBOL_GPL(spi_bitbang_transfer);

spi.c文件中

/**
 * spi_register_master - register SPI master controller
 * @master: initialized master, originally from spi_alloc_master()
 * Context: can sleep
 *
 * SPI master 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_master() 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 master's refcount).
 * After a successful return, the caller is responsible for calling
 * spi_unregister_master().
 */
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;

    /* even if it's just one always-selected device, there must
     * be at least one chipselect
     */
    if (master->num_chipselect == 0)
        return -EINVAL;

    /* convention:  dynamically assigned bus IDs count down from the max */
    if (master->bus_num < 0) {
        /* FIXME switch to an IDR based scheme, something like
         * I2C now uses, so we can't run out of "dynamic" IDs
         */
        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;

    /* register the device, then userspace will see it.
     * registration fails if the bus ID is in use.
     */
    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;

    /* Register devices from the device tree */
    of_register_spi_devices(master);
done:
    return status;
}
EXPORT_SYMBOL_GPL(spi_register_master);

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_new_device - instantiate one new SPI device
 * @master: 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).
 *
 * Returns the new device, or NULL.
 */
struct spi_device *spi_new_device(struct spi_master *master,
                  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(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);

/**
 * spi_alloc_device - Allocate a new SPI device
 * @master: Controller to which device is connected
 * Context: can sleep
 *
 * Allows a driver to allocate and initialize a spi_device without
 * registering it immediately.  This allows a driver to directly
 * fill the spi_device with device parameters before calling
 * spi_add_device() on it.
 *
 * Caller is responsible to call spi_add_device() on the returned
 * spi_device structure to add it to the SPI master.  If the caller
 * needs to discard the spi_device without adding it, then it should
 * call spi_dev_put() on it.
 *
 * Returns a pointer to the new device, or NULL.
 */
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 = &master->dev;
    spi->dev.bus = &spi_bus_type;
    spi->dev.release = spidev_release;
    device_initialize(&spi->dev);
    return spi;
}
EXPORT_SYMBOL_GPL(spi_alloc_device);

/**
 * spi_add_device - Add spi_device allocated with spi_alloc_device
 * @spi: spi_device to register
 *
 * Companion function to spi_alloc_device.  Devices allocated with
 * spi_alloc_device can be added onto the spi bus with this function.
 *
 * Returns 0 on success; negative errno on failure
 */
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;

    /* Chipselects are numbered 0..max; validate. */
    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;
    }

    /* Set the bus ID string */
    dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
            spi->chip_select);


    /* We need to make sure there's no other device with this
     * chipselect **BEFORE** we call setup(), else we'll trash
     * its configuration.  Lock against concurrent add() calls.
     */
    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;
    }

    /* Drivers may modify this initial i/o setup, but will
     * normally rely on the device being setup.  Devices
     * using SPI_CS_HIGH can't coexist well otherwise...
     */
    status = spi_setup(spi);
    if (status < 0) {
        dev_err(dev, "can't setup %s, status %d\n",
                dev_name(&spi->dev), status);
        goto done;
    }

    /* Device may be bound to an active driver when this returns */
    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);

/* Core methods for SPI master protocol drivers.  Some of the
 * other core methods are currently defined as inline functions.
 */

/**
 * spi_setup - setup SPI mode and clock rate
 * @spi: the device whose settings are being modified
 * Context: can sleep, and no requests are queued to the device
 *
 * SPI protocol drivers may need to update the transfer mode if the
 * device doesn't work with its default.  They may likewise need
 * to update clock rates or word sizes from initial values.  This function
 * changes those settings, and must be called from a context that can sleep.
 * Except for SPI_CS_HIGH, which takes effect immediately, the changes take
 * effect the next time the device is selected and data is transferred to
 * or from it.  When this function returns, the spi device is deselected.
 *
 * Note that this call will fail if the protocol driver specifies an option
 * that the underlying controller or its driver does not support.  For
 * example, not all hardware supports wire transfers using nine bit words,
 * LSB-first wire encoding, or active-high chipselects.
 */
int spi_setup(struct spi_device *spi)
{
    unsigned    bad_bits;
    int        status;

    /* help drivers fail *cleanly* when they need options
     * that aren't supported with their current master
     */
    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);

  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值