I.MX6 ar1020 SPI device driver hacking

/************************************************************************************
 *                    I.MX6 ar1020 SPI device driver hacking
 * 声明:
 *     1. 本文主要是解读I.MX6中ar1020 SPI设备注册,以及驱动调用流程;
 *     2. 本文主要使用了vim+ctags进行代码跟踪,所以几乎都是函数原型之间的调用;
 *
 *                                             2015-9-5 晴 深圳 南山平山村 曾剑锋
 ***********************************************************************************/

/*
 * initialize __mach_desc_MX6Q_SABRESD data structure.
 */
MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board")
    /* Maintainer: Freescale Semiconductor, Inc. */
    .boot_params = MX6_PHYS_OFFSET + 0x100,
    .fixup = fixup_mxc_board,
    .map_io = mx6_map_io,
    .init_irq = mx6_init_irq,
    .init_machine = mx6_sabresd_board_init,   ---------------+
    .timer = &mx6_sabresd_timer,                             |
    .reserve = mx6q_sabresd_reserve,                         |
MACHINE_END                                                  |
                                                             |
/*!                                                          |
 * Board specific initialization.                            |
 */                                                          |
static void __init mx6_sabresd_board_init(void)   <----------+
{
    ......
    imx6q_add_ecspi(0, &mx6q_sabresd_spi_data);               ---------------+---+
    imx6q_add_ecspi(1, &mx6q_sabresd_spi2_data);              ---------------*-+ |
    spi_device_init();                                        ---------------*-*-*-+
    ......                                                                   | | | |
    gpio_request(SABRESD_AR1020_INT, "ar1020-interrupt");                    | | | |
    gpio_direction_input(SABRESD_AR1020_INT);                                | | | |
    ......                                                                   | | | |
}                                                                            | | | |
                                                                             | | | |
static const struct spi_imx_master mx6q_sabresd_spi_data __initconst = {  <--+ | | |
    .chipselect     = mx6q_sabresd_spi_cs,              -----+                 | | |
    .num_chipselect = ARRAY_SIZE(mx6q_sabresd_spi_cs),       |                 | | |
};                                                           |                 | | |
                                                             |                 | | |
static int mx6q_sabresd_spi_cs[] = {              <----------+                 | | |
    SABRESD_ECSPI1_CS1,                                                        | | |
};                                                                             | | |
                                                                               | | |
static const struct spi_imx_master mx6q_sabresd_spi2_data __initconst = { <----+ | |
    .chipselect     = mx6q_sabresd_spi2_cs,            ------+                   | |
    .num_chipselect = ARRAY_SIZE(mx6q_sabresd_spi2_cs),      |                   | |
};                                                           |                   | |
                                                             |                   | |
static int mx6q_sabresd_spi2_cs[] = {             <----------+                   | |
    SABRESD_ECSPI2_CS0,                                                          | |
};                                                                               | |
                                                                                 | |
extern const struct imx_spi_imx_data imx6q_ecspi_data[] __initconst;  ------+    | |
#define imx6q_add_ecspi(id, pdata)    \                    <----------------*----+ |
    imx_add_spi_imx(&imx6q_ecspi_data[id], pdata)                     <-----+   ---*-----+
                                                                            |      |     |
const struct imx_spi_imx_data imx6q_ecspi_data[] __initconst = {      <-----+      |     |
#define imx6q_ecspi_data_entry(_id, _hwid)                \                        |     |
    imx_spi_imx_data_entry(MX6Q, ECSPI, "imx6q-ecspi", _id, _hwid, SZ_4K) ----+    |     |
    imx6q_ecspi_data_entry(0, 1),                                             |    |     |
    imx6q_ecspi_data_entry(1, 2),                                             |    |     |
    imx6q_ecspi_data_entry(2, 3),                                             |    |     |
    imx6q_ecspi_data_entry(3, 4),                                             |    |     |
    imx6q_ecspi_data_entry(4, 5),                                             |    |     |
};                                                                            |    |     |
#endif /* ifdef CONFIG_SOC_IMX6Q */                                           |    |     |
                                                                              |    |     |
