dts优缺点 linux

2020-04-05 19:01:34 weixin_42475720 阅读数 15

一. 字符设备驱动基础
1.字符设备驱动的几种不同写法
在学习设备树之前,首先了解一下字符设备驱动的三种写法,参考文档:字符设备驱动的三种写法
这篇文章很好的描述并对比了三种字符设备驱动的写法及各自的优缺点。

三种写法分别为:字符设备驱动的传统写法
总线设备驱动模型写法
以及接下来要介绍的dts设备树写法。

2.设备树基础

对于设备树的基本了解,可以参考以下几篇文档:

字符设备驱动-使用设备树

设备树的规范 - DTS格式

内核对设备树的处理

设备树基础

driver使用dts的前提是driver已经是支持platform框架的driver,这样dts才可以发挥作用,因为dts和platform是配合工作的。

step1:创建dts结构体

static struct of_device_id xx_of_match[] = {
	{.compatible = "xx_tech,devic_xx",},
	{},
};

step2:注册该driver的dts

MODULE_DEVICE_TABLE(of, ali_of_match);

step3:将该结构体加入platform成员

static struct platform_driver nim_platform_driver = {
	.probe    = xx_probe, 
	.remove   = xx_remove,
	.suspend  = xx_suspend,
	.resume   = xx_resume,
	.driver   = {
			.owner  = THIS_MODULE,
			.name   = "x_name", //这个name是用来和注册platform device的时候匹配用的
			.of_match_table = xx_of_match,
	},
};
step4:在dts文件中加入该节点
device_xx:device_xx1@0x18030000{
			compatible = "xx_tech,devic_xx";

二. 设备树匹配过程

从以上已经对设备树有了一个基本了解,那是否有一个疑惑:设备树描述的设备信添加链接描述息和对应的驱动程序是怎么匹配起来的呢?

它们的匹配是基于Linux的platform总线机制进行的,关于platform总线可以从以下几篇文章进行了解:

platform_device和platform_driver

Linux platform设备驱动原理及架构

Linux设备驱动模型之platform总线深入浅出(加入设备树)

基于platform总线的驱动分析

对platform总线有了了解后,再看下详细的匹配过程:

linux 驱动设备和 dts 匹配过程

Linux设备驱动——Platform设备与驱动match probe过程

了解完以上文章之后,你应该就对设备树有了基本的了解。

2017-12-16 09:40:05 jacobywu 阅读数 511

1. 前言

本文从regulator driver的角度,描述怎样基于regulator framework编写regulator驱动。同时,以此为契机,学习、理解regulator有关的物理特性,以便能够更好的使用它们。

2. regulator driver的实现步骤

2.1 确定系统中regulator有关的硬件组成

提起硬件,最好能有个例子,好在有device tree,一个活生生的硬件拓扑结构。这里以NVIDIA Tegra Dalmore A04开发板为例(regulator有关的device tree位于“arch\arm\boot\dts\tegra114-dalmore.dts”):

regulator hardware block这里的regulator结构是相当复杂的,其中彩色框代表最终的regulator抽象,它的前一级表示regulator的载体(可以是PMIC、CPU、等等)。下面将会详细说明:

a)CPU通过I2C controller,连接一个降压控制器(TI tps51632),该控制器输出名称为“vdd-cpu”的电压,就称作vdd-cpu regulator吧(因此,在kernel中,regulator是一个虚拟设备)。

b)CPU通过I2C controller,连接一个前端电源管理芯片(TI tps65090),该芯片除了具备充电管理功能外,内置了多个regulator,例如dcdc1、dcdc2等等。

c)CPU通过I2C controller,连接另一个电源管理芯片(TI tps65913),该芯片具有两个功能:GPIO输出和PMIC。PMIC内置了多个regulator,如vddio-ddr、vdd-core等等。

d)CPU内部也集成了一些regulator,如vdd_ac_bat等等。

注1:单纯从硬件的角度看,是不存在图中"regulators“、PMIC等实体的,它们的出现,已经包含了软件设计的思路。之所以画在这里,是方便后面的描述。

2.2 使用DTS,将硬件拓扑呈现出来

我们都知道,DTS的功能是描述设备的拓扑结构,并在系统初始化的时候,为被描述的设备创建并注册对应的platform device,最终和相应的platform driver相遇,执行其probe接口,实现设备的枚举功能。但是,在这些基本原则之外,还需要一些更深的思考:

DTS节点(node)怎么和设备对应?是以设备的“物理界限”为单位,还是以设备的“功能”为单位?

是不是所有的“设备”都应该在kernel中创建一个platform device?如果不是,创建的依据是什么?

这些思考在本文的例子(NVIDIA Tegra Dalmore A04的regulator)中体现尤为突出,它的本质是软件设计中的模块划分,从而决定了regulator在DTS中的呈现方式和层次。

1)tps51632

tps51632是一个简单的器件,位于i2c总线下面,包含一个regulator器件,因此其DTS比较简单,如下:

   1: /* arch\arm\boot\dts\tegra114-dalmore.dts */
   2: i2c@7000d000 {
   3:         status = "okay";
   4:         clock-frequency = <400000>;
   5:  
   6:         tps51632@43 {
   7:                 compatible = "ti,tps51632";
   8:                 reg = <0x43>;
   9:                 regulator-name = "vdd-cpu";
  10:                 regulator-min-microvolt = <500000>;
  11:                 regulator-max-microvolt = <1520000>;
  12:                 regulator-boot-on;
  13:                 regulator-always-on;
  14:         };
  15:         ...
  16: }

i2c控制器的node为“i2c@7000d000”,tps51632是其下的一个子node,名称为“tps51632@43”,compatible为“ti,tps51632”。tps51632下面以“regulator-”为前缀的字段,是regulator特有的字段,后面会统一介绍。

注2:为什么“i2c@7000d000”中没有compatible字段?其实是有的,可参考“arch\arm\boot\dts\tegra114.dtsi”,DTC在编译DTS时,会将这两个文件中的node合并。

注3:kernel在初始化时,只会为二级node(即“/”下面的节点,本文的例子是“i2c@7000d000”)创建platform设备,至于三级node(这里的“tps51632@43”),则由其bus(i2c)创建。后面我们会遇到其它的情况,到时再介绍。

2)tps65090

tps65090相对比较复杂,它位于相同的i2c总线下面,但包含两个相对复杂的功能实体,charger和PMIC,我们看看其DTS怎么写的:

   1: i2c@7000d000 {
   2:         status = "okay";
   3:         ...
   4:  
   5:         tps65090@48 {
   6:                 compatible = "ti,tps65090";
   7:                 reg = <0x48>;
   8:                 ...
   9:  
  10:                 charger: charger {
  11:                         compatible = "ti,tps65090-charger";
  12:                         ti,enable-low-current-chrg;
  13:                 };
  14:                 
  15:                 regulators {
  16:                         tps65090_dcdc1_reg: dcdc1 {
  17:                                 regulator-name = "vdd-sys-5v0";
  18:                                 regulator-always-on;
  19:                                 regulator-boot-on;
  20:                         };
  21:                 
  22:                         tps65090_dcdc2_reg: dcdc2 {
  23:                                 regulator-name = "vdd-sys-3v3";
  24:                                 regulator-always-on;
  25:                                 regulator-boot-on;
  26:                         };
  27:                         ...
  28:                 }
  29:         }
  30: }

