Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
base.c
Go to the documentation of this file.
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) {
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 {
119  struct list_head head;
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
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
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 }