设备驱动中的pinctrl(kernel-4.7)

在查看kernel源码时,很容易忽略大量的英文注释,其实,英文注解很好的提示代码的功用,所以,网上没有好的参考资料,可以参考英文注释来分析。
下面是driver/pinctrl/core.h中关于pinctrl的重要结构体定义:

/**
 * struct pinctrl_dev - pin control class device
 * @node: node to include this pin controller in the global pin controller list
 * @desc: the pin controller descriptor supplied when initializing this pin
 *  controller
 * @pin_desc_tree: each pin descriptor for this pin controller is stored in
 *  this radix tree
 * @gpio_ranges: a list of GPIO ranges that is handled by this pin controller,
 *  ranges are added to this list at runtime
 * @dev: the device entry for this pin controller
 * @owner: module providing the pin controller, used for refcounting
 * @driver_data: driver data for drivers registering to the pin controller
 *  subsystem
 * @p: result of pinctrl_get() for this device
 * @hog_default: default state for pins hogged by this device
 * @hog_sleep: sleep state for pins hogged by this device
 * @mutex: mutex taken on each pin controller specific action
 * @device_root: debugfs root for this device
 */
struct pinctrl_dev {
    struct list_head node;
    struct pinctrl_desc *desc;
    struct radix_tree_root pin_desc_tree;
    struct list_head gpio_ranges;
    struct device *dev;
    struct module *owner;
    void *driver_data;
    struct pinctrl *p;
    struct pinctrl_state *hog_default;
    struct pinctrl_state *hog_sleep;
    struct mutex mutex;
#ifdef CONFIG_DEBUG_FS
    struct dentry *device_root;
#endif
}; 


/**
 * struct pinctrl - per-device pin control state holder
 * @node: global list node
 * @dev: the device using this pin control handle
 * @states: a list of states for this device
 * @state: the current state
 * @dt_maps: the mapping table chunks dynamically parsed from device tree for
 *  this device, if any
 * @users: reference count
 */
struct pinctrl {
    struct list_head node;
    struct device *dev;
    struct list_head states;
    struct pinctrl_state *state;
    struct list_head dt_maps;
    struct kref users;
}; 


/**
 * struct pinctrl_state - a pinctrl state for a device
 * @node: list node for struct pinctrl's @states field
 * @name: the name of this state
 * @settings: a list of settings for this state
 */
struct pinctrl_state {
    struct list_head node;
    const char *name;
    struct list_head settings;
};

下面include/linux/pinctrl.h中pinctrl操作重要的结构体,阅读英文注释了解功用:

/**
 * struct pinctrl_pin_desc - boards/machines provide information on their
 * pins, pads or other muxable units in this struct
 * @number: unique pin number from the global pin number space
 * @name: a name for this pin
 * @drv_data: driver-defined per-pin data. pinctrl core does not touch this
 */
struct pinctrl_pin_desc {
    unsigned number;
    const char *name;
    void *drv_data;
};


/**
 * struct pinctrl_gpio_range - each pin controller can provide subranges of
 * the GPIO number space to be handled by the controller
 * @node: list node for internal use
 * @name: a name for the chip in this range
 * @id: an ID number for the chip in this range
 * @base: base offset of the GPIO range
 * @pin_base: base pin number of the GPIO range if pins == NULL
 * @pins: enumeration of pins in GPIO range or NULL
 * @npins: number of pins in the GPIO range, including the base number
 * @gc: an optional pointer to a gpio_chip
 */
struct pinctrl_gpio_range {
    struct list_head node;
    const char *name;
    unsigned int id;
    unsigned int base;
    unsigned int pin_base;
    unsigned const *pins;
    unsigned int npins;
    struct gpio_chip *gc;
};