和tps51632类似,但它下面又包含了两个子node:charger和regulators。其中charger竟然还有compatible字段。

回忆一下上面“注3”,kernel只会为"i2c@7000d000”创建platform device,“tps65090@48”则由i2c core创建,那么它下面的子node呢?一定是tps65090 driver处理了,感兴趣的读者可以阅读“drivers/mfd/tps65090.c”、“drivers/power/tps65090-charger.c”和“drivers/regulator/tps65090-regulator.c”,这里面还涉及了MFD(multi-function device,多功能设备),很有意思。

回到本文的主题上,虽然这里的regulators没有compatible字段,也会创建相应的platform device(具体可参考“drivers/mfd/tps65090.c”),这从侧面回答了上面的一个思考:从物理范畴,tps65090是一个独立的设备,但它内部有两个功能模块,因此会存在两个platform device

再来看regulators中子node----regulator,由于数量比较多,就没必要创建platform device了。同样,“regulator-”为前缀的字段,是regulator特有的字段,后面统一介绍。

3)tps65913,和tps65090类似,不再介绍。

4)CPU中的regulator

这一类regulator比较特殊,直接集成在CPU内部,DTS如下:

   1: regulators {
   2:         compatible = "simple-bus";
   3:         #address-cells = <1>;
   4:         #size-cells = <0>;
   5:  
   6:         vdd_ac_bat_reg: regulator@0 {
   7:                 compatible = "regulator-fixed";
   8:                 reg = <0>;
   9:                 regulator-name = "vdd_ac_bat";
  10:                 regulator-min-microvolt = <5000000>;
  11:                 regulator-max-microvolt = <5000000>;
  12:                 regulator-always-on;
  13:         };
  14:  
  15:         dvdd_ts_reg: regulator@1 {
  16:                 compatible = "regulator-fixed";
  17:                 reg = <1>;
  18:                 regulator-name = "dvdd_ts";
  19:                 regulator-min-microvolt = <1800000>;
  20:                 regulator-max-microvolt = <1800000>;
  21:                 enable-active-high;
  22:                 gpio = <&gpio TEGRA_GPIO(H, 5) GPIO_ACTIVE_HIGH>;
  23:         };
  24:         ...
  25: };

在回到刚才的话题上,kernel只为二级node创建platform device(这里的“regulators”),那三级node(一个个的regulator)呢?没有相对标准的bus帮它们创建怎么办?借助“simple-bus”,具体可以参考of_platform_bus_create(“Device Tree(三):代码分析”)。

另外,这里的例子比较简单,都是fixed regulator,regulator framework core可以帮忙实现fixed类型的regulator的驱动,后面会说明。

2.3 编写与DTS节点对应的driver

这些driver的存在形式是多种多样的,但所做的工作基本类似:

1)初始化regulator的宿主(如上面的tps5163、PMIC、等等),最终的目的是,通过宿主提供的接口,修改regulator的输出。

2)初始化用于描述regulator的静态信息(struct regulator_desc)和动态信息(struct regulator_config),并以这二者为参数,调用regulator_register接口,将regulator注册到kernel中。

3)静态信息中包含regulator的操作函数集(struct regulator_ops),后续regulator的控制,将会由regulator framework core直接调用这些回调函数完成。

4)后面的事情,例如sysfs attribute创建等,就交给regulator framework core了。

3. DTS相关的实现逻辑

3.1 DTS的内容

回忆一下“Linux Regulator Framework(1)_概述”中介绍的machine的主要功能:使用软件语言(struct regulator_init_data),静态的描述regulator在板级的物理现状。对regulator driver而言,DTS主要用于配置regulator的init data。先看一下struct regulator_init_data:

   1: /**
   2:  * struct regulator_init_data - regulator platform initialisation data.
   3:  *
   4:  * Initialisation constraints, our supply and consumers supplies.
   5:  *
   6:  * @supply_regulator: Parent regulator.  Specified using the regulator name
   7:  *                    as it appears in the name field in sysfs, which can
   8:  *                    be explicitly set using the constraints field 'name'.
   9:  *
  10:  * @constraints: Constraints.  These must be specified for the regulator to
  11:  *               be usable.
  12:  * @num_consumer_supplies: Number of consumer device supplies.
  13:  * @consumer_supplies: Consumer device supply configuration.
  14:  *
  15:  * @regulator_init: Callback invoked when the regulator has been registered.
  16:  * @driver_data: Data passed to regulator_init.
  17:  */
  18: struct regulator_init_data {
  19:         const char *supply_regulator;        /* or NULL for system supply */
  20:  
  21:         struct regulation_constraints constraints;
  22:  
  23:         int num_consumer_supplies;
  24:         struct regulator_consumer_supply *consumer_supplies;
  25:  
  26:         /* optional regulator machine specific init */
  27:         int (*regulator_init)(void *driver_data);
  28:         void *driver_data;      /* core does not touch this */
  29: };

supply_regulator,该regulator的前级regulator,一般在regulator driver中直接指定;

constraints,该regulator的使用限制,由DTS配置,并可以借助regulator core提供的辅助API(regulator_of_get_init_data)自动解析。后面会详细介绍;

num_consumer_supplies、consumer_supplies,使用该regulator的consumer的个数,及其设备名和supply名的map。用于建立consumer设备和regulator之间的关联,后面介绍consumer DTS时再详细说明;

regulator_init,regulator的init回调,由regulator driver提供,并在regulator注册时调用;

driver_data,保存driver的私有数据,并在调用regulator_init时传入。

看来DTS的内容都在struct regulation_constraints中,该结构保存了该regulator所有的物理限制,如下:

   1: struct regulation_constraints {
   2:  
   3:         const char *name;
   4:  
   5:         /* voltage output range (inclusive) - for voltage control */
   6:         int min_uV;
   7:         int max_uV;
   8:  
   9:         int uV_offset;
  10:  
  11:         /* current output range (inclusive) - for current control */
  12:         int min_uA;
  13:         int max_uA;
  14:  
  15:         /* valid regulator operating modes for this machine */
  16:         unsigned int valid_modes_mask;
  17:  
  18:         /* valid operations for regulator on this machine */
  19:         unsigned int valid_ops_mask;
  20:  
  21:         /* regulator input voltage - only if supply is another regulator */
  22:         int input_uV;
  23:  
  24:         /* regulator suspend states for global PMIC STANDBY/HIBERNATE */
  25:         struct regulator_state state_disk;
  26:         struct regulator_state state_mem;
  27:         struct regulator_state state_standby;
  28:         suspend_state_t initial_state; /* suspend state to set at init */
  29:  
  30:         /* mode to set on startup */
  31:         unsigned int initial_mode;
  32:  
  33:         unsigned int ramp_delay;
  34:         unsigned int enable_time;
  35:  
  36:         /* constraint flags */
  37:         unsigned always_on:1;   /* regulator never off when system is on */
  38:         unsigned boot_on:1;     /* bootloader/firmware enabled regulator */
  39:         unsigned apply_uV:1;    /* apply uV constraint if min == max */
  40:         unsigned ramp_disable:1; /* disable ramp delay */
  41: };