#define imx_spi_imx_data_entry_single(soc, type, _devid, _id, hwid, _size) \  |  <-*--+  |
    {                                \                                        |    |  |  |
        .devid = _devid,                    \                                 |    |  |  |
        .id = _id,                        \                                   |    |  |  |
        .iobase = soc ## _ ## type ## hwid ## _BASE_ADDR,    \                |    |  |  |
        .iosize = _size,                    \                                 |    |  |  |
        .irq = soc ## _INT_ ## type ## hwid,            \                     |    |  |  |
    }                                                                         |    |  |  |
                                                                              |    |  |  |
#define imx_spi_imx_data_entry(soc, type, devid, id, hwid, size)    \   <-----+    |  |  |
    [id] = imx_spi_imx_data_entry_single(soc, type, devid, id, hwid, size) --------*--+  |
                                                                                   |     |
struct platform_device *__init imx_add_spi_imx(          <-------------------------*-----+
        const struct imx_spi_imx_data *data,                                       |
        const struct spi_imx_master *pdata)                                        |
{                                                                                  |
    struct resource res[] = {                                                      |
        {                                                                          |
            .start = data->iobase,                                                 |
            .end = data->iobase + data->iosize - 1,                                |
            .flags = IORESOURCE_MEM,                                               |
        }, {                                                                       |
            .start = data->irq,                                                    |
            .end = data->irq,                                                      |
            .flags = IORESOURCE_IRQ,                                               |
        },                                                                         |
    };                                                                             |
    return imx_add_platform_device(data->devid, data->id,      ------+             |
            res, ARRAY_SIZE(res), pdata, sizeof(*pdata));            |             |
}                                                                    |             |
                                                                     |             |
static inline struct platform_device *imx_add_platform_device(  <----+             |
        const char *name, int id,                                                  |
        const struct resource *res, unsigned int num_resources,                    |
        const void *data, size_t size_data)                                        |
{                                                                                  |
    return imx_add_platform_device_dmamask(                     -----+             |
            name, id, res, num_resources, data, size_data, 0);       |             |
}                                                                    |             |
                                                                     |             |
struct platform_device *__init imx_add_platform_device_dmamask( <----+             |
        const char *name, int id,                                                  |
        const struct resource *res, unsigned int num_resources,                    |
        const void *data, size_t size_data, u64 dmamask)                           |
{                                                                                  |
    int ret = -ENOMEM;                                                             |
    struct platform_device *pdev;                                                  |
                                                                                   |
    pdev = platform_device_alloc(name, id);                                        |
    if (!pdev)                                                                     |
        goto err;                                                                  |
                                                                                   |
    if (dmamask) {                                                                 |
        /*                                                                         |
         * This memory isn't freed when the device is put,                         |
         * I don't have a nice idea for that though.  Conceptually                 |
         * dma_mask in struct device should not be a pointer.                      |
         * See http://thread.gmane.org/gmane.linux.kernel.pci/9081                 |
         */                                                                        |
        pdev->dev.dma_mask =                                                       |
            kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL);                      |
        if (!pdev->dev.dma_mask)                                                   |
            /* ret is still -ENOMEM; */                                            |
            goto err;                                                              |
                                                                                   |
        *pdev->dev.dma_mask = dmamask;                                             |
        pdev->dev.coherent_dma_mask = dmamask;                                     |
    }                                                                              |
                                                                                   |
    if (res) {                                                                     |
        ret = platform_device_add_resources(pdev, res, num_resources);             |
        if (ret)                                                                   |
            goto err;                                                              |
    }                                                                              |
                                                                                   |
    if (data) {                                                                    |
        ret = platform_device_add_data(pdev, data, size_data);                     |
        if (ret)                                                                   |
            goto err;                                                              |
    }                                                                              |
                                                                                   |
    ret = platform_device_add(pdev);            ------------------------+          |
    if (ret) {                                                          |          |
err:                                                                    |          |
        if (dmamask)                                                    |          |
            kfree(pdev->dev.dma_mask);                                  |          |
        platform_device_put(pdev);                                      |          |
        return ERR_PTR(ret);                                            |          |
    }                                                                   |          |
                                                                        |          |
    return pdev;                                                        |          |
}                                                                       |          |
                                                                        |          |