/**
 * struct pinctrl_ops - global pin control operations, to be implemented by
 * pin controller drivers.
 * @get_groups_count: Returns the count of total number of groups registered.
 * @get_group_name: return the group name of the pin group
 * @get_group_pins: return an array of pins corresponding to a certain
 *  group selector @pins, and the size of the array in @num_pins
 * @pin_dbg_show: optional debugfs display hook that will provide per-device
 *  info for a certain pin in debugfs
 * @dt_node_to_map: parse a device tree "pin configuration node", and create
 *  mapping table entries for it. These are returned through the @map and
 *  @num_maps output parameters. This function is optional, and may be
 *  omitted for pinctrl drivers that do not support device tree.
 * @dt_free_map: free mapping table entries created via @dt_node_to_map. The
 *  top-level @map pointer must be freed, along with any dynamically
 *  allocated members of the mapping table entries themselves. This
 *  function is optional, and may be omitted for pinctrl drivers that do
 *  not support device tree.
 */
struct pinctrl_ops {
    int (*get_groups_count) (struct pinctrl_dev *pctldev);
    const char *(*get_group_name) (struct pinctrl_dev *pctldev,
                       unsigned selector);
    int (*get_group_pins) (struct pinctrl_dev *pctldev,
                   unsigned selector,
                   const unsigned **pins,
                   unsigned *num_pins);
    void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,
              unsigned offset);
    int (*dt_node_to_map) (struct pinctrl_dev *pctldev,
                   struct device_node *np_config,
                   struct pinctrl_map **map, unsigned *num_maps);
    void (*dt_free_map) (struct pinctrl_dev *pctldev,
                 struct pinctrl_map *map, unsigned num_maps);
};

/**
 * struct pinctrl_desc - pin controller descriptor, register this to pin
 * control subsystem
 * @name: name for the pin controller
 * @pins: an array of pin descriptors describing all the pins handled by
 *  this pin controller
 * @npins: number of descriptors in the array, usually just ARRAY_SIZE()
 *  of the pins field above
 * @pctlops: pin control operation vtable, to support global concepts like
 *  grouping of pins, this is optional.
 * @pmxops: pinmux operations vtable, if you support pinmuxing in your driver
 * @confops: pin config operations vtable, if you support pin configuration in
 *  your driver
 * @owner: module providing the pin controller, used for refcounting
 * @num_custom_params: Number of driver-specific custom parameters to be parsed
 *  from the hardware description
 * @custom_params: List of driver_specific custom parameters to be parsed from
 *  the hardware description
 * @custom_conf_items: Information how to print @params in debugfs, must be
 *  the same size as the @custom_params, i.e. @num_custom_params
 */
struct pinctrl_desc {
    const char *name;
    const struct pinctrl_pin_desc *pins;
    unsigned int npins;
    const struct pinctrl_ops *pctlops;
    const struct pinmux_ops *pmxops;
    const struct pinconf_ops *confops;
    struct module *owner;
#ifdef CONFIG_GENERIC_PINCONF
    unsigned int num_custom_params;
    const struct pinconf_generic_params *custom_params;
    const struct pin_config_item *custom_conf_items;
#endif
};

当内核使用pinctrl,pinctrl模块被初始化,将cpu的pin在pinctrl框架中注册一个pin的描述符,这个描述符包含了每个pin描述符组成的数列,用来对特定的pin进行操作。kernel中给出pinctrl的使用示例,如下:

一个PGA的 chip底层 :

      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

将以上每个pin的控制器和名字都加入到驱动中,一边对pin进行操作:

#include <linux/pinctrl/pinctrl.h>

const struct pinctrl_pin_desc foo_pins[] = {
      PINCTRL_PIN(0, "A8"),
      PINCTRL_PIN(1, "B8"),
      PINCTRL_PIN(2, "C8"),
      ...
      PINCTRL_PIN(61, "F1"),
      PINCTRL_PIN(62, "G1"),
      PINCTRL_PIN(63, "H1"),
};

static struct pinctrl_desc foo_desc = {
    .name = "foo",
    .pins = foo_pins,
    .npins = ARRAY_SIZE(foo_pins),
    .owner = THIS_MODULE,
};

