mtk_spi 总线架构

mt6739-kernel4.4
0. file_list
2. alps/kernel-4.4/include/linux/spi/spi.h
        struct spi_device, struct spi_driver, struct spi_board_info, struct spi_transfer, struct spi_master, 
        struct spi_message, struct spi_statistics, 
        spi_master_get_devdata();
3. alps/kernel-4.4/drivers/spi/spi.c
        struct bus_type spi_bus_type, static struct class spi_master_class
        spi_new_device(); spi_add_device(); __spi_register_driver(); spi_dev_set_name();
        spi_register_master(); devm_spi_register_master(); spi_setup(); __spi_sync(); spi_sync();
        spi_match_device();     spi_uevent(); __spi_register_driver(); spi_alloc_master();
        spi_match_master_to_boardinfo();
5. alps/kernel-4.4/drivers/spi/spi-adi-v3.c     //亚德诺半导体
6. alps/kernel-4.4/drivers/spi/spi-mt65xx.c     //spi控制器驱动, spi_master
        struct mtk_spi; 
        module_platform_driver();    mtk_spi_setup();
7. alps/kernel-4.4/include/linux/list.h
        list_add_tail(); list_for_each_entry();
8. alps/kernel-4.4/drivers/base/bus.c
        bus_for_each_dev(); bus_register();
9. kernel-4.4/drivers/base/core.c
        device_add();
10. alps/kernel-4.4/include/linux/device.h
        dev_get_drvdata();
11. alps/kernel-4.4/include/linux/of_device.h     //dts
        of_driver_match_device();
12. alps/kernel-4.4/drivers/base/base.h
        driver_match_device();
13. alps/kernel-4.4/drivers/base/driver.c
        driver_register();

-----mt6735/37-kernel3.18
20. alps/kernel-3.18/drivers/spi/mediatek/mt6735/spi.c
    mt_spi_probe(){ pdev->id }


==master=start====================================================
/*总线添加master(spi控制器)*/
m1 ------ dts ------------------------------------------------
/{
    ... ...
    spi0: spi@1100a000 {
        compatible = "mediatek,mt6739-spi";
        mediatek,pad-select = <0>;
        reg = <0 0x1100a000 0 0x1000>;
        interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_LOW>;
        clocks = <&topckgen CLK_TOP_SYSPLL3_D2>,
                <&topckgen CLK_TOP_SPI_SEL>,
                <&infracfg_ao CLK_INFRA_SPI0>;
        clock-names = "parent-clk", "sel-clk", "spi-clk";
        };

    spi1: spi@11010000 {
        compatible = "mediatek,mt6739-spi";
        mediatek,pad-select = <0>;
        reg = <0 0x11010000 0 0x1000>;
        interrupts = <GIC_SPI 122 IRQ_TYPE_LEVEL_LOW>;
        clocks = <&topckgen CLK_TOP_SYSPLL3_D2>,
                <&topckgen CLK_TOP_SPI_SEL>,
                <&infracfg_ao CLK_INFRA_SPI1>;
        clock-names = "parent-clk", "sel-clk", "spi-clk";
    };

    spi2: spi@11012000 {
        compatible = "mediatek,mt6739-spi";
        mediatek,pad-select = <0>;
        reg = <0 0x11012000 0 0x1000>;
        interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_LOW>;
        clocks = <&topckgen CLK_TOP_SYSPLL3_D2>,
                <&topckgen CLK_TOP_SPI_SEL>,
                <&infracfg_ao CLK_INFRA_SPI2>;
        clock-names = "parent-clk", "sel-clk", "spi-clk";
    };
    ... ...
}
// ipt_slave
&spi0 {
    #address-cells = <1>;
    #size-cells = <0>;
    finger@0 {  
         compatible = "mediatek,finger-spi";
         reg = <0>;
         spi-max-frequency = <60000000>;  
         status = "okay";  
    };
};
m2. alps/kernel-4.4/drivers/spi/spi-mt65xx.c
static const struct of_device_id mtk_spi_of_match[] = {
    ... ...
    { .compatible = "mediatek,mt6739-spi",    //ipt
        .data = (void *)&mt6739_compat,
    },
    { .compatible = "mediatek,mt8135-spi",
        .data = (void *)&mtk_common_compat,
    },
    ... ...
    {}
};
MODULE_DEVICE_TABLE(of, mtk_spi_of_match);

