在移植内核的时候,通常会遇到引脚复用(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_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相关寄存器再修改它们来实现。

Linux下TIomap芯片MUX配置相关推荐

  1. Linux下Tomcat的安装配置

    Linux下Tomcat的安装配置 一.下载安装对应的jdk,并配置Java环境. 官网下载地址: http://www.oracle.com/technetwork/java/javase/down ...

  2. linux下apache服务器的配置和管理(启动、重启、中断服务)

    2019独角兽企业重金招聘Python工程师标准>>> linux下apache服务器的配置和管理 一.两个重要目录: Apache有两个重要的目录: 1.配置目录/etc/http ...

  3. linux下pptpd的安装配置(×××)

    linux下pptpd的安装配置(×××)--单网卡×××实现 (本文部分内容摘自互联网,特此声明!)          环境介绍:Linux(redhat) 2.6.18 +PPP+PPTPD    ...

  4. Linux下使用Google Authenticator配置SSH登录动态验证码

    说明: 1.一般ssh登录服务器,只需要输入账号和密码. 2.本教程的目的:在账号和密码之间再增加一个 验证码,只有输入正确的验证码之后,再输入 密码才能登录.这样就增强了ssh登录的安全性. 3.账 ...

  5. Linux下Apache虚拟主机配置

    说明: Apache版本:httpd-2.2.31 Apache安装目录:/usr/local/apache 目的: 配置Apache虚拟主机 具体操作: 1.修改Apache主配置文件 cp /us ...

  6. linux i2c 读写函数,Linux下读写芯片的I2C寄存器

    要想在Linux下读写芯片的I2C寄存器,一般需要在Linux编写一份该芯片的I2C驱动,关于Linux下如何编写I2C驱动,前一篇文章<手把手教你写Linux I2C设备驱动>已经做了初 ...

  7. Linux下的NFS搭建配置

    Linux下的NFS搭建配置 一.NFS介绍 1.NFS简介 2.NFS特点 二.环境介绍 三.服务端配置 1.创建共享目录 2.检查NFS包是否安装 3.安装nfs相关包 4.编辑配置文件 5.重启 ...

  8. Linux下的samba服务配置详解

    Linux下的samba服务配置详解 一.Samba介绍 二.Samba工具及特性 三.搭建环境介绍 四.Samba配置步骤 1.服务端操作 2.在客户端操作 五.测试用户的权限情况 一.Samba介 ...

  9. Linux下NDK的安装配置

    Linux下NDK的安装配置 在编写android程序的时候如果需要复用一部分以前的c语言程序,可以考虑使用NDK.由于设计到c和c++编程,故强烈建议在Linux下安装上eclipse的CDT插件. ...

最新文章

  1. mysql版本不一致会导致uuid_MySQL性能优化和高可用架构建议
  2. 在公共区块链中通过加密保护数据
  3. 无需安装Oracle,直接使用PL/SQL的方法
  4. 二维数组求最小值_05-最大子矩形-最大值减去最小值小于或等于num的子数组数量...
  5. python样本不均衡_使用Python中的smote处理正负样本之间的不平衡,python,实现,失衡,问题...
  6. Palindromic Numbers LightOJ - 1205 数位dp 求回文数
  7. 《TensorFlow 2.0深度学习算法实战教材》学习笔记(七、Kears高层接口)
  8. DNS 系统解析过程概述
  9. 使用TASM时报错extra characters on line的解决办法
  10. 微信点餐小程序怎么做(微信点餐小程序制作方法)
  11. SpringBoot个人学习
  12. Vue3和Vue2组件单元素的过渡
  13. Pytorch:VGG16
  14. windows 2012下安装.NET框架时出现组件的文件跟组件清单中的验证信息不匹配,无法安装
  15. [量子客]4月全球量子计算发展内参
  16. 设计n位乘加器(先乘后加)Design a n-bit multiplier (firstly multiply and then add)
  17. Linux 系列(一) - 基本命令
  18. Linux常用环境软件安装(提供对应安装包)
  19. 从华为进军安防解读未来安防行业发展趋势
  20. 为什么Int类型的取值范围是2的31次方减1

热门文章

  1. 2018年无人机行业发展环境与趋势分析 成为智慧化物流的关键突破口
  2. Android移动开发-VR全景照片简单实现
  3. Linux-禁止某些IP访问
  4. hdu 3576 Elevators in Jiayuan Students' Apartment
  5. 黄淮学院CSDN高校俱乐部与加拿大East-Link高管座谈会
  6. 梦里飞花逐流水[转载自公司内网,很喜欢的辞藻]
  7. python颜色风格,可视化库seaborn的风格和颜色以及热度图
  8. 【Java爬虫】学爬虫从简单的开始,无门槛小白都能学会,带你爬取豆瓣电影Top250
  9. 《孩子:挑战》儿童心理学奠基之作摘录分享(三)
  10. logback输出json格式日志并带traceId