brcmsmac: rework of mac80211 .flush() callback operation
[pandora-kernel.git] / drivers / gpu / drm / nouveau / core / subdev / gpio / base.c
1 /*
2  * Copyright 2011 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: Ben Skeggs
23  */
24
25 #include <subdev/gpio.h>
26 #include <subdev/bios.h>
27 #include <subdev/bios/gpio.h>
28
29 static int
30 nouveau_gpio_drive(struct nouveau_gpio *gpio,
31                    int idx, int line, int dir, int out)
32 {
33         return gpio->drive ? gpio->drive(gpio, line, dir, out) : -ENODEV;
34 }
35
36 static int
37 nouveau_gpio_sense(struct nouveau_gpio *gpio, int idx, int line)
38 {
39         return gpio->sense ? gpio->sense(gpio, line) : -ENODEV;
40 }
41
42 static int
43 nouveau_gpio_find(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
44                   struct dcb_gpio_func *func)
45 {
46         if (line == 0xff && tag == 0xff)
47                 return -EINVAL;
48
49         if (!dcb_gpio_parse(nouveau_bios(gpio), idx, tag, line, func))
50                 return 0;
51
52         /* Apple iMac G4 NV18 */
53         if (nv_device_match(nv_object(gpio), 0x0189, 0x10de, 0x0010)) {
54                 if (tag == DCB_GPIO_TVDAC0) {
55                         *func = (struct dcb_gpio_func) {
56                                 .func = DCB_GPIO_TVDAC0,
57                                 .line = 4,
58                                 .log[0] = 0,
59                                 .log[1] = 1,
60                         };
61                         return 0;
62                 }
63         }
64
65         return -EINVAL;
66 }
67
68 static int
69 nouveau_gpio_set(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, int state)
70 {
71         struct dcb_gpio_func func;
72         int ret;
73
74         ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
75         if (ret == 0) {
76                 int dir = !!(func.log[state] & 0x02);
77                 int out = !!(func.log[state] & 0x01);
78                 ret = nouveau_gpio_drive(gpio, idx, func.line, dir, out);
79         }
80
81         return ret;
82 }
83
84 static int
85 nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line)
86 {
87         struct dcb_gpio_func func;
88         int ret;
89
90         ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
91         if (ret == 0) {
92                 ret = nouveau_gpio_sense(gpio, idx, func.line);
93                 if (ret >= 0)
94                         ret = (ret == (func.log[1] & 1));
95         }
96
97         return ret;
98 }
99
100 static int
101 nouveau_gpio_irq(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, bool on)
102 {
103         struct dcb_gpio_func func;
104         int ret;
105
106         ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
107         if (ret == 0) {
108                 if (idx == 0 && gpio->irq_enable)
109                         gpio->irq_enable(gpio, func.line, on);
110                 else
111                         ret = -ENODEV;
112         }
113
114         return ret;
115 }
116
117 struct gpio_isr {
118         struct nouveau_gpio *gpio;
119         struct list_head head;
120         struct work_struct work;
121         int idx;
122         struct dcb_gpio_func func;
123         void (*handler)(void *, int);
124         void *data;
125         bool inhibit;
126 };
127
128 static void
129 nouveau_gpio_isr_bh(struct work_struct *work)
130 {
131         struct gpio_isr *isr = container_of(work, struct gpio_isr, work);
132         struct nouveau_gpio *gpio = isr->gpio;
133         unsigned long flags;
134         int state;
135
136         state = nouveau_gpio_get(gpio, isr->idx, isr->func.func,
137                                                  isr->func.line);
138         if (state >= 0)
139                 isr->handler(isr->data, state);
140
141         spin_lock_irqsave(&gpio->lock, flags);
142         isr->inhibit = false;
143         spin_unlock_irqrestore(&gpio->lock, flags);
144 }
145
146 static void
147 nouveau_gpio_isr_run(struct nouveau_gpio *gpio, int idx, u32 line_mask)
148 {
149         struct gpio_isr *isr;
150
151         if (idx != 0)
152                 return;
153
154         spin_lock(&gpio->lock);
155         list_for_each_entry(isr, &gpio->isr, head) {
156                 if (line_mask & (1 << isr->func.line)) {
157                         if (isr->inhibit)
158                                 continue;
159                         isr->inhibit = true;
160                         schedule_work(&isr->work);
161                 }
162         }
163         spin_unlock(&gpio->lock);
164 }
165
166 static int
167 nouveau_gpio_isr_add(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
168                      void (*handler)(void *, int), void *data)
169 {
170         struct gpio_isr *isr;
171         unsigned long flags;
172         int ret;
173
174         isr = kzalloc(sizeof(*isr), GFP_KERNEL);
175         if (!isr)
176                 return -ENOMEM;
177
178         ret = nouveau_gpio_find(gpio, idx, tag, line, &isr->func);
179         if (ret) {
180                 kfree(isr);
181                 return ret;
182         }
183
184         INIT_WORK(&isr->work, nouveau_gpio_isr_bh);
185         isr->gpio = gpio;
186         isr->handler = handler;
187         isr->data = data;
188         isr->idx = idx;
189
190         spin_lock_irqsave(&gpio->lock, flags);
191         list_add(&isr->head, &gpio->isr);
192         spin_unlock_irqrestore(&gpio->lock, flags);
193         return 0;
194 }
195
196 static void
197 nouveau_gpio_isr_del(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
198                      void (*handler)(void *, int), void *data)
199 {
200         struct gpio_isr *isr, *tmp;
201         struct dcb_gpio_func func;
202         unsigned long flags;
203         LIST_HEAD(tofree);
204         int ret;
205
206         ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
207         if (ret == 0) {
208                 spin_lock_irqsave(&gpio->lock, flags);
209                 list_for_each_entry_safe(isr, tmp, &gpio->isr, head) {
210                         if (memcmp(&isr->func, &func, sizeof(func)) ||
211                             isr->idx != idx ||
212                             isr->handler != handler || isr->data != data)
213                                 continue;
214                         list_move_tail(&isr->head, &tofree);
215                 }
216                 spin_unlock_irqrestore(&gpio->lock, flags);
217
218                 list_for_each_entry_safe(isr, tmp, &tofree, head) {
219                         flush_work(&isr->work);
220                         kfree(isr);
221                 }
222         }
223 }
224
225 int
226 nouveau_gpio_create_(struct nouveau_object *parent,
227                      struct nouveau_object *engine,
228                      struct nouveau_oclass *oclass, int length, void **pobject)
229 {
230         struct nouveau_gpio *gpio;
231         int ret;
232
233         ret = nouveau_subdev_create_(parent, engine, oclass, 0, "GPIO", "gpio",
234                                      length, pobject);
235         gpio = *pobject;
236         if (ret)
237                 return ret;
238
239         gpio->find = nouveau_gpio_find;
240         gpio->set  = nouveau_gpio_set;
241         gpio->get  = nouveau_gpio_get;
242         gpio->irq  = nouveau_gpio_irq;
243         gpio->isr_run = nouveau_gpio_isr_run;
244         gpio->isr_add = nouveau_gpio_isr_add;
245         gpio->isr_del = nouveau_gpio_isr_del;
246         INIT_LIST_HEAD(&gpio->isr);
247         spin_lock_init(&gpio->lock);
248         return 0;
249 }
250
251 static struct dmi_system_id gpio_reset_ids[] = {
252         {
253                 .ident = "Apple Macbook 10,1",
254                 .matches = {
255                         DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
256                         DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
257                 }
258         },
259         { }
260 };
261
262 int
263 nouveau_gpio_init(struct nouveau_gpio *gpio)
264 {
265         int ret = nouveau_subdev_init(&gpio->base);
266         if (ret == 0 && gpio->reset) {
267                 if (dmi_check_system(gpio_reset_ids))
268                         gpio->reset(gpio);
269         }
270         return ret;
271 }