static struct platform_driver mtk_spi_driver = {
    .driver = {
        .name = "mtk-spi",
        .pm    = &mtk_spi_pm,
        .of_match_table = mtk_spi_of_match,     //ipt
    },
    .probe = mtk_spi_probe,
    .remove = mtk_spi_remove,
};

module_platform_driver(mtk_spi_driver); //ipt

/* -- 调用流程 --- */
module_platform_driver(mtk_spi_driver);
    int mtk_spi_probe(struct platform_device *pdev);    
        |master->setup = mtk_spi_setup;
        |master = spi_alloc_master(&pdev->dev, sizeof(*mdata));
        |ret = devm_spi_register_master(&pdev->dev, master);
            ret = spi_register_master(master);
                for(;spi_master_list;) { //list_for_each_entry
                    spi_match_master_to_boardinfo(master, &bi->board_info);
                        dev = spi_new_device(master, bi);
                            |proxy = spi_alloc_device(master);
                            |status = spi_add_device(proxy);
                                |spi_dev_set_name(spi);
                                |status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
                                |status = spi_setup(spi);
                                    |status = spi->master->setup(spi);    //mtk_spi_setup
                                        mtk_spi_setup();
                                            |struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
                                            |gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
                                    |spi_set_cs(spi, false);
                                |status = device_add(&spi->dev);
                }//for
                of_register_spi_devices(master); //spi_register_master' invoke

/* ------- 细节 0 start------spi_bus_type-----------------*/
/* 
ps:  slave 在注册driver(spi_driver)的时候都要指定spi_bus_type 
=>
驱动&设备会在/sys/spi的drivers&devices文件下生成相关设备
*/
@ alps/kernel-4.4/drivers/spi/spi.c
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);

static int __init spi_init(void)
{
    status = bus_register(&spi_bus_type);
    return 0;
}
postcore_initcall(spi_init);
/*
生成/sys/bus/文件目录
驱动.bus配置了spi_bus_type, 生成的驱动会在/sys/bus/目录生成相关文件(drivers&devices)
*/
-----------------------------------------------
@ alps/driver/xxx/modexxx.c
static struct spi_driver modexxx_driver = {
    .driver = {
        .name = SPI_DRV_NAME,
        .bus = &spi_bus_type,    //ipt
        .owner = THIS_MODULE,
        .pm = &modexxx_pm,
#ifdef CONFIG_OF //ipt not user
        .of_match_table = of_match_ptr(modexxx_of_match),
#endif    
    },
    .id_table = modexxx_id,
    .probe = modexxx_probe,
    .remove = modexxx_remove,
};

static int modexxx_spi_init(void)
{
    ... ...
    ret=spi_register_driver(&modexxx_driver);
    ... ...
    return 1;
}
late_initcall_sync(modexxx_spi_init);

/* ------- 细节 0 ends------spi_bus_type-----------------*/

/* ------- 细节 1 start------bus_num-----------------*/
dts: mtk_spi_driver驱动根据"mediatek,mt6739-spi"匹配到设备之后, 注册master
bus_num: spi_alloc_master() => master->bus_num=-1; 
spi_register_master(struct spi_master *master)
    static atomic_t        dyn_bus_id = ATOMIC_INIT((1<<15) - 1); //32768-1=32667
    master->bus_num = of_alias_get_id(master->dev.of_node, "spi"); //bus_num=4294967277
    master->bus_num = atomic_dec_return(&dyn_bus_id);     //bus_num=32766=32767-1, ps: 后面的逐步-1, 32765, 32764
    ==>
    master->bus_num=32766;
/* ------- 细节 1 ends-------bus_num----------------*/

/* ------- 细节 2 start----------master在总线上的id及名称------------*/
spi_register_master(struct spi_master *master)
    dev_set_name(&master->dev, "spi%u", master->bus_num); //dev_name="spi32766"
        for(;spi_master_list;) { //list_for_each_entry
            spi_match_master_to_boardinfo(master, &bi->board_info);
                dev = spi_new_device(master, bi);
                    status = spi_add_device(proxy);
                        spi_dev_set_name(spi);
                            dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev), spi->chip_select); //dev_name="spi32766.0" spi(总线名称, .0 is cs)
/* ------- 细节 2 ends----------master在总线上的id及名称------------*/

