/************************************************************************ * 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 * 驱动初始化: * 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, --------+ .init_irq = ti81xx_init_irq, | .handle_irq = omap3_intc_handle_irq, | .timer = &omap3_am33xx_timer, | .init_machine = am335x_evm_init, | 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[] = { <----------+ /* l3 class */ &am33xx_l3_instr_hwmod, &am33xx_l3_main_hwmod, /* l3s class */ &am33xx_l3slow_hwmod, /* l4hs class */ &am33xx_l4_hs_hwmod, /* l4fw class */ &am33xx_l4fw_hwmod, /* l4ls class */ &am33xx_l4ls_hwmod, /* l4per class */ &am33xx_l4per_hwmod, /* l4wkup class */ &am33xx_l4wkup_hwmod, /* clkdiv32k class */ &am33xx_clkdiv32k_hwmod, /* mpu class */ &am33xx_mpu_hwmod, /* adc_tsc class */ &am33xx_adc_tsc_hwmod, /* aes class */ &am33xx_aes0_hwmod, /* cefuse class */ &am33xx_cefuse_hwmod, /* control class */ &am33xx_control_hwmod, /* dcan class */ &am33xx_dcan0_hwmod, &am33xx_dcan1_hwmod, /* debugss class */ &am33xx_debugss_hwmod, /* elm class */ &am33xx_elm_hwmod, /* emif_fw class */ &am33xx_emif_fw_hwmod, /* epwmss class */ &am33xx_ehrpwm0_hwmod, &am33xx_ehrpwm1_hwmod, &am33xx_ehrpwm2_hwmod, &am33xx_ecap0_hwmod, &am33xx_ecap1_hwmod, &am33xx_ecap2_hwmod, /* gpio class */ &am33xx_gpio0_hwmod, &am33xx_gpio1_hwmod, -----+ &am33xx_gpio2_hwmod, | &am33xx_gpio3_hwmod, | /* gpmc class */ | &am33xx_gpmc_hwmod, | /* i2c class */ | &am33xx_i2c1_hwmod, | &am33xx_i2c2_hwmod, | &am33xx_i2c3_hwmod, | /* ieee5000 class */ | &am33xx_ieee5000_hwmod, | /* mailbox class */ | &am33xx_mailbox_hwmod, | /* mcasp class */ | &am33xx_mcasp0_hwmod, | &am33xx_mcasp1_hwmod, | /* mmc class */ | &am33xx_mmc0_hwmod, | &am33xx_mmc1_hwmod, | &am33xx_mmc2_hwmod, | /* ocmcram class */ | &am33xx_ocmcram_hwmod, | /* ocpwp class */ | &am33xx_ocpwp_hwmod, | /* rtc class */ | &am33xx_rtc_hwmod, | /* sha0 class */ | &am33xx_sha0_hwmod, | /* smartreflex class */ | &am33xx_smartreflex0_hwmod, | &am33xx_smartreflex1_hwmod, | /* spi class */ | &am33xx_spi0_hwmod, | &am33xx_spi1_hwmod, | /* spinlock class */ | &am33xx_spinlock_hwmod, | /* uart class */ | &am33xx_uart1_hwmod, | &am33xx_uart2_hwmod, | &am33xx_uart3_hwmod, | &am33xx_uart4_hwmod, | &am33xx_uart5_hwmod, | &am33xx_uart6_hwmod, | /* timer class */ | &am33xx_timer0_hwmod, | &am33xx_timer1_hwmod, | &am33xx_timer2_hwmod, | &am33xx_timer3_hwmod, | &am33xx_timer4_hwmod, | &am33xx_timer5_hwmod, | &am33xx_timer6_hwmod, | &am33xx_timer7_hwmod, | /* wkup_m3 class */ | &am33xx_wkup_m3_hwmod, | /* wd_timer class */ | &am33xx_wd_timer1_hwmod, | /* usbss hwmod */ | &am33xx_usbss_hwmod, | /* cpgmac0 class */ | &am33xx_cpgmac0_hwmod, | /* mdio class */ | &am33xx_mdio_hwmod, | /* tptc class */ | &am33xx_tptc0_hwmod, | &am33xx_tptc1_hwmod, | &am33xx_tptc2_hwmod, | /* tpcc class */ | &am33xx_tpcc_hwmod, | /* LCDC class */ | &am33xx_lcdc_hwmod, | /* gfx/sgx */ | &am33xx_gfx_hwmod, | /* pruss */ | &am33xx_pruss_hwmod, | NULL, | }; | 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 -> GPIO3 */ V | 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, }; // 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); } } }
时间: 2024-10-11 01:28:05