Linux芯片级移植与底层驱动(基于3.7.4内核)(GPIO&&pinctrl&&clk)

6.   GPIO驱动

在drivers/gpio下实现了通用的基于gpiolib的GPIO驱动,其中定义了一个通用的用于描述底层GPIO控制器的gpio_chip结构体,并要求具体的SoC实现gpio_chip结构体的成员函数,最后透过gpiochip_add()注册gpio_chip。

gpio_chip结构体封装了底层的硬件的GPIO enable/disable等操作,它定义为:

  94struct gpio_chip {

  95       const char              *label;

  96       struct device           *dev;

  97       struct module           *owner;

  98

  99       int                    (*request)(struct gpio_chip *chip,

 100                                                unsigned offset);

 101       void                   (*free)(struct gpio_chip *chip,

 102                                               unsigned offset);

 103

 104       int                    (*direction_input)(struct gpio_chip *chip,

 105                                               unsigned offset);

 106       int                    (*get)(struct gpio_chip *chip,

 107                                               unsigned offset);

 108       int                     (*direction_output)(structgpio_chip *chip,

 109                                               unsigned offset, int value);

 110       int                    (*set_debounce)(struct gpio_chip *chip,

 111                                               unsigned offset, unsigned debounce);

 112

 113       void                   (*set)(struct gpio_chip *chip,

 114                                               unsigned offset, int value);

 115

 116       int                    (*to_irq)(struct gpio_chip *chip,

 117                                               unsigned offset);

 118

 119       void                   (*dbg_show)(struct seq_file *s,

 120                                               struct gpio_chip *chip);

 121       int                     base;

 122       u16                     ngpio;

 123       const char              *const*names;

 124       unsigned               can_sleep:1;

 125       unsigned               exported:1;

 126

 127#if defined(CONFIG_OF_GPIO)

 128       /*

 129         * If CONFIG_OF is enabled, then all GPIOcontrollers described in the

 130        * device tree automatically may have an OF translation

 131        */

 132       struct device_node *of_node;

 133       int of_gpio_n_cells;

 134       int (*of_xlate)(struct gpio_chip *gc,

 135                        const structof_phandle_args *gpiospec, u32 *flags);

 136#endif

 137};

透过这层封装,每个具体的要用到GPIO的设备驱动都使用通用的GPIO API来操作GPIO,这些API主要用于GPIO的申请、释放和设置:

intgpio_request(unsigned gpio, const char *label);

voidgpio_free(unsigned gpio);

intgpio_direction_input(unsigned gpio);

intgpio_direction_output(unsigned gpio, int value);

intgpio_set_debounce(unsigned gpio, unsigned debounce);

intgpio_get_value_cansleep(unsigned gpio);

voidgpio_set_value_cansleep(unsigned gpio, int value);

intgpio_request_one(unsigned gpio, unsigned long flags, const char *label);

intgpio_request_array(const struct gpio *array, size_t num);

voidgpio_free_array(const struct gpio *array, size_t num);

intdevm_gpio_request(struct device *dev, unsigned gpio, const char *label);

intdevm_gpio_request_one(struct device *dev, unsigned gpio,

                         unsigned long flags,const char *label);

voiddevm_gpio_free(struct device *dev, unsigned int gpio);

注意,内核中针对内存、IRQ、时钟、GPIO、pinctrl都有devm_开头的API,使用这部分API的时候,内核会有类似于Java资源自动回收机制,因此在代码中做出错处理时,无需释放相关的资源。

对于GPIO而言,特别值得一提的是,内核会创建/sys结点 /sys/class/gpio/gpioN/,透过它我们可以echo值从而改变GPIO的方向、设置和获取GPIO的值。

在拥有Device Tree支持的情况之下,我们可以透过Device Tree来描述某GPIO控制器提供的GPIO引脚被具体设备使用的情况。在GPIO控制器对应的结点中,需定义#gpio-cells 和gpio-controller属性,具体的设备结点则透过xxx-gpios属性来引用GPIO控制器结点及GPIO引脚。

如VEXPRESS电路板 DT文件arch/arm/boot/dts/vexpress-v2m.dtsi中拥有如下GPIO控制器结点:

73                         v2m_sysreg:sysreg@00000 {

74                                 compatible ="arm,vexpress-sysreg";

75                                 reg = <0x000000x1000>;

76                                gpio-controller;

77                                 #gpio-cells =<2>;

78                         };

VEXPRESS电路板上的MMC控制器会使用该结点GPIO控制器提供的GPIO引脚,则具体的mmci@05000设备结点的会通过-gpios属性引用GPIO:

111                         mmci@05000 {

112                                 compatible ="arm,pl180", "arm,primecell";

113                                 reg =<0x05000 0x1000>;

114                                 interrupts =<9 10>;

115                                cd-gpios = <&v2m_sysreg 0 0>;

116                                wp-gpios =<&v2m_sysreg 1 0>;

117                                …

121                         };

其中的cd-gpios用于SD/MMC卡的detection,而wp-gpios用于写保护,MMC主机控制器驱动会透过如下方法获取这2个GPIO,详见于drivers/mmc/host/mmci.c:

1220static void mmci_dt_populate_generic_pdata(struct device_node *np,

1221                                         structmmci_platform_data *pdata)

1222{

1223         int bus_width = 0;

1224

1225         pdata->gpio_wp =of_get_named_gpio(np, "wp-gpios", 0);

1226         pdata->gpio_cd =of_get_named_gpio(np, "cd-gpios", 0);

}


7.   pinctrl驱动

许多SoC内部都包含pin控制器,通过pin控制器的寄存器,我们可以配置一个或者一组引脚的功能和特性。在软件上,Linux内核的pinctrl驱动可以操作pin控制器为我们完成如下工作:

§  枚举并且命名pin控制器可控制的所有引脚;

§  提供引脚复用的能力;

§  提供配置引脚的能力,如驱动能力、上拉下拉、开漏(open drain)等。

pinctrl和引脚

在特定SoC的pinctrl驱动中,我们需要定义引脚。假设有一个PGA封装的芯片的引脚排布如下:

         A   B  C   D   E  F   G   H

 

   8   o   o   o  o   o   o  o   o

 

  7   o   o   o  o   o   o  o   o

  6    o  o   o   o  o   o   o   o

  5   o   o   o  o   o   o  o   o

  4   o   o   o  o   o   o  o   o

  3   o   o   o  o   o   o  o   o

  2   o   o   o  o   o   o  o   o

  1   o   o   o  o   o   o  o   o

在pinctrl驱动初始化的时候,需要向pinctrl子系统注册一个pinctrl_desc描述符,在该描述符中包含所有引脚的列表。可以通过如下代码来注册这个pin控制器并命名其所有引脚:

59#include <linux/pinctrl/pinctrl.h>

60

61const struct pinctrl_pin_descfoo_pins[] = {

62       PINCTRL_PIN(0, "A8"),

63       PINCTRL_PIN(1, "B8"),

64       PINCTRL_PIN(2, "C8"),

65       ...

66       PINCTRL_PIN(61, "F1"),

67       PINCTRL_PIN(62, "G1"),

68       PINCTRL_PIN(63, "H1"),

69};

70

71static struct pinctrl_descfoo_desc = {

72         .name = "foo",

73         .pins = foo_pins,

74         .npins = ARRAY_SIZE(foo_pins),

75         .maxpin = 63,

76         .owner = THIS_MODULE,

77};

78

79int __init foo_probe(void)

80{

81         struct pinctrl_dev *pctl;

82

83         pctl = pinctrl_register(&foo_desc,<PARENT>, NULL);

84         if (IS_ERR(pctl))

85                 pr_err("could not registerfoo pin driver\n");

86}

引脚组(pin group)

在pinctrl子系统中,支持将一组引脚绑定为同一功能。假设{ 0, 8, 16, 24 }这一组引脚承担SPI的功能,而{ 24, 25 }这一组引脚承担I2C接口功能。在驱动的代码中,需要体现这个分组关系,并且为这些分组实现pinctrl_ops的成员函数get_groups_count、get_groups_count和get_groups_count,将pinctrl_ops填充到前文pinctrl_desc的实例foo_desc中。

130#include <linux/pinctrl/pinctrl.h>

131

132struct foo_group {

133         const char *name;

134         const unsigned int *pins;

135         const unsigned num_pins;

136};

137

138static const unsigned int spi0_pins[] = { 0, 8, 16, 24 };

139static const unsigned int i2c0_pins[] = { 24, 25 };

140

141static const struct foo_group foo_groups[] = {

142         {

143                 .name = "spi0_grp",

144                 .pins = spi0_pins,

145                 .num_pins =ARRAY_SIZE(spi0_pins),

146         },

147         {

148                 .name = "i2c0_grp",

149                 .pins = i2c0_pins,

150                 .num_pins =ARRAY_SIZE(i2c0_pins),

151         },

152};

153

154

155static int foo_get_groups_count(struct pinctrl_dev *pctldev)

156{

157         return ARRAY_SIZE(foo_groups);

158}

159

160static const char *foo_get_group_name(struct pinctrl_dev *pctldev,

161                                       unsigned selector)

162{

163         return foo_groups[selector].name;

164}

165

166static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,

167                                unsigned **const pins,

168                                unsigned *const num_pins)

