哈尔滨网站建设培训班,天津建设工程,wordpress右侧悬浮插件,西安做网站在移植内核的时候#xff0c;通常会遇到引脚复用#xff08;MUX#xff09;的配置问题。在现在的Linux内核中#xff0c;对于TI的ARM芯片#xff0c;早已经有了比较通用的MUX配置框架。这对于许多TI的芯片都是通用的#xff0c;这次看AM335X的代码顺手写一下分析#xf…在移植内核的时候通常会遇到引脚复用MUX的配置问题。在现在的Linux内核中对于TI的ARM芯片早已经有了比较通用的MUX配置框架。这对于许多TI的芯片都是通用的这次看AM335X的代码顺手写一下分析以备后用。一、硬件对于许多TI的芯片来说引脚复用的配置是在Control Module配置模块的寄存器里配置的这个和三星的CPU有点不同三星的一般在GPIO的寄存器中配置。所以当你需要配置这些寄存器的时候请到数据手册的Control Module的Pad Control Registers查找。 TI的CPU芯片手册有两种一种是datasheetDS数据手册较小只是大概介绍下芯片的结构另一种是Technical Reference ManualTRM:技术参考手册较大详细介绍芯片的各部分功能原理和寄存器定义。在开发过程中这两个手册都需要参考是互补的。 对于AM335X关于引脚复用的列表及模式号与功能的对应可以在数据手册中找到 2 Terminal Description 2.2 Ball Characteristics 关于引脚复用寄存器定义及各引脚相应寄存器的偏移可以在TRM中找到 9 Control Module 9.1 Control Module 9.1.3 Functional Description 9.1.3.2 Pad Control Registers 包含引脚复用寄存器定义 9.1.5 Registers 9.1.5.1 CONTROL_MODULE Registers 包含引脚相应寄存器的偏移 二、软件 由于TI的芯片构架类似对于Linux内核来说早就已经为这个做好了一个软件上的框架无论是在启动的初始化阶段还是在系统运行时都可以通过这个框架提供的接口函数配置芯片的MUX。下面就来简要的分析一下。 以AM335X为例相关代码位置arch/arm/mach-omap2 mux.hmux.cmux33xx.hmux33xx.cboard-am335xevm.c (还有一些用到了arch/arm/plat-omap/include/plat/omap_hwmod.h 其中他们的层次关系是 1重要的数据结构 /** * struct mux_partition - 包含分区相关信息 * name: 当前分区名 * flags: 本分区的特定标志 * phys: 物理地址 * size: 分区大小 * base: ioremap 映射过的虚拟地址 * muxmodes: 本分区mux节点链表头 * node: 分区链表头 */ struct omap_mux_partition { const char *name; u32 flags; u32 phys; u32 size; void __iomem *base; struct list_head muxmodes; struct list_head node; }; 这个数据结构中包含了芯片中几乎所有定义好的mux的数据它在mux数据初始化函数omap_mux_init中初始化并添加到全局mux_partitions链表中通过node成员。而其中的muxmodes是所有mux信息节点的链表头用来链接以下数据结构 /** * struct omap_mux_entry - mux信息节点 * mux: omap_mux结构体 * node: 链表节点 */ struct omap_mux_entry { struct omap_mux mux; struct list_head node; }; 而在以上数据结构中struct omap_mux是记录单个mux节点数据的结构体 /** * struct omap_mux - omap mux 寄存器偏移和值的数据 * reg_offset: 从Control Module寄存器基地址算起的mux寄存器偏移 * gpio: GPIO 编号 * muxnames: 引脚可用的信号模式字符串指针数组 * balls: 封装中可用的引脚 */ struct omap_mux { u16 reg_offset; u16 gpio; #ifdef CONFIG_OMAP_MUX char *muxnames[OMAP_MUX_NR_MODES]; #ifdef CONFIG_DEBUG_FS char *balls[OMAP_MUX_NR_SIDES]; #endif #endif }; 而struct mux_partition中muxmodes链表及其节点数据的初始化都是在omap_mux_init初始化函数中omap_mux_init_list(partition, superset);而struct omap_mux节点数据中信息是由mux33xx.h和mux33xx.c中提供的。你可以在mux33xx.c中看到一个巨大的struct omap_mux结构体数组初始化代码这个代码一看就明了。不同的芯片只需要根据芯片资料修改这个结构体就好了但是am33xx的这个结构体当前还不完善gpio的数据还都是0。值得一提的是其中用到了一个宏 #define _AM33XX_MUXENTRY(M0, g, m0, m1, m2, m3, m4, m5, m6, m7) \ { \ .reg_offset (AM33XX_CONTROL_PADCONF_##M0##_OFFSET), \ .gpio (g), \ .muxnames { m0, m1, m2, m3, m4, m5, m6, m7 }, \ } 这个宏使得这个结构体数组的初始化变得清晰明了。 以上的数据结构是在系统初始化的时候使用的在struct omap_mux_partition完成初始化后omap_mux_init初始化函数最后会根据不同的板子初始化部分mux寄存器omap_mux_init_signals(partition, board_mux);其中牵涉到了以下结构体 /** * struct omap_board_mux - 初始化mux寄存器的数据 * reg_offset: 从Control Module寄存器基地址算起的mux寄存器偏移 * mux_value: 希望设置的mux寄存器值 */ struct omap_board_mux { u16 reg_offset; u16 value; }; 在最上层的板级初始化文件board-am335xevm.c中会定义一个这样的结构体数组确定所要初始化的引脚复用寄存器交由omap_mux_init_signals(partition, board_mux);使用。例如 #ifdef CONFIG_OMAP_MUX static struct omap_board_mux board_mux[] __initdata { 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 }, }; #else #define board_mux NULL #endif 其中用到了一个宏 /* 如果引脚没有定义为输入拉动电阻将会被禁用 * 如果定义为输入所提供的标志位将确定拉动电阻的配置 */ #define AM33XX_MUX(mode0, mux_value) \ { \ .reg_offset (AM33XX_CONTROL_PADCONF_##mode0##_OFFSET), \ .value (((mux_value) AM33XX_INPUT_EN) ? (mux_value)\ : ((mux_value) | AM33XX_PULL_DISA)), \ } 注意_AM33XX_MUXENTRY和AM33XX_MUX这两个宏前者是用于struct omap_mux的后者是用于struct omap_board_mux的。 2重要的接口函数 /** * omap_mux_init - MUX初始化的私有函数请勿使用 * 由各板级特定的MUX初始化函数调用 */ int omap_mux_init(const char *name, u32 flags, u32 mux_pbase, u32 mux_size, struct omap_mux *superset, struct omap_mux *package_subset, struct omap_board_mux *board_mux, struct omap_ball *package_balls); 这个函数是内部用于初始化struct mux_partition的最总要的函数但是这个函数并不作为接口函数使用而是供各芯片初始化函数“*_mux_init”所使用的。比如AM33XX芯片 /** * am33xx_mux_init() - 用板级特定的设置来初始化MUX系统 * board_mux: 板级特定的MUX配置表 */ int __init am33xx_mux_init(struct omap_board_mux *board_subset) { return omap_mux_init(core, 0, AM33XX_CONTROL_PADCONF_MUX_PBASE, AM33XX_CONTROL_PADCONF_MUX_SIZE, am33xx_muxmodes, NULL, board_subset, NULL); } 有了已经初始化好的struct mux_partition结构体我们可以利用mux.h提供的许多函数方便的初始化各mux寄存器 /** * omap_mux_init_signal - 根据信号名字符串初始化一个引脚的mux * muxname: mode0_name.signal_name的格式的Mux名称 * val: mux寄存器值 */ int omap_mux_init_signal(const char *muxname, int val); /** * omap_mux_get() - 通过名字返回一个mux分区 * name: mux分区名 * */ struct omap_mux_partition *omap_mux_get(const char *name); /** * omap_mux_read() - 读取mux寄存器通过分区结构体指针和寄存器偏移值 * partition: Mux分区 * mux_offset: mux寄存器偏移 * */ u16 omap_mux_read(struct omap_mux_partition *p, u16 mux_offset); /** * omap_mux_write() - 写mux寄存器通过分区结构体指针和寄存器偏移值 * partition: Mux分区 * val: 新的mux寄存器值 * mux_offset: mux寄存器偏移 * * 这个函数仅有在非GPIO信号的动态复用需要 */ void omap_mux_write(struct omap_mux_partition *p, u16 val, u16 mux_offset); /** * omap_mux_write_array() - 写mux寄存器阵列 * partition: Mux分区 * board_mux: mux寄存器阵列 用MAP_MUX_TERMINATOR结尾 * * 这个函数仅有在非GPIO信号的动态复用需要 */ void omap_mux_write_array(struct omap_mux_partition *p, struct omap_board_mux *board_mux); 在代码比较完备的芯片中struct omap_mux中的gpio成员有被初始化过这样就可以使用以下接口函数 /** * omap_mux_init_gpio - 根据GPIO编号初始化一个信号引脚 * gpio: GPIO编号 * val: mux寄存器值 */ int omap_mux_init_gpio(int gpio, int val); /** * omap_mux_get_gpio() - 根据GPIO编号获取一个mux寄存器值 * gpio: GPIO编号 * */ u16 omap_mux_get_gpio(int gpio); /** * omap_mux_set_gpio() - 根据GPIO编号设定一个mux寄存器值 * val: 新的mux寄存器值 * gpio: GPIO编号 * */ void omap_mux_set_gpio(u16 val, int gpio); 但是am33xx的gpio成员当前还都是0所有这些函数没法使用。 此外在mux.h中还导出了其他的软件接口和数据结构这些在am33xx中没有使用有需要的时候再看。 在板级初始化代码比如board-am335xevm.c运行完芯片特定的MUX初始化函数am33xx_mux_init(board_mux);之后也可以在各子系统初始化时通过上面的接口函数修改配置MUX比如在am33xx中使用了自己封装的一个函数和结构体 /* 模块引脚复用结构体 */ struct pinmux_config { const char *string_name; /* 信号名格式化字符串“模式0字符串.目标模式字符串“ */ int val; /* 其他mux寄存器可选配置值 */ }; /* * pin_mux - 单个模块引脚复用结构体 * 其中定义了本模块所有引脚复用细节. */ 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); } 你可以在board-am335xevm.c中看到如下的代码 static struct pinmux_config d_can_ia_pin_mux[] { {uart0_rxd.d_can0_tx, OMAP_MUX_MODE2 | AM33XX_PULL_ENBL}, {uart0_txd.d_can0_rx, OMAP_MUX_MODE2 | AM33XX_PIN_INPUT_PULLUP}, {NULL, 0}, }; ...... static void d_can_init(int evm_id, int profile) { switch (evm_id) { case IND_AUT_MTR_EVM: if ((profile PROFILE_0) || (profile PROFILE_1)) { setup_pin_mux(d_can_ia_pin_mux); /* Instance Zero */ am33xx_d_can_init(0); } break; case GEN_PURP_EVM: if (profile PROFILE_1) { setup_pin_mux(d_can_gp_pin_mux); /* Instance One */ am33xx_d_can_init(1); } break; default: break; } } 三、使用注意 上面初始化过的结构体和接口函数的定义都是带有__init和“__initdata”的所以这些都只能在内核初始化代码中使用一旦系统初始化结束并进入了文件系统这些定义都会被free。所有它们不能在内核模块.ok中被调用否则你就等着Oops吧。因为一个芯片的引脚复用一般是硬件设计的时候定死的一般不可能在启动后更改。如果你是在要在模块中改变引脚复用配置你只能通过自己ioremap相关寄存器再修改它们来实现。