xref: /netbsd/sys/arch/evbppc/obs405/dev/obsled.c (revision 05fd0bf3)
1*05fd0bf3Spooka /*	$NetBSD: obsled.c,v 1.10 2014/02/25 18:30:08 pooka Exp $	*/
23ad343e5Sshige 
33ad343e5Sshige /*
43ad343e5Sshige  * Copyright (c) 2004 Shigeyuki Fukushima.
53ad343e5Sshige  * All rights reserved.
63ad343e5Sshige  *
73ad343e5Sshige  * Redistribution and use in source and binary forms, with or without
83ad343e5Sshige  * modification, are permitted provided that the following conditions
93ad343e5Sshige  * are met:
103ad343e5Sshige  * 1. Redistributions of source code must retain the above copyright
113ad343e5Sshige  *    notice, this list of conditions and the following disclaimer.
123ad343e5Sshige  * 2. Redistributions in binary form must reproduce the above
133ad343e5Sshige  *    copyright notice, this list of conditions and the following
143ad343e5Sshige  *    disclaimer in the documentation and/or other materials provided
153ad343e5Sshige  *    with the distribution.
163ad343e5Sshige  * 3. The name of the author may not be used to endorse or promote
173ad343e5Sshige  *    products derived from this software without specific prior
183ad343e5Sshige  *    written permission.
193ad343e5Sshige  *
203ad343e5Sshige  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
213ad343e5Sshige  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
223ad343e5Sshige  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
233ad343e5Sshige  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
243ad343e5Sshige  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
253ad343e5Sshige  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
263ad343e5Sshige  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
273ad343e5Sshige  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
283ad343e5Sshige  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
293ad343e5Sshige  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
303ad343e5Sshige  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
313ad343e5Sshige  */
323ad343e5Sshige 
333ad343e5Sshige #include <sys/cdefs.h>
34*05fd0bf3Spooka __KERNEL_RCSID(0, "$NetBSD: obsled.c,v 1.10 2014/02/25 18:30:08 pooka Exp $");
353ad343e5Sshige 
363ad343e5Sshige #include <sys/param.h>
373ad343e5Sshige #include <sys/device.h>
383ad343e5Sshige #include <sys/queue.h>
39044ec00fSshige #include <sys/sysctl.h>
403ad343e5Sshige #include <sys/systm.h>
413ad343e5Sshige 
42b55107dcSshige #include <machine/obs266.h>
433ad343e5Sshige 
443ad343e5Sshige #include <powerpc/ibm4xx/dev/gpiovar.h>
453ad343e5Sshige 
463ad343e5Sshige struct obsled_softc {
47534c8a68Smatt         device_t sc_dev;
483ad343e5Sshige 	gpio_tag_t sc_tag;
493ad343e5Sshige         int sc_addr;
50044ec00fSshige 	int sc_led_state;	/* LED status (ON=1/OFF=0) */
51044ec00fSshige 	int sc_led_state_mib;
523ad343e5Sshige };
533ad343e5Sshige 
54534c8a68Smatt static void	obsled_attach(device_t, device_t, void *);
55534c8a68Smatt static int	obsled_match(device_t, cfdata_t, void *);
56044ec00fSshige static int	obsled_sysctl_verify(SYSCTLFN_PROTO);
57044ec00fSshige static void	obsled_set_state(struct obsled_softc *);
583ad343e5Sshige 
59534c8a68Smatt CFATTACH_DECL_NEW(obsled, sizeof(struct obsled_softc),
603ad343e5Sshige 	obsled_match, obsled_attach, NULL, NULL);
613ad343e5Sshige 
623ad343e5Sshige static int
obsled_match(device_t parent,cfdata_t cf,void * aux)63534c8a68Smatt obsled_match(device_t parent, cfdata_t cf, void *aux)
643ad343e5Sshige {
653ad343e5Sshige         struct gpio_attach_args *ga = aux;
663ad343e5Sshige 
673ad343e5Sshige 	/* XXX: support only OpenBlockS266 LED */
68b55107dcSshige         if (ga->ga_addr == OBS266_GPIO_LED1)
693ad343e5Sshige                 return (1);
70b55107dcSshige         else if (ga->ga_addr == OBS266_GPIO_LED2)
713ad343e5Sshige                 return (1);
72b55107dcSshige         else if (ga->ga_addr == OBS266_GPIO_LED4)
733ad343e5Sshige                 return (1);
743ad343e5Sshige 
753ad343e5Sshige         return (0);
763ad343e5Sshige }
773ad343e5Sshige 
783ad343e5Sshige static void
obsled_attach(device_t parent,device_t self,void * aux)79534c8a68Smatt obsled_attach(device_t parent, device_t self, void *aux)
803ad343e5Sshige {
81534c8a68Smatt         struct obsled_softc *sc = device_private(self);
823ad343e5Sshige         struct gpio_attach_args *ga = aux;
83044ec00fSshige 	struct sysctlnode *node;
84044ec00fSshige 	int err, node_mib;
85044ec00fSshige 	char led_name[5];
86534c8a68Smatt 	/* int led = (1 << device_unit(sc->sc_dev)); */
873ad343e5Sshige 
88044ec00fSshige 	snprintf(led_name, sizeof(led_name),
89534c8a68Smatt 		"led%d", (1 << device_unit(sc->sc_dev)) & 0x7);
90044ec00fSshige         aprint_naive(": OpenBlockS %s\n", led_name);
91044ec00fSshige         aprint_normal(": OpenBlockS %s\n", led_name);
923ad343e5Sshige 
93534c8a68Smatt 	sc->sc_dev = self;
943ad343e5Sshige         sc->sc_tag = ga->ga_tag;
953ad343e5Sshige         sc->sc_addr = ga->ga_addr;
96044ec00fSshige 	sc->sc_led_state = 0;
973ad343e5Sshige 
98b55107dcSshige 	obs266_led_set(OBS266_LED_OFF);
99044ec00fSshige 
100044ec00fSshige 	/* add sysctl interface */
101044ec00fSshige 	err = sysctl_createv(NULL, 0,
102044ec00fSshige 			NULL, (const struct sysctlnode **)&node,
103044ec00fSshige 			0, CTLTYPE_NODE,
104044ec00fSshige 			"obsled",
105044ec00fSshige 			NULL,
106044ec00fSshige 			NULL, 0, NULL, 0,
107044ec00fSshige 			CTL_HW, CTL_CREATE, CTL_EOL);
108044ec00fSshige 	if (err  != 0)
109044ec00fSshige 		return;
110044ec00fSshige 	node_mib = node->sysctl_num;
111044ec00fSshige 	err = sysctl_createv(NULL, 0,
112044ec00fSshige 			NULL, (const struct sysctlnode **)&node,
113044ec00fSshige 			CTLFLAG_READWRITE, CTLTYPE_INT,
114044ec00fSshige 			led_name,
115044ec00fSshige 			SYSCTL_DESCR("OpenBlockS LED state (0=off, 1=on)"),
1167c283e77Sdsl 			obsled_sysctl_verify, 0, (void *)sc, 0,
117044ec00fSshige 			CTL_HW, node_mib, CTL_CREATE, CTL_EOL);
118044ec00fSshige 	if (err  != 0)
119044ec00fSshige 		return;
120044ec00fSshige 
121044ec00fSshige 	sc->sc_led_state_mib = node->sysctl_num;
1223ad343e5Sshige #if 0
1233ad343e5Sshige 	{
1243ad343e5Sshige 		gpio_tag_t tag = sc->sc_tag;
1253ad343e5Sshige 	 	(*(tag)->io_or_write)((tag)->cookie, sc->sc_addr, 0);
1263ad343e5Sshige 	}
1273ad343e5Sshige #endif
1283ad343e5Sshige }
1293ad343e5Sshige 
130044ec00fSshige static int
obsled_sysctl_verify(SYSCTLFN_ARGS)131044ec00fSshige obsled_sysctl_verify(SYSCTLFN_ARGS)
132044ec00fSshige {
133044ec00fSshige 	struct obsled_softc *sc = rnode->sysctl_data;
134044ec00fSshige 	struct sysctlnode node;
135044ec00fSshige 	int err, state;
136044ec00fSshige 
137044ec00fSshige 	node = *rnode;
138044ec00fSshige 	state = sc->sc_led_state;
139044ec00fSshige 	node.sysctl_data = &state;
140044ec00fSshige 
141044ec00fSshige 	err = sysctl_lookup(SYSCTLFN_CALL(&node));
142044ec00fSshige 	if (err != 0 || newp == NULL)
143044ec00fSshige 		return err;
144044ec00fSshige 
145044ec00fSshige 	if (node.sysctl_num == sc->sc_led_state_mib) {
146044ec00fSshige 		if (state < 0 || state > 1)
147044ec00fSshige 			return EINVAL;
148044ec00fSshige 		sc->sc_led_state = state;
149044ec00fSshige 		obsled_set_state(sc);
150044ec00fSshige 	}
151044ec00fSshige 
152044ec00fSshige 	return 0;
153044ec00fSshige }
154044ec00fSshige 
155044ec00fSshige static void
obsled_set_state(struct obsled_softc * sc)156044ec00fSshige obsled_set_state(struct obsled_softc *sc)
157044ec00fSshige {
158044ec00fSshige 	gpio_tag_t tag = sc->sc_tag;
159044ec00fSshige 
160044ec00fSshige 	(*(tag)->io_or_write)((tag)->cookie,
161044ec00fSshige 				sc->sc_addr,
162044ec00fSshige 				(~(sc->sc_led_state) & 1));
163044ec00fSshige 	/* GPIO LED input ON=0/OFF=1 */
164044ec00fSshige }
165044ec00fSshige 
166044ec00fSshige /*
167044ec00fSshige  * Setting LED interface for inside kernel.
168044ec00fSshige  * Argumnt `led' is 3-bit LED state (led=0-7/ON=1/OFF=0).
169044ec00fSshige  */
1703ad343e5Sshige void
obs266_led_set(int led)171b55107dcSshige obs266_led_set(int led)
1723ad343e5Sshige {
173b504b9ffSdyoung 	device_t dv;
174b504b9ffSdyoung 	deviter_t di;
1753ad343e5Sshige 
176044ec00fSshige 	/*
177044ec00fSshige 	 * Sarching "obsled" devices from device tree.
178044ec00fSshige 	 * Do you have something better idea?
179044ec00fSshige 	 */
180b504b9ffSdyoung         for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST); dv != NULL;
181b504b9ffSdyoung 	     dv = deviter_next(&di)) {
182b504b9ffSdyoung 		if (device_is_a(dv, "obsles")) {
183b504b9ffSdyoung 			struct obsled_softc *sc = device_private(dv);
184044ec00fSshige 			sc->sc_led_state =
185b504b9ffSdyoung 			    (led & (1 << device_unit(dv))) >> device_unit(dv);
186044ec00fSshige 			obsled_set_state(sc);
1873ad343e5Sshige 		}
1883ad343e5Sshige 	}
189b504b9ffSdyoung 	deviter_release(&di);
1903ad343e5Sshige }
191