169{

170         *pins = (unsigned *) foo_groups[selector].pins;

171         *num_pins =foo_groups[selector].num_pins;

172         return 0;

173}

174

175static struct pinctrl_opsfoo_pctrl_ops = {

176         .get_groups_count =foo_get_groups_count,

177         .get_group_name = foo_get_group_name,

178         .get_group_pins = foo_get_group_pins,

179};

180

181

182static struct pinctrl_descfoo_desc = {

183        ...

184        .pctlops = &foo_pctrl_ops,

185};

get_groups_count()成员函数用于告知pinctrl子系统该SoC中合法的被选引脚组有多少个,而get_group_name()则提供引脚组的名字,get_group_pins()提供引脚组的引脚表。在设备驱动调用pinctrl通用API使能某一组引脚的对应功能时,pinctrl子系统的核心层会调用上述callback函数。

引脚配置

设备驱动有时候需要配置引脚,譬如可能把引脚设置为高阻或者三态(达到类似断连引脚的效果),或通过某阻值将引脚上拉/下拉以确保默认状态下引脚的电平状态。驱动中可以自定义相应板级引脚配置API的细节,譬如某设备驱动可能通过如下代码将某引脚上拉:

#include<linux/pinctrl/consumer.h>

ret= pin_config_set("foo-dev", "FOO_GPIO_PIN",PLATFORM_X_PULL_UP);

其中的PLATFORM_X_PULL_UP由特定的pinctrl驱动定义。在特定的pinctrl驱动中,需要实现完成这些配置所需要的callback函数(pinctrl_desc的confops成员函数):

222#include <linux/pinctrl/pinctrl.h>

223#include <linux/pinctrl/pinconf.h>

224#include "platform_x_pindefs.h"

225

226static int foo_pin_config_get(struct pinctrl_dev *pctldev,

227                     unsigned offset,

228                     unsigned long *config)

229{

230         struct my_conftype conf;

231

232         ... Find setting for pin @ offset ...

233

234         *config = (unsigned long) conf;

235}

236

237static int foo_pin_config_set(struct pinctrl_dev *pctldev,

238                     unsigned offset,

239                     unsigned long config)

240{

241         struct my_conftype *conf = (structmy_conftype *) config;

242

243         switch (conf) {

244                 case PLATFORM_X_PULL_UP:

245                 ...

246                 }

247         }

248}

249

250static int foo_pin_config_group_get (struct pinctrl_dev *pctldev,

251                     unsigned selector,

252                     unsigned long *config)

253{

254         ...

255}

256

257static int foo_pin_config_group_set (struct pinctrl_dev *pctldev,

258                     unsigned selector,

259                     unsigned long config)

260{

261         ...

262}

263

264static struct pinconf_opsfoo_pconf_ops = {

265         .pin_config_get = foo_pin_config_get,

266         .pin_config_set = foo_pin_config_set,

267         .pin_config_group_get =foo_pin_config_group_get,

268         .pin_config_group_set =foo_pin_config_group_set,

269};

270

271/* Pin config operations are handled by some pin controller */

272static struct pinctrl_descfoo_desc = {

273         ...

274         .confops = &foo_pconf_ops,

275};

其中的pin_config_group_get()、pin_config_group_set()针对的是可同时配置一个引脚组的状态情况,而pin_config_get()、pin_config_set()针对的则是单个引脚的配置。

与GPIO子系统的交互

pinctrl驱动中所覆盖的引脚可能同时可作为GPIO用,内核的GPIO子系统和pinctrl子系统本来是并行工作的,但是有时候需要交叉映射,这种情况下,需要在pinctrl驱动中告知pinctrl子系统核心层GPIO与底层pinctrl驱动所管理的引脚之间的映射关系。假设pinctrl驱动中定义的引脚32~47与gpio_chip实例chip_a的GPIO对应,引脚64~71与gpio_chip实例chip_b的GPIO对应,即映射关系为:

chip a:

  - GPIO range :[32 .. 47]

  - pinrange  : [32 .. 47]

chip b:

  - GPIO range :[48 .. 55]

  - pinrange  : [64 .. 71]

则在特定pinctrl驱动中可以透过如下代码注册2个GPIO范围:

305struct gpio_chip chip_a;

306struct gpio_chip chip_b;

307

308static struct pinctrl_gpio_range gpio_range_a = {

309         .name = "chip a",

310         .id = 0,

311         .base = 32,

312         .pin_base = 32,

313         .npins = 16,

314         .gc = &chip_a;

315};

316

317static struct pinctrl_gpio_range gpio_range_b = {

318         .name = "chip b",

319         .id = 0,

320         .base = 48,

321         .pin_base = 64,

322         .npins = 8,

323         .gc = &chip_b;

324};

325

