且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

OK335xS I2C device registe hacking

更新时间:2022-08-12 20:50:49

/***************************************************************************
 *                 OK335xS I2C device registe hacking
 *  声明:
 *     1. 本文是对OK335xS Linux内核中I2C设备注册代码进行跟踪;
 *     2. 本人在文中采用函数调用线路图进行标记,方便阅读;
 *     3. 代码跟踪的用的是vim+ctags;
 *                                    2015-7-1 晴 深圳 南山平山村 曾剑锋
 **************************************************************************/


MACHINE_START(AM335XEVM, "am335xevm")
    /* Maintainer: Texas Instruments */
    .atag_offset    = 0x100,
    .map_io     = am335x_evm_map_io,
    .init_early = am33xx_init_early,
    .init_irq   = ti81xx_init_irq,
    .handle_irq     = omap3_intc_handle_irq,
    .timer      = &omap3_am33xx_timer,
    .init_machine   = am335x_evm_init,     -------------+
MACHINE_END                                             |
                                                        |
static void __init am335x_evm_init(void)   <------------+
{                                                       
    ......                                              
    am33xx_mux_init(board_mux); --+ 
    omap_serial_init();           |
    am335x_evm_i2c_init();  ------*--------------------------------------+
    ......                        |                                      |
}                                 |                                      |
                                  V                                      |
static struct omap_board_mux board_mux[] __initdata = {                  |
    AM33XX_MUX(XDMA_EVENT_INTR0, OMAP_MUX_MODE3 | AM33XX_PIN_OUTPUT),--+ |
    AM33XX_MUX(I2C0_SDA, OMAP_MUX_MODE0 | AM33XX_SLEWCTRL_SLOW |       | |
            AM33XX_INPUT_EN | AM33XX_PIN_OUTPUT),                      | |
    AM33XX_MUX(I2C0_SCL, OMAP_MUX_MODE0 | AM33XX_SLEWCTRL_SLOW |       | |
            AM33XX_INPUT_EN | AM33XX_PIN_OUTPUT),                      | |
    { .reg_offset = OMAP_MUX_TERMINATOR },                             | |
};            +--------------------------------------------------------+ |
              V                                                          |