name,用于描述该constraints;

min_uV、max_uV,输出电压的范围,[min_uV, max_uV],单位为uV。只对voltage regulator有效;

uV_offset,consumer看到的电压和实际电压之间的偏移值。通常用于补偿压降。只对voltage regulator有效;

min_uA、max_uA,输出电流的范围,[min_uA, max_uA],单位为uA。只对current regulator有效;

valid_modes_mask,regulator mode相关的内容,和DTS无关,后面再解释;

valid_ops_mask,该regulator支持哪些操作,以bit mask的形式提供,包括: 
        REGULATOR_CHANGE_VOLTAGE,可以改变输出电压; 
        REGULATOR_CHANGE_CURRENT,可以改变输出电流; 
        REGULATOR_CHANGE_MODE,可以修改mode,后面再介绍; 
        REGULATOR_CHANGE_STATUS,可以enable/disable; 
        REGULATOR_CHANGE_DRMS,支持Dynamic Regulator Mode Switching(DRMS),可以动态的调整regulator的mode,有关mode描述可参考5.3章节; 
        REGULATOR_CHANGE_BYPASS,支持bypass模式。

input_uV,如果该regulator的输入是另一个regulator,该字段指定regulator期望的输入电压;

state_xxx、initial_state,regulator电源管理有关的字段,后面会专门介绍;

initial_mode,初始mode,具体请参考5.3节的介绍;

always_on,是否一直保持使能状态;

boot_on,是否在启动时使能;

rapm_delay,由于模拟器件的特性,电压改变,需要一定的生效时间。在一定的范围内,生效时间和电压的变化值成比例。该变量就是描述regulator器件的这个特性,单位为uV/us,即1us可以产生多大的电压变化。在rapm_disable不为1的情况下,当consumer要求改变电压时,regulator framework core会根据该变量,以及电压改变量,计算出需要等待的时间,进行延时操作;

rapm_disable,是否禁止延时操作;

enable time,regulator的开启时间,单位为us。consumer enable regulator时,regulator framework会根据该变量进行延时操作;

apply_uV,如果min_uV和max_uV相同,该变量指示“在regulator注册到kernel时,是否将电压设置为min_uV/max_uV。 

结合struct regulation_constraints结构,我们解释一下2.2小节中tps51632的DTS:

   1: tps51632@43 {
   2:         compatible = "ti,tps51632";
   3:         reg = <0x43>;
   4:         regulator-name = "vdd-cpu";
   5:         regulator-min-microvolt = <500000>;
   6:         regulator-max-microvolt = <1520000>;
   7:         regulator-boot-on;
   8:         regulator-always-on;
   9: };

regulator-name,对应struct regulation_constraints中name;

regulator-min-microvolt,对应struct regulation_constraints中的min_uV;

regulator-max-microvolt,对应struct regulation_constraints中的max_uV;

regulator-boot-on,对应struct regulation_constraints中的boot_on;

regulator-always-on,对应struct regulation_constraints中的always_on。

其它的字段,可以根据实际情况,自行添加,具体可参考“Documentation/devicetree/bindings/regulator/regulator.txt”中的描述。

3.2 DTS的解析

regulator的DTS信息,可以通过两种方法解析:

1)在regulator注册前,调用of_get_regulator_init_data接口自行解析,该接口的实现如下:

   1: struct regulator_init_data *of_get_regulator_init_data(struct device *dev,
   2:                                                 struct device_node *node)
   3: {
   4:         struct regulator_init_data *init_data;
   5:  
   6:         if (!node)
   7:                 return NULL;
   8:  
   9:         init_data = devm_kzalloc(dev, sizeof(*init_data), GFP_KERNEL);
  10:         if (!init_data)
  11:                 return NULL; /* Out of memory? */
  12:  
  13:         of_get_regulation_constraints(node, &init_data);
  14:         return init_data;
  15: }
  16: EXPORT_SYMBOL_GPL(of_get_regulator_init_data);

该接口有两个输入参数:设备指针,以及包含了DTS信息的node指针(以3.1中的例子,即“tps51632@43”所在的node)。

它会分配一个struct regulator_init_data变量,并调用of_get_regulation_constraints解析DTS,把结果保存在该变量中。

最后返回struct regulator_init_data变量的地址。

2)在regulator注册时,由regulator_register调用regulator_of_get_init_data帮忙解析,该接口的实现如下:

   1: struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
   2:                                             const struct regulator_desc *desc,
   3:                                             struct device_node **node)
   4: {
   5:         struct device_node *search, *child;
   6:         struct regulator_init_data *init_data = NULL;
   7:         const char *name;
   8:  
   9:         if (!dev->of_node || !desc->of_match)
  10:                 return NULL;
  11:  
  12:         if (desc->regulators_node)
  13:                 search = of_get_child_by_name(dev->of_node,
  14:                                               desc->regulators_node);
  15:         else
  16:                 search = dev->of_node;
  17:  
  18:         if (!search) {
  19:                 dev_dbg(dev, "Failed to find regulator container node '%s'\n",
  20:                         desc->regulators_node);
  21:                 return NULL;
  22:         }
  23:  
  24:         for_each_child_of_node(search, child) {
  25:                 name = of_get_property(child, "regulator-compatible", NULL);
  26:                 if (!name)
  27:                         name = child->name;
  28:  
  29:                 if (strcmp(desc->of_match, name))
  30:                         continue;
  31:  
  32:                 init_data = of_get_regulator_init_data(dev, child);
  33:                 if (!init_data) {
  34:                         dev_err(dev,
  35:                                 "failed to parse DT for regulator %s\n",
  36:                                 child->name);
  37:                         break;
  38:                 }
  39:  
  40:                 of_node_get(child);
  41:                 *node = child;
  42:                 break;
  43:         }
  44:         of_node_put(search);
  45:  
  46:         return init_data;
  47: }

与of_get_regulator_init_data不同的是,该接口以struct regulator_desc指针为参数,该参数提供了regulator DTS有关的搜索信息(desc->of_match),根据这些信息,可以获得包含regulator信息的DTS node。

它本质上是一种通用的DTS匹配逻辑(和kernel解析platform device的标准资源类似),大致如下:

a)调用者提供parent node(struct device指针中,代表regulators的宿主设备,如上面的tps65090@48),以及该regulator在DTS中的名称(由desc->of_match提供)。

b)还可以在struct regulator_desc中提供包含regulator DTS信息的node名称(可选,用于regulator不直接在parent node下的情况)。

