1 /* $Id: mpcsa_leds.c,v 1.2 2008/07/03 01:15:39 matt Exp $ */
5 * Copyright (c) 2007 Embedtronics Oy. All rights reserved.
7 * Based on arch/arm/ep93xx/epgpio.c,
8 * Copyright (c) 2005 HAMAJIMA Katsuomi. All rights reserved.
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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD$");
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/device.h>
40 #include <dev/spi/spivar.h>
41 #include <dev/gpio/gpiovar.h>
42 #include <evbarm/mpcsa/mpcsa_leds_var.h>
43 #include <evbarm/mpcsa/mpcsa_io.h>
50 #ifdef MPCSA_LEDS_DEBUG
51 int mpcsa_leds_debug
= MPCSA_LEDS_DEBUG
;
52 #define DPRINTFN(n,x) if (mpcsa_leds_debug>(n)) printf x;
57 #define MPCSA_LEDS_NPINS 16
58 #define LEDS_UPDATE_INTERVAL 100 /* update interval in milliseconds */
61 LMODE_NORMAL
, /* normal mode */
62 LMODE_COMM
, /* communication mode */
63 LMODE_BLINK
/* blink mode */
66 typedef struct led_state
{
67 int l_mode
; /* current command */
70 int l_conn_cnt
; /* connection count */
71 int l_comm_cnt
; /* comm/pkt count */
74 int l_blink_int
; /* blink interval */
75 int l_blink_cnt
; /* blink counter */
80 struct mpcsa_leds_softc
{
82 struct spi_handle
*sc_sh
;
85 struct gpio_chipset_tag sc_gpio_chipset
;
86 gpio_pin_t sc_pins
[MPCSA_LEDS_NPINS
];
90 led_state_t sc_leds
[MPCSA_LEDS_NPINS
];
93 struct spi_chunk sc_spi_chunk
;
94 struct spi_transfer sc_spi_transfer
;
97 static int mpcsa_leds_match(device_t
, cfdata_t
, void *);
98 static void mpcsa_leds_attach(device_t
, device_t
, void *);
99 static void mpcsa_leds_timer(void *aux
);
102 static int mpcsa_ledsbus_print(void *, const char *);
103 static int mpcsa_leds_pin_read(void *, int);
104 static void mpcsa_leds_pin_write(void *, int, int);
105 static void mpcsa_leds_pin_ctl(void *, int, int);
109 static int mpcsa_leds_search(device_t
, cfdata_t
, const int *, void *);
110 static int mpcsa_leds_print(void *, const char *);
113 CFATTACH_DECL(mpcsa_leds
, sizeof(struct mpcsa_leds_softc
),
114 mpcsa_leds_match
, mpcsa_leds_attach
, NULL
, NULL
);
116 static struct mpcsa_leds_softc
*mpcsa_leds_sc
;
119 mpcsa_leds_match(device_t parent
, cfdata_t match
, void *aux
)
121 struct spi_attach_args
*sa
= aux
;
123 if (strcmp(match
->cf_name
, "mpcsa_leds"))
126 if (spi_configure(sa
->sa_handle
, SPI_MODE_0
, 10000000))
133 mpcsa_leds_attach(device_t parent
, device_t self
, void *aux
)
135 struct mpcsa_leds_softc
*sc
= device_private(self
);
136 struct spi_attach_args
*sa
= aux
;
138 struct gpiobus_attach_args gba
;
142 aprint_naive(": output buffer\n");
143 aprint_normal(": 74HC595 or compatible shift register(s)\n");
145 sc
->sc_sh
= sa
->sa_handle
;
146 sc
->sc_pinstate
= 0xffff;
147 callout_init(&sc
->sc_c
, 0);
150 /* initialize and attach gpio(4) */
151 for (n
= 0; n
< MPCSA_LEDS_NPINS
; n
++) {
152 sc
->sc_pins
[n
].pin_num
= n
;
153 sc
->sc_pins
[n
].pin_caps
= (GPIO_PIN_OUTPUT
154 | GPIO_PIN_PUSHPULL
);
155 sc
->sc_pins
[n
].pin_flags
= GPIO_PIN_OUTPUT
| GPIO_PIN_LOW
;
158 sc
->sc_gpio_chipset
.gp_cookie
= sc
;
159 sc
->sc_gpio_chipset
.gp_pin_read
= mpcsa_leds_pin_read
;
160 sc
->sc_gpio_chipset
.gp_pin_write
= mpcsa_leds_pin_write
;
161 sc
->sc_gpio_chipset
.gp_pin_ctl
= mpcsa_leds_pin_ctl
;
162 gba
.gba_gc
= &sc
->sc_gpio_chipset
;
163 gba
.gba_pins
= sc
->sc_pins
;
164 gba
.gba_npins
= MPCSA_LEDS_NPINS
;
165 config_found_ia(self
, "gpiobus", &gba
, mpcsa_ledsbus_print
);
169 // config_search_ia(mpcsa_leds_search, self, "mpcsa_leds", mpcsa_leds_print);
171 /* update leds ten times a second or so */
172 mpcsa_leds_sc
= sc
; // @@@@
173 sc
->sc_spi_transfer
.st_flags
= SPI_F_DONE
;
174 callout_reset(&sc
->sc_c
, mstohz(LEDS_UPDATE_INTERVAL
), mpcsa_leds_timer
, sc
);
176 mpcsa_blink_led(LED_HB
, 500);
184 mpcsa_ledsbus_print(void *aux
, const char *name
)
186 gpiobus_print(aux
, name
);
192 mpcsa_leds_pin_read(void *arg
, int pin
)
194 struct mpcsa_leds_softc
*sc
= arg
;
195 pin
%= MPCSA_LEDS_NPINS
;
196 return (sc
->sc_pinstate
& htobe16(1U << pin
)) ? 1 : 0;
200 mpcsa_leds_pin_write(void *arg
, int pin
, int val
)
202 struct mpcsa_leds_softc
*sc
= arg
;
205 pin
%= MPCSA_LEDS_NPINS
;
206 if (!sc
->sc_pins
[pin
].pin_caps
)
211 sc
->sc_pinstate
|= htobe16(1U << pin
);
213 sc
->sc_pinstate
&= htobe16(~(1U << pin
));
216 if (spi_send(sc
->sc_sh
, 2, (const void *)&sc
->sc_pinstate
) != 0) {
223 mpcsa_leds_pin_ctl(void *arg
, int pin
, int flags
)
225 struct mpcsa_leds_softc
*sc
= arg
;
227 pin
%= MPCSA_LEDS_NPINS
;
228 if (!sc
->sc_pins
[pin
].pin_caps
)
231 // hmm, I think there's nothing to do
235 static void mpcsa_leds_timer(void *aux
)
238 struct mpcsa_leds_softc
*sc
= aux
;
241 callout_schedule(&sc
->sc_c
, mstohz(LEDS_UPDATE_INTERVAL
));
244 if (!(sc
->sc_spi_transfer
.st_flags
& SPI_F_DONE
)) {
250 pins
= be16toh(sc
->sc_pinstate
);
252 for (n
= 0; n
< MPCSA_LEDS_NPINS
; n
++) {
253 switch (sc
->sc_leds
[n
].l_mode
) {
258 if (sc
->sc_leds
[n
].l_comm_cnt
> 0) {
259 if (sc
->sc_leds
[n
].l_comm_cnt
< INFINITE_BLINK
)
260 sc
->sc_leds
[n
].l_comm_cnt
--;
262 sc
->sc_leds
[n
].l_comm_cnt
^= 1;
264 if ((sc
->sc_leds
[n
].l_conn_cnt
> 0) ^ (sc
->sc_leds
[n
].l_comm_cnt
& 1))
271 if (--sc
->sc_leds
[n
].l_blink_cnt
<= 0) {
273 sc
->sc_leds
[n
].l_blink_cnt
= sc
->sc_leds
[n
].l_blink_int
;
280 sc
->sc_pinstate
= pins
;
283 spi_transfer_init(&sc
->sc_spi_transfer
);
284 spi_chunk_init(&sc
->sc_spi_chunk
, 2, (const void *)&sc
->sc_pinstate
, NULL
);
285 spi_transfer_add(&sc
->sc_spi_transfer
, &sc
->sc_spi_chunk
);
286 if (spi_transfer(sc
->sc_sh
, &sc
->sc_spi_transfer
) != 0) {
287 /* an error occurred! */
291 void mpcsa_comm_led(int num
, int count
)
293 struct mpcsa_leds_softc
*sc
= mpcsa_leds_sc
;
294 if (!sc
|| num
< 1 || num
> MPCSA_LEDS_NPINS
) {
300 if (sc
->sc_leds
[num
].l_mode
!= LMODE_COMM
) {
301 sc
->sc_leds
[num
].l_mode
= LMODE_COMM
;
302 sc
->sc_leds
[num
].l_conn_cnt
= 0;
303 sc
->sc_leds
[num
].l_comm_cnt
= 0;
305 if (sc
->sc_leds
[num
].l_comm_cnt
< (count
* 2 - 1))
306 sc
->sc_leds
[num
].l_comm_cnt
+= count
;
307 else if ((count
* 2) < sc
->sc_leds
[num
].l_comm_cnt
)
308 sc
->sc_leds
[num
].l_comm_cnt
= count
* 2 - 2 - (sc
->sc_leds
[num
].l_comm_cnt
% 2);
312 void mpcsa_conn_led(int num
, int ok
)
314 struct mpcsa_leds_softc
*sc
= mpcsa_leds_sc
;
315 if (!sc
|| num
< 1 || num
> MPCSA_LEDS_NPINS
)
319 if (sc
->sc_leds
[num
].l_mode
!= LMODE_COMM
) {
320 sc
->sc_leds
[num
].l_mode
= LMODE_COMM
;
321 sc
->sc_leds
[num
].l_conn_cnt
= 0;
322 sc
->sc_leds
[num
].l_comm_cnt
= 0;
325 sc
->sc_leds
[num
].l_conn_cnt
++;
327 sc
->sc_leds
[num
].l_conn_cnt
--;
331 void mpcsa_blink_led(int num
, int interval
)
333 struct mpcsa_leds_softc
*sc
= mpcsa_leds_sc
;
334 if (!sc
|| num
< 1 || num
> MPCSA_LEDS_NPINS
) {
337 interval
= (interval
+ LEDS_UPDATE_INTERVAL
+ 1) / LEDS_UPDATE_INTERVAL
;
340 if (sc
->sc_leds
[num
].l_mode
!= LMODE_COMM
) {
341 sc
->sc_leds
[num
].l_mode
= LMODE_BLINK
;
342 sc
->sc_leds
[num
].l_blink_cnt
= interval
;
344 sc
->sc_leds
[num
].l_blink_int
= interval
;