|
@@ -40,9 +40,9 @@
|
|
|
|
|
|
/* list of all possible config options supported */
|
|
|
static struct pin_config {
|
|
|
- char *prop_cfg;
|
|
|
- unsigned int cfg_type;
|
|
|
-} pcfgs[] = {
|
|
|
+ const char *property;
|
|
|
+ enum pincfg_type param;
|
|
|
+} cfg_params[] = {
|
|
|
{ "samsung,pin-pud", PINCFG_TYPE_PUD },
|
|
|
{ "samsung,pin-drv", PINCFG_TYPE_DRV },
|
|
|
{ "samsung,pin-con-pdn", PINCFG_TYPE_CON_PDN },
|
|
@@ -59,163 +59,242 @@ static inline struct samsung_pin_bank *gc_to_pin_bank(struct gpio_chip *gc)
|
|
|
return container_of(gc, struct samsung_pin_bank, gpio_chip);
|
|
|
}
|
|
|
|
|
|
-/* check if the selector is a valid pin group selector */
|
|
|
static int samsung_get_group_count(struct pinctrl_dev *pctldev)
|
|
|
{
|
|
|
- struct samsung_pinctrl_drv_data *drvdata;
|
|
|
+ struct samsung_pinctrl_drv_data *pmx = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
|
|
- drvdata = pinctrl_dev_get_drvdata(pctldev);
|
|
|
- return drvdata->nr_groups;
|
|
|
+ return pmx->nr_groups;
|
|
|
}
|
|
|
|
|
|
-/* return the name of the group selected by the group selector */
|
|
|
static const char *samsung_get_group_name(struct pinctrl_dev *pctldev,
|
|
|
- unsigned selector)
|
|
|
+ unsigned group)
|
|
|
{
|
|
|
- struct samsung_pinctrl_drv_data *drvdata;
|
|
|
+ struct samsung_pinctrl_drv_data *pmx = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
|
|
- drvdata = pinctrl_dev_get_drvdata(pctldev);
|
|
|
- return drvdata->pin_groups[selector].name;
|
|
|
+ return pmx->pin_groups[group].name;
|
|
|
}
|
|
|
|
|
|
-/* return the pin numbers associated with the specified group */
|
|
|
static int samsung_get_group_pins(struct pinctrl_dev *pctldev,
|
|
|
- unsigned selector, const unsigned **pins, unsigned *num_pins)
|
|
|
+ unsigned group,
|
|
|
+ const unsigned **pins,
|
|
|
+ unsigned *num_pins)
|
|
|
{
|
|
|
- struct samsung_pinctrl_drv_data *drvdata;
|
|
|
+ struct samsung_pinctrl_drv_data *pmx = pinctrl_dev_get_drvdata(pctldev);
|
|
|
+
|
|
|
+ *pins = pmx->pin_groups[group].pins;
|
|
|
+ *num_pins = pmx->pin_groups[group].num_pins;
|
|
|
|
|
|
- drvdata = pinctrl_dev_get_drvdata(pctldev);
|
|
|
- *pins = drvdata->pin_groups[selector].pins;
|
|
|
- *num_pins = drvdata->pin_groups[selector].num_pins;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-/* create pinctrl_map entries by parsing device tree nodes */
|
|
|
-static int samsung_dt_node_to_map(struct pinctrl_dev *pctldev,
|
|
|
- struct device_node *np, struct pinctrl_map **maps,
|
|
|
- unsigned *nmaps)
|
|
|
+static int reserve_map(struct device *dev, struct pinctrl_map **map,
|
|
|
+ unsigned *reserved_maps, unsigned *num_maps,
|
|
|
+ unsigned reserve)
|
|
|
{
|
|
|
- struct device *dev = pctldev->dev;
|
|
|
- struct pinctrl_map *map;
|
|
|
- unsigned long *cfg = NULL;
|
|
|
- char *gname, *fname;
|
|
|
- int cfg_cnt = 0, map_cnt = 0, idx = 0;
|
|
|
-
|
|
|
- /* count the number of config options specfied in the node */
|
|
|
- for (idx = 0; idx < ARRAY_SIZE(pcfgs); idx++) {
|
|
|
- if (of_find_property(np, pcfgs[idx].prop_cfg, NULL))
|
|
|
- cfg_cnt++;
|
|
|
- }
|
|
|
+ unsigned old_num = *reserved_maps;
|
|
|
+ unsigned new_num = *num_maps + reserve;
|
|
|
+ struct pinctrl_map *new_map;
|
|
|
|
|
|
- /*
|
|
|
- * Find out the number of map entries to create. All the config options
|
|
|
- * can be accomadated into a single config map entry.
|
|
|
- */
|
|
|
- if (cfg_cnt)
|
|
|
- map_cnt = 1;
|
|
|
- if (of_find_property(np, "samsung,pin-function", NULL))
|
|
|
- map_cnt++;
|
|
|
- if (!map_cnt) {
|
|
|
- dev_err(dev, "node %s does not have either config or function "
|
|
|
- "configurations\n", np->name);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
+ if (old_num >= new_num)
|
|
|
+ return 0;
|
|
|
|
|
|
- /* Allocate memory for pin-map entries */
|
|
|
- map = kzalloc(sizeof(*map) * map_cnt, GFP_KERNEL);
|
|
|
- if (!map) {
|
|
|
- dev_err(dev, "could not alloc memory for pin-maps\n");
|
|
|
+ new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL);
|
|
|
+ if (!new_map) {
|
|
|
+ dev_err(dev, "krealloc(map) failed\n");
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
- *nmaps = 0;
|
|
|
|
|
|
- /*
|
|
|
- * Allocate memory for pin group name. The pin group name is derived
|
|
|
- * from the node name from which these map entries are be created.
|
|
|
- */
|
|
|
- gname = kzalloc(strlen(np->name) + GSUFFIX_LEN, GFP_KERNEL);
|
|
|
- if (!gname) {
|
|
|
- dev_err(dev, "failed to alloc memory for group name\n");
|
|
|
- goto free_map;
|
|
|
+ memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map));
|
|
|
+
|
|
|
+ *map = new_map;
|
|
|
+ *reserved_maps = new_num;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int add_map_mux(struct pinctrl_map **map, unsigned *reserved_maps,
|
|
|
+ unsigned *num_maps, const char *group,
|
|
|
+ const char *function)
|
|
|
+{
|
|
|
+ if (WARN_ON(*num_maps == *reserved_maps))
|
|
|
+ return -ENOSPC;
|
|
|
+
|
|
|
+ (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP;
|
|
|
+ (*map)[*num_maps].data.mux.group = group;
|
|
|
+ (*map)[*num_maps].data.mux.function = function;
|
|
|
+ (*num_maps)++;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int add_map_configs(struct device *dev, struct pinctrl_map **map,
|
|
|
+ unsigned *reserved_maps, unsigned *num_maps,
|
|
|
+ const char *group, unsigned long *configs,
|
|
|
+ unsigned num_configs)
|
|
|
+{
|
|
|
+ unsigned long *dup_configs;
|
|
|
+
|
|
|
+ if (WARN_ON(*num_maps == *reserved_maps))
|
|
|
+ return -ENOSPC;
|
|
|
+
|
|
|
+ dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!dup_configs) {
|
|
|
+ dev_err(dev, "kmemdup(configs) failed\n");
|
|
|
+ return -ENOMEM;
|
|
|
}
|
|
|
- sprintf(gname, "%s%s", np->name, GROUP_SUFFIX);
|
|
|
|
|
|
- /*
|
|
|
- * don't have config options? then skip over to creating function
|
|
|
- * map entries.
|
|
|
- */
|
|
|
- if (!cfg_cnt)
|
|
|
- goto skip_cfgs;
|
|
|
-
|
|
|
- /* Allocate memory for config entries */
|
|
|
- cfg = kzalloc(sizeof(*cfg) * cfg_cnt, GFP_KERNEL);
|
|
|
- if (!cfg) {
|
|
|
- dev_err(dev, "failed to alloc memory for configs\n");
|
|
|
- goto free_gname;
|
|
|
+ (*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
|
|
|
+ (*map)[*num_maps].data.configs.group_or_pin = group;
|
|
|
+ (*map)[*num_maps].data.configs.configs = dup_configs;
|
|
|
+ (*map)[*num_maps].data.configs.num_configs = num_configs;
|
|
|
+ (*num_maps)++;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int add_config(struct device *dev, unsigned long **configs,
|
|
|
+ unsigned *num_configs, unsigned long config)
|
|
|
+{
|
|
|
+ unsigned old_num = *num_configs;
|
|
|
+ unsigned new_num = old_num + 1;
|
|
|
+ unsigned long *new_configs;
|
|
|
+
|
|
|
+ new_configs = krealloc(*configs, sizeof(*new_configs) * new_num,
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!new_configs) {
|
|
|
+ dev_err(dev, "krealloc(configs) failed\n");
|
|
|
+ return -ENOMEM;
|
|
|
}
|
|
|
|
|
|
- /* Prepare a list of config settings */
|
|
|
- for (idx = 0, cfg_cnt = 0; idx < ARRAY_SIZE(pcfgs); idx++) {
|
|
|
- u32 value;
|
|
|
- if (!of_property_read_u32(np, pcfgs[idx].prop_cfg, &value))
|
|
|
- cfg[cfg_cnt++] =
|
|
|
- PINCFG_PACK(pcfgs[idx].cfg_type, value);
|
|
|
+ new_configs[old_num] = config;
|
|
|
+
|
|
|
+ *configs = new_configs;
|
|
|
+ *num_configs = new_num;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void samsung_dt_free_map(struct pinctrl_dev *pctldev,
|
|
|
+ struct pinctrl_map *map,
|
|
|
+ unsigned num_maps)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < num_maps; i++)
|
|
|
+ if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
|
|
|
+ kfree(map[i].data.configs.configs);
|
|
|
+
|
|
|
+ kfree(map);
|
|
|
+}
|
|
|
+
|
|
|
+static int samsung_dt_subnode_to_map(struct samsung_pinctrl_drv_data *drvdata,
|
|
|
+ struct device *dev,
|
|
|
+ struct device_node *np,
|
|
|
+ struct pinctrl_map **map,
|
|
|
+ unsigned *reserved_maps,
|
|
|
+ unsigned *num_maps)
|
|
|
+{
|
|
|
+ int ret, i;
|
|
|
+ u32 val;
|
|
|
+ unsigned long config;
|
|
|
+ unsigned long *configs = NULL;
|
|
|
+ unsigned num_configs = 0;
|
|
|
+ unsigned reserve;
|
|
|
+ struct property *prop;
|
|
|
+ const char *group;
|
|
|
+ bool has_func = false;
|
|
|
+
|
|
|
+ ret = of_property_read_u32(np, "samsung,pin-function", &val);
|
|
|
+ if (!ret)
|
|
|
+ has_func = true;
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(cfg_params); i++) {
|
|
|
+ ret = of_property_read_u32(np, cfg_params[i].property, &val);
|
|
|
+ if (!ret) {
|
|
|
+ config = PINCFG_PACK(cfg_params[i].param, val);
|
|
|
+ ret = add_config(dev, &configs, &num_configs, config);
|
|
|
+ if (ret < 0)
|
|
|
+ goto exit;
|
|
|
+ /* EINVAL=missing, which is fine since it's optional */
|
|
|
+ } else if (ret != -EINVAL) {
|
|
|
+ dev_err(dev, "could not parse property %s\n",
|
|
|
+ cfg_params[i].property);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- /* create the config map entry */
|
|
|
- map[*nmaps].data.configs.group_or_pin = gname;
|
|
|
- map[*nmaps].data.configs.configs = cfg;
|
|
|
- map[*nmaps].data.configs.num_configs = cfg_cnt;
|
|
|
- map[*nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
|
|
|
- *nmaps += 1;
|
|
|
-
|
|
|
-skip_cfgs:
|
|
|
- /* create the function map entry */
|
|
|
- if (of_find_property(np, "samsung,pin-function", NULL)) {
|
|
|
- fname = kzalloc(strlen(np->name) + FSUFFIX_LEN, GFP_KERNEL);
|
|
|
- if (!fname) {
|
|
|
- dev_err(dev, "failed to alloc memory for func name\n");
|
|
|
- goto free_cfg;
|
|
|
+ reserve = 0;
|
|
|
+ if (has_func)
|
|
|
+ reserve++;
|
|
|
+ if (num_configs)
|
|
|
+ reserve++;
|
|
|
+ ret = of_property_count_strings(np, "samsung,pins");
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(dev, "could not parse property samsung,pins\n");
|
|
|
+ goto exit;
|
|
|
+ }
|
|
|
+ reserve *= ret;
|
|
|
+
|
|
|
+ ret = reserve_map(dev, map, reserved_maps, num_maps, reserve);
|
|
|
+ if (ret < 0)
|
|
|
+ goto exit;
|
|
|
+
|
|
|
+ of_property_for_each_string(np, "samsung,pins", prop, group) {
|
|
|
+ if (has_func) {
|
|
|
+ ret = add_map_mux(map, reserved_maps,
|
|
|
+ num_maps, group, np->full_name);
|
|
|
+ if (ret < 0)
|
|
|
+ goto exit;
|
|
|
}
|
|
|
- sprintf(fname, "%s%s", np->name, FUNCTION_SUFFIX);
|
|
|
|
|
|
- map[*nmaps].data.mux.group = gname;
|
|
|
- map[*nmaps].data.mux.function = fname;
|
|
|
- map[*nmaps].type = PIN_MAP_TYPE_MUX_GROUP;
|
|
|
- *nmaps += 1;
|
|
|
+ if (num_configs) {
|
|
|
+ ret = add_map_configs(dev, map, reserved_maps,
|
|
|
+ num_maps, group, configs,
|
|
|
+ num_configs);
|
|
|
+ if (ret < 0)
|
|
|
+ goto exit;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- *maps = map;
|
|
|
- return 0;
|
|
|
+ ret = 0;
|
|
|
|
|
|
-free_cfg:
|
|
|
- kfree(cfg);
|
|
|
-free_gname:
|
|
|
- kfree(gname);
|
|
|
-free_map:
|
|
|
- kfree(map);
|
|
|
- return -ENOMEM;
|
|
|
+exit:
|
|
|
+ kfree(configs);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
-/* free the memory allocated to hold the pin-map table */
|
|
|
-static void samsung_dt_free_map(struct pinctrl_dev *pctldev,
|
|
|
- struct pinctrl_map *map, unsigned num_maps)
|
|
|
+static int samsung_dt_node_to_map(struct pinctrl_dev *pctldev,
|
|
|
+ struct device_node *np_config,
|
|
|
+ struct pinctrl_map **map,
|
|
|
+ unsigned *num_maps)
|
|
|
{
|
|
|
- int idx;
|
|
|
-
|
|
|
- for (idx = 0; idx < num_maps; idx++) {
|
|
|
- if (map[idx].type == PIN_MAP_TYPE_MUX_GROUP) {
|
|
|
- kfree(map[idx].data.mux.function);
|
|
|
- if (!idx)
|
|
|
- kfree(map[idx].data.mux.group);
|
|
|
- } else if (map->type == PIN_MAP_TYPE_CONFIGS_GROUP) {
|
|
|
- kfree(map[idx].data.configs.configs);
|
|
|
- if (!idx)
|
|
|
- kfree(map[idx].data.configs.group_or_pin);
|
|
|
+ struct samsung_pinctrl_drv_data *drvdata;
|
|
|
+ unsigned reserved_maps;
|
|
|
+ struct device_node *np;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ drvdata = pinctrl_dev_get_drvdata(pctldev);
|
|
|
+
|
|
|
+ reserved_maps = 0;
|
|
|
+ *map = NULL;
|
|
|
+ *num_maps = 0;
|
|
|
+
|
|
|
+ if (!of_get_child_count(np_config))
|
|
|
+ return samsung_dt_subnode_to_map(drvdata, pctldev->dev,
|
|
|
+ np_config, map,
|
|
|
+ &reserved_maps,
|
|
|
+ num_maps);
|
|
|
+
|
|
|
+ for_each_child_of_node(np_config, np) {
|
|
|
+ ret = samsung_dt_subnode_to_map(drvdata, pctldev->dev, np, map,
|
|
|
+ &reserved_maps, num_maps);
|
|
|
+ if (ret < 0) {
|
|
|
+ samsung_dt_free_map(pctldev, *map, *num_maps);
|
|
|
+ return ret;
|
|
|
}
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
- kfree(map);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/* list of pinctrl callbacks for the pinctrl core */
|
|
@@ -286,43 +365,38 @@ static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,
|
|
|
unsigned group, bool enable)
|
|
|
{
|
|
|
struct samsung_pinctrl_drv_data *drvdata;
|
|
|
- const unsigned int *pins;
|
|
|
+ struct samsung_pin_bank_type *type;
|
|
|
struct samsung_pin_bank *bank;
|
|
|
void __iomem *reg;
|
|
|
- u32 mask, shift, data, pin_offset, cnt;
|
|
|
+ u32 mask, shift, data, pin_offset;
|
|
|
unsigned long flags;
|
|
|
+ const struct samsung_pmx_func *func;
|
|
|
+ const struct samsung_pin_group *grp;
|
|
|
|
|
|
drvdata = pinctrl_dev_get_drvdata(pctldev);
|
|
|
- pins = drvdata->pin_groups[group].pins;
|
|
|
+ func = &drvdata->pmx_functions[selector];
|
|
|
+ grp = &drvdata->pin_groups[group];
|
|
|
|
|
|
- /*
|
|
|
- * for each pin in the pin group selected, program the correspoding pin
|
|
|
- * pin function number in the config register.
|
|
|
- */
|
|
|
- for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++) {
|
|
|
- struct samsung_pin_bank_type *type;
|
|
|
-
|
|
|
- pin_to_reg_bank(drvdata, pins[cnt] - drvdata->ctrl->base,
|
|
|
- ®, &pin_offset, &bank);
|
|
|
- type = bank->type;
|
|
|
- mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1;
|
|
|
- shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC];
|
|
|
- if (shift >= 32) {
|
|
|
- /* Some banks have two config registers */
|
|
|
- shift -= 32;
|
|
|
- reg += 4;
|
|
|
- }
|
|
|
+ pin_to_reg_bank(drvdata, grp->pins[0] - drvdata->ctrl->base,
|
|
|
+ ®, &pin_offset, &bank);
|
|
|
+ type = bank->type;
|
|
|
+ mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1;
|
|
|
+ shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC];
|
|
|
+ if (shift >= 32) {
|
|
|
+ /* Some banks have two config registers */
|
|
|
+ shift -= 32;
|
|
|
+ reg += 4;
|
|
|
+ }
|
|
|
|
|
|
- spin_lock_irqsave(&bank->slock, flags);
|
|
|
+ spin_lock_irqsave(&bank->slock, flags);
|
|
|
|
|
|
- data = readl(reg + type->reg_offset[PINCFG_TYPE_FUNC]);
|
|
|
- data &= ~(mask << shift);
|
|
|
- if (enable)
|
|
|
- data |= drvdata->pin_groups[group].func << shift;
|
|
|
- writel(data, reg + type->reg_offset[PINCFG_TYPE_FUNC]);
|
|
|
+ data = readl(reg + type->reg_offset[PINCFG_TYPE_FUNC]);
|
|
|
+ data &= ~(mask << shift);
|
|
|
+ if (enable)
|
|
|
+ data |= func->val << shift;
|
|
|
+ writel(data, reg + type->reg_offset[PINCFG_TYPE_FUNC]);
|
|
|
|
|
|
- spin_unlock_irqrestore(&bank->slock, flags);
|
|
|
- }
|
|
|
+ spin_unlock_irqrestore(&bank->slock, flags);
|
|
|
}
|
|
|
|
|
|
/* enable a specified pinmux by writing to registers */
|
|
@@ -559,87 +633,115 @@ static int samsung_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
|
|
|
return (virq) ? : -ENXIO;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * Parse the pin names listed in the 'samsung,pins' property and convert it
|
|
|
- * into a list of gpio numbers are create a pin group from it.
|
|
|
- */
|
|
|
-static int samsung_pinctrl_parse_dt_pins(struct platform_device *pdev,
|
|
|
- struct device_node *cfg_np,
|
|
|
- struct pinctrl_desc *pctl,
|
|
|
- unsigned int **pin_list,
|
|
|
- unsigned int *npins)
|
|
|
+static struct samsung_pin_group *samsung_pinctrl_create_groups(
|
|
|
+ struct device *dev,
|
|
|
+ struct samsung_pinctrl_drv_data *drvdata,
|
|
|
+ unsigned int *cnt)
|
|
|
{
|
|
|
- struct device *dev = &pdev->dev;
|
|
|
- struct property *prop;
|
|
|
- struct pinctrl_pin_desc const *pdesc = pctl->pins;
|
|
|
- unsigned int idx = 0, cnt;
|
|
|
- const char *pin_name;
|
|
|
+ struct pinctrl_desc *ctrldesc = &drvdata->pctl;
|
|
|
+ struct samsung_pin_group *groups, *grp;
|
|
|
+ const struct pinctrl_pin_desc *pdesc;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ groups = devm_kzalloc(dev, ctrldesc->npins * sizeof(*groups),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!groups)
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+ grp = groups;
|
|
|
+
|
|
|
+ pdesc = ctrldesc->pins;
|
|
|
+ for (i = 0; i < ctrldesc->npins; ++i, ++pdesc, ++grp) {
|
|
|
+ grp->name = pdesc->name;
|
|
|
+ grp->pins = &pdesc->number;
|
|
|
+ grp->num_pins = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ *cnt = ctrldesc->npins;
|
|
|
+ return groups;
|
|
|
+}
|
|
|
|
|
|
- *npins = of_property_count_strings(cfg_np, "samsung,pins");
|
|
|
- if (IS_ERR_VALUE(*npins)) {
|
|
|
- dev_err(dev, "invalid pin list in %s node", cfg_np->name);
|
|
|
+static int samsung_pinctrl_create_function(struct device *dev,
|
|
|
+ struct samsung_pinctrl_drv_data *drvdata,
|
|
|
+ struct device_node *func_np,
|
|
|
+ struct samsung_pmx_func *func)
|
|
|
+{
|
|
|
+ int npins;
|
|
|
+ int ret;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (of_property_read_u32(func_np, "samsung,pin-function", &func->val))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ npins = of_property_count_strings(func_np, "samsung,pins");
|
|
|
+ if (npins < 1) {
|
|
|
+ dev_err(dev, "invalid pin list in %s node", func_np->name);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- *pin_list = devm_kzalloc(dev, *npins * sizeof(**pin_list), GFP_KERNEL);
|
|
|
- if (!*pin_list) {
|
|
|
- dev_err(dev, "failed to allocate memory for pin list\n");
|
|
|
+ func->name = func_np->full_name;
|
|
|
+
|
|
|
+ func->groups = devm_kzalloc(dev, npins * sizeof(char *), GFP_KERNEL);
|
|
|
+ if (!func->groups)
|
|
|
return -ENOMEM;
|
|
|
- }
|
|
|
|
|
|
- of_property_for_each_string(cfg_np, "samsung,pins", prop, pin_name) {
|
|
|
- for (cnt = 0; cnt < pctl->npins; cnt++) {
|
|
|
- if (pdesc[cnt].name) {
|
|
|
- if (!strcmp(pin_name, pdesc[cnt].name)) {
|
|
|
- (*pin_list)[idx++] = pdesc[cnt].number;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if (cnt == pctl->npins) {
|
|
|
- dev_err(dev, "pin %s not valid in %s node\n",
|
|
|
- pin_name, cfg_np->name);
|
|
|
- devm_kfree(dev, *pin_list);
|
|
|
- return -EINVAL;
|
|
|
+ for (i = 0; i < npins; ++i) {
|
|
|
+ const char *gname;
|
|
|
+
|
|
|
+ ret = of_property_read_string_index(func_np, "samsung,pins",
|
|
|
+ i, &gname);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(dev,
|
|
|
+ "failed to read pin name %d from %s node\n",
|
|
|
+ i, func_np->name);
|
|
|
+ return ret;
|
|
|
}
|
|
|
+
|
|
|
+ func->groups[i] = gname;
|
|
|
}
|
|
|
|
|
|
- return 0;
|
|
|
+ func->num_groups = npins;
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * Parse the information about all the available pin groups and pin functions
|
|
|
- * from device node of the pin-controller. A pin group is formed with all
|
|
|
- * the pins listed in the "samsung,pins" property.
|
|
|
- */
|
|
|
-static int samsung_pinctrl_parse_dt(struct platform_device *pdev,
|
|
|
- struct samsung_pinctrl_drv_data *drvdata)
|
|
|
+static struct samsung_pmx_func *samsung_pinctrl_create_functions(
|
|
|
+ struct device *dev,
|
|
|
+ struct samsung_pinctrl_drv_data *drvdata,
|
|
|
+ unsigned int *cnt)
|
|
|
{
|
|
|
- struct device *dev = &pdev->dev;
|
|
|
+ struct samsung_pmx_func *functions, *func;
|
|
|
struct device_node *dev_np = dev->of_node;
|
|
|
struct device_node *cfg_np;
|
|
|
- struct samsung_pin_group *groups, *grp;
|
|
|
- struct samsung_pmx_func *functions, *func;
|
|
|
- unsigned *pin_list;
|
|
|
- unsigned int npins, grp_cnt, func_idx = 0;
|
|
|
- char *gname, *fname;
|
|
|
+ unsigned int func_cnt = 0;
|
|
|
int ret;
|
|
|
|
|
|
- grp_cnt = of_get_child_count(dev_np);
|
|
|
- if (!grp_cnt)
|
|
|
- return -EINVAL;
|
|
|
+ /*
|
|
|
+ * Iterate over all the child nodes of the pin controller node
|
|
|
+ * and create pin groups and pin function lists.
|
|
|
+ */
|
|
|
+ for_each_child_of_node(dev_np, cfg_np) {
|
|
|
+ struct device_node *func_np;
|
|
|
|
|
|
- groups = devm_kzalloc(dev, grp_cnt * sizeof(*groups), GFP_KERNEL);
|
|
|
- if (!groups) {
|
|
|
- dev_err(dev, "failed allocate memory for ping group list\n");
|
|
|
- return -EINVAL;
|
|
|
+ if (!of_get_child_count(cfg_np)) {
|
|
|
+ if (!of_find_property(cfg_np,
|
|
|
+ "samsung,pin-function", NULL))
|
|
|
+ continue;
|
|
|
+ ++func_cnt;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ for_each_child_of_node(cfg_np, func_np) {
|
|
|
+ if (!of_find_property(func_np,
|
|
|
+ "samsung,pin-function", NULL))
|
|
|
+ continue;
|
|
|
+ ++func_cnt;
|
|
|
+ }
|
|
|
}
|
|
|
- grp = groups;
|
|
|
|
|
|
- functions = devm_kzalloc(dev, grp_cnt * sizeof(*functions), GFP_KERNEL);
|
|
|
+ functions = devm_kzalloc(dev, func_cnt * sizeof(*functions),
|
|
|
+ GFP_KERNEL);
|
|
|
if (!functions) {
|
|
|
dev_err(dev, "failed to allocate memory for function list\n");
|
|
|
- return -EINVAL;
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
}
|
|
|
func = functions;
|
|
|
|
|
@@ -647,61 +749,68 @@ static int samsung_pinctrl_parse_dt(struct platform_device *pdev,
|
|
|
* Iterate over all the child nodes of the pin controller node
|
|
|
* and create pin groups and pin function lists.
|
|
|
*/
|
|
|
+ func_cnt = 0;
|
|
|
for_each_child_of_node(dev_np, cfg_np) {
|
|
|
- u32 function;
|
|
|
- if (!of_find_property(cfg_np, "samsung,pins", NULL))
|
|
|
+ struct device_node *func_np;
|
|
|
+
|
|
|
+ if (!of_get_child_count(cfg_np)) {
|
|
|
+ ret = samsung_pinctrl_create_function(dev, drvdata,
|
|
|
+ cfg_np, func);
|
|
|
+ if (ret < 0)
|
|
|
+ return ERR_PTR(ret);
|
|
|
+ if (ret > 0) {
|
|
|
+ ++func;
|
|
|
+ ++func_cnt;
|
|
|
+ }
|
|
|
continue;
|
|
|
+ }
|
|
|
|
|
|
- ret = samsung_pinctrl_parse_dt_pins(pdev, cfg_np,
|
|
|
- &drvdata->pctl, &pin_list, &npins);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
-
|
|
|
- /* derive pin group name from the node name */
|
|
|
- gname = devm_kzalloc(dev, strlen(cfg_np->name) + GSUFFIX_LEN,
|
|
|
- GFP_KERNEL);
|
|
|
- if (!gname) {
|
|
|
- dev_err(dev, "failed to alloc memory for group name\n");
|
|
|
- return -ENOMEM;
|
|
|
+ for_each_child_of_node(cfg_np, func_np) {
|
|
|
+ ret = samsung_pinctrl_create_function(dev, drvdata,
|
|
|
+ func_np, func);
|
|
|
+ if (ret < 0)
|
|
|
+ return ERR_PTR(ret);
|
|
|
+ if (ret > 0) {
|
|
|
+ ++func;
|
|
|
+ ++func_cnt;
|
|
|
+ }
|
|
|
}
|
|
|
- sprintf(gname, "%s%s", cfg_np->name, GROUP_SUFFIX);
|
|
|
+ }
|
|
|
|
|
|
- grp->name = gname;
|
|
|
- grp->pins = pin_list;
|
|
|
- grp->num_pins = npins;
|
|
|
- of_property_read_u32(cfg_np, "samsung,pin-function", &function);
|
|
|
- grp->func = function;
|
|
|
- grp++;
|
|
|
+ *cnt = func_cnt;
|
|
|
+ return functions;
|
|
|
+}
|
|
|
|
|
|
- if (!of_find_property(cfg_np, "samsung,pin-function", NULL))
|
|
|
- continue;
|
|
|
+/*
|
|
|
+ * Parse the information about all the available pin groups and pin functions
|
|
|
+ * from device node of the pin-controller. A pin group is formed with all
|
|
|
+ * the pins listed in the "samsung,pins" property.
|
|
|
+ */
|
|
|
|
|
|
- /* derive function name from the node name */
|
|
|
- fname = devm_kzalloc(dev, strlen(cfg_np->name) + FSUFFIX_LEN,
|
|
|
- GFP_KERNEL);
|
|
|
- if (!fname) {
|
|
|
- dev_err(dev, "failed to alloc memory for func name\n");
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
- sprintf(fname, "%s%s", cfg_np->name, FUNCTION_SUFFIX);
|
|
|
-
|
|
|
- func->name = fname;
|
|
|
- func->groups = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL);
|
|
|
- if (!func->groups) {
|
|
|
- dev_err(dev, "failed to alloc memory for group list "
|
|
|
- "in pin function");
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
- func->groups[0] = gname;
|
|
|
- func->num_groups = 1;
|
|
|
- func++;
|
|
|
- func_idx++;
|
|
|
+static int samsung_pinctrl_parse_dt(struct platform_device *pdev,
|
|
|
+ struct samsung_pinctrl_drv_data *drvdata)
|
|
|
+{
|
|
|
+ struct device *dev = &pdev->dev;
|
|
|
+ struct samsung_pin_group *groups;
|
|
|
+ struct samsung_pmx_func *functions;
|
|
|
+ unsigned int grp_cnt = 0, func_cnt = 0;
|
|
|
+
|
|
|
+ groups = samsung_pinctrl_create_groups(dev, drvdata, &grp_cnt);
|
|
|
+ if (IS_ERR(groups)) {
|
|
|
+ dev_err(dev, "failed to parse pin groups\n");
|
|
|
+ return PTR_ERR(groups);
|
|
|
+ }
|
|
|
+
|
|
|
+ functions = samsung_pinctrl_create_functions(dev, drvdata, &func_cnt);
|
|
|
+ if (IS_ERR(functions)) {
|
|
|
+ dev_err(dev, "failed to parse pin functions\n");
|
|
|
+ return PTR_ERR(groups);
|
|
|
}
|
|
|
|
|
|
drvdata->pin_groups = groups;
|
|
|
drvdata->nr_groups = grp_cnt;
|
|
|
drvdata->pmx_functions = functions;
|
|
|
- drvdata->nr_functions = func_idx;
|
|
|
+ drvdata->nr_functions = func_cnt;
|
|
|
|
|
|
return 0;
|
|
|
}
|