c)以parent device的node,或者指定的子node为基准,查找其下所有的node,如果node的名字或者“regulator-compatible”字段和desc->of_match匹配,则调用of_get_regulator_init_data从中解析DTS信息。

总结:1、2两种DTS解析的方法,各有优缺点:1直接,方便,容易理解,但会有冗余代码;2简洁,但需要regulator driver开发者非常熟悉解析的原理,并以此设计DTS和struct regulator_desc变量。大家可以根据实际情况,灵活使用。

4. 主要数据结构

4.1 struct regulator_desc

在注册regulator的时候,需要使用struct regulator_desc结构提供该regulator的静态描述。所谓的静态,是指这些描述不会在运行时改变,代表了设备的一种属性,如下:

   1: /* include/linux/regulator/driver.h */
   2:  
   3: struct regulator_desc {
   4:         const char *name;
   5:         const char *supply_name;
   6:         const char *of_match;
   7:         const char *regulators_node;
   8:         int id;
   9:         bool continuous_voltage_range;
  10:         unsigned n_voltages;
  11:         const struct regulator_ops *ops;
  12:         int irq;
  13:         enum regulator_type type;
  14:         struct module *owner;
  15:  
  16:         unsigned int min_uV;
  17:         unsigned int uV_step;
  18:         unsigned int linear_min_sel;
  19:         int fixed_uV;
  20:         unsigned int ramp_delay;
  21:  
  22:         const struct regulator_linear_range *linear_ranges;
  23:         int n_linear_ranges;
  24:  
  25:         const unsigned int *volt_table;
  26:  
  27:         unsigned int vsel_reg;
  28:         unsigned int vsel_mask;
  29:         unsigned int apply_reg;
  30:         unsigned int apply_bit;
  31:         unsigned int enable_reg;
  32:         unsigned int enable_mask;
  33:         unsigned int enable_val;
  34:         unsigned int disable_val;
  35:         bool enable_is_inverted;
  36:         unsigned int bypass_reg;
  37:         unsigned int bypass_mask;
  38:         unsigned int bypass_val_on;
  39:         unsigned int bypass_val_off;
  40:  
  41:         unsigned int enable_time;
  42:  
  43:         unsigned int off_on_delay;
  44: };

name,该regulator的名称,唯一标识该regulator,必须提供;

supply_name,该regulator的输入regulator的名称;

of_match、regulators_node,提供信息,以便在注册的时候自动从DTS中解析init_data,具体可参考3.2小节的描述;

id,标识该regulator的一个数字;

continuous_voltage_range,为true时,表示该regulator可以在一定范围输出连续的电压;

n_voltages,consumer可以通过ops.list_voltage()接口,获取该regulator可以输出的电压值。该变量指定可以获取的电压值的个数,后面讲到ops时,再介绍;

ops,该regulator的操作函数集,见后面;

irq,该regulator的中断号(有的话);

type,该regulator的类型,包括REGULATOR_VOLTAGE和REGULATOR_CURRENT两种;

其它字段,和regulator的输出控制有关,后面会以专题的形式介绍。

struct regulator_ops提供了regulator的所有操作,如下:

   1: struct regulator_ops {
   2:  
   3:         /* enumerate supported voltages */
   4:         int (*list_voltage) (struct regulator_dev *, unsigned selector);
   5:  
   6:         /* get/set regulator voltage */
   7:         int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV,
   8:                             unsigned *selector);
   9:         int (*map_voltage)(struct regulator_dev *, int min_uV, int max_uV);
  10:         int (*set_voltage_sel) (struct regulator_dev *, unsigned selector);
  11:         int (*get_voltage) (struct regulator_dev *);
  12:         int (*get_voltage_sel) (struct regulator_dev *);
  13:  
  14:         /* get/set regulator current  */
  15:         int (*set_current_limit) (struct regulator_dev *,
  16:                                  int min_uA, int max_uA);
  17:         int (*get_current_limit) (struct regulator_dev *);
  18:  
  19:         /* enable/disable regulator */
  20:         int (*enable) (struct regulator_dev *);
  21:         int (*disable) (struct regulator_dev *);
  22:         int (*is_enabled) (struct regulator_dev *);
  23:  
  24:         /* get/set regulator operating mode (defined in consumer.h) */
  25:         int (*set_mode) (struct regulator_dev *, unsigned int mode);
  26:         unsigned int (*get_mode) (struct regulator_dev *);
  27:  
  28:         /* Time taken to enable or set voltage on the regulator */
  29:         int (*enable_time) (struct regulator_dev *);
  30:         int (*set_ramp_delay) (struct regulator_dev *, int ramp_delay);
  31:         int (*set_voltage_time_sel) (struct regulator_dev *,
  32:                                      unsigned int old_selector,
  33:                                      unsigned int new_selector);
  34:  
  35:         /* report regulator status ... most other accessors report
  36:          * control inputs, this reports results of combining inputs
  37:          * from Linux (and other sources) with the actual load.
  38:          * returns REGULATOR_STATUS_* or negative errno.
  39:          */
  40:         int (*get_status)(struct regulator_dev *);
  41:  
  42:         /* get most efficient regulator operating mode for load */
  43:         unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV,
  44:                                           int output_uV, int load_uA);
  45:  
  46:         /* control and report on bypass mode */
  47:         int (*set_bypass)(struct regulator_dev *dev, bool enable);
  48:         int (*get_bypass)(struct regulator_dev *dev, bool *enable);
  49:         
  50:         /* the operations below are for configuration of regulator state when
  51:          * its parent PMIC enters a global STANDBY/HIBERNATE state */
  52:         
  53:         /* set regulator suspend voltage */
  54:         int (*set_suspend_voltage) (struct regulator_dev *, int uV);
  55:         
  56:         /* enable/disable regulator in suspend state */
  57:         int (*set_suspend_enable) (struct regulator_dev *);
  58:         int (*set_suspend_disable) (struct regulator_dev *);
  59:         
  60:         /* set regulator suspend operating mode (defined in consumer.h) */
  61:         int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode);
  62: };

这些回调函数的具体意义,请参考后续的描述。

4.2 struct regulator_config

struct regulator_config保存了regulator的动态信息,所谓的动态信息,是指那些会在driver运行过程中改变、或者driver运行后才会确定的信息,如下:

   1: struct regulator_config {
   2:         struct device *dev;
   3:         const struct regulator_init_data *init_data;
   4:         void *driver_data;
   5:         struct device_node *of_node;
   6:         struct regmap *regmap;
   7:  
   8:         int ena_gpio;
   9:         unsigned int ena_gpio_invert:1;
  10:         unsigned int ena_gpio_flags;
  11: };

dev,对应的struct device指针。会在regulator_register时,由regulator core分配,保存在此,以便后续使用;

init_data,init data指针,在解析DTS后,保存在此,以便后续使用;

of_node,可以为空;

regmap,参考后续描述;

ena_gpio、ena_gpio_invert、ena_gpio_flags,控制regulator使能的GPIO及其active极性。

