linux SPI驅動框架(一)---控制器驅動

轉載請標明出處floater的csdn blog,http://blog.csdn.net/flaoter

Linux SPI驅動分爲核心層,控制器驅動層和設備驅動層。核心層是Linux的SPI核心部分,提供了核心數據結構的定義,總線、設備和驅動的註冊、註銷管理等,提供與上層的統一接口。linux將I2C、SPI、USB等總線驅動隔離成控制器驅動和設備驅動,使兩者相對獨立。
本文以qcom的spi控制器爲例,對spi控制器驅動進行解析。kernel代碼版本是3.18。

1 控制器設備註冊

控制器的設備註冊在kernel啓動階段的dt解析過程中完成,註冊控制器設備爲platform_device。dts中描述如下:

spi_6: spi@757a000 { /* BLSP1 QUP6 */
        compatible = "qcom,spi-qup-v2";
        #address-cells = <1>;
        #size-cells = <0>;
        reg-names = "spi_physical", "spi_bam_physical";
        reg = <0x0757a000 0x600>,
              <0x07544000 0x2b000>;
        interrupt-names = "spi_irq", "spi_bam_irq";
        interrupts = <0 100 0>, <0 238 0>;
        spi-max-frequency = <19200000>;

        qcom,infinite-mode = <0>;
        qcom,rt-priority;
        qcom,use-bam;
        qcom,ver-reg-exists;
        qcom,bam-consumer-pipe-index = <22>;
        qcom,bam-producer-pipe-index = <23>;
        qcom,master-id = <86>;
        qcom,use-pinctrl;
        pinctrl-names = "spi_default", "spi_sleep";
        pinctrl-0 = <&spi_6_active>;
        pinctrl-1 = <&spi_6_sleep>;

        clock-names = "iface_clk", "core_clk";

        clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
                 <&clock_gcc clk_gcc_blsp1_qup6_spi_apps_clk>;
    };

2 控制器驅動註冊

控制器設計者一般都會自定義一個數據結構用於描述控制器基本信息,不同設計廠商的數據結構成員不同,但肯定與自身控制器的使用密切相關的內容,比如內存基地址,clock,中斷號,端口fifo大小等。linux驅動與設備是一對多的關係,在spi_master設備註冊時,控制器的結構體信息會提供給spi_master作爲私有數據。

struct spi_qup {
    void __iomem        *base;   //controller基址,操作controller寄存器時使用
    struct device       *dev;
    struct clk      *cclk;  /* core clock */  
    struct clk      *iclk;  /* interface clock */
    int         irq;
    spinlock_t      lock;

    int         in_fifo_sz;  
    int         out_fifo_sz;
    int         in_blk_sz;
    int         out_blk_sz;

    struct spi_transfer *xfer;
    struct completion   done;
    int         error;
    int         w_size; /* bytes per SPI word */
    int         tx_bytes;
    int         rx_bytes;
    int         qup_v1;
};

除了platform_device的控制器設備註冊外,spi控制器還需要註冊spi_bus_type的spi_master設備作爲spi master,此數據接口是控制器驅動的核心數據結構。

struct spi_master {
    struct device   dev;

    struct list_head list;

    /* other than negative (== assign one dynamically), bus_num is fully
     * board-specific.  usually that simplifies to being SOC-specific.
     * example:  one SOC has three SPI controllers, numbered 0..2,
     * and one board's schematics might show it using SPI-2.  software
     * would normally use bus_num=2 for that controller.
     */
    s16         bus_num;

    /* chipselects will be integral to many controllers; some others
     * might use board-specific GPIOs.
     */
    u16         num_chipselect;

    /* some SPI controllers pose alignment requirements on DMAable
     * buffers; let protocol drivers know about these requirements.
     */
    u16         dma_alignment;

    /* spi_device.mode flags understood by this controller driver */
    u16         mode_bits;

    /* bitmask of supported bits_per_word for transfers */
    u32         bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))

    /* limits on transfer speed */
    u32         min_speed_hz;
    u32         max_speed_hz;

    /* other constraints relevant to this driver */
    u16         flags;