#define AM33XX_MUX(mode0, mux_value)                    \                |
{                                   \                                    |
    // 若果mode0 = I2C0_SDA;                                            |
    // AM33XX_CONTROL_PADCONF_I2C0_SDA_OFFSET    ----------------------+ |
    .reg_offset = (AM33XX_CONTROL_PADCONF_##mode0##_OFFSET),    \      | |
    .value      = (((mux_value) & AM33XX_INPUT_EN) ? (mux_value)\      | |
                : ((mux_value) | AM33XX_PULL_DISA)),    \              | |
}                                                                      | |
                                                                       | |
/**                                                                    | |
 * 参考文档:AM335x ARM Cortex-A8 Microprocessors (MPUs) Technical     | |
 *    Reference Manual (Rev. H).pdf                                    | |
 * 参考文档:Sitara AM335x ARM Cortex-A8 Microprocessors (MPUs)        | |
 *    (Rev. F).pdf                                                     | |
 *  ------------------------------------------------------------------ | |
 *  | Offset | Acronym       | Register Description | Section        | | |
 *  +--------+---------------+----------------------|----------------+ | |
 *  | 988h   | conf_i2c0_sda |                      | Section 9.3.51 | | |
 */ ------------------------------------------------------------------ | |
#define AM33XX_CONTROL_PADCONF_I2C0_SDA_OFFSET          0x0988   <-----+ |
                                                                         |
static void __init am335x_evm_i2c_init(void)                <------------+
{                                                            
    /* Initially assume General Purpose EVM Config */
    am335x_evm_id = GEN_PURP_EVM;

    evm_init_cpld();                         --------------+
                                                           |
    setup_pin_mux(pmic_irq_pin_mux);                       |
    omap_register_i2c_bus(1, 100, am335x_i2c0_boardinfo, --*--------+
                ARRAY_SIZE(am335x_i2c0_boardinfo));        |        |
}                                                          |        |
                                                           |        |
static void evm_init_cpld(void)            <---------------+        |
{                                                                   |
    i2c_add_driver(&cpld_reg_driver);                               |
}                              |                                    |
                               V                                    |
static struct i2c_driver cpld_reg_driver = {                        |
    .driver = {                                                     |
        .name   = "cpld_reg",                                       |
    },                                                              |
    .probe      = cpld_reg_probe,    ---+                           |
    .remove     = cpld_reg_remove,      |                           |
    .id_table   = cpld_reg_id,          |                           |
};                +---------------------+                           |
                  V                                                 |
static int cpld_reg_probe(struct i2c_client *client,                |
        const struct i2c_device_id *id)                             |
{                                                                   |
    cpld_client = client;                                           |
    return 0;                                                       |
}                                                 +-----------------+
                                                  V                 |
static struct i2c_board_info __initdata am335x_i2c0_boardinfo[] = { |
    {                                                               |
        /* Daughter Board EEPROM */                                 |
        I2C_BOARD_INFO("24c256", DAUG_BOARD_I2C_ADDR),              |
        .platform_data  = &am335x_daughter_board_eeprom_info,       |
    },                                                              |
    {                                                               |
        /* Baseboard board EEPROM */                                |
        I2C_BOARD_INFO("24c256", BASEBOARD_I2C_ADDR),               |
        .platform_data  = &am335x_baseboard_eeprom_info,            |
    },                                                              |
    {                                                               |
        I2C_BOARD_INFO("cpld_reg", 0x35),                           |
    },                                                              |
    {                                                               |
        I2C_BOARD_INFO("tlc59108", 0x40),                           |
    },                                                              |
    {                                                               |
        I2C_BOARD_INFO("tps65910", TPS65910_I2C_ID1),               |
        .platform_data  = &am335x_tps65910_info,                    |
    },                                                              |
    {                                                               |
        I2C_BOARD_INFO("tlv320aic3x", 0x1b),                        |
    },                                                              |
};                    +---------------------------------------------+
                      V
int __init omap_register_i2c_bus(int bus_id, u32 clkrate,
              struct i2c_board_info const *info,
              unsigned len)
{                
    int err;

    BUG_ON(bus_id < 1 || bus_id > omap_i2c_nr_ports());

    if (info) { 
        err = i2c_register_board_info(bus_id, info, len);    ---+
        if (err)                                                |
            return err;                                         |
    }                                                           |
                                                                |
    if (!i2c_pdata[bus_id - 1].clkrate)                         |
        i2c_pdata[bus_id - 1].clkrate = clkrate;                |
                                                                |
    i2c_pdata[bus_id - 1].clkrate &= ~OMAP_I2C_CMDLINE_SETUP;   |
                                                                |
    return omap_i2c_add_bus(bus_id);        --------------------*----------+
}                       +---------------------------------------+          |
                        V                                                  |
int __init i2c_register_board_info(int busnum,                             |
    struct i2c_board_info const *info, unsigned len)                       |
{                                                                          |
    int status;                                                            |
                                                                           |
    down_write(&__i2c_board_lock);                                         |
                                                                           |
    /* dynamic bus numbers will be assigned after the last static one */   |
    if (busnum >= __i2c_first_dynamic_bus_num)                             |
        __i2c_first_dynamic_bus_num = busnum + 1;                          |
                                                                           |
    for (status = 0; len; len--, info++) {                                 |
        struct i2c_devinfo  *devinfo;                                      |
                                                                           |
        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);                   |
        if (!devinfo) {                                                    |
            pr_debug("i2c-core: can't register boardinfo!\n");             |
            status = -ENOMEM;                                              |
            break;                                                         |
        }                                                                  |
                                                                           |
        devinfo->busnum = busnum;                                          |
        devinfo->board_info = *info;                                       |
        list_add_tail(&devinfo->list, &__i2c_board_list);                  |
    }                                                                      |
                                                                           |
    up_write(&__i2c_board_lock);                                           |
                                                                           |
    return status;                                                         |
}                                                                          |
                                                                           |
static int __init omap_i2c_add_bus(int bus_id)          <------------------+
{       
    if (cpu_class_is_omap1())
        return omap1_i2c_add_bus(bus_id);
    else    
        return omap2_i2c_add_bus(bus_id);      ----------+
}                                                        |
                                                         |
static inline int omap2_i2c_add_bus(int bus_id)   <------+
{
    int l;
    struct omap_hwmod *oh;
    struct platform_device *pdev;
    char oh_name[MAX_OMAP_I2C_HWMOD_NAME_LEN];
    struct omap_i2c_bus_platform_data *pdata;
    struct omap_i2c_dev_attr *dev_attr;

    if (!cpu_is_am33xx())
        omap2_i2c_mux_pins(bus_id);

    l = snprintf(oh_name, MAX_OMAP_I2C_HWMOD_NAME_LEN, "i2c%d", bus_id);
    WARN(l >= MAX_OMAP_I2C_HWMOD_NAME_LEN,
        "String buffer overflow in I2C%d device setup\n", bus_id);
    oh = omap_hwmod_lookup(oh_name);      ---------------------------------+
    if (!oh) {                                                             |
            pr_err("Could not look up %s\n", oh_name);                     |
            return -EEXIST;                                                |
    }                                                                      |
                                                                           |
    pdata = &i2c_pdata[bus_id - 1];                                        |
    /*                                                                     |
     * pass the hwmod class's CPU-specific knowledge of I2C IP revision in |
     * use, and functionality implementation flags, up to the OMAP I2C     |
     * driver via platform data                                            |
     */                                                                    |
    pdata->rev = oh->class->rev;                                           |
                                                                           |
    dev_attr = (struct omap_i2c_dev_attr *)oh->dev_attr;                   |
    pdata->flags = dev_attr->flags;                                        |
                                                                           |
    /*                                                                     |
     * When waiting for completion of a i2c transfer, we need to           |
     * set a wake up latency constraint for the MPU. This is to            |
     * ensure quick enough wakeup from idle, when transfer                 |
     * completes.                                                          |
     * Only omap3 has support for constraints                              |
     */                                                                    |
    if (cpu_is_omap34xx())                                                 |
        pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat;   |
                                                                           |
    pdata->device_reset = omap_device_reset;                               |
    pdev = omap_device_build(name, bus_id, oh, pdata,          ---------+  |
            sizeof(struct omap_i2c_bus_platform_data),                  |  |
            NULL, 0, 0);                                                |  |
    WARN(IS_ERR(pdev), "Could not build omap_device for %s\n", name);   |  |
                                                                        |  |
    return PTR_RET(pdev);                                               |  |
}                          +--------------------------------------------*--+
                           V                                            |
