From f233dbca6227703eaae2f67d6d9c79819773f16b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 11 Oct 2016 17:45:59 +0200 Subject: pinctrl: sunxi: Rework the pin config building code In order to support more easily the generic pinctrl properties, rework the pinctrl maps configuration and split it into several sub-functions. One of the side-effects from that rework is that we only parse the pin configuration once, since it's going to be common to every pin, instead of having to parsing once for each pin. Signed-off-by: Maxime Ripard Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 178 +++++++++++++++++++++++++--------- 1 file changed, 130 insertions(+), 48 deletions(-) (limited to 'drivers/pinctrl/sunxi') diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 0facbea5f465..cf7eb799c6de 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -145,6 +145,110 @@ static int sunxi_pctrl_get_group_pins(struct pinctrl_dev *pctldev, return 0; } +static bool sunxi_pctrl_has_bias_prop(struct device_node *node) +{ + return of_find_property(node, "allwinner,pull", NULL); +} + +static bool sunxi_pctrl_has_drive_prop(struct device_node *node) +{ + return of_find_property(node, "allwinner,drive", NULL); +} + +static int sunxi_pctrl_parse_bias_prop(struct device_node *node) +{ + u32 val; + + if (of_property_read_u32(node, "allwinner,pull", &val)) + return -EINVAL; + + switch (val) { + case 1: + return PIN_CONFIG_BIAS_PULL_UP; + case 2: + return PIN_CONFIG_BIAS_PULL_DOWN; + } + + return -EINVAL; +} + +static int sunxi_pctrl_parse_drive_prop(struct device_node *node) +{ + u32 val; + + if (of_property_read_u32(node, "allwinner,drive", &val)) + return -EINVAL; + + return (val + 1) * 10; +} + +static const char *sunxi_pctrl_parse_function_prop(struct device_node *node) +{ + const char *function; + int ret; + + ret = of_property_read_string(node, "allwinner,function", &function); + if (!ret) + return function; + + return NULL; +} + +static const char *sunxi_pctrl_find_pins_prop(struct device_node *node, + int *npins) +{ + int count; + + count = of_property_count_strings(node, "allwinner,pins"); + if (count > 0) { + *npins = count; + return "allwinner,pins"; + } + + return NULL; +} + +static unsigned long *sunxi_pctrl_build_pin_config(struct device_node *node, + unsigned int *len) +{ + unsigned long *pinconfig; + unsigned int configlen = 0, idx = 0; + + if (sunxi_pctrl_has_drive_prop(node)) + configlen++; + if (sunxi_pctrl_has_bias_prop(node)) + configlen++; + + pinconfig = kzalloc(configlen * sizeof(*pinconfig), GFP_KERNEL); + if (!pinconfig) + return NULL; + + if (sunxi_pctrl_has_drive_prop(node)) { + int drive = sunxi_pctrl_parse_drive_prop(node); + if (drive < 0) + goto err_free; + + pinconfig[idx++] = pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH, + drive); + } + + if (sunxi_pctrl_has_bias_prop(node)) { + int pull = sunxi_pctrl_parse_bias_prop(node); + if (pull < 0) + goto err_free; + + pinconfig[idx++] = pinconf_to_config_packed(pull, 0); + } + + + *len = configlen; + return pinconfig; + +err_free: + kfree(pinconfig); + return NULL; +} + static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *node, struct pinctrl_map **map, @@ -153,38 +257,45 @@ static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); unsigned long *pinconfig; struct property *prop; - const char *function; + const char *function, *pin_prop; const char *group; - int ret, nmaps, i = 0; - u32 val; + int ret, npins, nmaps, configlen = 0, i = 0; *map = NULL; *num_maps = 0; - ret = of_property_read_string(node, "allwinner,function", &function); - if (ret) { - dev_err(pctl->dev, - "missing allwinner,function property in node %s\n", + function = sunxi_pctrl_parse_function_prop(node); + if (!function) { + dev_err(pctl->dev, "missing function property in node %s\n", node->name); return -EINVAL; } - nmaps = of_property_count_strings(node, "allwinner,pins") * 2; - if (nmaps < 0) { - dev_err(pctl->dev, - "missing allwinner,pins property in node %s\n", + pin_prop = sunxi_pctrl_find_pins_prop(node, &npins); + if (!pin_prop) { + dev_err(pctl->dev, "missing pins property in node %s\n", node->name); return -EINVAL; } + /* + * We have two maps for each pin: one for the function, one + * for the configuration (bias, strength, etc) + */ + nmaps = npins * 2; *map = kmalloc(nmaps * sizeof(struct pinctrl_map), GFP_KERNEL); if (!*map) return -ENOMEM; - of_property_for_each_string(node, "allwinner,pins", prop, group) { + pinconfig = sunxi_pctrl_build_pin_config(node, &configlen); + if (!pinconfig) { + ret = -EINVAL; + goto err_free_map; + } + + of_property_for_each_string(node, pin_prop, prop, group) { struct sunxi_pinctrl_group *grp = sunxi_pinctrl_find_group_by_name(pctl, group); - int j = 0, configlen = 0; if (!grp) { dev_err(pctl->dev, "unknown pin %s", group); @@ -207,34 +318,6 @@ static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, (*map)[i].type = PIN_MAP_TYPE_CONFIGS_GROUP; (*map)[i].data.configs.group_or_pin = group; - - if (of_find_property(node, "allwinner,drive", NULL)) - configlen++; - if (of_find_property(node, "allwinner,pull", NULL)) - configlen++; - - pinconfig = kzalloc(configlen * sizeof(*pinconfig), GFP_KERNEL); - if (!pinconfig) { - kfree(*map); - return -ENOMEM; - } - - if (!of_property_read_u32(node, "allwinner,drive", &val)) { - u16 strength = (val + 1) * 10; - pinconfig[j++] = - pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH, - strength); - } - - if (!of_property_read_u32(node, "allwinner,pull", &val)) { - enum pin_config_param pull = PIN_CONFIG_END; - if (val == 1) - pull = PIN_CONFIG_BIAS_PULL_UP; - else if (val == 2) - pull = PIN_CONFIG_BIAS_PULL_DOWN; - pinconfig[j++] = pinconf_to_config_packed(pull, 0); - } - (*map)[i].data.configs.configs = pinconfig; (*map)[i].data.configs.num_configs = configlen; @@ -244,19 +327,18 @@ static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, *num_maps = nmaps; return 0; + +err_free_map: + kfree(map); + return ret; } static void sunxi_pctrl_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); - } - + /* All the maps have the same pin config, free only the first one */ + kfree(map[0].data.configs.configs); kfree(map); } -- cgit v1.2.1 From 42676fa4aa87eda4fc762df495d4bde2ddc4bfce Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 11 Oct 2016 17:46:00 +0200 Subject: pinctrl: sunxi: Use macros from bindings header file for DT parsing Since we have some bindings header for our hardcoded flags, let's use them when we can. Acked-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/pinctrl/sunxi') diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index cf7eb799c6de..62a9d012b35f 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -28,6 +28,8 @@ #include #include +#include + #include "../core.h" #include "pinctrl-sunxi.h" @@ -163,9 +165,9 @@ static int sunxi_pctrl_parse_bias_prop(struct device_node *node) return -EINVAL; switch (val) { - case 1: + case SUN4I_PINCTRL_PULL_UP: return PIN_CONFIG_BIAS_PULL_UP; - case 2: + case SUN4I_PINCTRL_PULL_DOWN: return PIN_CONFIG_BIAS_PULL_DOWN; } -- cgit v1.2.1 From 07fe64ba213f36ca8f6ffd8c4d5893f022744fdb Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 11 Oct 2016 17:46:01 +0200 Subject: pinctrl: sunxi: Handle bias disable So far, putting NO_PULL in allwinner,pull was ignored, behaving like if that property was not there at all. Obviously, this is not the right thing to do, and in that case, we really need to just disable the bias. Acked-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/pinctrl/sunxi') diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 62a9d012b35f..c44bf1320e08 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -165,6 +165,8 @@ static int sunxi_pctrl_parse_bias_prop(struct device_node *node) return -EINVAL; switch (val) { + case SUN4I_PINCTRL_NO_PULL: + return PIN_CONFIG_BIAS_DISABLE; case SUN4I_PINCTRL_PULL_UP: return PIN_CONFIG_BIAS_PULL_UP; case SUN4I_PINCTRL_PULL_DOWN: @@ -401,6 +403,12 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, | dlevel << sunxi_dlevel_offset(pin), pctl->membase + sunxi_dlevel_reg(pin)); break; + case PIN_CONFIG_BIAS_DISABLE: + val = readl(pctl->membase + sunxi_pull_reg(pin)); + mask = PULL_PINS_MASK << sunxi_pull_offset(pin); + writel((val & ~mask), + pctl->membase + sunxi_pull_reg(pin)); + break; case PIN_CONFIG_BIAS_PULL_UP: val = readl(pctl->membase + sunxi_pull_reg(pin)); mask = PULL_PINS_MASK << sunxi_pull_offset(pin); -- cgit v1.2.1 From cefbf1a1b29531a970bc2908a50a75d6474fcc38 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 20 Oct 2016 15:49:03 +0200 Subject: pinctrl: sunxi: Support generic binding Our bindings are mostly irrelevant now that we have generic pinctrl bindings that cover exactly the same uses cases. Add support for the new ones, and obviously keep our old binding support in order to keep the ABI stable. Acked-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 48 +++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) (limited to 'drivers/pinctrl/sunxi') diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index c44bf1320e08..7e27204d3eea 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -149,18 +149,33 @@ static int sunxi_pctrl_get_group_pins(struct pinctrl_dev *pctldev, static bool sunxi_pctrl_has_bias_prop(struct device_node *node) { - return of_find_property(node, "allwinner,pull", NULL); + return of_find_property(node, "bias-pull-up", NULL) || + of_find_property(node, "bias-pull-down", NULL) || + of_find_property(node, "bias-disable", NULL) || + of_find_property(node, "allwinner,pull", NULL); } static bool sunxi_pctrl_has_drive_prop(struct device_node *node) { - return of_find_property(node, "allwinner,drive", NULL); + return of_find_property(node, "drive-strength", NULL) || + of_find_property(node, "allwinner,drive", NULL); } static int sunxi_pctrl_parse_bias_prop(struct device_node *node) { u32 val; + /* Try the new style binding */ + if (of_find_property(node, "bias-pull-up", NULL)) + return PIN_CONFIG_BIAS_PULL_UP; + + if (of_find_property(node, "bias-pull-down", NULL)) + return PIN_CONFIG_BIAS_PULL_DOWN; + + if (of_find_property(node, "bias-disable", NULL)) + return PIN_CONFIG_BIAS_DISABLE; + + /* And fall back to the old binding */ if (of_property_read_u32(node, "allwinner,pull", &val)) return -EINVAL; @@ -180,6 +195,21 @@ static int sunxi_pctrl_parse_drive_prop(struct device_node *node) { u32 val; + /* Try the new style binding */ + if (!of_property_read_u32(node, "drive-strength", &val)) { + /* We can't go below 10mA ... */ + if (val < 10) + return -EINVAL; + + /* ... and only up to 40 mA ... */ + if (val > 40) + val = 40; + + /* by steps of 10 mA */ + return rounddown(val, 10); + } + + /* And then fall back to the old binding */ if (of_property_read_u32(node, "allwinner,drive", &val)) return -EINVAL; @@ -191,6 +221,12 @@ static const char *sunxi_pctrl_parse_function_prop(struct device_node *node) const char *function; int ret; + /* Try the generic binding */ + ret = of_property_read_string(node, "function", &function); + if (!ret) + return function; + + /* And fall back to our legacy one */ ret = of_property_read_string(node, "allwinner,function", &function); if (!ret) return function; @@ -203,6 +239,14 @@ static const char *sunxi_pctrl_find_pins_prop(struct device_node *node, { int count; + /* Try the generic binding */ + count = of_property_count_strings(node, "pins"); + if (count > 0) { + *npins = count; + return "pins"; + } + + /* And fall back to our legacy one */ count = of_property_count_strings(node, "allwinner,pins"); if (count > 0) { *npins = count; -- cgit v1.2.1 From e11dee2e98f8abc99ad5336796576a827853ccfa Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 20 Oct 2016 15:49:02 +0200 Subject: pinctrl: sunxi: Deal with configless pins Even though the our binding had the assumption that the allwinner,pull and allwinner,drive properties were optional, the code never took that into account. Fix that. Signed-off-by: Maxime Ripard Acked-by: Chen-Yu Tsai Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 51 +++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 14 deletions(-) (limited to 'drivers/pinctrl/sunxi') diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 7e27204d3eea..ebe2c73d211e 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -261,20 +261,29 @@ static unsigned long *sunxi_pctrl_build_pin_config(struct device_node *node, { unsigned long *pinconfig; unsigned int configlen = 0, idx = 0; + int ret; if (sunxi_pctrl_has_drive_prop(node)) configlen++; if (sunxi_pctrl_has_bias_prop(node)) configlen++; + /* + * If we don't have any configuration, bail out + */ + if (!configlen) + return NULL; + pinconfig = kzalloc(configlen * sizeof(*pinconfig), GFP_KERNEL); if (!pinconfig) - return NULL; + return ERR_PTR(-ENOMEM); if (sunxi_pctrl_has_drive_prop(node)) { int drive = sunxi_pctrl_parse_drive_prop(node); - if (drive < 0) + if (drive < 0) { + ret = drive; goto err_free; + } pinconfig[idx++] = pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH, drive); @@ -282,8 +291,10 @@ static unsigned long *sunxi_pctrl_build_pin_config(struct device_node *node, if (sunxi_pctrl_has_bias_prop(node)) { int pull = sunxi_pctrl_parse_bias_prop(node); - if (pull < 0) + if (pull < 0) { + ret = pull; goto err_free; + } pinconfig[idx++] = pinconf_to_config_packed(pull, 0); } @@ -294,7 +305,7 @@ static unsigned long *sunxi_pctrl_build_pin_config(struct device_node *node, err_free: kfree(pinconfig); - return NULL; + return ERR_PTR(ret); } static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, @@ -328,7 +339,10 @@ static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, /* * We have two maps for each pin: one for the function, one - * for the configuration (bias, strength, etc) + * for the configuration (bias, strength, etc). + * + * We might be slightly overshooting, since we might not have + * any configuration. */ nmaps = npins * 2; *map = kmalloc(nmaps * sizeof(struct pinctrl_map), GFP_KERNEL); @@ -336,8 +350,8 @@ static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, return -ENOMEM; pinconfig = sunxi_pctrl_build_pin_config(node, &configlen); - if (!pinconfig) { - ret = -EINVAL; + if (IS_ERR(pinconfig)) { + ret = PTR_ERR(pinconfig); goto err_free_map; } @@ -364,15 +378,24 @@ static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, i++; - (*map)[i].type = PIN_MAP_TYPE_CONFIGS_GROUP; - (*map)[i].data.configs.group_or_pin = group; - (*map)[i].data.configs.configs = pinconfig; - (*map)[i].data.configs.num_configs = configlen; - - i++; + if (pinconfig) { + (*map)[i].type = PIN_MAP_TYPE_CONFIGS_GROUP; + (*map)[i].data.configs.group_or_pin = group; + (*map)[i].data.configs.configs = pinconfig; + (*map)[i].data.configs.num_configs = configlen; + i++; + } } - *num_maps = nmaps; + *num_maps = i; + + /* + * We know have the number of maps we need, we can resize our + * map array + */ + *map = krealloc(*map, i * sizeof(struct pinctrl_map), GFP_KERNEL); + if (!map) + return -ENOMEM; return 0; -- cgit v1.2.1 From 0c8c6ba00cbf2c0a6164aa41d43d017d65caf321 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sat, 29 Oct 2016 20:00:30 -0400 Subject: pinctrl: sunxi: make bool drivers explicitly non-modular None of the Kconfigs for any of these drivers are tristate, meaning that they currently are not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the drivers there is no doubt they are builtin-only. All drivers get essentially the same change, so they are handled in batch. Changes are (1) use builtin_platform_driver, (2) use init.h header (3) delete module_exit related code, (4) delete MODULE_DEVICE_TABLE, and (5) delete MODULE_LICENCE/MODULE_AUTHOR and associated tags. Since module_platform_driver() uses the same init level priority as builtin_platform_driver() the init ordering remains unchanged with this commit. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We do delete the MODULE_LICENSE etc. tags since all that information is already contained at the top of each file in the comments. Cc: Boris Brezillon Cc: Chen-Yu Tsai Cc: Hans de Goede Cc: Linus Walleij Cc: Patrice Chotard Cc: Hongzhou Yang Cc: Fabian Frederick Cc: Maxime Coquelin Cc: Vishnu Patekar Cc: Mylene Josserand Cc: linux-gpio@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Paul Gortmaker Acked-by: Maxime Ripard Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-gr8.c | 9 ++------- drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c | 9 ++------- drivers/pinctrl/sunxi/pinctrl-sun5i-a10s.c | 9 ++------- drivers/pinctrl/sunxi/pinctrl-sun5i-a13.c | 9 ++------- drivers/pinctrl/sunxi/pinctrl-sun6i-a31-r.c | 10 ++-------- drivers/pinctrl/sunxi/pinctrl-sun6i-a31.c | 9 ++------- drivers/pinctrl/sunxi/pinctrl-sun6i-a31s.c | 9 ++------- drivers/pinctrl/sunxi/pinctrl-sun7i-a20.c | 9 ++------- drivers/pinctrl/sunxi/pinctrl-sun8i-a23-r.c | 11 ++--------- drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c | 10 ++-------- drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c | 9 ++------- drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c | 9 ++------- drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c | 9 ++------- 13 files changed, 26 insertions(+), 95 deletions(-) (limited to 'drivers/pinctrl/sunxi') diff --git a/drivers/pinctrl/sunxi/pinctrl-gr8.c b/drivers/pinctrl/sunxi/pinctrl-gr8.c index 2904d2b7378b..2f232c3a0579 100644 --- a/drivers/pinctrl/sunxi/pinctrl-gr8.c +++ b/drivers/pinctrl/sunxi/pinctrl-gr8.c @@ -12,7 +12,7 @@ * warranty of any kind, whether express or implied. */ -#include +#include #include #include #include @@ -525,7 +525,6 @@ static const struct of_device_id sun5i_gr8_pinctrl_match[] = { { .compatible = "nextthing,gr8-pinctrl", }, {} }; -MODULE_DEVICE_TABLE(of, sun5i_gr8_pinctrl_match); static struct platform_driver sun5i_gr8_pinctrl_driver = { .probe = sun5i_gr8_pinctrl_probe, @@ -534,8 +533,4 @@ static struct platform_driver sun5i_gr8_pinctrl_driver = { .of_match_table = sun5i_gr8_pinctrl_match, }, }; -module_platform_driver(sun5i_gr8_pinctrl_driver); - -MODULE_AUTHOR("Mylene Josserand +#include #include #include #include @@ -1035,7 +1035,6 @@ static const struct of_device_id sun4i_a10_pinctrl_match[] = { { .compatible = "allwinner,sun4i-a10-pinctrl", }, {} }; -MODULE_DEVICE_TABLE(of, sun4i_a10_pinctrl_match); static struct platform_driver sun4i_a10_pinctrl_driver = { .probe = sun4i_a10_pinctrl_probe, @@ -1044,8 +1043,4 @@ static struct platform_driver sun4i_a10_pinctrl_driver = { .of_match_table = sun4i_a10_pinctrl_match, }, }; -module_platform_driver(sun4i_a10_pinctrl_driver); - -MODULE_AUTHOR("Maxime Ripard +#include #include #include #include @@ -674,7 +674,6 @@ static const struct of_device_id sun5i_a10s_pinctrl_match[] = { { .compatible = "allwinner,sun5i-a10s-pinctrl", }, {} }; -MODULE_DEVICE_TABLE(of, sun5i_a10s_pinctrl_match); static struct platform_driver sun5i_a10s_pinctrl_driver = { .probe = sun5i_a10s_pinctrl_probe, @@ -683,8 +682,4 @@ static struct platform_driver sun5i_a10s_pinctrl_driver = { .of_match_table = sun5i_a10s_pinctrl_match, }, }; -module_platform_driver(sun5i_a10s_pinctrl_driver); - -MODULE_AUTHOR("Maxime Ripard +#include #include #include #include @@ -392,7 +392,6 @@ static const struct of_device_id sun5i_a13_pinctrl_match[] = { { .compatible = "allwinner,sun5i-a13-pinctrl", }, {} }; -MODULE_DEVICE_TABLE(of, sun5i_a13_pinctrl_match); static struct platform_driver sun5i_a13_pinctrl_driver = { .probe = sun5i_a13_pinctrl_probe, @@ -401,8 +400,4 @@ static struct platform_driver sun5i_a13_pinctrl_driver = { .of_match_table = sun5i_a13_pinctrl_match, }, }; -module_platform_driver(sun5i_a13_pinctrl_driver); - -MODULE_AUTHOR("Maxime Ripard +#include #include #include #include @@ -136,7 +136,6 @@ static const struct of_device_id sun6i_a31_r_pinctrl_match[] = { { .compatible = "allwinner,sun6i-a31-r-pinctrl", }, {} }; -MODULE_DEVICE_TABLE(of, sun6i_a31_r_pinctrl_match); static struct platform_driver sun6i_a31_r_pinctrl_driver = { .probe = sun6i_a31_r_pinctrl_probe, @@ -145,9 +144,4 @@ static struct platform_driver sun6i_a31_r_pinctrl_driver = { .of_match_table = sun6i_a31_r_pinctrl_match, }, }; -module_platform_driver(sun6i_a31_r_pinctrl_driver); - -MODULE_AUTHOR("Boris Brezillon +#include #include #include #include @@ -934,7 +934,6 @@ static const struct of_device_id sun6i_a31_pinctrl_match[] = { { .compatible = "allwinner,sun6i-a31-pinctrl", }, {} }; -MODULE_DEVICE_TABLE(of, sun6i_a31_pinctrl_match); static struct platform_driver sun6i_a31_pinctrl_driver = { .probe = sun6i_a31_pinctrl_probe, @@ -943,8 +942,4 @@ static struct platform_driver sun6i_a31_pinctrl_driver = { .of_match_table = sun6i_a31_pinctrl_match, }, }; -module_platform_driver(sun6i_a31_pinctrl_driver); - -MODULE_AUTHOR("Maxime Ripard +#include #include #include #include @@ -798,7 +798,6 @@ static const struct of_device_id sun6i_a31s_pinctrl_match[] = { { .compatible = "allwinner,sun6i-a31s-pinctrl", }, {} }; -MODULE_DEVICE_TABLE(of, sun6i_a31s_pinctrl_match); static struct platform_driver sun6i_a31s_pinctrl_driver = { .probe = sun6i_a31s_pinctrl_probe, @@ -807,8 +806,4 @@ static struct platform_driver sun6i_a31s_pinctrl_driver = { .of_match_table = sun6i_a31s_pinctrl_match, }, }; -module_platform_driver(sun6i_a31s_pinctrl_driver); - -MODULE_AUTHOR("Hans de Goede "); -MODULE_DESCRIPTION("Allwinner A31s pinctrl driver"); -MODULE_LICENSE("GPL"); +builtin_platform_driver(sun6i_a31s_pinctrl_driver); diff --git a/drivers/pinctrl/sunxi/pinctrl-sun7i-a20.c b/drivers/pinctrl/sunxi/pinctrl-sun7i-a20.c index 435ad30f45db..b6f4c68ffb39 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun7i-a20.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun7i-a20.c @@ -10,7 +10,7 @@ * warranty of any kind, whether express or implied. */ -#include +#include #include #include #include @@ -1045,7 +1045,6 @@ static const struct of_device_id sun7i_a20_pinctrl_match[] = { { .compatible = "allwinner,sun7i-a20-pinctrl", }, {} }; -MODULE_DEVICE_TABLE(of, sun7i_a20_pinctrl_match); static struct platform_driver sun7i_a20_pinctrl_driver = { .probe = sun7i_a20_pinctrl_probe, @@ -1054,8 +1053,4 @@ static struct platform_driver sun7i_a20_pinctrl_driver = { .of_match_table = sun7i_a20_pinctrl_match, }, }; -module_platform_driver(sun7i_a20_pinctrl_driver); - -MODULE_AUTHOR("Maxime Ripard +#include #include #include #include @@ -123,7 +123,6 @@ static const struct of_device_id sun8i_a23_r_pinctrl_match[] = { { .compatible = "allwinner,sun8i-a23-r-pinctrl", }, {} }; -MODULE_DEVICE_TABLE(of, sun8i_a23_r_pinctrl_match); static struct platform_driver sun8i_a23_r_pinctrl_driver = { .probe = sun8i_a23_r_pinctrl_probe, @@ -132,10 +131,4 @@ static struct platform_driver sun8i_a23_r_pinctrl_driver = { .of_match_table = sun8i_a23_r_pinctrl_match, }, }; -module_platform_driver(sun8i_a23_r_pinctrl_driver); - -MODULE_AUTHOR("Chen-Yu Tsai "); -MODULE_AUTHOR("Boris Brezillon +#include #include #include #include @@ -575,7 +575,6 @@ static const struct of_device_id sun8i_a23_pinctrl_match[] = { { .compatible = "allwinner,sun8i-a23-pinctrl", }, {} }; -MODULE_DEVICE_TABLE(of, sun8i_a23_pinctrl_match); static struct platform_driver sun8i_a23_pinctrl_driver = { .probe = sun8i_a23_pinctrl_probe, @@ -584,9 +583,4 @@ static struct platform_driver sun8i_a23_pinctrl_driver = { .of_match_table = sun8i_a23_pinctrl_match, }, }; -module_platform_driver(sun8i_a23_pinctrl_driver); - -MODULE_AUTHOR("Chen-Yu Tsai "); -MODULE_AUTHOR("Maxime Ripard +#include #include #include #include @@ -498,7 +498,6 @@ static const struct of_device_id sun8i_a33_pinctrl_match[] = { { .compatible = "allwinner,sun8i-a33-pinctrl", }, {} }; -MODULE_DEVICE_TABLE(of, sun8i_a33_pinctrl_match); static struct platform_driver sun8i_a33_pinctrl_driver = { .probe = sun8i_a33_pinctrl_probe, @@ -507,8 +506,4 @@ static struct platform_driver sun8i_a33_pinctrl_driver = { .of_match_table = sun8i_a33_pinctrl_match, }, }; -module_platform_driver(sun8i_a33_pinctrl_driver); - -MODULE_AUTHOR("Vishnu Patekar "); -MODULE_DESCRIPTION("Allwinner a33 pinctrl driver"); -MODULE_LICENSE("GPL"); +builtin_platform_driver(sun8i_a33_pinctrl_driver); diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c index 90b973e15982..9aec1d2232dd 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c @@ -12,7 +12,7 @@ * warranty of any kind, whether express or implied. */ -#include +#include #include #include #include @@ -587,7 +587,6 @@ static const struct of_device_id sun8i_a83t_pinctrl_match[] = { { .compatible = "allwinner,sun8i-a83t-pinctrl", }, {} }; -MODULE_DEVICE_TABLE(of, sun8i_a83t_pinctrl_match); static struct platform_driver sun8i_a83t_pinctrl_driver = { .probe = sun8i_a83t_pinctrl_probe, @@ -596,8 +595,4 @@ static struct platform_driver sun8i_a83t_pinctrl_driver = { .of_match_table = sun8i_a83t_pinctrl_match, }, }; -module_platform_driver(sun8i_a83t_pinctrl_driver); - -MODULE_AUTHOR("Vishnu Patekar "); -MODULE_DESCRIPTION("Allwinner a83t pinctrl driver"); -MODULE_LICENSE("GPL"); +builtin_platform_driver(sun8i_a83t_pinctrl_driver); diff --git a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c index 1b580ba76453..bc14e954d7a2 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c @@ -10,7 +10,7 @@ * warranty of any kind, whether express or implied. */ -#include +#include #include #include #include @@ -733,7 +733,6 @@ static const struct of_device_id sun9i_a80_pinctrl_match[] = { { .compatible = "allwinner,sun9i-a80-pinctrl", }, {} }; -MODULE_DEVICE_TABLE(of, sun9i_a80_pinctrl_match); static struct platform_driver sun9i_a80_pinctrl_driver = { .probe = sun9i_a80_pinctrl_probe, @@ -742,8 +741,4 @@ static struct platform_driver sun9i_a80_pinctrl_driver = { .of_match_table = sun9i_a80_pinctrl_match, }, }; -module_platform_driver(sun9i_a80_pinctrl_driver); - -MODULE_AUTHOR("Maxime Ripard "); -MODULE_DESCRIPTION("Allwinner A80 pinctrl driver"); -MODULE_LICENSE("GPL"); +builtin_platform_driver(sun9i_a80_pinctrl_driver); -- cgit v1.2.1 From 88f01a1bd0e0dbd01b65907023dbe53cf524ea2a Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 11 Nov 2016 10:35:10 +0800 Subject: pinctrl: sunxi: Free configs in pinctrl_map only if it is a config map In the recently refactored sunxi pinctrl library, we are only allocating one set of pin configs for each pinmux setting node. When the pinctrl_map structure is freed, the pin configs should also be freed. However the code assumed the first map would contain the configs, which actually never happens, as the mux function map gets added first. The proper way to do this is to look through all the maps and free the first one whose type is actually PIN_MAP_TYPE_CONFIGS_GROUP. Also slightly expand the comment explaining this. Fixes: f233dbca6227 ("pinctrl: sunxi: Rework the pin config building code") Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers/pinctrl/sunxi') diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index ebe2c73d211e..e199d95af8c0 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -408,8 +408,21 @@ static void sunxi_pctrl_dt_free_map(struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned num_maps) { - /* All the maps have the same pin config, free only the first one */ - kfree(map[0].data.configs.configs); + int i; + + /* pin config is never in the first map */ + for (i = 1; i < num_maps; i++) { + if (map[i].type != PIN_MAP_TYPE_CONFIGS_GROUP) + continue; + + /* + * All the maps share the same pin config, + * free only the first one we find. + */ + kfree(map[i].data.configs.configs); + break; + } + kfree(map); } -- cgit v1.2.1 From 223dba00b4072efc590c7d648f230db1b44186b9 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 11 Nov 2016 17:50:34 +0800 Subject: pinctrl: sunxi: Fix PIN_CONFIG_BIAS_PULL_{DOWN,UP} argument According to pinconf-generic.h, the argument for PIN_CONFIG_BIAS_PULL_{DOWN,UP} is non-zero if the bias is enabled with a pull up/down resistor, zero if it is directly connected to VDD or ground. Since Allwinner hardware uses a weak pull resistor internally, the argument should be 1. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/pinctrl/sunxi') diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index e199d95af8c0..e04edda8629d 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -291,12 +291,16 @@ static unsigned long *sunxi_pctrl_build_pin_config(struct device_node *node, if (sunxi_pctrl_has_bias_prop(node)) { int pull = sunxi_pctrl_parse_bias_prop(node); + int arg = 0; if (pull < 0) { ret = pull; goto err_free; } - pinconfig[idx++] = pinconf_to_config_packed(pull, 0); + if (pull != PIN_CONFIG_BIAS_DISABLE) + arg = 1; /* hardware uses weak pull resistors */ + + pinconfig[idx++] = pinconf_to_config_packed(pull, arg); } -- cgit v1.2.1 From c5fda170e87a4bdaeb278f7e50f7a1f654e94eb5 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 11 Nov 2016 17:50:35 +0800 Subject: pinctrl: sunxi: Add support for fetching pinconf settings from hardware The sunxi pinctrl driver only caches whatever pinconf setting was last set on a given pingroup. This is not particularly helpful, nor is it correct. Fix this by actually reading the hardware registers and returning the correct results or error codes. Also filter out unsupported pinconf settings. Since this driver has a peculiar setup of 1 pin per group, we can support both pin and pingroup pinconf setting read back with the same code. The sunxi_pconf_reg helper and code structure is inspired by pinctrl-msm. With this done we can also claim to support generic pinconf, by setting .is_generic = true in pinconf_ops. Also remove the cached config value. The behavior of this was never correct, as it only cached 1 setting instead of all of them. Since we can now read back settings directly from the hardware, it is no longer required. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 86 +++++++++++++++++++++++++++++++++-- drivers/pinctrl/sunxi/pinctrl-sunxi.h | 1 - 2 files changed, 81 insertions(+), 6 deletions(-) (limited to 'drivers/pinctrl/sunxi') diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index e04edda8629d..ed71bff39869 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -438,15 +438,91 @@ static const struct pinctrl_ops sunxi_pctrl_ops = { .get_group_pins = sunxi_pctrl_get_group_pins, }; +static int sunxi_pconf_reg(unsigned pin, enum pin_config_param param, + u32 *offset, u32 *shift, u32 *mask) +{ + switch (param) { + case PIN_CONFIG_DRIVE_STRENGTH: + *offset = sunxi_dlevel_reg(pin); + *shift = sunxi_dlevel_offset(pin); + *mask = DLEVEL_PINS_MASK; + break; + + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_DISABLE: + *offset = sunxi_pull_reg(pin); + *shift = sunxi_pull_offset(pin); + *mask = PULL_PINS_MASK; + break; + + default: + return -ENOTSUPP; + } + + return 0; +} + +static int sunxi_pconf_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + u32 offset, shift, mask, val; + u16 arg; + int ret; + + pin -= pctl->desc->pin_base; + + ret = sunxi_pconf_reg(pin, param, &offset, &shift, &mask); + if (ret < 0) + return ret; + + val = (readl(pctl->membase + offset) >> shift) & mask; + + switch (pinconf_to_config_param(*config)) { + case PIN_CONFIG_DRIVE_STRENGTH: + arg = (val + 1) * 10; + break; + + case PIN_CONFIG_BIAS_PULL_UP: + if (val != SUN4I_PINCTRL_PULL_UP) + return -EINVAL; + arg = 1; /* hardware is weak pull-up */ + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + if (val != SUN4I_PINCTRL_PULL_DOWN) + return -EINVAL; + arg = 1; /* hardware is weak pull-down */ + break; + + case PIN_CONFIG_BIAS_DISABLE: + if (val != SUN4I_PINCTRL_NO_PULL) + return -EINVAL; + arg = 0; + break; + + default: + /* sunxi_pconf_reg should catch anything unsupported */ + WARN_ON(1); + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + static int sunxi_pconf_group_get(struct pinctrl_dev *pctldev, unsigned group, unsigned long *config) { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct sunxi_pinctrl_group *g = &pctl->groups[group]; - *config = pctl->groups[group].config; - - return 0; + /* We only support 1 pin per group. Chain it to the pin callback */ + return sunxi_pconf_get(pctldev, g->pin, config); } static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, @@ -508,8 +584,6 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, default: break; } - /* cache the config value */ - g->config = configs[i]; } /* for each config */ spin_unlock_irqrestore(&pctl->lock, flags); @@ -518,6 +592,8 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, } static const struct pinconf_ops sunxi_pconf_ops = { + .is_generic = true, + .pin_config_get = sunxi_pconf_get, .pin_config_group_get = sunxi_pconf_group_get, .pin_config_group_set = sunxi_pconf_group_set, }; diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h index 0afce1ab12d0..a7efb31d6523 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h @@ -109,7 +109,6 @@ struct sunxi_pinctrl_function { struct sunxi_pinctrl_group { const char *name; - unsigned long config; unsigned pin; }; -- cgit v1.2.1 From 51814827190214986c452a166718bf12d32211c7 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 11 Nov 2016 17:50:36 +0800 Subject: pinctrl: sunxi: Make sunxi_pconf_group_set use sunxi_pconf_reg helper The sunxi_pconf_reg helper introduced in the last patch gives us the chance to rework sunxi_pconf_group_set to have it match the structure of sunxi_pconf_(group_)get and make it easier to understand. For each config to set, it: 1. checks if the parameter is supported. 2. checks if the argument is within limits. 3. converts argument to the register value. 4. writes to the register with spinlock held. As a result the function now blocks unsupported config parameters, instead of silently ignoring them. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 64 +++++++++++++++++------------------ 1 file changed, 32 insertions(+), 32 deletions(-) (limited to 'drivers/pinctrl/sunxi') diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index ed71bff39869..fa11a3100346 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -532,23 +532,27 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sunxi_pinctrl_group *g = &pctl->groups[group]; - unsigned long flags; unsigned pin = g->pin - pctl->desc->pin_base; - u32 val, mask; - u16 strength; - u8 dlevel; int i; - spin_lock_irqsave(&pctl->lock, flags); - for (i = 0; i < num_configs; i++) { - switch (pinconf_to_config_param(configs[i])) { + enum pin_config_param param; + unsigned long flags; + u32 offset, shift, mask, reg; + u16 arg, val; + int ret; + + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + ret = sunxi_pconf_reg(pin, param, &offset, &shift, &mask); + if (ret < 0) + return ret; + + switch (param) { case PIN_CONFIG_DRIVE_STRENGTH: - strength = pinconf_to_config_argument(configs[i]); - if (strength > 40) { - spin_unlock_irqrestore(&pctl->lock, flags); + if (arg < 10 || arg > 40) return -EINVAL; - } /* * We convert from mA to what the register expects: * 0: 10mA @@ -556,37 +560,33 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, * 2: 30mA * 3: 40mA */ - dlevel = strength / 10 - 1; - val = readl(pctl->membase + sunxi_dlevel_reg(pin)); - mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(pin); - writel((val & ~mask) - | dlevel << sunxi_dlevel_offset(pin), - pctl->membase + sunxi_dlevel_reg(pin)); + val = arg / 10 - 1; break; case PIN_CONFIG_BIAS_DISABLE: - val = readl(pctl->membase + sunxi_pull_reg(pin)); - mask = PULL_PINS_MASK << sunxi_pull_offset(pin); - writel((val & ~mask), - pctl->membase + sunxi_pull_reg(pin)); + val = 0; break; case PIN_CONFIG_BIAS_PULL_UP: - val = readl(pctl->membase + sunxi_pull_reg(pin)); - mask = PULL_PINS_MASK << sunxi_pull_offset(pin); - writel((val & ~mask) | 1 << sunxi_pull_offset(pin), - pctl->membase + sunxi_pull_reg(pin)); + if (arg == 0) + return -EINVAL; + val = 1; break; case PIN_CONFIG_BIAS_PULL_DOWN: - val = readl(pctl->membase + sunxi_pull_reg(pin)); - mask = PULL_PINS_MASK << sunxi_pull_offset(pin); - writel((val & ~mask) | 2 << sunxi_pull_offset(pin), - pctl->membase + sunxi_pull_reg(pin)); + if (arg == 0) + return -EINVAL; + val = 2; break; default: - break; + /* sunxi_pconf_reg should catch anything unsupported */ + WARN_ON(1); + return -ENOTSUPP; } - } /* for each config */ - spin_unlock_irqrestore(&pctl->lock, flags); + spin_lock_irqsave(&pctl->lock, flags); + reg = readl(pctl->membase + offset); + reg &= ~(mask << shift); + writel(reg | val << shift, pctl->membase + offset); + spin_unlock_irqrestore(&pctl->lock, flags); + } /* for each config */ return 0; } -- cgit v1.2.1 From 7c926492d38a3feef4b4b29c91b7c03eb1b8b546 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 14 Nov 2016 21:53:03 +0100 Subject: pinctrl: sunxi: Add support for interrupt debouncing The pin controller found in the Allwinner SoCs has support for interrupts debouncing. However, this is not done per-pin, preventing us from using the generic pinconf binding for that, but per irq bank, which, depending on the SoC, ranges from one to five. Introduce a device-wide property to deal with this using a microsecond resolution. We can re-use the per-pin input-debounce property for that, so let's do it! Signed-off-by: Maxime Ripard Acked-by: Rob Herring Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 84 +++++++++++++++++++++++++++++++++++ drivers/pinctrl/sunxi/pinctrl-sunxi.h | 7 +++ 2 files changed, 91 insertions(+) (limited to 'drivers/pinctrl/sunxi') diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index fa11a3100346..2339d4718b4d 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -1122,6 +1122,88 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev) return 0; } +static int sunxi_pinctrl_get_debounce_div(struct clk *clk, int freq, int *diff) +{ + unsigned long clock = clk_get_rate(clk); + unsigned int best_diff = ~0, best_div; + int i; + + for (i = 0; i < 8; i++) { + int cur_diff = abs(freq - (clock >> i)); + + if (cur_diff < best_diff) { + best_diff = cur_diff; + best_div = i; + } + } + + *diff = best_diff; + return best_div; +} + +static int sunxi_pinctrl_setup_debounce(struct sunxi_pinctrl *pctl, + struct device_node *node) +{ + unsigned int hosc_diff, losc_diff; + unsigned int hosc_div, losc_div; + struct clk *hosc, *losc; + u8 div, src; + int i, ret; + + /* Deal with old DTs that didn't have the oscillators */ + if (of_count_phandle_with_args(node, "clocks", "#clock-cells") != 3) + return 0; + + /* If we don't have any setup, bail out */ + if (!of_find_property(node, "input-debounce", NULL)) + return 0; + + losc = devm_clk_get(pctl->dev, "losc"); + if (IS_ERR(losc)) + return PTR_ERR(losc); + + hosc = devm_clk_get(pctl->dev, "hosc"); + if (IS_ERR(hosc)) + return PTR_ERR(hosc); + + for (i = 0; i < pctl->desc->irq_banks; i++) { + unsigned long debounce_freq; + u32 debounce; + + ret = of_property_read_u32_index(node, "input-debounce", + i, &debounce); + if (ret) + return ret; + + if (!debounce) + continue; + + debounce_freq = DIV_ROUND_CLOSEST(USEC_PER_SEC, debounce); + losc_div = sunxi_pinctrl_get_debounce_div(losc, + debounce_freq, + &losc_diff); + + hosc_div = sunxi_pinctrl_get_debounce_div(hosc, + debounce_freq, + &hosc_diff); + + if (hosc_diff < losc_diff) { + div = hosc_div; + src = 1; + } else { + div = losc_div; + src = 0; + } + + writel(src | div << 4, + pctl->membase + + sunxi_irq_debounce_reg_from_bank(i, + pctl->desc->irq_bank_base)); + } + + return 0; +} + int sunxi_pinctrl_init(struct platform_device *pdev, const struct sunxi_pinctrl_desc *desc) { @@ -1284,6 +1366,8 @@ int sunxi_pinctrl_init(struct platform_device *pdev, pctl); } + sunxi_pinctrl_setup_debounce(pctl, node); + dev_info(&pdev->dev, "initialized sunXi PIO driver\n"); return 0; diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h index a7efb31d6523..f78a44a03189 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h @@ -69,6 +69,8 @@ #define IRQ_STATUS_IRQ_BITS 1 #define IRQ_STATUS_IRQ_MASK ((1 << IRQ_STATUS_IRQ_BITS) - 1) +#define IRQ_DEBOUNCE_REG 0x218 + #define IRQ_MEM_SIZE 0x20 #define IRQ_EDGE_RISING 0x00 @@ -265,6 +267,11 @@ static inline u32 sunxi_irq_ctrl_offset(u16 irq) return irq_num * IRQ_CTRL_IRQ_BITS; } +static inline u32 sunxi_irq_debounce_reg_from_bank(u8 bank, unsigned bank_base) +{ + return IRQ_DEBOUNCE_REG + (bank_base + bank) * IRQ_MEM_SIZE; +} + static inline u32 sunxi_irq_status_reg_from_bank(u8 bank, unsigned bank_base) { return IRQ_STATUS_REG + (bank_base + bank) * IRQ_MEM_SIZE; -- cgit v1.2.1 From d8a22212737314cc02692cc90eda7d844fa20257 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 16 Nov 2016 15:18:18 +0100 Subject: pinctrl: sunxi: fix theoretical uninitialized variable access gcc warns about a way that it could use an uninitialized variable: drivers/pinctrl/sunxi/pinctrl-sunxi.c: In function 'sunxi_pinctrl_init': drivers/pinctrl/sunxi/pinctrl-sunxi.c:1191:8: error: 'best_div' may be used uninitialized in this function [-Werror=maybe-uninitialized] This cannot really happen except if 'freq' is UINT_MAX and 'clock' is zero, and both of these are forbidden. To shut up the warning anyway, this changes the logic to initialize the return code to the first divider value before looking at the others. Fixes: 7c926492d38a ("pinctrl: sunxi: Add support for interrupt debouncing") Signed-off-by: Arnd Bergmann Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/pinctrl/sunxi') diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 2339d4718b4d..6b7953da4228 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -1125,10 +1125,13 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev) static int sunxi_pinctrl_get_debounce_div(struct clk *clk, int freq, int *diff) { unsigned long clock = clk_get_rate(clk); - unsigned int best_diff = ~0, best_div; + unsigned int best_diff, best_div; int i; - for (i = 0; i < 8; i++) { + best_diff = abs(freq - clock); + best_div = 0; + + for (i = 1; i < 8; i++) { int cur_diff = abs(freq - (clock >> i)); if (cur_diff < best_diff) { -- cgit v1.2.1 From b3cde198b17f504643cc1eeffc4623f03326f436 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 18 Nov 2016 14:35:57 +0300 Subject: pinctrl: sunxi: Testing the wrong variable Smatch complains that we dereference "map" before testing it for NULL which is true. We should be testing "*map" instead. Also on the error path, we should free *map and set it to NULL. Signed-off-by: Dan Carpenter Acked-by: Maxime Ripard Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/pinctrl/sunxi') diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 6b7953da4228..0eb51e33cb1b 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -398,13 +398,14 @@ static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, * map array */ *map = krealloc(*map, i * sizeof(struct pinctrl_map), GFP_KERNEL); - if (!map) + if (!*map) return -ENOMEM; return 0; err_free_map: - kfree(map); + kfree(*map); + *map = NULL; return ret; } -- cgit v1.2.1