1 /* $NetBSD: toaster.c,v 1.9 2009/05/12 08:44:20 cegger Exp $ */
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: toaster.c,v 1.9 2009/05/12 08:44:20 cegger Exp $");
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/sysctl.h>
41 #include <sys/types.h>
42 #include <sys/kernel.h>
43 #include <sys/device.h>
44 #include <sys/callout.h>
45 #include <sys/select.h>
48 #include <machine/autoconf.h>
50 #include <dev/isa/tsdiovar.h>
51 #include <dev/isa/tsdioreg.h>
53 struct toaster_softc
{
55 bus_space_tag_t sc_iot
;
56 bus_space_handle_t sc_gpioh
;
59 u_int32_t led_width
[4];
60 u_int32_t led_duty
[4];
61 u_int32_t led_width_sysctl
[4];
62 u_int32_t led_duty_sysctl
[4];
63 callout_t led_callout
[4];
66 static int toaster_match(device_t
, cfdata_t
, void *);
67 static void toaster_attach(device_t
, device_t
, void *);
69 extern struct cfdriver toaster_cd
;
71 CFATTACH_DECL(toaster
, sizeof(struct toaster_softc
),
72 toaster_match
, toaster_attach
, NULL
, NULL
);
74 static struct toaster_softc
*toaster_sc
= NULL
;
77 toaster_match(device_t parent
, cfdata_t match
, void *aux
)
79 /* No more than one toaster per system */
80 if (toaster_sc
== NULL
)
86 #define TSDIO_GET(x) bus_space_read_1(sc->sc_iot, sc->sc_gpioh, \
89 #define TSDIO_SET(x, y) bus_space_write_1(sc->sc_iot, sc->sc_gpioh, \
92 #define TSDIO_SETBITS(x, y) bus_space_write_1(sc->sc_iot, sc->sc_gpioh, \
93 (TSDIO_ ## x), TSDIO_GET(x) | (y))
95 #define TSDIO_CLEARBITS(x, y) bus_space_write_1(sc->sc_iot, sc->sc_gpioh, \
96 (TSDIO_ ## x), TSDIO_GET(x) & (~(y)))
98 #define LEDCALLOUT_DECL(x) static void led ## x ## _on(void *); \
99 static void led ## x ## _off(void *); \
101 led ## x ## _on(arg) \
104 struct toaster_softc *sc = arg; \
106 if (sc->led_duty[(x)]) { \
107 TSDIO_CLEARBITS(PBDR, (1 << (4 + (x)))); \
108 callout_reset(&sc->led_callout[(x)], \
109 sc->led_duty[(x)], led ## x ## _off, arg); \
111 TSDIO_SETBITS(PBDR, (1 << (4 + (x)))); \
116 led ## x ## _off(arg) \
119 struct toaster_softc *sc = arg; \
120 int offtime = sc->led_width[(x)] - sc->led_duty[(x)]; \
123 TSDIO_SETBITS(PBDR, (1 << (4 + (x)))); \
124 callout_reset(&sc->led_callout[(x)], offtime, \
125 led ## x ## _on, arg); \
135 led_sysctl(SYSCTLFN_ARGS
)
138 struct sysctlnode node
;
139 struct toaster_softc
*sc
= toaster_sc
;
142 t
= *(int*)rnode
->sysctl_data
;
143 node
.sysctl_data
= &t
;
144 error
= sysctl_lookup(SYSCTLFN_CALL(&node
));
145 if (error
|| newp
== NULL
)
148 if (t
< 0) return EINVAL
;
150 *(int*)rnode
->sysctl_data
= t
;
152 if (node
.sysctl_num
== sc
->led_width_sysctl
[0] ||
153 node
.sysctl_num
== sc
->led_duty_sysctl
[0])
155 if (node
.sysctl_num
== sc
->led_width_sysctl
[1] ||
156 node
.sysctl_num
== sc
->led_duty_sysctl
[1])
158 if (node
.sysctl_num
== sc
->led_width_sysctl
[2] ||
159 node
.sysctl_num
== sc
->led_duty_sysctl
[2])
161 if (node
.sysctl_num
== sc
->led_width_sysctl
[3] ||
162 node
.sysctl_num
== sc
->led_duty_sysctl
[3])
169 latch_sysctl(SYSCTLFN_ARGS
)
172 struct sysctlnode node
;
173 struct toaster_softc
*sc
= toaster_sc
;
176 t
= *(int*)rnode
->sysctl_data
;
177 node
.sysctl_data
= &t
;
178 error
= sysctl_lookup(SYSCTLFN_CALL(&node
));
179 if (error
|| newp
== NULL
)
182 if (t
!= 0 && t
!= 1) return EINVAL
;
184 *(int*)rnode
->sysctl_data
= t
;
187 TSDIO_SETBITS(PADR
, 0x1);
189 TSDIO_CLEARBITS(PADR
, 0x1);
195 burner_sysctl(SYSCTLFN_ARGS
)
198 struct sysctlnode node
;
199 struct toaster_softc
*sc
= toaster_sc
;
202 t
= *(int*)rnode
->sysctl_data
;
203 node
.sysctl_data
= &t
;
204 error
= sysctl_lookup(SYSCTLFN_CALL(&node
));
205 if (error
|| newp
== NULL
)
208 if (t
!= 0 && t
!= 1) return EINVAL
;
210 *(int*)rnode
->sysctl_data
= t
;
213 TSDIO_SETBITS(PADR
, 0x2);
215 TSDIO_CLEARBITS(PADR
, 0x2);
222 toaster_attach(device_t parent
, device_t self
, void *aux
)
224 struct toaster_softc
*sc
= (void *)self
;
225 struct tsdio_attach_args
*taa
= aux
;
226 const struct sysctlnode
*node
, *datnode
;
230 sc
->sc_iot
= taa
->ta_iot
;
231 sc
->sc_gpioh
= taa
->ta_ioh
;
233 TSDIO_SETBITS(DDR
, 0x2); /* Port B as outputs */
234 TSDIO_SETBITS(PBDR
, 0xf0); /* Turn off LED's */
236 aprint_normal(": internal toaster control outputs\n");
237 aprint_normal_dev(&sc
->sc_dev
, "using port B, bits 4-7 for front panel LEDs\n");
238 aprint_normal_dev(&sc
->sc_dev
, "using port A, bit 0 for magnetic latch\n");
239 aprint_normal_dev(&sc
->sc_dev
, "using port A, bit 1 for burner element\n");
241 callout_init(&sc
->led_callout
[0], 0);
242 callout_init(&sc
->led_callout
[1], 0);
243 callout_init(&sc
->led_callout
[2], 0);
244 callout_init(&sc
->led_callout
[3], 0);
245 sc
->led_duty
[0] = sc
->led_width
[0] = 0;
246 sc
->led_duty
[1] = sc
->led_width
[1] = 0;
247 sc
->led_duty
[2] = sc
->led_width
[2] = 0;
248 sc
->led_duty
[3] = sc
->led_width
[3] = 0;
253 if (sysctl_createv(NULL
, 0, NULL
, NULL
,
254 CTLFLAG_PERMANENT
, CTLTYPE_NODE
, "hw",
255 NULL
, NULL
, 0, NULL
, 0,
256 CTL_HW
, CTL_EOL
) != 0) {
257 aprint_error_dev(&sc
->sc_dev
, "could not create sysctl\n");
260 if (sysctl_createv(NULL
, 0, NULL
, &node
,
261 0, CTLTYPE_NODE
, device_xname(&sc
->sc_dev
),
264 CTL_HW
, CTL_CREATE
, CTL_EOL
) != 0) {
265 aprint_error_dev(&sc
->sc_dev
, "could not create sysctl\n");
269 #define LEDSYSCTL_SETUP(x) if ((i = sysctl_createv(NULL, \
271 CTLFLAG_READWRITE|CTLFLAG_ANYWRITE, \
275 "LED duty cycle in HZ tick units"), \
276 led_sysctl, 0, &sc->led_duty[(x)], 0, \
277 CTL_HW, node->sysctl_num, \
278 CTL_CREATE, CTL_EOL)) \
280 aprint_error_dev(&sc->sc_dev, "could not create sysctl\n"); \
283 sc->led_duty_sysctl[(x)] = datnode->sysctl_num; \
285 if ((i = sysctl_createv(NULL, 0, NULL, &datnode, \
286 CTLFLAG_READWRITE|CTLFLAG_ANYWRITE, \
290 "LED cycle width in HZ tick units"), \
291 led_sysctl, 0, &sc->led_width[(x)], 0, \
292 CTL_HW, node->sysctl_num, \
293 CTL_CREATE, CTL_EOL)) \
295 aprint_error_dev(&sc->sc_dev, "could not create sysctl\n"); \
298 sc->led_width_sysctl[(x)] = datnode->sysctl_num;
305 if ((i
= sysctl_createv(NULL
, 0, NULL
, &datnode
,
306 CTLFLAG_READWRITE
|CTLFLAG_ANYWRITE
,
310 "magnetic latch that holds the toast down"),
311 latch_sysctl
, 0, &sc
->latch
, 0,
312 CTL_HW
, node
->sysctl_num
,
313 CTL_CREATE
, CTL_EOL
))
315 aprint_error_dev(&sc
->sc_dev
, "could not create sysctl\n");
319 if ((i
= sysctl_createv(NULL
, 0, NULL
, &datnode
,
320 CTLFLAG_READWRITE
, CTLTYPE_INT
,
323 "800-watt burner element control for toasting"),
324 burner_sysctl
, 0, &sc
->burner
, 0,
325 CTL_HW
, node
->sysctl_num
,
326 CTL_CREATE
, CTL_EOL
))
328 aprint_error_dev(&sc
->sc_dev
, "could not create sysctl\n");