/**                                                                     |          |
 * platform_device_add - add a platform device to device hierarchy      |          |
 * @pdev: platform device we're adding                                  |          |
 *                                                                      |          |
 * This is part 2 of platform_device_register(), though may be called   |          |
 * separately _iff_ pdev was allocated by platform_device_alloc().      |          |
 */                                                                     |          |
int platform_device_add(struct platform_device *pdev)       <-----------+          |
{                                                                                  |
    int i, ret = 0;                                                                |
                                                                                   |
    if (!pdev)                                                                     |
        return -EINVAL;                                                            |
                                                                                   |
    if (!pdev->dev.parent)                                                         |
        pdev->dev.parent = &platform_bus;                                          |
                                                                                   |
    pdev->dev.bus = &platform_bus_type;                                            |
                                                                                   |
    if (pdev->id != -1)                                                            |
        dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);                  |
    else                                                                           |
        dev_set_name(&pdev->dev, "%s", pdev->name);                                |
                                                                                   |
    for (i = 0; i < pdev->num_resources; i++) {                                    |
        struct resource *p, *r = &pdev->resource[i];                               |
                                                                                   |
        if (r->name == NULL)                                                       |
            r->name = dev_name(&pdev->dev);                                        |
                                                                                   |
        p = r->parent;                                                             |
        if (!p) {                                                                  |
            if (resource_type(r) == IORESOURCE_MEM)                                |
                p = &iomem_resource;                                               |
            else if (resource_type(r) == IORESOURCE_IO)                            |
                p = &ioport_resource;                                              |
        }                                                                          |
                                                                                   |
        if (p && insert_resource(p, r)) {                                          |
            printk(KERN_ERR                                                        |
                   "%s: failed to claim resource %d\n",                            |
                   dev_name(&pdev->dev), i);                                       |
            ret = -EBUSY;                                                          |
            goto failed;                                                           |
        }                                                                          |
    }                                                                              |
                                                                                   |
    pr_debug("Registering platform device '%s'. Parent at %s\n",                   |
         dev_name(&pdev->dev), dev_name(pdev->dev.parent));                        |
                                                                                   |
    ret = device_add(&pdev->dev);                                                  |
    if (ret == 0)                                                                  |
        return ret;                                                                |
                                                                                   |
 failed:                                                                           |
    while (--i >= 0) {                                                             |
        struct resource *r = &pdev->resource[i];                                   |
        unsigned long type = resource_type(r);                                     |
                                                                                   |
        if (type == IORESOURCE_MEM || type == IORESOURCE_IO)                       |
            release_resource(r);                                                   |
    }                                                                              |
                                                                                   |
    return ret;                                                                    |
}                                                                                  |
EXPORT_SYMBOL_GPL(platform_device_add);                                            |
                                                                                   |
static void spi_device_init(void)                       <--------------------------+
{
    spi_register_board_info(imx6_sabresd_spi_nor_device,        -----------------+
                ARRAY_SIZE(imx6_sabresd_spi_nor_device));                        |
}                                                                                |
                                                                                 |
static struct spi_board_info imx6_sabresd_spi_nor_device[] __initdata = {  <-----+
    {               ^-----------------------------------------------------+      |
        .modalias = "ar1020-spi",                                         |      |
        .max_speed_hz = 50000, /* max spi clock (SCK) speed in HZ */      |      |
        .bus_num = 1,                                                     |      |
        .chip_select = 0,                                                 |      |
        .mode = SPI_MODE_1,                                               |      |
        .irq = gpio_to_irq(SABRESD_AR1020_INT),         ----------------+ |      |
                                                                        | |      |
        //.platform_data = &imx6_sabresd__spi_flash_data,               | |      |
    },                                                                  | |      |
              +---------------------------------------------------------|-+      |
};            |                                                         |        |
              V                                                         |        |
