OK335xS UART device registe hacking
/************************************************************************* * OK335xS UART device registe hacking * 声明: * 1. 本文是对OK335xS Linux内核中UART设备注册代码进行跟踪; * 2. 本人在文中采用函数调用线路图进行标记,方便阅读; * 3. 代码跟踪的用的是vim+ctags; * * 2015-6-30 晴 深圳 南山平山村 曾剑锋 ************************************************************************/ \\\\\\\\\\\-*- 目录 -*-///////// | 一、板级文件: | 二、跟踪am33xx_init_early: | 三、跟踪am335x_evm_init: \\\\\\\\\\\\\\\\\////////////// 一、板级文件: 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 MACHINE_START(AM335XIAEVM, "am335xiaevm") /* Maintainer: Texas Instruments */ .atag_offset = 0x100, .map_io = am335x_evm_map_io, .init_irq = ti81xx_init_irq, .init_early = am33xx_init_early, .timer = &omap3_am33xx_timer, .init_machine = am335x_evm_init, MACHINE_END 二、跟踪am33xx_init_early: 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(); | } | | int __init am33xx_hwmod_init(void) <---+ { return omap_hwmod_register(am33xx_hwmods); -------------+ } | | V | static __initdata struct omap_hwmod *am33xx_hwmods[] = { | ...... | /* uart class */ | &am33xx_uart1_hwmod, ----+ | &am33xx_uart2_hwmod, | | &am33xx_uart3_hwmod, | | &am33xx_uart4_hwmod, | | &am33xx_uart5_hwmod, | | &am33xx_uart6_hwmod, | | ...... | | } | | V | static struct omap_hwmod am33xx_uart1_hwmod = { | .name = "uart1", // 后续注册设备时会用到name | .class = &uart_class, ->----+ | .clkdm_name = "l4_wkup_clkdm", | | .mpu_irqs = am33xx_uart1_irqs, -*-------+ | .main_clk = "uart1_fck", | | | .sdma_reqs = uart1_edma_reqs, --*-------*----------+ | ...... | | | | }; | | | | V | | | static struct omap_hwmod_class uart_class = { | | | .name = "uart", | | | .sysc = &uart_sysc, | | | }; | | | | | | /* uart1 */ V | | static struct omap_hwmod_dma_info uart1_edma_reqs[] = { | | { .name = "tx", .dma_req = 26, }, | | { .name = "rx", .dma_req = 27, }, | | { .dma_req = -1 } +--------------+ | }; | | V | static struct omap_hwmod_irq_info am33xx_uart1_irqs[] = { | { .irq = 72 }, | { .irq = -1 } | }; | | 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; } 三、跟踪am335x_evm_init: 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); | /* 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"); | | printk("zengjf test code 1.\n"); | } | | void __init omap_serial_init(void) <---------------------------------+ { omap_serial_board_init(NULL); -------+ } +-----------------+ V void __init omap_serial_board_init(struct omap_uart_port_info *info) { struct omap_uart_state *uart; struct omap_board_data bdata; list_for_each_entry(uart, &uart_list, node) { bdata.id = uart->num; ^---------------------------------+ bdata.flags = 0; | bdata.pads = NULL; | bdata.pads_cnt = 0; | | if (cpu_is_omap44xx() || (cpu_is_omap34xx() && | !cpu_is_am33xx())) | omap_serial_fill_default_pads(&bdata); | | if (!info) | omap_serial_init_port(&bdata, NULL); -------------------*---+ else | | omap_serial_init_port(&bdata, &info[uart->num]); | | } | | } | | | | static int __init omap_serial_early_init(void) | | { | | do { | | ...... | | snprintf(oh_name, MAX_UART_HWMOD_NAME_LEN, | | "uart%d", num_uarts + 1); | | oh = omap_hwmod_lookup(oh_name); ----------------------+ | | if (!oh) | | | break; | | | | | | uart = kzalloc(sizeof(struct omap_uart_state), GFP_KERNEL);| | | if (WARN_ON(!uart)) | | | return -ENODEV; | | | | | | uart->oh = oh; | | | uart->num = num_uarts++; | | | list_add_tail(&uart->node, &uart_list); <------*--+ | ...... | | | } while (1); | | | | | | return 0; | | | } // 这里很重要 | | | core_initcall(omap_serial_early_init); <------*--+ | | | 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 void __init omap_serial_init_port(struct omap_board_data *bdata, struct omap_uart_port_info *info) { struct omap_uart_state *uart; struct omap_hwmod *oh; struct platform_device *pdev; void *pdata = NULL; u32 pdata_size = 0; char *name; struct omap_uart_port_info omap_up; if (WARN_ON(!bdata)) return; if (WARN_ON(bdata->id < 0)) return; if (WARN_ON(bdata->id >= num_uarts)) return; list_for_each_entry(uart, &uart_list, node) if (bdata->id == uart->num) break; if (!info) info = omap_serial_default_info; oh = uart->oh; name = DRIVER_NAME; omap_up.dma_enabled = info->dma_enabled; omap_up.uartclk = OMAP24XX_BASE_BAUD * 16; omap_up.flags = UPF_BOOT_AUTOCONF; omap_up.get_context_loss_count = omap_pm_get_dev_context_loss_count; omap_up.set_forceidle = omap_uart_set_forceidle; omap_up.set_noidle = omap_uart_set_noidle; omap_up.enable_wakeup = omap_uart_enable_wakeup; omap_up.dma_rx_buf_size = info->dma_rx_buf_size; omap_up.dma_rx_timeout = info->dma_rx_timeout; omap_up.dma_rx_poll_rate = info->dma_rx_poll_rate; omap_up.autosuspend_timeout = info->autosuspend_timeout; /* Enable the MDR1 Errata i202 for OMAP2430/3xxx/44xx */ if (!cpu_is_omap2420() && !cpu_is_ti816x()) omap_up.errata |= UART_ERRATA_i202_MDR1_ACCESS; /* Enable DMA Mode Force Idle Errata i291 for omap34xx/3630 */ if ((cpu_is_omap34xx() || cpu_is_omap3630()) && !cpu_is_am33xx()) omap_up.errata |= UART_ERRATA_i291_DMA_FORCEIDLE; pdata = &omap_up; pdata_size = sizeof(struct omap_uart_port_info); if (WARN_ON(!oh)) return; pdev = omap_device_build(name, uart->num, oh, pdata, pdata_size, ------+ NULL, 0, false); | WARN(IS_ERR(pdev), "Could not build omap_device for %s: %s.\n", | name, oh->name); | | if ((console_uart_id == bdata->id) && no_console_suspend) | omap_device_disable_idle_on_suspend(pdev); | | oh->mux = omap_hwmod_mux_init(bdata->pads, bdata->pads_cnt); | | uart->pdev = pdev; | | oh->dev_attr = uart; | | if (((cpu_is_omap34xx() || cpu_is_omap44xx()) && bdata->pads) | && !uart_debug) | device_init_wakeup(&pdev->dev, true); | } +------------------------------------------+ 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); | | } | | | | | | 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; | } | | 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); }