1. 程式人生 > >Linux晶片級移植與底層驅動(基於3.7.4核心)(GPIO&&pinctrl&&clk)

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:[email protected] {

74                                 compatible ="arm,vexpress-sysreg";

75                                 reg = <0x000000x1000>;

76                                gpio-controller;

77                                 #gpio-cells =<2>;

78                         };

VEXPRESS電路板上的MMC控制器會使用該結點GPIO控制器提供的GPIO引腳,則具體的[email protected]裝置結點的會通過-gpios屬性引用GPIO:

111                         [email protected] {

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,