]> git.openfabrics.org - ~shefty/rdma-dev.git/blob - drivers/gpu/drm/nouveau/core/subdev/gpio/base.c
drm/nouveau/bios: implement opcode 0xa9
[~shefty/rdma-dev.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         struct nouveau_bios *bios = nouveau_bios(gpio);
47         u8  ver, len;
48         u16 data;
49
50         if (line == 0xff && tag == 0xff)
51                 return -EINVAL;
52
53         data = dcb_gpio_match(bios, idx, tag, line, &ver, &len, func);
54         if (data)
55                 return 0;
56
57         /* Apple iMac G4 NV18 */
58         if (nv_device_match(nv_object(gpio), 0x0189, 0x10de, 0x0010)) {
59                 if (tag == DCB_GPIO_TVDAC0) {
60                         *func = (struct dcb_gpio_func) {
61                                 .func = DCB_GPIO_TVDAC0,
62                                 .line = 4,
63                                 .log[0] = 0,
64                                 .log[1] = 1,
65                         };
66                         return 0;
67                 }
68         }
69
70         return -EINVAL;
71 }
72
73 static int
74 nouveau_gpio_set(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, int state)
75 {
76         struct dcb_gpio_func func;
77         int ret;
78
79         ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
80         if (ret == 0) {
81                 int dir = !!(func.log[state] & 0x02);
82                 int out = !!(func.log[state] & 0x01);
83                 ret = nouveau_gpio_drive(gpio, idx, func.line, dir, out);
84         }
85
86         return ret;
87 }
88
89 static int
90 nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line)
91 {
92         struct dcb_gpio_func func;
93         int ret;
94
95         ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
96         if (ret == 0) {
97                 ret = nouveau_gpio_sense(gpio, idx, func.line);
98                 if (ret >= 0)
99                         ret = (ret == (func.log[1] & 1));
100         }
101
102         return ret;
103 }
104
105 static int
106 nouveau_gpio_irq(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, bool on)
107 {
108         struct dcb_gpio_func func;
109         int ret;
110
111         ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
112         if (ret == 0) {
113                 if (idx == 0 && gpio->irq_enable)
114                         gpio->irq_enable(gpio, func.line, on);
115                 else
116                         ret = -ENODEV;
117         }
118
119         return ret;
120 }
121
122 struct gpio_isr {
123         struct nouveau_gpio *gpio;
124         struct list_head head;
125         struct work_struct work;
126         int idx;
127         struct dcb_gpio_func func;
128         void (*handler)(void *, int);
129         void *data;
130         bool inhibit;
131 };
132
133 static void
134 nouveau_gpio_isr_bh(struct work_struct *work)
135 {
136         struct gpio_isr *isr = container_of(work, struct gpio_isr, work);
137         struct nouveau_gpio *gpio = isr->gpio;
138         unsigned long flags;
139         int state;
140
141         state = nouveau_gpio_get(gpio, isr->idx, isr->func.func,
142                                                  isr->func.line);
143         if (state >= 0)
144                 isr->handler(isr->data, state);
145
146         spin_lock_irqsave(&gpio->lock, flags);
147         isr->inhibit = false;
148         spin_unlock_irqrestore(&gpio->lock, flags);
149 }
150
151 static void
152 nouveau_gpio_isr_run(struct nouveau_gpio *gpio, int idx, u32 line_mask)
153 {
154         struct gpio_isr *isr;
155
156         if (idx != 0)
157                 return;
158
159         spin_lock(&gpio->lock);
160         list_for_each_entry(isr, &gpio->isr, head) {
161                 if (line_mask & (1 << isr->func.line)) {
162                         if (isr->inhibit)
163                                 continue;
164                         isr->inhibit = true;
165                         schedule_work(&isr->work);
166                 }
167         }
168         spin_unlock(&gpio->lock);
169 }
170
171 static int
172 nouveau_gpio_isr_add(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
173                      void (*handler)(void *, int), void *data)
174 {
175         struct gpio_isr *isr;
176         unsigned long flags;
177         int ret;
178
179         isr = kzalloc(sizeof(*isr), GFP_KERNEL);
180         if (!isr)
181                 return -ENOMEM;
182
183         ret = nouveau_gpio_find(gpio, idx, tag, line, &isr->func);
184         if (ret) {
185                 kfree(isr);
186                 return ret;
187         }
188
189         INIT_WORK(&isr->work, nouveau_gpio_isr_bh);
190         isr->gpio = gpio;
191         isr->handler = handler;
192         isr->data = data;
193         isr->idx = idx;
194
195         spin_lock_irqsave(&gpio->lock, flags);
196         list_add(&isr->head, &gpio->isr);
197         spin_unlock_irqrestore(&gpio->lock, flags);
198         return 0;
199 }
200
201 static void
202 nouveau_gpio_isr_del(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
203                      void (*handler)(void *, int), void *data)
204 {
205         struct gpio_isr *isr, *tmp;
206         struct dcb_gpio_func func;
207         unsigned long flags;
208         LIST_HEAD(tofree);
209         int ret;
210
211         ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
212         if (ret == 0) {
213                 spin_lock_irqsave(&gpio->lock, flags);
214                 list_for_each_entry_safe(isr, tmp, &gpio->isr, head) {
215                         if (memcmp(&isr->func, &func, sizeof(func)) ||
216                             isr->idx != idx ||
217                             isr->handler != handler || isr->data != data)
218                                 continue;
219                         list_move_tail(&isr->head, &tofree);
220                 }
221                 spin_unlock_irqrestore(&gpio->lock, flags);
222
223                 list_for_each_entry_safe(isr, tmp, &tofree, head) {
224                         flush_work(&isr->work);
225                         kfree(isr);
226                 }
227         }
228 }
229
230 int
231 nouveau_gpio_create_(struct nouveau_object *parent,
232                      struct nouveau_object *engine,
233                      struct nouveau_oclass *oclass, int length, void **pobject)
234 {
235         struct nouveau_gpio *gpio;
236         int ret;
237
238         ret = nouveau_subdev_create_(parent, engine, oclass, 0, "GPIO", "gpio",
239                                      length, pobject);
240         gpio = *pobject;
241         if (ret)
242                 return ret;
243
244         gpio->find = nouveau_gpio_find;
245         gpio->set  = nouveau_gpio_set;
246         gpio->get  = nouveau_gpio_get;
247         gpio->irq  = nouveau_gpio_irq;
248         gpio->isr_run = nouveau_gpio_isr_run;
249         gpio->isr_add = nouveau_gpio_isr_add;
250         gpio->isr_del = nouveau_gpio_isr_del;
251         INIT_LIST_HEAD(&gpio->isr);
252         spin_lock_init(&gpio->lock);
253         return 0;
254 }
255
256 static struct dmi_system_id gpio_reset_ids[] = {
257         {
258                 .ident = "Apple Macbook 10,1",
259                 .matches = {
260                         DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
261                         DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
262                 }
263         },
264         { }
265 };
266
267 int
268 nouveau_gpio_init(struct nouveau_gpio *gpio)
269 {
270         int ret = nouveau_subdev_init(&gpio->base);
271         if (ret == 0 && gpio->reset) {
272                 if (dmi_check_system(gpio_reset_ids))
273                         gpio->reset(gpio, DCB_GPIO_UNUSED);
274         }
275         return ret;
276 }