326{

327         struct pinctrl_dev *pctl;

328         ...

329         pinctrl_add_gpio_range(pctl,&gpio_range_a);

330         pinctrl_add_gpio_range(pctl,&gpio_range_b);

331}

在基于内核gpiolib的GPIO驱动中,若设备驱动需进行GPIO申请gpio_request()和释放gpio_free(),GPIO驱动则会调用pinctrl子系统中的pinctrl_request_gpio()和pinctrl_free_gpio()通用API,pinctrl子系统会查找申请的GPIO和pin的映射关系,并确认引脚是否被其他复用功能所占用。与pinctrl子系统通用层pinctrl_request_gpio()和pinctrl_free_gpio()API对应,在底层的具体pinctrl驱动中,需要实现pinmux_ops结构体的gpio_request_enable()和gpio_disable_free()成员函数。

除了gpio_request_enable()和gpio_disable_free()成员函数外,pinmux_ops结构体主要还用来封装pinmux功能enable/disable的callback函数,下面可以看到它更多的细节。

引脚复用(pinmux)

pinctrl驱动中可处理引脚复用,它定义了FUNCTIONS(功能),驱动可以设置某FUNCTIONS的enable或者disable。各个FUNCTIONS联合起来组成一个一维数组,譬如{ spi0, i2c0, mmc0 }就描述了3个不同的FUNCTIONS。

一个特定的功能总是要求一些引脚组(pingroup)来完成,引脚组的数量为可以为1个或者多个。假设对前文所描述的PGA封装的SoC而言,如下图:

387

388        A   B   C  D   E   F  G   H

389       +---+

390    8  | o | o  o   o   o  o   o   o

391       |   |

392    7  | o | o  o   o   o  o   o   o

393       |   |

394    6  | o | o  o   o   o   o  o   o

395      +---+---+

396    5  | o | o | o  o   o   o  o   o

397      +---+---+               +---+

398    4    o  o   o   o  o   o | o | o

399                               |   |

400    3    o  o   o   o  o   o | o | o

401                               |   |

402    2    o  o   o   o  o   o | o | o

403      +-------+-------+-------+---+---+

404    1  | o   o| o   o | o   o | o | o |

405      +-------+-------+-------+---+---+

I2C功能由{ A5, B5 }引脚组成,而在定义引脚描述的pinctrl_pin_desc结构体实例foo_pins的时候,将它们的序号定义为了{ 24, 25 } ;而SPI功能则由可以由{ A8, A7, A6, A5 }和 { G4, G3, G2, G1 }, 也即{ 0, 8, 16, 24 }和{ 38, 46, 54, 62 }两个引脚组完成(注意在整个系统中,引脚组的名字不会重叠)。

由此,功能和引脚组的组合就可以决定一组引脚在系统里的作用,因此在设置某组引脚的作用时,pinctrl的核心层会将功能的序号以及引脚组的序号传递给底层pinctrl驱动中相关的callback函数。

在整个系统中,驱动或板级代码调用pinmux相关的API获取引脚后,会形成一个(pinctrl、使用引脚的设备、功能、引脚组)的映射关系,假设在某电路板上,将让spi0 设备使用pinctrl0的fspi0功能以及gspi0引脚组,让i2c0设备使用pinctrl0的fi2c0功能和gi2c0引脚组,我们将得到如下的映射关系:

502   {

503     {"map-spi0", spi0, pinctrl0, fspi0,gspi0},

504     {"map-i2c0", i2c0, pinctrl0,fi2c0, gi2c0}

505   }

pinctrl子系统的核心会保证每个引脚的排他性,因此一个引脚如果已经被某设备用掉了,而其他的设备又申请该引脚行驶其他的功能或GPIO,则pinctrl核心层会让该次申请失败。

特定pinctrl驱动中pinmux相关的代码主要处理如何enable/disable某一{功能,引脚组}的组合,譬如,当spi0设备申请pinctrl0的fspi0功能和gspi0引脚组以便将gspi0引脚组配置为SPI接口时,相关的callback被组织进一个pinmux_ops结构体,而该结构体的实例最终成为前文pinctrl_desc的pmxops成员:

562#include <linux/pinctrl/pinctrl.h>

563#include <linux/pinctrl/pinmux.h>

564

565struct foo_group {

566         const char *name;

567         const unsigned int *pins;

568         const unsigned num_pins;

569};

570

571static const unsigned spi0_0_pins[] = { 0, 8, 16, 24 };

572static const unsigned spi0_1_pins[] = { 38, 46, 54, 62 };

573static const unsigned i2c0_pins[] = { 24, 25 };

574static const unsigned mmc0_1_pins[] = { 56, 57 };

575static const unsigned mmc0_2_pins[] = { 58, 59 };

576static const unsigned mmc0_3_pins[] = { 60, 61, 62, 63 };