/* ------- 细节 3 start------加载master下的从设备-----------------*/
// 关键字: ipt_slave, 由上面dts配置可以知道, master配置spi0: spi@1100a000下存在finger@0 {... }
spi_register_master(struct spi_master *master)
    of_register_spi_devices(master);
        for_each_available_child_of_node(master->dev.of_node, nc) { //遍历master下的子设备
            spi = of_register_spi_device(master, nc);
                |spi = spi_alloc_device(master);
                    //对以上finger@0 {}的配置解析(reg, spi-max-frequency等)
                    |rc = of_property_read_u32(nc, "reg", &value);
                    |rc = of_property_read_u32(nc, "spi-max-frequency", &value);
        } //for
/* ------- 细节 3 ends------加载master下的从设备-----------------*/

/* ------- 细节 4 start------如果未使用dts, 匹配spi master和spi slave-----------------*/
/* 关键字: not_dts, 
以设备modexxx为例, 如果没有dts配置,
需要自己使用spi_register_board_info添加设备dev(供spi_register_driver匹配);
根据bus_num和master的bus_num配置绑定(dts不需要,具体关注”细节 3“) */
static struct spi_driver modexxx_driver = {
    .driver = {
        .name = SPI_DRV_NAME,
        .bus = &spi_bus_type,    //ipt
        .owner = THIS_MODULE,
        .pm = &modexxx_pm,
#ifdef CONFIG_OF //ipt not user
        .of_match_table = of_match_ptr(modexxx_of_match),
#endif    
    },
    .id_table = modexxx_id,
    .probe = modexxx_probe,
    .remove = modexxx_remove,
};

// ipt
#ifndef CONFIG_OF 
static struct spi_board_info spi_board_modexxx[] __initdata = {
    [0] = {
        .modalias = "mode_name",
        .bus_num = 0,    // ipt not_dts
        .chip_select = 0,
        .mode = SPI_MODE_0,
        .max_speed_hz = 6000000,
    },
};
#endif

static int modexxx_spi_init(void)
{
    int ret;
#ifndef CONFIG_OF // ipt not_dts
    ret=spi_register_board_info(spi_board_modexxx, ARRAY_SIZE(spi_board_modexxx));
#endif
    printk("modexxx_spi_init\r\n");
    ret=spi_register_driver(&modexxx_driver);
    if(ret<0)
    {
        printk("modexxx_spi_init  fail\r\n");    
    }
    return 1;
}
late_initcall_sync(modexxx_spi_init);
ps:
spi_register_board_info(struct spi_board_info const *info, unsigned n);
    list_add_tail(&bi->list, &board_list);
    list_for_each_entry(master, &spi_master_list, list)
        spi_match_master_to_boardinfo(master, &bi->board_info);
/* ------- 细节 4 ends------如果未使用dts, 匹配spi master和spi slave-----------------*/
==master=ends====================================================


==================================================
/*通用接口层*/
postcore_initcall(spi_init);
    buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
    status = bus_register(&spi_bus_type);
    status = class_register(&spi_master_class);

==================================================
/* 数据发送 */
spi_sync(struct spi_device *spi, struct spi_message *message);
    __spi_sync(struct spi_device *spi, struct spi_message *message, int bus_locked);
        #if
            trace_spi_message_submit(message);
            status = __spi_queued_transfer(spi, message, false);
        #else
            status = spi_async_locked(spi, message);
                ret = __spi_async(spi, message);
                    |trace_spi_message_submit(message);
                    |master->transfer(spi, message);
        #endif


spi_async(struct spi_device *spi, struct spi_message *message)
    static int __spi_async(struct spi_device *spi, struct spi_message *message)
        |trace_spi_message_submit(message);
        |master->transfer(spi, message);

==================================================
ret=spi_register_driver(&cdfinger_driver);
    __spi_register_driver(struct module *owner, struct spi_driver *sdrv)
        driver_register(&sdrv->driver);

ps:
#define spi_register_driver(driver) \
    __spi_register_driver(THIS_MODULE, driver)
==================================================
mt6737/35-kernel3.18

/* 
mt6737(kernel3.18)&mt6739(kernel4.4)注册spimaster的机制不同
*/
0. xxx.dts/dtsi
        spi0:spi@1100a000 {
            compatible = "mediatek,mt6735-spi";
            cell-index = <0>;    //ipt
            spi-padmacro = <0>;
            reg = <0x1100a000 0x1000>;
            interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_LOW>;
            clocks = <&perisys PERI_SPI0>;
            clock-names = "spi-main";
            clock-frequency = <109000000>;
            clock-div = <1>;
        };

