summaryrefslogtreecommitdiff
path: root/drivers/pinctrl/pinctrl-at91-pio4.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/pinctrl-at91-pio4.c')
-rw-r--r--drivers/pinctrl/pinctrl-at91-pio4.c138
1 files changed, 121 insertions, 17 deletions
diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c
index 26fb5d61d5..50e3dd449a 100644
--- a/drivers/pinctrl/pinctrl-at91-pio4.c
+++ b/drivers/pinctrl/pinctrl-at91-pio4.c
@@ -9,10 +9,13 @@
#include <common.h>
#include <dm.h>
#include <asm/global_data.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
#include <dm/pinctrl.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/err.h>
+#include <dm/uclass-internal.h>
#include <mach/atmel_pio4.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -28,6 +31,25 @@ struct atmel_pio4_plat {
unsigned int slew_rate_support;
};
+/*
+ * Table keeping track of the pinctrl driver's slew rate support and the
+ * corresponding index into the struct udevice_id of the gpio_atmel_pio4 GPIO
+ * driver. This has been done in order to align the DT of U-Boot with the DT of
+ * Linux. In Linux, a phandle from a '-gpio' DT property is linked to the
+ * pinctrl driver, unlike U-Boot which redirects this phandle to a corresponding
+ * UCLASS_GPIO driver. Thus, in order to link the two, a hook to the bind method
+ * of the pinctrl driver in U-Boot has been added. This bind method will attach
+ * the GPIO driver to the pinctrl DT node using this table.
+ * @slew_rate_support pinctrl driver's slew rate support
+ * @gdidx index into the GPIO driver's struct udevide_id
+ * (needed in order to properly bind with driver_data)
+ */
+
+struct atmel_pinctrl_data {
+ unsigned int slew_rate_support;
+ int gdidx;
+};
+
static const struct pinconf_param conf_params[] = {
{ "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
{ "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
@@ -130,12 +152,11 @@ static inline struct atmel_pio4_port *atmel_pio4_bank_base(struct udevice *dev,
#define MAX_PINMUX_ENTRIES 40
-static int atmel_pinctrl_set_state(struct udevice *dev, struct udevice *config)
+static int atmel_process_config_dev(struct udevice *dev, struct udevice *config)
{
struct atmel_pio4_plat *plat = dev_get_plat(dev);
- struct atmel_pio4_port *bank_base;
- const void *blob = gd->fdt_blob;
int node = dev_of_offset(config);
+ struct atmel_pio4_port *bank_base;
u32 offset, func, bank, line;
u32 cells[MAX_PINMUX_ENTRIES];
u32 i, conf;
@@ -143,18 +164,17 @@ static int atmel_pinctrl_set_state(struct udevice *dev, struct udevice *config)
conf = atmel_pinctrl_get_pinconf(config, plat);
- count = fdtdec_get_int_array_count(blob, node, "pinmux",
+ /*
+ * The only case where this function returns a negative error value
+ * is when there is no "pinmux" property attached to this node
+ */
+ count = fdtdec_get_int_array_count(gd->fdt_blob, node, "pinmux",
cells, ARRAY_SIZE(cells));
- if (count < 0) {
- printf("%s: bad pinmux array %d\n", __func__, count);
- return -EINVAL;
- }
+ if (count < 0)
+ return count;
- if (count > MAX_PINMUX_ENTRIES) {
- printf("%s: unsupported pinmux array count %d\n",
- __func__, count);
+ if (count > MAX_PINMUX_ENTRIES)
return -EINVAL;
- }
for (i = 0 ; i < count; i++) {
offset = ATMEL_GET_PIN_NO(cells[i]);
@@ -174,6 +194,56 @@ static int atmel_pinctrl_set_state(struct udevice *dev, struct udevice *config)
return 0;
}
+static int atmel_pinctrl_set_state(struct udevice *dev, struct udevice *config)
+{
+ int node = dev_of_offset(config);
+ struct udevice *subconfig;
+ int subnode, subnode_count = 0, ret;
+
+ /*
+ * If this function returns a negative error code then that means
+ * that either the "pinmux" property of the node is missing, which is
+ * the case for pinctrl nodes that do not have all the pins with the
+ * same configuration and are split in multiple subnodes, or something
+ * else went wrong and we have to stop. For the latter case, it would
+ * mean that the node failed even though it has no subnodes.
+ */
+ ret = atmel_process_config_dev(dev, config);
+ if (!ret)
+ return ret;
+
+ /*
+ * If we reach here, it means that the subnode pinctrl's DT has multiple
+ * subnodes. If it does not, then something else went wrong in the
+ * previous call to atmel_process_config_dev.
+ */
+ fdt_for_each_subnode(subnode, gd->fdt_blob, node) {
+ /* Get subnode as an udevice */
+ ret = uclass_find_device_by_of_offset(UCLASS_PINCONFIG, subnode,
+ &subconfig);
+ if (ret)
+ return ret;
+
+ /*
+ * If this time the function returns an error code on a subnode
+ * then something is totally wrong so abort.
+ */
+ ret = atmel_process_config_dev(dev, subconfig);
+ if (ret)
+ return ret;
+
+ subnode_count++;
+ }
+
+ /*
+ * If we somehow got here and we do not have any subnodes, abort.
+ */
+ if (!subnode_count)
+ return -EINVAL;
+
+ return 0;
+}
+
const struct pinctrl_ops atmel_pinctrl_ops = {
.set_state = atmel_pinctrl_set_state,
};
@@ -181,24 +251,57 @@ const struct pinctrl_ops atmel_pinctrl_ops = {
static int atmel_pinctrl_probe(struct udevice *dev)
{
struct atmel_pio4_plat *plat = dev_get_plat(dev);
- ulong priv = dev_get_driver_data(dev);
+ struct atmel_pinctrl_data *priv = (struct atmel_pinctrl_data *)dev_get_driver_data(dev);
fdt_addr_t addr_base;
- dev = dev_get_parent(dev);
addr_base = dev_read_addr(dev);
if (addr_base == FDT_ADDR_T_NONE)
return -EINVAL;
plat->reg_base = (struct atmel_pio4_port *)addr_base;
- plat->slew_rate_support = priv;
+ plat->slew_rate_support = priv->slew_rate_support;
return 0;
}
+static int atmel_pinctrl_bind(struct udevice *dev)
+{
+ struct udevice *g;
+ struct driver *drv;
+ ofnode node = dev_ofnode(dev);
+ struct atmel_pinctrl_data *priv = (struct atmel_pinctrl_data *)dev_get_driver_data(dev);
+
+ if (!CONFIG_IS_ENABLED(ATMEL_PIO4))
+ return 0;
+
+ /* Obtain a handle to the GPIO driver */
+ drv = lists_driver_lookup_name("gpio_atmel_pio4");
+ if (!drv)
+ return -ENOENT;
+
+ /*
+ * Bind the GPIO driver to the pinctrl DT node, together
+ * with its corresponding driver_data.
+ */
+ return device_bind_with_driver_data(dev, drv, drv->name,
+ drv->of_match[priv->gdidx].data,
+ node, &g);
+}
+
+static const struct atmel_pinctrl_data atmel_sama5d2_pinctrl_data = {
+ .gdidx = 0,
+};
+
+static const struct atmel_pinctrl_data microchip_sama7g5_pinctrl_data = {
+ .slew_rate_support = 1,
+ .gdidx = 1,
+};
+
static const struct udevice_id atmel_pinctrl_match[] = {
- { .compatible = "atmel,sama5d2-pinctrl" },
+ { .compatible = "atmel,sama5d2-pinctrl",
+ .data = (ulong)&atmel_sama5d2_pinctrl_data, },
{ .compatible = "microchip,sama7g5-pinctrl",
- .data = (ulong)1, },
+ .data = (ulong)&microchip_sama7g5_pinctrl_data, },
{}
};
@@ -206,6 +309,7 @@ U_BOOT_DRIVER(atmel_pinctrl) = {
.name = "pinctrl_atmel_pio4",
.id = UCLASS_PINCTRL,
.of_match = atmel_pinctrl_match,
+ .bind = atmel_pinctrl_bind,
.probe = atmel_pinctrl_probe,
.plat_auto = sizeof(struct atmel_pio4_plat),
.ops = &atmel_pinctrl_ops,