struct spi_board_info {                                                 |        |
    /* the device name and module name are coupled, like platform_bus;  |        |
     * "modalias" is normally the driver name.                          |        |
     *                                                                  |        |
     * platform_data goes to spi_device.dev.platform_data,              |        |
     * controller_data goes to spi_device.controller_data,              |        |
     * irq is copied too                                                |        |
     */                                                                 |        |
    char        modalias[SPI_NAME_SIZE];                                |        |
    const void    *platform_data;                                       |        |
    void        *controller_data;                                       |        |
    int        irq;                                                     |        |
                                                                        |        |
    /* slower signaling on noisy or low voltage boards */               |        |
    u32        max_speed_hz;                                            |        |
                                                                        |        |
                                                                        |        |
    /* bus_num is board specific and matches the bus_num of some        |        |
     * spi_master that will probably be registered later.               |        |
     *                                                                  |        |
     * chip_select reflects how this chip is wired to that master;      |        |
     * it's less than num_chipselect.                                   |        |
     */                                                                 |        |
    u16        bus_num;                                                 |        |
    u16        chip_select;                                             |        |
                                                                        |        |
    /* mode becomes spi_device.mode, and is essential for chips         |        |
     * where the default of SPI_CS_HIGH = 0 is wrong.                   |        |
     */                                                                 |        |
    u8        mode;                                                     |        |
                                                                        |        |
    /* ... may need additional spi_device chip config data here.        |        |
     * avoid stuff protocol drivers can set; but include stuff          |        |
     * needed to behave without being bound to a driver:                |        |
     *  - quirks like clock rate mattering when not selected            |        |
     */                                                                 |        |
};                                                                      |        |
                                                                        |        |
#define SABRESD_AR1020_INT    IMX_GPIO_NR(1, 17)           <------------+        |
                                                                                 |
int __init                                                                       |
spi_register_board_info(struct spi_board_info const *info, unsigned n)   <-------+
{
    struct boardinfo *bi;
    int i;

    bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);
    if (!bi)
        return -ENOMEM;

    for (i = 0; i < n; i++, bi++, info++) {
        struct spi_master *master;

        memcpy(&bi->board_info, info, sizeof(*info));
        mutex_lock(&board_lock);
        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);   -----+
        mutex_unlock(&board_lock);                                         |
    }                                                                      |
                                                                           |
    return 0;                                                              |
}                                                                          |
                                                                           |
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;                                                      |
    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);              |                                 |
                                                  |                                 |
struct bus_type spi_bus_type = {             <----+                                 |
    .name        = "spi",                                                           |
    .dev_attrs    = spi_dev_attrs,                                                  |
    .match        = spi_match_device,        ----------------------------------+    |
    .uevent        = spi_uevent,                                               |    |
    .pm        = &spi_pm,                                                      |    |
};                                                                             |    |
EXPORT_SYMBOL_GPL(spi_bus_type);                                               |    |
                                                                               |    |
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;                                                                   |
                                                                                    |
    if (sdrv->id_table)                                                             |
        return !!spi_match_id(sdrv->id_table, spi);     --------------------------+ |
                                                                                  | |
    return strcmp(spi->modalias, drv->name) == 0;                                 | |
}                                                                                 | |
                                                                                  | |
/**                                                                               | |
 * modalias support makes "modprobe $MODALIAS" new-style hotplug work,            | |
 * and the sysfs version makes coldplug work too.                                 | |
 */                                                                               | |
static const struct spi_device_id *spi_match_id(const struct spi_device_id *id, <-+ |
                        const struct spi_device *sdev)                              |
{                                                                                   |
    while (id->name[0]) {                                                           |
        if (!strcmp(sdev->modalias, id->name))           // this very important     |
            return id;                                   // for match driver name   |
        id++;                                                                       |
    }                                                                               |
    return NULL;                                                                    |
}                                                                                   |
                                                                                    |
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);

hacking ar1020-spi driver:

cat drivers/input/touchscreen/ar1020-spi.c
/* Enable the ar1020_spi_init() to be run by the kernel during initialization */
module_init(ar1020_spi_init);                          -----------------------+
                                                                              |
/* Enables the ar1020_spi_exit() to be called during cleanup.  This only      |
has an effect if the driver is compiled as a kernel module. */                |
module_exit(ar1020_spi_exit);                                                 |
                                                                              |