#define SPI_MASTER_HALF_DUPLEX  BIT(0)      /* can't do full duplex */
#define SPI_MASTER_NO_RX    BIT(1)      /* can't do buffer read */
#define SPI_MASTER_NO_TX    BIT(2)      /* can't do buffer write */
#define SPI_MASTER_MUST_RX      BIT(3)      /* requires rx */
#define SPI_MASTER_MUST_TX      BIT(4)      /* requires tx */

    /* lock and mutex for SPI bus locking */
    spinlock_t      bus_lock_spinlock;
    struct mutex        bus_lock_mutex;

    /* flag indicating that the SPI bus is locked for exclusive use */
    bool            bus_lock_flag;

    /* Setup mode and clock, etc (spi driver may call many times).
     *
     * IMPORTANT:  this may be called when transfers to another
     * device are active.  DO NOT UPDATE SHARED REGISTERS in ways
     * which could break those transfers.
     */
    int         (*setup)(struct spi_device *spi);

    /* bidirectional bulk transfers
     *
     * + The transfer() method may not sleep; its main role is
     *   just to add the message to the queue.
     * + For now there's no remove-from-queue operation, or
     *   any other request management
     * + To a given spi_device, message queueing is pure fifo
     *
     * + The master's main job is to process its message queue,
     *   selecting a chip then transferring data
     * + If there are multiple spi_device children, the i/o queue
     *   arbitration algorithm is unspecified (round robin, fifo,
     *   priority, reservations, preemption, etc)
     *
     * + Chipselect stays active during the entire message
     *   (unless modified by spi_transfer.cs_change != 0).
     * + The message transfers use clock and SPI mode parameters
     *   previously established by setup() for this device
     */
    int         (*transfer)(struct spi_device *spi,
                        struct spi_message *mesg);

    /* called on release() to free memory provided by spi_master */
    void            (*cleanup)(struct spi_device *spi);

    /*
     * Used to enable core support for DMA handling, if can_dma()
     * exists and returns true then the transfer will be mapped
     * prior to transfer_one() being called.  The driver should
     * not modify or store xfer and dma_tx and dma_rx must be set
     * while the device is prepared.
     */
    bool            (*can_dma)(struct spi_master *master,
                       struct spi_device *spi,
                       struct spi_transfer *xfer);

    /*
     * These hooks are for drivers that want to use the generic
     * master transfer queueing mechanism. If these are used, the
     * transfer() function above must NOT be specified by the driver.
     * Over time we expect SPI drivers to be phased over to this API.
     */
    bool                queued;
    struct kthread_worker       kworker;
    struct task_struct      *kworker_task;
    struct kthread_work     pump_messages;
    spinlock_t          queue_lock;
    struct list_head        queue;
    struct spi_message      *cur_msg;
    bool                busy;
    bool                running;
    bool                rt;
    bool                auto_runtime_pm;
    bool                            cur_msg_prepared;
    bool                cur_msg_mapped;
    struct completion               xfer_completion;
    size_t              max_dma_len;

    int (*prepare_transfer_hardware)(struct spi_master *master);
    int (*transfer_one_message)(struct spi_master *master,
                    struct spi_message *mesg);
    int (*unprepare_transfer_hardware)(struct spi_master *master);
    int (*prepare_message)(struct spi_master *master,
                   struct spi_message *message);
    int (*unprepare_message)(struct spi_master *master,
                 struct spi_message *message);

    /*
     * These hooks are for drivers that use a generic implementation
     * of transfer_one_message() provied by the core.
     */
    void (*set_cs)(struct spi_device *spi, bool enable);
    int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
                struct spi_transfer *transfer);

    /* gpio chip select */
    int         *cs_gpios;

    /* DMA channels for use with core dmaengine helpers */
    struct dma_chan     *dma_tx;
    struct dma_chan     *dma_rx;

    /* dummy data for full duplex devices */
    void            *dummy_rx;
    void            *dummy_tx;
};

spi控制器是platform_device,註冊的驅動是plaform_driver。它的probe依賴於dts中設備的compatible屬性與驅動中of_device_id的比對結果,一致的情況下,probe會被加載執行。
spi_qup.c