577

578static const struct foo_group foo_groups[] = {

579         {

580                 .name ="spi0_0_grp",

581                 .pins = spi0_0_pins,

582                 .num_pins =ARRAY_SIZE(spi0_0_pins),

583         },

584         {

585                 .name ="spi0_1_grp",

586                 .pins = spi0_1_pins,

587                 .num_pins = ARRAY_SIZE(spi0_1_pins),

588         },

589         {

590                 .name = "i2c0_grp",

591                 .pins = i2c0_pins,

592                 .num_pins =ARRAY_SIZE(i2c0_pins),

593         },

594         {

595                 .name ="mmc0_1_grp",

596                 .pins = mmc0_1_pins,

597                 .num_pins =ARRAY_SIZE(mmc0_1_pins),

598         },

599         {

600                 .name ="mmc0_2_grp",

601                 .pins = mmc0_2_pins,

602                 .num_pins =ARRAY_SIZE(mmc0_2_pins),

603         },

604         {

605                 .name ="mmc0_3_grp",

606                 .pins = mmc0_3_pins,

607                 .num_pins =ARRAY_SIZE(mmc0_3_pins),

608         },

609};

610

611

612static int foo_get_groups_count(struct pinctrl_dev *pctldev)

613{

614         return ARRAY_SIZE(foo_groups);

615}

616

617static const char *foo_get_group_name(struct pinctrl_dev *pctldev,

618                                       unsigned selector)

619{

620         return foo_groups[selector].name;

621}

622

623static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,

624                                unsigned **const pins,

625                                unsigned *const num_pins)

626{

627         *pins = (unsigned *) foo_groups[selector].pins;

628         *num_pins =foo_groups[selector].num_pins;

629         return 0;

630}

631

632static struct pinctrl_ops foo_pctrl_ops = {

633         .get_groups_count =foo_get_groups_count,

634         .get_group_name = foo_get_group_name,

635         .get_group_pins = foo_get_group_pins,

636};

637

638struct foo_pmx_func {

639         const char *name;

640         const char * const *groups;

641         const unsigned num_groups;

642};

643

644static const char * const spi0_groups[] = { "spi0_0_grp","spi0_1_grp" };

645static const char * const i2c0_groups[] = { "i2c0_grp" };

646static const char * const mmc0_groups[] = { "mmc0_1_grp","mmc0_2_grp",

647                                        "mmc0_3_grp" };

648

649static const struct foo_pmx_func foo_functions[] = {

650         {

651                 .name = "spi0",

652                 .groups = spi0_groups,

653                 .num_groups =ARRAY_SIZE(spi0_groups),

654         },

655         {

656                 .name = "i2c0",

657                 .groups = i2c0_groups,

658                 .num_groups =ARRAY_SIZE(i2c0_groups),

659         },

660         {

661                 .name = "mmc0",

662                 .groups = mmc0_groups,

663                 .num_groups =ARRAY_SIZE(mmc0_groups),

664         },

665};

666

667int foo_get_functions_count(struct pinctrl_dev *pctldev)

668{

669         return ARRAY_SIZE(foo_functions);

670}

671

672const char *foo_get_fname(struct pinctrl_dev *pctldev, unsigned selector)

673{

674         return foo_functions[selector].name;

675}

676

677static int foo_get_groups(struct pinctrl_dev *pctldev, unsigned selector,

678                           const char * const**groups,

679                           unsigned * constnum_groups)

680{

681         *groups = foo_functions[selector].groups;

682         *num_groups =foo_functions[selector].num_groups;

683         return 0;

684}

685

686int foo_enable(struct pinctrl_dev *pctldev, unsigned selector,

687                 unsigned group)

688{

689         u8 regbit = (1 << selector + group);

690

691         writeb((readb(MUX)|regbit), MUX)

692         return 0;

693}

694

695void foo_disable(struct pinctrl_dev *pctldev, unsigned selector,

696                 unsigned group)

697{

698         u8 regbit = (1 << selector +group);

699

700         writeb((readb(MUX) & ~(regbit)),MUX)

701         return 0;

702}

703

704 structpinmux_ops foo_pmxops = {

705         .get_functions_count =foo_get_functions_count,

706         .get_function_name = foo_get_fname,

707         .get_function_groups = foo_get_groups,

708         .enable = foo_enable,

709         .disable = foo_disable,

710};

711

712/* Pinmux operations are handled by some pin controller */

713static struct pinctrl_desc foo_desc = {

714         ...

715         .pctlops = &foo_pctrl_ops,

716         .pmxops = &foo_pmxops,

717};

718

具体的pinctrl、使用引脚的设备、功能、引脚组的映射关系,可以在板文件中透过定义pinctrl_map结构体的实例来展开,如:

828static struct pinctrl_map __initdata mapping[] = {

829        PIN_MAP_MUX_GROUP("foo-i2c.o", PINCTRL_STATE_DEFAULT,"pinctrl-foo", NULL, "i2c0"),

830};

PIN_MAP_MUX_GROUP是一个快捷的宏,用于赋值pinctrl_map的各个成员:

88#define PIN_MAP_MUX_GROUP(dev, state, pinctrl, grp, func)              \

89         {                                                              \

90                 .dev_name = dev,                                        \

91                 .name = state,                                          \

92                 .type =PIN_MAP_TYPE_MUX_GROUP,                        \

93                 .ctrl_dev_name = pinctrl,                               \

94                 .data.mux = {                                           \

95                         .group = grp,                                   \

96                         .function = func,                               \

97                 },                                                      \

98         }

99

当然,这种映射关系最好是在DeviceTree中透过结点的属性进行,具体的结点属性的定义方法依赖于具体的pinctrl驱动,最终在pinctrl驱动中透过pinctrl_ops结构体的.dt_node_to_map()成员函数读出属性并建立映射表。

又由于1个功能可能可由2个不同的引脚组实现,可能形成如下对于同1个功能有2个可选引脚组的pinctrl_map:

staticstruct pinctrl_map __initdata mapping[] = {

        PIN_MAP_MUX_GROUP("foo-spi.0","spi0-pos-A", "pinctrl-foo", "spi0_0_grp", "spi0"),

        PIN_MAP_MUX_GROUP("foo-spi.0","spi0-pos-B", "pinctrl-foo", "spi0_1_grp", "spi0"),

};

在运行时,我们可以透过类似的API去查找并设置位置A的引脚组行驶SPI接口的功能:

954         p = devm_pinctrl_get(dev);

955         s = pinctrl_lookup_state(p, "spi0-pos-A");

956         ret = pinctrl_select_state(p, s);

或者可以更加简单地使用:

p =devm_pinctrl_get_select(dev, "spi0-pos-A");

若想运行时切换位置A和B的引脚组行使SPI的接口功能,代码结构类似:

1163foo_probe()

1164{

1165         /* Setup */

1166         p = devm_pinctrl_get(&device);

1167         if (IS_ERR(p))

1168                 ...

1169

1170         s1 = pinctrl_lookup_state(foo->p," spi0-pos-A");

1171         if (IS_ERR(s1))

1172                 ...

1173

1174         s2 = pinctrl_lookup_state(foo->p," spi0-pos-B");

1175         if (IS_ERR(s2))

1176                 ...

1177}

1178

1179foo_switch()

1180{

1181         /* Enable on position A */

1182         ret = pinctrl_select_state(s1);

1183         if (ret < 0)

1184             ...

1185

1186         ...

1187

1188         /* Enable on position B */

1189         ret = pinctrl_select_state(s2);

1190         if (ret < 0)

1191             ...

1192

1193         ...

1194}

pinctrl子系统中定义了pinctrl_get_select_default()以及有devm_前缀的devm_ pinctrl_get_select() API,许多驱动如drivers/i2c/busses/i2c-imx.c、drivers/leds/leds-gpio.c、drivers/spi/spi-imx.c、drivers/tty/serial/omap-serial.c、sound/soc/mxs/mxs-saif.c都是透过这一API来获取自己的引脚组的。xxx_get_select_default()最终会调用

pinctrl_get_select(dev,PINCTRL_STATE_DEFAULT);

其中PINCTRL_STATE_DEFAULT定义为"default",它描述了缺省状态下某设备的pinmux功能和引脚组映射情况。

8.   clock驱动

在一个SoC中,晶振、PLL、divider和gate等会形成一个clock树形结构,在Linux 2.6中,也存有clk_get_rate()、clk_set_rate()、clk_get_parent()、clk_set_parent()等通用API,但是这些API由每个SoC单独实现,而且各个SoC供应商在实现方面的差异很大,于是内核增加了一个新的common clk框架以解决这个碎片化问题。之所以称为common clk,这个common主要体现在:

§  统一的clk结构体,统一的定义于clk.h中的clk API,这些API会调用到统一的clk_ops中的callback函数;

这个统一的 clk结构体的定义如下:

       struct clk {

               const char              *name;

               const struct clk_ops    *ops;

               struct clk_hw           *hw;

               char                    **parent_names;

               struct clk              **parents;

               struct clk              *parent;

               struct hlist_head       children;

               struct hlist_node       child_node;

               ...

       };