static int __init ar1020_spi_init(void)                <----------------------+
{
    int retval;

    printk("AR1020 SPI: ar1020_spi_init: begin\n");
    strcpy(receiveBuffer,"");
    strcpy(sendBuffer,"");

    /*
     * Creates a kobject "ar1020" that appears as a sub-directory
     * under "/sys/kernel".
     */
    ar1020_kobj = kobject_create_and_add("ar1020", kernel_kobj);
    if (!ar1020_kobj)
    {
        printk(KERN_ERR "AR1020 SPI: cannot create kobject\n");
        return -ENOMEM;
    }

    /* Create the files associated with this kobject */
    retval = sysfs_create_group(ar1020_kobj, &attr_group);
    if (retval)
    {
        printk(KERN_ERR "AR1020 SPI: error registering ar1020-spi driver's sysfs interface\n");
        kobject_put(ar1020_kobj);
    }

    return spi_register_driver(&ar1020_spi_driver);    --------+
}                                                              |
                                                               |
static struct spi_driver ar1020_spi_driver = {         <-------+
    .driver = {
        .name    = "ar1020-spi",
        .bus    = &spi_bus_type,                       -------------------------+
        .owner    = THIS_MODULE,                                                |
    },                                                                          |
    .probe        = ar1020_spi_probe,                  -------------------------*-+
    .remove        = ar1020_spi_remove,                                         | |
    /* suspend/resume functions not needed since controller automatically       | |
         put's itself to sleep mode after configurable short period of time */  | |
    .suspend    = NULL,                                                         | |
    .resume        = NULL,                                                      | |
};                                                                              | |
                                                                                | |
struct bus_type spi_bus_type = {                      <-------------------------+ |
    .name        = "spi",                                                         |
    .dev_attrs    = spi_dev_attrs,                                                |
    .match        = spi_match_device,                                             |
    .uevent        = spi_uevent,                                                  |
    .pm        = &spi_pm,                                                         |
};                                                                                |
EXPORT_SYMBOL_GPL(spi_bus_type);                                                  |
                                                                                  |