static const struct of_device_id spi_qup_dt_match[] = {
    { .compatible = "qcom,spi-qup-v1.1.1", },
    { .compatible = "qcom,spi-qup-v2.1.1", },
    { .compatible = "qcom,spi-qup-v2.2.1", },
    { }
};
MODULE_DEVICE_TABLE(of, spi_qup_dt_match);

static struct platform_driver spi_qup_driver = {
    .driver = {
        .name       = "spi_qup",
        .owner      = THIS_MODULE,
        .pm     = &spi_qup_dev_pm_ops,
        .of_match_table = spi_qup_dt_match,
    },
    .probe = spi_qup_probe,
    .remove = spi_qup_remove,
};
module_platform_driver(spi_qup_driver);

probe函數的工作大體包括,
1. 解析dts,對控制器結構體spi_qup填充,設置控制器工作狀態,註冊中斷等與廠商控制器自身相關
2. spi_master結構體填充,註冊spi_master設備
spi_qup.c

static int spi_qup_probe(struct platform_device *pdev)
{
    struct spi_master *master;
    struct clk *iclk, *cclk;
    struct spi_qup *controller;
    struct resource *res;
    struct device *dev;
    void __iomem *base;
    u32 max_freq, iomode, num_cs;
    int ret, irq, size;

    dev = &pdev->dev;
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    base = devm_ioremap_resource(dev, res);
    if (IS_ERR(base))
        return PTR_ERR(base);

    irq = platform_get_irq(pdev, 0);
    if (irq < 0)
        return irq;

    cclk = devm_clk_get(dev, "core");
    if (IS_ERR(cclk))
        return PTR_ERR(cclk);

    iclk = devm_clk_get(dev, "iface");
    if (IS_ERR(iclk))
        return PTR_ERR(iclk);

    /* This is optional parameter */
    if (of_property_read_u32(dev->of_node, "spi-max-frequency", &max_freq))
        max_freq = SPI_MAX_RATE;

    if (!max_freq || max_freq > SPI_MAX_RATE) {
        dev_err(dev, "invalid clock frequency %d\n", max_freq);
        return -ENXIO;
    }

    ret = clk_prepare_enable(cclk);
    if (ret) {
        dev_err(dev, "cannot enable core clock\n");
        return ret;
    }

    ret = clk_prepare_enable(iclk);
    if (ret) {
        clk_disable_unprepare(cclk);
        dev_err(dev, "cannot enable iface clock\n");
        return ret;
    }

    master = spi_alloc_master(dev, sizeof(struct spi_qup));
    if (!master) {
        clk_disable_unprepare(cclk);
        clk_disable_unprepare(iclk);
        dev_err(dev, "cannot allocate master\n");
        return -ENOMEM;
    }

    /* use num-cs unless not present or out of range */
    if (of_property_read_u32(dev->of_node, "num-cs", &num_cs) ||
        num_cs > SPI_NUM_CHIPSELECTS)
        master->num_chipselect = SPI_NUM_CHIPSELECTS;
    else
        master->num_chipselect = num_cs;

    master->bus_num = pdev->id;
    master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
    master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
    master->max_speed_hz = max_freq;
    master->transfer_one = spi_qup_transfer_one;
    master->dev.of_node = pdev->dev.of_node;
    master->auto_runtime_pm = true;

    platform_set_drvdata(pdev, master);  //pdev->dev->dradata = master

    controller = spi_master_get_devdata(master);   //master->dev->drvdata = controller

    controller->dev = dev;
    controller->base = base;
    controller->iclk = iclk;
    controller->cclk = cclk;
    controller->irq = irq;

    /* set v1 flag if device is version 1 */
    if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1"))
        controller->qup_v1 = 1;

    spin_lock_init(&controller->lock);
    init_completion(&controller->done);

    iomode = readl_relaxed(base + QUP_IO_M_MODES);

    size = QUP_IO_M_OUTPUT_BLOCK_SIZE(iomode);
    if (size)
        controller->out_blk_sz = size * 16;
    else
        controller->out_blk_sz = 4;

    size = QUP_IO_M_INPUT_BLOCK_SIZE(iomode);
    if (size)
        controller->in_blk_sz = size * 16;
    else
        controller->in_blk_sz = 4;

    size = QUP_IO_M_OUTPUT_FIFO_SIZE(iomode);
    controller->out_fifo_sz = controller->out_blk_sz * (2 << size);

    size = QUP_IO_M_INPUT_FIFO_SIZE(iomode);
    controller->in_fifo_sz = controller->in_blk_sz * (2 << size);

    dev_info(dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
         controller->in_blk_sz, controller->in_fifo_sz,
         controller->out_blk_sz, controller->out_fifo_sz);

    writel_relaxed(1, base + QUP_SW_RESET);

    ret = spi_qup_set_state(controller, QUP_STATE_RESET);
    if (ret) {
        dev_err(dev, "cannot set RESET state\n");
        goto error;
    }

    writel_relaxed(0, base + QUP_OPERATIONAL);
    writel_relaxed(0, base + QUP_IO_M_MODES);

    if (!controller->qup_v1)
        writel_relaxed(0, base + QUP_OPERATIONAL_MASK);

    writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN,
               base + SPI_ERROR_FLAGS_EN);

    /* if earlier version of the QUP, disable INPUT_OVERRUN */
    if (controller->qup_v1)
        writel_relaxed(QUP_ERROR_OUTPUT_OVER_RUN |
            QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN,
            base + QUP_ERROR_FLAGS_EN);

    writel_relaxed(0, base + SPI_CONFIG);
    writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);

    ret = devm_request_irq(dev, irq, spi_qup_qup_irq,
                   IRQF_TRIGGER_HIGH, pdev->name, controller);
    if (ret)
        goto error;

    pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
    pm_runtime_use_autosuspend(dev);
    pm_runtime_set_active(dev);
    pm_runtime_enable(dev);

    ret = devm_spi_register_master(dev, master);   //spi_register_master, 註冊spi_master設備
    if (ret)
        goto disable_pm;

    return 0;

