1/* $NetBSD: nouveau_subdev_gpio_base.c,v 1.1.1.1 2014/08/06 12:36:30 riastradh Exp $ */
2
3/*
4 * Copyright 2011 Red Hat Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * Authors: Ben Skeggs
25 */
26
27#include <sys/cdefs.h>
28__KERNEL_RCSID(0, "$NetBSD: nouveau_subdev_gpio_base.c,v 1.1.1.1 2014/08/06 12:36:30 riastradh Exp $");
29
30#include <subdev/gpio.h>
31#include <subdev/bios.h>
32#include <subdev/bios/gpio.h>
33
34static int
35nouveau_gpio_drive(struct nouveau_gpio *gpio,
36 int idx, int line, int dir, int out)
37{
38 return gpio->drive ? gpio->drive(gpio, line, dir, out) : -ENODEV;
39}
40
41static int
42nouveau_gpio_sense(struct nouveau_gpio *gpio, int idx, int line)
43{
44 return gpio->sense ? gpio->sense(gpio, line) : -ENODEV;
45}
46
47static int
48nouveau_gpio_find(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
49 struct dcb_gpio_func *func)
50{
51 struct nouveau_bios *bios = nouveau_bios(gpio);
52 u8 ver, len;
53 u16 data;
54
55 if (line == 0xff && tag == 0xff)
56 return -EINVAL;
57
58 data = dcb_gpio_match(bios, idx, tag, line, &ver, &len, func);
59 if (data)
60 return 0;
61
62 /* Apple iMac G4 NV18 */
63 if (nv_device_match(nv_object(gpio), 0x0189, 0x10de, 0x0010)) {
64 if (tag == DCB_GPIO_TVDAC0) {
65 *func = (struct dcb_gpio_func) {
66 .func = DCB_GPIO_TVDAC0,
67 .line = 4,
68 .log[0] = 0,
69 .log[1] = 1,
70 };
71 return 0;
72 }
73 }
74
75 return -ENOENT;
76}
77
78static int
79nouveau_gpio_set(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, int state)
80{
81 struct dcb_gpio_func func;
82 int ret;
83
84 ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
85 if (ret == 0) {
86 int dir = !!(func.log[state] & 0x02);
87 int out = !!(func.log[state] & 0x01);
88 ret = nouveau_gpio_drive(gpio, idx, func.line, dir, out);
89 }
90
91 return ret;
92}
93
94static int
95nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line)
96{
97 struct dcb_gpio_func func;
98 int ret;
99
100 ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
101 if (ret == 0) {
102 ret = nouveau_gpio_sense(gpio, idx, func.line);
103 if (ret >= 0)
104 ret = (ret == (func.log[1] & 1));
105 }
106
107 return ret;
108}
109
110void
111_nouveau_gpio_dtor(struct nouveau_object *object)
112{
113 struct nouveau_gpio *gpio = (void *)object;
114 nouveau_event_destroy(&gpio->events);
115 nouveau_subdev_destroy(&gpio->base);
116}
117
118int
119nouveau_gpio_create_(struct nouveau_object *parent,
120 struct nouveau_object *engine,
121 struct nouveau_oclass *oclass, int lines,
122 int length, void **pobject)
123{
124 struct nouveau_gpio *gpio;
125 int ret;
126
127 ret = nouveau_subdev_create_(parent, engine, oclass, 0, "GPIO", "gpio",
128 length, pobject);
129 gpio = *pobject;
130 if (ret)
131 return ret;
132
133 ret = nouveau_event_create(lines, &gpio->events);
134 if (ret)
135 return ret;
136
137 gpio->find = nouveau_gpio_find;
138 gpio->set = nouveau_gpio_set;
139 gpio->get = nouveau_gpio_get;
140 return 0;
141}
142
143static struct dmi_system_id gpio_reset_ids[] = {
144 {
145 .ident = "Apple Macbook 10,1",
146 .matches = {
147 DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
148 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
149 }
150 },
151 { }
152};
153
154int
155nouveau_gpio_init(struct nouveau_gpio *gpio)
156{
157 int ret = nouveau_subdev_init(&gpio->base);
158 if (ret == 0 && gpio->reset) {
159 if (dmi_check_system(gpio_reset_ids))
160 gpio->reset(gpio, DCB_GPIO_UNUSED);
161 }
162 return ret;
163}
164