static int __devinit ar1020_spi_probe(struct spi_device *client)    <-------------+
{
    struct ar1020_spi_priv *priv=NULL;
    struct input_dev *input_dev=NULL;
    int err=0;
    int i;
    int j;
    char buff[5];
    int ret;

    printk("AR1020 SPI: ar1020_spi_probe: begin\n");

    for (i=0;i<5;i++)
    {
        buff[i]=0;
    }

    if (!client) {
        printk(KERN_ERR "AR1020 SPI: client pointer is NULL\n");
        err = -EINVAL;
        goto error;
    }

    if ((!client->irq) && (touchIRQ == -1) && (!testSPIdata) && (!probeForIRQ)) {
        printk(KERN_ERR "AR1020 SPI: no IRQ set for touch controller\n");
        err = -EINVAL;
        goto error;
    }

    priv = kzalloc(sizeof(struct ar1020_spi_priv), GFP_KERNEL);
    input_dev = input_allocate_device();
    if (!priv) {
        printk(KERN_ERR "AR1020 SPI: kzalloc error\n");
        err = -ENOMEM;
        goto error;
    }

    /* Backup pointer so sysfs helper functions may also have access to private data */
    privRef=priv;

    if (!input_dev)
    {
        printk(KERN_ERR "AR1020 SPI: input allocate error\n");
        err = -ENOMEM;
        goto error;
    }

    priv->client = client;
    priv->irq = client->irq;
    priv->input = input_dev;

    /* Verify raw SPI data stream to ensure bus is setup correctly in the platform settings. */
    if (testSPIdata)
    {
        printk("AR1020 SPI: In testing mode to verify packet.  To inhibit this mode,\n");
        printk("unset the \"testSPIdata\" kernel parameter.\n");
        while (1)
        {
            msleep(1);
            spi_read(priv->client,&buff[0],1);

                  if (!(0x80 & buff[0]))
            {
                if ((buff[0]!= 0x4d) && (buff[0]!=0x00))
                {
                    printk("0x%02x ",buff[0]);
                }
            }
            else
            {
                printk("\n0x%02x ",buff[0]);
                for (i=1;i<5;i++)
                {
                    spi_read(priv->client,&buff[i],1);
                    printk("0x%02x ",buff[i]);
                }
                printk("\n");
            }

        }

    }

    /* Detect IRQ id that controller IRQ line is attached to.  This detection only works
       if the controller's IRQ line is attached to a GPIO line configured as an input.
       These lines are often marked as EINT (external interrupt) on the board schematic.
       This probe assumes that SPI read communication with the controller is working
       correctly.
    */
    if (probeForIRQ)
    {
        printk("AR1020 SPI: Probing for interrupt id.\n");
        printk("AR1020 SPI: Please touch screen before IRQ probe for successful detection.\n");
        printk("AR1020 SPI: Probing will commence in five seconds.\n\n");
        printk("AR1020 SPI: Kernel exception messages may appear during the\n");
        printk("AR1020 SPI: probing process.\n");

        msleep(5000);
        for (i=probeMin;i<probeMax;i++)
        {
            printk("AR1020 SPI: Testing IRQ %d\n",i);
            priv->irq=i;

            /* set type on new handler and register gpio pin as our interrupt */
            //danny modify

            //set_irq_type(i, IRQ_TYPE_EDGE_RISING);
            //err = request_threaded_irq(client->irq, NULL,
            //      ft5x06_ts_interrupt, IRQF_TRIGGER_FALLING,
            //      client->dev.driver->name, data);

            // if (0 >= (ret=request_threaded_irq(i, NULL,
            // test_irq_handler_func,IRQF_TRIGGER_RISING, "AR1020 IRQ", priv)))
            if (0 >= (ret=request_irq(i, test_irq_handler_func,IRQF_TRIGGER_RISING, "AR1020 IRQ", priv)))
            {
                priv->testCount=0;

                /* read SPI data to ensure IRQ line is not asserted */
                for (j=0;j<5;j++)
                {
                  spi_read(priv->client,&buff[j],1);
                }

                msleep(1000);
                if (ret>=0)
                {
                    free_irq(i, priv);
                }

                /* successful detection if count within this range */
                if ((priv->testCount > 0) && (priv->testCount < 3))
                {
                    printk("AR1020 SPI: Touch IRQ detected at ID: %d.\n",i);
                    priv->irq=i;
                    break;
                }
            }
            else
            {
                printk("AR1020 SPI: IRQ %d not available.\n", i);
            }
        }
        if (i==probeMax)
        {
            printk("AR1020 SPI: Touch IRQ not detected. Using IRQ %d.\n",priv->irq);
        }

    }
    /* Use default settings */
    else if (touchIRQ == -1)
    {
        printk("AR1020 SPI: Using IRQ %d set via board's platform setting.\n", priv->irq);
    }
    else
    {
        printk("AR1020 SPI: Using IRQ %d set via kernel parameter\n", touchIRQ);
        priv->irq=touchIRQ;
    }

    INIT_WORK(&priv->work, ar1020_spi_readdata);           --------------------------------------+
                                                                                                 |
    input_dev->name = "AR1020 Touchscreen";                                                      |
                                                                                                 |
    input_dev->open = ar1020_spi_open;                                                           |
    input_dev->close = ar1020_spi_close;                                                         |
                                                                                                 |
    __set_bit(EV_KEY, input_dev->evbit);                                                         |
    __set_bit(EV_ABS, input_dev->evbit);                                                         |
    __set_bit(BTN_TOUCH, input_dev->keybit);                                                     |
    __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);                                            |
    //-------------add for virtualkeys                                                           |
    set_bit(EV_SYN, input_dev->evbit);                                                           |
    set_bit(KEY_HOME, input_dev->keybit);                                                        |
    //set_bit(KEY_SEARCH, input_dev->keybit);                                                    |
    set_bit(KEY_BACK, input_dev->keybit);                                                        |
    set_bit(KEY_MENU, input_dev->keybit);                                                        |
                                                                                                 |
    /*                                                                                           |
    // input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);                                |
    // input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);                             |
                                                                                                 |
    input_set_abs_params(input_dev, ABS_X, 0, 4095, 0, 0);                                       |
    input_set_abs_params(input_dev, ABS_Y, 0, 4095, 0, 0);                                       |
    input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0x7f, 0, 0);                                |
    */                                                                                           |
    input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,                                        |
                                 4095, 0, 0);                                                    |
    input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,                                        |
                                 4095, 0, 0);                                                    |
    input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,                                       |
                                 1, 0, 0);                                                       |
    input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0X7F, 0, 0);                          |
    input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0x7f, 0, 0);                             |
                                                                                                 |
    err = input_register_device(input_dev);                                                      |
    if (err)                                                                                     |
    {                                                                                            |
        printk(KERN_ERR "AR1020 SPI: error registering input device\n");                         |
        goto error;                                                                              |
    }                                                                                            |
                                                                                                 |
    for (i=0;i<5;i++)                                                                            |
    {                                                                                            |
      spi_read(priv->client,&buff[i],1);                                                         |
    }                                                                                            |
                                                                                                 |
    /* set type and register gpio pin as our interrupt */                                        |
    //danny modify                                                                               |
    //err = request_threaded_irq(client->irq, NULL,                                              |
    //      ft5x06_ts_interrupt, IRQF_TRIGGER_FALLING,                                           |
    //      client->dev.driver->name, data);                                                     |
    //set_irq_type(priv->irq, IRQ_TYPE_EDGE_RISING);                                             |
    request_irq(priv->irq, touch_irq_handler_func, IRQF_TRIGGER_RISING, "AR1020 SPI IRQ", priv); |
    // request_threaded_irq(priv->irq, NULL,  ^----------------------------------+               |
    // touch_irq_handler_func, IRQF_TRIGGER_RISING,                              |               |
    // "AR1020 SPI IRQ", priv);                                                  |               |
    printk("zengjf AR1020 SPI probe over.\n");                                   |               |
                                                                                 |               |
    return 0;                                                                    |               |
                                                                                 |               |
 error:                                                                          |               |
                                                                                 |               |
    if (input_dev)                                                               |               |
        input_free_device(input_dev);                                            |               |
                                                                                 |               |
    if (priv)                                                                    |               |
        kfree(priv);                                                             |               |
                                                                                 |               |
    return err;                                                                  |               |
                                                                                 |               |
    return 0;                                                                    |               |
}                                                                                |               |
                                                                                 |               |
