[PATCH v4 3/9] pinctrl: single: support pinconf generic
Haojian Zhuang
haojian.zhuang at gmail.com
Wed Nov 7 10:19:36 EST 2012
Add pinconf generic support with POWER SOURCE, BIAS PULL.
Signed-off-by: Haojian Zhuang <haojian.zhuang at gmail.com>
---
drivers/pinctrl/Kconfig | 1 +
drivers/pinctrl/pinctrl-single.c | 354 +++++++++++++++++++++++++++++++-------
2 files changed, 293 insertions(+), 62 deletions(-)
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 7bf914d..e9f2d2d 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -139,6 +139,7 @@ config PINCTRL_SINGLE
depends on OF
select PINMUX
select PINCONF
+ select GENERIC_PINCONF
help
This selects the device tree based generic pinctrl driver.
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
index a7c5fdd..77aec05 100644
--- a/drivers/pinctrl/pinctrl-single.c
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -20,6 +20,7 @@
#include <linux/of_device.h>
#include <linux/of_address.h>
+#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
@@ -30,7 +31,7 @@
#define PCS_MUX_BITS_NAME "pinctrl-single,bits"
#define PCS_REG_NAME_LEN ((sizeof(unsigned long) * 2) + 1)
#define PCS_OFF_DISABLED ~0U
-#define PCS_MAX_GPIO_VALUES 3
+#define PCS_MAX_GPIO_VALUES 2
/**
* struct pcs_pingroup - pingroups for a function
@@ -81,12 +82,20 @@ struct pcs_function {
* struct pcs_gpio_range - pinctrl gpio range
* @range: subrange of the GPIO number space
* @gpio_func: gpio function value in the pinmux register
- * @func_en: need to handle gpio function in the pinmux register
*/
struct pcs_gpio_range {
struct pinctrl_gpio_range range;
int gpio_func;
- unsigned func_en:1;
+};
+
+/**
+ * struct pcs_conf - configuration of pinctrl device
+ * @nconf: number of configuration that is defined for pinconf
+ * @is_generic: for pin controller that want to use the generic interface
+ */
+struct pcs_conf {
+ unsigned nconfs;
+ bool is_generic;
};
/**
@@ -130,16 +139,24 @@ struct pcs_name {
* @fshift: function register shift
* @foff: value to turn mux off
* @fmax: max number of functions in fmask
+ * @bmask: bias mask in pinconf
+ * @bshift: bias register shift
+ * @bdis: bias disable value in pinconf
+ * @bpullup: bias pull up value in pinconf
+ * @bpulldown: bias pull down value in pinconf
+ * @ismask: input schmitt mask in pinconf
+ * @isshift: input schmitt register shift
+ * @psmask: power source mask in pinconf
+ * @psshift: power source register shift
* @names: array of register names for pins
* @pins: physical pins on the SoC
+ * @conf: value of pinconf
* @pgtree: pingroup index radix tree
* @ftree: function index radix tree
* @pingroups: list of pingroups
* @functions: list of functions
- * @ranges: list of gpio ranges
* @ngroups: number of pingroups
* @nfuncs: number of functions
- * @nranges: number of gpio ranges
* @desc: pin controller descriptor
* @read: register read function to use
* @write: register write function to use
@@ -156,17 +173,25 @@ struct pcs_device {
unsigned fshift;
unsigned foff;
unsigned fmax;
+ unsigned bmask;
+ unsigned bshift;
+ unsigned bdis;
+ unsigned bpullup;
+ unsigned bpulldown;
+ unsigned ismask;
+ unsigned isshift;
+ unsigned psmask;
+ unsigned psshift;
bool bits_per_mux;
struct pcs_name *names;
struct pcs_data pins;
+ struct pcs_conf conf;
struct radix_tree_root pgtree;
struct radix_tree_root ftree;
struct list_head pingroups;
struct list_head functions;
- struct list_head ranges;
unsigned ngroups;
unsigned nfuncs;
- unsigned nranges;
struct pinctrl_desc desc;
unsigned (*read)(void __iomem *reg);
void (*write)(unsigned val, void __iomem *reg);
@@ -428,8 +453,6 @@ static int pcs_request_gpio(struct pinctrl_dev *pctldev,
unsigned data;
gpio = container_of(range, struct pcs_gpio_range, range);
- if (!gpio->func_en)
- return -ENOTSUPP;
end = range->pin_base + range->npins - 1;
if (pin < range->pin_base || pin > end) {
dev_err(pctldev->dev, "pin %d isn't in the range of "
@@ -452,28 +475,163 @@ static struct pinmux_ops pcs_pinmux_ops = {
.gpio_request_enable = pcs_request_gpio,
};
+static void pcs_free_pingroups(struct pcs_device *pcs);
+
static int pcs_pinconf_get(struct pinctrl_dev *pctldev,
unsigned pin, unsigned long *config)
{
+ struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
+ enum pin_config_param param = pinconf_to_config_param(*config);
+ unsigned data;
+ u32 offset;
+
+ offset = pin * (pcs->width / BITS_PER_BYTE);
+ data = pcs->read(pcs->base + offset);
+
+ switch (param) {
+ case PIN_CONFIG_POWER_SOURCE:
+ if (pcs->psmask == PCS_OFF_DISABLED
+ || pcs->psshift == PCS_OFF_DISABLED)
+ return -ENOTSUPP;
+ data &= pcs->psmask;
+ data = data >> pcs->psshift;
+ *config = data;
+ return 0;
+ break;
+ case PIN_CONFIG_BIAS_DISABLE:
+ if (pcs->bmask == PCS_OFF_DISABLED
+ || pcs->bshift == PCS_OFF_DISABLED
+ || pcs->bdis == PCS_OFF_DISABLED)
+ return -ENOTSUPP;
+ data &= pcs->bmask;
+ *config = 0;
+ if (data == pcs->bdis)
+ return 0;
+ else
+ return -EINVAL;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ if (pcs->bmask == PCS_OFF_DISABLED
+ || pcs->bshift == PCS_OFF_DISABLED
+ || pcs->bpullup == PCS_OFF_DISABLED)
+ return -ENOTSUPP;
+ data &= pcs->bmask;
+ *config = 0;
+ if (data == pcs->bpullup)
+ return 0;
+ else
+ return -EINVAL;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ if (pcs->bmask == PCS_OFF_DISABLED
+ || pcs->bshift == PCS_OFF_DISABLED
+ || pcs->bpulldown == PCS_OFF_DISABLED)
+ return -ENOTSUPP;
+ data &= pcs->bmask;
+ *config = 0;
+ if (data == pcs->bpulldown)
+ return 0;
+ else
+ return -EINVAL;
+ break;
+ default:
+ break;
+ }
return -ENOTSUPP;
}
static int pcs_pinconf_set(struct pinctrl_dev *pctldev,
unsigned pin, unsigned long config)
{
- return -ENOTSUPP;
+ struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
+ enum pin_config_param config_param = pinconf_to_config_param(config);
+ unsigned ret, mask = ~0UL;
+ u32 offset, data;
+
+ switch (config_param) {
+ case PIN_CONFIG_POWER_SOURCE:
+ if (pcs->psmask == PCS_OFF_DISABLED
+ || pcs->psshift == PCS_OFF_DISABLED)
+ return 0;
+ mask = pcs->psmask;
+ data = (pinconf_to_config_argument(config) << pcs->psshift)
+ & pcs->psmask;
+ break;
+ case PIN_CONFIG_BIAS_DISABLE:
+ if (pcs->bmask == PCS_OFF_DISABLED
+ || pcs->bshift == PCS_OFF_DISABLED)
+ return 0;
+ mask = pcs->bmask;
+ data = (pinconf_to_config_argument(config) << pcs->bshift)
+ & pcs->bmask;
+ break;
+ default:
+ return 0;
+ }
+ offset = pin * (pcs->width / BITS_PER_BYTE);
+ ret = pcs->read(pcs->base + offset) & ~mask;
+ pcs->write(ret | data, pcs->base + offset);
+ return 0;
}
static int pcs_pinconf_group_get(struct pinctrl_dev *pctldev,
unsigned group, unsigned long *config)
{
- return -ENOTSUPP;
+ struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
+ struct pcs_pingroup *pins;
+
+ pins = radix_tree_lookup(&pcs->pgtree, group);
+ if (!pins) {
+ dev_err(pcs->dev, "%s could not find pingroup%i\n",
+ __func__, group);
+ return -EINVAL;
+ }
+ return pcs_pinconf_get(pctldev, pins->gpins[0], config);
}
static int pcs_pinconf_group_set(struct pinctrl_dev *pctldev,
unsigned group, unsigned long config)
{
- return -ENOTSUPP;
+ struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
+ enum pin_config_param config_param = pinconf_to_config_param(config);
+ struct pcs_pingroup *pins;
+ u32 offset, data;
+ unsigned ret, mask = ~0UL;
+ int i;
+
+ switch (config_param) {
+ case PIN_CONFIG_POWER_SOURCE:
+ if (pcs->psmask == PCS_OFF_DISABLED
+ || pcs->psshift == PCS_OFF_DISABLED)
+ return 0;
+ mask = pcs->psmask;
+ data = (pinconf_to_config_argument(config) << pcs->psshift)
+ & pcs->psmask;
+ break;
+ case PIN_CONFIG_BIAS_DISABLE:
+ if (pcs->bmask == PCS_OFF_DISABLED
+ || pcs->bshift == PCS_OFF_DISABLED)
+ return 0;
+ mask = pcs->bmask;
+ data = (pinconf_to_config_argument(config) << pcs->bshift)
+ & pcs->bmask;
+ break;
+ default:
+ return 0;
+ }
+
+ pins = radix_tree_lookup(&pcs->pgtree, group);
+ if (!pins) {
+ dev_err(pcs->dev, "%s could not find pingroup%i\n",
+ __func__, group);
+ return -EINVAL;
+ }
+ for (i = 0; i < pins->ngpins; i++) {
+ offset = pins->gpins[i] * (pcs->width / BITS_PER_BYTE);
+ ret = pcs->read(pcs->base + offset) & ~mask;
+ pcs->write(ret | data, pcs->base + offset);
+ }
+ return 0;
}
static void pcs_pinconf_dbg_show(struct pinctrl_dev *pctldev,
@@ -688,6 +846,7 @@ static int pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset)
* @pcs: pinctrl driver instance
* @np: device node of the mux entry
* @map: map entry
+ * @num_configs: number of pin configurations
* @pgnames: pingroup names
*
* Note that this binding currently supports only sets of one register + value.
@@ -707,9 +866,12 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
const char **pgnames)
{
struct pcs_func_vals *vals;
+ struct pinctrl_map *p = *map;
const __be32 *mux;
int size, params, rows, *pins, index = 0, found = 0, res = -ENOMEM;
struct pcs_function *function;
+ unsigned long *config;
+ u32 value, nconfs;
if (pcs->bits_per_mux) {
params = 3;
@@ -763,6 +925,7 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
pins[found++] = pin;
}
+ nconfs = pcs->conf.nconfs;
pgnames[0] = np->name;
function = pcs_add_function(pcs, np, np->name, vals, found, pgnames, 1);
if (!function)
@@ -772,12 +935,42 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
if (res < 0)
goto free_function;
- (*map)->type = PIN_MAP_TYPE_MUX_GROUP;
- (*map)->data.mux.group = np->name;
- (*map)->data.mux.function = np->name;
+ p->type = PIN_MAP_TYPE_MUX_GROUP;
+ p->data.mux.group = np->name;
+ p->data.mux.function = np->name;
+
+ if (!nconfs)
+ return 0;
+
+ config = devm_kzalloc(pcs->dev, sizeof(*config) * nconfs, GFP_KERNEL);
+ if (!config) {
+ res = -ENOMEM;
+ goto free_pingroup;
+ }
+ index = 0;
+ if (!of_property_read_u32(np, "pinctrl-single,input-schmitt", &value))
+ config[index++] =
+ pinconf_to_config_packed(PIN_CONFIG_INPUT_SCHMITT,
+ value & 0xffff);
+ if (!of_property_read_u32(np, "pinctrl-single,bias", &value))
+ config[index++] =
+ pinconf_to_config_packed(PIN_CONFIG_BIAS_DISABLE,
+ value & 0xffff);
+ if (!of_property_read_u32(np, "pinctrl-single,power-source", &value))
+ config[index++] =
+ pinconf_to_config_packed(PIN_CONFIG_POWER_SOURCE,
+ value & 0xffff);
+ p++;
+ p->type = PIN_MAP_TYPE_CONFIGS_GROUP;
+ p->data.configs.group_or_pin = np->name;
+ p->data.configs.configs = config;
+ p->data.configs.num_configs = nconfs;
return 0;
+free_pingroup:
+ pcs_free_pingroups(pcs);
+
free_function:
pcs_remove_function(pcs, function);
@@ -789,6 +982,7 @@ free_vals:
return res;
}
+
/**
* pcs_dt_node_to_map() - allocates and parses pinctrl maps
* @pctldev: pinctrl instance
@@ -806,34 +1000,27 @@ static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev,
pcs = pinctrl_dev_get_drvdata(pctldev);
- *map = devm_kzalloc(pcs->dev, sizeof(**map), GFP_KERNEL);
- if (!map)
- return -ENOMEM;
+ if (!pcs->conf.nconfs)
+ *num_maps = 1;
+ else
+ *num_maps = 2;
- *num_maps = 0;
+ *map = devm_kzalloc(pcs->dev, sizeof(**map) * (*num_maps), GFP_KERNEL);
+ if (!*map)
+ return -ENOMEM;
pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames), GFP_KERNEL);
- if (!pgnames) {
- ret = -ENOMEM;
- goto free_map;
- }
+ if (!pgnames)
+ return -ENOMEM;
ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, pgnames);
if (ret < 0) {
dev_err(pcs->dev, "no pins entries for %s\n",
np_config->name);
- goto free_pgnames;
+ return ret;
}
- *num_maps = 1;
return 0;
-
-free_pgnames:
- devm_kfree(pcs->dev, pgnames);
-free_map:
- devm_kfree(pcs->dev, *map);
-
- return ret;
}
/**
@@ -918,52 +1105,40 @@ static int __devinit pcs_add_gpio_range(struct device_node *node,
struct pcs_device *pcs)
{
struct pcs_gpio_range *gpio;
- struct device_node *np;
- const __be32 *list;
- const char list_name[] = "pinctrl-single,gpio-ranges";
+ struct device_node *child;
+ struct resource r;
const char name[] = "pinctrl-single";
u32 gpiores[PCS_MAX_GPIO_VALUES];
- int ret, size, i, mux_bytes = 0;
+ int ret, i = 0, mux_bytes = 0;
- list = of_get_property(node, list_name, &size);
- if (!list)
- return 0;
- size = size / sizeof(*list);
- for (i = 0; i < size; i++) {
- np = of_parse_phandle(node, list_name, i);
+ for_each_child_of_node(node, child) {
+ ret = of_address_to_resource(child, 0, &r);
+ if (ret < 0)
+ continue;
memset(gpiores, 0, sizeof(u32) * PCS_MAX_GPIO_VALUES);
- ret = of_property_read_u32_array(np, "pinctrl-single,gpio",
+ ret = of_property_read_u32_array(child, "pinctrl-single,gpio",
gpiores, PCS_MAX_GPIO_VALUES);
if (ret < 0)
- return -ENOENT;
+ continue;
gpio = devm_kzalloc(pcs->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio) {
dev_err(pcs->dev, "failed to allocate pcs gpio\n");
return -ENOMEM;
}
- gpio->range.id = i;
- gpio->range.base = gpiores[0];
- gpio->range.npins = gpiores[1];
gpio->range.name = devm_kzalloc(pcs->dev, sizeof(name),
GFP_KERNEL);
if (!gpio->range.name) {
dev_err(pcs->dev, "failed to allocate range name\n");
return -ENOMEM;
}
- memcpy(&gpio->range.name, name, sizeof(name));
- mux_bytes = pcs->width / BITS_PER_BYTE;
- gpio->range.pin_base = gpiores[2] / mux_bytes;
- memset(gpiores, 0, sizeof(u32) * PCS_MAX_GPIO_VALUES);
- ret = of_property_read_u32(np, "pinctrl-single,gpio-func",
- &gpio->gpio_func);
- if (ret < 0)
- return -ENOENT;
- gpio->func_en = 1;
+ memcpy((char *)gpio->range.name, name, sizeof(name));
- mutex_lock(&pcs->mutex);
- list_add_tail(&gpio->range.node, &pcs->ranges);
- pcs->nranges++;
- mutex_unlock(&pcs->mutex);
+ gpio->range.id = i++;
+ gpio->range.base = gpiores[0];
+ gpio->gpio_func = gpiores[1];
+ mux_bytes = pcs->width / BITS_PER_BYTE;
+ gpio->range.pin_base = (r.start - pcs->res->start) / mux_bytes;
+ gpio->range.npins = (r.end - r.start) / mux_bytes + 1;
pinctrl_add_gpio_range(pcs->pctl, &gpio->range);
}
@@ -976,11 +1151,13 @@ static int __devinit pcs_probe(struct platform_device *pdev)
const struct of_device_id *match;
struct resource *res;
struct pcs_device *pcs;
+ const struct pcs_conf *conf;
int ret;
match = of_match_device(pcs_of_match, &pdev->dev);
if (!match)
return -EINVAL;
+ conf = match->data;
pcs = devm_kzalloc(&pdev->dev, sizeof(*pcs), GFP_KERNEL);
if (!pcs) {
@@ -988,10 +1165,10 @@ static int __devinit pcs_probe(struct platform_device *pdev)
return -ENOMEM;
}
pcs->dev = &pdev->dev;
+ memcpy(&pcs->conf, conf, sizeof(*conf));
mutex_init(&pcs->mutex);
INIT_LIST_HEAD(&pcs->pingroups);
INIT_LIST_HEAD(&pcs->functions);
- INIT_LIST_HEAD(&pcs->ranges);
PCS_GET_PROP_U32("pinctrl-single,register-width", &pcs->width,
"register width not specified\n");
@@ -1009,6 +1186,46 @@ static int __devinit pcs_probe(struct platform_device *pdev)
pcs->bits_per_mux = of_property_read_bool(np,
"pinctrl-single,bit-per-mux");
+ if (conf->nconfs) {
+ pcs_pinconf_ops.is_generic = true;
+ ret = of_property_read_u32(np,
+ "pinctrl-single,power-source-mask",
+ &pcs->psmask);
+ if (ret) {
+ pcs->psmask = PCS_OFF_DISABLED;
+ pcs->psshift = PCS_OFF_DISABLED;
+ } else
+ pcs->psshift = ffs(pcs->psmask) - 1;
+ ret = of_property_read_u32(np,
+ "pinctrl-single,bias-mask", &pcs->bmask);
+ if (ret) {
+ pcs->bmask = PCS_OFF_DISABLED;
+ pcs->bshift = PCS_OFF_DISABLED;
+ } else
+ pcs->bshift = ffs(pcs->bmask) - 1;
+ ret = of_property_read_u32(np,
+ "pinctrl-single,bias-disable", &pcs->bdis);
+ if (ret)
+ pcs->bdis = PCS_OFF_DISABLED;
+ ret = of_property_read_u32(np,
+ "pinctrl-single,bias-pull-up", &pcs->bpullup);
+ if (ret)
+ pcs->bpullup = PCS_OFF_DISABLED;
+ ret = of_property_read_u32(np,
+ "pinctrl-single,bias-pull-down",
+ &pcs->bpulldown);
+ if (ret)
+ pcs->bpulldown = PCS_OFF_DISABLED;
+ ret = of_property_read_u32(np,
+ "pinctrl-single,input-schmitt-mask",
+ &pcs->ismask);
+ if (ret) {
+ pcs->ismask = PCS_OFF_DISABLED;
+ pcs->isshift = PCS_OFF_DISABLED;
+ } else
+ pcs->isshift = ffs(pcs->ismask) - 1;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(pcs->dev, "could not get resource\n");
@@ -1094,8 +1311,21 @@ static int __devexit pcs_remove(struct platform_device *pdev)
return 0;
}
+/* PINCONF isn't supported */
+static struct pcs_conf pinctrl_conf __devinitdata = {
+ .nconfs = 0,
+ .is_generic = false,
+};
+
+/* Generic PINCONF is supported. */
+static struct pcs_conf pinconf_conf __devinitdata = {
+ .nconfs = 7,
+ .is_generic = true,
+};
+
static struct of_device_id pcs_of_match[] __devinitdata = {
- { .compatible = DRIVER_NAME, },
+ { .compatible = "pinctrl-single", .data = &pinctrl_conf },
+ { .compatible = "pinconf-single", .data = &pinconf_conf },
{ },
};
MODULE_DEVICE_TABLE(of, pcs_of_match);
--
1.7.10.4
More information about the linux-arm-kernel
mailing list