blob: d21a3dd75522cb012ea98f2c9ceccfb1b5b04446 [file] [log] [blame]
Beniamino Galvani677b5352016-08-16 11:49:49 +02001/*
2 * (C) Copyright 2016 - Beniamino Galvani <b.galvani@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0+
5 */
6
7#include <common.h>
8#include <dm/device.h>
9#include <dm/pinctrl.h>
10#include <fdt_support.h>
11#include <linux/err.h>
12#include <linux/io.h>
13#include <linux/sizes.h>
14
15#include "pinctrl-meson.h"
16
17DECLARE_GLOBAL_DATA_PTR;
18
19static const char *meson_pinctrl_dummy_name = "_dummy";
20
21static int meson_pinctrl_get_groups_count(struct udevice *dev)
22{
23 struct meson_pinctrl *priv = dev_get_priv(dev);
24
25 return priv->data->num_groups;
26}
27
28static const char *meson_pinctrl_get_group_name(struct udevice *dev,
29 unsigned selector)
30{
31 struct meson_pinctrl *priv = dev_get_priv(dev);
32
33 if (!priv->data->groups[selector].name)
34 return meson_pinctrl_dummy_name;
35
36 return priv->data->groups[selector].name;
37}
38
39static int meson_pinmux_get_functions_count(struct udevice *dev)
40{
41 struct meson_pinctrl *priv = dev_get_priv(dev);
42
43 return priv->data->num_funcs;
44}
45
46static const char *meson_pinmux_get_function_name(struct udevice *dev,
47 unsigned selector)
48{
49 struct meson_pinctrl *priv = dev_get_priv(dev);
50
51 return priv->data->funcs[selector].name;
52}
53
54static void meson_pinmux_disable_other_groups(struct meson_pinctrl *priv,
55 unsigned int pin, int sel_group)
56{
57 struct meson_pmx_group *group;
58 void __iomem *addr;
59 int i, j;
60
61 for (i = 0; i < priv->data->num_groups; i++) {
62 group = &priv->data->groups[i];
63 if (group->is_gpio || i == sel_group)
64 continue;
65
66 for (j = 0; j < group->num_pins; j++) {
67 if (group->pins[j] == pin) {
68 /* We have found a group using the pin */
69 debug("pinmux: disabling %s\n", group->name);
70 addr = priv->reg_mux + group->reg * 4;
71 writel(readl(addr) & ~BIT(group->bit), addr);
72 }
73 }
74 }
75}
76
77static int meson_pinmux_group_set(struct udevice *dev,
78 unsigned group_selector,
79 unsigned func_selector)
80{
81 struct meson_pinctrl *priv = dev_get_priv(dev);
82 const struct meson_pmx_group *group;
83 const struct meson_pmx_func *func;
84 void __iomem *addr;
85 int i;
86
87 group = &priv->data->groups[group_selector];
88 func = &priv->data->funcs[func_selector];
89
90 debug("pinmux: set group %s func %s\n", group->name, func->name);
91
92 /*
93 * Disable groups using the same pins.
94 * The selected group is not disabled to avoid glitches.
95 */
96 for (i = 0; i < group->num_pins; i++) {
97 meson_pinmux_disable_other_groups(priv,
98 group->pins[i],
99 group_selector);
100 }
101
102 /* Function 0 (GPIO) doesn't need any additional setting */
103 if (func_selector) {
104 addr = priv->reg_mux + group->reg * 4;
105 writel(readl(addr) | BIT(group->bit), addr);
106 }
107
108 return 0;
109}
110
111const struct pinctrl_ops meson_pinctrl_ops = {
112 .get_groups_count = meson_pinctrl_get_groups_count,
113 .get_group_name = meson_pinctrl_get_group_name,
114 .get_functions_count = meson_pinmux_get_functions_count,
115 .get_function_name = meson_pinmux_get_function_name,
116 .pinmux_group_set = meson_pinmux_group_set,
117 .set_state = pinctrl_generic_set_state,
118};
119
120static fdt_addr_t parse_address(int offset, const char *name, int na, int ns)
121{
122 int index, len = 0;
123 const fdt32_t *reg;
124
125 index = fdt_find_string(gd->fdt_blob, offset, "reg-names", name);
126 if (index < 0)
127 return FDT_ADDR_T_NONE;
128
129 reg = fdt_getprop(gd->fdt_blob, offset, "reg", &len);
130 if (!reg || (len <= (index * sizeof(fdt32_t) * (na + ns))))
131 return FDT_ADDR_T_NONE;
132
133 reg += index * (na + ns);
134
135 return fdt_translate_address((void *)gd->fdt_blob, offset, reg);
136}
137
138int meson_pinctrl_probe(struct udevice *dev)
139{
140 struct meson_pinctrl *priv = dev_get_priv(dev);
141 fdt_addr_t addr;
142 int node, gpio = -1, len;
143 int na, ns;
144
145 na = fdt_address_cells(gd->fdt_blob, dev->parent->of_offset);
146 if (na < 1) {
147 debug("bad #address-cells\n");
148 return -EINVAL;
149 }
150
151 ns = fdt_size_cells(gd->fdt_blob, dev->parent->of_offset);
152 if (ns < 1) {
153 debug("bad #size-cells\n");
154 return -EINVAL;
155 }
156
157 fdt_for_each_subnode(gd->fdt_blob, node, dev->of_offset) {
158 if (fdt_getprop(gd->fdt_blob, node, "gpio-controller", &len)) {
159 gpio = node;
160 break;
161 }
162 }
163
164 if (!gpio) {
165 debug("gpio node not found\n");
166 return -EINVAL;
167 }
168
169 addr = parse_address(gpio, "mux", na, ns);
170 if (addr == FDT_ADDR_T_NONE) {
171 debug("mux not found\n");
172 return -EINVAL;
173 }
174
175 priv->reg_mux = (void __iomem *)addr;
176 priv->data = (struct meson_pinctrl_data *)dev_get_driver_data(dev);
177
178 return 0;
179}