struct omap_hwmod *omap_hwmod_lookup(const char *name)                  |
{                                                                       |
    struct omap_hwmod *oh;                                              |
                                                                        |
    if (!name)                                                          |
        return NULL;                                                    |
                                                                        |
    oh = _lookup(name); -----+                                          |
                             |                                          |
    return oh;               |                                          |
}                            |                                          |
                             V                                          |
static struct omap_hwmod *_lookup(const char *name)                     |
{                                                                       |
    struct omap_hwmod *oh, *temp_oh;                                    |
                                                                        |
    oh = NULL;                                                          |
                                                                        |
    list_for_each_entry(temp_oh, &omap_hwmod_list, node) {              |
        if (!strcmp(name, temp_oh->name)) {                             |
            oh = temp_oh;                                               |
            break;                                                      |
        }                                                               |
    }                           +---------------------------------------+
                                |
    return oh;                  |
}                               |
                                V
struct platform_device *omap_device_build(const char *pdev_name, int pdev_id,
                      struct omap_hwmod *oh, void *pdata,
                      int pdata_len,
                      struct omap_device_pm_latency *pm_lats,
                      int pm_lats_cnt, int is_early_device)
{
    struct omap_hwmod *ohs[] = { oh };

    if (!oh)
        return ERR_PTR(-EINVAL);

    return omap_device_build_ss(pdev_name, pdev_id, ohs, 1, pdata,  ---+
                    pdata_len, pm_lats, pm_lats_cnt,                   |
                    is_early_device);                                  |
}                                  +-----------------------------------+
                                   V
struct platform_device *omap_device_build_ss(const char *pdev_name, int pdev_id,
                     struct omap_hwmod **ohs, int oh_cnt,
                     void *pdata, int pdata_len,
                     struct omap_device_pm_latency *pm_lats,
                     int pm_lats_cnt, int is_early_device)
{
    int ret = -ENOMEM;
    struct platform_device *pdev;
    struct omap_device *od;

    if (!ohs || oh_cnt == 0 || !pdev_name)
        return ERR_PTR(-EINVAL);

    if (!pdata && pdata_len > 0)
        return ERR_PTR(-EINVAL);

    pdev = platform_device_alloc(pdev_name, pdev_id);
    if (!pdev) {
        ret = -ENOMEM;
        goto odbs_exit;
    }

    /* Set the dev_name early to allow dev_xxx in omap_device_alloc */
    if (pdev->id != -1)
        dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
    else
        dev_set_name(&pdev->dev, "%s", pdev->name);

    od = omap_device_alloc(pdev, ohs, oh_cnt, pm_lats, pm_lats_cnt);
    if (!od)
        goto odbs_exit1;

    ret = platform_device_add_data(pdev, pdata, pdata_len);
    if (ret)
        goto odbs_exit2;

    if (is_early_device)
        ret = omap_early_device_register(pdev);  ---------------------+
    else                                                              |
        ret = omap_device_register(pdev);        ---------------------*--+
    if (ret)                                                          |  |
        goto odbs_exit2;                                              |  |
                                                                      |  |
    return pdev;                                                      |  |
                                                                      |  |
odbs_exit2:                                                           |  |
    omap_device_delete(od);                                           |  |
odbs_exit1:                                                           |  |
    platform_device_put(pdev);                                        |  |
odbs_exit:                                                            |  |
                                                                      |  |
    pr_err("omap_device: %s: build failed (%d)\n", pdev_name, ret);   |  |
                                                                      |  |
    return ERR_PTR(ret);                                              |  |
}                        +--------------------------------------------+  |
                         V                                               |
static int omap_early_device_register(struct platform_device *pdev)      |
{                                                                        |
    struct platform_device *devices[1];                                  |
                                                                         |
    devices[0] = pdev;                                                   |
    early_platform_add_devices(devices, 1);                              |
    return 0;                                                            |
}             +----------------------------------------------------------+
              V
int omap_device_register(struct platform_device *pdev)
{
    pr_debug("omap_device: %s: registering\n", pdev->name);

    pdev->dev.parent = &omap_device_parent;
    pdev->dev.pm_domain = &omap_device_pm_domain;
    return platform_device_add(pdev);
}