其中的clk_ops定义为:

       struct clk_ops {

               int             (*prepare)(struct clk_hw *hw);

               void            (*unprepare)(struct clk_hw *hw);

               int             (*enable)(struct clk_hw *hw);

               void            (*disable)(struct clk_hw *hw);

               int             (*is_enabled)(struct clk_hw *hw);

               unsigned long   (*recalc_rate)(struct clk_hw *hw,

                                              unsigned long parent_rate);

               long            (*round_rate)(struct clk_hw *hw,unsigned long,

                                              unsigned long *);

               int             (*set_parent)(struct clk_hw *hw,u8 index);

               u8              (*get_parent)(struct clk_hw *hw);

               int             (*set_rate)(struct clk_hw *hw,unsigned long);

               void            (*init)(struct clk_hw *hw);

       };

§  对于具体的SoC如何去实现针对自己SoC的clk驱动, 如何提供硬件特定的callback函数的方法也进行了统一。

在common的clk结构体中,clk_hw是联系clk_ops中callback函数和具体硬件细节的纽带,clk_hw中只包含common clk结构体的指针以及具体硬件的init数据:

structclk_hw {

         struct clk *clk;

         const struct clk_init_data *init;

};

其中的clk_init_data包含了具体时钟的name、可能的parent的name的列表parent_names、可能的parent数量num_parents等,实际上这些name的匹配对建立时钟间的父子关系功不可没:

136struct clk_init_data {

137         const char              *name;

138         const struct clk_ops    *ops;

139         const char              **parent_names;

140         u8                      num_parents;

141         unsigned long           flags;

142};

从clk核心层到具体芯片clk驱动的调用顺序为:

clk_enable(clk);

è clk->ops->enable(clk->hw);

通用的clk API(如clk_enable)在调用底层的clk结构体的clk_ops的成员函数(如clk->ops->enable)时,会将clk->hw传递过去。

一般在具体的驱动中会定义针对特定clk(如foo)的结构体,该结构体中包含clk_hw成员以及硬件私有数据:

structclk_foo {

       struct clk_hw hw;

       ... hardware specific data goes here ...

};

并定义to_clk_foo()宏以便通过clk_hw获取clk_foo:

#defineto_clk_foo(_hw) container_of(_hw, struct clk_foo, hw)

在针对clk_foo的clk_ops的callback函数中我们便可以透过clk_hw和to_clk_foo最终获得硬件私有数据并访问硬件读写寄存器以改变时钟的状态:

structclk_ops clk_foo_ops {

       .enable         = &clk_foo_enable;

       .disable        = &clk_foo_disable;

};

intclk_foo_enable(struct clk_hw *hw)

{

       struct clk_foo *foo;

 

       foo = to_clk_foo(hw);

 

       ... perform magic on foo ...

 

       return 0;

};

在具体的clk驱动中,需要透过clk_register()以及它的变体注册硬件上所有的clk,通过clk_register_clkdev()注册clk与使用clk的设备之间的映射关系,也即进行clk和使用clk的设备之间的绑定,这2个函数的原型为:

struct clk *clk_register(struct device *dev, structclk_hw *hw);

int clk_register_clkdev(struct clk *clk, const char*con_id,

         const char*dev_fmt, ...);

另外,针对不同的clk类型(如固定频率的clk、clk gate、clkdivider等),clk子系统又提供了几个快捷函数以完成clk_register()的过程:

struct clk *clk_register_fixed_rate(struct device*dev, const char *name,

               const char *parent_name, unsigned long flags,

                unsigned long fixed_rate);

struct clk *clk_register_gate(struct device *dev,const char *name,

               const char *parent_name, unsigned long flags,

               void __iomem *reg, u8 bit_idx,

               u8 clk_gate_flags, spinlock_t *lock);

struct clk *clk_register_divider(struct device *dev,const char *name,

               const char *parent_name, unsigned long flags,

               void __iomem *reg, u8 shift, u8 width,

               u8 clk_divider_flags, spinlock_t *lock);

以drivers/clk/clk-prima2.c为例,该驱动对应的芯片SiRFprimaII外围接了一个26MHz的晶振和一个32.768KHz供给的RTC的晶振,在26MHz晶振的后面又有3个PLL,当然PLL后面又接了更多的clk结点,则我们看到它的相关驱动代码形如:

staticunsigned long pll_clk_recalc_rate(struct clk_hw *hw,

     unsigned long parent_rate)

{

     unsigned long fin = parent_rate;

     struct clk_pll *clk = to_pllclk(hw);

     

}

 

staticlong pll_clk_round_rate(struct clk_hw *hw, unsigned long rate,

     unsigned long *parent_rate)

{

     

}

 

staticint pll_clk_set_rate(struct clk_hw *hw, unsigned long rate,

     unsigned long parent_rate)

{

     

}

 

