diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-04-07 06:28:00 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-04-07 06:28:00 -0400 |
commit | c38f62b08d800104fa9b0e9d6e9141459986c06d (patch) | |
tree | 1d04d768c8aa0c1a544d1f068317c7beb0101be2 /drivers/pinctrl/pinctrl-tegra.c | |
parent | 250f32747e62cb415b85083e247184188f24e566 (diff) | |
parent | 8abe05c6eb358967f16bce8a02c88d57c82cfbd6 (diff) |
Merge tag 'asoc-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: fixes for 3.4
A bunch of driver-specific fixes and one generic fix for the new support
for platform DAPM contexts - we were picking the wrong default for the
idle_bias_off setting which was meaning we weren't actually achieving
any useful runtime PM on platform devices.
Diffstat (limited to 'drivers/pinctrl/pinctrl-tegra.c')
-rw-r--r-- | drivers/pinctrl/pinctrl-tegra.c | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c new file mode 100644 index 000000000000..9b329688120c --- /dev/null +++ b/drivers/pinctrl/pinctrl-tegra.c | |||
@@ -0,0 +1,559 @@ | |||
1 | /* | ||
2 | * Driver for the NVIDIA Tegra pinmux | ||
3 | * | ||
4 | * Copyright (c) 2011, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Derived from code: | ||
7 | * Copyright (C) 2010 Google, Inc. | ||
8 | * Copyright (C) 2010 NVIDIA Corporation | ||
9 | * Copyright (C) 2009-2011 ST-Ericsson AB | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms and conditions of the GNU General Public License, | ||
13 | * version 2, as published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
18 | * more details. | ||
19 | */ | ||
20 | |||
21 | #include <linux/err.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/io.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/of_device.h> | ||
26 | #include <linux/pinctrl/pinctrl.h> | ||
27 | #include <linux/pinctrl/pinmux.h> | ||
28 | #include <linux/pinctrl/pinconf.h> | ||
29 | |||
30 | #include <mach/pinconf-tegra.h> | ||
31 | |||
32 | #include "pinctrl-tegra.h" | ||
33 | |||
34 | #define DRIVER_NAME "tegra-pinmux-disabled" | ||
35 | |||
36 | struct tegra_pmx { | ||
37 | struct device *dev; | ||
38 | struct pinctrl_dev *pctl; | ||
39 | |||
40 | const struct tegra_pinctrl_soc_data *soc; | ||
41 | |||
42 | int nbanks; | ||
43 | void __iomem **regs; | ||
44 | }; | ||
45 | |||
46 | static inline u32 pmx_readl(struct tegra_pmx *pmx, u32 bank, u32 reg) | ||
47 | { | ||
48 | return readl(pmx->regs[bank] + reg); | ||
49 | } | ||
50 | |||
51 | static inline void pmx_writel(struct tegra_pmx *pmx, u32 val, u32 bank, u32 reg) | ||
52 | { | ||
53 | writel(val, pmx->regs[bank] + reg); | ||
54 | } | ||
55 | |||
56 | static int tegra_pinctrl_list_groups(struct pinctrl_dev *pctldev, | ||
57 | unsigned group) | ||
58 | { | ||
59 | struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); | ||
60 | |||
61 | if (group >= pmx->soc->ngroups) | ||
62 | return -EINVAL; | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | static const char *tegra_pinctrl_get_group_name(struct pinctrl_dev *pctldev, | ||
68 | unsigned group) | ||
69 | { | ||
70 | struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); | ||
71 | |||
72 | if (group >= pmx->soc->ngroups) | ||
73 | return NULL; | ||
74 | |||
75 | return pmx->soc->groups[group].name; | ||
76 | } | ||
77 | |||
78 | static int tegra_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, | ||
79 | unsigned group, | ||
80 | const unsigned **pins, | ||
81 | unsigned *num_pins) | ||
82 | { | ||
83 | struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); | ||
84 | |||
85 | if (group >= pmx->soc->ngroups) | ||
86 | return -EINVAL; | ||
87 | |||
88 | *pins = pmx->soc->groups[group].pins; | ||
89 | *num_pins = pmx->soc->groups[group].npins; | ||
90 | |||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static void tegra_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, | ||
95 | struct seq_file *s, | ||
96 | unsigned offset) | ||
97 | { | ||
98 | seq_printf(s, " " DRIVER_NAME); | ||
99 | } | ||
100 | |||
101 | static struct pinctrl_ops tegra_pinctrl_ops = { | ||
102 | .list_groups = tegra_pinctrl_list_groups, | ||
103 | .get_group_name = tegra_pinctrl_get_group_name, | ||
104 | .get_group_pins = tegra_pinctrl_get_group_pins, | ||
105 | .pin_dbg_show = tegra_pinctrl_pin_dbg_show, | ||
106 | }; | ||
107 | |||
108 | static int tegra_pinctrl_list_funcs(struct pinctrl_dev *pctldev, | ||
109 | unsigned function) | ||
110 | { | ||
111 | struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); | ||
112 | |||
113 | if (function >= pmx->soc->nfunctions) | ||
114 | return -EINVAL; | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static const char *tegra_pinctrl_get_func_name(struct pinctrl_dev *pctldev, | ||
120 | unsigned function) | ||
121 | { | ||
122 | struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); | ||
123 | |||
124 | if (function >= pmx->soc->nfunctions) | ||
125 | return NULL; | ||
126 | |||
127 | return pmx->soc->functions[function].name; | ||
128 | } | ||
129 | |||
130 | static int tegra_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, | ||
131 | unsigned function, | ||
132 | const char * const **groups, | ||
133 | unsigned * const num_groups) | ||
134 | { | ||
135 | struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); | ||
136 | |||
137 | if (function >= pmx->soc->nfunctions) | ||
138 | return -EINVAL; | ||
139 | |||
140 | *groups = pmx->soc->functions[function].groups; | ||
141 | *num_groups = pmx->soc->functions[function].ngroups; | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static int tegra_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned function, | ||
147 | unsigned group) | ||
148 | { | ||
149 | struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); | ||
150 | const struct tegra_pingroup *g; | ||
151 | int i; | ||
152 | u32 val; | ||
153 | |||
154 | if (group >= pmx->soc->ngroups) | ||
155 | return -EINVAL; | ||
156 | g = &pmx->soc->groups[group]; | ||
157 | |||
158 | if (g->mux_reg < 0) | ||
159 | return -EINVAL; | ||
160 | |||
161 | for (i = 0; i < ARRAY_SIZE(g->funcs); i++) { | ||
162 | if (g->funcs[i] == function) | ||
163 | break; | ||
164 | } | ||
165 | if (i == ARRAY_SIZE(g->funcs)) | ||
166 | return -EINVAL; | ||
167 | |||
168 | val = pmx_readl(pmx, g->mux_bank, g->mux_reg); | ||
169 | val &= ~(0x3 << g->mux_bit); | ||
170 | val |= i << g->mux_bit; | ||
171 | pmx_writel(pmx, val, g->mux_bank, g->mux_reg); | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static void tegra_pinctrl_disable(struct pinctrl_dev *pctldev, | ||
177 | unsigned function, unsigned group) | ||
178 | { | ||
179 | struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); | ||
180 | const struct tegra_pingroup *g; | ||
181 | u32 val; | ||
182 | |||
183 | if (group >= pmx->soc->ngroups) | ||
184 | return; | ||
185 | g = &pmx->soc->groups[group]; | ||
186 | |||
187 | if (g->mux_reg < 0) | ||
188 | return; | ||
189 | |||
190 | val = pmx_readl(pmx, g->mux_bank, g->mux_reg); | ||
191 | val &= ~(0x3 << g->mux_bit); | ||
192 | val |= g->func_safe << g->mux_bit; | ||
193 | pmx_writel(pmx, val, g->mux_bank, g->mux_reg); | ||
194 | } | ||
195 | |||
196 | static struct pinmux_ops tegra_pinmux_ops = { | ||
197 | .list_functions = tegra_pinctrl_list_funcs, | ||
198 | .get_function_name = tegra_pinctrl_get_func_name, | ||
199 | .get_function_groups = tegra_pinctrl_get_func_groups, | ||
200 | .enable = tegra_pinctrl_enable, | ||
201 | .disable = tegra_pinctrl_disable, | ||
202 | }; | ||
203 | |||
204 | static int tegra_pinconf_reg(struct tegra_pmx *pmx, | ||
205 | const struct tegra_pingroup *g, | ||
206 | enum tegra_pinconf_param param, | ||
207 | s8 *bank, s16 *reg, s8 *bit, s8 *width) | ||
208 | { | ||
209 | switch (param) { | ||
210 | case TEGRA_PINCONF_PARAM_PULL: | ||
211 | *bank = g->pupd_bank; | ||
212 | *reg = g->pupd_reg; | ||
213 | *bit = g->pupd_bit; | ||
214 | *width = 2; | ||
215 | break; | ||
216 | case TEGRA_PINCONF_PARAM_TRISTATE: | ||
217 | *bank = g->tri_bank; | ||
218 | *reg = g->tri_reg; | ||
219 | *bit = g->tri_bit; | ||
220 | *width = 1; | ||
221 | break; | ||
222 | case TEGRA_PINCONF_PARAM_ENABLE_INPUT: | ||
223 | *bank = g->einput_bank; | ||
224 | *reg = g->einput_reg; | ||
225 | *bit = g->einput_bit; | ||
226 | *width = 1; | ||
227 | break; | ||
228 | case TEGRA_PINCONF_PARAM_OPEN_DRAIN: | ||
229 | *bank = g->odrain_bank; | ||
230 | *reg = g->odrain_reg; | ||
231 | *bit = g->odrain_bit; | ||
232 | *width = 1; | ||
233 | break; | ||
234 | case TEGRA_PINCONF_PARAM_LOCK: | ||
235 | *bank = g->lock_bank; | ||
236 | *reg = g->lock_reg; | ||
237 | *bit = g->lock_bit; | ||
238 | *width = 1; | ||
239 | break; | ||
240 | case TEGRA_PINCONF_PARAM_IORESET: | ||
241 | *bank = g->ioreset_bank; | ||
242 | *reg = g->ioreset_reg; | ||
243 | *bit = g->ioreset_bit; | ||
244 | *width = 1; | ||
245 | break; | ||
246 | case TEGRA_PINCONF_PARAM_HIGH_SPEED_MODE: | ||
247 | *bank = g->drv_bank; | ||
248 | *reg = g->drv_reg; | ||
249 | *bit = g->hsm_bit; | ||
250 | *width = 1; | ||
251 | break; | ||
252 | case TEGRA_PINCONF_PARAM_SCHMITT: | ||
253 | *bank = g->drv_bank; | ||
254 | *reg = g->drv_reg; | ||
255 | *bit = g->schmitt_bit; | ||
256 | *width = 1; | ||
257 | break; | ||
258 | case TEGRA_PINCONF_PARAM_LOW_POWER_MODE: | ||
259 | *bank = g->drv_bank; | ||
260 | *reg = g->drv_reg; | ||
261 | *bit = g->lpmd_bit; | ||
262 | *width = 1; | ||
263 | break; | ||
264 | case TEGRA_PINCONF_PARAM_DRIVE_DOWN_STRENGTH: | ||
265 | *bank = g->drv_bank; | ||
266 | *reg = g->drv_reg; | ||
267 | *bit = g->drvdn_bit; | ||
268 | *width = g->drvdn_width; | ||
269 | break; | ||
270 | case TEGRA_PINCONF_PARAM_DRIVE_UP_STRENGTH: | ||
271 | *bank = g->drv_bank; | ||
272 | *reg = g->drv_reg; | ||
273 | *bit = g->drvup_bit; | ||
274 | *width = g->drvup_width; | ||
275 | break; | ||
276 | case TEGRA_PINCONF_PARAM_SLEW_RATE_FALLING: | ||
277 | *bank = g->drv_bank; | ||
278 | *reg = g->drv_reg; | ||
279 | *bit = g->slwf_bit; | ||
280 | *width = g->slwf_width; | ||
281 | break; | ||
282 | case TEGRA_PINCONF_PARAM_SLEW_RATE_RISING: | ||
283 | *bank = g->drv_bank; | ||
284 | *reg = g->drv_reg; | ||
285 | *bit = g->slwr_bit; | ||
286 | *width = g->slwr_width; | ||
287 | break; | ||
288 | default: | ||
289 | dev_err(pmx->dev, "Invalid config param %04x\n", param); | ||
290 | return -ENOTSUPP; | ||
291 | } | ||
292 | |||
293 | if (*reg < 0) { | ||
294 | dev_err(pmx->dev, | ||
295 | "Config param %04x not supported on group %s\n", | ||
296 | param, g->name); | ||
297 | return -ENOTSUPP; | ||
298 | } | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static int tegra_pinconf_get(struct pinctrl_dev *pctldev, | ||
304 | unsigned pin, unsigned long *config) | ||
305 | { | ||
306 | return -ENOTSUPP; | ||
307 | } | ||
308 | |||
309 | static int tegra_pinconf_set(struct pinctrl_dev *pctldev, | ||
310 | unsigned pin, unsigned long config) | ||
311 | { | ||
312 | return -ENOTSUPP; | ||
313 | } | ||
314 | |||
315 | static int tegra_pinconf_group_get(struct pinctrl_dev *pctldev, | ||
316 | unsigned group, unsigned long *config) | ||
317 | { | ||
318 | struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); | ||
319 | enum tegra_pinconf_param param = TEGRA_PINCONF_UNPACK_PARAM(*config); | ||
320 | u16 arg; | ||
321 | const struct tegra_pingroup *g; | ||
322 | int ret; | ||
323 | s8 bank, bit, width; | ||
324 | s16 reg; | ||
325 | u32 val, mask; | ||
326 | |||
327 | if (group >= pmx->soc->ngroups) | ||
328 | return -EINVAL; | ||
329 | g = &pmx->soc->groups[group]; | ||
330 | |||
331 | ret = tegra_pinconf_reg(pmx, g, param, &bank, ®, &bit, &width); | ||
332 | if (ret < 0) | ||
333 | return ret; | ||
334 | |||
335 | val = pmx_readl(pmx, bank, reg); | ||
336 | mask = (1 << width) - 1; | ||
337 | arg = (val >> bit) & mask; | ||
338 | |||
339 | *config = TEGRA_PINCONF_PACK(param, arg); | ||
340 | |||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, | ||
345 | unsigned group, unsigned long config) | ||
346 | { | ||
347 | struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); | ||
348 | enum tegra_pinconf_param param = TEGRA_PINCONF_UNPACK_PARAM(config); | ||
349 | u16 arg = TEGRA_PINCONF_UNPACK_ARG(config); | ||
350 | const struct tegra_pingroup *g; | ||
351 | int ret; | ||
352 | s8 bank, bit, width; | ||
353 | s16 reg; | ||
354 | u32 val, mask; | ||
355 | |||
356 | if (group >= pmx->soc->ngroups) | ||
357 | return -EINVAL; | ||
358 | g = &pmx->soc->groups[group]; | ||
359 | |||
360 | ret = tegra_pinconf_reg(pmx, g, param, &bank, ®, &bit, &width); | ||
361 | if (ret < 0) | ||
362 | return ret; | ||
363 | |||
364 | val = pmx_readl(pmx, bank, reg); | ||
365 | |||
366 | /* LOCK can't be cleared */ | ||
367 | if (param == TEGRA_PINCONF_PARAM_LOCK) { | ||
368 | if ((val & BIT(bit)) && !arg) | ||
369 | return -EINVAL; | ||
370 | } | ||
371 | |||
372 | /* Special-case Boolean values; allow any non-zero as true */ | ||
373 | if (width == 1) | ||
374 | arg = !!arg; | ||
375 | |||
376 | /* Range-check user-supplied value */ | ||
377 | mask = (1 << width) - 1; | ||
378 | if (arg & ~mask) | ||
379 | return -EINVAL; | ||
380 | |||
381 | /* Update register */ | ||
382 | val &= ~(mask << bit); | ||
383 | val |= arg << bit; | ||
384 | pmx_writel(pmx, val, bank, reg); | ||
385 | |||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | static void tegra_pinconf_dbg_show(struct pinctrl_dev *pctldev, | ||
390 | struct seq_file *s, unsigned offset) | ||
391 | { | ||
392 | } | ||
393 | |||
394 | static void tegra_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, | ||
395 | struct seq_file *s, unsigned selector) | ||
396 | { | ||
397 | } | ||
398 | |||
399 | struct pinconf_ops tegra_pinconf_ops = { | ||
400 | .pin_config_get = tegra_pinconf_get, | ||
401 | .pin_config_set = tegra_pinconf_set, | ||
402 | .pin_config_group_get = tegra_pinconf_group_get, | ||
403 | .pin_config_group_set = tegra_pinconf_group_set, | ||
404 | .pin_config_dbg_show = tegra_pinconf_dbg_show, | ||
405 | .pin_config_group_dbg_show = tegra_pinconf_group_dbg_show, | ||
406 | }; | ||
407 | |||
408 | static struct pinctrl_gpio_range tegra_pinctrl_gpio_range = { | ||
409 | .name = "Tegra GPIOs", | ||
410 | .id = 0, | ||
411 | .base = 0, | ||
412 | }; | ||
413 | |||
414 | static struct pinctrl_desc tegra_pinctrl_desc = { | ||
415 | .name = DRIVER_NAME, | ||
416 | .pctlops = &tegra_pinctrl_ops, | ||
417 | .pmxops = &tegra_pinmux_ops, | ||
418 | .confops = &tegra_pinconf_ops, | ||
419 | .owner = THIS_MODULE, | ||
420 | }; | ||
421 | |||
422 | static struct of_device_id tegra_pinctrl_of_match[] __devinitdata = { | ||
423 | #ifdef CONFIG_PINCTRL_TEGRA20 | ||
424 | { | ||
425 | .compatible = "nvidia,tegra20-pinmux-disabled", | ||
426 | .data = tegra20_pinctrl_init, | ||
427 | }, | ||
428 | #endif | ||
429 | #ifdef CONFIG_PINCTRL_TEGRA30 | ||
430 | { | ||
431 | .compatible = "nvidia,tegra30-pinmux-disabled", | ||
432 | .data = tegra30_pinctrl_init, | ||
433 | }, | ||
434 | #endif | ||
435 | {}, | ||
436 | }; | ||
437 | |||
438 | static int __devinit tegra_pinctrl_probe(struct platform_device *pdev) | ||
439 | { | ||
440 | const struct of_device_id *match; | ||
441 | tegra_pinctrl_soc_initf initf = NULL; | ||
442 | struct tegra_pmx *pmx; | ||
443 | struct resource *res; | ||
444 | int i; | ||
445 | |||
446 | match = of_match_device(tegra_pinctrl_of_match, &pdev->dev); | ||
447 | if (match) | ||
448 | initf = (tegra_pinctrl_soc_initf)match->data; | ||
449 | #ifdef CONFIG_PINCTRL_TEGRA20 | ||
450 | if (!initf) | ||
451 | initf = tegra20_pinctrl_init; | ||
452 | #endif | ||
453 | if (!initf) { | ||
454 | dev_err(&pdev->dev, | ||
455 | "Could not determine SoC-specific init func\n"); | ||
456 | return -EINVAL; | ||
457 | } | ||
458 | |||
459 | pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL); | ||
460 | if (!pmx) { | ||
461 | dev_err(&pdev->dev, "Can't alloc tegra_pmx\n"); | ||
462 | return -ENOMEM; | ||
463 | } | ||
464 | pmx->dev = &pdev->dev; | ||
465 | |||
466 | (*initf)(&pmx->soc); | ||
467 | |||
468 | tegra_pinctrl_gpio_range.npins = pmx->soc->ngpios; | ||
469 | tegra_pinctrl_desc.pins = pmx->soc->pins; | ||
470 | tegra_pinctrl_desc.npins = pmx->soc->npins; | ||
471 | |||
472 | for (i = 0; ; i++) { | ||
473 | res = platform_get_resource(pdev, IORESOURCE_MEM, i); | ||
474 | if (!res) | ||
475 | break; | ||
476 | } | ||
477 | pmx->nbanks = i; | ||
478 | |||
479 | pmx->regs = devm_kzalloc(&pdev->dev, pmx->nbanks * sizeof(*pmx->regs), | ||
480 | GFP_KERNEL); | ||
481 | if (!pmx->regs) { | ||
482 | dev_err(&pdev->dev, "Can't alloc regs pointer\n"); | ||
483 | return -ENODEV; | ||
484 | } | ||
485 | |||
486 | for (i = 0; i < pmx->nbanks; i++) { | ||
487 | res = platform_get_resource(pdev, IORESOURCE_MEM, i); | ||
488 | if (!res) { | ||
489 | dev_err(&pdev->dev, "Missing MEM resource\n"); | ||
490 | return -ENODEV; | ||
491 | } | ||
492 | |||
493 | if (!devm_request_mem_region(&pdev->dev, res->start, | ||
494 | resource_size(res), | ||
495 | dev_name(&pdev->dev))) { | ||
496 | dev_err(&pdev->dev, | ||
497 | "Couldn't request MEM resource %d\n", i); | ||
498 | return -ENODEV; | ||
499 | } | ||
500 | |||
501 | pmx->regs[i] = devm_ioremap(&pdev->dev, res->start, | ||
502 | resource_size(res)); | ||
503 | if (!pmx->regs[i]) { | ||
504 | dev_err(&pdev->dev, "Couldn't ioremap regs %d\n", i); | ||
505 | return -ENODEV; | ||
506 | } | ||
507 | } | ||
508 | |||
509 | pmx->pctl = pinctrl_register(&tegra_pinctrl_desc, &pdev->dev, pmx); | ||
510 | if (IS_ERR(pmx->pctl)) { | ||
511 | dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); | ||
512 | return PTR_ERR(pmx->pctl); | ||
513 | } | ||
514 | |||
515 | pinctrl_add_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range); | ||
516 | |||
517 | platform_set_drvdata(pdev, pmx); | ||
518 | |||
519 | dev_dbg(&pdev->dev, "Probed Tegra pinctrl driver\n"); | ||
520 | |||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | static int __devexit tegra_pinctrl_remove(struct platform_device *pdev) | ||
525 | { | ||
526 | struct tegra_pmx *pmx = platform_get_drvdata(pdev); | ||
527 | |||
528 | pinctrl_remove_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range); | ||
529 | pinctrl_unregister(pmx->pctl); | ||
530 | |||
531 | return 0; | ||
532 | } | ||
533 | |||
534 | static struct platform_driver tegra_pinctrl_driver = { | ||
535 | .driver = { | ||
536 | .name = DRIVER_NAME, | ||
537 | .owner = THIS_MODULE, | ||
538 | .of_match_table = tegra_pinctrl_of_match, | ||
539 | }, | ||
540 | .probe = tegra_pinctrl_probe, | ||
541 | .remove = __devexit_p(tegra_pinctrl_remove), | ||
542 | }; | ||
543 | |||
544 | static int __init tegra_pinctrl_init(void) | ||
545 | { | ||
546 | return platform_driver_register(&tegra_pinctrl_driver); | ||
547 | } | ||
548 | arch_initcall(tegra_pinctrl_init); | ||
549 | |||
550 | static void __exit tegra_pinctrl_exit(void) | ||
551 | { | ||
552 | platform_driver_unregister(&tegra_pinctrl_driver); | ||
553 | } | ||
554 | module_exit(tegra_pinctrl_exit); | ||
555 | |||
556 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); | ||
557 | MODULE_DESCRIPTION("NVIDIA Tegra pinctrl driver"); | ||
558 | MODULE_LICENSE("GPL v2"); | ||
559 | MODULE_DEVICE_TABLE(of, tegra_pinctrl_of_match); | ||