1. alps/kernel-3.18/drivers/spi/mediatek/mt6735/spi.c
static int __init mt_spi_probe(struct platform_device *pdev)
{
    struct spi_master *master;


    master = spi_alloc_master(&pdev->dev, sizeof(struct mt_spi_t));
    ms = spi_master_get_devdata(master);
    ... ...
    if (of_property_read_u32(pdev->dev.of_node, "cell-index", &pdev->id)) {    //ipt
        dev_err(&pdev->dev, "SPI get cell-index failed\n");
        return -ENODEV;
    }
    ... ...
    master->num_chipselect = 2;
    master->mode_bits = (SPI_CPOL | SPI_CPHA);
    master->bus_num = pdev->id;    //ipt
    master->setup = mt_spi_setup;
    master->transfer = mt_spi_transfer;
    master->cleanup = mt_spi_cleanup;
    platform_set_drvdata(pdev, master);
    ... ...
    spi_master_set_devdata(master, ms);
    ... ...
    ret = spi_register_master(master);    //ipt
    ... ...
}

=================================================

3. alps/kernel-4.4/drivers/spi/spi.c
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);


static struct class spi_master_class = {
    .name        = "spi_master",
    .owner        = THIS_MODULE,
    .dev_release    = spi_master_release,
    .dev_groups    = spi_master_groups,
};


static int __init spi_init(void)
{
    status = bus_register(&spi_bus_type);
    status = class_register(&spi_master_class);
}
postcore_initcall(spi_init);


2. alps/kernel-4.4/include/linux/spi/spi.h
struct spi_device {
    struct device        dev;
    struct spi_master    *master;
    u32            max_speed_hz;
    u8            chip_select;
    u8            bits_per_word;
    u16            mode;
#define    SPI_CPHA    0x01            /* clock phase */
... ...
#define    SPI_RX_QUAD    0x800            /* receive with 4 wires */
    int            irq;
    void            *controller_state;
    void            *controller_data;
    char            modalias[SPI_NAME_SIZE];
    int            cs_gpio;    /* chip select gpio */

    /* the statistics */
    struct spi_statistics    statistics;
};

struct spi_board_info {
    char        modalias[SPI_NAME_SIZE];
    const void    *platform_data;
    void        *controller_data;
    int        irq;
    u32        max_speed_hz;

    u16        bus_num;
    u16        chip_select;

    u16        mode;
};

#define spi_register_driver(driver) \
    __spi_register_driver(THIS_MODULE, driver)

3. alps/kernel-4.4/drivers/spi/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;
}
EXPORT_SYMBOL_GPL(devm_spi_register_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;

    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;

printk("jun before operation bus_num=%u", master->bus_num); //bus_num=4294867295
    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;
    }

    INIT_LIST_HEAD(&master->queue);
    spin_lock_init(&master->queue_lock);
    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);        //bus_num=32766
    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;
        }
    }
    /* add statistics */
    spin_lock_init(&master->statistics.lock);
printk("jun after operation bus_num=%u", master->bus_num);    //bus_num=32766
    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);

    /* Register devices from the device tree and ACPI */
    of_register_spi_devices(master);
    acpi_register_spi_devices(master);
printk("jun end operation bus_num=%u", master->bus_num);    //bus_num=32766

    dump_stack();
done:
    return status;
}
EXPORT_SYMBOL_GPL(spi_register_master);

6. alps/kernel-4.4/drivers/spi/spi-mt65xx.c     //spi控制器驱动, spi_master
module_platform_driver(mtk_spi_driver);
    int mtk_spi_probe(struct platform_device *pdev);    
        | master->setup = mtk_spi_setup;
        |master = spi_alloc_master(&pdev->dev, sizeof(*mdata));
        |ret = devm_spi_register_master(&pdev->dev, master);
            ret = spi_register_master(master);
                |dev_set_name(&master->dev, "spi%u", master->bus_num);
                |for(;;) {    //list_for_each_entry(bi, &board_list, list)
                    spi_match_master_to_boardinfo(master, &bi->board_info);
                        dev = spi_new_device(master, bi);
                            |proxy = spi_alloc_device(master);
                            |status = spi_add_device(proxy);
                                |spi_dev_set_name(spi);
                                |status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
                                |status = spi_setup(spi);
                                    |status = spi->master->setup(spi);    //mtk_spi_setup
                                        mtk_spi_setup();
                                            |struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
                                            |gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
                                    |spi_set_cs(spi, false);
                                |status = device_add(&spi->dev);
                }


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

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