static irqreturn_t touch_irq_handler_func(int irq, void *dev_id)    <------------+               |
{                                                                                                |
    struct ar1020_spi_priv *priv = (struct ar1020_spi_priv *)dev_id;                             |
    char buff[5];                                                                                |
    int i;                                                                                       |
    int err;                                                                                     |
    for (i=0;i<5;i++)                                                                            |
    {                                                                                            |
        buff[i]=0;                                                                               |
    }                                                                                            |
                                                                                                 |
    printk("<danny debug> ar1020 interrupt up\n");                                               |
    if (!priv) {                                                                                 |
        printk(KERN_ERR "AR1020 SPI: touch_irq_handler_funct: no private data\n");               |
        err = -EINVAL;                                                                           |
        return err;                                                                              |
    }                                                                                            |
                                                                                                 |
     /* delegate SPI transactions since hardware interupts need to be handled very fast */       |
    schedule_work(&priv->work);                                                                  |
                                                                                                 |
    return IRQ_HANDLED;                                                                          |
}                                                                                                |
                                                                                                 |
static void ar1020_spi_readdata(struct work_struct *work)          <-----------------------------+
{
    struct ar1020_spi_priv *priv =
        container_of(work, struct ar1020_spi_priv, work);
    int index=0;
    char buff[9];
    int ret;
    int i;

    /* We want to ensure we only read packets when we are not in the middle of command communication.
       Disable command mode after receiving command response to resume receiving packets. */
    if (commandMode)
    {
        commandDataPending=1;
        /* process up to 9 bytes */
        strcpy(receiveBuffer,"");

        /* header byte */
        spi_read(priv->client,&buff[0],1);
        snprintf(receiveBuffer,sizeof(receiveBuffer),"0x%02x",0xff&buff[0]);

        if (0x55 != buff[0])
        {
            printk("AR1020 SPI: invalid header byte\n");
            return;
        }

        /* num data bytes */
        spi_read(priv->client,&buff[1],1);
        snprintf(receiveBuffer,sizeof(receiveBuffer),"%s 0x%02x",receiveBuffer,0xff&buff[1]);
        if (buff[1] > 6)
        {
            printk("AR1020 SPI: invalid byte count\n");
            return;
        }

        for (i=0;i<buff[1];i++)
        {
            spi_read(priv->client,&buff[i+2],1);
            snprintf(receiveBuffer,sizeof(receiveBuffer),"%s 0x%02x",receiveBuffer,0xff&buff[i+2]);
        }
        snprintf(receiveBuffer,sizeof(receiveBuffer),"%s\n",receiveBuffer);
        printk(KERN_DEBUG "AR1020 SPI: command response: %s",receiveBuffer);
        return;
    }

    for (i=0;i<5;i++)
    {
        buff[i]=0;
    }

    /* process up to 9 bytes */
    for (i=0;i<9;i++)
    {
        spi_read(priv->client,&buff[index],1);
        ret=decodeAR1020Packet(priv,buff, &index, buff[index]);
        /* if a one is returned, then we have a full packet */
        if (1==ret)
        {
          break;
        }
    }
}

 