4.3 struct regulator_dev

struct regulator_dev是regulator设备的抽象,当driver以struct regulator_desc、struct regulator_config两个类型的参数,调用regulator_register将regulator注册到kernel之后,regulator就会分配一个struct regulator_dev变量,后续所有的regulator操作,都将以该变量为对象。

   1: struct regulator_dev {
   2:         const struct regulator_desc *desc;
   3:         int exclusive;
   4:         u32 use_count;
   5:         u32 open_count;
   6:         u32 bypass_count;
   7:  
   8:         /* lists we belong to */
   9:         struct list_head list; /* list of all regulators */
  10:  
  11:         /* lists we own */
  12:         struct list_head consumer_list; /* consumers we supply */
  13:  
  14:         struct blocking_notifier_head notifier;
  15:         struct mutex mutex; /* consumer lock */
  16:         struct module *owner;
  17:         struct device dev;
  18:         struct regulation_constraints *constraints;
  19:         struct regulator *supply;       /* for tree */
  20:         struct regmap *regmap;
  21:  
  22:         struct delayed_work disable_work;
  23:         int deferred_disables;
  24:  
  25:         void *reg_data;         /* regulator_dev data */
  26:  
  27:         struct dentry *debugfs;
  28:  
  29:         struct regulator_enable_gpio *ena_pin;
  30:         unsigned int ena_gpio_state:1;
  31:  
  32:         /* time when this regulator was disabled last time */
  33:         unsigned long last_off_jiffy;
  34: };

desc,保存了regulator静态描述信息的指针(从这个角度看,所谓的静态描述,其变量必须为全局变量);

exclusive、use_count、open_count、bypass_count,一些状态记录;

constraints,保存了regulator的constraints指针;

supply,该regulator的supply;

等等。

5 实现逻辑分析

本章简单的分析一下regulator driver相关的实现逻辑。如果要理解有些逻辑,必须具备一些regulator的基础知识,因此在需要的时候,会穿插介绍这些知识。

5.1 regulator core的初始化

regulator core的初始化操作由regulator_init接口负责,主要工作包括:

1)注册regulator class(/sys/class/regulator/)。

2)注册用于调试的debugfs。

和power switch class、input class等类似,regulator framework也是一种class,可以称作regulator class。

5.2 regulator register

regulator的注册,由regulator_register/devm_regulator_register接口负责,如下:

   1: /**
   2:  * regulator_register - register regulator
   3:  * @regulator_desc: regulator to register
   4:  * @config: runtime configuration for regulator
   5:  *
   6:  * Called by regulator drivers to register a regulator.
   7:  * Returns a valid pointer to struct regulator_dev on success
   8:  * or an ERR_PTR() on error.
   9:  */
  10: struct regulator_dev *
  11: regulator_register(const struct regulator_desc *regulator_desc,
  12:            const struct regulator_config *config)
  13: {
  14:     const struct regulation_constraints *constraints = NULL;
  15:     const struct regulator_init_data *init_data;
  16:     static atomic_t regulator_no = ATOMIC_INIT(0);
  17:     struct regulator_dev *rdev;
  18:     struct device *dev;
  19:     int ret, i;
  20:     const char *supply = NULL;
  21:  
  22:     if (regulator_desc == NULL || config == NULL)
  23:         return ERR_PTR(-EINVAL);
  24:  
  25:     dev = config->dev;
  26:     WARN_ON(!dev);
  27:  
  28:     if (regulator_desc->name == NULL || regulator_desc->ops == NULL)
  29:         return ERR_PTR(-EINVAL);
  30:  
  31:     if (regulator_desc->type != REGULATOR_VOLTAGE &&
  32:         regulator_desc->type != REGULATOR_CURRENT)
  33:         return ERR_PTR(-EINVAL);
  34:  
  35:     /* Only one of each should be implemented */
  36:     WARN_ON(regulator_desc->ops->get_voltage &&
  37:         regulator_desc->ops->get_voltage_sel);
  38:     WARN_ON(regulator_desc->ops->set_voltage &&
  39:         regulator_desc->ops->set_voltage_sel);
  40:  
  41:     /* If we're using selectors we must implement list_voltage. */
  42:     if (regulator_desc->ops->get_voltage_sel &&
  43:         !regulator_desc->ops->list_voltage) {
  44:         return ERR_PTR(-EINVAL);
  45:     }
  46:     if (regulator_desc->ops->set_voltage_sel &&
  47:         !regulator_desc->ops->list_voltage) {
  48:         return ERR_PTR(-EINVAL);
  49:     }
  50:  
  51:     rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
  52:     if (rdev == NULL)
  53:         return ERR_PTR(-ENOMEM);
  54:  
  55:     init_data = regulator_of_get_init_data(dev, regulator_desc,
  56:                            &rdev->dev.of_node);
  57:     if (!init_data) {
  58:         init_data = config->init_data;
  59:         rdev->dev.of_node = of_node_get(config->of_node);
  60:     }
  61:  
  62:     mutex_lock(®ulator_list_mutex);
  63:  
  64:     mutex_init(&rdev->mutex);
  65:     rdev->reg_data = config->driver_data;
  66:     rdev->owner = regulator_desc->owner;
  67:     rdev->desc = regulator_desc;
  68:     if (config->regmap)
  69:         rdev->regmap = config->regmap;
  70:     else if (dev_get_regmap(dev, NULL))
  71:         rdev->regmap = dev_get_regmap(dev, NULL);
  72:     else if (dev->parent)
  73:         rdev->regmap = dev_get_regmap(dev->parent, NULL);
  74:     INIT_LIST_HEAD(&rdev->consumer_list);
  75:     INIT_LIST_HEAD(&rdev->list);
  76:     BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
  77:     INIT_DELAYED_WORK(&rdev->disable_work, regulator_disable_work);
  78:  
  79:     /* preform any regulator specific init */
  80:     if (init_data && init_data->regulator_init) {
  81:         ret = init_data->regulator_init(rdev->reg_data);
  82:         if (ret < 0)
  83:             goto clean;
  84:     }
  85:  
  86:     /* register with sysfs */
  87:     rdev->dev.class = ®ulator_class;
  88:     rdev->dev.parent = dev;
  89:     dev_set_name(&rdev->dev, "regulator.%d",
  90:              atomic_inc_return(®ulator_no) - 1);
  91:     ret = device_register(&rdev->dev);
  92:     if (ret != 0) {
  93:         put_device(&rdev->dev);
  94:         goto clean;
  95:     }
  96:  
  97:     dev_set_drvdata(&rdev->dev, rdev);
  98:  
  99:     if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) {
 100:         ret = regulator_ena_gpio_request(rdev, config);
 101:         if (ret != 0) {
 102:             rdev_err(rdev, "Failed to request enable GPIO%d: %d\n",
 103:                  config->ena_gpio, ret);
 104:             goto wash;
 105:         }
 106:  
 107:         if (config->ena_gpio_flags & GPIOF_OUT_INIT_HIGH)
 108:             rdev->ena_gpio_state = 1;
 109:  
 110:         if (config->ena_gpio_invert)
 111:             rdev->ena_gpio_state = !rdev->ena_gpio_state;
 112:     }
 113:  
 114:     /* set regulator constraints */
 115:     if (init_data)
 116:         constraints = &init_data->constraints;
 117:  
 118:     ret = set_machine_constraints(rdev, constraints);
 119:     if (ret < 0)
 120:         goto scrub;
 121:  
 122:     /* add attributes supported by this regulator */
 123:     ret = add_regulator_attributes(rdev);
 124:     if (ret < 0)
 125:         goto scrub;
 126:  
 127:     if (init_data && init_data->supply_regulator)
 128:         supply = init_data->supply_regulator;
 129:     else if (regulator_desc->supply_name)
 130:         supply = regulator_desc->supply_name;
 131:  
 132:     if (supply) {
 133:         struct regulator_dev *r;
 134:  
 135:         r = regulator_dev_lookup(dev, supply, &ret);
 136:  
 137:         if (ret == -ENODEV) {
 138:             /*
 139:              * No supply was specified for this regulator and
 140:              * there will never be one.
 141:              */
 142:             ret = 0;
 143:             goto add_dev;
 144:         } else if (!r) {
 145:             dev_err(dev, "Failed to find supply %s\n", supply);
 146:             ret = -EPROBE_DEFER;
 147:             goto scrub;
 148:         }
 149:  
 150:         ret = set_supply(rdev, r);
 151:         if (ret < 0)
 152:             goto scrub;
 153:  
 154:         /* Enable supply if rail is enabled */
 155:         if (_regulator_is_enabled(rdev)) {
 156:             ret = regulator_enable(rdev->supply);
 157:             if (ret < 0)
 158:                 goto scrub;
 159:         }
 160:     }
 161:  
 162: add_dev:
 163:     /* add consumers devices */
 164:     if (init_data) {
 165:         for (i = 0; i < init_data->num_consumer_supplies; i++) {
 166:             ret = set_consumer_device_supply(rdev,
 167:                 init_data->consumer_supplies[i].dev_name,
 168:                 init_data->consumer_supplies[i].supply);
 169:             if (ret < 0) {
 170:                 dev_err(dev, "Failed to set supply %s\n",
 171:                     init_data->consumer_supplies[i].supply);
 172:                 goto unset_supplies;
 173:             }
 174:         }
 175:     }
 176:  
 177:     list_add(&rdev->list, ®ulator_list);
 178:  
 179:     rdev_init_debugfs(rdev);
 180: out:
 181:     mutex_unlock(®ulator_list_mutex);
 182:     return rdev;
 183:  
 184: unset_supplies:
 185:     unset_regulator_supplies(rdev);
 186:  
 187: scrub:
 188:     if (rdev->supply)
 189:         _regulator_put(rdev->supply);
 190:     regulator_ena_gpio_free(rdev);
 191:     kfree(rdev->constraints);
 192: wash:
 193:     device_unregister(&rdev->dev);
 194:     /* device core frees rdev */
 195:     rdev = ERR_PTR(ret);
 196:     goto out;
 197:  
 198: clean:
 199:     kfree(rdev);
 200:     rdev = ERR_PTR(ret);
 201:     goto out;
 202: }
 203: EXPORT_SYMBOL_GPL(regulator_register);

