Line 0
Link Here
|
|
|
1 |
/*- |
2 |
* Copyright (c) 2014 Larry Baird |
3 |
* All rights reserved. |
4 |
* |
5 |
* Redistribution and use in source and binary forms, with or without |
6 |
* modification, are permitted provided that the following conditions |
7 |
* are met: |
8 |
* 1. Redistributions of source code must retain the above copyright |
9 |
* notice, this list of conditions and the following disclaimer. |
10 |
* 2. Redistributions in binary form must reproduce the above copyright |
11 |
* notice, this list of conditions and the following disclaimer in the |
12 |
* documentation and/or other materials provided with the distribution. |
13 |
* |
14 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
15 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
17 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
18 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
19 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
20 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
21 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
22 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
23 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
24 |
* SUCH DAMAGE. |
25 |
*/ |
26 |
|
27 |
#include <sys/param.h> |
28 |
#include <sys/conf.h> |
29 |
#include <sys/bus.h> |
30 |
#include <sys/priv.h> |
31 |
#include <sys/types.h> |
32 |
#include <sys/uio.h> |
33 |
#include <sys/proc.h> |
34 |
#include <dev/pci/pcireg.h> |
35 |
#include <dev/pci/pcivar.h> |
36 |
#include <sys/kernel.h> |
37 |
#include <sys/systm.h> |
38 |
#include <sys/module.h> |
39 |
#include <sys/rman.h> |
40 |
#include <x86/bus.h> |
41 |
#include <isa/isavar.h> |
42 |
#include <dev/led/led.h> |
43 |
|
44 |
/* SB7xx RRG 2.3.3.1.1. */ |
45 |
#define AMDSB_PMIO_INDEX 0xcd6 |
46 |
#define AMDSB_PMIO_DATA (PMIO_INDEX + 1) |
47 |
#define AMDSB_PMIO_WIDTH 2 |
48 |
|
49 |
#define AMDSB_SMBUS_DEVID 0x43851002 |
50 |
|
51 |
/* SB8xx RRG 2.3.3. */ |
52 |
#define AMDSB8_PM_WDT_EN 0x24 |
53 |
|
54 |
/* Here are some magic numbers from APU BIOS. */ |
55 |
#define GPIO_OFFSET 0x100 |
56 |
#define GPIO_187 187 // MODESW |
57 |
#define GPIO_188 188 // Unknown ?? |
58 |
#define GPIO_189 189 // LED1# |
59 |
#define GPIO_190 190 // LED2# |
60 |
#define GPIO_191 191 // LED3# |
61 |
|
62 |
#define LED_ON 0x08 |
63 |
#define LED_OFF 0xC8 |
64 |
|
65 |
struct apuled { |
66 |
struct resource *res; |
67 |
bus_size_t offset; |
68 |
struct cdev *led; |
69 |
}; |
70 |
|
71 |
struct apuled_softc { |
72 |
int sc_rid; |
73 |
int sc_type; |
74 |
struct resource *sc_res; |
75 |
struct apuled sc_led[3]; |
76 |
struct cdev *sc_sw; |
77 |
}; |
78 |
|
79 |
/* |
80 |
* Mode switch methods. |
81 |
*/ |
82 |
static int modesw_open(struct cdev *dev, int flags, int fmt, |
83 |
struct thread *td); |
84 |
static int modesw_close(struct cdev *dev, int flags, int fmt, |
85 |
struct thread *td); |
86 |
static int modesw_read(struct cdev *dev, struct uio *uio, int ioflag); |
87 |
|
88 |
static struct cdevsw modesw_cdev = { |
89 |
.d_version = D_VERSION, |
90 |
.d_open = modesw_open, |
91 |
.d_read = modesw_read, |
92 |
.d_close = modesw_close, |
93 |
.d_name = "modesw", |
94 |
}; |
95 |
|
96 |
/* |
97 |
* Device methods. |
98 |
*/ |
99 |
static int apuled_probe(device_t dev); |
100 |
static int apuled_attach(device_t dev); |
101 |
static int apuled_detach(device_t dev); |
102 |
static void apuled_identify(driver_t *driver, device_t parent); |
103 |
|
104 |
static device_method_t apuled_methods[] = { |
105 |
/* Device interface */ |
106 |
DEVMETHOD(device_probe, apuled_probe), |
107 |
DEVMETHOD(device_attach, apuled_attach), |
108 |
DEVMETHOD(device_detach, apuled_detach), |
109 |
DEVMETHOD(device_identify, apuled_identify), |
110 |
|
111 |
DEVMETHOD_END |
112 |
}; |
113 |
|
114 |
static driver_t apuled_driver = { |
115 |
"apuled", |
116 |
apuled_methods, |
117 |
sizeof(struct apuled_softc), |
118 |
}; |
119 |
|
120 |
static devclass_t apuled_devclass; |
121 |
DRIVER_MODULE(apuled, isa, apuled_driver, apuled_devclass, NULL, NULL); |
122 |
|
123 |
static int |
124 |
hw_is_apu( void ) |
125 |
{ |
126 |
int apu = 0; |
127 |
char *maker; |
128 |
char *product; |
129 |
|
130 |
maker = kern_getenv("smbios.system.maker"); |
131 |
if (maker != NULL) { |
132 |
if ( 0 == strcmp( "PC Engines", maker ) ) { |
133 |
product = kern_getenv("smbios.system.product"); |
134 |
if (product != NULL) { |
135 |
if ( 0 == strcmp( "APU", product ) ) |
136 |
apu = 1; |
137 |
|
138 |
freeenv(product); |
139 |
} |
140 |
} |
141 |
|
142 |
freeenv(maker); |
143 |
} |
144 |
|
145 |
return (apu); |
146 |
} |
147 |
|
148 |
static void |
149 |
apu_led_callback(void *ptr, int onoff) |
150 |
{ |
151 |
struct apuled *led = (struct apuled *)ptr; |
152 |
u_int8_t value; |
153 |
|
154 |
value = bus_read_1(led->res, led->offset); |
155 |
|
156 |
if ( onoff ) |
157 |
value = LED_ON; |
158 |
else |
159 |
value = LED_OFF; |
160 |
|
161 |
bus_write_1(led->res, led->offset, value); |
162 |
} |
163 |
|
164 |
static void |
165 |
apuled_identify(driver_t *driver, device_t parent) |
166 |
{ |
167 |
device_t child; |
168 |
device_t smb; |
169 |
|
170 |
if (resource_disabled("apuled", 0)) |
171 |
return; |
172 |
|
173 |
if (device_find_child(parent, "apuled", -1) != NULL) |
174 |
return; |
175 |
|
176 |
/* Do was have expected south bridge? */ |
177 |
smb = pci_find_bsf(0, 20, 0); |
178 |
if (smb == NULL) |
179 |
return; |
180 |
|
181 |
if (pci_get_devid(smb) != AMDSB_SMBUS_DEVID) |
182 |
return; |
183 |
|
184 |
if ( hw_is_apu() ) { |
185 |
child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "apuled", -1); |
186 |
if ( child == NULL ) |
187 |
device_printf(parent, "apuled: bus add child failed\n"); |
188 |
} |
189 |
} |
190 |
|
191 |
static int |
192 |
apuled_probe(device_t dev) |
193 |
{ |
194 |
struct resource *res; |
195 |
int rc; |
196 |
uint32_t gpio_mmio_base; |
197 |
int i; |
198 |
int rid; |
199 |
|
200 |
/* Make sure we do not claim some ISA PNP device. */ |
201 |
if (isa_get_logicalid(dev) != 0) |
202 |
return (ENXIO); |
203 |
|
204 |
if ( ! hw_is_apu() ) |
205 |
return (ENXIO); |
206 |
|
207 |
/* Find the ACPImmioAddr base address */ |
208 |
rc = bus_set_resource(dev, SYS_RES_IOPORT, 0, AMDSB_PMIO_INDEX, |
209 |
AMDSB_PMIO_WIDTH); |
210 |
if (rc != 0) { |
211 |
device_printf(dev, "bus_set_resource for find address failed\n"); |
212 |
return (ENXIO); |
213 |
} |
214 |
|
215 |
rid = 0; |
216 |
res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0ul, ~0ul, |
217 |
AMDSB_PMIO_WIDTH, RF_ACTIVE | RF_SHAREABLE); |
218 |
if (res == NULL) { |
219 |
device_printf(dev, "bus_alloc_resource for finding base address failed.\n"); |
220 |
return (ENXIO); |
221 |
} |
222 |
|
223 |
/* Find base address of memory mapped WDT registers. */ |
224 |
for (gpio_mmio_base = 0, i = 0; i < 4; i++) { |
225 |
gpio_mmio_base <<= 8; |
226 |
bus_write_1(res, 0, AMDSB8_PM_WDT_EN + 3 - i); |
227 |
gpio_mmio_base |= bus_read_1(res, 1); |
228 |
} |
229 |
gpio_mmio_base &= 0xFFFFF000; |
230 |
|
231 |
if ( bootverbose ) |
232 |
device_printf(dev, "MMIO base adddress is 0x%x\n", gpio_mmio_base); |
233 |
|
234 |
bus_release_resource(dev, SYS_RES_IOPORT, rid, res); |
235 |
bus_delete_resource(dev, SYS_RES_IOPORT, rid); |
236 |
|
237 |
rc = bus_set_resource(dev, SYS_RES_MEMORY, 0, |
238 |
gpio_mmio_base + GPIO_OFFSET + GPIO_187, GPIO_191 - GPIO_187); |
239 |
if (rc != 0) { |
240 |
device_printf(dev, "bus_set_resource for memory region failed\n"); |
241 |
return (ENXIO); |
242 |
} |
243 |
|
244 |
return (0); |
245 |
} |
246 |
|
247 |
|
248 |
static int |
249 |
apuled_attach(device_t dev) |
250 |
{ |
251 |
struct apuled_softc *sc = device_get_softc(dev); |
252 |
int i; |
253 |
|
254 |
sc->sc_rid = 0; |
255 |
sc->sc_type = SYS_RES_MEMORY; |
256 |
|
257 |
sc->sc_res = bus_alloc_resource_any( dev, sc->sc_type, &sc->sc_rid, |
258 |
RF_ACTIVE | RF_SHAREABLE); |
259 |
|
260 |
if ( sc->sc_res == NULL ) { |
261 |
device_printf( dev, "Unable to allocate bus resource\n" ); |
262 |
return (ENXIO); |
263 |
} |
264 |
|
265 |
sc->sc_sw = make_dev(&modesw_cdev, 0, UID_ROOT, GID_WHEEL, 0440, "modesw"); |
266 |
sc->sc_sw->si_drv1 = sc; |
267 |
|
268 |
for ( i = 0; i < 3; i++ ) { |
269 |
char name[30]; |
270 |
|
271 |
snprintf( name, sizeof(name), "led%d", i + 1 ); |
272 |
|
273 |
sc->sc_led[ i ].res = sc->sc_res; |
274 |
sc->sc_led[ i ].offset = GPIO_189 - GPIO_187 + i; |
275 |
|
276 |
sc->sc_led[ i ].led = led_create(apu_led_callback, |
277 |
&sc->sc_led[ i ], name); |
278 |
|
279 |
if ( sc->sc_led[ i ].led == NULL ) { |
280 |
device_printf( dev, "%s creation failed\n", name ); |
281 |
|
282 |
/* Make sure power LED stays on by default */ |
283 |
} else if ( i == 0 ) { |
284 |
apu_led_callback(&sc->sc_led[ i ], TRUE); |
285 |
} |
286 |
} |
287 |
|
288 |
device_printf( dev, "created\n" ); |
289 |
|
290 |
return (0); |
291 |
} |
292 |
|
293 |
int |
294 |
apuled_detach(device_t dev) |
295 |
{ |
296 |
struct apuled_softc *sc = device_get_softc(dev); |
297 |
int i; |
298 |
|
299 |
for ( i = 0; i < 3; i++ ) |
300 |
if ( sc->sc_led[ i ].led != NULL ) { |
301 |
if ( i == 0 ) |
302 |
apu_led_callback(&sc->sc_led[ i ], TRUE); |
303 |
else |
304 |
apu_led_callback(&sc->sc_led[ i ], FALSE); |
305 |
|
306 |
led_destroy(sc->sc_led[ i ].led); |
307 |
} |
308 |
|
309 |
if ( sc->sc_res != NULL ) { |
310 |
bus_release_resource(dev, sc->sc_type, sc->sc_rid, sc->sc_res); |
311 |
bus_delete_resource(dev, sc->sc_type, sc->sc_rid ); |
312 |
} |
313 |
|
314 |
if ( sc->sc_sw != NULL ) |
315 |
destroy_dev(sc->sc_sw); |
316 |
|
317 |
return (0); |
318 |
} |
319 |
|
320 |
static int |
321 |
modesw_open(struct cdev *dev __unused, int flags __unused, int fmt __unused, |
322 |
struct thread *td) |
323 |
{ |
324 |
int error; |
325 |
|
326 |
error = priv_check(td, PRIV_IO); |
327 |
if (error != 0) |
328 |
return (error); |
329 |
error = securelevel_gt(td->td_ucred, 0); |
330 |
if (error != 0) |
331 |
return (error); |
332 |
|
333 |
return (error); |
334 |
} |
335 |
|
336 |
static int |
337 |
modesw_read(struct cdev *dev, struct uio *uio, int ioflag) { |
338 |
struct apuled_softc *sc = dev->si_drv1; |
339 |
uint8_t value; |
340 |
char ch; |
341 |
int error; |
342 |
|
343 |
/* Is mode switch pressed? */ |
344 |
value = bus_read_1(sc->sc_res, GPIO_187 - GPIO_187 ); |
345 |
if (value == 0x28 ) |
346 |
ch = '1'; |
347 |
else |
348 |
ch = '0'; |
349 |
|
350 |
error = uiomove(&ch, sizeof(ch), uio); |
351 |
|
352 |
return (error); |
353 |
} |
354 |
|
355 |
static int |
356 |
modesw_close(struct cdev *dev __unused, int flags __unused, int fmt __unused, |
357 |
struct thread *td __unused) |
358 |
{ |
359 |
return (0); |
360 |
} |