diff options
Diffstat (limited to 'arch/mips/lantiq/xway/gpio.c')
-rw-r--r-- | arch/mips/lantiq/xway/gpio.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/arch/mips/lantiq/xway/gpio.c b/arch/mips/lantiq/xway/gpio.c new file mode 100644 index 000000000000..a321451a5455 --- /dev/null +++ b/arch/mips/lantiq/xway/gpio.c | |||
@@ -0,0 +1,195 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify it | ||
3 | * under the terms of the GNU General Public License version 2 as published | ||
4 | * by the Free Software Foundation. | ||
5 | * | ||
6 | * Copyright (C) 2010 John Crispin <blogic@openwrt.org> | ||
7 | */ | ||
8 | |||
9 | #include <linux/slab.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/platform_device.h> | ||
12 | #include <linux/gpio.h> | ||
13 | #include <linux/ioport.h> | ||
14 | #include <linux/io.h> | ||
15 | |||
16 | #include <lantiq_soc.h> | ||
17 | |||
18 | #define LTQ_GPIO_OUT 0x00 | ||
19 | #define LTQ_GPIO_IN 0x04 | ||
20 | #define LTQ_GPIO_DIR 0x08 | ||
21 | #define LTQ_GPIO_ALTSEL0 0x0C | ||
22 | #define LTQ_GPIO_ALTSEL1 0x10 | ||
23 | #define LTQ_GPIO_OD 0x14 | ||
24 | |||
25 | #define PINS_PER_PORT 16 | ||
26 | #define MAX_PORTS 3 | ||
27 | |||
28 | #define ltq_gpio_getbit(m, r, p) (!!(ltq_r32(m + r) & (1 << p))) | ||
29 | #define ltq_gpio_setbit(m, r, p) ltq_w32_mask(0, (1 << p), m + r) | ||
30 | #define ltq_gpio_clearbit(m, r, p) ltq_w32_mask((1 << p), 0, m + r) | ||
31 | |||
32 | struct ltq_gpio { | ||
33 | void __iomem *membase; | ||
34 | struct gpio_chip chip; | ||
35 | }; | ||
36 | |||
37 | static struct ltq_gpio ltq_gpio_port[MAX_PORTS]; | ||
38 | |||
39 | int gpio_to_irq(unsigned int gpio) | ||
40 | { | ||
41 | return -EINVAL; | ||
42 | } | ||
43 | EXPORT_SYMBOL(gpio_to_irq); | ||
44 | |||
45 | int irq_to_gpio(unsigned int gpio) | ||
46 | { | ||
47 | return -EINVAL; | ||
48 | } | ||
49 | EXPORT_SYMBOL(irq_to_gpio); | ||
50 | |||
51 | int ltq_gpio_request(unsigned int pin, unsigned int alt0, | ||
52 | unsigned int alt1, unsigned int dir, const char *name) | ||
53 | { | ||
54 | int id = 0; | ||
55 | |||
56 | if (pin >= (MAX_PORTS * PINS_PER_PORT)) | ||
57 | return -EINVAL; | ||
58 | if (gpio_request(pin, name)) { | ||
59 | pr_err("failed to setup lantiq gpio: %s\n", name); | ||
60 | return -EBUSY; | ||
61 | } | ||
62 | if (dir) | ||
63 | gpio_direction_output(pin, 1); | ||
64 | else | ||
65 | gpio_direction_input(pin); | ||
66 | while (pin >= PINS_PER_PORT) { | ||
67 | pin -= PINS_PER_PORT; | ||
68 | id++; | ||
69 | } | ||
70 | if (alt0) | ||
71 | ltq_gpio_setbit(ltq_gpio_port[id].membase, | ||
72 | LTQ_GPIO_ALTSEL0, pin); | ||
73 | else | ||
74 | ltq_gpio_clearbit(ltq_gpio_port[id].membase, | ||
75 | LTQ_GPIO_ALTSEL0, pin); | ||
76 | if (alt1) | ||
77 | ltq_gpio_setbit(ltq_gpio_port[id].membase, | ||
78 | LTQ_GPIO_ALTSEL1, pin); | ||
79 | else | ||
80 | ltq_gpio_clearbit(ltq_gpio_port[id].membase, | ||
81 | LTQ_GPIO_ALTSEL1, pin); | ||
82 | return 0; | ||
83 | } | ||
84 | EXPORT_SYMBOL(ltq_gpio_request); | ||
85 | |||
86 | static void ltq_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) | ||
87 | { | ||
88 | struct ltq_gpio *ltq_gpio = container_of(chip, struct ltq_gpio, chip); | ||
89 | |||
90 | if (value) | ||
91 | ltq_gpio_setbit(ltq_gpio->membase, LTQ_GPIO_OUT, offset); | ||
92 | else | ||
93 | ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_OUT, offset); | ||
94 | } | ||
95 | |||
96 | static int ltq_gpio_get(struct gpio_chip *chip, unsigned int offset) | ||
97 | { | ||
98 | struct ltq_gpio *ltq_gpio = container_of(chip, struct ltq_gpio, chip); | ||
99 | |||
100 | return ltq_gpio_getbit(ltq_gpio->membase, LTQ_GPIO_IN, offset); | ||
101 | } | ||
102 | |||
103 | static int ltq_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) | ||
104 | { | ||
105 | struct ltq_gpio *ltq_gpio = container_of(chip, struct ltq_gpio, chip); | ||
106 | |||
107 | ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_OD, offset); | ||
108 | ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_DIR, offset); | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static int ltq_gpio_direction_output(struct gpio_chip *chip, | ||
114 | unsigned int offset, int value) | ||
115 | { | ||
116 | struct ltq_gpio *ltq_gpio = container_of(chip, struct ltq_gpio, chip); | ||
117 | |||
118 | ltq_gpio_setbit(ltq_gpio->membase, LTQ_GPIO_OD, offset); | ||
119 | ltq_gpio_setbit(ltq_gpio->membase, LTQ_GPIO_DIR, offset); | ||
120 | ltq_gpio_set(chip, offset, value); | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static int ltq_gpio_req(struct gpio_chip *chip, unsigned offset) | ||
126 | { | ||
127 | struct ltq_gpio *ltq_gpio = container_of(chip, struct ltq_gpio, chip); | ||
128 | |||
129 | ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_ALTSEL0, offset); | ||
130 | ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_ALTSEL1, offset); | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static int ltq_gpio_probe(struct platform_device *pdev) | ||
135 | { | ||
136 | struct resource *res; | ||
137 | |||
138 | if (pdev->id >= MAX_PORTS) { | ||
139 | dev_err(&pdev->dev, "invalid gpio port %d\n", | ||
140 | pdev->id); | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
144 | if (!res) { | ||
145 | dev_err(&pdev->dev, "failed to get memory for gpio port %d\n", | ||
146 | pdev->id); | ||
147 | return -ENOENT; | ||
148 | } | ||
149 | res = devm_request_mem_region(&pdev->dev, res->start, | ||
150 | resource_size(res), dev_name(&pdev->dev)); | ||
151 | if (!res) { | ||
152 | dev_err(&pdev->dev, | ||
153 | "failed to request memory for gpio port %d\n", | ||
154 | pdev->id); | ||
155 | return -EBUSY; | ||
156 | } | ||
157 | ltq_gpio_port[pdev->id].membase = devm_ioremap_nocache(&pdev->dev, | ||
158 | res->start, resource_size(res)); | ||
159 | if (!ltq_gpio_port[pdev->id].membase) { | ||
160 | dev_err(&pdev->dev, "failed to remap memory for gpio port %d\n", | ||
161 | pdev->id); | ||
162 | return -ENOMEM; | ||
163 | } | ||
164 | ltq_gpio_port[pdev->id].chip.label = "ltq_gpio"; | ||
165 | ltq_gpio_port[pdev->id].chip.direction_input = ltq_gpio_direction_input; | ||
166 | ltq_gpio_port[pdev->id].chip.direction_output = | ||
167 | ltq_gpio_direction_output; | ||
168 | ltq_gpio_port[pdev->id].chip.get = ltq_gpio_get; | ||
169 | ltq_gpio_port[pdev->id].chip.set = ltq_gpio_set; | ||
170 | ltq_gpio_port[pdev->id].chip.request = ltq_gpio_req; | ||
171 | ltq_gpio_port[pdev->id].chip.base = PINS_PER_PORT * pdev->id; | ||
172 | ltq_gpio_port[pdev->id].chip.ngpio = PINS_PER_PORT; | ||
173 | platform_set_drvdata(pdev, <q_gpio_port[pdev->id]); | ||
174 | return gpiochip_add(<q_gpio_port[pdev->id].chip); | ||
175 | } | ||
176 | |||
177 | static struct platform_driver | ||
178 | ltq_gpio_driver = { | ||
179 | .probe = ltq_gpio_probe, | ||
180 | .driver = { | ||
181 | .name = "ltq_gpio", | ||
182 | .owner = THIS_MODULE, | ||
183 | }, | ||
184 | }; | ||
185 | |||
186 | int __init ltq_gpio_init(void) | ||
187 | { | ||
188 | int ret = platform_driver_register(<q_gpio_driver); | ||
189 | |||
190 | if (ret) | ||
191 | pr_info("ltq_gpio : Error registering platfom driver!"); | ||
192 | return ret; | ||
193 | } | ||
194 | |||
195 | postcore_initcall(ltq_gpio_init); | ||