主要工作包括:

22~49,检查参数的合法性。其中35~49行,涉及到电压控制的方式,后面后详细说明;

55~60,协助从DTS解析init data,如果解析不到,则使用config中的;

68~73,协助获取regulator的register map(有的话),并保存在register device指针中。regulator driver会在需要的时候使用(通常是在ops回调函数中);

74~77,初始化一些全局变量,consumer_list用于保存所有的consumer,list用于将自己添加到一个全局的regulator链表(regulator_list)上,disable_work是用于disable regulator的work queue;

86~95,将regulator device注册到kernel;

99~112,申请regulator enable gpio(有的话),并将相应的信息保存在regulator device指针中;

114~120,将从DTS中解析的constraints,应用起来(这个过程比较复杂,就不介绍了,感兴趣的读者可以自行分析);

123,根据regulator的操作函数集,注册相应的attribute(和PSY class类似);

127~160,如果该regulator有supply,根据supply的名字,获取相应的regulator device指针,同时根据supply指针,分配一个struct regulator结构,保存在该regulator的supply指针中。最后,如果该regulator处于使能状态,则需要使能其supply(这些动作,需要以consumer的视角操作,因而需要一个struct regulator变量);

162~175,add consumer devices,等到介绍consumer时,再详细描述。

注4:register map是kernel提供的一种管理寄存器的机制,特别是较为复杂的寄存器,如codec等。本文不会过多描述,如需要,会专门写一篇文章介绍该机制。

5.3 regulator的操作模式(operation mode)

regulator的主要功能,是输出电压/电流的调整(或改变)。由于模拟器件的特性,电压/电流的改变,是需要一定的时间的。对有些regulator而言,可以工作在不同的模式,这些模式有不同的改变速度,可想而知,较快的速度,有较大的功耗。下面是operation mode定义(位于include/linux/regulator/consumer.h中):

   1: /*
   2:  * Regulator operating modes.
   3:  *
   4:  * Regulators can run in a variety of different operating modes depending on
   5:  * output load. This allows further system power savings by selecting the
   6:  * best (and most efficient) regulator mode for a desired load.
   7:  *
   8:  * Most drivers will only care about NORMAL. The modes below are generic and
   9:  * will probably not match the naming convention of your regulator data sheet
  10:  * but should match the use cases in the datasheet.
  11:  *
  12:  * In order of power efficiency (least efficient at top).
  13:  *
  14:  *  Mode       Description
  15:  *  FAST       Regulator can handle fast changes in it's load.
  16:  *             e.g. useful in CPU voltage & frequency scaling where
  17:  *             load can quickly increase with CPU frequency increases.
  18:  *
  19:  *  NORMAL     Normal regulator power supply mode. Most drivers will
  20:  *             use this mode.
  21:  *
  22:  *  IDLE       Regulator runs in a more efficient mode for light
  23:  *             loads. Can be used for devices that have a low power
  24:  *             requirement during periods of inactivity. This mode
  25:  *             may be more noisy than NORMAL and may not be able
  26:  *             to handle fast load switching.
  27:  *
  28:  *  STANDBY    Regulator runs in the most efficient mode for very
  29:  *             light loads. Can be used by devices when they are
  30:  *             in a sleep/standby state. This mode is likely to be
  31:  *             the most noisy and may not be able to handle fast load
  32:  *             switching.
  33:  *
  34:  * NOTE: Most regulators will only support a subset of these modes. Some
  35:  * will only just support NORMAL.
  36:  *
  37:  * These modes can be OR'ed together to make up a mask of valid register modes.
  38:  */
  39:  
  40: #define REGULATOR_MODE_FAST                     0x1
  41: #define REGULATOR_MODE_NORMAL                   0x2
  42: #define REGULATOR_MODE_IDLE                     0x4
  43: #define REGULATOR_MODE_STANDBY                  0x8

