diff options
author | Stephen Warren <swarren@nvidia.com> | 2012-04-04 11:27:50 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2012-04-18 07:53:12 -0400 |
commit | 60f7f5003d69b92558e9fc0789339f2b1d41f78d (patch) | |
tree | eea25d34d9322a1b21d9bb5728f592ea6edb1dbb /drivers/pinctrl/pinctrl-tegra.c | |
parent | a3c9454e530d51fad49bbc57e19d50a30f94ce14 (diff) |
pinctrl: tegra: Add complete device tree support
Implement pinctrl_ops dt_node_to_map() and dt_free_map(). These allow
complete specification of the desired pinmux configuration using device
tree.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Dong Aisheng <dong.aisheng@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl/pinctrl-tegra.c')
-rw-r--r-- | drivers/pinctrl/pinctrl-tegra.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c index 41311a2a4256..2c98fba01ca5 100644 --- a/drivers/pinctrl/pinctrl-tegra.c +++ b/drivers/pinctrl/pinctrl-tegra.c | |||
@@ -23,9 +23,11 @@ | |||
23 | #include <linux/io.h> | 23 | #include <linux/io.h> |
24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
25 | #include <linux/of_device.h> | 25 | #include <linux/of_device.h> |
26 | #include <linux/pinctrl/machine.h> | ||
26 | #include <linux/pinctrl/pinctrl.h> | 27 | #include <linux/pinctrl/pinctrl.h> |
27 | #include <linux/pinctrl/pinmux.h> | 28 | #include <linux/pinctrl/pinmux.h> |
28 | #include <linux/pinctrl/pinconf.h> | 29 | #include <linux/pinctrl/pinconf.h> |
30 | #include <linux/slab.h> | ||
29 | 31 | ||
30 | #include <mach/pinconf-tegra.h> | 32 | #include <mach/pinconf-tegra.h> |
31 | 33 | ||
@@ -88,11 +90,214 @@ static void tegra_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, | |||
88 | seq_printf(s, " " DRIVER_NAME); | 90 | seq_printf(s, " " DRIVER_NAME); |
89 | } | 91 | } |
90 | 92 | ||
93 | static int reserve_map(struct pinctrl_map **map, unsigned *reserved_maps, | ||
94 | unsigned *num_maps, unsigned reserve) | ||
95 | { | ||
96 | unsigned old_num = *reserved_maps; | ||
97 | unsigned new_num = *num_maps + reserve; | ||
98 | struct pinctrl_map *new_map; | ||
99 | |||
100 | if (old_num >= new_num) | ||
101 | return 0; | ||
102 | |||
103 | new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL); | ||
104 | if (!new_map) | ||
105 | return -ENOMEM; | ||
106 | |||
107 | memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map)); | ||
108 | |||
109 | *map = new_map; | ||
110 | *reserved_maps = new_num; | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static int add_map_mux(struct pinctrl_map **map, unsigned *reserved_maps, | ||
116 | unsigned *num_maps, const char *group, | ||
117 | const char *function) | ||
118 | { | ||
119 | if (*num_maps == *reserved_maps) | ||
120 | return -ENOSPC; | ||
121 | |||
122 | (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; | ||
123 | (*map)[*num_maps].data.mux.group = group; | ||
124 | (*map)[*num_maps].data.mux.function = function; | ||
125 | (*num_maps)++; | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static int add_map_configs(struct pinctrl_map **map, unsigned *reserved_maps, | ||
131 | unsigned *num_maps, const char *group, | ||
132 | unsigned long *configs, unsigned num_configs) | ||
133 | { | ||
134 | unsigned long *dup_configs; | ||
135 | |||
136 | if (*num_maps == *reserved_maps) | ||
137 | return -ENOSPC; | ||
138 | |||
139 | dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs), | ||
140 | GFP_KERNEL); | ||
141 | if (!dup_configs) | ||
142 | return -ENOMEM; | ||
143 | |||
144 | (*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_GROUP; | ||
145 | (*map)[*num_maps].data.configs.group_or_pin = group; | ||
146 | (*map)[*num_maps].data.configs.configs = dup_configs; | ||
147 | (*map)[*num_maps].data.configs.num_configs = num_configs; | ||
148 | (*num_maps)++; | ||
149 | |||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static int add_config(unsigned long **configs, unsigned *num_configs, | ||
154 | unsigned long config) | ||
155 | { | ||
156 | unsigned old_num = *num_configs; | ||
157 | unsigned new_num = old_num + 1; | ||
158 | unsigned long *new_configs; | ||
159 | |||
160 | new_configs = krealloc(*configs, sizeof(*new_configs) * new_num, | ||
161 | GFP_KERNEL); | ||
162 | if (!new_configs) | ||
163 | return -ENOMEM; | ||
164 | |||
165 | new_configs[old_num] = config; | ||
166 | |||
167 | *configs = new_configs; | ||
168 | *num_configs = new_num; | ||
169 | |||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | void tegra_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, | ||
174 | struct pinctrl_map *map, unsigned num_maps) | ||
175 | { | ||
176 | int i; | ||
177 | |||
178 | for (i = 0; i < num_maps; i++) | ||
179 | if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP) | ||
180 | kfree(map[i].data.configs.configs); | ||
181 | |||
182 | kfree(map); | ||
183 | } | ||
184 | |||
185 | static const struct cfg_param { | ||
186 | const char *property; | ||
187 | enum tegra_pinconf_param param; | ||
188 | } cfg_params[] = { | ||
189 | {"nvidia,pull", TEGRA_PINCONF_PARAM_PULL}, | ||
190 | {"nvidia,tristate", TEGRA_PINCONF_PARAM_TRISTATE}, | ||
191 | {"nvidia,enable-input", TEGRA_PINCONF_PARAM_ENABLE_INPUT}, | ||
192 | {"nvidia,open-drain", TEGRA_PINCONF_PARAM_OPEN_DRAIN}, | ||
193 | {"nvidia,lock", TEGRA_PINCONF_PARAM_LOCK}, | ||
194 | {"nvidia,io-reset", TEGRA_PINCONF_PARAM_IORESET}, | ||
195 | {"nvidia,high-speed-mode", TEGRA_PINCONF_PARAM_HIGH_SPEED_MODE}, | ||
196 | {"nvidia,schmitt", TEGRA_PINCONF_PARAM_SCHMITT}, | ||
197 | {"nvidia,low-power-mode", TEGRA_PINCONF_PARAM_LOW_POWER_MODE}, | ||
198 | {"nvidia,pull-down-strength", TEGRA_PINCONF_PARAM_DRIVE_DOWN_STRENGTH}, | ||
199 | {"nvidia,pull-up-strength", TEGRA_PINCONF_PARAM_DRIVE_UP_STRENGTH}, | ||
200 | {"nvidia,slew-rate-falling", TEGRA_PINCONF_PARAM_SLEW_RATE_FALLING}, | ||
201 | {"nvidia,slew-rate-rising", TEGRA_PINCONF_PARAM_SLEW_RATE_RISING}, | ||
202 | }; | ||
203 | |||
204 | int tegra_pinctrl_dt_subnode_to_map(struct device_node *np, | ||
205 | struct pinctrl_map **map, | ||
206 | unsigned *reserved_maps, | ||
207 | unsigned *num_maps) | ||
208 | { | ||
209 | int ret, i; | ||
210 | const char *function; | ||
211 | u32 val; | ||
212 | unsigned long config; | ||
213 | unsigned long *configs = NULL; | ||
214 | unsigned num_configs = 0; | ||
215 | unsigned reserve; | ||
216 | struct property *prop; | ||
217 | const char *group; | ||
218 | |||
219 | ret = of_property_read_string(np, "nvidia,function", &function); | ||
220 | if (ret < 0) | ||
221 | function = NULL; | ||
222 | |||
223 | for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { | ||
224 | ret = of_property_read_u32(np, cfg_params[i].property, &val); | ||
225 | if (!ret) { | ||
226 | config = TEGRA_PINCONF_PACK(cfg_params[i].param, val); | ||
227 | ret = add_config(&configs, &num_configs, config); | ||
228 | if (ret < 0) | ||
229 | goto exit; | ||
230 | } | ||
231 | } | ||
232 | |||
233 | reserve = 0; | ||
234 | if (function != NULL) | ||
235 | reserve++; | ||
236 | if (num_configs) | ||
237 | reserve++; | ||
238 | ret = of_property_count_strings(np, "nvidia,pins"); | ||
239 | if (ret < 0) | ||
240 | goto exit; | ||
241 | reserve *= ret; | ||
242 | |||
243 | ret = reserve_map(map, reserved_maps, num_maps, reserve); | ||
244 | if (ret < 0) | ||
245 | goto exit; | ||
246 | |||
247 | of_property_for_each_string(np, "nvidia,pins", prop, group) { | ||
248 | if (function) { | ||
249 | ret = add_map_mux(map, reserved_maps, num_maps, | ||
250 | group, function); | ||
251 | if (ret < 0) | ||
252 | goto exit; | ||
253 | } | ||
254 | |||
255 | if (num_configs) { | ||
256 | ret = add_map_configs(map, reserved_maps, num_maps, | ||
257 | group, configs, num_configs); | ||
258 | if (ret < 0) | ||
259 | goto exit; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | ret = 0; | ||
264 | |||
265 | exit: | ||
266 | kfree(configs); | ||
267 | return ret; | ||
268 | } | ||
269 | |||
270 | int tegra_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, | ||
271 | struct device_node *np_config, | ||
272 | struct pinctrl_map **map, unsigned *num_maps) | ||
273 | { | ||
274 | unsigned reserved_maps; | ||
275 | struct device_node *np; | ||
276 | int ret; | ||
277 | |||
278 | reserved_maps = 0; | ||
279 | *map = NULL; | ||
280 | *num_maps = 0; | ||
281 | |||
282 | for_each_child_of_node(np_config, np) { | ||
283 | ret = tegra_pinctrl_dt_subnode_to_map(np, map, &reserved_maps, | ||
284 | num_maps); | ||
285 | if (ret < 0) { | ||
286 | tegra_pinctrl_dt_free_map(pctldev, *map, *num_maps); | ||
287 | return ret; | ||
288 | } | ||
289 | } | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | |||
91 | static struct pinctrl_ops tegra_pinctrl_ops = { | 294 | static struct pinctrl_ops tegra_pinctrl_ops = { |
92 | .get_groups_count = tegra_pinctrl_get_groups_count, | 295 | .get_groups_count = tegra_pinctrl_get_groups_count, |
93 | .get_group_name = tegra_pinctrl_get_group_name, | 296 | .get_group_name = tegra_pinctrl_get_group_name, |
94 | .get_group_pins = tegra_pinctrl_get_group_pins, | 297 | .get_group_pins = tegra_pinctrl_get_group_pins, |
95 | .pin_dbg_show = tegra_pinctrl_pin_dbg_show, | 298 | .pin_dbg_show = tegra_pinctrl_pin_dbg_show, |
299 | .dt_node_to_map = tegra_pinctrl_dt_node_to_map, | ||
300 | .dt_free_map = tegra_pinctrl_dt_free_map, | ||
96 | }; | 301 | }; |
97 | 302 | ||
98 | static int tegra_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) | 303 | static int tegra_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) |