时间: 2024-10-26 00:01:55

I.MX6 ar1020 SPI device driver hacking的相关文章

I.MX6 Linux I2C device&amp; driver hacking

/******************************************************************************************* * I.MX6 Linux I2C device& driver hacking * 声明: * 1. 本文主要是对Linux I2C驱动进行代码跟踪,主要是为了能够对I2C驱动框架有个全面的了解: * 2. 本文源代码来自myzr_android4_2_2_1_1_0.tar.bz2: * 3. 如果你有兴趣,

I.MX6 AD7606-4 device driver registe hacking

/********************************************************************** * I.MX6 AD7606-4 device driver registe hacking * 说明: * 看一下AD7606的驱动注册上是否存在一些问题. * * 2017-8-4 深圳 龙华樟坑村 曾剑锋 *********************************************************************/ /

I.MX6 PWM buzzer driver hacking with Demo test

/***************************************************************************** * I.MX6 PWM buzzer driver hacking with Demo test * 声明: * 1. I.MX6和OK335xS实现PWM驱动函数是不一样的: * 2. 通过分析PWM驱动,了解有哪些驱动函数可以用: * 3. 使用I.MX6提供的PWM函数,编写测试用例buzzer驱动: * 4. 使用C编写测试程序.

I.MX6 gpio-keys driver hacking

/**************************************************************************** * I.MX6 gpio-keys driver hacking * 说明: * 1. 本文解读gpio-keys驱动是如何注册,最终处理函数在哪里. * 2. 从最后生成的设备节点来看,我们直接可以通过操作该设备节点来来让系统 * 进行相关操作,譬如关机.挂起等操作. * * 2016-3-17 深圳 南山平山村 曾剑锋 *********

I.MX6 bq27441 driver hacking

/************************************************************************* * I.MX6 bq27441 driver hacking * 声明: * 本文主要是记录对电池计量芯片bq27441芯片驱动注册过程进行代码跟踪. * * 2016-2-19 深圳 南山平山村 曾剑锋 ************************************************************************

I.MX6 Ar8031 device register hacking

/***************************************************************************** * I.MX6 Ar8031 device register hacking * 声明: * 主要是为了知道网卡的注册流程,如果需要对网卡中的一些需求进行修改时,能够 * 能够快速的对需求进行分析.修改. * * 2015-8-15 雨 深圳 南山区平山村 曾剑锋 **************************************

linux SPI bus demo hacking

/********************************************************************** * linux SPI bus demo hacking * 说明: * 本文主要解析linux应用程序如何使用SPI总线和设备通信. * * 2016-3-28 深圳 南山平山村 曾剑锋 *********************************************************************/ // 参考文档: //

I.MX6 PHY fixup 调用流程 hacking

/********************************************************************************** * I.MX6 PHY fixup 调用流程 hacking * 说明: * 跟一下i.MX6中对PHY进行fixup的代码是如何被调用的. * * 2017-4-14 深圳 龙华民治樟坑村 曾剑锋 ******************************************************************

I.MX6 Linux Serial Baud Rate hacking

/******************************************************************************** * I.MX6 Linux Serial Baud Rate hacking * 声明: * 1. 本文的源代码来自:myzr_android4_2_2_1_1_0.tar.bz2: * 2. 本文的目的是为了知道I.MX6串口支持的波特率有哪些,最大是多大, * 并加以验证,因为IMX6DQRM_revC.pdf数据手册上说能达到5