disable_pm:
    pm_runtime_disable(&pdev->dev);
error:
    clk_disable_unprepare(cclk);
    clk_disable_unprepare(iclk);
    spi_master_put(master);
    return ret;
}

spi_master註冊函數展開如下:
spi.c

int devm_spi_register_master(struct device *dev, struct spi_master *master)
{
    struct spi_master **ptr;
    int ret;

    ptr = devres_alloc(devm_spi_unregister, sizeof(*ptr), GFP_KERNEL);
    if (!ptr)
        return -ENOMEM;

    ret = spi_register_master(master);
    if (!ret) {
        *ptr = master;
        devres_add(dev, ptr);
    } else {
        devres_free(ptr);
    }

    return ret;
}

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;

    status = of_spi_register_master(master);
    if (status)
        return status;

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

    if ((master->bus_num < 0) && master->dev.of_node)
        master->bus_num = of_alias_get_id(master->dev.of_node, "spi");

    /* 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;
    init_completion(&master->xfer_completion);
    if (!master->max_dma_len)
        master->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(&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)" : "");

    /* If we're using a queued driver, start the queue */
    if (master->transfer)
        dev_info(dev, "master is unqueued, this is deprecated\n");
    else {
        status = spi_master_initialize_queue(master);  //隊列,工作線程初始化   
        if (status) {
            device_del(&master->dev);
            goto done;
        }
    }

    mutex_lock(&board_lock);
    list_add_tail(&master->list, &spi_master_list);   //將spi_master加入spi_master_list
    list_for_each_entry(bi, &board_list, list)
        spi_match_master_to_boardinfo(master, &bi->board_info);  //掃描board_list,對已經在list中的此master對應的slave設備進行註冊
    mutex_unlock(&board_lock);

    /* Register devices from the device tree and ACPI */
    of_register_spi_devices(master);   //對dt中聲明的此master對應的slave設備進行註冊
    acpi_register_spi_devices(master);
done:
    return status;
}

至此,通過device_add函數,spi_master註冊到內核。

3 總線傳輸

spi的總線傳輸分爲同步傳輸和異步傳輸,同步傳輸較簡單適合有少量數據的傳輸,異步傳輸較複雜,適合對大量數據的傳輸。本節對異步傳輸進行解釋。總線傳輸涉及到幾個重要的結構體,隊列,內核工作線程和廠商的總線傳輸實現幾個方面。

3.1 數據結構

