1/*	$NetBSD: iq31244_7seg.c,v 1.2 2003/07/15 00:25:01 lukem Exp $	*/
2
3/*-
4 * Copyright (c) 2001, 2002, 2003 Wasabi Systems, Inc.
5 * All rights reserved.
6 *
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This product includes software developed for the NetBSD Project by
20 *	Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 *    or promote products derived from this software without specific prior
23 *    written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38/*
39 * Support for the 7-segment display on the Intel IQ31244.
40 */
41
42#include <sys/cdefs.h>
43__FBSDID("$FreeBSD$");
44
45#include <sys/param.h>
46#include <sys/systm.h>
47#include <sys/kernel.h>
48#include <sys/module.h>
49#include <sys/bus.h>
50#include <sys/sysctl.h>
51
52#include <machine/bus.h>
53
54#include <arm/xscale/i80321/iq80321reg.h>
55#include <arm/xscale/i80321/iq80321var.h>
56
57#define	WRITE(x, v)	*((__volatile uint8_t *) (x)) = (v)
58
59static int snakestate;
60
61/*
62 * The 7-segment display looks like so:
63 *
64 *         A
65 *	+-----+
66 *	|     |
67 *    F	|     | B
68 *	|  G  |
69 *	+-----+
70 *	|     |
71 *    E	|     | C
72 *	|  D  |
73 *	+-----+ o  DP
74 *
75 * Setting a bit clears the corresponding segment on the
76 * display.
77 */
78#define	SEG_A			(1 << 0)
79#define	SEG_B			(1 << 1)
80#define	SEG_C			(1 << 2)
81#define	SEG_D			(1 << 3)
82#define	SEG_E			(1 << 4)
83#define	SEG_F			(1 << 5)
84#define	SEG_G			(1 << 6)
85#define	SEG_DP			(1 << 7)
86
87static const uint8_t digitmap[] = {
88/*	+#####+
89 *	#     #
90 *	#     #
91 *	#     #
92 *	+-----+
93 *	#     #
94 *	#     #
95 *	#     #
96 *	+#####+
97 */
98	SEG_G,
99
100/*	+-----+
101 *	|     #
102 *	|     #
103 *	|     #
104 *	+-----+
105 *	|     #
106 *	|     #
107 *	|     #
108 *	+-----+
109 */
110	SEG_A|SEG_D|SEG_E|SEG_F|SEG_G,
111
112/*	+#####+
113 *	|     #
114 *	|     #
115 *	|     #
116 *	+#####+
117 *	#     |
118 *	#     |
119 *	#     |
120 *	+#####+
121 */
122	SEG_C|SEG_F,
123
124/*	+#####+
125 *	|     #
126 *	|     #
127 *	|     #
128 *	+#####+
129 *	|     #
130 *	|     #
131 *	|     #
132 *	+#####+
133 */
134	SEG_E|SEG_F,
135
136/*	+-----+
137 *	#     #
138 *	#     #
139 *	#     #
140 *	+#####+
141 *	|     #
142 *	|     #
143 *	|     #
144 *	+-----+
145 */
146	SEG_A|SEG_D|SEG_E,
147
148/*	+#####+
149 *	#     |
150 *	#     |
151 *	#     |
152 *	+#####+
153 *	|     #
154 *	|     #
155 *	|     #
156 *	+#####+
157 */
158	SEG_B|SEG_E,
159
160/*	+#####+
161 *	#     |
162 *	#     |
163 *	#     |
164 *	+#####+
165 *	#     #
166 *	#     #
167 *	#     #
168 *	+#####+
169 */
170	SEG_B,
171
172/*	+#####+
173 *	|     #
174 *	|     #
175 *	|     #
176 *	+-----+
177 *	|     #
178 *	|     #
179 *	|     #
180 *	+-----+
181 */
182	SEG_D|SEG_E|SEG_F,
183
184/*	+#####+
185 *	#     #
186 *	#     #
187 *	#     #
188 *	+#####+
189 *	#     #
190 *	#     #
191 *	#     #
192 *	+#####+
193 */
194	0,
195
196/*	+#####+
197 *	#     #
198 *	#     #
199 *	#     #
200 *	+#####+
201 *	|     #
202 *	|     #
203 *	|     #
204 *	+-----+
205 */
206	SEG_D|SEG_E,
207};
208
209static uint8_t
210iq80321_7seg_xlate(char c)
211{
212	uint8_t rv;
213
214	if (c >= '0' && c <= '9')
215		rv = digitmap[c - '0'];
216	else if (c == '.')
217		rv = (uint8_t) ~SEG_DP;
218	else
219		rv = 0xff;
220
221	return (rv);
222}
223
224void
225iq80321_7seg(char a, char b)
226{
227	uint8_t msb, lsb;
228
229	msb = iq80321_7seg_xlate(a);
230	lsb = iq80321_7seg_xlate(b);
231
232	snakestate = 0;
233
234	WRITE(IQ80321_7SEG_MSB, msb);
235	WRITE(IQ80321_7SEG_LSB, lsb);
236}
237
238static const uint8_t snakemap[][2] = {
239
240/*	+#####+		+#####+
241 *	|     |		|     |
242 *	|     |		|     |
243 *	|     |		|     |
244 *	+-----+		+-----+
245 *	|     |		|     |
246 *	|     |		|     |
247 *	|     |		|     |
248 *	+-----+		+-----+
249 */
250	{ ~SEG_A,	~SEG_A },
251
252/*	+-----+		+-----+
253 *	#     |		|     #
254 *	#     |		|     #
255 *	#     |		|     #
256 *	+-----+		+-----+
257 *	|     |		|     |
258 *	|     |		|     |
259 *	|     |		|     |
260 *	+-----+		+-----+
261 */
262	{ ~SEG_F,	~SEG_B },
263
264/*	+-----+		+-----+
265 *	|     |		|     |
266 *	|     |		|     |
267 *	|     |		|     |
268 *	+#####+		+#####+
269 *	|     |		|     |
270 *	|     |		|     |
271 *	|     |		|     |
272 *	+-----+		+-----+
273 */
274	{ ~SEG_G,	~SEG_G },
275
276/*	+-----+		+-----+
277 *	|     |		|     |
278 *	|     |		|     |
279 *	|     |		|     |
280 *	+-----+		+-----+
281 *	|     #		#     |
282 *	|     #		#     |
283 *	|     #		#     |
284 *	+-----+		+-----+
285 */
286	{ ~SEG_C,	~SEG_E },
287
288/*	+-----+		+-----+
289 *	|     |		|     |
290 *	|     |		|     |
291 *	|     |		|     |
292 *	+-----+		+-----+
293 *	|     |		|     |
294 *	|     |		|     |
295 *	|     |		|     |
296 *	+#####+		+#####+
297 */
298	{ ~SEG_D,	~SEG_D },
299
300/*	+-----+		+-----+
301 *	|     |		|     |
302 *	|     |		|     |
303 *	|     |		|     |
304 *	+-----+		+-----+
305 *	#     |		|     #
306 *	#     |		|     #
307 *	#     |		|     #
308 *	+-----+		+-----+
309 */
310	{ ~SEG_E,	~SEG_C },
311
312/*	+-----+		+-----+
313 *	|     |		|     |
314 *	|     |		|     |
315 *	|     |		|     |
316 *	+#####+		+#####+
317 *	|     |		|     |
318 *	|     |		|     |
319 *	|     |		|     |
320 *	+-----+		+-----+
321 */
322	{ ~SEG_G,	~SEG_G },
323
324/*	+-----+		+-----+
325 *	|     #		#     |
326 *	|     #		#     |
327 *	|     #		#     |
328 *	+-----+		+-----+
329 *	|     |		|     |
330 *	|     |		|     |
331 *	|     |		|     |
332 *	+-----+		+-----+
333 */
334	{ ~SEG_B,	~SEG_F },
335};
336
337static SYSCTL_NODE(_hw, OID_AUTO, sevenseg, CTLFLAG_RD, 0, "7 seg");
338static int freq = 20;
339SYSCTL_INT(_hw_sevenseg, OID_AUTO, freq, CTLFLAG_RW, &freq, 0,
340    "7 Seg update frequency");
341static void
342iq31244_7seg_snake(void)
343{
344	static int snakefreq;
345	int cur = snakestate;
346
347	snakefreq++;
348	if ((snakefreq % freq))
349		return;
350	WRITE(IQ80321_7SEG_MSB, snakemap[cur][0]);
351	WRITE(IQ80321_7SEG_LSB, snakemap[cur][1]);
352
353	snakestate = (cur + 1) & 7;
354}
355
356struct iq31244_7seg_softc {
357	device_t	dev;
358};
359
360static int
361iq31244_7seg_probe(device_t dev)
362{
363
364	device_set_desc(dev, "IQ31244 7seg");
365	return (0);
366}
367
368extern void (*i80321_hardclock_hook)(void);
369static int
370iq31244_7seg_attach(device_t dev)
371{
372
373	i80321_hardclock_hook = iq31244_7seg_snake;
374	return (0);
375}
376
377static device_method_t iq31244_7seg_methods[] = {
378	DEVMETHOD(device_probe, iq31244_7seg_probe),
379	DEVMETHOD(device_attach, iq31244_7seg_attach),
380	{0, 0},
381};
382
383static driver_t iq31244_7seg_driver = {
384	"iqseg",
385	iq31244_7seg_methods,
386	sizeof(struct iq31244_7seg_softc),
387};
388static devclass_t iq31244_7seg_devclass;
389
390DRIVER_MODULE(iqseg, iq, iq31244_7seg_driver, iq31244_7seg_devclass, 0, 0);
391