sh-pfc: Add DT support

Support device instantiation through the device tree. The compatible
property is used to select the SoC pinmux information.

Set the gpio_chip device field to the PFC device to enable automatic
GPIO OF support.

Cc: [email protected]
Signed-off-by: Laurent Pinchart <[email protected]>
Acked-by: Heiko Stuebner <[email protected]>
Signed-off-by: Linus Walleij <[email protected]>
This commit is contained in:
Laurent Pinchart 2013-06-17 20:50:02 +02:00 • committed by Linus Walleij
commit fe1c9a822c
3 changed files with 314 additions and 1 deletions

View file

@ -18,6 +18,8 @@
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pinctrl/machine.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@ -348,13 +350,72 @@ int sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type)
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id sh_pfc_of_table[] = {
#ifdef CONFIG_PINCTRL_PFC_R8A73A4
{
.compatible = "renesas,pfc-r8a73a4",
.data = &r8a73a4_pinmux_info,
},
#endif
#ifdef CONFIG_PINCTRL_PFC_R8A7740
{
.compatible = "renesas,pfc-r8a7740",
.data = &r8a7740_pinmux_info,
},
#endif
#ifdef CONFIG_PINCTRL_PFC_R8A7778
{
.compatible = "renesas,pfc-r8a7778",
.data = &r8a7778_pinmux_info,
},
#endif
#ifdef CONFIG_PINCTRL_PFC_R8A7779
{
.compatible = "renesas,pfc-r8a7779",
.data = &r8a7779_pinmux_info,
},
#endif
#ifdef CONFIG_PINCTRL_PFC_R8A7790
{
.compatible = "renesas,pfc-r8a7790",
.data = &r8a7790_pinmux_info,
},
#endif
#ifdef CONFIG_PINCTRL_PFC_SH7372
{
.compatible = "renesas,pfc-sh7372",
.data = &sh7372_pinmux_info,
},
#endif
#ifdef CONFIG_PINCTRL_PFC_SH73A0
{
.compatible = "renesas,pfc-sh73a0",
.data = &sh73a0_pinmux_info,
},
#endif
{ },
};
MODULE_DEVICE_TABLE(of, sh_pfc_of_table);
#endif
static int sh_pfc_probe(struct platform_device *pdev)
{
const struct platform_device_id *platid = platform_get_device_id(pdev);
#ifdef CONFIG_OF
struct device_node *np = pdev->dev.of_node;
#endif
const struct sh_pfc_soc_info *info;
struct sh_pfc *pfc;
int ret;
info = (void *)pdev->id_entry->driver_data;
#ifdef CONFIG_OF
if (np)
info = of_match_device(sh_pfc_of_table, &pdev->dev)->data;
else
#endif
info = platid ? (const void *)platid->driver_data : NULL;
if (info == NULL)
return -ENODEV;
@ -480,6 +541,7 @@ static struct platform_driver sh_pfc_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(sh_pfc_of_table),
},
};

View file

@ -14,7 +14,9 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h>
@ -72,11 +74,125 @@ static void sh_pfc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
seq_printf(s, "%s", DRV_NAME);
}
static int sh_pfc_dt_subnode_to_map(struct device *dev, struct device_node *np,
struct pinctrl_map **map,
unsigned int *num_maps, unsigned int *index)
{
struct pinctrl_map *maps = *map;
unsigned int nmaps = *num_maps;
unsigned int idx = *index;
const char *function = NULL;
struct property *prop;
const char *group;
int ret;
/* Parse the function and configuration properties. At least a function
* or one configuration must be specified.
*/
ret = of_property_read_string(np, "renesas,function", &function);
if (ret < 0 && ret != -EINVAL) {
dev_err(dev, "Invalid function in DT\n");
return ret;
}
if (!function) {
dev_err(dev, "DT node must contain at least one function\n");
goto done;
}
/* Count the number of groups and reallocate mappings. */
ret = of_property_count_strings(np, "renesas,groups");
if (ret < 0 && ret != -EINVAL) {
dev_err(dev, "Invalid pin groups list in DT\n");
goto done;
}
if (!ret) {
dev_err(dev, "No group provided in DT node\n");
ret = -ENODEV;
goto done;
}
nmaps += ret;
maps = krealloc(maps, sizeof(*maps) * nmaps, GFP_KERNEL);
if (maps == NULL) {
ret = -ENOMEM;
goto done;
}
*map = maps;
*num_maps = nmaps;
/* Iterate over pins and groups and create the mappings. */
of_property_for_each_string(np, "renesas,groups", prop, group) {
maps[idx].type = PIN_MAP_TYPE_MUX_GROUP;
maps[idx].data.mux.group = group;
maps[idx].data.mux.function = function;
idx++;
}
ret = 0;
done:
*index = idx;
return ret;
}
static void sh_pfc_dt_free_map(struct pinctrl_dev *pctldev,
struct pinctrl_map *map, unsigned num_maps)
{
kfree(map);
}
static int sh_pfc_dt_node_to_map(struct pinctrl_dev *pctldev,
struct device_node *np,
struct pinctrl_map **map, unsigned *num_maps)
{
struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
struct device *dev = pmx->pfc->dev;
struct device_node *child;
unsigned int index;
int ret;
*map = NULL;
*num_maps = 0;
index = 0;
for_each_child_of_node(np, child) {
ret = sh_pfc_dt_subnode_to_map(dev, child, map, num_maps,
&index);
if (ret < 0)
goto done;
}
/* If no mapping has been found in child nodes try the config node. */
if (*num_maps == 0) {
ret = sh_pfc_dt_subnode_to_map(dev, np, map, num_maps, &index);
if (ret < 0)
goto done;
}
if (*num_maps)
return 0;
dev_err(dev, "no mapping found in node %s\n", np->full_name);
ret = -EINVAL;
done:
if (ret < 0)
sh_pfc_dt_free_map(pctldev, *map, *num_maps);
return ret;
}
static const struct pinctrl_ops sh_pfc_pinctrl_ops = {
.get_groups_count = sh_pfc_get_groups_count,
.get_group_name = sh_pfc_get_group_name,
.get_group_pins = sh_pfc_get_group_pins,
.pin_dbg_show = sh_pfc_pin_dbg_show,
.dt_node_to_map = sh_pfc_dt_node_to_map,
.dt_free_map = sh_pfc_dt_free_map,
};
static int sh_pfc_get_functions_count(struct pinctrl_dev *pctldev)