am335x gpio分析
/************************************************************************ * am335x_gpio * 本文主要记录am335x gpio初始化过程,包括设置引脚复用寄存器,驱动注册。 * 主要文件: * 设备初始化: * arch/arm/mach-omap2/board_am335xevm.c * arch/arm/mach-omap2/io.c * arch/arm/mach-omap2/omap_hwmod_33xx_data.c
* arch/arm/mach-omap2/mux.c * 驱动初始化: * drivers/gpio/gpio-omap.c * * Tony Liu, 2016-4-30, Shenzhen ***********************************************************************/ //设备注册和初始化 arch/arm/mach-omap2/board_am335xevm.c MACHINE_START(AM335XEVM, "am335xevm") /* Maintainer: Texas Instruments */ .atag_offset = 0x100, .map_io = am335x_evm_map_io, .init_early = am33xx_init_early, --------+ //GPIO寄存器地址指定 .init_irq = ti81xx_init_irq, | .handle_irq = omap3_intc_handle_irq, | .timer = &omap3_am33xx_timer, | .init_machine = am335x_evm_init, --------|------------------------+ //GPIO引脚复用配置 MACHINE_END | | | | kernel/arch/arm/mach-omap2/io.c <--------+ | void __init am33xx_init_early(void) | { | omap2_set_globals_am33xx(); | omap3xxx_check_revision(); | am33xx_check_features(); | omap_common_init_early(); | am33xx_voltagedomains_init(); | omap44xx_prminst_init(); | am33xx_powerdomains_init(); | omap44xx_cminst_init(); | am33xx_clockdomains_init(); | am33xx_hwmod_init(); ---------+ | omap_hwmod_init_postsetup(); | | omap3xxx_clk_init(); | | } | | | | kernel/arch/arm/mach-omap2/omap_hwmod_33xx_data.c | | int __init am33xx_hwmod_init(void) <-------+ | { | return omap_hwmod_register(am33xx_hwmods); ---------------------------+ | } | | //将寄存器的信息添加到链表中 | | int __init omap_hwmod_register(struct omap_hwmod **ohs) | | { | | int r, i; | | | | if (!ohs) | | return 0; | | | | i = 0; | | do { | | r = _register(ohs[i]); -----------------+ | | WARN(r, "omap_hwmod: %s: _register returned %d\n", ohs[i]->name, | | | r); | | | } while (ohs[++i]); | | | | | | return 0; | | | } | | | | | | static int __init _register(struct omap_hwmod *oh) <----------------+ | | { | | int ms_id; | | | | if (!oh || !oh->name || !oh->class || !oh->class->name || | | (oh->_state != _HWMOD_STATE_UNKNOWN)) | | return -EINVAL; | | | | pr_debug("omap_hwmod: %s: registering\n", oh->name); | | //查找这个结构是否已经添加到双链中 | | if (_lookup(oh->name)) | | return -EEXIST; | | | | ms_id = _find_mpu_port_index(oh); --------------+ | | if (!IS_ERR_VALUE(ms_id)) | | | oh->_mpu_port_index = ms_id; | | | else | | | oh->_int_flags |= _HWMOD_NO_MPU_PORT; | | | | | | list_add_tail(&oh->node, &omap_hwmod_list); | | | | | | spin_lock_init(&oh->_lock); | | | | | | oh->_state = _HWMOD_STATE_REGISTERED; | | | | | | /* | | | * XXX Rather than doing a strcmp(), this should test a flag | | | * set in the hwmod data, inserted by the autogenerator code. | | | */ | | | if (!strcmp(oh->name, MPU_INITIATOR_NAME)) | | | mpu_oh = oh; | | | | | | return 0; | | | } | | | | | | static int __init _find_mpu_port_index(struct omap_hwmod *oh) <----+ | | { | | int i; | | int found = 0; | | | | if (!oh || oh->slaves_cnt == 0) | | return -EINVAL; | | | | for (i = 0; i < oh->slaves_cnt; i++) { | | struct omap_hwmod_ocp_if *os = oh->slaves[i]; | | | | if (os->user & OCP_USER_MPU) { | | found = 1; | | break; | | } | | } | | | | if (found) | | pr_debug("omap_hwmod: %s: MPU OCP slave port ID %d\n", | | oh->name, i); | | else | | pr_debug("omap_hwmod: %s: no MPU OCP slave port found\n", | | oh->name); | | | | return (found) ? i : -EINVAL; | | } | | | | | | //gpio | | static __initdata struct omap_hwmod *am33xx_hwmods[] = { <----------+ | ...... | /* gpio class */ | &am33xx_gpio0_hwmod, | &am33xx_gpio1_hwmod, -----+ | &am33xx_gpio2_hwmod, | | &am33xx_gpio3_hwmod, | | ...... | | }; | | V | static struct omap_hwmod am33xx_gpio1_hwmod = { | .name = "gpio2", | .class = &am33xx_gpio_hwmod_class, ------------------+ | .clkdm_name = "l4ls_clkdm", | | .mpu_irqs = am33xx_gpio1_irqs, ----------------+ | | .main_clk = "gpio1_ick", | | | .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, | | | .prcm = { | | | .omap4 = { | | | .clkctrl_offs = AM33XX_CM_PER_GPIO1_CLKCTRL_OFFSET, | | | .modulemode = MODULEMODE_SWCTRL, | | | }, | | | }, | | | .opt_clks = gpio1_opt_clks, ----------+ | | | .opt_clks_cnt = ARRAY_SIZE(gpio1_opt_clks), | | | | .dev_attr = &gpio_dev_attr, ------+ | | | | .slaves = am33xx_gpio1_slaves, ------|---|--------| |-+ | .slaves_cnt = ARRAY_SIZE(am33xx_gpio1_slaves), | | | | | | }; | | | | | | | | | | | | | | | | | | static struct omap_gpio_dev_attr gpio_dev_attr = { <--+ | | | | | .bank_width = 32, | | | | | .dbck_flag = true, | | | | | }; | | | | | | | | | | static struct omap_hwmod_opt_clk gpio1_opt_clks[] = { <--+ | | | | { .role = "dbclk", .clk = "gpio1_dbclk" }, | | | | }; | | | | | | | | //指定irq中断号,和芯片手册中一样 | | | | | | | | static struct omap_hwmod_irq_info am33xx_gpio1_irqs[] = { <-----+ | | | { .irq = 98 }, | | | { .irq = -1 } | | | }; | | | | | | //所有的GPIO都有相同的am33xx_gpio_hwmod_class | | | static struct omap_hwmod_class am33xx_gpio_hwmod_class = { <-----+ | | .name = "gpio", | | .sysc = &am33xx_gpio_sysc, ----------+ | | .rev = 2, | | | }; | | | | | | static struct omap_hwmod_class_sysconfig am33xx_gpio_sysc = { <---+ | | .rev_offs = 0x0000, //GPIO_REVISION 寄存器 | | .sysc_offs = 0x0010, //GPIO_SYSCONFIG | | .syss_offs = 0x0114, //GPIO_SYSSTATUS | | .sysc_flags = (SYSC_HAS_AUTOIDLE | SYSC_HAS_ENAWAKEUP | | | SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET | | | SYSS_HAS_RESET_STATUS), | | .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | | | SIDLE_SMART_WKUP), | | .sysc_fields = &omap_hwmod_sysc_type1, ---+ | | }; | | | V | | struct omap_hwmod_sysc_fields omap_hwmod_sysc_type1 = { | | .midle_shift = SYSC_TYPE1_MIDLEMODE_SHIFT, | | .clkact_shift = SYSC_TYPE1_CLOCKACTIVITY_SHIFT, | | .sidle_shift = SYSC_TYPE1_SIDLEMODE_SHIFT, | | .enwkup_shift = SYSC_TYPE1_ENAWAKEUP_SHIFT, | | .srst_shift = SYSC_TYPE1_SOFTRESET_SHIFT, | | .autoidle_shift = SYSC_TYPE1_AUTOIDLE_SHIFT, | | }; | | | | static struct omap_hwmod_ocp_if *am33xx_gpio1_slaves[] = { <------+ | &am33xx_l4_per__gpio1, ----+ | }; | | V | static struct omap_hwmod_ocp_if am33xx_l4_per__gpio1 = { | .master = &am33xx_l4per_hwmod, ---------+ | .slave = &am33xx_gpio1_hwmod, | | .clk = "l4ls_gclk", | | .addr = am33xx_gpio1_addrs, | | .user = OCP_USER_MPU | OCP_USER_SDMA, | | }; | | | | static struct omap_hwmod am33xx_l4per_hwmod = { <--+ | .name = "l4_per", | .class = &l4_hwmod_class, | .clkdm_name = "l4ls_clkdm", | .flags = (HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET), | .masters = am33xx_l4_per_masters, -----------------+ | .masters_cnt = ARRAY_SIZE(am33xx_l4_per_masters), | | .slaves = am33xx_l4_per_slaves, ------------|--+ | .slaves_cnt = ARRAY_SIZE(am33xx_l4_per_slaves), | | | }; | | | | | | static struct omap_hwmod_ocp_if *am33xx_l4_per_masters[] = { <-+ | | &am33xx_l4_per__dcan0, | | &am33xx_l4_per__dcan1, | | &am33xx_l4_per__gpio1, | | &am33xx_l4_per__gpio2, ------+ | | &am33xx_l4_per__gpio3, | | | }; | | | V | | static struct omap_hwmod_ocp_if am33xx_l4_per__gpio2 = { | | .master = &am33xx_l4per_hwmod, | | .slave = &am33xx_gpio2_hwmod, | | .clk = "l4ls_gclk", | | .addr = am33xx_gpio2_addrs, ----------+ | | .user = OCP_USER_MPU | OCP_USER_SDMA, | | | }; | | | | | | /* L4 PER -> GPIO2 */ V | | //GPIO2对应的寄存器地址 | | static struct omap_hwmod_addr_space am33xx_gpio2_addrs[] = { | | { | | .pa_start = 0x481AC000, | | .pa_end = 0x481AC000 + SZ_4K - 1, | | .flags = ADDR_TYPE_RT, | | }, | | { } | | }; | | | | /* Slave interfaces on the L4_PER interconnect */ | | static struct omap_hwmod_ocp_if *am33xx_l4_per_slaves[] = { <--+ | &am33xx_l3_slow__l4_per, -----+ | }; | | V | static struct omap_hwmod_ocp_if am33xx_l3_slow__l4_per = { | .master = &am33xx_l3slow_hwmod, | .slave = &am33xx_l4per_hwmod, | .user = OCP_USER_MPU, | }; | | //GPIO引脚复用配置 | static void __init am335x_evm_init(void) <--------------------+ { am33xx_cpuidle_init(); am33xx_mux_init(board_mux); omap_serial_init(); am335x_evm_i2c_init(); omap_sdrc_init(NULL, NULL); usb_musb_init(&musb_board_data); omap_board_config = am335x_evm_config; omap_board_config_size = ARRAY_SIZE(am335x_evm_config); daughter_brd_detected = false; //自己定义的函数 setup_xxx_xxxx(); --------------+ | /*create /proc/boardname to export info to userspace*/ | proc_init(); | | /* Create an alias for icss clock */ | if (clk_add_alias("pruss", NULL, "pruss_uart_gclk", NULL)) | pr_warn("failed to create an alias: icss_uart_gclk --> pruss\n"); | /* Create an alias for gfx/sgx clock */ | if (clk_add_alias("sgx_ck", NULL, "gfx_fclk", NULL)) | pr_warn("failed to create an alias: gfx_fclk --> sgx_ck\n"); | } | | static void setup_xxx_xxxx(void) <-------------+ { /*which doesn't have Write Protect pin LAN8710A_PHY_ID */ am335x_mmc[0].gpio_wp = -EINVAL; int ret; +-----------------------------------------+ //配置设备所有引脚复用 | | _configure_device(EVM_SK, xxx_xxxx_dev_cfg, PROFILE_NONE); ----------+ | //phy配置 | | am33xx_cpsw_init(AM33XX_CPSW_MODE_MII, xxx_xxxx_phy1, xxx_xxxx_phy2); | | phy_register_fixup_for_uid(LAN8710A_PHY_ID , AM335X_EVM_PHY_MASK, | | am33xx_evm_tx_clk_dly_phy_fixup); | | } | | | | static void _configure_device(int evm_id, struct evm_dev_cfg *dev_cfg, <---+ | int profile) | { | int i; | | am335x_evm_set_id(evm_id); | | //循环调用结构体数组的初始化函数,设置引脚复用 | if (profile == PROFILE_NONE) { | for (i = 0; dev_cfg->device_init != NULL; dev_cfg++) { | if (dev_cfg->device_on == DEV_ON_BASEBOARD) | dev_cfg->device_init(evm_id, profile); | else if (daughter_brd_detected == true) | dev_cfg->device_init(evm_id, profile); | } | } else { | for (i = 0; dev_cfg->device_init != NULL; dev_cfg++) { | if (dev_cfg->profile & profile) { | if (dev_cfg->device_on == DEV_ON_BASEBOARD) | dev_cfg->device_init(evm_id, profile); | else if (daughter_brd_detected == true) | dev_cfg->device_init(evm_id, profile); | } | } | } | } | | static struct evm_dev_cfg xxx_xxxx_dev_cfg[] = { <------------+ ...... {gpio_keys_init_forlinx, DEV_ON_BASEBOARD, PROFILE_ALL}, {gpio_led_init, DEV_ON_BASEBOARD, PROFILE_ALL}, ---------+ ...... | {NULL, 0, 0}, | }; | | static void gpio_led_init(int evm_id, int profile) <-------+ { int err; +--------------------------------+ | | setup_pin_mux(gpio_led_mux); --------------|----------+ err = platform_device_register(&leds_gpio); --------|----------|------------------+ if (err) | | | pr_err("failed to register gpio led device\n"); | | | } | | | | | | static struct pinmux_config gpio_led_mux[] = { <-----+ | | {"gpmc_a0.gpio1_16", OMAP_MUX_MODE7 | AM33XX_PIN_INPUT}, | | {"gpmc_a1.gpio1_17", OMAP_MUX_MODE7 | AM33XX_PIN_INPUT}, | | {"gpmc_a2.gpio1_18", OMAP_MUX_MODE7 | AM33XX_PIN_INPUT}, | | {"gpmc_a3.gpio1_19", OMAP_MUX_MODE7 | AM33XX_PIN_INPUT}, | | {"emu1.gpio3_8", OMAP_MUX_MODE7 | AM33XX_PIN_INPUT}, | | {NULL, 0}, | | }; | | | | static void setup_pin_mux(struct pinmux_config *pin_mux) <-------+ | { | int i; | //初始化 | for (i = 0; pin_mux->string_name != NULL; pin_mux++) | omap_mux_init_signal(pin_mux->string_name, pin_mux->val); ---+ | | | } | | | | arch/arm/mach-omap2/mux.c | | int __init omap_mux_init_signal(const char *muxname, int val) <---+ | { | struct omap_mux_partition *partition = NULL; | struct omap_mux *mux = NULL; | u16 old_mode; | int mux_mode; | //确认muxname字符窜中配置的模式是否存在 | mux_mode = omap_mux_get_by_name(muxname, &partition, &mux); ----+ | if (mux_mode < 0) | | return mux_mode; | | //读取旧的模式 | | old_mode = omap_mux_read(partition, mux->reg_offset); -----|------------+ | mux_mode |= val; | | | pr_debug("%s: Setting signal %s 0x%04x -> 0x%04x\n", | | | __func__, muxname, old_mode, mux_mode); | | | //将模式和至写入寄存器中,配置引脚复用 | | | omap_mux_write(partition, mux_mode, mux->reg_offset); -----|------------|--+ | | | | | return 0; | | | | } | | | | | | | | arch/arm/mach-omap2/mux.c | | | | int omap_mux_get_by_name(const char *muxname, <-----+ | | | struct omap_mux_partition **found_partition, | | | struct omap_mux **found_mux) | | | { | | | struct omap_mux_partition *partition; +---------------------------+ | | | | | | | | list_for_each_entry(partition, &mux_partitions, node) { | | | | struct omap_mux *mux = NULL; | | | | int mux_mode = _omap_mux_get_by_name(partition, muxname, &mux); | -----+ | | | if (mux_mode < 0) | | | | | continue; | | | | | | | | | | *found_partition = partition; | | | | | *found_mux = mux; | | | | | | | | | | return mux_mode; | | | | | } | | | | | | | | | | return -ENODEV; | | | | | } | | | | | | | | | | static LIST_HEAD(mux_partitions); <------------------------+ | | | | | | | | | | | | static int __init _omap_mux_get_by_name(struct omap_mux_partition *partition, <-+ | | | const char *muxname, | | | struct omap_mux **found_mux) | | | { | | | struct omap_mux *mux = NULL; | | | struct omap_mux_entry *e; | | | const char *mode_name; | | | int found = 0, found_mode = 0, mode0_len = 0; | | | struct list_head *muxmodes = &partition->muxmodes; | | | //查找模块的名字,例如"gpmc_a0.gpio1_16",名字是 gpio1_16 | | | mode_name = strchr(muxname, '.'); | | | if (mode_name) { | | | mode0_len = strlen(muxname) - strlen(mode_name); | | | mode_name++; | | | } else { | | | mode_name = muxname; | | | } | | | | | | list_for_each_entry(e, muxmodes, node) { | | | char *m0_entry; | | | int i; | | | | | | mux = &e->mux; | | | m0_entry = mux->muxnames[0]; | | | //查找例如“gpmc_a0.gpio1_16"中,是否存在 gpmc_a0这个默认的模式0 | | | /* First check for full name in mode0.muxmode format */ | | | if (mode0_len && strncmp(muxname, m0_entry, mode0_len)) | | | continue; | | | /* 按照模式名称进行匹配,查找这个引脚是否有对应名称的模式 | | | * 例如“gpio1_16",如果存在,返回模式对应的index | | | */ | | | /* Then check for muxmode only */ | | | for (i = 0; i < OMAP_MUX_NR_MODES; i++) { | | | char *mode_cur = mux->muxnames[i]; | | | | | | if (!mode_cur) | | | continue; | | | | | | if (!strcmp(mode_name, mode_cur)) { | | | *found_mux = mux; | | | found++; | | | found_mode = i; | | | } | | | } | | | } | | | | | | if (found == 1) { | | | return found_mode; | | | } | | | | | | if (found > 1) { | | | pr_err("%s: Multiple signal paths (%i) for %s\n", __func__, | | | found, muxname); | | | return -EINVAL; | | | } | | | | | | pr_err("%s: Could not find signal %s\n", __func__, muxname); | | | | | | return -ENODEV; | | | } | | | | | | u16 omap_mux_read(struct omap_mux_partition *partition, u16 reg) <--------+ | | { | | if (partition->flags & OMAP_MUX_REG_8BIT) | | return __raw_readb(partition->base + reg); | | else | | return __raw_readw(partition->base + reg); | | } | | | | void omap_mux_write(struct omap_mux_partition *partition, u16 val, <-------+ | u16 reg) | { | if (partition->flags & OMAP_MUX_REG_8BIT) | __raw_writeb(val, partition->base + reg); | else | __raw_writew(val, partition->base + reg); | } | | | static struct platform_device leds_gpio = { <-------------------+ .name = "leds-gpio", .id = -1, .dev = { .platform_data = &gpio_led_info, }, | }; | | V static struct gpio_led_platform_data gpio_led_info = { .leds = gpio_leds, -------+ .num_leds = ARRAY_SIZE(gpio_leds), | }; | | static struct gpio_led gpio_leds[] = { <------+ { .name = "usr0", .gpio = GPIO_TO_PIN(1, 16), /* D1 */ .active_low = 1, }, { .name = "usr1", .gpio = GPIO_TO_PIN(1, 17), /* D2 */ .active_low = 1, }, { .name = "usr2", .gpio = GPIO_TO_PIN(1, 18), /* D3 */ .active_low = 1, }, { .name = "usr3", .gpio = GPIO_TO_PIN(1, 19), /* D4 */ .active_low = 1, }, { .name = "heartbeat", .gpio = GPIO_TO_PIN(3, 8), /* D4 */ .default_trigger = "heartbeat", }, }; 驱动注册 // linux/drivers/gpio/gpio-omap.c static int __init omap_gpio_drv_reg(void) { return platform_driver_register(&omap_gpio_driver); -----------+ } | postcore_initcall(omap_gpio_drv_reg); | | static int __init omap_gpio_sysinit(void) | { | mpuio_init(); | | #if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS) | if (cpu_is_omap16xx() || cpu_class_is_omap2()) | register_syscore_ops(&omap_gpio_syscore_ops); | #endif | | return 0; | } | | arch_initcall(omap_gpio_sysinit); | | | static struct platform_driver omap_gpio_driver = { <-----------+ .probe = omap_gpio_probe, -----+ .driver = { | .name = "omap_gpio", | }, | }; | V static int __devinit omap_gpio_probe(struct platform_device *pdev) { static int gpio_init_done; struct omap_gpio_platform_data *pdata; struct resource *res; int id; struct gpio_bank *bank; if (!pdev->dev.platform_data) return -EINVAL; pdata = pdev->dev.platform_data; if (!gpio_init_done) { int ret; ret = init_gpio_info(pdev); if (ret) return ret; } id = pdev->id; bank = &gpio_bank[id]; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (unlikely(!res)) { dev_err(&pdev->dev, "GPIO Bank %i Invalid IRQ resource\n", id); return -ENODEV; } bank->irq = res->start; bank->virtual_irq_start = pdata->virtual_irq_start; bank->method = pdata->bank_type; bank->dev = &pdev->dev; bank->dbck_flag = pdata->dbck_flag; bank->stride = pdata->bank_stride; bank->width = pdata->bank_width; bank->regs = pdata->regs; //将寄存器地址复制给bank->regs结构体 if (bank->regs->set_dataout && bank->regs->clr_dataout) bank->set_dataout = _set_gpio_dataout_reg; else bank->set_dataout = _set_gpio_dataout_mask; spin_lock_init(&bank->lock); /* Static mapping, never released */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (unlikely(!res)) { dev_err(&pdev->dev, "GPIO Bank %i Invalid mem resource\n", id); return -ENODEV; } bank->base = ioremap(res->start, resource_size(res)); if (!bank->base) { dev_err(&pdev->dev, "Could not ioremap gpio bank%i\n", id); return -ENOMEM; } pm_runtime_enable(bank->dev); pm_runtime_get_sync(bank->dev); omap_gpio_mod_init(bank, id); -----------------------------------+ omap_gpio_chip_init(bank); ------------------------------+ | omap_gpio_show_rev(bank); ---+ | | | | | if (!gpio_init_done) | | | gpio_init_done = 1; | | | | | | return 0; | | | } | | | //查看版本号并打印 V | | static void __init omap_gpio_show_rev(struct gpio_bank *bank) | | { | | static bool called; | | u32 rev; | | | | if (called || bank->regs->revision == USHRT_MAX) | | return; | | | | rev = __raw_readw(bank->base + bank->regs->revision); | | pr_info("OMAP GPIO hardware version %d.%d\n", | | (rev >> 4) & 0x0f, rev & 0x0f); | | | | called = true; | | } | | | | static void __devinit omap_gpio_chip_init(struct gpio_bank *bank) <-+ | { | int j; | static int gpio; | | bank->mod_usage = 0; | /* | * REVISIT eventually switch from OMAP-specific gpio structs | * over to the generic ones | */ | bank->chip.request = omap_gpio_request; | bank->chip.free = omap_gpio_free; | bank->chip.direction_input = gpio_input; ----------+ | bank->chip.get = gpio_get; | | bank->chip.direction_output = gpio_output; | | bank->chip.set_debounce = gpio_debounce; | | bank->chip.set = gpio_set; | | bank->chip.to_irq = gpio_2irq; | | if (bank_is_mpuio(bank)) { | | bank->chip.label = "mpuio"; | | #ifdef CONFIG_ARCH_OMAP16XX | | bank->chip.dev = &omap_mpuio_device.dev; | | #endif | | bank->chip.base = OMAP_MPUIO(0); | | } else { | | bank->chip.label = "gpio"; | | bank->chip.base = gpio; | | gpio += bank->width; | | } | | bank->chip.ngpio = bank->width; | | | | gpiochip_add(&bank->chip); | | | | for (j = bank->virtual_irq_start; | | j < bank->virtual_irq_start + bank->width; j++) { | | irq_set_lockdep_class(j, &gpio_lock_class); | | irq_set_chip_data(j, bank); | | if (bank_is_mpuio(bank)) { | | omap_mpuio_alloc_gc(bank, j, bank->width); | | } else { | | irq_set_chip(j, &gpio_irq_chip); | | irq_set_handler(j, handle_simple_irq); | | set_irq_flags(j, IRQF_VALID); | | } | | } | | irq_set_chained_handler(bank->irq, gpio_irq_handler); | | irq_set_handler_data(bank->irq, bank); | | } | | | | static int gpio_input(struct gpio_chip *chip, unsigned offset) <--+ | { | struct gpio_bank *bank; | unsigned long flags; | | bank = container_of(chip, struct gpio_bank, chip); | spin_lock_irqsave(&bank->lock, flags); | _set_gpio_direction(bank, offset, 1); --------+ | spin_unlock_irqrestore(&bank->lock, flags); | | return 0; | | } | | | | static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset, <-+ | int dir) | { | unsigned int base = GPIO_BASE(offset / 32); | unsigned int reg; | | reg = __raw_readl(base + GPIO_DIR); | if (dir) | reg |= 1 << (offset % 32); | else | reg &= ~(1 << (offset % 32)); | __raw_writel(reg, base + GPIO_DIR); | } | | static void omap_gpio_mod_init(struct gpio_bank *bank, int id) <-----+ { if (cpu_class_is_omap2()) { if (cpu_is_omap44xx() || cpu_is_am33xx()) { __raw_writel(0xffffffff, bank->base + OMAP4_GPIO_IRQSTATUSCLR0); __raw_writel(0x00000000, bank->base + OMAP4_GPIO_DEBOUNCENABLE); /* Initialize interface clk ungated, module enabled */ __raw_writel(0, bank->base + OMAP4_GPIO_CTRL); } else if (cpu_is_omap34xx()) { __raw_writel(0x00000000, bank->base + OMAP24XX_GPIO_IRQENABLE1); __raw_writel(0xffffffff, bank->base + OMAP24XX_GPIO_IRQSTATUS1); __raw_writel(0x00000000, bank->base + OMAP24XX_GPIO_DEBOUNCE_EN); /* Initialize interface clk ungated, module enabled */ __raw_writel(0, bank->base + OMAP24XX_GPIO_CTRL); } else if (cpu_is_omap24xx()) { static const u32 non_wakeup_gpios[] = { 0xe203ffc0, 0x08700040 }; if (id < ARRAY_SIZE(non_wakeup_gpios)) bank->non_wakeup_gpios = non_wakeup_gpios[id]; } } else if (cpu_class_is_omap1()) { if (bank_is_mpuio(bank)) __raw_writew(0xffff, bank->base + OMAP_MPUIO_GPIO_MASKIT / bank->stride); if (cpu_is_omap15xx() && bank->method == METHOD_GPIO_1510) { __raw_writew(0xffff, bank->base + OMAP1510_GPIO_INT_MASK); __raw_writew(0x0000, bank->base + OMAP1510_GPIO_INT_STATUS); } if (cpu_is_omap16xx() && bank->method == METHOD_GPIO_1610) { __raw_writew(0x0000, bank->base + OMAP1610_GPIO_IRQENABLE1); __raw_writew(0xffff, bank->base + OMAP1610_GPIO_IRQSTATUS1); __raw_writew(0x0014, bank->base + OMAP1610_GPIO_SYSCONFIG); /* * Enable system clock for GPIO module. * The CAM_CLK_CTRL *is* really the right place. */ omap_writel(omap_readl(ULPD_CAM_CLK_CTRL) | 0x04, ULPD_CAM_CLK_CTRL); } if (cpu_is_omap7xx() && bank->method == METHOD_GPIO_7XX) { __raw_writel(0xffffffff, bank->base + OMAP7XX_GPIO_INT_MASK); __raw_writel(0x00000000, bank->base + OMAP7XX_GPIO_INT_STATUS); } } }