transfer使用的幾個鏈表數據結構的關係如下,它們是spi_mater, spi_message和spi_transfer。
spi_struct

3.2 隊列&內核工作線程

spi的總線傳輸是以spi_master->transfer進行實際的傳輸的。在實際傳輸之前要對結構體,隊列,工作線程進行初始化。它們的初始化是在spi_master註冊函數spi_register_master中調用 spi_master_initialize_queue實現的。spi_master_initialize_queue 實現spi_master成員函數transfer, transfer_one_message,queue和內核工作線程初始化。
spi.c

static int spi_master_initialize_queue(struct spi_master *master)
{
    int ret;

    master->transfer = spi_queued_transfer;           //msater->transfer初始化
    if (!master->transfer_one_message)
        master->transfer_one_message = spi_transfer_one_message;   //master->transfer_one_message初始化

    /* Initialize and start queue */
    ret = spi_init_queue(master);   //master->queue和master->kworker初始化
    if (ret) {
        dev_err(&master->dev, "problem initializing queue\n");
        goto err_init_queue;
    }
    master->queued = true;
    ret = spi_start_queue(master);
    if (ret) {
        dev_err(&master->dev, "problem starting queue\n");
        goto err_start_queue;
    }

    return 0;
}

transfer函數只是將上層傳來的spi_message掛到一個鏈表上,然後就返回了,發送的調度是以kthread_worker和kthread_work機制實現的,通過創建一個內核線程來執行kthread_work的func。spi_init_queue實現了隊列和工作線程的初始化。
spi.c

static int spi_init_queue(struct spi_master *master)
{
    INIT_LIST_HEAD(&master->queue);
    spin_lock_init(&master->queue_lock);
        ...
    init_kthread_worker(&master->kworker);
    master->kworker_task = kthread_run(kthread_worker_fn,
                       &master->kworker, "%s",
                       dev_name(&master->dev));
    if (IS_ERR(master->kworker_task)) {
        dev_err(&master->dev, "failed to create message pump task\n");
        return -ENOMEM;
    }
    init_kthread_work(&master->pump_messages, spi_pump_messages);
}

transfer函數進行了入隊操作,實現如下:
spi.c

static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)
{
...
    list_add_tail(&msg->queue, &master->queue);
    if (!master->busy)
        queue_kthread_work(&master->kworker, &master->pump_messages);
...
}

kthread_worker中真正的func函數spi_pump_message實現了出隊操作:
spi.c

static void spi_pump_messages(struct kthread_work *work)
{
    struct spi_master *master =
        container_of(work, struct spi_master, pump_messages);
...
    /* Extract head of queue */
    master->cur_msg =
        list_first_entry(&master->queue, struct spi_message, queue);    //從隊列頭取得spi_message

    list_del_init(&master->cur_msg->queue);
...

    if (!was_busy && master->prepare_transfer_hardware) {        //未實現,pass
        ret = master->prepare_transfer_hardware(master);
        if (ret) {
            dev_err(&master->dev,
                "failed to prepare transfer hardware\n");

            if (master->auto_runtime_pm)
                pm_runtime_put(master->dev.parent);
            return;
        }
    }

    trace_spi_message_start(master->cur_msg);

    if (master->prepare_message) {   //未實現,pass
        ret = master->prepare_message(master, master->cur_msg);
        if (ret) {
            dev_err(&master->dev,
                "failed to prepare message: %d\n", ret);
            master->cur_msg->status = ret;
            spi_finalize_current_message(master);
            return;
        }
        master->cur_msg_prepared = true;
    }

    ret = spi_map_msg(master, master->cur_msg);  //未實現,pass
    if (ret) {
        master->cur_msg->status = ret;
        spi_finalize_current_message(master);
        return;
    }

    ret = master->transfer_one_message(master, master->cur_msg);  
    if (ret) {
        dev_err(&master->dev,
            "failed to transfer one message from queue\n");
        return;
    }
}

transfer_one_message實現如下,掃描msg->transfers爲列表頭的隊列,調用master->transfer_one進行發送。transfer_one_message的發送數據結構是struct spi_message,transfer的發送數據結構是spi_transfer。
spi.c

