1/* $NetBSD: nouveau_subdev_gpio_nv50.c,v 1.1.1.1 2014/08/06 12:36:30 riastradh Exp $ */
2
3/*
4 * Copyright 2012 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_nv50.c,v 1.1.1.1 2014/08/06 12:36:30 riastradh Exp $");
29
30#include "priv.h"
31
32struct nv50_gpio_priv {
33 struct nouveau_gpio base;
34};
35
36static void
37nv50_gpio_reset(struct nouveau_gpio *gpio, u8 match)
38{
39 struct nouveau_bios *bios = nouveau_bios(gpio);
40 struct nv50_gpio_priv *priv = (void *)gpio;
41 u8 ver, len;
42 u16 entry;
43 int ent = -1;
44
45 while ((entry = dcb_gpio_entry(bios, 0, ++ent, &ver, &len))) {
46 static const u32 regs[] = { 0xe100, 0xe28c };
47 u32 data = nv_ro32(bios, entry);
48 u8 line = (data & 0x0000001f);
49 u8 func = (data & 0x0000ff00) >> 8;
50 u8 defs = !!(data & 0x01000000);
51 u8 unk0 = !!(data & 0x02000000);
52 u8 unk1 = !!(data & 0x04000000);
53 u32 val = (unk1 << 16) | unk0;
54 u32 reg = regs[line >> 4];
55 u32 lsh = line & 0x0f;
56
57 if ( func == DCB_GPIO_UNUSED ||
58 (match != DCB_GPIO_UNUSED && match != func))
59 continue;
60
61 gpio->set(gpio, 0, func, line, defs);
62
63 nv_mask(priv, reg, 0x00010001 << lsh, val << lsh);
64 }
65}
66
67static int
68nv50_gpio_location(int line, u32 *reg, u32 *shift)
69{
70 const u32 nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
71
72 if (line >= 32)
73 return -EINVAL;
74
75 *reg = nv50_gpio_reg[line >> 3];
76 *shift = (line & 7) << 2;
77 return 0;
78}
79
80static int
81nv50_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
82{
83 u32 reg, shift;
84
85 if (nv50_gpio_location(line, &reg, &shift))
86 return -EINVAL;
87
88 nv_mask(gpio, reg, 3 << shift, (((dir ^ 1) << 1) | out) << shift);
89 return 0;
90}
91
92static int
93nv50_gpio_sense(struct nouveau_gpio *gpio, int line)
94{
95 u32 reg, shift;
96
97 if (nv50_gpio_location(line, &reg, &shift))
98 return -EINVAL;
99
100 return !!(nv_rd32(gpio, reg) & (4 << shift));
101}
102
103void
104nv50_gpio_intr(struct nouveau_subdev *subdev)
105{
106 struct nv50_gpio_priv *priv = (void *)subdev;
107 u32 intr0, intr1 = 0;
108 u32 hi, lo;
109 int i;
110
111 intr0 = nv_rd32(priv, 0xe054) & nv_rd32(priv, 0xe050);
112 if (nv_device(priv)->chipset > 0x92)
113 intr1 = nv_rd32(priv, 0xe074) & nv_rd32(priv, 0xe070);
114
115 hi = (intr0 & 0x0000ffff) | (intr1 << 16);
116 lo = (intr0 >> 16) | (intr1 & 0xffff0000);
117
118 for (i = 0; (hi | lo) && i < 32; i++) {
119 if ((hi | lo) & (1 << i))
120 nouveau_event_trigger(priv->base.events, i);
121 }
122
123 nv_wr32(priv, 0xe054, intr0);
124 if (nv_device(priv)->chipset > 0x92)
125 nv_wr32(priv, 0xe074, intr1);
126}
127
128void
129nv50_gpio_intr_enable(struct nouveau_event *event, int line)
130{
131 const u32 addr = line < 16 ? 0xe050 : 0xe070;
132 const u32 mask = 0x00010001 << (line & 0xf);
133 nv_wr32(event->priv, addr + 0x04, mask);
134 nv_mask(event->priv, addr + 0x00, mask, mask);
135}
136
137void
138nv50_gpio_intr_disable(struct nouveau_event *event, int line)
139{
140 const u32 addr = line < 16 ? 0xe050 : 0xe070;
141 const u32 mask = 0x00010001 << (line & 0xf);
142 nv_wr32(event->priv, addr + 0x04, mask);
143 nv_mask(event->priv, addr + 0x00, mask, 0x00000000);
144}
145
146static int
147nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
148 struct nouveau_oclass *oclass, void *data, u32 size,
149 struct nouveau_object **pobject)
150{
151 struct nv50_gpio_priv *priv;
152 int ret;
153
154 ret = nouveau_gpio_create(parent, engine, oclass,
155 nv_device(parent)->chipset > 0x92 ? 32 : 16,
156 &priv);
157 *pobject = nv_object(priv);
158 if (ret)
159 return ret;
160
161 priv->base.reset = nv50_gpio_reset;
162 priv->base.drive = nv50_gpio_drive;
163 priv->base.sense = nv50_gpio_sense;
164 priv->base.events->priv = priv;
165 priv->base.events->enable = nv50_gpio_intr_enable;
166 priv->base.events->disable = nv50_gpio_intr_disable;
167 nv_subdev(priv)->intr = nv50_gpio_intr;
168 return 0;
169}
170
171void
172nv50_gpio_dtor(struct nouveau_object *object)
173{
174 struct nv50_gpio_priv *priv = (void *)object;
175 nouveau_gpio_destroy(&priv->base);
176}
177
178int
179nv50_gpio_init(struct nouveau_object *object)
180{
181 struct nv50_gpio_priv *priv = (void *)object;
182 int ret;
183
184 ret = nouveau_gpio_init(&priv->base);
185 if (ret)
186 return ret;
187
188 /* disable, and ack any pending gpio interrupts */
189 nv_wr32(priv, 0xe050, 0x00000000);
190 nv_wr32(priv, 0xe054, 0xffffffff);
191 if (nv_device(priv)->chipset > 0x92) {
192 nv_wr32(priv, 0xe070, 0x00000000);
193 nv_wr32(priv, 0xe074, 0xffffffff);
194 }
195
196 return 0;
197}
198
199int
200nv50_gpio_fini(struct nouveau_object *object, bool suspend)
201{
202 struct nv50_gpio_priv *priv = (void *)object;
203 nv_wr32(priv, 0xe050, 0x00000000);
204 if (nv_device(priv)->chipset > 0x92)
205 nv_wr32(priv, 0xe070, 0x00000000);
206 return nouveau_gpio_fini(&priv->base, suspend);
207}
208
209struct nouveau_oclass
210nv50_gpio_oclass = {
211 .handle = NV_SUBDEV(GPIO, 0x50),
212 .ofuncs = &(struct nouveau_ofuncs) {
213 .ctor = nv50_gpio_ctor,
214 .dtor = nv50_gpio_dtor,
215 .init = nv50_gpio_init,
216 .fini = nv50_gpio_fini,
217 },
218};
219