Skip to content

Commit

Permalink
pinctrl: core: Use delayed work for hogs
Browse files Browse the repository at this point in the history
Having the pin control framework call pin controller functions
before it's probe has finished is not nice as the pin controller
device driver does not yet have struct pinctrl_dev handle.

Let's fix this issue by adding deferred work for late init. This is
needed to be able to add pinctrl generic helper functions that expect
to know struct pinctrl_dev handle. Note that we now need to call
create_pinctrl() directly as we don't want to add the pin controller
to the list of controllers until the hogs are claimed. We also need
to pass the pinctrl_dev to the device tree parser functions as they
otherwise won't find the right controller at this point.

Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
  • Loading branch information
tmlind authored and linusw committed Dec 30, 2016
1 parent 063589c commit df61b36
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 39 deletions.
90 changes: 58 additions & 32 deletions drivers/pinctrl/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,8 @@ static struct pinctrl_state *create_state(struct pinctrl *p,
return state;
}

static int add_setting(struct pinctrl *p, struct pinctrl_map const *map)
static int add_setting(struct pinctrl *p, struct pinctrl_dev *pctldev,
struct pinctrl_map const *map)
{
struct pinctrl_state *state;
struct pinctrl_setting *setting;
Expand All @@ -744,7 +745,11 @@ static int add_setting(struct pinctrl *p, struct pinctrl_map const *map)

setting->type = map->type;

setting->pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name);
if (pctldev)
setting->pctldev = pctldev;
else
setting->pctldev =
get_pinctrl_dev_from_devname(map->ctrl_dev_name);
if (setting->pctldev == NULL) {
kfree(setting);
/* Do not defer probing of hogs (circular loop) */
Expand Down Expand Up @@ -800,7 +805,8 @@ static struct pinctrl *find_pinctrl(struct device *dev)

static void pinctrl_free(struct pinctrl *p, bool inlist);

static struct pinctrl *create_pinctrl(struct device *dev)
static struct pinctrl *create_pinctrl(struct device *dev,
struct pinctrl_dev *pctldev)
{
struct pinctrl *p;
const char *devname;
Expand All @@ -823,7 +829,7 @@ static struct pinctrl *create_pinctrl(struct device *dev)
INIT_LIST_HEAD(&p->states);
INIT_LIST_HEAD(&p->dt_maps);

ret = pinctrl_dt_to_map(p);
ret = pinctrl_dt_to_map(p, pctldev);
if (ret < 0) {
kfree(p);
return ERR_PTR(ret);
Expand All @@ -838,7 +844,7 @@ static struct pinctrl *create_pinctrl(struct device *dev)
if (strcmp(map->dev_name, devname))
continue;

ret = add_setting(p, map);
ret = add_setting(p, pctldev, map);
/*
* At this point the adding of a setting may:
*
Expand Down Expand Up @@ -899,7 +905,7 @@ struct pinctrl *pinctrl_get(struct device *dev)
return p;
}

return create_pinctrl(dev);
return create_pinctrl(dev, NULL);
}
EXPORT_SYMBOL_GPL(pinctrl_get);

Expand Down Expand Up @@ -1737,6 +1743,46 @@ static int pinctrl_check_ops(struct pinctrl_dev *pctldev)
return 0;
}

/**
* pinctrl_late_init() - finish pin controller device registration
* @work: work struct
*/
static void pinctrl_late_init(struct work_struct *work)
{
struct pinctrl_dev *pctldev;

pctldev = container_of(work, struct pinctrl_dev, late_init.work);

pctldev->p = create_pinctrl(pctldev->dev, pctldev);
if (!IS_ERR(pctldev->p)) {
kref_get(&pctldev->p->users);
pctldev->hog_default =
pinctrl_lookup_state(pctldev->p, PINCTRL_STATE_DEFAULT);
if (IS_ERR(pctldev->hog_default)) {
dev_dbg(pctldev->dev,
"failed to lookup the default state\n");
} else {
if (pinctrl_select_state(pctldev->p,
pctldev->hog_default))
dev_err(pctldev->dev,
"failed to select default state\n");
}

pctldev->hog_sleep =
pinctrl_lookup_state(pctldev->p,
PINCTRL_STATE_SLEEP);
if (IS_ERR(pctldev->hog_sleep))
dev_dbg(pctldev->dev,
"failed to lookup the sleep state\n");
}

mutex_lock(&pinctrldev_list_mutex);
list_add_tail(&pctldev->node, &pinctrldev_list);
mutex_unlock(&pinctrldev_list_mutex);

pinctrl_init_device_debugfs(pctldev);
}

/**
* pinctrl_register() - register a pin controller device
* @pctldesc: descriptor for this pin controller
Expand Down Expand Up @@ -1766,6 +1812,7 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
pctldev->driver_data = driver_data;
INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL);
INIT_LIST_HEAD(&pctldev->gpio_ranges);
INIT_DELAYED_WORK(&pctldev->late_init, pinctrl_late_init);
pctldev->dev = dev;
mutex_init(&pctldev->mutex);

Expand Down Expand Up @@ -1800,32 +1847,10 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
goto out_err;
}

mutex_lock(&pinctrldev_list_mutex);
list_add_tail(&pctldev->node, &pinctrldev_list);
mutex_unlock(&pinctrldev_list_mutex);

pctldev->p = pinctrl_get(pctldev->dev);

if (!IS_ERR(pctldev->p)) {
pctldev->hog_default =
pinctrl_lookup_state(pctldev->p, PINCTRL_STATE_DEFAULT);
if (IS_ERR(pctldev->hog_default)) {
dev_dbg(dev, "failed to lookup the default state\n");
} else {
if (pinctrl_select_state(pctldev->p,
pctldev->hog_default))
dev_err(dev,
"failed to select default state\n");
}

pctldev->hog_sleep =
pinctrl_lookup_state(pctldev->p,
PINCTRL_STATE_SLEEP);
if (IS_ERR(pctldev->hog_sleep))
dev_dbg(dev, "failed to lookup the sleep state\n");
}

pinctrl_init_device_debugfs(pctldev);
if (pinctrl_dt_has_hogs(pctldev))
schedule_delayed_work(&pctldev->late_init, 0);
else
pinctrl_late_init(&pctldev->late_init.work);

return pctldev;

Expand All @@ -1848,6 +1873,7 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev)
if (pctldev == NULL)
return;

cancel_delayed_work_sync(&pctldev->late_init);
mutex_lock(&pctldev->mutex);
pinctrl_remove_device_debugfs(pctldev);
mutex_unlock(&pctldev->mutex);
Expand Down
2 changes: 2 additions & 0 deletions drivers/pinctrl/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct pinctrl_gpio_range;
* @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
* @late_init: delayed work for pin controller to finish registration
* @mutex: mutex taken on each pin controller specific action
* @device_root: debugfs root for this device
*/
Expand All @@ -47,6 +48,7 @@ struct pinctrl_dev {
struct pinctrl *p;
struct pinctrl_state *hog_default;
struct pinctrl_state *hog_sleep;
struct delayed_work late_init;
struct mutex mutex;
#ifdef CONFIG_DEBUG_FS
struct dentry *device_root;
Expand Down
28 changes: 23 additions & 5 deletions drivers/pinctrl/devicetree.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,12 @@ struct pinctrl_dev *of_pinctrl_get(struct device_node *np)
return get_pinctrl_dev_from_of_node(np);
}

static int dt_to_map_one_config(struct pinctrl *p, const char *statename,
static int dt_to_map_one_config(struct pinctrl *p,
struct pinctrl_dev *pctldev,
const char *statename,
struct device_node *np_config)
{
struct device_node *np_pctldev;
struct pinctrl_dev *pctldev;
const struct pinctrl_ops *ops;
int ret;
struct pinctrl_map *map;
Expand All @@ -121,7 +122,8 @@ static int dt_to_map_one_config(struct pinctrl *p, const char *statename,
/* OK let's just assume this will appear later then */
return -EPROBE_DEFER;
}
pctldev = get_pinctrl_dev_from_of_node(np_pctldev);
if (!pctldev)
pctldev = get_pinctrl_dev_from_of_node(np_pctldev);
if (pctldev)
break;
/* Do not defer probing of hogs (circular loop) */
Expand Down Expand Up @@ -166,7 +168,22 @@ static int dt_remember_dummy_state(struct pinctrl *p, const char *statename)
return dt_remember_or_free_map(p, statename, NULL, map, 1);
}

int pinctrl_dt_to_map(struct pinctrl *p)
bool pinctrl_dt_has_hogs(struct pinctrl_dev *pctldev)
{
struct device_node *np;
struct property *prop;
int size;

np = pctldev->dev->of_node;
if (!np)
return false;

prop = of_find_property(np, "pinctrl-0", &size);

return prop ? true : false;
}

int pinctrl_dt_to_map(struct pinctrl *p, struct pinctrl_dev *pctldev)
{
struct device_node *np = p->dev->of_node;
int state, ret;
Expand Down Expand Up @@ -233,7 +250,8 @@ int pinctrl_dt_to_map(struct pinctrl *p)
}

/* Parse the node */
ret = dt_to_map_one_config(p, statename, np_config);
ret = dt_to_map_one_config(p, pctldev, statename,
np_config);
of_node_put(np_config);
if (ret < 0)
goto err;
Expand Down
12 changes: 10 additions & 2 deletions drivers/pinctrl/devicetree.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ struct of_phandle_args;

#ifdef CONFIG_OF

bool pinctrl_dt_has_hogs(struct pinctrl_dev *pctldev);

void pinctrl_dt_free_maps(struct pinctrl *p);
int pinctrl_dt_to_map(struct pinctrl *p);
int pinctrl_dt_to_map(struct pinctrl *p, struct pinctrl_dev *pctldev);

int pinctrl_count_index_with_args(const struct device_node *np,
const char *list_name);
Expand All @@ -32,7 +34,13 @@ int pinctrl_parse_index_with_args(const struct device_node *np,

#else

static inline int pinctrl_dt_to_map(struct pinctrl *p)
static inline bool pinctrl_dt_has_hogs(struct pinctrl_dev *pctldev)
{
return false;
}

static inline int pinctrl_dt_to_map(struct pinctrl *p,
struct pinctrl_dev *pctldev)
{
return 0;
}
Expand Down

0 comments on commit df61b36

Please sign in to comment.