summaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/core.c')
-rw-r--r--drivers/pinctrl/core.c90
1 files changed, 58 insertions, 32 deletions
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index fb38e20..d02e8de 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -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;
@@ -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) */
@@ -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;
@@ -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);
@@ -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:
*
@@ -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);
@@ -1738,6 +1744,46 @@ static int pinctrl_check_ops(struct pinctrl_dev *pctldev)
}
/**
+ * 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
* @dev: parent device for this pin controller
@@ -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);
@@ -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;
@@ -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);
OpenPOWER on IntegriCloud