相应的,regulator framework提供了一些机制,用于operation mode的操作,包括:

1)struct regulation_constraints中用于表示初始模式的字段initial_mode。

2)regulator ops中的set_mode/get_mode回调函数。

5.4 电压操作的两种方式

kernel抽象了两种电压操作的方法:

1)直接操作电压,对应struct regulator_ops中的如下回调函数:

   1: /* get/set regulator voltage */
   2: int (*list_voltage) (struct regulator_dev *, unsigned selector);
   3: int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV,
   4:                     unsigned *selector);
   5: int (*get_voltage) (struct regulator_dev *);

其中set_voltage用于将电压设置为min_uV和max_uV范围内、和min_uV最接近的电压。该接口可以返回一个selector参数,用于告知调用者,实际的电压值;

get_voltage,用于返回当前的电压值;

list_voltage,以selector为参数,获取对应的电压值。

注5:有关selector的描述,可参考下面的介绍。

2)selector的形式

regulator driver以selector的形式,反映电压值。selector是一个从0开始的整数,driver提供如下的接口:

   1: /* enumerate supported voltages */
   2: int (*list_voltage) (struct regulator_dev *, unsigned selector);
   3:  
   4: int (*map_voltage)(struct regulator_dev *, int min_uV, int max_uV);
   5: int (*set_voltage_sel) (struct regulator_dev *, unsigned selector);
   6: int (*get_voltage_sel) (struct regulator_dev *);

list_voltage,上面已经介绍;

map_voltage,是和list_voltage相对的接口,用于将电压范围map成一个selector值;

set_voltage_sel/get_voltage_sel,以selector的形式,操作电压。

regulator driver可以根据实际情况,选择一种实现方式。

5.5 regulator framework提供的sysfs接口

根据regulator提供的ops情况,regulator framework可以通过sysfs提供多种attribute,它们位于/sys/class/regulator/.../目录下,数量相当多,这里就不一一描述了,具体可参考:

https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-regulator

6. 后记

这篇文章写的相当纠结,相当混乱,我相信读者很难看懂……

本以为会很容易写,但里面的知识点太零碎了,以至于无法很好的归纳、抽象、突出重点。崩溃!

时间原因,就先这样了,有机会再回头来整理,希望读者谅解。

2019-08-09 13:07:40 weixin_42892101 阅读数 59

查看原理图,熟悉引脚号、如何操作io口

顶层应用程序使用:open write read ioctl进行读取操作写入文件

驱动层提供应用层操作的接口:drv_open drv_write drv_read drv_ioctl

1、分配一个结构体 file_operations (文件操作)

2、去设置这个结构体

驱动写法,差别在于如何指定硬件资源!
2.1、传统方法:在驱动代码drv.c中写死
.open=led_open 用于设置引脚输入输出
.write=led_write 用于设置引脚状态

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
//#include <asm/uaccess.h>
#include <linux/uaccess.h>//用户空间访问
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>

#define S3C2440_GPA(n)		(0<<16	|	n)
#define S3C2440_GPB(n)		(1<<16	|	n)
#define S3C2440_GPC(n)		(2<<16	|	n)
#define S3C2440_GPD(n)		(3<<16	|	n)
#define S3C2440_GPE(n)		(4<<16	|	n)
#define S3C2440_GPF(n)		(5<<16	|	n)
#define S3C2440_GPG(n)		(6<<16	|	n)
#define S3C2440_GPH(n)		(7<<16	|	n)
#define S3C2440_GPI(n)		(8<<16	|	n)
#define S3C2440_GPJ(n)		(9<<16	|	n)
static struct class *led_class;//增加信息

static int led_pin = S3c2440_GPF(5);
static volatile unsigned int *gpio_con;//配置引脚输出
static volatile unsigned int *gpio_dat;//电平
static int major;//接入注册内核完成后,返回当前主设备号值
static unsigned int gpio_base[] = //芯片寄存器地址
{
	0x56000000,/*GPACON*/
	0x56000010,/*GPBCON*/
	0x56000020,/*GPCCON*/
	0x56000030,/*GPDCON*/
	0x56000040,/*GPECON*/
	0x56000050,/*GPFCON*/
	0x56000060,/*GPGCON*/
	0x56000070,/*GPHCON*/
	0x560000D0/*GPJCON*/
};
static int led_open(struct inode *node,struct file *filp)
{
	/* 把LED引脚配置为输出引脚 */
	/* GPF5 -0x56000050 */
	int bank = led_pin >> 16;
	int base = gpio_base[bank];
	int pin = led_pin & 0xffff;
	gpio_con = ioremap(base,8);
	if(gpio_con)
	{
		printk("ioremap(0x%x) = 0x%x\n",base,gpio_con);//内核用printk,应用层用printf
	}
	else
	{
		return -EINVAL;
	}
	gpio_dat = gpio_con + 1;
	*gpio_con &= ~(3<<(pin *2));//控制寄存器中11:10位设置为01为输出模式,此步先清0
	*gpio_con |= (1<<(pin *2));//第10位置1
	return 0;//成功!
}

static ssize_t led_write (struct file *filp,const char __user *buf,size_t size,loff_t off)//__user强调告诉你 :buf来自应用程序的内核里面不可以直接使用访问
{
	/* 根据应用程序传入的值来设置LED引脚 */
	unsigned char val;//内核空间变量
	copy_from_user(&val,buf,1);//获得应用程序传入的值,将用户空间传入的值拷贝到内核空间
	if(val)
	{
		/* 点灯 */
		*gpio_dat &= ~(1<<pin);//实际硬件为低电平导通
	}
	else
	{
		/* 灭灯 */
		*gpio_dat |= (1<<pin);
	}
	return 1;//返回已写入1个数据
}
static int (*led_release)(struct inode *,struct file *filp)
{
	printk("iounmap(0x%x)\n",gpio_con);//内核用printk,应用层用printf
	iounmap(gpio_con);
	return 0;
}
//分配设置注册file_operations
static struct file_operations myled_oprs = {
	.owner = THIS_MODULE,
	.open  = led_open,
	.write = led_write,
	.release = led_release
};  

static int myled_init(void)
{
	major = register_chrdev(0,"myled",&myled_oprs);
	led_class = class_create(THIS_MODULE,"myled");//创建一个class名字随意
	/*MKDEV(major,0) 主设备号,次设备号*/
	device_create(led_class,NULL,MKDEV(major,0),NULL,"led");/*/dev/led*/
	return 0;
}
static void myled_exit(void)
{
	unregister_chrdev(major,"myled");
	device_destroy(led_class,MKDEV(major,0));
	class_destroy(led_class);
}
module_init(myled_init);//别名函数--init_module指向myled_init函数
module_exit(myled_exit);//别名函数--clean_module指向myled_exit函数
moule_license("GPL");

优点:简单,缺点:更换引脚重新编译不易扩展