staticstruct clk_ops std_pll_ops = {

     .recalc_rate = pll_clk_recalc_rate,

     .round_rate = pll_clk_round_rate,

     .set_rate = pll_clk_set_rate,

};

 

staticconst char *pll_clk_parents[] = {

     "osc",

};

 

staticstruct clk_init_data clk_pll1_init = {

     .name = "pll1",

     .ops = &std_pll_ops,

     .parent_names = pll_clk_parents,

     .num_parents = ARRAY_SIZE(pll_clk_parents),

};

 

staticstruct clk_init_data clk_pll2_init = {

     .name = "pll2",

     .ops = &std_pll_ops,

     .parent_names = pll_clk_parents,

     .num_parents = ARRAY_SIZE(pll_clk_parents),

};

 

staticstruct clk_init_data clk_pll3_init = {

     .name = "pll3",

     .ops = &std_pll_ops,

     .parent_names = pll_clk_parents,

     .num_parents = ARRAY_SIZE(pll_clk_parents),

};

 

staticstruct clk_pll clk_pll1 = {

     .regofs = SIRFSOC_CLKC_PLL1_CFG0,

     .hw = {

         .init = &clk_pll1_init,

     },

};

 

staticstruct clk_pll clk_pll2 = {

     .regofs = SIRFSOC_CLKC_PLL2_CFG0,

     .hw = {

         .init = &clk_pll2_init,

     },

};

 

staticstruct clk_pll clk_pll3 = {

     .regofs = SIRFSOC_CLKC_PLL3_CFG0,

     .hw = {

         .init = &clk_pll3_init,

     },

};

void__init sirfsoc_of_clk_init(void)

{

     

 

     /* These are always available (RTC and26MHz OSC)*/

     clk = clk_register_fixed_rate(NULL,"rtc", NULL,

         CLK_IS_ROOT, 32768);

     BUG_ON(!clk);

     clk = clk_register_fixed_rate(NULL,"osc", NULL,

         CLK_IS_ROOT, 26000000);

     BUG_ON(!clk);

 

     clk = clk_register(NULL, &clk_pll1.hw);

     BUG_ON(!clk);

     clk = clk_register(NULL, &clk_pll2.hw);

     BUG_ON(!clk);

     clk = clk_register(NULL, &clk_pll3.hw);

     BUG_ON(!clk);

     

     clk = clk_register(NULL, &clk_gps.hw);

     BUG_ON(!clk);

     clk_register_clkdev(clk, NULL,"a8010000.gps");

     

}

    另外,目前内核更加倡导的方法是透过Device Tree来描述电路板上的clk树,以及clk和设备之间的绑定关系。通常我们需要在clk控制器的结点中定义#clock-cells属性,并且在clk驱动中透过of_clk_add_provider()注册clk控制器为一个clk树的提供者(provider),并建立系统中各个clk和index的映射表,如:

       Clock                   ID

       ---------------------------

       rtc                     0

       osc                     1

       pll1                    2

       pll2                    3

       pll3                    4

       mem                     5

       sys                     6

       security                7

       dsp                     8

       gps                     9

       mf                      10

在每个具体的设备中,对应的.dts结点上的clocks = <&clks index>属性指向其引用的clk控制器结点以及使用的clk的index,如:

gps@a8010000{

        compatible ="sirf,prima2-gps";

        reg = <0xa8010000 0x10000>;

        interrupts = <7>;

        clocks = <&clks 9>;

};

要特别强调的是,在具体的设备驱动中,一定要透过通用clk API来操作所有的clk,而不要直接透过读写clk控制器的寄存器来进行,这些API包括:

structclk *clk_get(struct device *dev, const char *id);

structclk *devm_clk_get(struct device *dev, const char *id);

intclk_enable(struct clk *clk);

intclk_prepare(struct clk *clk);

voidclk_unprepare(struct clk *clk);

voidclk_disable(struct clk *clk);

staticinline int clk_prepare_enable(struct clk *clk);

staticinline void clk_disable_unprepare(struct clk *clk);

unsignedlong clk_get_rate(struct clk *clk);

intclk_set_rate(struct clk *clk, unsigned long rate);

structclk *clk_get_parent(struct clk *clk);

intclk_set_parent(struct clk *clk, struct clk *parent);

值得一提的是,名称中含有prepare、unprepare字符串的API是内核后来才加入的,过去只有clk_enable和clk_disable。只有clk_enable和clk_disable带来的问题是,有时候,某些硬件的enable/disable clk可能引起睡眠使得enable/disable不能在原子上下文进行。加上prepare后,把过去的clk_enable分解成不可在原子上下文调用的clk_prepare(该函数可能睡眠)和可以在原子上下文调用的clk_enable。而clk_prepare_enable则同时完成prepare和enable的工作,当然也只能在可能睡眠的上下文调用该API。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值