static int spi_transfer_one_message(struct spi_master *master,
                    struct spi_message *msg)
{
    struct spi_transfer *xfer;
    bool keep_cs = false;
    int ret = 0;
    int ms = 1;

    spi_set_cs(msg->spi, true);

    list_for_each_entry(xfer, &msg->transfers, transfer_list) {
        trace_spi_transfer_start(msg, xfer);

        if (xfer->tx_buf || xfer->rx_buf) {
            reinit_completion(&master->xfer_completion);

            ret = master->transfer_one(master, msg->spi, xfer);   
            if (ret < 0) {
                dev_err(&msg->spi->dev,
                    "SPI transfer failed: %d\n", ret);
                goto out;
            }

            if (ret > 0) {
                ret = 0;
                ms = xfer->len * 8 * 1000 / xfer->speed_hz;
                ms += ms + 100; /* some tolerance */

                ms = wait_for_completion_timeout(&master->xfer_completion,
                                 msecs_to_jiffies(ms));
            }

            if (ms == 0) {
                dev_err(&msg->spi->dev,
                    "SPI transfer timed out\n");
                msg->status = -ETIMEDOUT;
            }
        } else {
            if (xfer->len)
                dev_err(&msg->spi->dev,
                    "Bufferless transfer has length %u\n",
                    xfer->len);
        }

        trace_spi_transfer_stop(msg, xfer);

        if (msg->status != -EINPROGRESS)
            goto out;

        if (xfer->delay_usecs)
            udelay(xfer->delay_usecs);

        if (xfer->cs_change) {
            if (list_is_last(&xfer->transfer_list,
                     &msg->transfers)) {
                keep_cs = true;
            } else {
                spi_set_cs(msg->spi, false);
                udelay(10);
                spi_set_cs(msg->spi, true);
            }
        }

        msg->actual_length += xfer->len;
    }

out:
    if (ret != 0 || !keep_cs)
        spi_set_cs(msg->spi, false);

    if (msg->status == -EINPROGRESS)
        msg->status = ret;

    spi_finalize_current_message(master);

    return ret;
}

spi_transfer在spi-qup.c的probe函數中已經進行了賦值,因此最終的transfer依賴於控制器廠商的實現。

異步傳輸函數spi_async是以spi_async—-> __spi_async—->master->transfer關係調用transfer函數的。

3.3 廠商的總線傳輸

控制器驅動註冊函數中進行了傳輸函數的賦值。

master->transfer_one = spi_qup_transfer_one;

spi_qup_transfer_one的實現如下,涉及到控制器狀態機的控制和向fifo寫數據等寄存器操作。如果對此函數感興趣可以查看qcom的spec。
spi_qup.c

static int spi_qup_transfer_one(struct spi_master *master,
                  struct spi_device *spi,
                  struct spi_transfer *xfer)
{
    struct spi_qup *controller = spi_master_get_devdata(master);
    unsigned long timeout, flags;
    int ret = -EIO;

    ret = spi_qup_io_config(spi, xfer);
    if (ret)
        return ret;

    timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
    timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
    timeout = 100 * msecs_to_jiffies(timeout);

    reinit_completion(&controller->done);

    spin_lock_irqsave(&controller->lock, flags);
    controller->xfer     = xfer;
    controller->error    = 0;
    controller->rx_bytes = 0;
    controller->tx_bytes = 0;
    spin_unlock_irqrestore(&controller->lock, flags);

    if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
        dev_warn(controller->dev, "cannot set RUN state\n");
        goto exit;
    }

    if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
        dev_warn(controller->dev, "cannot set PAUSE state\n");
        goto exit;
    }

    spi_qup_fifo_write(controller, xfer);

    if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
        dev_warn(controller->dev, "cannot set EXECUTE state\n");
        goto exit;
    }

    if (!wait_for_completion_timeout(&controller->done, timeout))
        ret = -ETIMEDOUT;
exit:
    spi_qup_set_state(controller, QUP_STATE_RESET);
    spin_lock_irqsave(&controller->lock, flags);
    controller->xfer = NULL;
    if (!ret)
        ret = controller->error;
    spin_unlock_irqrestore(&controller->lock, flags);
    return ret;
}
發佈了29 篇原創文章 · 獲贊 27 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章