2.2、总线设备驱动模型:2个文件(drv.c分配/设置/注册 platform_driver结构体和dev.c分配/设置/注册 platform_device结构体)在dev.c中指定引脚
优点:稍微复杂、容易扩展,缺点:更换引脚重新编译

2.3、使用设备树:3个文件(drv.c分配/设置/注册 platform_driver结构体和dts编译生成dtb文件分配/设置/注册 platform_device结构体
在dts中指定引脚,编译为dtb文件
内核在运行的时候读取解析dtb文件重新分配/设置/注册 platform_device
优点:稍微复杂、易扩展、无冗余代码、不需要重新编译内核或则驱动,只需要提供不一样的设备树文件,缺点:

3、注册(告诉内核)

理解:内核结构体中存放一个又一个驱动结构体
将驱动程序放入结构体中放入新的驱动,这个过程是注册!
放入其中哪一项,可以通过指定设备号,输入0时,则内核遍历到空的地方自动存入!
入口:register_chrdev(主设备号通常给0由系统分配,“name”,&结构体名) 返回内核中存入的主设备号
出口:unregister_chrdev(内核中存入的主设备号,“name”)

2015-07-20 13:55:16 zdc524 阅读数 25876

优点

  • 面向文档存储(类JSON数据模式简单而强大)
{ "system" : { "currentTime" : { "$date" : "2015-07-27T14:06:40.976+0800" },
    "hostname" : "dachuanz-test",
    "cpuAddrSize" : 64,
    "memSizeMB" : 3791,
    "numCores" : 4,
    "cpuArch" : "x86_64",
    "numaEnabled" : false },
  "os" : { "type" : "Linux",
    "name" : "CentOS Linux release 7.1.1503 (Core) ",
    "version" : "Kernel 3.10.0-229.7.2.el7.x86_64" },
  "extra" : { "versionString" : "Linux version 3.10.0-229.7.2.el7.x86_64 (builder@kbuilder.dev.centos.org) (gcc version 4.8.3 20140911 (Red Hat 4.8.3-9) (GCC) ) #1 SMP Tue Jun 23 22:06:11 UTC 2015",
    "libcVersion" : "2.17",
    "kernelVersion" : "3.10.0-229.7.2.el7.x86_64",
    "cpuFrequencyMHz" : "1596.149",
    "cpuFeatures" : "fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx lm constant_tsc arch_perfmon pebs bts rep_good nopl aperfmperf pni dtes64 monitor ds_cpl vmx tm2 ssse3 cx16 xtpr pdcm dca lahf_lm dtherm tpr_shadow",
    "pageSize" : { "$numberLong" : "4096" },
    "numPages" : 970635,
    "maxOpenFiles" : 64000 },
  "ok" : 1 }
  • 动态查询

  • 全索引支持,扩展到内部对象和内嵌数组

  • 查询记录分析

  • 快速,就地更新

  • 高效存储二进制大对象 (比如照片和视频)

  • 复制和故障切换支持

  • Auto- Sharding自动分片支持云级扩展性

  • MapReduce 支持复杂聚合

  • 商业支持,培训和咨询

缺点

  • 不支持事务(进行开发时需要注意,哪些功能需要使用数据库提供的事务支持)
  • MongoDB占用空间过大 (不过这个确定对于目前快速下跌的硬盘价格来说,也不算什么缺点了
  • MongoDB没有如MySQL那样成熟的维护工具,这对于开发和IT运营都是个值得注意的地方
  • 在32位系统上,不支持大于2.5G的数据(很多操作系统都已经抛弃了32位版本,所以这个也算不上什么缺点了,3.4版本已经放弃支持32 位 x86平台)
2018-03-23 09:51:37 a249093278 阅读数 1069

前提:因为需要把BeagleBoard-x15这个开发板当做OpenPLC的Server端,与IO板之间是进行UART通信,所以我这里使用的是UART1(ttyS0)这个串口,但是该引脚默认当成GPIO口,故需要修改dts文件使能该串口。另外,UART3(ttyS2)默认是打开的,已当成串口调试口,可连接开发板与PC端Linux上用minicom进行调试。

开发环境:

1)开发板:BeagleBoard-x15

2)kernel:linux 4.9.69-ti-rt-r85

3)工具:PinMux

一、用PinMux进行引脚复用

1)进入TI提供的云工具页面:https://dev.ti.com/ ,这里面还提供了很多其它有用的工具来帮助大家开发,这里选择PinMux,登录进入主页面,这里选择自己开发板SoC的型号,我这里用AM5728_SR2.0,点击Start

注:和AM5828_SR1.1_beta有什么区别,这里没有细分)

2)进行复用界面


解读:左边是选择你想复用的外围设备,我这里添加UART,中间是配置选项更改Use Case为SW flow control,右边上面是一些配置文件,可以看到这里生成了8个文件,前四个是用于TI官方的RTOS系统,后三个用于linux设备树的更改,最后一个.cvs是一个Excel文档,总结所用到的引脚的相关信息,电压域,上下拉等。右下图是一张引脚功能图,可以看到刚刚被我们复用的是哪个引脚,绿色那两个分别是UART1的rxd(Pin B27),一个是txd(Pin C26)。

二、根据提供的三个Linux 配置文件修改dts

1)三个文件的内容分别为

(1)devicetree


(2)genericFileFormatIOdelay.txt 是个空文件


(3)genericFileFormatPadConf.txt 



我开发板使用的设备树启动文件是am57xx-beagle-x15-revc.dtb,这个二进制文件是由am57xx-beagle-x15-revc.dts编译而成,故我们需要修改的实际是dts,在am57xx-beagle-x15-revc.dts中添加(设备树文件位于{linux-kernel}/KERNEL/arch/arm/boot/dts/)

&dra7_pmx_core {
	gpio_keys_pins_default: gpio_keys_pins_default {
		pinctrl-single,pins = <
			DRA7XX_CORE_IOPAD(0x358C, PIN_INPUT_PULLUP | MUX_MODE14)	/* cam0_field.gpio4_2 */
		>;
	};
	myuart1_pins_default: myuart1_pins_default {
		pinctrl-single,pins = <
			DRA7XX_CORE_IOPAD(0x37E0, PIN_INPUT | MUX_MODE0)
			DRA7XX_CORE_IOPAD(0x37E4, PIN_INPUT | MUX_MODE0)
		>;
	};
};

2)在linux kernel目录下编译生成dtb

        make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- am57xx-beagle-x15-revc.dts

3)替换掉开发板中的dtb文件,然后启动,用 dmesg | grep “ttyS” 查看是否成功 


可以看到ttyS0(UART1)已经成功启动!

三、查找bb-x15的原理图 BEAGLEBOARD_X15_REV_B1.pdf 看UART1对应的引脚是哪两个


通过上图使用3根杜邦线连接P18的30 和60,还有任意一个地线即可进行uart的通信了!

LINUX CMA 初探

阅读数 368

Linux 设备树详解

阅读数 477