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 ; | |
| |
i = ; | |
do { | |
r = _register(ohs[i]); -----------------+ | |
WARN(r, "omap_hwmod: %s: _register returned %d\n", ohs[i]->name, | | |
r); | | |
} while (ohs[++i]); | | |
| | |
return ; | | |
} | | |
| | |
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 ; | | |
} | | |
| | |
static int __init _find_mpu_port_index(struct omap_hwmod *oh) <----+ | |
{ | |
int i; | |
int found = ; | |
| |
if (!oh || oh->slaves_cnt == ) | |
return -EINVAL; | |
| |
for (i = ; i < oh->slaves_cnt; i++) { | |
struct omap_hwmod_ocp_if *os = oh->slaves[i]; | |
| |
if (os->user & OCP_USER_MPU) { | |
found = ; | |
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 = , | | | | |
.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 = }, | | |
{ .irq = - } | | |
}; | | |
| | |
//所有的GPIO都有相同的am33xx_gpio_hwmod_class | | |
static struct omap_hwmod_class am33xx_gpio_hwmod_class = { <-----+ | |
.name = "gpio", | |
.sysc = &am33xx_gpio_sysc, ----------+ | |
.rev = , | | |
}; | | |
| | |
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 - , | |
.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[].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 = ; 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 = ; 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, , }, |
}; |
|
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, }, | |
}; | |
| |
static void setup_pin_mux(struct pinmux_config *pin_mux) <-------+ |
{ |
int i; |
//初始化 |
for (i = ; 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 < ) | |
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 ; | | | |
} | | | |
| | | |
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 < ) | | | | |
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 = , found_mode = , mode0_len = ; | | |
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[]; | | |
//查找例如“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 = ; 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 == ) { | | |
return found_mode; | | |
} | | |
| | |
if (found > ) { | | |
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 = -,
.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(, ), /* D1 */
.active_low = ,
},
{
.name = "usr1",
.gpio = GPIO_TO_PIN(, ), /* D2 */
.active_low = ,
},
{
.name = "usr2",
.gpio = GPIO_TO_PIN(, ), /* D3 */
.active_low = ,
},
{
.name = "usr3",
.gpio = GPIO_TO_PIN(, ), /* D4 */
.active_low = ,
},
{
.name = "heartbeat",
.gpio = GPIO_TO_PIN(, ), /* 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 ; |
} |
|
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, );
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, );
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 = ; | | |
| | |
return ; | | |
} | | |
//查看版本号并打印 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 >> ) & 0x0f, rev & 0x0f); | |
| |
called = true; | |
} | |
| |
static void __devinit omap_gpio_chip_init(struct gpio_bank *bank) <-+ |
{ |
int j; |
static int gpio; |
|
bank->mod_usage = ; |
/* |
* 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(); | |
} 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, ); --------+ |
spin_unlock_irqrestore(&bank->lock, flags); | |
return ; | |
} | |
| |
static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset, <-+ |
int dir) |
{ |
unsigned int base = GPIO_BASE(offset / ); |
unsigned int reg; |
|
reg = __raw_readl(base + GPIO_DIR); |
if (dir) |
reg |= << (offset % ); |
else |
reg &= ~( << (offset % )); |
__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(, 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(, 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);
}
}
}
am335x gpio分析的更多相关文章
- am335x uart分析
/************************************************************ * am335x uart分析 * 本文记录am335x uart驱动的注册 ...
- am335x i2c分析
/***************************************************************************** * am335x i2c分析 * i2c驱 ...
- am335x gpio 模拟 spi 驱动添加
kernel 内 make menuconfig // make menuconfig Device Drivers ---> [*] SPI support ---> <*> ...
- imx6 gpio分析
本文主要介绍如何配置IOMUX寄存器,设置IO复用寄存器,配置为GPIO功能.参考: http://www.jianshu.com/p/3c2053508342 http://www.embest-t ...
- am335x gpio 控制的另一种方法
#include <linux/gpio.h> #include <linux/module.h> #include <linux/kernel.h> #in ...
- am335x gpio控制
1.执行下面的命令,可以显示目前驱动已经申请到的IO状态 : $ mount -t debugfs debugfs /sys/kernel/debug $ cat /sys/kernel/debug ...
- am335x mux配置
/**************************************************************** * am335x mux配置 * * am335x的引脚复寄存器是C ...
- Android(Java)控制GPIO的方法及耗时分析
前面两篇分别介绍了通过脚本和C代码读写/sys/class/gpio以控制GPIO.实际项目调试时经常还需要在Java代码里控制GPIO,其实现与C代码类似,唯一不同是Android权限.本文重点介绍 ...
- Android(Linux)控制GPIO的方法及实时性分析
Linux下控制GPIO的方法有N种,详细请参考<RPi GPIO Code Samples>,文中用十多种语言演示了如何控制GPIO,非常全面详尽.因此,这里不再多做赘述,仅把调试过程中 ...
随机推荐
- mysql-5.7 innodb_buffer_pool刷新机制详解
一.innodb的脏页刷新机制说明: 1.当innodb中的脏页比例超过innodb_max_dirty_pages_pct_lwm的值时,这个时候innodb就会开始刷新脏页到磁盘. 2.当inno ...
- zookeeper集群的部署
因为这里zookeeper的集群部署都会2n+1台 Dubbo建议使用Zookeeper作为服务的注册中心. Zookeeper集群中只要有过半的节点是正常的情况下,那么整个集群对外就是可用的.正是基 ...
- rsync + inotify-tools实现文件的实时同步
文章摘自:http://lxw66.blog.51cto.com/5547576/1331048 rsync 帮助文档:http://man.linuxde.net/rsync 最近有个想法就是部署一 ...
- Servlet与WebService关系
转自:http://www.cnblogs.com/cy163/archive/2008/04/16/1155767.html 其实从实现的效果上,它们是很相似的 相同点: 客 ...
- Artificial-Intelligence BOOKs与算法
http://mindhacks.cn/2008/09/11/machine-learning-and-ai-resources/ https://www.amazon.com/Information ...
- C# winform 多线程异步操作线程启动暂停与恢复
/// <summary> /// 线程控制模块 /// </summary> private ManualResetEvent manualResetEvent = new ...
- linux 双显卡问题。。。
bumblebee的作用是禁用nvidia独立显卡,需要使用独显时,使用”optirun 程序名“手动开启nvidia来运行需要加速的程序,如optirun vmware. 打开N卡设置: optir ...
- css reset.css
/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 License: none (public domain) */ html, ...
- C++面向对象程序设计的一些知识点(2)
1.C++中三种继承方式及派生类中访问控制规则 (1).C++支持的三种继承方式是public.protected.private.C++允许一个类同时以不同的方式对不同的基类加以继承. (2). 不 ...
- Linux下crontab内环境变量与Shell环境变量的关系【转】
crontab,总是不会缺省的从用户profile文件中读取环境变量参数 经常导致在手工执行某个脚本时是成功的,但是到crontab中试图执行时就会报错. 解决办法如下: 方法一:在shell文件中获 ...