5093|0

2057

帖子

0

TA的资源

五彩晶圆(初级)

楼主
 

TI Sitara AM335x系统之Linux下AM335X芯片 MUX 配置分析 [复制链接]

[url=]Linux下AM335X芯片 MUX 配置分析[/url](转载自http://blog.chinaunix.net/uid-20543672-id-3067021.html
在移植内核的时候,通常会遇到引脚复用(MUX)的配置问题。在现在的Linux内核中,对于TI的ARM芯片,早已经有了比较通用的MUX配置框架。这对于许多TI的芯片都是通用的,这次看AM335X的代码顺手写一下分析,以备后用。

一、硬件
    对于许多TI的芯片来说,引脚复用的配置是在Control Module(配置模块)的寄存器里配置的,(这个和三星的CPU有点不同,三星的一般在GPIO的寄存器中配置)。所以当你需要配置这些寄存器的时候,请到数据手册的Control Module的Pad Control Registers查找。

  • TI的CPU芯片手册有两种:
  • 一种是datasheet(DS:数据手册),较小,只是大概介绍下芯片的结构;
  • 另一种是Technical Reference Manual(TRM:技术参考手册),较大,详细介绍芯片的各部分功能原理和寄存器定义。
  • 在开发过程中,这两个手册都需要参考,是互补的。



对于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.h
  • mux.c
  • mux33xx.h
  • mux33xx.c
  • board-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_MUXENTRYAM33XX_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相关寄存器再修改它们来实现
点赞 关注(1)
 

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表