diff options
author | Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | 2013-06-17 20:50:02 +0200 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2013-06-18 10:57:51 +0200 |
commit | fe1c9a822ce72c6ec8476a2501c412265ee2172c (patch) | |
tree | 743e3e67152fd5883e02a4eded386787d7fa8be5 | |
parent | 3a7f520e63727e14de9567515d8727c2c01fedb4 (diff) |
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: devicetree-discuss@lists.ozlabs.org
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Acked-by: Heiko Stuebner <heiko@sntech.de>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r-- | Documentation/devicetree/bindings/pinctrl/renesas,pfc-pinctrl.txt | 135 | ||||
-rw-r--r-- | drivers/pinctrl/sh-pfc/core.c | 64 | ||||
-rw-r--r-- | drivers/pinctrl/sh-pfc/pinctrl.c | 116 |
3 files changed, 314 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/pinctrl/renesas,pfc-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/renesas,pfc-pinctrl.txt new file mode 100644 index 00000000000..8264cbcdd41 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/renesas,pfc-pinctrl.txt @@ -0,0 +1,135 @@ +* Renesas Pin Function Controller (GPIO and Pin Mux/Config) + +The Pin Function Controller (PFC) is a Pin Mux/Config controller. On SH7372, +SH73A0, R8A73A4 and R8A7740 it also acts as a GPIO controller. + + +Pin Control +----------- + +Required Properties: + + - compatible: should be one of the following. + - "renesas,pfc-r8a73a4": for R8A73A4 (R-Mobile APE6) compatible pin-controller. + - "renesas,pfc-r8a7740": for R8A7740 (R-Mobile A1) compatible pin-controller. + - "renesas,pfc-r8a7778": for R8A7778 (R-Mobile M1) compatible pin-controller. + - "renesas,pfc-r8a7779": for R8A7779 (R-Car H1) compatible pin-controller. + - "renesas,pfc-r8a7790": for R8A7790 (R-Car H2) compatible pin-controller. + - "renesas,pfc-sh7372": for SH7372 (SH-Mobile AP4) compatible pin-controller. + - "renesas,pfc-sh73a0": for SH73A0 (SH-Mobile AG5) compatible pin-controller. + + - reg: Base address and length of each memory resource used by the pin + controller hardware module. + +Optional properties: + + - #gpio-range-cells: Mandatory when the PFC doesn't handle GPIO, forbidden + otherwise. Should be 3. + +The PFC node also acts as a container for pin configuration nodes. Please refer +to pinctrl-bindings.txt in this directory for the definition of the term "pin +configuration node" and for the common pinctrl bindings used by client devices. + +Each pin configuration node represents desired functions to select on a pin +group or a list of pin groups. The functions and pin groups can be specified +directly in the pin configuration node, or grouped in child subnodes. Several +functions can thus be referenced as a single pin configuration node by client +devices. + +A configuration node or subnode must contain a function and reference at least +one pin group. + +All pin configuration nodes and subnodes names are ignored. All of those nodes +are parsed through phandles and processed purely based on their content. + +Pin Configuration Node Properties: + +- renesas,groups : An array of strings, each string containing the name of a pin + group. + +- renesas,function: A string containing the name of the function to mux to the + pin group(s) specified by the renesas,groups property + + Valid values for pin, group and function names can be found in the group and + function arrays of the PFC data file corresponding to the SoC + (drivers/pinctrl/sh-pfc/pfc-*.c) + + +GPIO +---- + +On SH7372, SH73A0, R8A73A4 and R8A7740 the PFC node is also a GPIO controller +node. + +Required Properties: + + - gpio-controller: Marks the device node as a gpio controller. + + - #gpio-cells: Should be 2. The first cell is the GPIO number and the second + cell specifies GPIO flags, as defined in <dt-bindings/gpio/gpio.h>. Only the + GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW flags are supported. + +The syntax of the gpio specifier used by client nodes should be the following +with values derived from the SoC user manual. + + <[phandle of the gpio controller node] + [pin number within the gpio controller] + [flags]> + +On other mach-shmobile platforms GPIO is handled by the gpio-rcar driver. +Please refer to Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt +for documentation of the GPIO device tree bindings on those platforms. + + +Examples +-------- + +Example 1: SH73A0 (SH-Mobile AG5) pin controller node + + pfc: pfc@e6050000 { + compatible = "renesas,pfc-sh73a0"; + reg = <0xe6050000 0x8000>, + <0xe605801c 0x1c>; + gpio-controller; + #gpio-cells = <2>; + }; + +Example 2: A GPIO LED node that references a GPIO + + #include <dt-bindings/gpio/gpio.h> + + leds { + compatible = "gpio-leds"; + led1 { + gpios = <&pfc 20 GPIO_ACTIVE_LOW>; + }; + }; + +Example 3: KZM-A9-GT (SH-Mobile AG5) default pin state hog and pin control maps + for the MMCIF and SCIFA4 devices + + &pfc { + pinctrl-0 = <&scifa4_pins>; + pinctrl-names = "default"; + + mmcif_pins: mmcif { + renesas,groups = "mmc0_data8_0", "mmc0_ctrl_0"; + renesas,function = "mmc0"; + }; + + scifa4_pins: scifa4 { + renesas,groups = "scifa4_data", "scifa4_ctrl"; + renesas,function = "scifa4"; + }; + }; + +Example 4: KZM-A9-GT (SH-Mobile AG5) default pin state for the MMCIF device + + &mmcif { + pinctrl-0 = <&mmcif_pins>; + pinctrl-names = "default"; + + bus-width = <8>; + vmmc-supply = <®_1p8v>; + status = "okay"; + }; diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c index 13f40c59ebf..4eea849a1ad 100644 --- a/drivers/pinctrl/sh-pfc/core.c +++ b/drivers/pinctrl/sh-pfc/core.c @@ -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), }, }; diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c index 3492ec9a33b..7e32bb8c08d 100644 --- a/drivers/pinctrl/sh-pfc/pinctrl.c +++ b/drivers/pinctrl/sh-pfc/pinctrl.c @@ -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) |