int __init foo_probe(void)
{
    struct pinctrl_dev *pctl;

    pctl = pinctrl_register(&foo_desc, <PARENT>, NULL);
    if (!pctl)
        pr_err("could not register foo pin driver\n");
}

每个具体的pin的名字和功用需要查看芯片手册来进行注册使用。
driver/pinctrl.h中定义了PINCTR_PIN()创建对pin的注册。

/* Convenience macro to define a single named or anonymous pin descriptor */
#define PINCTRL_PIN(a, b) { .number = a, .name = b }
#define PINCTRL_PIN_ANON(a) { .number = a } 

如果需要对一组pin 进行控制操作,则使用pinctrl子系统提供的group管理进行。例如,如果有一组SPI接口使用的pins:on { 0, 8, 16, 24 },
一组I2C使用的pins:on { 24, 25 } ,这两组pinctrl的注册使用可以用pinctlr_ops来实现:

#include <linux/pinctrl/pinctrl.h>

struct foo_group {
    const char *name;
    const unsigned int *pins;
    const unsigned num_pins;
};

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

static const struct foo_group foo_groups[] = {
    {
        .name = "spi0_grp",
        .pins = spi0_pins,
        .num_pins = ARRAY_SIZE(spi0_pins),
    },
    {
        .name = "i2c0_grp",
        .pins = i2c0_pins,
        .num_pins = ARRAY_SIZE(i2c0_pins),
    },
};


static int foo_get_groups_count(struct pinctrl_dev *pctldev)
{
    return ARRAY_SIZE(foo_groups);
}

static const char *foo_get_group_name(struct pinctrl_dev *pctldev,
                       unsigned selector)
{
    return foo_groups[selector].name;
}

static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
                   const unsigned **pins,
                   unsigned *num_pins)
{
    *pins = (unsigned *) foo_groups[selector].pins;
    *num_pins = foo_groups[selector].num_pins;
    return 0;
}

static struct pinctrl_ops foo_pctrl_ops = {
    .get_groups_count = foo_get_groups_count,
    .get_group_name = foo_get_group_name,
    .get_group_pins = foo_get_group_pins,
};


static struct pinctrl_desc foo_desc = {
       ...
       .pctlops = &foo_pctrl_ops,
};

pinctrl子系统将会调用get_groups_count() 来获取定义好的一组pins,然后调用其他函数来获取组中pin名。上面是简单示例,具体的驱动中会需要定义更复杂的group来实现特定的pin操作。

pin的配置在驱动中可以将配置条件到mapping table中,

#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h>
#include "platform_x_pindefs.h"

static int foo_pin_config_get(struct pinctrl_dev *pctldev,
            unsigned offset,
            unsigned long *config)
{
    struct my_conftype conf;

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

    *config = (unsigned long) conf;
}

static int foo_pin_config_set(struct pinctrl_dev *pctldev,
            unsigned offset,
            unsigned long config)
{
    struct my_conftype *conf = (struct my_conftype *) config;

    switch (conf) {
        case PLATFORM_X_PULL_UP:
        ...
        }
    }
}

static int foo_pin_config_group_get (struct pinctrl_dev *pctldev,
            unsigned selector,
            unsigned long *config)
{
    ...
}

static int foo_pin_config_group_set (struct pinctrl_dev *pctldev,
            unsigned selector,
            unsigned long config)
{
    ...
}

static struct pinconf_ops foo_pconf_ops = {
    .pin_config_get = foo_pin_config_get,
    .pin_config_set = foo_pin_config_set,
    .pin_config_group_get = foo_pin_config_group_get,
    .pin_config_group_set = foo_pin_config_group_set,
};

/* Pin config operations are handled by some pin controller */
static struct pinctrl_desc foo_desc = {
    ...
    .confops = &foo_pconf_ops,
};

未完待续。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值