struct spi_device *spi_alloc_device(struct spi_master *master)
{
    struct spi_device    *spi;

    if (!spi_master_get(master))
        return NULL;

    spi = kzalloc(sizeof(*spi), GFP_KERNEL);
    if (!spi) {
        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;
    spi->cs_gpio = -ENOENT;

    spin_lock_init(&spi->statistics.lock);

    device_initialize(&spi->dev);
    return spi;
}
EXPORT_SYMBOL_GPL(spi_alloc_device);

int spi_add_device(struct spi_device *spi)
{
    static DEFINE_MUTEX(spi_add_lock);
    struct spi_master *master = spi->master;
    struct device *dev = master->dev.parent;
    int status;

    /* Chipselects are numbered 0..max; validate. */
    if (spi->chip_select >= master->num_chipselect) {
        dev_err(dev, "cs%d >= max %d\n",
            spi->chip_select,
            master->num_chipselect);
        return -EINVAL;
    }

    /* Set the bus ID string */
    spi_dev_set_name(spi);

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

    status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
    if (status) {
        dev_err(dev, "chipselect %d already in use\n",
                spi->chip_select);
        goto done;
    }

    if (master->cs_gpios)
        spi->cs_gpio = master->cs_gpios[spi->chip_select];

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

//set name spi327666.0
static void spi_dev_set_name(struct spi_device *spi)
{
    struct acpi_device *adev = ACPI_COMPANION(&spi->dev);

    if (adev) {
        dev_set_name(&spi->dev, "spi-%s", acpi_dev_name(adev));
        return;
    }

    dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
             spi->chip_select);
}

int bus_for_each_dev(struct bus_type *bus, struct device *start,
             void *data, int (*fn)(struct device *, void *))
{
    struct klist_iter i;
    struct device *dev;
    int error = 0;

    if (!bus || !bus->p)
        return -EINVAL;

    klist_iter_init_node(&bus->p->klist_devices, &i,
                 (start ? &start->p->knode_bus : NULL));
    while ((dev = next_device(&i)) && !error)
        error = fn(dev, data);
    klist_iter_exit(&i);
    return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);

int spi_setup(struct spi_device *spi)
{
    unsigned    bad_bits, ugly_bits;
    int        status;

    /* check mode to prevent that DUAL and QUAD set at the same time
     */
    if (((spi->mode & SPI_TX_DUAL) && (spi->mode & SPI_TX_QUAD)) ||
        ((spi->mode & SPI_RX_DUAL) && (spi->mode & SPI_RX_QUAD))) {
        dev_err(&spi->dev,
        "setup: can not select dual and quad at the same time\n");
        return -EINVAL;
    }
    /* if it is SPI_3WIRE mode, DUAL and QUAD should be forbidden
     */
    if ((spi->mode & SPI_3WIRE) && (spi->mode &
        (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)))
        return -EINVAL;
    /* help drivers fail *cleanly* when they need options
     * that aren't supported with their current master
     */
    bad_bits = spi->mode & ~spi->master->mode_bits;
    ugly_bits = bad_bits &
            (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD);
    if (ugly_bits) {
        dev_warn(&spi->dev,
             "setup: ignoring unsupported mode bits %x\n",
             ugly_bits);
        spi->mode &= ~ugly_bits;
        bad_bits &= ~ugly_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_validate_bits_per_word(spi->master, spi->bits_per_word);
    if (status)
        return status;

    if (!spi->max_speed_hz)
        spi->max_speed_hz = spi->master->max_speed_hz;

    if (spi->master->setup)
        status = spi->master->setup(spi);

    spi_set_cs(spi, false);

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

static void spi_set_cs(struct spi_device *spi, bool enable)
{
    if (spi->mode & SPI_CS_HIGH)
        enable = !enable;

    if (gpio_is_valid(spi->cs_gpio))
        gpio_set_value(spi->cs_gpio, !enable);
    else if (spi->master->set_cs)
        spi->master->set_cs(spi, !enable);
}

static int mtk_spi_setup(struct spi_device *spi)
{
    struct mtk_spi *mdata = spi_master_get_devdata(spi->master);

    if (!spi->controller_data)
        spi->controller_data = (void *)&mtk_default_chip_info;

    if (mdata->dev_comp->need_pad_sel && gpio_is_valid(spi->cs_gpio))
        gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));

    return 0;
}

static inline void *spi_master_get_devdata(struct spi_master *master)
{
    return dev_get_drvdata(&master->dev);
}

static inline void *dev_get_drvdata(const struct device *dev)
{
    return dev->driver_data;
}


int spi_sync(struct spi_device *spi, struct spi_message *message)
{
    return __spi_sync(spi, message, 0);
}
EXPORT_SYMBOL_GPL(spi_sync);

static int spi_match_device(struct device *dev, struct device_driver *drv)
{
    const struct spi_device    *spi = to_spi_device(dev);
    const struct spi_driver    *sdrv = to_spi_driver(drv);

    /* Attempt an OF style match */
    if (of_driver_match_device(dev, drv))
        return 1;

    /* Then try ACPI */
    if (acpi_driver_match_device(dev, drv))
        return 1;

    if (sdrv->id_table)
        return !!spi_match_id(sdrv->id_table, spi);

    return strcmp(spi->modalias, drv->name) == 0;
}

static int spi_uevent(struct device *dev, struct kobj_uevent_env *env)
{
    const struct spi_device        *spi = to_spi_device(dev);
    int rc;

    rc = acpi_device_uevent_modalias(dev, env);
    if (rc != -ENODEV)
        return rc;

    add_uevent_var(env, "MODALIAS=%s%s", SPI_MODULE_PREFIX, spi->modalias);
    return 0;
}

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

static inline int driver_match_device(struct device_driver *drv,
                      struct device *dev)
{
    return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

=========dts start===============================
static inline int of_driver_match_device(struct device *dev,
                     const struct device_driver *drv)
{
    return of_match_device(drv->of_match_table, dev) != NULL;
}

#define of_match_device(matches, dev)    \
    __of_match_device(of_match_ptr(matches), (dev))


=========dts end===============================


int __spi_register_driver(struct module *owner, struct spi_driver *sdrv)
{
    sdrv->driver.owner = owner;
    sdrv->driver.bus = &spi_bus_type;
    if (sdrv->probe)
        sdrv->driver.probe = spi_drv_probe;
    if (sdrv->remove)
        sdrv->driver.remove = spi_drv_remove;
    if (sdrv->shutdown)
        sdrv->driver.shutdown = spi_drv_shutdown;
    return driver_register(&sdrv->driver);
}
EXPORT_SYMBOL_GPL(__spi_register_driver);

int driver_register(struct device_driver *drv)
{
    int ret;
    struct device_driver *other;

    BUG_ON(!drv->bus->p);

    if ((drv->bus->probe && drv->probe) ||
        (drv->bus->remove && drv->remove) ||
        (drv->bus->shutdown && drv->shutdown))
        printk(KERN_WARNING "Driver '%s' needs updating - please use "
            "bus_type methods\n", drv->name);

    other = driver_find(drv->name, drv->bus);
    if (other) {
        printk(KERN_ERR "Error: Driver '%s' is already registered, "
            "aborting...\n", drv->name);
        return -EBUSY;
    }

    ret = bus_add_driver(drv);
    if (ret)
        return ret;
    ret = driver_add_groups(drv, drv->groups);
    if (ret) {
        bus_remove_driver(drv);
        return ret;
    }
    kobject_uevent(&drv->p->kobj, KOBJ_ADD);

    return ret;
}
EXPORT_SYMBOL_GPL(driver_register);

struct mtk_spi {
    void __iomem *base;
    void __iomem *peri_regs;
    u32 state;
    int pad_num;
    u32 *pad_sel;
    struct clk *parent_clk, *sel_clk, *spi_clk;
    struct spi_transfer *cur_transfer;
    u32 xfer_len;
    struct scatterlist *tx_sgl, *rx_sgl;
    u32 tx_sgl_len, rx_sgl_len;
    const struct mtk_spi_compatible *dev_comp;
    u32 dram_8gb_offset;
};


=========================================
OTHER:
=========================================
========1================================
static LIST_HEAD(spi_master_list);
LIST_HEAD: 双向链表结构体的指针的数据结构


========2==start========================
#define module_platform_driver(__platform_driver) \  
    module_driver(__platform_driver, platform_driver_register, \  
            platform_driver_unregister)

==>
#define module_driver(__driver, __register, __unregister, ...) \  
static int __init __driver##_init(void) \  
{ \  
    return __register(&(__driver) , ##__VA_ARGS__); \  
} \  
module_init(__driver##_init); \  
static void __exit __driver##_exit(void) \  
{ \  
    __unregister(&(__driver) , ##__VA_ARGS__); \  
} \  
module_exit(__driver##_exit);

==>
module_platform_driver(xxx);
最终展开后就是如下形式:
static int __init xxx_init(void)
{
        return platform_driver_register(&xxx);
}
module_init(xxx_init);
static void __exit xxx_init(void)
{
        return platform_driver_unregister(&xxx);
}
module_exit(xxx_exit);
========2==end=========================


========3==start=========================
将new所代表的list_head插入head所索引的队列的尾部
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
    __list_add(new, head->prev, head);
}
static inline void __list_add(struct list_head *new,
                  struct list_head *prev,
                  struct list_head *next)
{
    next->prev = new; //(1)
    new->next = next; //(2)
    new->prev = prev; //(3)
    prev->next = new; //(4)
}
----------------------------------------------------------------------------------
#define list_for_each_entry(pos, head, member)                \
    for (pos = list_first_entry(head, typeof(*pos), member);    \
         &pos->member != (head);                    \
         pos = list_next_entry(pos, member))

#define list_first_entry(ptr, type, member) \
    list_entry((ptr)->next, type, member)

#define list_entry(ptr, type, member) \
    container_of(ptr, type, member

container_of: 根据member的地址计算出contain(list)的首地址

#define list_next_entry(pos, member) \
    list_entry((pos)->member.next, typeof(*(pos)), member)
list_next_entry: 如果next为空 => 计算不出首地址 => 退出循环

========3==end=========================

 

内容概要:本文档详细介绍了基于直驱永磁同步发电机(PMSG)的1.5MW风力发电系统在Simulink环境下的建模与仿真全过程,涵盖了风力机空气动力学模型、PMSG电磁特性建模、不可控整流与逆变电路、直流环节、空间矢量脉宽调制(SVPWM)技术以及核心控制策略的设计。重点实现了最大功率点跟踪(MPPT)控制以提升风能捕获效率,并构建了电压外环与电流内环协同工作的双闭环控制系统,通过仿真验证了系统在不同风速条件下稳定运行的能力及动态响应性能。; 适合人群:适用于具备电力系统、电机控制理论基础及Simulink仿真操作经验的研究生、科研人员和从事新能源发电系统开发的工程技术人员;特别适合正在进行风电系统建模、控制算法研究或完成相关毕业设计的专业人士。; 使用场景及目标:①深入理解直驱式PMSG风力发电系统的整体架构与工作机理;②掌握从物理部件建模到控制策略实现的完整Simulink仿真流程;③学习并复现MPPT控制、双闭环控制等关键技术方案;④为后续开展低电压穿越、并网稳定性分析、故障诊断等高级课题提供可靠的仿真平台支撑。; 阅读建议:建议结合Matlab/Simulink软件动手实践,逐模块搭建模型,重点关注各控制环节的参数设计与调试方法,同时可参照文中提供的其他风电相关资源进行拓展学习与对比分析。
已经博主授权,源码转载自 https://pan.quark.cn/s/868afdd63918 在信息技术领域中,前端开发构成了Web应用程序构建的关键环节,而登录注册页面则是用户与网站进行互动的起始界面。"150款web登录注册页面模板(附带效果图+源码)"这一资源为前端工程师们提供了一系列预先设计的界面组件,支持他们迅速构建既美观又实用的登录及注册界面,从而有效缩减开发周期并增强工作效率。 这些模板囊括了多样化的风格和设计潮流,涵盖了扁平化设计、Material Design、渐变色彩、暗黑模式等,能够适应不同项目的特定要求。在设计中强调用户体验,通过科学的布局安排,提升了表单的便捷操作性和可辨识度,并且不忽视视觉层面的吸引力。设计师通常会关注自适应设计,保证页面在多种设备(涵盖手机、平板及桌面电脑)上均能呈现良好的视觉效果。 这些模板均配备了源代码,使得开发者得以深入探究并个性化定制每个构成部分,涉及HTML的页面构造、CSS的样式修饰以及JavaScript的交互逻辑。HTML主要承担着页面基础结构的搭建,CSS用于实现页面美化与布局控制,JavaScript则常用于处理表单验证和交互效果。对于那些精通这三种技术的开发者而言,他们可以根据个人需求对模板进行功能扩展和样式调整。 在实际部署时,登录注册页面通常需要集成基础的输入项,例如用户名、密码、电子邮箱等,并且必须重视安全性考量,诸如密码强度指引、验证码系统等。除此之外,为了优化用户体验,还可能集成记住密码、自动填充、社交平台登录(例如微信、QQ、微博)等功能。 在开发阶段,前端工程师还需关注Web标准和无障碍访问(WCAG)规范,确保页面的通用友好性,这包括视障、听障或其他有特殊需求的用户群体。具体措施涉及标...
源码直接下载地址: https://pan.quark.cn/s/9af8b9f95652 ### Multisim模型的导入和使用 ### 一、引言 随着电子设计自动化(EDA)工具的进步,Multisim已经成为电子工程师进行电路仿真、分析和设计的关键工具之一。借助Multisim,工程师们能够便捷地构建电路模型,并对电路进行仿真验证。本文将系统阐述如何在Multisim中导入并运用芯片仿真模型,这对于提升电子产品的研发效能具有显著价值。 ### 二、Multisim中构建新元器件 构建新元器件是Multisim中的核心功能,特别是对于那些需要特定模型或无法从Multisim库中直接获取的元器件来说更为关键。以下为构建新元器件的具体流程: ##### 步骤1:录入元器件信息 在Multisim中启动“Component Wizard”,即元器件向导,开始创建新的元器件。首先需要录入元器件的基本资料,包括型号、主要功能、类型等。这些资料将有助于用户更高效地管理和检索元器件。 ##### 步骤2:录入封装信息 接下来需要设定元器件的封装信息。在这一环节中,用户需要依据实际芯片的封装规格来选择适宜的引脚数量。同时,还需明确是构建单一部件元器件还是复合部件元器件。如果是复合部件元器件,则必须确保引脚数量与符号中使用的引脚数量保持一致。 ##### 步骤3:录入符号信息 在此步骤中,用户可以编辑元器件在仿真过程中的显示符号。编辑符号可以通过三种途径进行:直接编辑、从数据库中复制现有符号或复制当前符号以备将来使用。编辑符号时应注重其在电路图中的可辨识度和清晰度。 ##### 步骤4:设定管脚参数 在该步骤中,用户需要参照数据手册上的管脚顺序为每个管脚命名,并选择恰当的类型。...
代码转载自:https://pan.quark.cn/s/7b1a6710052c Vivado 2018.2 与 ModelSim 的协同仿真操作 Vivado 2018.2 是由 Xilinx 公司开发的一款用于 FPGA 设计的工具,它包含了丰富的设计和仿真功能。然而,在实际应用过程中,用户可能会遇到其自带的仿真工具运行效率不高的问题。为了提升仿真效率并简化设计验证流程,可以考虑采用第三方仿真工具 ModelSim。ModelSim 是一款性能卓越且市场应用广泛的仿真软件,接下来的内容将详细阐述如何实现 Vivado 2018.2 与 ModelSim 的联合使用。 配置 ModelSim 的安装路径 在使用 Vivado 2018.2 时,首先需要配置 ModelSim 的安装位置。用户可以通过点击 Vivado 菜单中的“Tools”——>“Settings...”选项,然后在弹出的设置界面中,选择“Tool Settings”下的“3rd Party Simulators”选项卡。在“Install Paths”区域,找到“ModelSim”条目,并在此输入或选择 ModelSim 的具体安装路径。 执行器件库编译操作 在 ModelSim 的安装目录下,创建一个名为 xilinx_lib 的子文件夹。随后,在 Vivado 菜单中通过“Tools”——>“Compile Simulation Libraries...”选项启动器件库编译流程,并设定相应的编译参数。在打开的对话框里,将仿真工具选择为“ModelSim Simulator”,保持语言和库的默认设置不变,同时指定编译器件库的存放位置和 ModelSim 可执行文件的路径。 ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值