summaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl/pinctrl-single.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/pinctrl-single.c')
-rw-r--r--drivers/pinctrl/pinctrl-single.c127
1 files changed, 85 insertions, 42 deletions
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
index 3c581ed5d3..0e706656b9 100644
--- a/drivers/pinctrl/pinctrl-single.c
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* pinctrl-single - Generic device tree based pinctrl driver for one
* register per pin type pinmux controllers
*
* Copyright (c) 2013 Sascha Hauer <s.hauer@pengutronix.de>
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <common.h>
@@ -29,7 +17,14 @@ struct pinctrl_single {
struct pinctrl_device pinctrl;
unsigned (*read)(void __iomem *reg);
void (*write)(unsigned val, void __iomem *reg);
- unsigned width;
+ unsigned int width;
+ unsigned int fmask;
+ unsigned int fshift;
+ unsigned int fmax;
+
+ bool bits_per_mux;
+ unsigned int bits_per_pin;
+ unsigned int args_count;
};
static unsigned __maybe_unused pcs_readb(void __iomem *reg)
@@ -66,27 +61,54 @@ static int pcs_set_state(struct pinctrl_device *pdev, struct device_node *np)
{
struct pinctrl_single *pcs = container_of(pdev, struct pinctrl_single, pinctrl);
unsigned size = 0, index = 0;
+ unsigned int offset, val, rows, mask, reg, i;
const __be32 *mux;
- dev_dbg(pcs->pinctrl.dev, "set state: %s\n", np->full_name);
-
- mux = of_get_property(np, "pinctrl-single,pins", &size);
-
- size /= sizeof(*mux); /* Number of elements in array */
-
- if (!mux || !size || (size & 1)) {
- dev_err(pcs->pinctrl.dev, "bad data for mux %s\n",
- np->full_name);
- return -EINVAL;
- }
-
- while (index < size) {
- unsigned offset, val;
-
- offset = be32_to_cpup(mux + index++);
- val = be32_to_cpup(mux + index++);
-
- pcs->write(val, pcs->base + offset);
+ dev_dbg(pcs->pinctrl.dev, "set state: %pOF\n", np);
+ if (pcs->bits_per_mux) {
+ mux = of_get_property(np, "pinctrl-single,bits", &size);
+ if (size % 3 != 0)
+ dev_err(pcs->pinctrl.dev,
+ "invalid args_count for spec: %u\n", size);
+
+ size /= sizeof(*mux); /* Number of elements in array */
+ rows = size / 3;
+
+ for (i = 0; i < rows; i++) {
+ offset = be32_to_cpup(mux + index++);
+ val = be32_to_cpup(mux + index++);
+ mask = be32_to_cpup(mux + index++);
+ reg = pcs->read(pcs->base + offset);
+ reg &= ~mask;
+ reg |= val;
+ pcs->write(reg, pcs->base + offset);
+ }
+ } else {
+ mux = of_get_property(np, "pinctrl-single,pins", &size);
+
+ size /= sizeof(*mux); /* Number of elements in array */
+
+ if (!mux || !size || (size % (pcs->args_count + 1))) {
+ dev_err(pcs->pinctrl.dev, "bad data for mux %pOF\n", np);
+ return -EINVAL;
+ }
+
+ while (index < size) {
+ unsigned int offset, val;
+
+ offset = be32_to_cpup(mux + index++);
+ val = be32_to_cpup(mux + index++);
+ if (pcs->args_count > 1) {
+ /* If a 2nd data cell is present, it's ORed into
+ * the 1st. Additional cells are undefined,
+ * just skip them.
+ */
+ val |= be32_to_cpup(mux + index);
+ index += pcs->args_count - 1;
+ }
+
+ pcs->write(val, pcs->base + offset);
+ }
}
return 0;
@@ -96,11 +118,11 @@ static struct pinctrl_ops pcs_ops = {
.set_state = pcs_set_state,
};
-static int pcs_probe(struct device_d *dev)
+int pinctrl_single_probe(struct device *dev)
{
struct resource *iores;
struct pinctrl_single *pcs;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
int ret = 0;
pcs = xzalloc(sizeof(*pcs));
@@ -137,6 +159,30 @@ static int pcs_probe(struct device_d *dev)
goto out;
}
+ ret = of_property_read_u32(np, "pinctrl-single,function-mask",
+ &pcs->fmask);
+ if (!ret) {
+ pcs->fshift = __ffs(pcs->fmask);
+ pcs->fmax = pcs->fmask >> pcs->fshift;
+ } else {
+ /* If mask property doesn't exist, function mux is invalid. */
+ pcs->fmask = 0;
+ pcs->fshift = 0;
+ pcs->fmax = 0;
+ }
+
+ pcs->bits_per_mux =
+ of_property_read_bool(np, "pinctrl-single,bit-per-mux");
+ if (pcs->bits_per_mux)
+ pcs->bits_per_pin = fls(pcs->fmask);
+
+ /* If no pinctrl-cells is present, default to old style of 2 cells with
+ * bits per mux and 1 cell otherwise.
+ */
+ ret = of_property_read_u32(np, "#pinctrl-cells", &pcs->args_count);
+ if (ret)
+ pcs->args_count = pcs->bits_per_mux ? 2 : 1;
+
ret = pinctrl_register(&pcs->pinctrl);
if (ret)
goto out;
@@ -156,15 +202,12 @@ static __maybe_unused struct of_device_id pcs_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, pcs_dt_ids);
-static struct driver_d pcs_driver = {
+static struct driver pcs_driver = {
.name = "pinctrl-single",
- .probe = pcs_probe,
+ .probe = pinctrl_single_probe,
.of_compatible = DRV_OF_COMPAT(pcs_dt_ids),
};
-static int pcs_init(void)
-{
- return platform_driver_register(&pcs_driver);
-}
-core_initcall(pcs_init);
+core_platform_driver(pcs_driver);