diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/sbus/char |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/sbus/char')
-rw-r--r-- | drivers/sbus/char/Kconfig | 93 | ||||
-rw-r--r-- | drivers/sbus/char/Makefile | 25 | ||||
-rw-r--r-- | drivers/sbus/char/aurora.c | 2372 | ||||
-rw-r--r-- | drivers/sbus/char/aurora.h | 276 | ||||
-rw-r--r-- | drivers/sbus/char/bbc_envctrl.c | 645 | ||||
-rw-r--r-- | drivers/sbus/char/bbc_i2c.c | 488 | ||||
-rw-r--r-- | drivers/sbus/char/bbc_i2c.h | 20 | ||||
-rw-r--r-- | drivers/sbus/char/bpp.c | 1079 | ||||
-rw-r--r-- | drivers/sbus/char/cd180.h | 240 | ||||
-rw-r--r-- | drivers/sbus/char/cpwatchdog.c | 832 | ||||
-rw-r--r-- | drivers/sbus/char/display7seg.c | 234 | ||||
-rw-r--r-- | drivers/sbus/char/envctrl.c | 1181 | ||||
-rw-r--r-- | drivers/sbus/char/flash.c | 255 | ||||
-rw-r--r-- | drivers/sbus/char/jsflash.c | 627 | ||||
-rw-r--r-- | drivers/sbus/char/max1617.h | 27 | ||||
-rw-r--r-- | drivers/sbus/char/openprom.c | 630 | ||||
-rw-r--r-- | drivers/sbus/char/riowatchdog.c | 293 | ||||
-rw-r--r-- | drivers/sbus/char/rtc.c | 178 | ||||
-rw-r--r-- | drivers/sbus/char/uctrl.c | 422 | ||||
-rw-r--r-- | drivers/sbus/char/vfc.h | 179 | ||||
-rw-r--r-- | drivers/sbus/char/vfc_dev.c | 742 | ||||
-rw-r--r-- | drivers/sbus/char/vfc_i2c.c | 347 | ||||
-rw-r--r-- | drivers/sbus/char/vfc_i2c.h | 44 |
23 files changed, 11229 insertions, 0 deletions
diff --git a/drivers/sbus/char/Kconfig b/drivers/sbus/char/Kconfig new file mode 100644 index 00000000000..90d8ef1f0bc --- /dev/null +++ b/drivers/sbus/char/Kconfig | |||
@@ -0,0 +1,93 @@ | |||
1 | |||
2 | menu "Misc Linux/SPARC drivers" | ||
3 | |||
4 | config SUN_OPENPROMIO | ||
5 | tristate "/dev/openprom device support" | ||
6 | help | ||
7 | This driver provides user programs with an interface to the SPARC | ||
8 | PROM device tree. The driver implements a SunOS-compatible | ||
9 | interface and a NetBSD-compatible interface. | ||
10 | |||
11 | To compile this driver as a module, choose M here: the | ||
12 | module will be called openprom. | ||
13 | |||
14 | If unsure, say Y. | ||
15 | |||
16 | config SUN_MOSTEK_RTC | ||
17 | tristate "Mostek real time clock support" | ||
18 | help | ||
19 | The Mostek RTC chip is used on all known Sun computers except | ||
20 | some JavaStations. For a JavaStation you need to say Y both here | ||
21 | and to "Enhanced Real Time Clock Support". | ||
22 | |||
23 | Say Y here unless you are building a special purpose kernel. | ||
24 | |||
25 | config OBP_FLASH | ||
26 | tristate "OBP Flash Device support" | ||
27 | depends on SPARC64 | ||
28 | help | ||
29 | The OpenBoot PROM on Ultra systems is flashable. If you want to be | ||
30 | able to upgrade the OBP firmware, say Y here. | ||
31 | |||
32 | config SUN_BPP | ||
33 | tristate "Bidirectional parallel port support (OBSOLETE)" | ||
34 | depends on EXPERIMENTAL | ||
35 | help | ||
36 | Say Y here to support Sun's obsolete variant of IEEE1284 | ||
37 | bidirectional parallel port protocol as /dev/bppX. Can be built on | ||
38 | x86 machines. | ||
39 | |||
40 | config SUN_VIDEOPIX | ||
41 | tristate "Videopix Frame Grabber (EXPERIMENTAL)" | ||
42 | depends on EXPERIMENTAL && (BROKEN || !64BIT) | ||
43 | help | ||
44 | Say Y here to support the Videopix Frame Grabber from Sun | ||
45 | Microsystems, commonly found on SPARCstations. This card, which is | ||
46 | based on the Phillips SAA9051, can handle NTSC and PAL/SECAM and | ||
47 | SVIDEO signals. | ||
48 | |||
49 | config SUN_AURORA | ||
50 | tristate "Aurora Multiboard 1600se (EXPERIMENTAL)" | ||
51 | depends on EXPERIMENTAL && BROKEN | ||
52 | help | ||
53 | The Aurora Multiboard is a multi-port high-speed serial controller. | ||
54 | If you have one of these, say Y. | ||
55 | |||
56 | config TADPOLE_TS102_UCTRL | ||
57 | tristate "Tadpole TS102 Microcontroller support (EXPERIMENTAL)" | ||
58 | depends on EXPERIMENTAL && SPARC32 | ||
59 | help | ||
60 | Say Y here to directly support the TS102 Microcontroller interface | ||
61 | on the Tadpole Sparcbook 3. This device handles power-management | ||
62 | events, and can also notice the attachment/detachment of external | ||
63 | monitors and mice. | ||
64 | |||
65 | config SUN_JSFLASH | ||
66 | tristate "JavaStation OS Flash SIMM (EXPERIMENTAL)" | ||
67 | depends on EXPERIMENTAL && SPARC32 | ||
68 | help | ||
69 | If you say Y here, you will be able to boot from your JavaStation's | ||
70 | Flash memory. | ||
71 | |||
72 | # XXX Why don't we do "source drivers/char/Config.in" somewhere? | ||
73 | # no shit | ||
74 | config APM_RTC_IS_GMT | ||
75 | bool | ||
76 | depends on EXPERIMENTAL && SPARC32 && PCI | ||
77 | default y | ||
78 | help | ||
79 | Say Y here if your RTC (Real Time Clock a.k.a. hardware clock) | ||
80 | stores the time in GMT (Greenwich Mean Time). Say N if your RTC | ||
81 | stores localtime. | ||
82 | |||
83 | It is in fact recommended to store GMT in your RTC, because then you | ||
84 | don't have to worry about daylight savings time changes. The only | ||
85 | reason not to use GMT in your RTC is if you also run a broken OS | ||
86 | that doesn't understand GMT. | ||
87 | |||
88 | config RTC | ||
89 | tristate "PC-style Real Time Clock Support" | ||
90 | depends on PCI && EXPERIMENTAL && SPARC32 | ||
91 | |||
92 | endmenu | ||
93 | |||
diff --git a/drivers/sbus/char/Makefile b/drivers/sbus/char/Makefile new file mode 100644 index 00000000000..3a5ea1dc789 --- /dev/null +++ b/drivers/sbus/char/Makefile | |||
@@ -0,0 +1,25 @@ | |||
1 | # | ||
2 | # Makefile for the kernel miscellaneous SPARC device drivers. | ||
3 | # | ||
4 | # Dave Redman Frame Buffer tuning support. | ||
5 | # | ||
6 | # 7 October 2000, Bartlomiej Zolnierkiewicz <bkz@linux-ide.org> | ||
7 | # Rewritten to use lists instead of if-statements. | ||
8 | # | ||
9 | |||
10 | vfc-objs := vfc_dev.o vfc_i2c.o | ||
11 | bbc-objs := bbc_i2c.o bbc_envctrl.o | ||
12 | |||
13 | obj-$(CONFIG_ENVCTRL) += envctrl.o | ||
14 | obj-$(CONFIG_DISPLAY7SEG) += display7seg.o | ||
15 | obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwatchdog.o | ||
16 | obj-$(CONFIG_WATCHDOG_RIO) += riowatchdog.o | ||
17 | obj-$(CONFIG_OBP_FLASH) += flash.o | ||
18 | obj-$(CONFIG_SUN_OPENPROMIO) += openprom.o | ||
19 | obj-$(CONFIG_SUN_MOSTEK_RTC) += rtc.o | ||
20 | obj-$(CONFIG_SUN_BPP) += bpp.o | ||
21 | obj-$(CONFIG_SUN_VIDEOPIX) += vfc.o | ||
22 | obj-$(CONFIG_SUN_AURORA) += aurora.o | ||
23 | obj-$(CONFIG_TADPOLE_TS102_UCTRL) += uctrl.o | ||
24 | obj-$(CONFIG_SUN_JSFLASH) += jsflash.o | ||
25 | obj-$(CONFIG_BBC_I2C) += bbc.o | ||
diff --git a/drivers/sbus/char/aurora.c b/drivers/sbus/char/aurora.c new file mode 100644 index 00000000000..e5fa1703856 --- /dev/null +++ b/drivers/sbus/char/aurora.c | |||
@@ -0,0 +1,2372 @@ | |||
1 | /* $Id: aurora.c,v 1.19 2002/01/08 16:00:16 davem Exp $ | ||
2 | * linux/drivers/sbus/char/aurora.c -- Aurora multiport driver | ||
3 | * | ||
4 | * Copyright (c) 1999 by Oliver Aldulea (oli at bv dot ro) | ||
5 | * | ||
6 | * This code is based on the RISCom/8 multiport serial driver written | ||
7 | * by Dmitry Gorodchanin (pgmdsg@ibi.com), based on the Linux serial | ||
8 | * driver, written by Linus Torvalds, Theodore T'so and others. | ||
9 | * The Aurora multiport programming info was obtained mainly from the | ||
10 | * Cirrus Logic CD180 documentation (available on the web), and by | ||
11 | * doing heavy tests on the board. Many thanks to Eddie C. Dost for the | ||
12 | * help on the sbus interface. | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, write to the Free Software | ||
26 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
27 | * | ||
28 | * Revision 1.0 | ||
29 | * | ||
30 | * This is the first public release. | ||
31 | * | ||
32 | * Most of the information you need is in the aurora.h file. Please | ||
33 | * read that file before reading this one. | ||
34 | * | ||
35 | * Several parts of the code do not have comments yet. | ||
36 | * | ||
37 | * n.b. The board can support 115.2 bit rates, but only on a few | ||
38 | * ports. The total badwidth of one chip (ports 0-7 or 8-15) is equal | ||
39 | * to OSC_FREQ div 16. In case of my board, each chip can take 6 | ||
40 | * channels of 115.2 kbaud. This information is not well-tested. | ||
41 | * | ||
42 | * Fixed to use tty_get_baud_rate(). | ||
43 | * Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12 | ||
44 | */ | ||
45 | |||
46 | #include <linux/module.h> | ||
47 | |||
48 | #include <linux/errno.h> | ||
49 | #include <linux/sched.h> | ||
50 | #ifdef AURORA_INT_DEBUG | ||
51 | #include <linux/timer.h> | ||
52 | #endif | ||
53 | #include <linux/interrupt.h> | ||
54 | #include <linux/tty.h> | ||
55 | #include <linux/tty_flip.h> | ||
56 | #include <linux/major.h> | ||
57 | #include <linux/string.h> | ||
58 | #include <linux/fcntl.h> | ||
59 | #include <linux/mm.h> | ||
60 | #include <linux/kernel.h> | ||
61 | #include <linux/init.h> | ||
62 | #include <linux/delay.h> | ||
63 | #include <linux/bitops.h> | ||
64 | |||
65 | #include <asm/io.h> | ||
66 | #include <asm/irq.h> | ||
67 | #include <asm/oplib.h> | ||
68 | #include <asm/system.h> | ||
69 | #include <asm/kdebug.h> | ||
70 | #include <asm/sbus.h> | ||
71 | #include <asm/uaccess.h> | ||
72 | |||
73 | #include "aurora.h" | ||
74 | #include "cd180.h" | ||
75 | |||
76 | unsigned char irqs[4] = { | ||
77 | 0, 0, 0, 0 | ||
78 | }; | ||
79 | |||
80 | #ifdef AURORA_INT_DEBUG | ||
81 | int irqhit=0; | ||
82 | #endif | ||
83 | |||
84 | #ifndef MIN | ||
85 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) | ||
86 | #endif | ||
87 | |||
88 | static struct tty_driver *aurora_driver; | ||
89 | static struct Aurora_board aurora_board[AURORA_NBOARD] = { | ||
90 | {0,}, | ||
91 | }; | ||
92 | |||
93 | static struct Aurora_port aurora_port[AURORA_TNPORTS] = { | ||
94 | { 0, }, | ||
95 | }; | ||
96 | |||
97 | /* no longer used. static struct Aurora_board * IRQ_to_board[16] = { NULL, } ;*/ | ||
98 | static unsigned char * tmp_buf = NULL; | ||
99 | static DECLARE_MUTEX(tmp_buf_sem); | ||
100 | |||
101 | DECLARE_TASK_QUEUE(tq_aurora); | ||
102 | |||
103 | static inline int aurora_paranoia_check(struct Aurora_port const * port, | ||
104 | char *name, const char *routine) | ||
105 | { | ||
106 | #ifdef AURORA_PARANOIA_CHECK | ||
107 | static const char *badmagic = | ||
108 | KERN_DEBUG "aurora: Warning: bad aurora port magic number for device %s in %s\n"; | ||
109 | static const char *badinfo = | ||
110 | KERN_DEBUG "aurora: Warning: null aurora port for device %s in %s\n"; | ||
111 | |||
112 | if (!port) { | ||
113 | printk(badinfo, name, routine); | ||
114 | return 1; | ||
115 | } | ||
116 | if (port->magic != AURORA_MAGIC) { | ||
117 | printk(badmagic, name, routine); | ||
118 | return 1; | ||
119 | } | ||
120 | #endif | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * | ||
126 | * Service functions for aurora driver. | ||
127 | * | ||
128 | */ | ||
129 | |||
130 | /* Get board number from pointer */ | ||
131 | extern inline int board_No (struct Aurora_board const * bp) | ||
132 | { | ||
133 | return bp - aurora_board; | ||
134 | } | ||
135 | |||
136 | /* Get port number from pointer */ | ||
137 | extern inline int port_No (struct Aurora_port const * port) | ||
138 | { | ||
139 | return AURORA_PORT(port - aurora_port); | ||
140 | } | ||
141 | |||
142 | /* Get pointer to board from pointer to port */ | ||
143 | extern inline struct Aurora_board * port_Board(struct Aurora_port const * port) | ||
144 | { | ||
145 | return &aurora_board[AURORA_BOARD(port - aurora_port)]; | ||
146 | } | ||
147 | |||
148 | /* Wait for Channel Command Register ready */ | ||
149 | extern inline void aurora_wait_CCR(struct aurora_reg128 * r) | ||
150 | { | ||
151 | unsigned long delay; | ||
152 | |||
153 | #ifdef AURORA_DEBUG | ||
154 | printk("aurora_wait_CCR\n"); | ||
155 | #endif | ||
156 | /* FIXME: need something more descriptive than 100000 :) */ | ||
157 | for (delay = 100000; delay; delay--) | ||
158 | if (!sbus_readb(&r->r[CD180_CCR])) | ||
159 | return; | ||
160 | printk(KERN_DEBUG "aurora: Timeout waiting for CCR.\n"); | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * aurora probe functions. | ||
165 | */ | ||
166 | |||
167 | /* Must be called with enabled interrupts */ | ||
168 | extern inline void aurora_long_delay(unsigned long delay) | ||
169 | { | ||
170 | unsigned long i; | ||
171 | |||
172 | #ifdef AURORA_DEBUG | ||
173 | printk("aurora_long_delay: start\n"); | ||
174 | #endif | ||
175 | for (i = jiffies + delay; time_before(jiffies, i); ) ; | ||
176 | #ifdef AURORA_DEBUG | ||
177 | printk("aurora_long_delay: end\n"); | ||
178 | #endif | ||
179 | } | ||
180 | |||
181 | /* Reset and setup CD180 chip */ | ||
182 | static int aurora_init_CD180(struct Aurora_board * bp, int chip) | ||
183 | { | ||
184 | unsigned long flags; | ||
185 | int id; | ||
186 | |||
187 | #ifdef AURORA_DEBUG | ||
188 | printk("aurora_init_CD180: start %d:%d\n", | ||
189 | board_No(bp), chip); | ||
190 | #endif | ||
191 | save_flags(flags); cli(); | ||
192 | sbus_writeb(0, &bp->r[chip]->r[CD180_CAR]); | ||
193 | sbus_writeb(0, &bp->r[chip]->r[CD180_GSVR]); | ||
194 | |||
195 | /* Wait for CCR ready */ | ||
196 | aurora_wait_CCR(bp->r[chip]); | ||
197 | |||
198 | /* Reset CD180 chip */ | ||
199 | sbus_writeb(CCR_HARDRESET, &bp->r[chip]->r[CD180_CCR]); | ||
200 | udelay(1); | ||
201 | sti(); | ||
202 | id=1000; | ||
203 | while((--id) && | ||
204 | (sbus_readb(&bp->r[chip]->r[CD180_GSVR])!=0xff))udelay(100); | ||
205 | if(!id) { | ||
206 | printk(KERN_ERR "aurora%d: Chip %d failed init.\n", | ||
207 | board_No(bp), chip); | ||
208 | restore_flags(flags); | ||
209 | return(-1); | ||
210 | } | ||
211 | cli(); | ||
212 | sbus_writeb((board_No(bp)<<5)|((chip+1)<<3), | ||
213 | &bp->r[chip]->r[CD180_GSVR]); /* Set ID for this chip */ | ||
214 | sbus_writeb(0x80|bp->ACK_MINT, | ||
215 | &bp->r[chip]->r[CD180_MSMR]); /* Prio for modem intr */ | ||
216 | sbus_writeb(0x80|bp->ACK_TINT, | ||
217 | &bp->r[chip]->r[CD180_TSMR]); /* Prio for transmitter intr */ | ||
218 | sbus_writeb(0x80|bp->ACK_RINT, | ||
219 | &bp->r[chip]->r[CD180_RSMR]); /* Prio for receiver intr */ | ||
220 | /* Setting up prescaler. We need 4 tick per 1 ms */ | ||
221 | sbus_writeb((bp->oscfreq/(1000000/AURORA_TPS)) >> 8, | ||
222 | &bp->r[chip]->r[CD180_PPRH]); | ||
223 | sbus_writeb((bp->oscfreq/(1000000/AURORA_TPS)) & 0xff, | ||
224 | &bp->r[chip]->r[CD180_PPRL]); | ||
225 | |||
226 | sbus_writeb(SRCR_AUTOPRI|SRCR_GLOBPRI, | ||
227 | &bp->r[chip]->r[CD180_SRCR]); | ||
228 | |||
229 | id = sbus_readb(&bp->r[chip]->r[CD180_GFRCR]); | ||
230 | printk(KERN_INFO "aurora%d: Chip %d id %02x: ", | ||
231 | board_No(bp), chip,id); | ||
232 | if(sbus_readb(&bp->r[chip]->r[CD180_SRCR]) & 128) { | ||
233 | switch (id) { | ||
234 | case 0x82:printk("CL-CD1864 rev A\n");break; | ||
235 | case 0x83:printk("CL-CD1865 rev A\n");break; | ||
236 | case 0x84:printk("CL-CD1865 rev B\n");break; | ||
237 | case 0x85:printk("CL-CD1865 rev C\n");break; | ||
238 | default:printk("Unknown.\n"); | ||
239 | }; | ||
240 | } else { | ||
241 | switch (id) { | ||
242 | case 0x81:printk("CL-CD180 rev B\n");break; | ||
243 | case 0x82:printk("CL-CD180 rev C\n");break; | ||
244 | default:printk("Unknown.\n"); | ||
245 | }; | ||
246 | } | ||
247 | restore_flags(flags); | ||
248 | #ifdef AURORA_DEBUG | ||
249 | printk("aurora_init_CD180: end\n"); | ||
250 | #endif | ||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static int valid_irq(unsigned char irq) | ||
255 | { | ||
256 | int i; | ||
257 | for(i=0;i<TYPE_1_IRQS;i++) | ||
258 | if (type_1_irq[i]==irq) return 1; | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static irqreturn_t aurora_interrupt(int irq, void * dev_id, struct pt_regs * regs); | ||
263 | |||
264 | /* Main probing routine, also sets irq. */ | ||
265 | static int aurora_probe(void) | ||
266 | { | ||
267 | struct sbus_bus *sbus; | ||
268 | struct sbus_dev *sdev; | ||
269 | int grrr; | ||
270 | char buf[30]; | ||
271 | int bn = 0; | ||
272 | struct Aurora_board *bp; | ||
273 | |||
274 | for_each_sbus(sbus) { | ||
275 | for_each_sbusdev(sdev, sbus) { | ||
276 | /* printk("Try: %x %s\n",sdev,sdev->prom_name);*/ | ||
277 | if (!strcmp(sdev->prom_name, "sio16")) { | ||
278 | #ifdef AURORA_DEBUG | ||
279 | printk(KERN_INFO "aurora: sio16 at %p\n",sdev); | ||
280 | #endif | ||
281 | if((sdev->reg_addrs[0].reg_size!=1) && | ||
282 | (sdev->reg_addrs[1].reg_size!=128) && | ||
283 | (sdev->reg_addrs[2].reg_size!=128) && | ||
284 | (sdev->reg_addrs[3].reg_size!=4)) { | ||
285 | printk(KERN_ERR "aurora%d: registers' sizes " | ||
286 | "do not match.\n", bn); | ||
287 | break; | ||
288 | } | ||
289 | bp = &aurora_board[bn]; | ||
290 | bp->r0 = (struct aurora_reg1 *) | ||
291 | sbus_ioremap(&sdev->resource[0], 0, | ||
292 | sdev->reg_addrs[0].reg_size, | ||
293 | "sio16"); | ||
294 | if (bp->r0 == NULL) { | ||
295 | printk(KERN_ERR "aurora%d: can't map " | ||
296 | "reg_addrs[0]\n", bn); | ||
297 | break; | ||
298 | } | ||
299 | #ifdef AURORA_DEBUG | ||
300 | printk("Map reg 0: %p\n", bp->r0); | ||
301 | #endif | ||
302 | bp->r[0] = (struct aurora_reg128 *) | ||
303 | sbus_ioremap(&sdev->resource[1], 0, | ||
304 | sdev->reg_addrs[1].reg_size, | ||
305 | "sio16"); | ||
306 | if (bp->r[0] == NULL) { | ||
307 | printk(KERN_ERR "aurora%d: can't map " | ||
308 | "reg_addrs[1]\n", bn); | ||
309 | break; | ||
310 | } | ||
311 | #ifdef AURORA_DEBUG | ||
312 | printk("Map reg 1: %p\n", bp->r[0]); | ||
313 | #endif | ||
314 | bp->r[1] = (struct aurora_reg128 *) | ||
315 | sbus_ioremap(&sdev->resource[2], 0, | ||
316 | sdev->reg_addrs[2].reg_size, | ||
317 | "sio16"); | ||
318 | if (bp->r[1] == NULL) { | ||
319 | printk(KERN_ERR "aurora%d: can't map " | ||
320 | "reg_addrs[2]\n", bn); | ||
321 | break; | ||
322 | } | ||
323 | #ifdef AURORA_DEBUG | ||
324 | printk("Map reg 2: %p\n", bp->r[1]); | ||
325 | #endif | ||
326 | bp->r3 = (struct aurora_reg4 *) | ||
327 | sbus_ioremap(&sdev->resource[3], 0, | ||
328 | sdev->reg_addrs[3].reg_size, | ||
329 | "sio16"); | ||
330 | if (bp->r3 == NULL) { | ||
331 | printk(KERN_ERR "aurora%d: can't map " | ||
332 | "reg_addrs[3]\n", bn); | ||
333 | break; | ||
334 | } | ||
335 | #ifdef AURORA_DEBUG | ||
336 | printk("Map reg 3: %p\n", bp->r3); | ||
337 | #endif | ||
338 | /* Variables setup */ | ||
339 | bp->flags = 0; | ||
340 | #ifdef AURORA_DEBUG | ||
341 | grrr=prom_getint(sdev->prom_node,"intr"); | ||
342 | printk("intr pri %d\n", grrr); | ||
343 | #endif | ||
344 | if ((bp->irq=irqs[bn]) && valid_irq(bp->irq) && | ||
345 | !request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) { | ||
346 | free_irq(bp->irq|0x30, bp); | ||
347 | } else | ||
348 | if ((bp->irq=prom_getint(sdev->prom_node, "bintr")) && valid_irq(bp->irq) && | ||
349 | !request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) { | ||
350 | free_irq(bp->irq|0x30, bp); | ||
351 | } else | ||
352 | if ((bp->irq=prom_getint(sdev->prom_node, "intr")) && valid_irq(bp->irq) && | ||
353 | !request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) { | ||
354 | free_irq(bp->irq|0x30, bp); | ||
355 | } else | ||
356 | for(grrr=0;grrr<TYPE_1_IRQS;grrr++) { | ||
357 | if ((bp->irq=type_1_irq[grrr])&&!request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) { | ||
358 | free_irq(bp->irq|0x30, bp); | ||
359 | break; | ||
360 | } else { | ||
361 | printk(KERN_ERR "aurora%d: Could not get an irq for this board !!!\n",bn); | ||
362 | bp->flags=0xff; | ||
363 | } | ||
364 | } | ||
365 | if(bp->flags==0xff)break; | ||
366 | printk(KERN_INFO "aurora%d: irq %d\n",bn,bp->irq&0x0f); | ||
367 | buf[0]=0; | ||
368 | grrr=prom_getproperty(sdev->prom_node,"dtr_rts",buf,sizeof(buf)); | ||
369 | if(!strcmp(buf,"swapped")){ | ||
370 | printk(KERN_INFO "aurora%d: Swapped DTR and RTS\n",bn); | ||
371 | bp->DTR=MSVR_RTS; | ||
372 | bp->RTS=MSVR_DTR; | ||
373 | bp->MSVDTR=CD180_MSVRTS; | ||
374 | bp->MSVRTS=CD180_MSVDTR; | ||
375 | bp->flags|=AURORA_BOARD_DTR_FLOW_OK; | ||
376 | }else{ | ||
377 | #ifdef AURORA_FORCE_DTR_FLOW | ||
378 | printk(KERN_INFO "aurora%d: Forcing swapped DTR-RTS\n",bn); | ||
379 | bp->DTR=MSVR_RTS; | ||
380 | bp->RTS=MSVR_DTR; | ||
381 | bp->MSVDTR=CD180_MSVRTS; | ||
382 | bp->MSVRTS=CD180_MSVDTR; | ||
383 | bp->flags|=AURORA_BOARD_DTR_FLOW_OK; | ||
384 | #else | ||
385 | printk(KERN_INFO "aurora%d: Normal DTR and RTS\n",bn); | ||
386 | bp->DTR=MSVR_DTR; | ||
387 | bp->RTS=MSVR_RTS; | ||
388 | bp->MSVDTR=CD180_MSVDTR; | ||
389 | bp->MSVRTS=CD180_MSVRTS; | ||
390 | #endif | ||
391 | } | ||
392 | bp->oscfreq=prom_getint(sdev->prom_node,"clk")*100; | ||
393 | printk(KERN_INFO "aurora%d: Oscillator: %d Hz\n",bn,bp->oscfreq); | ||
394 | grrr=prom_getproperty(sdev->prom_node,"chip",buf,sizeof(buf)); | ||
395 | printk(KERN_INFO "aurora%d: Chips: %s\n",bn,buf); | ||
396 | grrr=prom_getproperty(sdev->prom_node,"manu",buf,sizeof(buf)); | ||
397 | printk(KERN_INFO "aurora%d: Manufacturer: %s\n",bn,buf); | ||
398 | grrr=prom_getproperty(sdev->prom_node,"model",buf,sizeof(buf)); | ||
399 | printk(KERN_INFO "aurora%d: Model: %s\n",bn,buf); | ||
400 | grrr=prom_getproperty(sdev->prom_node,"rev",buf,sizeof(buf)); | ||
401 | printk(KERN_INFO "aurora%d: Revision: %s\n",bn,buf); | ||
402 | grrr=prom_getproperty(sdev->prom_node,"mode",buf,sizeof(buf)); | ||
403 | printk(KERN_INFO "aurora%d: Mode: %s\n",bn,buf); | ||
404 | #ifdef MODULE | ||
405 | bp->count=0; | ||
406 | #endif | ||
407 | bp->flags = AURORA_BOARD_PRESENT; | ||
408 | /* hardware ack */ | ||
409 | bp->ACK_MINT=1; | ||
410 | bp->ACK_TINT=2; | ||
411 | bp->ACK_RINT=3; | ||
412 | bn++; | ||
413 | } | ||
414 | } | ||
415 | } | ||
416 | return bn; | ||
417 | } | ||
418 | |||
419 | static void aurora_release_io_range(struct Aurora_board *bp) | ||
420 | { | ||
421 | sbus_iounmap((unsigned long)bp->r0, 1); | ||
422 | sbus_iounmap((unsigned long)bp->r[0], 128); | ||
423 | sbus_iounmap((unsigned long)bp->r[1], 128); | ||
424 | sbus_iounmap((unsigned long)bp->r3, 4); | ||
425 | } | ||
426 | |||
427 | extern inline void aurora_mark_event(struct Aurora_port * port, int event) | ||
428 | { | ||
429 | #ifdef AURORA_DEBUG | ||
430 | printk("aurora_mark_event: start\n"); | ||
431 | #endif | ||
432 | set_bit(event, &port->event); | ||
433 | queue_task(&port->tqueue, &tq_aurora); | ||
434 | mark_bh(AURORA_BH); | ||
435 | #ifdef AURORA_DEBUG | ||
436 | printk("aurora_mark_event: end\n"); | ||
437 | #endif | ||
438 | } | ||
439 | |||
440 | static __inline__ struct Aurora_port * aurora_get_port(struct Aurora_board const * bp, | ||
441 | int chip, | ||
442 | unsigned char const *what) | ||
443 | { | ||
444 | unsigned char channel; | ||
445 | struct Aurora_port * port; | ||
446 | |||
447 | channel = ((chip << 3) | | ||
448 | ((sbus_readb(&bp->r[chip]->r[CD180_GSCR]) & GSCR_CHAN) >> GSCR_CHAN_OFF)); | ||
449 | port = &aurora_port[board_No(bp) * AURORA_NPORT * AURORA_NCD180 + channel]; | ||
450 | if (port->flags & ASYNC_INITIALIZED) | ||
451 | return port; | ||
452 | |||
453 | printk(KERN_DEBUG "aurora%d: %s interrupt from invalid port %d\n", | ||
454 | board_No(bp), what, channel); | ||
455 | return NULL; | ||
456 | } | ||
457 | |||
458 | static void aurora_receive_exc(struct Aurora_board const * bp, int chip) | ||
459 | { | ||
460 | struct Aurora_port *port; | ||
461 | struct tty_struct *tty; | ||
462 | unsigned char status; | ||
463 | unsigned char ch; | ||
464 | |||
465 | if (!(port = aurora_get_port(bp, chip, "Receive_x"))) | ||
466 | return; | ||
467 | |||
468 | tty = port->tty; | ||
469 | if (tty->flip.count >= TTY_FLIPBUF_SIZE) { | ||
470 | #ifdef AURORA_INTNORM | ||
471 | printk("aurora%d: port %d: Working around flip buffer overflow.\n", | ||
472 | board_No(bp), port_No(port)); | ||
473 | #endif | ||
474 | return; | ||
475 | } | ||
476 | |||
477 | #ifdef AURORA_REPORT_OVERRUN | ||
478 | status = sbus_readb(&bp->r[chip]->r[CD180_RCSR]); | ||
479 | if (status & RCSR_OE) { | ||
480 | port->overrun++; | ||
481 | #if 1 | ||
482 | printk("aurora%d: port %d: Overrun. Total %ld overruns.\n", | ||
483 | board_No(bp), port_No(port), port->overrun); | ||
484 | #endif | ||
485 | } | ||
486 | status &= port->mark_mask; | ||
487 | #else | ||
488 | status = sbus_readb(&bp->r[chip]->r[CD180_RCSR]) & port->mark_mask; | ||
489 | #endif | ||
490 | ch = sbus_readb(&bp->r[chip]->r[CD180_RDR]); | ||
491 | if (!status) | ||
492 | return; | ||
493 | |||
494 | if (status & RCSR_TOUT) { | ||
495 | /* printk("aurora%d: port %d: Receiver timeout. Hardware problems ?\n", | ||
496 | board_No(bp), port_No(port));*/ | ||
497 | return; | ||
498 | |||
499 | } else if (status & RCSR_BREAK) { | ||
500 | printk(KERN_DEBUG "aurora%d: port %d: Handling break...\n", | ||
501 | board_No(bp), port_No(port)); | ||
502 | *tty->flip.flag_buf_ptr++ = TTY_BREAK; | ||
503 | if (port->flags & ASYNC_SAK) | ||
504 | do_SAK(tty); | ||
505 | |||
506 | } else if (status & RCSR_PE) | ||
507 | *tty->flip.flag_buf_ptr++ = TTY_PARITY; | ||
508 | |||
509 | else if (status & RCSR_FE) | ||
510 | *tty->flip.flag_buf_ptr++ = TTY_FRAME; | ||
511 | |||
512 | else if (status & RCSR_OE) | ||
513 | *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; | ||
514 | |||
515 | else | ||
516 | *tty->flip.flag_buf_ptr++ = 0; | ||
517 | |||
518 | *tty->flip.char_buf_ptr++ = ch; | ||
519 | tty->flip.count++; | ||
520 | queue_task(&tty->flip.tqueue, &tq_timer); | ||
521 | } | ||
522 | |||
523 | static void aurora_receive(struct Aurora_board const * bp, int chip) | ||
524 | { | ||
525 | struct Aurora_port *port; | ||
526 | struct tty_struct *tty; | ||
527 | unsigned char count,cnt; | ||
528 | |||
529 | if (!(port = aurora_get_port(bp, chip, "Receive"))) | ||
530 | return; | ||
531 | |||
532 | tty = port->tty; | ||
533 | |||
534 | count = sbus_readb(&bp->r[chip]->r[CD180_RDCR]); | ||
535 | |||
536 | #ifdef AURORA_REPORT_FIFO | ||
537 | port->hits[count > 8 ? 9 : count]++; | ||
538 | #endif | ||
539 | |||
540 | while (count--) { | ||
541 | if (tty->flip.count >= TTY_FLIPBUF_SIZE) { | ||
542 | #ifdef AURORA_INTNORM | ||
543 | printk("aurora%d: port %d: Working around flip buffer overflow.\n", | ||
544 | board_No(bp), port_No(port)); | ||
545 | #endif | ||
546 | break; | ||
547 | } | ||
548 | cnt = sbus_readb(&bp->r[chip]->r[CD180_RDR]); | ||
549 | *tty->flip.char_buf_ptr++ = cnt; | ||
550 | *tty->flip.flag_buf_ptr++ = 0; | ||
551 | tty->flip.count++; | ||
552 | } | ||
553 | queue_task(&tty->flip.tqueue, &tq_timer); | ||
554 | } | ||
555 | |||
556 | static void aurora_transmit(struct Aurora_board const * bp, int chip) | ||
557 | { | ||
558 | struct Aurora_port *port; | ||
559 | struct tty_struct *tty; | ||
560 | unsigned char count; | ||
561 | |||
562 | if (!(port = aurora_get_port(bp, chip, "Transmit"))) | ||
563 | return; | ||
564 | |||
565 | tty = port->tty; | ||
566 | |||
567 | if (port->SRER & SRER_TXEMPTY) { | ||
568 | /* FIFO drained */ | ||
569 | sbus_writeb(port_No(port) & 7, | ||
570 | &bp->r[chip]->r[CD180_CAR]); | ||
571 | udelay(1); | ||
572 | port->SRER &= ~SRER_TXEMPTY; | ||
573 | sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); | ||
574 | return; | ||
575 | } | ||
576 | |||
577 | if ((port->xmit_cnt <= 0 && !port->break_length) | ||
578 | || tty->stopped || tty->hw_stopped) { | ||
579 | sbus_writeb(port_No(port) & 7, | ||
580 | &bp->r[chip]->r[CD180_CAR]); | ||
581 | udelay(1); | ||
582 | port->SRER &= ~SRER_TXRDY; | ||
583 | sbus_writeb(port->SRER, | ||
584 | &bp->r[chip]->r[CD180_SRER]); | ||
585 | return; | ||
586 | } | ||
587 | |||
588 | if (port->break_length) { | ||
589 | if (port->break_length > 0) { | ||
590 | if (port->COR2 & COR2_ETC) { | ||
591 | sbus_writeb(CD180_C_ESC, | ||
592 | &bp->r[chip]->r[CD180_TDR]); | ||
593 | sbus_writeb(CD180_C_SBRK, | ||
594 | &bp->r[chip]->r[CD180_TDR]); | ||
595 | port->COR2 &= ~COR2_ETC; | ||
596 | } | ||
597 | count = MIN(port->break_length, 0xff); | ||
598 | sbus_writeb(CD180_C_ESC, | ||
599 | &bp->r[chip]->r[CD180_TDR]); | ||
600 | sbus_writeb(CD180_C_DELAY, | ||
601 | &bp->r[chip]->r[CD180_TDR]); | ||
602 | sbus_writeb(count, | ||
603 | &bp->r[chip]->r[CD180_TDR]); | ||
604 | if (!(port->break_length -= count)) | ||
605 | port->break_length--; | ||
606 | } else { | ||
607 | sbus_writeb(CD180_C_ESC, | ||
608 | &bp->r[chip]->r[CD180_TDR]); | ||
609 | sbus_writeb(CD180_C_EBRK, | ||
610 | &bp->r[chip]->r[CD180_TDR]); | ||
611 | sbus_writeb(port->COR2, | ||
612 | &bp->r[chip]->r[CD180_COR2]); | ||
613 | aurora_wait_CCR(bp->r[chip]); | ||
614 | sbus_writeb(CCR_CORCHG2, | ||
615 | &bp->r[chip]->r[CD180_CCR]); | ||
616 | port->break_length = 0; | ||
617 | } | ||
618 | return; | ||
619 | } | ||
620 | |||
621 | count = CD180_NFIFO; | ||
622 | do { | ||
623 | u8 byte = port->xmit_buf[port->xmit_tail++]; | ||
624 | |||
625 | sbus_writeb(byte, &bp->r[chip]->r[CD180_TDR]); | ||
626 | port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1); | ||
627 | if (--port->xmit_cnt <= 0) | ||
628 | break; | ||
629 | } while (--count > 0); | ||
630 | |||
631 | if (port->xmit_cnt <= 0) { | ||
632 | sbus_writeb(port_No(port) & 7, | ||
633 | &bp->r[chip]->r[CD180_CAR]); | ||
634 | udelay(1); | ||
635 | port->SRER &= ~SRER_TXRDY; | ||
636 | sbus_writeb(port->SRER, | ||
637 | &bp->r[chip]->r[CD180_SRER]); | ||
638 | } | ||
639 | if (port->xmit_cnt <= port->wakeup_chars) | ||
640 | aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP); | ||
641 | } | ||
642 | |||
643 | static void aurora_check_modem(struct Aurora_board const * bp, int chip) | ||
644 | { | ||
645 | struct Aurora_port *port; | ||
646 | struct tty_struct *tty; | ||
647 | unsigned char mcr; | ||
648 | |||
649 | if (!(port = aurora_get_port(bp, chip, "Modem"))) | ||
650 | return; | ||
651 | |||
652 | tty = port->tty; | ||
653 | |||
654 | mcr = sbus_readb(&bp->r[chip]->r[CD180_MCR]); | ||
655 | if (mcr & MCR_CDCHG) { | ||
656 | if (sbus_readb(&bp->r[chip]->r[CD180_MSVR]) & MSVR_CD) | ||
657 | wake_up_interruptible(&port->open_wait); | ||
658 | else | ||
659 | schedule_task(&port->tqueue_hangup); | ||
660 | } | ||
661 | |||
662 | /* We don't have such things yet. My aurora board has DTR and RTS swapped, but that doesn't count in this driver. Let's hope | ||
663 | * Aurora didn't made any boards with CTS or DSR broken... | ||
664 | */ | ||
665 | /* #ifdef AURORA_BRAIN_DAMAGED_CTS | ||
666 | if (mcr & MCR_CTSCHG) { | ||
667 | if (aurora_in(bp, CD180_MSVR) & MSVR_CTS) { | ||
668 | tty->hw_stopped = 0; | ||
669 | port->SRER |= SRER_TXRDY; | ||
670 | if (port->xmit_cnt <= port->wakeup_chars) | ||
671 | aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP); | ||
672 | } else { | ||
673 | tty->hw_stopped = 1; | ||
674 | port->SRER &= ~SRER_TXRDY; | ||
675 | } | ||
676 | sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); | ||
677 | } | ||
678 | if (mcr & MCR_DSRCHG) { | ||
679 | if (aurora_in(bp, CD180_MSVR) & MSVR_DSR) { | ||
680 | tty->hw_stopped = 0; | ||
681 | port->SRER |= SRER_TXRDY; | ||
682 | if (port->xmit_cnt <= port->wakeup_chars) | ||
683 | aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP); | ||
684 | } else { | ||
685 | tty->hw_stopped = 1; | ||
686 | port->SRER &= ~SRER_TXRDY; | ||
687 | } | ||
688 | sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); | ||
689 | } | ||
690 | #endif AURORA_BRAIN_DAMAGED_CTS */ | ||
691 | |||
692 | /* Clear change bits */ | ||
693 | sbus_writeb(0, &bp->r[chip]->r[CD180_MCR]); | ||
694 | } | ||
695 | |||
696 | /* The main interrupt processing routine */ | ||
697 | static irqreturn_t aurora_interrupt(int irq, void * dev_id, struct pt_regs * regs) | ||
698 | { | ||
699 | unsigned char status; | ||
700 | unsigned char ack,chip/*,chip_id*/; | ||
701 | struct Aurora_board * bp = (struct Aurora_board *) dev_id; | ||
702 | unsigned long loop = 0; | ||
703 | |||
704 | #ifdef AURORA_INT_DEBUG | ||
705 | printk("IRQ%d %d\n",irq,++irqhit); | ||
706 | #ifdef AURORA_FLOODPRO | ||
707 | if (irqhit>=AURORA_FLOODPRO) | ||
708 | sbus_writeb(8, &bp->r0->r); | ||
709 | #endif | ||
710 | #endif | ||
711 | |||
712 | /* old bp = IRQ_to_board[irq&0x0f];*/ | ||
713 | |||
714 | if (!bp || !(bp->flags & AURORA_BOARD_ACTIVE)) | ||
715 | return IRQ_NONE; | ||
716 | |||
717 | /* The while() below takes care of this. | ||
718 | status = sbus_readb(&bp->r[0]->r[CD180_SRSR]); | ||
719 | #ifdef AURORA_INT_DEBUG | ||
720 | printk("mumu: %02x\n", status); | ||
721 | #endif | ||
722 | if (!(status&SRSR_ANYINT)) | ||
723 | return IRQ_NONE; * Nobody has anything to say, so exit * | ||
724 | */ | ||
725 | while ((loop++ < 48) && | ||
726 | (status = sbus_readb(&bp->r[0]->r[CD180_SRSR]) & SRSR_ANYINT)){ | ||
727 | #ifdef AURORA_INT_DEBUG | ||
728 | printk("SRSR: %02x\n", status); | ||
729 | #endif | ||
730 | if (status & SRSR_REXT) { | ||
731 | ack = sbus_readb(&bp->r3->r[bp->ACK_RINT]); | ||
732 | #ifdef AURORA_INT_DEBUG | ||
733 | printk("R-ACK %02x\n", ack); | ||
734 | #endif | ||
735 | if ((ack >> 5) == board_No(bp)) { | ||
736 | if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) { | ||
737 | if ((ack&GSVR_ITMASK)==GSVR_IT_RGD) { | ||
738 | aurora_receive(bp,chip); | ||
739 | sbus_writeb(0, | ||
740 | &bp->r[chip]->r[CD180_EOSRR]); | ||
741 | } else if ((ack & GSVR_ITMASK) == GSVR_IT_REXC) { | ||
742 | aurora_receive_exc(bp,chip); | ||
743 | sbus_writeb(0, | ||
744 | &bp->r[chip]->r[CD180_EOSRR]); | ||
745 | } | ||
746 | } | ||
747 | } | ||
748 | } else if (status & SRSR_TEXT) { | ||
749 | ack = sbus_readb(&bp->r3->r[bp->ACK_TINT]); | ||
750 | #ifdef AURORA_INT_DEBUG | ||
751 | printk("T-ACK %02x\n", ack); | ||
752 | #endif | ||
753 | if ((ack >> 5) == board_No(bp)) { | ||
754 | if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) { | ||
755 | if ((ack&GSVR_ITMASK)==GSVR_IT_TX) { | ||
756 | aurora_transmit(bp,chip); | ||
757 | sbus_writeb(0, | ||
758 | &bp->r[chip]->r[CD180_EOSRR]); | ||
759 | } | ||
760 | } | ||
761 | } | ||
762 | } else if (status & SRSR_MEXT) { | ||
763 | ack = sbus_readb(&bp->r3->r[bp->ACK_MINT]); | ||
764 | #ifdef AURORA_INT_DEBUG | ||
765 | printk("M-ACK %02x\n", ack); | ||
766 | #endif | ||
767 | if ((ack >> 5) == board_No(bp)) { | ||
768 | if ((chip = ((ack>>3)&3)-1) < AURORA_NCD180) { | ||
769 | if ((ack&GSVR_ITMASK)==GSVR_IT_MDM) { | ||
770 | aurora_check_modem(bp,chip); | ||
771 | sbus_writeb(0, | ||
772 | &bp->r[chip]->r[CD180_EOSRR]); | ||
773 | } | ||
774 | } | ||
775 | } | ||
776 | } | ||
777 | } | ||
778 | /* I guess this faster code can be used with CD1865, using AUROPRI and GLOBPRI. */ | ||
779 | #if 0 | ||
780 | while ((loop++ < 48)&&(status=bp->r[0]->r[CD180_SRSR]&SRSR_ANYINT)){ | ||
781 | #ifdef AURORA_INT_DEBUG | ||
782 | printk("SRSR: %02x\n",status); | ||
783 | #endif | ||
784 | ack = sbus_readb(&bp->r3->r[0]); | ||
785 | #ifdef AURORA_INT_DEBUG | ||
786 | printk("ACK: %02x\n",ack); | ||
787 | #endif | ||
788 | if ((ack>>5)==board_No(bp)) { | ||
789 | if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) { | ||
790 | ack&=GSVR_ITMASK; | ||
791 | if (ack==GSVR_IT_RGD) { | ||
792 | aurora_receive(bp,chip); | ||
793 | sbus_writeb(0, | ||
794 | &bp->r[chip]->r[CD180_EOSRR]); | ||
795 | } else if (ack==GSVR_IT_REXC) { | ||
796 | aurora_receive_exc(bp,chip); | ||
797 | sbus_writeb(0, | ||
798 | &bp->r[chip]->r[CD180_EOSRR]); | ||
799 | } else if (ack==GSVR_IT_TX) { | ||
800 | aurora_transmit(bp,chip); | ||
801 | sbus_writeb(0, | ||
802 | &bp->r[chip]->r[CD180_EOSRR]); | ||
803 | } else if (ack==GSVR_IT_MDM) { | ||
804 | aurora_check_modem(bp,chip); | ||
805 | sbus_writeb(0, | ||
806 | &bp->r[chip]->r[CD180_EOSRR]); | ||
807 | } | ||
808 | } | ||
809 | } | ||
810 | } | ||
811 | #endif | ||
812 | |||
813 | /* This is the old handling routine, used in riscom8 for only one CD180. I keep it here for reference. */ | ||
814 | #if 0 | ||
815 | for(chip=0;chip<AURORA_NCD180;chip++){ | ||
816 | chip_id=(board_No(bp)<<5)|((chip+1)<<3); | ||
817 | loop=0; | ||
818 | while ((loop++ < 1) && | ||
819 | ((status = sbus_readb(&bp->r[chip]->r[CD180_SRSR])) & | ||
820 | (SRSR_TEXT | SRSR_MEXT | SRSR_REXT))) { | ||
821 | |||
822 | if (status & SRSR_REXT) { | ||
823 | ack = sbus_readb(&bp->r3->r[bp->ACK_RINT]); | ||
824 | if (ack == (chip_id | GSVR_IT_RGD)) { | ||
825 | #ifdef AURORA_INTMSG | ||
826 | printk("RX ACK\n"); | ||
827 | #endif | ||
828 | aurora_receive(bp,chip); | ||
829 | } else if (ack == (chip_id | GSVR_IT_REXC)) { | ||
830 | #ifdef AURORA_INTMSG | ||
831 | printk("RXC ACK\n"); | ||
832 | #endif | ||
833 | aurora_receive_exc(bp,chip); | ||
834 | } else { | ||
835 | #ifdef AURORA_INTNORM | ||
836 | printk("aurora%d-%d: Bad receive ack 0x%02x.\n", | ||
837 | board_No(bp), chip, ack); | ||
838 | #endif | ||
839 | } | ||
840 | } else if (status & SRSR_TEXT) { | ||
841 | ack = sbus_readb(&bp->r3->r[bp->ACK_TINT]); | ||
842 | if (ack == (chip_id | GSVR_IT_TX)){ | ||
843 | #ifdef AURORA_INTMSG | ||
844 | printk("TX ACK\n"); | ||
845 | #endif | ||
846 | aurora_transmit(bp,chip); | ||
847 | } else { | ||
848 | #ifdef AURORA_INTNORM | ||
849 | printk("aurora%d-%d: Bad transmit ack 0x%02x.\n", | ||
850 | board_No(bp), chip, ack); | ||
851 | #endif | ||
852 | } | ||
853 | } else if (status & SRSR_MEXT) { | ||
854 | ack = sbus_readb(&bp->r3->r[bp->ACK_MINT]); | ||
855 | if (ack == (chip_id | GSVR_IT_MDM)){ | ||
856 | #ifdef AURORA_INTMSG | ||
857 | printk("MDM ACK\n"); | ||
858 | #endif | ||
859 | aurora_check_modem(bp,chip); | ||
860 | } else { | ||
861 | #ifdef AURORA_INTNORM | ||
862 | printk("aurora%d-%d: Bad modem ack 0x%02x.\n", | ||
863 | board_No(bp), chip, ack); | ||
864 | #endif | ||
865 | } | ||
866 | } | ||
867 | sbus_writeb(0, &bp->r[chip]->r[CD180_EOSRR]); | ||
868 | } | ||
869 | } | ||
870 | #endif | ||
871 | |||
872 | return IRQ_HANDLED; | ||
873 | } | ||
874 | |||
875 | #ifdef AURORA_INT_DEBUG | ||
876 | static void aurora_timer (unsigned long ignored); | ||
877 | |||
878 | static struct timer_list aurora_poll_timer = | ||
879 | TIMER_INITIALIZER(aurora_timer, 0, 0); | ||
880 | |||
881 | static void | ||
882 | aurora_timer (unsigned long ignored) | ||
883 | { | ||
884 | unsigned long flags; | ||
885 | int i; | ||
886 | |||
887 | save_flags(flags); cli(); | ||
888 | |||
889 | printk("SRSR: %02x,%02x - ", | ||
890 | sbus_readb(&aurora_board[0].r[0]->r[CD180_SRSR]), | ||
891 | sbus_readb(&aurora_board[0].r[1]->r[CD180_SRSR])); | ||
892 | for (i = 0; i < 4; i++) { | ||
893 | udelay(1); | ||
894 | printk("%02x ", | ||
895 | sbus_readb(&aurora_board[0].r3->r[i])); | ||
896 | } | ||
897 | printk("\n"); | ||
898 | |||
899 | aurora_poll_timer.expires = jiffies + 300; | ||
900 | add_timer (&aurora_poll_timer); | ||
901 | |||
902 | restore_flags(flags); | ||
903 | } | ||
904 | #endif | ||
905 | |||
906 | /* | ||
907 | * Routines for open & close processing. | ||
908 | */ | ||
909 | |||
910 | /* Called with disabled interrupts */ | ||
911 | static int aurora_setup_board(struct Aurora_board * bp) | ||
912 | { | ||
913 | int error; | ||
914 | |||
915 | #ifdef AURORA_ALLIRQ | ||
916 | int i; | ||
917 | for (i = 0; i < AURORA_ALLIRQ; i++) { | ||
918 | error = request_irq(allirq[i]|0x30, aurora_interrupt, SA_SHIRQ, | ||
919 | "sio16", bp); | ||
920 | if (error) | ||
921 | printk(KERN_ERR "IRQ%d request error %d\n", | ||
922 | allirq[i], error); | ||
923 | } | ||
924 | #else | ||
925 | error = request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, | ||
926 | "sio16", bp); | ||
927 | if (error) { | ||
928 | printk(KERN_ERR "IRQ request error %d\n", error); | ||
929 | return error; | ||
930 | } | ||
931 | #endif | ||
932 | /* Board reset */ | ||
933 | sbus_writeb(0, &bp->r0->r); | ||
934 | udelay(1); | ||
935 | if (bp->flags & AURORA_BOARD_TYPE_2) { | ||
936 | /* unknown yet */ | ||
937 | } else { | ||
938 | sbus_writeb((AURORA_CFG_ENABLE_IO | AURORA_CFG_ENABLE_IRQ | | ||
939 | (((bp->irq)&0x0f)>>2)), | ||
940 | &bp->r0->r); | ||
941 | } | ||
942 | udelay(10000); | ||
943 | |||
944 | if (aurora_init_CD180(bp,0))error=1;error=0; | ||
945 | if (aurora_init_CD180(bp,1))error++; | ||
946 | if (error == AURORA_NCD180) { | ||
947 | printk(KERN_ERR "Both chips failed initialisation.\n"); | ||
948 | return -EIO; | ||
949 | } | ||
950 | |||
951 | #ifdef AURORA_INT_DEBUG | ||
952 | aurora_poll_timer.expires= jiffies + 1; | ||
953 | add_timer(&aurora_poll_timer); | ||
954 | #endif | ||
955 | #ifdef AURORA_DEBUG | ||
956 | printk("aurora_setup_board: end\n"); | ||
957 | #endif | ||
958 | return 0; | ||
959 | } | ||
960 | |||
961 | /* Called with disabled interrupts */ | ||
962 | static void aurora_shutdown_board(struct Aurora_board *bp) | ||
963 | { | ||
964 | int i; | ||
965 | |||
966 | #ifdef AURORA_DEBUG | ||
967 | printk("aurora_shutdown_board: start\n"); | ||
968 | #endif | ||
969 | |||
970 | #ifdef AURORA_INT_DEBUG | ||
971 | del_timer(&aurora_poll_timer); | ||
972 | #endif | ||
973 | |||
974 | #ifdef AURORA_ALLIRQ | ||
975 | for(i=0;i<AURORA_ALLIRQ;i++){ | ||
976 | free_irq(allirq[i]|0x30, bp); | ||
977 | /* IRQ_to_board[allirq[i]&0xf] = NULL;*/ | ||
978 | } | ||
979 | #else | ||
980 | free_irq(bp->irq|0x30, bp); | ||
981 | /* IRQ_to_board[bp->irq&0xf] = NULL;*/ | ||
982 | #endif | ||
983 | /* Drop all DTR's */ | ||
984 | for(i=0;i<16;i++){ | ||
985 | sbus_writeb(i & 7, &bp->r[i>>3]->r[CD180_CAR]); | ||
986 | udelay(1); | ||
987 | sbus_writeb(0, &bp->r[i>>3]->r[CD180_MSVR]); | ||
988 | udelay(1); | ||
989 | } | ||
990 | /* Board shutdown */ | ||
991 | sbus_writeb(0, &bp->r0->r); | ||
992 | |||
993 | #ifdef AURORA_DEBUG | ||
994 | printk("aurora_shutdown_board: end\n"); | ||
995 | #endif | ||
996 | } | ||
997 | |||
998 | /* Setting up port characteristics. | ||
999 | * Must be called with disabled interrupts | ||
1000 | */ | ||
1001 | static void aurora_change_speed(struct Aurora_board *bp, struct Aurora_port *port) | ||
1002 | { | ||
1003 | struct tty_struct *tty; | ||
1004 | unsigned long baud; | ||
1005 | long tmp; | ||
1006 | unsigned char cor1 = 0, cor3 = 0; | ||
1007 | unsigned char mcor1 = 0, mcor2 = 0,chip; | ||
1008 | |||
1009 | #ifdef AURORA_DEBUG | ||
1010 | printk("aurora_change_speed: start\n"); | ||
1011 | #endif | ||
1012 | if (!(tty = port->tty) || !tty->termios) | ||
1013 | return; | ||
1014 | |||
1015 | chip = AURORA_CD180(port_No(port)); | ||
1016 | |||
1017 | port->SRER = 0; | ||
1018 | port->COR2 = 0; | ||
1019 | port->MSVR = MSVR_RTS|MSVR_DTR; | ||
1020 | |||
1021 | baud = tty_get_baud_rate(tty); | ||
1022 | |||
1023 | /* Select port on the board */ | ||
1024 | sbus_writeb(port_No(port) & 7, | ||
1025 | &bp->r[chip]->r[CD180_CAR]); | ||
1026 | udelay(1); | ||
1027 | |||
1028 | if (!baud) { | ||
1029 | /* Drop DTR & exit */ | ||
1030 | port->MSVR &= ~(bp->DTR|bp->RTS); | ||
1031 | sbus_writeb(port->MSVR, | ||
1032 | &bp->r[chip]->r[CD180_MSVR]); | ||
1033 | return; | ||
1034 | } else { | ||
1035 | /* Set DTR on */ | ||
1036 | port->MSVR |= bp->DTR; | ||
1037 | sbus_writeb(port->MSVR, | ||
1038 | &bp->r[chip]->r[CD180_MSVR]); | ||
1039 | } | ||
1040 | |||
1041 | /* Now we must calculate some speed dependent things. */ | ||
1042 | |||
1043 | /* Set baud rate for port. */ | ||
1044 | tmp = (((bp->oscfreq + baud/2) / baud + | ||
1045 | CD180_TPC/2) / CD180_TPC); | ||
1046 | |||
1047 | /* tmp = (bp->oscfreq/7)/baud; | ||
1048 | if((tmp%10)>4)tmp=tmp/10+1;else tmp=tmp/10;*/ | ||
1049 | /* printk("Prescaler period: %d\n",tmp);*/ | ||
1050 | |||
1051 | sbus_writeb((tmp >> 8) & 0xff, | ||
1052 | &bp->r[chip]->r[CD180_RBPRH]); | ||
1053 | sbus_writeb((tmp >> 8) & 0xff, | ||
1054 | &bp->r[chip]->r[CD180_TBPRH]); | ||
1055 | sbus_writeb(tmp & 0xff, &bp->r[chip]->r[CD180_RBPRL]); | ||
1056 | sbus_writeb(tmp & 0xff, &bp->r[chip]->r[CD180_TBPRL]); | ||
1057 | |||
1058 | baud = (baud + 5) / 10; /* Estimated CPS */ | ||
1059 | |||
1060 | /* Two timer ticks seems enough to wakeup something like SLIP driver */ | ||
1061 | tmp = ((baud + HZ/2) / HZ) * 2 - CD180_NFIFO; | ||
1062 | port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ? | ||
1063 | SERIAL_XMIT_SIZE - 1 : tmp); | ||
1064 | |||
1065 | /* Receiver timeout will be transmission time for 1.5 chars */ | ||
1066 | tmp = (AURORA_TPS + AURORA_TPS/2 + baud/2) / baud; | ||
1067 | tmp = (tmp > 0xff) ? 0xff : tmp; | ||
1068 | sbus_writeb(tmp, &bp->r[chip]->r[CD180_RTPR]); | ||
1069 | |||
1070 | switch (C_CSIZE(tty)) { | ||
1071 | case CS5: | ||
1072 | cor1 |= COR1_5BITS; | ||
1073 | break; | ||
1074 | case CS6: | ||
1075 | cor1 |= COR1_6BITS; | ||
1076 | break; | ||
1077 | case CS7: | ||
1078 | cor1 |= COR1_7BITS; | ||
1079 | break; | ||
1080 | case CS8: | ||
1081 | cor1 |= COR1_8BITS; | ||
1082 | break; | ||
1083 | } | ||
1084 | |||
1085 | if (C_CSTOPB(tty)) | ||
1086 | cor1 |= COR1_2SB; | ||
1087 | |||
1088 | cor1 |= COR1_IGNORE; | ||
1089 | if (C_PARENB(tty)) { | ||
1090 | cor1 |= COR1_NORMPAR; | ||
1091 | if (C_PARODD(tty)) | ||
1092 | cor1 |= COR1_ODDP; | ||
1093 | if (I_INPCK(tty)) | ||
1094 | cor1 &= ~COR1_IGNORE; | ||
1095 | } | ||
1096 | /* Set marking of some errors */ | ||
1097 | port->mark_mask = RCSR_OE | RCSR_TOUT; | ||
1098 | if (I_INPCK(tty)) | ||
1099 | port->mark_mask |= RCSR_FE | RCSR_PE; | ||
1100 | if (I_BRKINT(tty) || I_PARMRK(tty)) | ||
1101 | port->mark_mask |= RCSR_BREAK; | ||
1102 | if (I_IGNPAR(tty)) | ||
1103 | port->mark_mask &= ~(RCSR_FE | RCSR_PE); | ||
1104 | if (I_IGNBRK(tty)) { | ||
1105 | port->mark_mask &= ~RCSR_BREAK; | ||
1106 | if (I_IGNPAR(tty)) | ||
1107 | /* Real raw mode. Ignore all */ | ||
1108 | port->mark_mask &= ~RCSR_OE; | ||
1109 | } | ||
1110 | /* Enable Hardware Flow Control */ | ||
1111 | if (C_CRTSCTS(tty)) { | ||
1112 | /*#ifdef AURORA_BRAIN_DAMAGED_CTS | ||
1113 | port->SRER |= SRER_DSR | SRER_CTS; | ||
1114 | mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; | ||
1115 | mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; | ||
1116 | tty->hw_stopped = !(aurora_in(bp, CD180_MSVR) & (MSVR_CTS|MSVR_DSR)); | ||
1117 | #else*/ | ||
1118 | port->COR2 |= COR2_CTSAE; | ||
1119 | /*#endif*/ | ||
1120 | if (bp->flags&AURORA_BOARD_DTR_FLOW_OK) { | ||
1121 | mcor1 |= AURORA_RXTH; | ||
1122 | } | ||
1123 | } | ||
1124 | /* Enable Software Flow Control. FIXME: I'm not sure about this */ | ||
1125 | /* Some people reported that it works, but I still doubt */ | ||
1126 | if (I_IXON(tty)) { | ||
1127 | port->COR2 |= COR2_TXIBE; | ||
1128 | cor3 |= (COR3_FCT | COR3_SCDE); | ||
1129 | if (I_IXANY(tty)) | ||
1130 | port->COR2 |= COR2_IXM; | ||
1131 | sbus_writeb(START_CHAR(tty), | ||
1132 | &bp->r[chip]->r[CD180_SCHR1]); | ||
1133 | sbus_writeb(STOP_CHAR(tty), | ||
1134 | &bp->r[chip]->r[CD180_SCHR2]); | ||
1135 | sbus_writeb(START_CHAR(tty), | ||
1136 | &bp->r[chip]->r[CD180_SCHR3]); | ||
1137 | sbus_writeb(STOP_CHAR(tty), | ||
1138 | &bp->r[chip]->r[CD180_SCHR4]); | ||
1139 | } | ||
1140 | if (!C_CLOCAL(tty)) { | ||
1141 | /* Enable CD check */ | ||
1142 | port->SRER |= SRER_CD; | ||
1143 | mcor1 |= MCOR1_CDZD; | ||
1144 | mcor2 |= MCOR2_CDOD; | ||
1145 | } | ||
1146 | |||
1147 | if (C_CREAD(tty)) | ||
1148 | /* Enable receiver */ | ||
1149 | port->SRER |= SRER_RXD; | ||
1150 | |||
1151 | /* Set input FIFO size (1-8 bytes) */ | ||
1152 | cor3 |= AURORA_RXFIFO; | ||
1153 | /* Setting up CD180 channel registers */ | ||
1154 | sbus_writeb(cor1, &bp->r[chip]->r[CD180_COR1]); | ||
1155 | sbus_writeb(port->COR2, &bp->r[chip]->r[CD180_COR2]); | ||
1156 | sbus_writeb(cor3, &bp->r[chip]->r[CD180_COR3]); | ||
1157 | /* Make CD180 know about registers change */ | ||
1158 | aurora_wait_CCR(bp->r[chip]); | ||
1159 | sbus_writeb(CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3, | ||
1160 | &bp->r[chip]->r[CD180_CCR]); | ||
1161 | /* Setting up modem option registers */ | ||
1162 | sbus_writeb(mcor1, &bp->r[chip]->r[CD180_MCOR1]); | ||
1163 | sbus_writeb(mcor2, &bp->r[chip]->r[CD180_MCOR2]); | ||
1164 | /* Enable CD180 transmitter & receiver */ | ||
1165 | aurora_wait_CCR(bp->r[chip]); | ||
1166 | sbus_writeb(CCR_TXEN | CCR_RXEN, &bp->r[chip]->r[CD180_CCR]); | ||
1167 | /* Enable interrupts */ | ||
1168 | sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); | ||
1169 | /* And finally set RTS on */ | ||
1170 | sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); | ||
1171 | #ifdef AURORA_DEBUG | ||
1172 | printk("aurora_change_speed: end\n"); | ||
1173 | #endif | ||
1174 | } | ||
1175 | |||
1176 | /* Must be called with interrupts enabled */ | ||
1177 | static int aurora_setup_port(struct Aurora_board *bp, struct Aurora_port *port) | ||
1178 | { | ||
1179 | unsigned long flags; | ||
1180 | |||
1181 | #ifdef AURORA_DEBUG | ||
1182 | printk("aurora_setup_port: start %d\n",port_No(port)); | ||
1183 | #endif | ||
1184 | if (port->flags & ASYNC_INITIALIZED) | ||
1185 | return 0; | ||
1186 | |||
1187 | if (!port->xmit_buf) { | ||
1188 | /* We may sleep in get_zeroed_page() */ | ||
1189 | unsigned long tmp; | ||
1190 | |||
1191 | if (!(tmp = get_zeroed_page(GFP_KERNEL))) | ||
1192 | return -ENOMEM; | ||
1193 | |||
1194 | if (port->xmit_buf) { | ||
1195 | free_page(tmp); | ||
1196 | return -ERESTARTSYS; | ||
1197 | } | ||
1198 | port->xmit_buf = (unsigned char *) tmp; | ||
1199 | } | ||
1200 | |||
1201 | save_flags(flags); cli(); | ||
1202 | |||
1203 | if (port->tty) | ||
1204 | clear_bit(TTY_IO_ERROR, &port->tty->flags); | ||
1205 | |||
1206 | #ifdef MODULE | ||
1207 | if ((port->count == 1) && ((++bp->count) == 1)) | ||
1208 | bp->flags |= AURORA_BOARD_ACTIVE; | ||
1209 | #endif | ||
1210 | |||
1211 | port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; | ||
1212 | aurora_change_speed(bp, port); | ||
1213 | port->flags |= ASYNC_INITIALIZED; | ||
1214 | |||
1215 | restore_flags(flags); | ||
1216 | #ifdef AURORA_DEBUG | ||
1217 | printk("aurora_setup_port: end\n"); | ||
1218 | #endif | ||
1219 | return 0; | ||
1220 | } | ||
1221 | |||
1222 | /* Must be called with interrupts disabled */ | ||
1223 | static void aurora_shutdown_port(struct Aurora_board *bp, struct Aurora_port *port) | ||
1224 | { | ||
1225 | struct tty_struct *tty; | ||
1226 | unsigned char chip; | ||
1227 | |||
1228 | #ifdef AURORA_DEBUG | ||
1229 | printk("aurora_shutdown_port: start\n"); | ||
1230 | #endif | ||
1231 | if (!(port->flags & ASYNC_INITIALIZED)) | ||
1232 | return; | ||
1233 | |||
1234 | chip = AURORA_CD180(port_No(port)); | ||
1235 | |||
1236 | #ifdef AURORA_REPORT_OVERRUN | ||
1237 | printk("aurora%d: port %d: Total %ld overruns were detected.\n", | ||
1238 | board_No(bp), port_No(port), port->overrun); | ||
1239 | #endif | ||
1240 | #ifdef AURORA_REPORT_FIFO | ||
1241 | { | ||
1242 | int i; | ||
1243 | |||
1244 | printk("aurora%d: port %d: FIFO hits [ ", | ||
1245 | board_No(bp), port_No(port)); | ||
1246 | for (i = 0; i < 10; i++) { | ||
1247 | printk("%ld ", port->hits[i]); | ||
1248 | } | ||
1249 | printk("].\n"); | ||
1250 | } | ||
1251 | #endif | ||
1252 | if (port->xmit_buf) { | ||
1253 | free_page((unsigned long) port->xmit_buf); | ||
1254 | port->xmit_buf = NULL; | ||
1255 | } | ||
1256 | |||
1257 | if (!(tty = port->tty) || C_HUPCL(tty)) { | ||
1258 | /* Drop DTR */ | ||
1259 | port->MSVR &= ~(bp->DTR|bp->RTS); | ||
1260 | sbus_writeb(port->MSVR, | ||
1261 | &bp->r[chip]->r[CD180_MSVR]); | ||
1262 | } | ||
1263 | |||
1264 | /* Select port */ | ||
1265 | sbus_writeb(port_No(port) & 7, | ||
1266 | &bp->r[chip]->r[CD180_CAR]); | ||
1267 | udelay(1); | ||
1268 | |||
1269 | /* Reset port */ | ||
1270 | aurora_wait_CCR(bp->r[chip]); | ||
1271 | sbus_writeb(CCR_SOFTRESET, &bp->r[chip]->r[CD180_CCR]); | ||
1272 | |||
1273 | /* Disable all interrupts from this port */ | ||
1274 | port->SRER = 0; | ||
1275 | sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); | ||
1276 | |||
1277 | if (tty) | ||
1278 | set_bit(TTY_IO_ERROR, &tty->flags); | ||
1279 | port->flags &= ~ASYNC_INITIALIZED; | ||
1280 | |||
1281 | #ifdef MODULE | ||
1282 | if (--bp->count < 0) { | ||
1283 | printk(KERN_DEBUG "aurora%d: aurora_shutdown_port: " | ||
1284 | "bad board count: %d\n", | ||
1285 | board_No(bp), bp->count); | ||
1286 | bp->count = 0; | ||
1287 | } | ||
1288 | |||
1289 | if (!bp->count) | ||
1290 | bp->flags &= ~AURORA_BOARD_ACTIVE; | ||
1291 | #endif | ||
1292 | |||
1293 | #ifdef AURORA_DEBUG | ||
1294 | printk("aurora_shutdown_port: end\n"); | ||
1295 | #endif | ||
1296 | } | ||
1297 | |||
1298 | |||
1299 | static int block_til_ready(struct tty_struct *tty, struct file * filp, | ||
1300 | struct Aurora_port *port) | ||
1301 | { | ||
1302 | DECLARE_WAITQUEUE(wait, current); | ||
1303 | struct Aurora_board *bp = port_Board(port); | ||
1304 | int retval; | ||
1305 | int do_clocal = 0; | ||
1306 | int CD; | ||
1307 | unsigned char chip; | ||
1308 | |||
1309 | #ifdef AURORA_DEBUG | ||
1310 | printk("block_til_ready: start\n"); | ||
1311 | #endif | ||
1312 | chip = AURORA_CD180(port_No(port)); | ||
1313 | |||
1314 | /* If the device is in the middle of being closed, then block | ||
1315 | * until it's done, and then try again. | ||
1316 | */ | ||
1317 | if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { | ||
1318 | interruptible_sleep_on(&port->close_wait); | ||
1319 | if (port->flags & ASYNC_HUP_NOTIFY) | ||
1320 | return -EAGAIN; | ||
1321 | else | ||
1322 | return -ERESTARTSYS; | ||
1323 | } | ||
1324 | |||
1325 | /* If non-blocking mode is set, or the port is not enabled, | ||
1326 | * then make the check up front and then exit. | ||
1327 | */ | ||
1328 | if ((filp->f_flags & O_NONBLOCK) || | ||
1329 | (tty->flags & (1 << TTY_IO_ERROR))) { | ||
1330 | port->flags |= ASYNC_NORMAL_ACTIVE; | ||
1331 | return 0; | ||
1332 | } | ||
1333 | |||
1334 | if (C_CLOCAL(tty)) | ||
1335 | do_clocal = 1; | ||
1336 | |||
1337 | /* Block waiting for the carrier detect and the line to become | ||
1338 | * free (i.e., not in use by the callout). While we are in | ||
1339 | * this loop, info->count is dropped by one, so that | ||
1340 | * rs_close() knows when to free things. We restore it upon | ||
1341 | * exit, either normal or abnormal. | ||
1342 | */ | ||
1343 | retval = 0; | ||
1344 | add_wait_queue(&port->open_wait, &wait); | ||
1345 | cli(); | ||
1346 | if (!tty_hung_up_p(filp)) | ||
1347 | port->count--; | ||
1348 | sti(); | ||
1349 | port->blocked_open++; | ||
1350 | while (1) { | ||
1351 | cli(); | ||
1352 | sbus_writeb(port_No(port) & 7, | ||
1353 | &bp->r[chip]->r[CD180_CAR]); | ||
1354 | udelay(1); | ||
1355 | CD = sbus_readb(&bp->r[chip]->r[CD180_MSVR]) & MSVR_CD; | ||
1356 | port->MSVR=bp->RTS; | ||
1357 | |||
1358 | /* auto drops DTR */ | ||
1359 | sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); | ||
1360 | sti(); | ||
1361 | set_current_state(TASK_INTERRUPTIBLE); | ||
1362 | if (tty_hung_up_p(filp) || | ||
1363 | !(port->flags & ASYNC_INITIALIZED)) { | ||
1364 | if (port->flags & ASYNC_HUP_NOTIFY) | ||
1365 | retval = -EAGAIN; | ||
1366 | else | ||
1367 | retval = -ERESTARTSYS; | ||
1368 | break; | ||
1369 | } | ||
1370 | if (!(port->flags & ASYNC_CLOSING) && | ||
1371 | (do_clocal || CD)) | ||
1372 | break; | ||
1373 | if (signal_pending(current)) { | ||
1374 | retval = -ERESTARTSYS; | ||
1375 | break; | ||
1376 | } | ||
1377 | schedule(); | ||
1378 | } | ||
1379 | current->state = TASK_RUNNING; | ||
1380 | remove_wait_queue(&port->open_wait, &wait); | ||
1381 | if (!tty_hung_up_p(filp)) | ||
1382 | port->count++; | ||
1383 | port->blocked_open--; | ||
1384 | if (retval) | ||
1385 | return retval; | ||
1386 | |||
1387 | port->flags |= ASYNC_NORMAL_ACTIVE; | ||
1388 | #ifdef AURORA_DEBUG | ||
1389 | printk("block_til_ready: end\n"); | ||
1390 | #endif | ||
1391 | return 0; | ||
1392 | } | ||
1393 | |||
1394 | static int aurora_open(struct tty_struct * tty, struct file * filp) | ||
1395 | { | ||
1396 | int board; | ||
1397 | int error; | ||
1398 | struct Aurora_port * port; | ||
1399 | struct Aurora_board * bp; | ||
1400 | unsigned long flags; | ||
1401 | |||
1402 | #ifdef AURORA_DEBUG | ||
1403 | printk("aurora_open: start\n"); | ||
1404 | #endif | ||
1405 | |||
1406 | board = AURORA_BOARD(tty->index); | ||
1407 | if (board > AURORA_NBOARD || | ||
1408 | !(aurora_board[board].flags & AURORA_BOARD_PRESENT)) { | ||
1409 | #ifdef AURORA_DEBUG | ||
1410 | printk("aurora_open: error board %d present %d\n", | ||
1411 | board, aurora_board[board].flags & AURORA_BOARD_PRESENT); | ||
1412 | #endif | ||
1413 | return -ENODEV; | ||
1414 | } | ||
1415 | |||
1416 | bp = &aurora_board[board]; | ||
1417 | port = aurora_port + board * AURORA_NPORT * AURORA_NCD180 + AURORA_PORT(tty->index); | ||
1418 | if ((aurora_paranoia_check(port, tty->name, "aurora_open")) { | ||
1419 | #ifdef AURORA_DEBUG | ||
1420 | printk("aurora_open: error paranoia check\n"); | ||
1421 | #endif | ||
1422 | return -ENODEV; | ||
1423 | } | ||
1424 | |||
1425 | port->count++; | ||
1426 | tty->driver_data = port; | ||
1427 | port->tty = tty; | ||
1428 | |||
1429 | if ((error = aurora_setup_port(bp, port))) { | ||
1430 | #ifdef AURORA_DEBUG | ||
1431 | printk("aurora_open: error aurora_setup_port ret %d\n",error); | ||
1432 | #endif | ||
1433 | return error; | ||
1434 | } | ||
1435 | |||
1436 | if ((error = block_til_ready(tty, filp, port))) { | ||
1437 | #ifdef AURORA_DEBUG | ||
1438 | printk("aurora_open: error block_til_ready ret %d\n",error); | ||
1439 | #endif | ||
1440 | return error; | ||
1441 | } | ||
1442 | |||
1443 | #ifdef AURORA_DEBUG | ||
1444 | printk("aurora_open: end\n"); | ||
1445 | #endif | ||
1446 | return 0; | ||
1447 | } | ||
1448 | |||
1449 | static void aurora_close(struct tty_struct * tty, struct file * filp) | ||
1450 | { | ||
1451 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
1452 | struct Aurora_board *bp; | ||
1453 | unsigned long flags; | ||
1454 | unsigned long timeout; | ||
1455 | unsigned char chip; | ||
1456 | |||
1457 | #ifdef AURORA_DEBUG | ||
1458 | printk("aurora_close: start\n"); | ||
1459 | #endif | ||
1460 | |||
1461 | if (!port || (aurora_paranoia_check(port, tty->name, "close")) | ||
1462 | return; | ||
1463 | |||
1464 | chip = AURORA_CD180(port_No(port)); | ||
1465 | |||
1466 | save_flags(flags); cli(); | ||
1467 | if (tty_hung_up_p(filp)) { | ||
1468 | restore_flags(flags); | ||
1469 | return; | ||
1470 | } | ||
1471 | |||
1472 | bp = port_Board(port); | ||
1473 | if ((tty->count == 1) && (port->count != 1)) { | ||
1474 | printk(KERN_DEBUG "aurora%d: aurora_close: bad port count; " | ||
1475 | "tty->count is 1, port count is %d\n", | ||
1476 | board_No(bp), port->count); | ||
1477 | port->count = 1; | ||
1478 | } | ||
1479 | if (--port->count < 0) { | ||
1480 | printk(KERN_DEBUG "aurora%d: aurora_close: bad port " | ||
1481 | "count for tty%d: %d\n", | ||
1482 | board_No(bp), port_No(port), port->count); | ||
1483 | port->count = 0; | ||
1484 | } | ||
1485 | if (port->count) { | ||
1486 | restore_flags(flags); | ||
1487 | return; | ||
1488 | } | ||
1489 | port->flags |= ASYNC_CLOSING; | ||
1490 | |||
1491 | /* Now we wait for the transmit buffer to clear; and we notify | ||
1492 | * the line discipline to only process XON/XOFF characters. | ||
1493 | */ | ||
1494 | tty->closing = 1; | ||
1495 | if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE){ | ||
1496 | #ifdef AURORA_DEBUG | ||
1497 | printk("aurora_close: waiting to flush...\n"); | ||
1498 | #endif | ||
1499 | tty_wait_until_sent(tty, port->closing_wait); | ||
1500 | } | ||
1501 | |||
1502 | /* At this point we stop accepting input. To do this, we | ||
1503 | * disable the receive line status interrupts, and tell the | ||
1504 | * interrupt driver to stop checking the data ready bit in the | ||
1505 | * line status register. | ||
1506 | */ | ||
1507 | port->SRER &= ~SRER_RXD; | ||
1508 | if (port->flags & ASYNC_INITIALIZED) { | ||
1509 | port->SRER &= ~SRER_TXRDY; | ||
1510 | port->SRER |= SRER_TXEMPTY; | ||
1511 | sbus_writeb(port_No(port) & 7, | ||
1512 | &bp->r[chip]->r[CD180_CAR]); | ||
1513 | udelay(1); | ||
1514 | sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); | ||
1515 | /* | ||
1516 | * Before we drop DTR, make sure the UART transmitter | ||
1517 | * has completely drained; this is especially | ||
1518 | * important if there is a transmit FIFO! | ||
1519 | */ | ||
1520 | timeout = jiffies+HZ; | ||
1521 | while(port->SRER & SRER_TXEMPTY) { | ||
1522 | current->state = TASK_INTERRUPTIBLE; | ||
1523 | schedule_timeout(port->timeout); | ||
1524 | if (time_after(jiffies, timeout)) | ||
1525 | break; | ||
1526 | } | ||
1527 | } | ||
1528 | #ifdef AURORA_DEBUG | ||
1529 | printk("aurora_close: shutdown_port\n"); | ||
1530 | #endif | ||
1531 | aurora_shutdown_port(bp, port); | ||
1532 | if (tty->driver->flush_buffer) | ||
1533 | tty->driver->flush_buffer(tty); | ||
1534 | tty_ldisc_flush(tty); | ||
1535 | tty->closing = 0; | ||
1536 | port->event = 0; | ||
1537 | port->tty = 0; | ||
1538 | if (port->blocked_open) { | ||
1539 | if (port->close_delay) { | ||
1540 | current->state = TASK_INTERRUPTIBLE; | ||
1541 | schedule_timeout(port->close_delay); | ||
1542 | } | ||
1543 | wake_up_interruptible(&port->open_wait); | ||
1544 | } | ||
1545 | port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); | ||
1546 | wake_up_interruptible(&port->close_wait); | ||
1547 | restore_flags(flags); | ||
1548 | #ifdef AURORA_DEBUG | ||
1549 | printk("aurora_close: end\n"); | ||
1550 | #endif | ||
1551 | } | ||
1552 | |||
1553 | static int aurora_write(struct tty_struct * tty, | ||
1554 | const unsigned char *buf, int count) | ||
1555 | { | ||
1556 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
1557 | struct Aurora_board *bp; | ||
1558 | int c, total = 0; | ||
1559 | unsigned long flags; | ||
1560 | unsigned char chip; | ||
1561 | |||
1562 | #ifdef AURORA_DEBUG | ||
1563 | printk("aurora_write: start %d\n",count); | ||
1564 | #endif | ||
1565 | if ((aurora_paranoia_check(port, tty->name, "aurora_write")) | ||
1566 | return 0; | ||
1567 | |||
1568 | chip = AURORA_CD180(port_No(port)); | ||
1569 | |||
1570 | bp = port_Board(port); | ||
1571 | |||
1572 | if (!tty || !port->xmit_buf || !tmp_buf) | ||
1573 | return 0; | ||
1574 | |||
1575 | save_flags(flags); | ||
1576 | while (1) { | ||
1577 | cli(); | ||
1578 | c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, | ||
1579 | SERIAL_XMIT_SIZE - port->xmit_head)); | ||
1580 | if (c <= 0) { | ||
1581 | restore_flags(flags); | ||
1582 | break; | ||
1583 | } | ||
1584 | memcpy(port->xmit_buf + port->xmit_head, buf, c); | ||
1585 | port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); | ||
1586 | port->xmit_cnt += c; | ||
1587 | restore_flags(flags); | ||
1588 | |||
1589 | buf += c; | ||
1590 | count -= c; | ||
1591 | total += c; | ||
1592 | } | ||
1593 | |||
1594 | cli(); | ||
1595 | if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && | ||
1596 | !(port->SRER & SRER_TXRDY)) { | ||
1597 | port->SRER |= SRER_TXRDY; | ||
1598 | sbus_writeb(port_No(port) & 7, | ||
1599 | &bp->r[chip]->r[CD180_CAR]); | ||
1600 | udelay(1); | ||
1601 | sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); | ||
1602 | } | ||
1603 | restore_flags(flags); | ||
1604 | #ifdef AURORA_DEBUG | ||
1605 | printk("aurora_write: end %d\n",total); | ||
1606 | #endif | ||
1607 | return total; | ||
1608 | } | ||
1609 | |||
1610 | static void aurora_put_char(struct tty_struct * tty, unsigned char ch) | ||
1611 | { | ||
1612 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
1613 | unsigned long flags; | ||
1614 | |||
1615 | #ifdef AURORA_DEBUG | ||
1616 | printk("aurora_put_char: start %c\n",ch); | ||
1617 | #endif | ||
1618 | if ((aurora_paranoia_check(port, tty->name, "aurora_put_char")) | ||
1619 | return; | ||
1620 | |||
1621 | if (!tty || !port->xmit_buf) | ||
1622 | return; | ||
1623 | |||
1624 | save_flags(flags); cli(); | ||
1625 | |||
1626 | if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { | ||
1627 | restore_flags(flags); | ||
1628 | return; | ||
1629 | } | ||
1630 | |||
1631 | port->xmit_buf[port->xmit_head++] = ch; | ||
1632 | port->xmit_head &= SERIAL_XMIT_SIZE - 1; | ||
1633 | port->xmit_cnt++; | ||
1634 | restore_flags(flags); | ||
1635 | #ifdef AURORA_DEBUG | ||
1636 | printk("aurora_put_char: end\n"); | ||
1637 | #endif | ||
1638 | } | ||
1639 | |||
1640 | static void aurora_flush_chars(struct tty_struct * tty) | ||
1641 | { | ||
1642 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
1643 | unsigned long flags; | ||
1644 | unsigned char chip; | ||
1645 | |||
1646 | /*#ifdef AURORA_DEBUG | ||
1647 | printk("aurora_flush_chars: start\n"); | ||
1648 | #endif*/ | ||
1649 | if ((aurora_paranoia_check(port, tty->name, "aurora_flush_chars")) | ||
1650 | return; | ||
1651 | |||
1652 | chip = AURORA_CD180(port_No(port)); | ||
1653 | |||
1654 | if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || | ||
1655 | !port->xmit_buf) | ||
1656 | return; | ||
1657 | |||
1658 | save_flags(flags); cli(); | ||
1659 | port->SRER |= SRER_TXRDY; | ||
1660 | sbus_writeb(port_No(port) & 7, | ||
1661 | &port_Board(port)->r[chip]->r[CD180_CAR]); | ||
1662 | udelay(1); | ||
1663 | sbus_writeb(port->SRER, | ||
1664 | &port_Board(port)->r[chip]->r[CD180_SRER]); | ||
1665 | restore_flags(flags); | ||
1666 | /*#ifdef AURORA_DEBUG | ||
1667 | printk("aurora_flush_chars: end\n"); | ||
1668 | #endif*/ | ||
1669 | } | ||
1670 | |||
1671 | static int aurora_write_room(struct tty_struct * tty) | ||
1672 | { | ||
1673 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
1674 | int ret; | ||
1675 | |||
1676 | #ifdef AURORA_DEBUG | ||
1677 | printk("aurora_write_room: start\n"); | ||
1678 | #endif | ||
1679 | if ((aurora_paranoia_check(port, tty->name, "aurora_write_room")) | ||
1680 | return 0; | ||
1681 | |||
1682 | ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; | ||
1683 | if (ret < 0) | ||
1684 | ret = 0; | ||
1685 | #ifdef AURORA_DEBUG | ||
1686 | printk("aurora_write_room: end\n"); | ||
1687 | #endif | ||
1688 | return ret; | ||
1689 | } | ||
1690 | |||
1691 | static int aurora_chars_in_buffer(struct tty_struct *tty) | ||
1692 | { | ||
1693 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
1694 | |||
1695 | if ((aurora_paranoia_check(port, tty->name, "aurora_chars_in_buffer")) | ||
1696 | return 0; | ||
1697 | |||
1698 | return port->xmit_cnt; | ||
1699 | } | ||
1700 | |||
1701 | static void aurora_flush_buffer(struct tty_struct *tty) | ||
1702 | { | ||
1703 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
1704 | unsigned long flags; | ||
1705 | |||
1706 | #ifdef AURORA_DEBUG | ||
1707 | printk("aurora_flush_buffer: start\n"); | ||
1708 | #endif | ||
1709 | if ((aurora_paranoia_check(port, tty->name, "aurora_flush_buffer")) | ||
1710 | return; | ||
1711 | |||
1712 | save_flags(flags); cli(); | ||
1713 | port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; | ||
1714 | restore_flags(flags); | ||
1715 | |||
1716 | tty_wakeup(tty); | ||
1717 | #ifdef AURORA_DEBUG | ||
1718 | printk("aurora_flush_buffer: end\n"); | ||
1719 | #endif | ||
1720 | } | ||
1721 | |||
1722 | static int aurora_tiocmget(struct tty_struct *tty, struct file *file) | ||
1723 | { | ||
1724 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
1725 | struct Aurora_board * bp; | ||
1726 | unsigned char status,chip; | ||
1727 | unsigned int result; | ||
1728 | unsigned long flags; | ||
1729 | |||
1730 | #ifdef AURORA_DEBUG | ||
1731 | printk("aurora_get_modem_info: start\n"); | ||
1732 | #endif | ||
1733 | if ((aurora_paranoia_check(port, tty->name, __FUNCTION__)) | ||
1734 | return -ENODEV; | ||
1735 | |||
1736 | chip = AURORA_CD180(port_No(port)); | ||
1737 | |||
1738 | bp = port_Board(port); | ||
1739 | |||
1740 | save_flags(flags); cli(); | ||
1741 | |||
1742 | sbus_writeb(port_No(port) & 7, &bp->r[chip]->r[CD180_CAR]); | ||
1743 | udelay(1); | ||
1744 | |||
1745 | status = sbus_readb(&bp->r[chip]->r[CD180_MSVR]); | ||
1746 | result = 0/*bp->r[chip]->r[AURORA_RI] & (1u << port_No(port)) ? 0 : TIOCM_RNG*/; | ||
1747 | |||
1748 | restore_flags(flags); | ||
1749 | |||
1750 | result |= ((status & bp->RTS) ? TIOCM_RTS : 0) | ||
1751 | | ((status & bp->DTR) ? TIOCM_DTR : 0) | ||
1752 | | ((status & MSVR_CD) ? TIOCM_CAR : 0) | ||
1753 | | ((status & MSVR_DSR) ? TIOCM_DSR : 0) | ||
1754 | | ((status & MSVR_CTS) ? TIOCM_CTS : 0); | ||
1755 | |||
1756 | #ifdef AURORA_DEBUG | ||
1757 | printk("aurora_get_modem_info: end\n"); | ||
1758 | #endif | ||
1759 | return result; | ||
1760 | } | ||
1761 | |||
1762 | static int aurora_tiocmset(struct tty_struct *tty, struct file *file, | ||
1763 | unsigned int set, unsigned int clear) | ||
1764 | { | ||
1765 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
1766 | unsigned int arg; | ||
1767 | unsigned long flags; | ||
1768 | struct Aurora_board *bp = port_Board(port); | ||
1769 | unsigned char chip; | ||
1770 | |||
1771 | #ifdef AURORA_DEBUG | ||
1772 | printk("aurora_set_modem_info: start\n"); | ||
1773 | #endif | ||
1774 | if ((aurora_paranoia_check(port, tty->name, __FUNCTION__)) | ||
1775 | return -ENODEV; | ||
1776 | |||
1777 | chip = AURORA_CD180(port_No(port)); | ||
1778 | |||
1779 | save_flags(flags); cli(); | ||
1780 | if (set & TIOCM_RTS) | ||
1781 | port->MSVR |= bp->RTS; | ||
1782 | if (set & TIOCM_DTR) | ||
1783 | port->MSVR |= bp->DTR; | ||
1784 | if (clear & TIOCM_RTS) | ||
1785 | port->MSVR &= ~bp->RTS; | ||
1786 | if (clear & TIOCM_DTR) | ||
1787 | port->MSVR &= ~bp->DTR; | ||
1788 | |||
1789 | sbus_writeb(port_No(port) & 7, &bp->r[chip]->r[CD180_CAR]); | ||
1790 | udelay(1); | ||
1791 | |||
1792 | sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); | ||
1793 | |||
1794 | restore_flags(flags); | ||
1795 | #ifdef AURORA_DEBUG | ||
1796 | printk("aurora_set_modem_info: end\n"); | ||
1797 | #endif | ||
1798 | return 0; | ||
1799 | } | ||
1800 | |||
1801 | static void aurora_send_break(struct Aurora_port * port, unsigned long length) | ||
1802 | { | ||
1803 | struct Aurora_board *bp = port_Board(port); | ||
1804 | unsigned long flags; | ||
1805 | unsigned char chip; | ||
1806 | |||
1807 | #ifdef AURORA_DEBUG | ||
1808 | printk("aurora_send_break: start\n"); | ||
1809 | #endif | ||
1810 | chip = AURORA_CD180(port_No(port)); | ||
1811 | |||
1812 | save_flags(flags); cli(); | ||
1813 | |||
1814 | port->break_length = AURORA_TPS / HZ * length; | ||
1815 | port->COR2 |= COR2_ETC; | ||
1816 | port->SRER |= SRER_TXRDY; | ||
1817 | sbus_writeb(port_No(port) & 7, &bp->r[chip]->r[CD180_CAR]); | ||
1818 | udelay(1); | ||
1819 | |||
1820 | sbus_writeb(port->COR2, &bp->r[chip]->r[CD180_COR2]); | ||
1821 | sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); | ||
1822 | aurora_wait_CCR(bp->r[chip]); | ||
1823 | |||
1824 | sbus_writeb(CCR_CORCHG2, &bp->r[chip]->r[CD180_CCR]); | ||
1825 | aurora_wait_CCR(bp->r[chip]); | ||
1826 | |||
1827 | restore_flags(flags); | ||
1828 | #ifdef AURORA_DEBUG | ||
1829 | printk("aurora_send_break: end\n"); | ||
1830 | #endif | ||
1831 | } | ||
1832 | |||
1833 | static int aurora_set_serial_info(struct Aurora_port * port, | ||
1834 | struct serial_struct * newinfo) | ||
1835 | { | ||
1836 | struct serial_struct tmp; | ||
1837 | struct Aurora_board *bp = port_Board(port); | ||
1838 | int change_speed; | ||
1839 | unsigned long flags; | ||
1840 | |||
1841 | #ifdef AURORA_DEBUG | ||
1842 | printk("aurora_set_serial_info: start\n"); | ||
1843 | #endif | ||
1844 | if (copy_from_user(&tmp, newinfo, sizeof(tmp))) | ||
1845 | return -EFAULT; | ||
1846 | #if 0 | ||
1847 | if ((tmp.irq != bp->irq) || | ||
1848 | (tmp.port != bp->base) || | ||
1849 | (tmp.type != PORT_CIRRUS) || | ||
1850 | (tmp.baud_base != (bp->oscfreq + CD180_TPC/2) / CD180_TPC) || | ||
1851 | (tmp.custom_divisor != 0) || | ||
1852 | (tmp.xmit_fifo_size != CD180_NFIFO) || | ||
1853 | (tmp.flags & ~AURORA_LEGAL_FLAGS)) | ||
1854 | return -EINVAL; | ||
1855 | #endif | ||
1856 | |||
1857 | change_speed = ((port->flags & ASYNC_SPD_MASK) != | ||
1858 | (tmp.flags & ASYNC_SPD_MASK)); | ||
1859 | |||
1860 | if (!capable(CAP_SYS_ADMIN)) { | ||
1861 | if ((tmp.close_delay != port->close_delay) || | ||
1862 | (tmp.closing_wait != port->closing_wait) || | ||
1863 | ((tmp.flags & ~ASYNC_USR_MASK) != | ||
1864 | (port->flags & ~ASYNC_USR_MASK))) | ||
1865 | return -EPERM; | ||
1866 | port->flags = ((port->flags & ~ASYNC_USR_MASK) | | ||
1867 | (tmp.flags & ASYNC_USR_MASK)); | ||
1868 | } else { | ||
1869 | port->flags = ((port->flags & ~ASYNC_FLAGS) | | ||
1870 | (tmp.flags & ASYNC_FLAGS)); | ||
1871 | port->close_delay = tmp.close_delay; | ||
1872 | port->closing_wait = tmp.closing_wait; | ||
1873 | } | ||
1874 | if (change_speed) { | ||
1875 | save_flags(flags); cli(); | ||
1876 | aurora_change_speed(bp, port); | ||
1877 | restore_flags(flags); | ||
1878 | } | ||
1879 | #ifdef AURORA_DEBUG | ||
1880 | printk("aurora_set_serial_info: end\n"); | ||
1881 | #endif | ||
1882 | return 0; | ||
1883 | } | ||
1884 | |||
1885 | extern int aurora_get_serial_info(struct Aurora_port * port, | ||
1886 | struct serial_struct * retinfo) | ||
1887 | { | ||
1888 | struct serial_struct tmp; | ||
1889 | struct Aurora_board *bp = port_Board(port); | ||
1890 | |||
1891 | #ifdef AURORA_DEBUG | ||
1892 | printk("aurora_get_serial_info: start\n"); | ||
1893 | #endif | ||
1894 | if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp))) | ||
1895 | return -EFAULT; | ||
1896 | |||
1897 | memset(&tmp, 0, sizeof(tmp)); | ||
1898 | tmp.type = PORT_CIRRUS; | ||
1899 | tmp.line = port - aurora_port; | ||
1900 | tmp.port = 0; | ||
1901 | tmp.irq = bp->irq; | ||
1902 | tmp.flags = port->flags; | ||
1903 | tmp.baud_base = (bp->oscfreq + CD180_TPC/2) / CD180_TPC; | ||
1904 | tmp.close_delay = port->close_delay * HZ/100; | ||
1905 | tmp.closing_wait = port->closing_wait * HZ/100; | ||
1906 | tmp.xmit_fifo_size = CD180_NFIFO; | ||
1907 | copy_to_user(retinfo, &tmp, sizeof(tmp)); | ||
1908 | #ifdef AURORA_DEBUG | ||
1909 | printk("aurora_get_serial_info: end\n"); | ||
1910 | #endif | ||
1911 | return 0; | ||
1912 | } | ||
1913 | |||
1914 | static int aurora_ioctl(struct tty_struct * tty, struct file * filp, | ||
1915 | unsigned int cmd, unsigned long arg) | ||
1916 | |||
1917 | { | ||
1918 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
1919 | int retval; | ||
1920 | |||
1921 | #ifdef AURORA_DEBUG | ||
1922 | printk("aurora_ioctl: start\n"); | ||
1923 | #endif | ||
1924 | if ((aurora_paranoia_check(port, tty->name, "aurora_ioctl")) | ||
1925 | return -ENODEV; | ||
1926 | |||
1927 | switch (cmd) { | ||
1928 | case TCSBRK: /* SVID version: non-zero arg --> no break */ | ||
1929 | retval = tty_check_change(tty); | ||
1930 | if (retval) | ||
1931 | return retval; | ||
1932 | tty_wait_until_sent(tty, 0); | ||
1933 | if (!arg) | ||
1934 | aurora_send_break(port, HZ/4); /* 1/4 second */ | ||
1935 | return 0; | ||
1936 | case TCSBRKP: /* support for POSIX tcsendbreak() */ | ||
1937 | retval = tty_check_change(tty); | ||
1938 | if (retval) | ||
1939 | return retval; | ||
1940 | tty_wait_until_sent(tty, 0); | ||
1941 | aurora_send_break(port, arg ? arg*(HZ/10) : HZ/4); | ||
1942 | return 0; | ||
1943 | case TIOCGSOFTCAR: | ||
1944 | return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg); | ||
1945 | case TIOCSSOFTCAR: | ||
1946 | if (get_user(arg,(unsigned long *)arg)) | ||
1947 | return -EFAULT; | ||
1948 | tty->termios->c_cflag = | ||
1949 | ((tty->termios->c_cflag & ~CLOCAL) | | ||
1950 | (arg ? CLOCAL : 0)); | ||
1951 | return 0; | ||
1952 | case TIOCGSERIAL: | ||
1953 | return aurora_get_serial_info(port, (struct serial_struct *) arg); | ||
1954 | case TIOCSSERIAL: | ||
1955 | return aurora_set_serial_info(port, (struct serial_struct *) arg); | ||
1956 | default: | ||
1957 | return -ENOIOCTLCMD; | ||
1958 | }; | ||
1959 | #ifdef AURORA_DEBUG | ||
1960 | printk("aurora_ioctl: end\n"); | ||
1961 | #endif | ||
1962 | return 0; | ||
1963 | } | ||
1964 | |||
1965 | static void aurora_throttle(struct tty_struct * tty) | ||
1966 | { | ||
1967 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
1968 | struct Aurora_board *bp; | ||
1969 | unsigned long flags; | ||
1970 | unsigned char chip; | ||
1971 | |||
1972 | #ifdef AURORA_DEBUG | ||
1973 | printk("aurora_throttle: start\n"); | ||
1974 | #endif | ||
1975 | if ((aurora_paranoia_check(port, tty->name, "aurora_throttle")) | ||
1976 | return; | ||
1977 | |||
1978 | bp = port_Board(port); | ||
1979 | chip = AURORA_CD180(port_No(port)); | ||
1980 | |||
1981 | save_flags(flags); cli(); | ||
1982 | port->MSVR &= ~bp->RTS; | ||
1983 | sbus_writeb(port_No(port) & 7, &bp->r[chip]->r[CD180_CAR]); | ||
1984 | udelay(1); | ||
1985 | if (I_IXOFF(tty)) { | ||
1986 | aurora_wait_CCR(bp->r[chip]); | ||
1987 | sbus_writeb(CCR_SSCH2, &bp->r[chip]->r[CD180_CCR]); | ||
1988 | aurora_wait_CCR(bp->r[chip]); | ||
1989 | } | ||
1990 | sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); | ||
1991 | restore_flags(flags); | ||
1992 | #ifdef AURORA_DEBUG | ||
1993 | printk("aurora_throttle: end\n"); | ||
1994 | #endif | ||
1995 | } | ||
1996 | |||
1997 | static void aurora_unthrottle(struct tty_struct * tty) | ||
1998 | { | ||
1999 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
2000 | struct Aurora_board *bp; | ||
2001 | unsigned long flags; | ||
2002 | unsigned char chip; | ||
2003 | |||
2004 | #ifdef AURORA_DEBUG | ||
2005 | printk("aurora_unthrottle: start\n"); | ||
2006 | #endif | ||
2007 | if ((aurora_paranoia_check(port, tty->name, "aurora_unthrottle")) | ||
2008 | return; | ||
2009 | |||
2010 | bp = port_Board(port); | ||
2011 | |||
2012 | chip = AURORA_CD180(port_No(port)); | ||
2013 | |||
2014 | save_flags(flags); cli(); | ||
2015 | port->MSVR |= bp->RTS; | ||
2016 | sbus_writeb(port_No(port) & 7, | ||
2017 | &bp->r[chip]->r[CD180_CAR]); | ||
2018 | udelay(1); | ||
2019 | if (I_IXOFF(tty)) { | ||
2020 | aurora_wait_CCR(bp->r[chip]); | ||
2021 | sbus_writeb(CCR_SSCH1, | ||
2022 | &bp->r[chip]->r[CD180_CCR]); | ||
2023 | aurora_wait_CCR(bp->r[chip]); | ||
2024 | } | ||
2025 | sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); | ||
2026 | restore_flags(flags); | ||
2027 | #ifdef AURORA_DEBUG | ||
2028 | printk("aurora_unthrottle: end\n"); | ||
2029 | #endif | ||
2030 | } | ||
2031 | |||
2032 | static void aurora_stop(struct tty_struct * tty) | ||
2033 | { | ||
2034 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
2035 | struct Aurora_board *bp; | ||
2036 | unsigned long flags; | ||
2037 | unsigned char chip; | ||
2038 | |||
2039 | #ifdef AURORA_DEBUG | ||
2040 | printk("aurora_stop: start\n"); | ||
2041 | #endif | ||
2042 | if ((aurora_paranoia_check(port, tty->name, "aurora_stop")) | ||
2043 | return; | ||
2044 | |||
2045 | bp = port_Board(port); | ||
2046 | |||
2047 | chip = AURORA_CD180(port_No(port)); | ||
2048 | |||
2049 | save_flags(flags); cli(); | ||
2050 | port->SRER &= ~SRER_TXRDY; | ||
2051 | sbus_writeb(port_No(port) & 7, | ||
2052 | &bp->r[chip]->r[CD180_CAR]); | ||
2053 | udelay(1); | ||
2054 | sbus_writeb(port->SRER, | ||
2055 | &bp->r[chip]->r[CD180_SRER]); | ||
2056 | restore_flags(flags); | ||
2057 | #ifdef AURORA_DEBUG | ||
2058 | printk("aurora_stop: end\n"); | ||
2059 | #endif | ||
2060 | } | ||
2061 | |||
2062 | static void aurora_start(struct tty_struct * tty) | ||
2063 | { | ||
2064 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
2065 | struct Aurora_board *bp; | ||
2066 | unsigned long flags; | ||
2067 | unsigned char chip; | ||
2068 | |||
2069 | #ifdef AURORA_DEBUG | ||
2070 | printk("aurora_start: start\n"); | ||
2071 | #endif | ||
2072 | if ((aurora_paranoia_check(port, tty->name, "aurora_start")) | ||
2073 | return; | ||
2074 | |||
2075 | bp = port_Board(port); | ||
2076 | |||
2077 | chip = AURORA_CD180(port_No(port)); | ||
2078 | |||
2079 | save_flags(flags); cli(); | ||
2080 | if (port->xmit_cnt && port->xmit_buf && !(port->SRER & SRER_TXRDY)) { | ||
2081 | port->SRER |= SRER_TXRDY; | ||
2082 | sbus_writeb(port_No(port) & 7, | ||
2083 | &bp->r[chip]->r[CD180_CAR]); | ||
2084 | udelay(1); | ||
2085 | sbus_writeb(port->SRER, | ||
2086 | &bp->r[chip]->r[CD180_SRER]); | ||
2087 | } | ||
2088 | restore_flags(flags); | ||
2089 | #ifdef AURORA_DEBUG | ||
2090 | printk("aurora_start: end\n"); | ||
2091 | #endif | ||
2092 | } | ||
2093 | |||
2094 | /* | ||
2095 | * This routine is called from the scheduler tqueue when the interrupt | ||
2096 | * routine has signalled that a hangup has occurred. The path of | ||
2097 | * hangup processing is: | ||
2098 | * | ||
2099 | * serial interrupt routine -> (scheduler tqueue) -> | ||
2100 | * do_aurora_hangup() -> tty->hangup() -> aurora_hangup() | ||
2101 | * | ||
2102 | */ | ||
2103 | static void do_aurora_hangup(void *private_) | ||
2104 | { | ||
2105 | struct Aurora_port *port = (struct Aurora_port *) private_; | ||
2106 | struct tty_struct *tty; | ||
2107 | |||
2108 | #ifdef AURORA_DEBUG | ||
2109 | printk("do_aurora_hangup: start\n"); | ||
2110 | #endif | ||
2111 | tty = port->tty; | ||
2112 | if (tty != NULL) { | ||
2113 | tty_hangup(tty); /* FIXME: module removal race - AKPM */ | ||
2114 | #ifdef AURORA_DEBUG | ||
2115 | printk("do_aurora_hangup: end\n"); | ||
2116 | #endif | ||
2117 | } | ||
2118 | } | ||
2119 | |||
2120 | static void aurora_hangup(struct tty_struct * tty) | ||
2121 | { | ||
2122 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
2123 | struct Aurora_board *bp; | ||
2124 | |||
2125 | #ifdef AURORA_DEBUG | ||
2126 | printk("aurora_hangup: start\n"); | ||
2127 | #endif | ||
2128 | if ((aurora_paranoia_check(port, tty->name, "aurora_hangup")) | ||
2129 | return; | ||
2130 | |||
2131 | bp = port_Board(port); | ||
2132 | |||
2133 | aurora_shutdown_port(bp, port); | ||
2134 | port->event = 0; | ||
2135 | port->count = 0; | ||
2136 | port->flags &= ~ASYNC_NORMAL_ACTIVE; | ||
2137 | port->tty = 0; | ||
2138 | wake_up_interruptible(&port->open_wait); | ||
2139 | #ifdef AURORA_DEBUG | ||
2140 | printk("aurora_hangup: end\n"); | ||
2141 | #endif | ||
2142 | } | ||
2143 | |||
2144 | static void aurora_set_termios(struct tty_struct * tty, struct termios * old_termios) | ||
2145 | { | ||
2146 | struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; | ||
2147 | unsigned long flags; | ||
2148 | |||
2149 | #ifdef AURORA_DEBUG | ||
2150 | printk("aurora_set_termios: start\n"); | ||
2151 | #endif | ||
2152 | if ((aurora_paranoia_check(port, tty->name, "aurora_set_termios")) | ||
2153 | return; | ||
2154 | |||
2155 | if (tty->termios->c_cflag == old_termios->c_cflag && | ||
2156 | tty->termios->c_iflag == old_termios->c_iflag) | ||
2157 | return; | ||
2158 | |||
2159 | save_flags(flags); cli(); | ||
2160 | aurora_change_speed(port_Board(port), port); | ||
2161 | restore_flags(flags); | ||
2162 | |||
2163 | if ((old_termios->c_cflag & CRTSCTS) && | ||
2164 | !(tty->termios->c_cflag & CRTSCTS)) { | ||
2165 | tty->hw_stopped = 0; | ||
2166 | aurora_start(tty); | ||
2167 | } | ||
2168 | #ifdef AURORA_DEBUG | ||
2169 | printk("aurora_set_termios: end\n"); | ||
2170 | #endif | ||
2171 | } | ||
2172 | |||
2173 | static void do_aurora_bh(void) | ||
2174 | { | ||
2175 | run_task_queue(&tq_aurora); | ||
2176 | } | ||
2177 | |||
2178 | static void do_softint(void *private_) | ||
2179 | { | ||
2180 | struct Aurora_port *port = (struct Aurora_port *) private_; | ||
2181 | struct tty_struct *tty; | ||
2182 | |||
2183 | #ifdef AURORA_DEBUG | ||
2184 | printk("do_softint: start\n"); | ||
2185 | #endif | ||
2186 | tty = port->tty; | ||
2187 | if (tty == NULL) | ||
2188 | return; | ||
2189 | |||
2190 | if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { | ||
2191 | tty_wakeup(tty); | ||
2192 | } | ||
2193 | #ifdef AURORA_DEBUG | ||
2194 | printk("do_softint: end\n"); | ||
2195 | #endif | ||
2196 | } | ||
2197 | |||
2198 | static struct tty_operations aurora_ops = { | ||
2199 | .open = aurora_open, | ||
2200 | .close = aurora_close, | ||
2201 | .write = aurora_write, | ||
2202 | .put_char = aurora_put_char, | ||
2203 | .flush_chars = aurora_flush_chars, | ||
2204 | .write_room = aurora_write_room, | ||
2205 | .chars_in_buffer = aurora_chars_in_buffer, | ||
2206 | .flush_buffer = aurora_flush_buffer, | ||
2207 | .ioctl = aurora_ioctl, | ||
2208 | .throttle = aurora_throttle, | ||
2209 | .unthrottle = aurora_unthrottle, | ||
2210 | .set_termios = aurora_set_termios, | ||
2211 | .stop = aurora_stop, | ||
2212 | .start = aurora_start, | ||
2213 | .hangup = aurora_hangup, | ||
2214 | .tiocmget = aurora_tiocmget, | ||
2215 | .tiocmset = aurora_tiocmset, | ||
2216 | }; | ||
2217 | |||
2218 | static int aurora_init_drivers(void) | ||
2219 | { | ||
2220 | int error; | ||
2221 | int i; | ||
2222 | |||
2223 | #ifdef AURORA_DEBUG | ||
2224 | printk("aurora_init_drivers: start\n"); | ||
2225 | #endif | ||
2226 | tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL); | ||
2227 | if (tmp_buf == NULL) { | ||
2228 | printk(KERN_ERR "aurora: Couldn't get free page.\n"); | ||
2229 | return 1; | ||
2230 | } | ||
2231 | init_bh(AURORA_BH, do_aurora_bh); | ||
2232 | aurora_driver = alloc_tty_driver(AURORA_INPORTS); | ||
2233 | if (!aurora_driver) { | ||
2234 | printk(KERN_ERR "aurora: Couldn't allocate tty driver.\n"); | ||
2235 | free_page((unsigned long) tmp_buf); | ||
2236 | return 1; | ||
2237 | } | ||
2238 | aurora_driver->owner = THIS_MODULE; | ||
2239 | aurora_driver->name = "ttyA"; | ||
2240 | aurora_driver->major = AURORA_MAJOR; | ||
2241 | aurora_driver->type = TTY_DRIVER_TYPE_SERIAL; | ||
2242 | aurora_driver->subtype = SERIAL_TYPE_NORMAL; | ||
2243 | aurora_driver->init_termios = tty_std_termios; | ||
2244 | aurora_driver->init_termios.c_cflag = | ||
2245 | B9600 | CS8 | CREAD | HUPCL | CLOCAL; | ||
2246 | aurora_driver->flags = TTY_DRIVER_REAL_RAW; | ||
2247 | tty_set_operations(aurora_driver, &aurora_ops); | ||
2248 | error = tty_register_driver(aurora_driver); | ||
2249 | if (error) { | ||
2250 | put_tty_driver(aurora_driver); | ||
2251 | free_page((unsigned long) tmp_buf); | ||
2252 | printk(KERN_ERR "aurora: Couldn't register aurora driver, error = %d\n", | ||
2253 | error); | ||
2254 | return 1; | ||
2255 | } | ||
2256 | |||
2257 | memset(aurora_port, 0, sizeof(aurora_port)); | ||
2258 | for (i = 0; i < AURORA_TNPORTS; i++) { | ||
2259 | aurora_port[i].magic = AURORA_MAGIC; | ||
2260 | aurora_port[i].tqueue.routine = do_softint; | ||
2261 | aurora_port[i].tqueue.data = &aurora_port[i]; | ||
2262 | aurora_port[i].tqueue_hangup.routine = do_aurora_hangup; | ||
2263 | aurora_port[i].tqueue_hangup.data = &aurora_port[i]; | ||
2264 | aurora_port[i].close_delay = 50 * HZ/100; | ||
2265 | aurora_port[i].closing_wait = 3000 * HZ/100; | ||
2266 | init_waitqueue_head(&aurora_port[i].open_wait); | ||
2267 | init_waitqueue_head(&aurora_port[i].close_wait); | ||
2268 | } | ||
2269 | #ifdef AURORA_DEBUG | ||
2270 | printk("aurora_init_drivers: end\n"); | ||
2271 | #endif | ||
2272 | return 0; | ||
2273 | } | ||
2274 | |||
2275 | static void aurora_release_drivers(void) | ||
2276 | { | ||
2277 | #ifdef AURORA_DEBUG | ||
2278 | printk("aurora_release_drivers: start\n"); | ||
2279 | #endif | ||
2280 | free_page((unsigned long)tmp_buf); | ||
2281 | tty_unregister_driver(aurora_driver); | ||
2282 | put_tty_driver(aurora_driver); | ||
2283 | #ifdef AURORA_DEBUG | ||
2284 | printk("aurora_release_drivers: end\n"); | ||
2285 | #endif | ||
2286 | } | ||
2287 | |||
2288 | /* | ||
2289 | * Called at boot time. | ||
2290 | * | ||
2291 | * You can specify IO base for up to RC_NBOARD cards, | ||
2292 | * using line "riscom8=0xiobase1,0xiobase2,.." at LILO prompt. | ||
2293 | * Note that there will be no probing at default | ||
2294 | * addresses in this case. | ||
2295 | * | ||
2296 | */ | ||
2297 | void __init aurora_setup(char *str, int *ints) | ||
2298 | { | ||
2299 | int i; | ||
2300 | |||
2301 | for(i=0;(i<ints[0])&&(i<4);i++) { | ||
2302 | if (ints[i+1]) irqs[i]=ints[i+1]; | ||
2303 | } | ||
2304 | } | ||
2305 | |||
2306 | static int __init aurora_real_init(void) | ||
2307 | { | ||
2308 | int found; | ||
2309 | int i; | ||
2310 | |||
2311 | printk(KERN_INFO "aurora: Driver starting.\n"); | ||
2312 | if(aurora_init_drivers()) | ||
2313 | return -EIO; | ||
2314 | found = aurora_probe(); | ||
2315 | if(!found) { | ||
2316 | aurora_release_drivers(); | ||
2317 | printk(KERN_INFO "aurora: No Aurora Multiport boards detected.\n"); | ||
2318 | return -EIO; | ||
2319 | } else { | ||
2320 | printk(KERN_INFO "aurora: %d boards found.\n", found); | ||
2321 | } | ||
2322 | for (i = 0; i < found; i++) { | ||
2323 | int ret = aurora_setup_board(&aurora_board[i]); | ||
2324 | |||
2325 | if (ret) { | ||
2326 | #ifdef AURORA_DEBUG | ||
2327 | printk(KERN_ERR "aurora_init: error aurora_setup_board ret %d\n", | ||
2328 | ret); | ||
2329 | #endif | ||
2330 | return ret; | ||
2331 | } | ||
2332 | } | ||
2333 | return 0; | ||
2334 | } | ||
2335 | |||
2336 | int irq = 0; | ||
2337 | int irq1 = 0; | ||
2338 | int irq2 = 0; | ||
2339 | int irq3 = 0; | ||
2340 | module_param(irq , int, 0); | ||
2341 | module_param(irq1, int, 0); | ||
2342 | module_param(irq2, int, 0); | ||
2343 | module_param(irq3, int, 0); | ||
2344 | |||
2345 | static int __init aurora_init(void) | ||
2346 | { | ||
2347 | if (irq ) irqs[0]=irq ; | ||
2348 | if (irq1) irqs[1]=irq1; | ||
2349 | if (irq2) irqs[2]=irq2; | ||
2350 | if (irq3) irqs[3]=irq3; | ||
2351 | return aurora_real_init(); | ||
2352 | } | ||
2353 | |||
2354 | static void __exit aurora_cleanup(void) | ||
2355 | { | ||
2356 | int i; | ||
2357 | |||
2358 | #ifdef AURORA_DEBUG | ||
2359 | printk("cleanup_module: aurora_release_drivers\n"); | ||
2360 | #endif | ||
2361 | |||
2362 | aurora_release_drivers(); | ||
2363 | for (i = 0; i < AURORA_NBOARD; i++) | ||
2364 | if (aurora_board[i].flags & AURORA_BOARD_PRESENT) { | ||
2365 | aurora_shutdown_board(&aurora_board[i]); | ||
2366 | aurora_release_io_range(&aurora_board[i]); | ||
2367 | } | ||
2368 | } | ||
2369 | |||
2370 | module_init(aurora_init); | ||
2371 | module_exit(aurora_cleanup); | ||
2372 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/sbus/char/aurora.h b/drivers/sbus/char/aurora.h new file mode 100644 index 00000000000..b8b5476d986 --- /dev/null +++ b/drivers/sbus/char/aurora.h | |||
@@ -0,0 +1,276 @@ | |||
1 | /* $Id: aurora.h,v 1.6 2001/06/05 12:23:38 davem Exp $ | ||
2 | * linux/drivers/sbus/char/aurora.h -- Aurora multiport driver | ||
3 | * | ||
4 | * Copyright (c) 1999 by Oliver Aldulea (oli@bv.ro) | ||
5 | * | ||
6 | * This code is based on the RISCom/8 multiport serial driver written | ||
7 | * by Dmitry Gorodchanin (pgmdsg@ibi.com), based on the Linux serial | ||
8 | * driver, written by Linus Torvalds, Theodore T'so and others. | ||
9 | * The Aurora multiport programming info was obtained mainly from the | ||
10 | * Cirrus Logic CD180 documentation (available on the web), and by | ||
11 | * doing heavy tests on the board. Many thanks to Eddie C. Dost for the | ||
12 | * help on the sbus interface. | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, write to the Free Software | ||
26 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
27 | * | ||
28 | * Revision 1.0 | ||
29 | * | ||
30 | * This is the first public release. | ||
31 | * | ||
32 | * This version needs a lot of feedback. This is the version that works | ||
33 | * with _my_ board. My board is model 1600se, revision '@(#)1600se.fth | ||
34 | * 1.2 3/28/95 1'. The driver might work with your board, but I do not | ||
35 | * guarantee it. If you have _any_ type of board, I need to know if the | ||
36 | * driver works or not, I need to know exactly your board parameters | ||
37 | * (get them with 'cd /proc/openprom/iommu/sbus/sio16/; ls *; cat *') | ||
38 | * Also, I need your board revision code, which is written on the board. | ||
39 | * Send me the output of my driver too (it outputs through klogd). | ||
40 | * | ||
41 | * If the driver does not work, you can try enabling the debug options | ||
42 | * to see what's wrong or what should be done. | ||
43 | * | ||
44 | * I'm sorry about the alignment of the code. It was written in a | ||
45 | * 128x48 environment. | ||
46 | * | ||
47 | * I must say that I do not like Aurora Technologies' policy. I asked | ||
48 | * them to help me do this driver faster, but they ended by something | ||
49 | * like "don't call us, we'll call you", and I never heard anything | ||
50 | * from them. They told me "knowing the way the board works, I don't | ||
51 | * doubt you and others on the net will make the driver." | ||
52 | * The truth about this board is that it has nothing intelligent on it. | ||
53 | * If you want to say to somebody what kind of board you have, say that | ||
54 | * it uses Cirrus Logic processors (CD180). The power of the board is | ||
55 | * in those two chips. The rest of the board is the interface to the | ||
56 | * sbus and to the peripherals. Still, they did something smart: they | ||
57 | * reversed DTR and RTS to make on-board automatic hardware flow | ||
58 | * control usable. | ||
59 | * Thanks to Aurora Technologies for wasting my time, nerves and money. | ||
60 | */ | ||
61 | |||
62 | #ifndef __LINUX_AURORA_H | ||
63 | #define __LINUX_AURORA_H | ||
64 | |||
65 | #include <linux/serial.h> | ||
66 | #include <linux/serialP.h> | ||
67 | |||
68 | #ifdef __KERNEL__ | ||
69 | |||
70 | /* This is the number of boards to support. I've only tested this driver with | ||
71 | * one board, so it might not work. | ||
72 | */ | ||
73 | #define AURORA_NBOARD 1 | ||
74 | |||
75 | /* Useful ? Yes. But you can safely comment the warnings if they annoy you | ||
76 | * (let me say that again: the warnings in the code, not this define). | ||
77 | */ | ||
78 | #define AURORA_PARANOIA_CHECK | ||
79 | |||
80 | /* Well, after many lost nights, I found that the IRQ for this board is | ||
81 | * selected from four built-in values by writing some bits in the | ||
82 | * configuration register. This causes a little problem to occur: which | ||
83 | * IRQ to select ? Which one is the best for the user ? Well, I finally | ||
84 | * decided for the following algorithm: if the "bintr" value is not acceptable | ||
85 | * (not within type_1_irq[], then test the "intr" value, if that fails too, | ||
86 | * try each value from type_1_irq until succeded. Hope it's ok. | ||
87 | * You can safely reorder the irq's. | ||
88 | */ | ||
89 | #define TYPE_1_IRQS 4 | ||
90 | unsigned char type_1_irq[TYPE_1_IRQS] = { | ||
91 | 3, 5, 9, 13 | ||
92 | }; | ||
93 | /* I know something about another method of interrupt setting, but not enough. | ||
94 | * Also, this is for another type of board, so I first have to learn how to | ||
95 | * detect it. | ||
96 | #define TYPE_2_IRQS 3 | ||
97 | unsigned char type_2_irq[TYPE_2_IRQS] = { | ||
98 | 0, 0, 0 ** could anyone find these for me ? (see AURORA_ALLIRQ below) ** | ||
99 | }; | ||
100 | unsigned char type_2_mask[TYPE_2_IRQS] = { | ||
101 | 32, 64, 128 | ||
102 | }; | ||
103 | */ | ||
104 | |||
105 | /* The following section should only be modified by those who know what | ||
106 | * they're doing (or don't, but want to help with some feedback). Modifying | ||
107 | * anything raises a _big_ probability for your system to hang, but the | ||
108 | * sacrifice worths. (I sacrificed my ext2fs many, many times...) | ||
109 | */ | ||
110 | |||
111 | /* This one tries to dump to console the name of almost every function called, | ||
112 | * and many other debugging info. | ||
113 | */ | ||
114 | #undef AURORA_DEBUG | ||
115 | |||
116 | /* These are the most dangerous and useful defines. They do printk() during | ||
117 | * the interrupt processing routine(s), so if you manage to get "flooded" by | ||
118 | * irq's, start thinking about the "Power off/on" button... | ||
119 | */ | ||
120 | #undef AURORA_INTNORM /* This one enables the "normal" messages, but some | ||
121 | * of them cause flood, so I preffered putting | ||
122 | * them under a define */ | ||
123 | #undef AURORA_INT_DEBUG /* This one is really bad. */ | ||
124 | |||
125 | /* Here's something helpful: after n irq's, the board will be disabled. This | ||
126 | * prevents irq flooding during debug (no need to think about power | ||
127 | * off/on anymore...) | ||
128 | */ | ||
129 | #define AURORA_FLOODPRO 10 | ||
130 | |||
131 | /* This one helps finding which irq the board calls, in case of a strange/ | ||
132 | * unsupported board. AURORA_INT_DEBUG should be enabled, because I don't | ||
133 | * think /proc/interrupts or any command will be available in case of an irq | ||
134 | * flood... "allirq" is the list of all free irq's. | ||
135 | */ | ||
136 | /* | ||
137 | #define AURORA_ALLIRQ 6 | ||
138 | int allirq[AURORA_ALLIRQ]={ | ||
139 | 2,3,5,7,9,13 | ||
140 | }; | ||
141 | */ | ||
142 | |||
143 | /* These must not be modified. These values are assumed during the code for | ||
144 | * performance optimisations. | ||
145 | */ | ||
146 | #define AURORA_NCD180 2 /* two chips per board */ | ||
147 | #define AURORA_NPORT 8 /* 8 ports per chip */ | ||
148 | |||
149 | /* several utilities */ | ||
150 | #define AURORA_BOARD(line) (((line) >> 4) & 0x01) | ||
151 | #define AURORA_CD180(line) (((line) >> 3) & 0x01) | ||
152 | #define AURORA_PORT(line) ((line) & 15) | ||
153 | |||
154 | #define AURORA_TNPORTS (AURORA_NBOARD*AURORA_NCD180*AURORA_NPORT) | ||
155 | |||
156 | /* Ticks per sec. Used for setting receiver timeout and break length */ | ||
157 | #define AURORA_TPS 4000 | ||
158 | |||
159 | #define AURORA_MAGIC 0x0A18 | ||
160 | |||
161 | /* Yeah, after heavy testing I decided it must be 6. | ||
162 | * Sure, You can change it if needed. | ||
163 | */ | ||
164 | #define AURORA_RXFIFO 6 /* Max. receiver FIFO size (1-8) */ | ||
165 | |||
166 | #define AURORA_RXTH 7 | ||
167 | |||
168 | struct aurora_reg1 { | ||
169 | __volatile__ unsigned char r; | ||
170 | }; | ||
171 | |||
172 | struct aurora_reg128 { | ||
173 | __volatile__ unsigned char r[128]; | ||
174 | }; | ||
175 | |||
176 | struct aurora_reg4 { | ||
177 | __volatile__ unsigned char r[4]; | ||
178 | }; | ||
179 | |||
180 | struct Aurora_board { | ||
181 | unsigned long flags; | ||
182 | struct aurora_reg1 * r0; /* This is the board configuration | ||
183 | * register (write-only). */ | ||
184 | struct aurora_reg128 * r[2]; /* These are the registers for the | ||
185 | * two chips. */ | ||
186 | struct aurora_reg4 * r3; /* These are used for hardware-based | ||
187 | * acknowledge. Software-based ack is | ||
188 | * not supported by CD180. */ | ||
189 | unsigned int oscfreq; /* The on-board oscillator | ||
190 | * frequency, in Hz. */ | ||
191 | unsigned char irq; | ||
192 | #ifdef MODULE | ||
193 | signed char count; /* counts the use of the board */ | ||
194 | #endif | ||
195 | /* Values for the dtr_rts swapped mode. */ | ||
196 | unsigned char DTR; | ||
197 | unsigned char RTS; | ||
198 | unsigned char MSVDTR; | ||
199 | unsigned char MSVRTS; | ||
200 | /* Values for hardware acknowledge. */ | ||
201 | unsigned char ACK_MINT, ACK_TINT, ACK_RINT; | ||
202 | }; | ||
203 | |||
204 | /* Board configuration register */ | ||
205 | #define AURORA_CFG_ENABLE_IO 8 | ||
206 | #define AURORA_CFG_ENABLE_IRQ 4 | ||
207 | |||
208 | /* Board flags */ | ||
209 | #define AURORA_BOARD_PRESENT 0x00000001 | ||
210 | #define AURORA_BOARD_ACTIVE 0x00000002 | ||
211 | #define AURORA_BOARD_TYPE_2 0x00000004 /* don't know how to | ||
212 | * detect this yet */ | ||
213 | #define AURORA_BOARD_DTR_FLOW_OK 0x00000008 | ||
214 | |||
215 | /* The story goes like this: Cirrus programmed the CD-180 chip to do automatic | ||
216 | * hardware flow control, and do it using CTS and DTR. CTS is ok, but, if you | ||
217 | * have a modem and the chip drops DTR, then the modem will drop the carrier | ||
218 | * (ain't that cute...). Luckily, the guys at Aurora decided to swap DTR and | ||
219 | * RTS, which makes the flow control usable. I hope that all the boards made | ||
220 | * by Aurora have these two signals swapped. If your's doesn't but you have a | ||
221 | * breakout box, you can try to reverse them yourself, then set the following | ||
222 | * flag. | ||
223 | */ | ||
224 | #undef AURORA_FORCE_DTR_FLOW | ||
225 | |||
226 | /* In fact, a few more words have to be said about hardware flow control. | ||
227 | * This driver handles "output" flow control through the on-board facility | ||
228 | * CTS Auto Enable. For the "input" flow control there are two cases when | ||
229 | * the flow should be controlled. The first case is when the kernel is so | ||
230 | * busy that it cannot process IRQ's in time; this flow control can only be | ||
231 | * activated by the on-board chip, and if the board has RTS and DTR swapped, | ||
232 | * this facility is usable. The second case is when the application is so | ||
233 | * busy that it cannot receive bytes from the kernel, and this flow must be | ||
234 | * activated by software. This second case is not yet implemented in this | ||
235 | * driver. Unfortunately, I estimate that the second case is the one that | ||
236 | * occurs the most. | ||
237 | */ | ||
238 | |||
239 | |||
240 | struct Aurora_port { | ||
241 | int magic; | ||
242 | int baud_base; | ||
243 | int flags; | ||
244 | struct tty_struct * tty; | ||
245 | int count; | ||
246 | int blocked_open; | ||
247 | long event; | ||
248 | int timeout; | ||
249 | int close_delay; | ||
250 | unsigned char * xmit_buf; | ||
251 | int custom_divisor; | ||
252 | int xmit_head; | ||
253 | int xmit_tail; | ||
254 | int xmit_cnt; | ||
255 | wait_queue_head_t open_wait; | ||
256 | wait_queue_head_t close_wait; | ||
257 | struct tq_struct tqueue; | ||
258 | struct tq_struct tqueue_hangup; | ||
259 | short wakeup_chars; | ||
260 | short break_length; | ||
261 | unsigned short closing_wait; | ||
262 | unsigned char mark_mask; | ||
263 | unsigned char SRER; | ||
264 | unsigned char MSVR; | ||
265 | unsigned char COR2; | ||
266 | #ifdef AURORA_REPORT_OVERRUN | ||
267 | unsigned long overrun; | ||
268 | #endif | ||
269 | #ifdef AURORA_REPORT_FIFO | ||
270 | unsigned long hits[10]; | ||
271 | #endif | ||
272 | }; | ||
273 | |||
274 | #endif | ||
275 | #endif /*__LINUX_AURORA_H*/ | ||
276 | |||
diff --git a/drivers/sbus/char/bbc_envctrl.c b/drivers/sbus/char/bbc_envctrl.c new file mode 100644 index 00000000000..d5259f7fee6 --- /dev/null +++ b/drivers/sbus/char/bbc_envctrl.c | |||
@@ -0,0 +1,645 @@ | |||
1 | /* $Id: bbc_envctrl.c,v 1.4 2001/04/06 16:48:08 davem Exp $ | ||
2 | * bbc_envctrl.c: UltraSPARC-III environment control driver. | ||
3 | * | ||
4 | * Copyright (C) 2001 David S. Miller (davem@redhat.com) | ||
5 | */ | ||
6 | |||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/sched.h> | ||
9 | #include <linux/slab.h> | ||
10 | #include <linux/delay.h> | ||
11 | #include <asm/oplib.h> | ||
12 | #include <asm/ebus.h> | ||
13 | #define __KERNEL_SYSCALLS__ | ||
14 | static int errno; | ||
15 | #include <asm/unistd.h> | ||
16 | |||
17 | #include "bbc_i2c.h" | ||
18 | #include "max1617.h" | ||
19 | |||
20 | #undef ENVCTRL_TRACE | ||
21 | |||
22 | /* WARNING: Making changes to this driver is very dangerous. | ||
23 | * If you misprogram the sensor chips they can | ||
24 | * cut the power on you instantly. | ||
25 | */ | ||
26 | |||
27 | /* Two temperature sensors exist in the SunBLADE-1000 enclosure. | ||
28 | * Both are implemented using max1617 i2c devices. Each max1617 | ||
29 | * monitors 2 temperatures, one for one of the cpu dies and the other | ||
30 | * for the ambient temperature. | ||
31 | * | ||
32 | * The max1617 is capable of being programmed with power-off | ||
33 | * temperature values, one low limit and one high limit. These | ||
34 | * can be controlled independently for the cpu or ambient temperature. | ||
35 | * If a limit is violated, the power is simply shut off. The frequency | ||
36 | * with which the max1617 does temperature sampling can be controlled | ||
37 | * as well. | ||
38 | * | ||
39 | * Three fans exist inside the machine, all three are controlled with | ||
40 | * an i2c digital to analog converter. There is a fan directed at the | ||
41 | * two processor slots, another for the rest of the enclosure, and the | ||
42 | * third is for the power supply. The first two fans may be speed | ||
43 | * controlled by changing the voltage fed to them. The third fan may | ||
44 | * only be completely off or on. The third fan is meant to only be | ||
45 | * disabled/enabled when entering/exiting the lowest power-saving | ||
46 | * mode of the machine. | ||
47 | * | ||
48 | * An environmental control kernel thread periodically monitors all | ||
49 | * temperature sensors. Based upon the samples it will adjust the | ||
50 | * fan speeds to try and keep the system within a certain temperature | ||
51 | * range (the goal being to make the fans as quiet as possible without | ||
52 | * allowing the system to get too hot). | ||
53 | * | ||
54 | * If the temperature begins to rise/fall outside of the acceptable | ||
55 | * operating range, a periodic warning will be sent to the kernel log. | ||
56 | * The fans will be put on full blast to attempt to deal with this | ||
57 | * situation. After exceeding the acceptable operating range by a | ||
58 | * certain threshold, the kernel thread will shut down the system. | ||
59 | * Here, the thread is attempting to shut the machine down cleanly | ||
60 | * before the hardware based power-off event is triggered. | ||
61 | */ | ||
62 | |||
63 | /* These settings are in Celsius. We use these defaults only | ||
64 | * if we cannot interrogate the cpu-fru SEEPROM. | ||
65 | */ | ||
66 | struct temp_limits { | ||
67 | s8 high_pwroff, high_shutdown, high_warn; | ||
68 | s8 low_warn, low_shutdown, low_pwroff; | ||
69 | }; | ||
70 | |||
71 | static struct temp_limits cpu_temp_limits[2] = { | ||
72 | { 100, 85, 80, 5, -5, -10 }, | ||
73 | { 100, 85, 80, 5, -5, -10 }, | ||
74 | }; | ||
75 | |||
76 | static struct temp_limits amb_temp_limits[2] = { | ||
77 | { 65, 55, 40, 5, -5, -10 }, | ||
78 | { 65, 55, 40, 5, -5, -10 }, | ||
79 | }; | ||
80 | |||
81 | enum fan_action { FAN_SLOWER, FAN_SAME, FAN_FASTER, FAN_FULLBLAST, FAN_STATE_MAX }; | ||
82 | |||
83 | struct bbc_cpu_temperature { | ||
84 | struct bbc_cpu_temperature *next; | ||
85 | |||
86 | struct bbc_i2c_client *client; | ||
87 | int index; | ||
88 | |||
89 | /* Current readings, and history. */ | ||
90 | s8 curr_cpu_temp; | ||
91 | s8 curr_amb_temp; | ||
92 | s8 prev_cpu_temp; | ||
93 | s8 prev_amb_temp; | ||
94 | s8 avg_cpu_temp; | ||
95 | s8 avg_amb_temp; | ||
96 | |||
97 | int sample_tick; | ||
98 | |||
99 | enum fan_action fan_todo[2]; | ||
100 | #define FAN_AMBIENT 0 | ||
101 | #define FAN_CPU 1 | ||
102 | }; | ||
103 | |||
104 | struct bbc_cpu_temperature *all_bbc_temps; | ||
105 | |||
106 | struct bbc_fan_control { | ||
107 | struct bbc_fan_control *next; | ||
108 | |||
109 | struct bbc_i2c_client *client; | ||
110 | int index; | ||
111 | |||
112 | int psupply_fan_on; | ||
113 | int cpu_fan_speed; | ||
114 | int system_fan_speed; | ||
115 | }; | ||
116 | |||
117 | struct bbc_fan_control *all_bbc_fans; | ||
118 | |||
119 | #define CPU_FAN_REG 0xf0 | ||
120 | #define SYS_FAN_REG 0xf2 | ||
121 | #define PSUPPLY_FAN_REG 0xf4 | ||
122 | |||
123 | #define FAN_SPEED_MIN 0x0c | ||
124 | #define FAN_SPEED_MAX 0x3f | ||
125 | |||
126 | #define PSUPPLY_FAN_ON 0x1f | ||
127 | #define PSUPPLY_FAN_OFF 0x00 | ||
128 | |||
129 | static void set_fan_speeds(struct bbc_fan_control *fp) | ||
130 | { | ||
131 | /* Put temperatures into range so we don't mis-program | ||
132 | * the hardware. | ||
133 | */ | ||
134 | if (fp->cpu_fan_speed < FAN_SPEED_MIN) | ||
135 | fp->cpu_fan_speed = FAN_SPEED_MIN; | ||
136 | if (fp->cpu_fan_speed > FAN_SPEED_MAX) | ||
137 | fp->cpu_fan_speed = FAN_SPEED_MAX; | ||
138 | if (fp->system_fan_speed < FAN_SPEED_MIN) | ||
139 | fp->system_fan_speed = FAN_SPEED_MIN; | ||
140 | if (fp->system_fan_speed > FAN_SPEED_MAX) | ||
141 | fp->system_fan_speed = FAN_SPEED_MAX; | ||
142 | #ifdef ENVCTRL_TRACE | ||
143 | printk("fan%d: Changed fan speed to cpu(%02x) sys(%02x)\n", | ||
144 | fp->index, | ||
145 | fp->cpu_fan_speed, fp->system_fan_speed); | ||
146 | #endif | ||
147 | |||
148 | bbc_i2c_writeb(fp->client, fp->cpu_fan_speed, CPU_FAN_REG); | ||
149 | bbc_i2c_writeb(fp->client, fp->system_fan_speed, SYS_FAN_REG); | ||
150 | bbc_i2c_writeb(fp->client, | ||
151 | (fp->psupply_fan_on ? | ||
152 | PSUPPLY_FAN_ON : PSUPPLY_FAN_OFF), | ||
153 | PSUPPLY_FAN_REG); | ||
154 | } | ||
155 | |||
156 | static void get_current_temps(struct bbc_cpu_temperature *tp) | ||
157 | { | ||
158 | tp->prev_amb_temp = tp->curr_amb_temp; | ||
159 | bbc_i2c_readb(tp->client, | ||
160 | (unsigned char *) &tp->curr_amb_temp, | ||
161 | MAX1617_AMB_TEMP); | ||
162 | tp->prev_cpu_temp = tp->curr_cpu_temp; | ||
163 | bbc_i2c_readb(tp->client, | ||
164 | (unsigned char *) &tp->curr_cpu_temp, | ||
165 | MAX1617_CPU_TEMP); | ||
166 | #ifdef ENVCTRL_TRACE | ||
167 | printk("temp%d: cpu(%d C) amb(%d C)\n", | ||
168 | tp->index, | ||
169 | (int) tp->curr_cpu_temp, (int) tp->curr_amb_temp); | ||
170 | #endif | ||
171 | } | ||
172 | |||
173 | |||
174 | static void do_envctrl_shutdown(struct bbc_cpu_temperature *tp) | ||
175 | { | ||
176 | static int shutting_down = 0; | ||
177 | static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; | ||
178 | char *argv[] = { "/sbin/shutdown", "-h", "now", NULL }; | ||
179 | char *type = "???"; | ||
180 | s8 val = -1; | ||
181 | |||
182 | if (shutting_down != 0) | ||
183 | return; | ||
184 | |||
185 | if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_shutdown || | ||
186 | tp->curr_amb_temp < amb_temp_limits[tp->index].low_shutdown) { | ||
187 | type = "ambient"; | ||
188 | val = tp->curr_amb_temp; | ||
189 | } else if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_shutdown || | ||
190 | tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_shutdown) { | ||
191 | type = "CPU"; | ||
192 | val = tp->curr_cpu_temp; | ||
193 | } | ||
194 | |||
195 | printk(KERN_CRIT "temp%d: Outside of safe %s " | ||
196 | "operating temperature, %d C.\n", | ||
197 | tp->index, type, val); | ||
198 | |||
199 | printk(KERN_CRIT "kenvctrld: Shutting down the system now.\n"); | ||
200 | |||
201 | shutting_down = 1; | ||
202 | if (execve("/sbin/shutdown", argv, envp) < 0) | ||
203 | printk(KERN_CRIT "envctrl: shutdown execution failed\n"); | ||
204 | } | ||
205 | |||
206 | #define WARN_INTERVAL (30 * HZ) | ||
207 | |||
208 | static void analyze_ambient_temp(struct bbc_cpu_temperature *tp, unsigned long *last_warn, int tick) | ||
209 | { | ||
210 | int ret = 0; | ||
211 | |||
212 | if (time_after(jiffies, (*last_warn + WARN_INTERVAL))) { | ||
213 | if (tp->curr_amb_temp >= | ||
214 | amb_temp_limits[tp->index].high_warn) { | ||
215 | printk(KERN_WARNING "temp%d: " | ||
216 | "Above safe ambient operating temperature, %d C.\n", | ||
217 | tp->index, (int) tp->curr_amb_temp); | ||
218 | ret = 1; | ||
219 | } else if (tp->curr_amb_temp < | ||
220 | amb_temp_limits[tp->index].low_warn) { | ||
221 | printk(KERN_WARNING "temp%d: " | ||
222 | "Below safe ambient operating temperature, %d C.\n", | ||
223 | tp->index, (int) tp->curr_amb_temp); | ||
224 | ret = 1; | ||
225 | } | ||
226 | if (ret) | ||
227 | *last_warn = jiffies; | ||
228 | } else if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_warn || | ||
229 | tp->curr_amb_temp < amb_temp_limits[tp->index].low_warn) | ||
230 | ret = 1; | ||
231 | |||
232 | /* Now check the shutdown limits. */ | ||
233 | if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_shutdown || | ||
234 | tp->curr_amb_temp < amb_temp_limits[tp->index].low_shutdown) { | ||
235 | do_envctrl_shutdown(tp); | ||
236 | ret = 1; | ||
237 | } | ||
238 | |||
239 | if (ret) { | ||
240 | tp->fan_todo[FAN_AMBIENT] = FAN_FULLBLAST; | ||
241 | } else if ((tick & (8 - 1)) == 0) { | ||
242 | s8 amb_goal_hi = amb_temp_limits[tp->index].high_warn - 10; | ||
243 | s8 amb_goal_lo; | ||
244 | |||
245 | amb_goal_lo = amb_goal_hi - 3; | ||
246 | |||
247 | /* We do not try to avoid 'too cold' events. Basically we | ||
248 | * only try to deal with over-heating and fan noise reduction. | ||
249 | */ | ||
250 | if (tp->avg_amb_temp < amb_goal_hi) { | ||
251 | if (tp->avg_amb_temp >= amb_goal_lo) | ||
252 | tp->fan_todo[FAN_AMBIENT] = FAN_SAME; | ||
253 | else | ||
254 | tp->fan_todo[FAN_AMBIENT] = FAN_SLOWER; | ||
255 | } else { | ||
256 | tp->fan_todo[FAN_AMBIENT] = FAN_FASTER; | ||
257 | } | ||
258 | } else { | ||
259 | tp->fan_todo[FAN_AMBIENT] = FAN_SAME; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | static void analyze_cpu_temp(struct bbc_cpu_temperature *tp, unsigned long *last_warn, int tick) | ||
264 | { | ||
265 | int ret = 0; | ||
266 | |||
267 | if (time_after(jiffies, (*last_warn + WARN_INTERVAL))) { | ||
268 | if (tp->curr_cpu_temp >= | ||
269 | cpu_temp_limits[tp->index].high_warn) { | ||
270 | printk(KERN_WARNING "temp%d: " | ||
271 | "Above safe CPU operating temperature, %d C.\n", | ||
272 | tp->index, (int) tp->curr_cpu_temp); | ||
273 | ret = 1; | ||
274 | } else if (tp->curr_cpu_temp < | ||
275 | cpu_temp_limits[tp->index].low_warn) { | ||
276 | printk(KERN_WARNING "temp%d: " | ||
277 | "Below safe CPU operating temperature, %d C.\n", | ||
278 | tp->index, (int) tp->curr_cpu_temp); | ||
279 | ret = 1; | ||
280 | } | ||
281 | if (ret) | ||
282 | *last_warn = jiffies; | ||
283 | } else if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_warn || | ||
284 | tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_warn) | ||
285 | ret = 1; | ||
286 | |||
287 | /* Now check the shutdown limits. */ | ||
288 | if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_shutdown || | ||
289 | tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_shutdown) { | ||
290 | do_envctrl_shutdown(tp); | ||
291 | ret = 1; | ||
292 | } | ||
293 | |||
294 | if (ret) { | ||
295 | tp->fan_todo[FAN_CPU] = FAN_FULLBLAST; | ||
296 | } else if ((tick & (8 - 1)) == 0) { | ||
297 | s8 cpu_goal_hi = cpu_temp_limits[tp->index].high_warn - 10; | ||
298 | s8 cpu_goal_lo; | ||
299 | |||
300 | cpu_goal_lo = cpu_goal_hi - 3; | ||
301 | |||
302 | /* We do not try to avoid 'too cold' events. Basically we | ||
303 | * only try to deal with over-heating and fan noise reduction. | ||
304 | */ | ||
305 | if (tp->avg_cpu_temp < cpu_goal_hi) { | ||
306 | if (tp->avg_cpu_temp >= cpu_goal_lo) | ||
307 | tp->fan_todo[FAN_CPU] = FAN_SAME; | ||
308 | else | ||
309 | tp->fan_todo[FAN_CPU] = FAN_SLOWER; | ||
310 | } else { | ||
311 | tp->fan_todo[FAN_CPU] = FAN_FASTER; | ||
312 | } | ||
313 | } else { | ||
314 | tp->fan_todo[FAN_CPU] = FAN_SAME; | ||
315 | } | ||
316 | } | ||
317 | |||
318 | static void analyze_temps(struct bbc_cpu_temperature *tp, unsigned long *last_warn) | ||
319 | { | ||
320 | tp->avg_amb_temp = (s8)((int)((int)tp->avg_amb_temp + (int)tp->curr_amb_temp) / 2); | ||
321 | tp->avg_cpu_temp = (s8)((int)((int)tp->avg_cpu_temp + (int)tp->curr_cpu_temp) / 2); | ||
322 | |||
323 | analyze_ambient_temp(tp, last_warn, tp->sample_tick); | ||
324 | analyze_cpu_temp(tp, last_warn, tp->sample_tick); | ||
325 | |||
326 | tp->sample_tick++; | ||
327 | } | ||
328 | |||
329 | static enum fan_action prioritize_fan_action(int which_fan) | ||
330 | { | ||
331 | struct bbc_cpu_temperature *tp; | ||
332 | enum fan_action decision = FAN_STATE_MAX; | ||
333 | |||
334 | /* Basically, prioritize what the temperature sensors | ||
335 | * recommend we do, and perform that action on all the | ||
336 | * fans. | ||
337 | */ | ||
338 | for (tp = all_bbc_temps; tp; tp = tp->next) { | ||
339 | if (tp->fan_todo[which_fan] == FAN_FULLBLAST) { | ||
340 | decision = FAN_FULLBLAST; | ||
341 | break; | ||
342 | } | ||
343 | if (tp->fan_todo[which_fan] == FAN_SAME && | ||
344 | decision != FAN_FASTER) | ||
345 | decision = FAN_SAME; | ||
346 | else if (tp->fan_todo[which_fan] == FAN_FASTER) | ||
347 | decision = FAN_FASTER; | ||
348 | else if (decision != FAN_FASTER && | ||
349 | decision != FAN_SAME && | ||
350 | tp->fan_todo[which_fan] == FAN_SLOWER) | ||
351 | decision = FAN_SLOWER; | ||
352 | } | ||
353 | if (decision == FAN_STATE_MAX) | ||
354 | decision = FAN_SAME; | ||
355 | |||
356 | return decision; | ||
357 | } | ||
358 | |||
359 | static int maybe_new_ambient_fan_speed(struct bbc_fan_control *fp) | ||
360 | { | ||
361 | enum fan_action decision = prioritize_fan_action(FAN_AMBIENT); | ||
362 | int ret; | ||
363 | |||
364 | if (decision == FAN_SAME) | ||
365 | return 0; | ||
366 | |||
367 | ret = 1; | ||
368 | if (decision == FAN_FULLBLAST) { | ||
369 | if (fp->system_fan_speed >= FAN_SPEED_MAX) | ||
370 | ret = 0; | ||
371 | else | ||
372 | fp->system_fan_speed = FAN_SPEED_MAX; | ||
373 | } else { | ||
374 | if (decision == FAN_FASTER) { | ||
375 | if (fp->system_fan_speed >= FAN_SPEED_MAX) | ||
376 | ret = 0; | ||
377 | else | ||
378 | fp->system_fan_speed += 2; | ||
379 | } else { | ||
380 | int orig_speed = fp->system_fan_speed; | ||
381 | |||
382 | if (orig_speed <= FAN_SPEED_MIN || | ||
383 | orig_speed <= (fp->cpu_fan_speed - 3)) | ||
384 | ret = 0; | ||
385 | else | ||
386 | fp->system_fan_speed -= 1; | ||
387 | } | ||
388 | } | ||
389 | |||
390 | return ret; | ||
391 | } | ||
392 | |||
393 | static int maybe_new_cpu_fan_speed(struct bbc_fan_control *fp) | ||
394 | { | ||
395 | enum fan_action decision = prioritize_fan_action(FAN_CPU); | ||
396 | int ret; | ||
397 | |||
398 | if (decision == FAN_SAME) | ||
399 | return 0; | ||
400 | |||
401 | ret = 1; | ||
402 | if (decision == FAN_FULLBLAST) { | ||
403 | if (fp->cpu_fan_speed >= FAN_SPEED_MAX) | ||
404 | ret = 0; | ||
405 | else | ||
406 | fp->cpu_fan_speed = FAN_SPEED_MAX; | ||
407 | } else { | ||
408 | if (decision == FAN_FASTER) { | ||
409 | if (fp->cpu_fan_speed >= FAN_SPEED_MAX) | ||
410 | ret = 0; | ||
411 | else { | ||
412 | fp->cpu_fan_speed += 2; | ||
413 | if (fp->system_fan_speed < | ||
414 | (fp->cpu_fan_speed - 3)) | ||
415 | fp->system_fan_speed = | ||
416 | fp->cpu_fan_speed - 3; | ||
417 | } | ||
418 | } else { | ||
419 | if (fp->cpu_fan_speed <= FAN_SPEED_MIN) | ||
420 | ret = 0; | ||
421 | else | ||
422 | fp->cpu_fan_speed -= 1; | ||
423 | } | ||
424 | } | ||
425 | |||
426 | return ret; | ||
427 | } | ||
428 | |||
429 | static void maybe_new_fan_speeds(struct bbc_fan_control *fp) | ||
430 | { | ||
431 | int new; | ||
432 | |||
433 | new = maybe_new_ambient_fan_speed(fp); | ||
434 | new |= maybe_new_cpu_fan_speed(fp); | ||
435 | |||
436 | if (new) | ||
437 | set_fan_speeds(fp); | ||
438 | } | ||
439 | |||
440 | static void fans_full_blast(void) | ||
441 | { | ||
442 | struct bbc_fan_control *fp; | ||
443 | |||
444 | /* Since we will not be monitoring things anymore, put | ||
445 | * the fans on full blast. | ||
446 | */ | ||
447 | for (fp = all_bbc_fans; fp; fp = fp->next) { | ||
448 | fp->cpu_fan_speed = FAN_SPEED_MAX; | ||
449 | fp->system_fan_speed = FAN_SPEED_MAX; | ||
450 | fp->psupply_fan_on = 1; | ||
451 | set_fan_speeds(fp); | ||
452 | } | ||
453 | } | ||
454 | |||
455 | #define POLL_INTERVAL (5 * 1000) | ||
456 | static unsigned long last_warning_jiffies; | ||
457 | static struct task_struct *kenvctrld_task; | ||
458 | |||
459 | static int kenvctrld(void *__unused) | ||
460 | { | ||
461 | daemonize("kenvctrld"); | ||
462 | allow_signal(SIGKILL); | ||
463 | kenvctrld_task = current; | ||
464 | |||
465 | printk(KERN_INFO "bbc_envctrl: kenvctrld starting...\n"); | ||
466 | last_warning_jiffies = jiffies - WARN_INTERVAL; | ||
467 | for (;;) { | ||
468 | struct bbc_cpu_temperature *tp; | ||
469 | struct bbc_fan_control *fp; | ||
470 | |||
471 | msleep_interruptible(POLL_INTERVAL); | ||
472 | if (signal_pending(current)) | ||
473 | break; | ||
474 | |||
475 | for (tp = all_bbc_temps; tp; tp = tp->next) { | ||
476 | get_current_temps(tp); | ||
477 | analyze_temps(tp, &last_warning_jiffies); | ||
478 | } | ||
479 | for (fp = all_bbc_fans; fp; fp = fp->next) | ||
480 | maybe_new_fan_speeds(fp); | ||
481 | } | ||
482 | printk(KERN_INFO "bbc_envctrl: kenvctrld exiting...\n"); | ||
483 | |||
484 | fans_full_blast(); | ||
485 | |||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | static void attach_one_temp(struct linux_ebus_child *echild, int temp_idx) | ||
490 | { | ||
491 | struct bbc_cpu_temperature *tp = kmalloc(sizeof(*tp), GFP_KERNEL); | ||
492 | |||
493 | if (!tp) | ||
494 | return; | ||
495 | memset(tp, 0, sizeof(*tp)); | ||
496 | tp->client = bbc_i2c_attach(echild); | ||
497 | if (!tp->client) { | ||
498 | kfree(tp); | ||
499 | return; | ||
500 | } | ||
501 | |||
502 | tp->index = temp_idx; | ||
503 | { | ||
504 | struct bbc_cpu_temperature **tpp = &all_bbc_temps; | ||
505 | while (*tpp) | ||
506 | tpp = &((*tpp)->next); | ||
507 | tp->next = NULL; | ||
508 | *tpp = tp; | ||
509 | } | ||
510 | |||
511 | /* Tell it to convert once every 5 seconds, clear all cfg | ||
512 | * bits. | ||
513 | */ | ||
514 | bbc_i2c_writeb(tp->client, 0x00, MAX1617_WR_CFG_BYTE); | ||
515 | bbc_i2c_writeb(tp->client, 0x02, MAX1617_WR_CVRATE_BYTE); | ||
516 | |||
517 | /* Program the hard temperature limits into the chip. */ | ||
518 | bbc_i2c_writeb(tp->client, amb_temp_limits[tp->index].high_pwroff, | ||
519 | MAX1617_WR_AMB_HIGHLIM); | ||
520 | bbc_i2c_writeb(tp->client, amb_temp_limits[tp->index].low_pwroff, | ||
521 | MAX1617_WR_AMB_LOWLIM); | ||
522 | bbc_i2c_writeb(tp->client, cpu_temp_limits[tp->index].high_pwroff, | ||
523 | MAX1617_WR_CPU_HIGHLIM); | ||
524 | bbc_i2c_writeb(tp->client, cpu_temp_limits[tp->index].low_pwroff, | ||
525 | MAX1617_WR_CPU_LOWLIM); | ||
526 | |||
527 | get_current_temps(tp); | ||
528 | tp->prev_cpu_temp = tp->avg_cpu_temp = tp->curr_cpu_temp; | ||
529 | tp->prev_amb_temp = tp->avg_amb_temp = tp->curr_amb_temp; | ||
530 | |||
531 | tp->fan_todo[FAN_AMBIENT] = FAN_SAME; | ||
532 | tp->fan_todo[FAN_CPU] = FAN_SAME; | ||
533 | } | ||
534 | |||
535 | static void attach_one_fan(struct linux_ebus_child *echild, int fan_idx) | ||
536 | { | ||
537 | struct bbc_fan_control *fp = kmalloc(sizeof(*fp), GFP_KERNEL); | ||
538 | |||
539 | if (!fp) | ||
540 | return; | ||
541 | memset(fp, 0, sizeof(*fp)); | ||
542 | fp->client = bbc_i2c_attach(echild); | ||
543 | if (!fp->client) { | ||
544 | kfree(fp); | ||
545 | return; | ||
546 | } | ||
547 | |||
548 | fp->index = fan_idx; | ||
549 | |||
550 | { | ||
551 | struct bbc_fan_control **fpp = &all_bbc_fans; | ||
552 | while (*fpp) | ||
553 | fpp = &((*fpp)->next); | ||
554 | fp->next = NULL; | ||
555 | *fpp = fp; | ||
556 | } | ||
557 | |||
558 | /* The i2c device controlling the fans is write-only. | ||
559 | * So the only way to keep track of the current power | ||
560 | * level fed to the fans is via software. Choose half | ||
561 | * power for cpu/system and 'on' fo the powersupply fan | ||
562 | * and set it now. | ||
563 | */ | ||
564 | fp->psupply_fan_on = 1; | ||
565 | fp->cpu_fan_speed = (FAN_SPEED_MAX - FAN_SPEED_MIN) / 2; | ||
566 | fp->cpu_fan_speed += FAN_SPEED_MIN; | ||
567 | fp->system_fan_speed = (FAN_SPEED_MAX - FAN_SPEED_MIN) / 2; | ||
568 | fp->system_fan_speed += FAN_SPEED_MIN; | ||
569 | |||
570 | set_fan_speeds(fp); | ||
571 | } | ||
572 | |||
573 | int bbc_envctrl_init(void) | ||
574 | { | ||
575 | struct linux_ebus_child *echild; | ||
576 | int temp_index = 0; | ||
577 | int fan_index = 0; | ||
578 | int devidx = 0; | ||
579 | int err = 0; | ||
580 | |||
581 | while ((echild = bbc_i2c_getdev(devidx++)) != NULL) { | ||
582 | if (!strcmp(echild->prom_name, "temperature")) | ||
583 | attach_one_temp(echild, temp_index++); | ||
584 | if (!strcmp(echild->prom_name, "fan-control")) | ||
585 | attach_one_fan(echild, fan_index++); | ||
586 | } | ||
587 | if (temp_index != 0 && fan_index != 0) | ||
588 | err = kernel_thread(kenvctrld, NULL, CLONE_FS | CLONE_FILES); | ||
589 | return err; | ||
590 | } | ||
591 | |||
592 | static void destroy_one_temp(struct bbc_cpu_temperature *tp) | ||
593 | { | ||
594 | bbc_i2c_detach(tp->client); | ||
595 | kfree(tp); | ||
596 | } | ||
597 | |||
598 | static void destroy_one_fan(struct bbc_fan_control *fp) | ||
599 | { | ||
600 | bbc_i2c_detach(fp->client); | ||
601 | kfree(fp); | ||
602 | } | ||
603 | |||
604 | void bbc_envctrl_cleanup(void) | ||
605 | { | ||
606 | struct bbc_cpu_temperature *tp; | ||
607 | struct bbc_fan_control *fp; | ||
608 | |||
609 | if (kenvctrld_task != NULL) { | ||
610 | force_sig(SIGKILL, kenvctrld_task); | ||
611 | for (;;) { | ||
612 | struct task_struct *p; | ||
613 | int found = 0; | ||
614 | |||
615 | read_lock(&tasklist_lock); | ||
616 | for_each_process(p) { | ||
617 | if (p == kenvctrld_task) { | ||
618 | found = 1; | ||
619 | break; | ||
620 | } | ||
621 | } | ||
622 | read_unlock(&tasklist_lock); | ||
623 | if (!found) | ||
624 | break; | ||
625 | msleep(1000); | ||
626 | } | ||
627 | kenvctrld_task = NULL; | ||
628 | } | ||
629 | |||
630 | tp = all_bbc_temps; | ||
631 | while (tp != NULL) { | ||
632 | struct bbc_cpu_temperature *next = tp->next; | ||
633 | destroy_one_temp(tp); | ||
634 | tp = next; | ||
635 | } | ||
636 | all_bbc_temps = NULL; | ||
637 | |||
638 | fp = all_bbc_fans; | ||
639 | while (fp != NULL) { | ||
640 | struct bbc_fan_control *next = fp->next; | ||
641 | destroy_one_fan(fp); | ||
642 | fp = next; | ||
643 | } | ||
644 | all_bbc_fans = NULL; | ||
645 | } | ||
diff --git a/drivers/sbus/char/bbc_i2c.c b/drivers/sbus/char/bbc_i2c.c new file mode 100644 index 00000000000..1c8b612d823 --- /dev/null +++ b/drivers/sbus/char/bbc_i2c.c | |||
@@ -0,0 +1,488 @@ | |||
1 | /* $Id: bbc_i2c.c,v 1.2 2001/04/02 09:59:08 davem Exp $ | ||
2 | * bbc_i2c.c: I2C low-level driver for BBC device on UltraSPARC-III | ||
3 | * platforms. | ||
4 | * | ||
5 | * Copyright (C) 2001 David S. Miller (davem@redhat.com) | ||
6 | */ | ||
7 | |||
8 | #include <linux/module.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/types.h> | ||
11 | #include <linux/slab.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/wait.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <asm/oplib.h> | ||
18 | #include <asm/ebus.h> | ||
19 | #include <asm/spitfire.h> | ||
20 | #include <asm/bbc.h> | ||
21 | |||
22 | #include "bbc_i2c.h" | ||
23 | |||
24 | /* Convert this driver to use i2c bus layer someday... */ | ||
25 | #define I2C_PCF_PIN 0x80 | ||
26 | #define I2C_PCF_ESO 0x40 | ||
27 | #define I2C_PCF_ES1 0x20 | ||
28 | #define I2C_PCF_ES2 0x10 | ||
29 | #define I2C_PCF_ENI 0x08 | ||
30 | #define I2C_PCF_STA 0x04 | ||
31 | #define I2C_PCF_STO 0x02 | ||
32 | #define I2C_PCF_ACK 0x01 | ||
33 | |||
34 | #define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ENI | I2C_PCF_STA | I2C_PCF_ACK) | ||
35 | #define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK) | ||
36 | #define I2C_PCF_REPSTART ( I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) | ||
37 | #define I2C_PCF_IDLE (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ACK) | ||
38 | |||
39 | #define I2C_PCF_INI 0x40 /* 1 if not initialized */ | ||
40 | #define I2C_PCF_STS 0x20 | ||
41 | #define I2C_PCF_BER 0x10 | ||
42 | #define I2C_PCF_AD0 0x08 | ||
43 | #define I2C_PCF_LRB 0x08 | ||
44 | #define I2C_PCF_AAS 0x04 | ||
45 | #define I2C_PCF_LAB 0x02 | ||
46 | #define I2C_PCF_BB 0x01 | ||
47 | |||
48 | /* The BBC devices have two I2C controllers. The first I2C controller | ||
49 | * connects mainly to configuration proms (NVRAM, cpu configuration, | ||
50 | * dimm types, etc.). Whereas the second I2C controller connects to | ||
51 | * environmental control devices such as fans and temperature sensors. | ||
52 | * The second controller also connects to the smartcard reader, if present. | ||
53 | */ | ||
54 | |||
55 | #define NUM_CHILDREN 8 | ||
56 | struct bbc_i2c_bus { | ||
57 | struct bbc_i2c_bus *next; | ||
58 | int index; | ||
59 | spinlock_t lock; | ||
60 | void __iomem *i2c_bussel_reg; | ||
61 | void __iomem *i2c_control_regs; | ||
62 | unsigned char own, clock; | ||
63 | |||
64 | wait_queue_head_t wq; | ||
65 | volatile int waiting; | ||
66 | |||
67 | struct linux_ebus_device *bus_edev; | ||
68 | struct { | ||
69 | struct linux_ebus_child *device; | ||
70 | int client_claimed; | ||
71 | } devs[NUM_CHILDREN]; | ||
72 | }; | ||
73 | |||
74 | static struct bbc_i2c_bus *all_bbc_i2c; | ||
75 | |||
76 | struct bbc_i2c_client { | ||
77 | struct bbc_i2c_bus *bp; | ||
78 | struct linux_ebus_child *echild; | ||
79 | int bus; | ||
80 | int address; | ||
81 | }; | ||
82 | |||
83 | static int find_device(struct bbc_i2c_bus *bp, struct linux_ebus_child *echild) | ||
84 | { | ||
85 | int i; | ||
86 | |||
87 | for (i = 0; i < NUM_CHILDREN; i++) { | ||
88 | if (bp->devs[i].device == echild) { | ||
89 | if (bp->devs[i].client_claimed) | ||
90 | return 0; | ||
91 | return 1; | ||
92 | } | ||
93 | } | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static void set_device_claimage(struct bbc_i2c_bus *bp, struct linux_ebus_child *echild, int val) | ||
98 | { | ||
99 | int i; | ||
100 | |||
101 | for (i = 0; i < NUM_CHILDREN; i++) { | ||
102 | if (bp->devs[i].device == echild) { | ||
103 | bp->devs[i].client_claimed = val; | ||
104 | return; | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | #define claim_device(BP,ECHILD) set_device_claimage(BP,ECHILD,1) | ||
110 | #define release_device(BP,ECHILD) set_device_claimage(BP,ECHILD,0) | ||
111 | |||
112 | static struct bbc_i2c_bus *find_bus_for_device(struct linux_ebus_child *echild) | ||
113 | { | ||
114 | struct bbc_i2c_bus *bp = all_bbc_i2c; | ||
115 | |||
116 | while (bp != NULL) { | ||
117 | if (find_device(bp, echild) != 0) | ||
118 | break; | ||
119 | bp = bp->next; | ||
120 | } | ||
121 | |||
122 | return bp; | ||
123 | } | ||
124 | |||
125 | struct linux_ebus_child *bbc_i2c_getdev(int index) | ||
126 | { | ||
127 | struct bbc_i2c_bus *bp = all_bbc_i2c; | ||
128 | struct linux_ebus_child *echild = NULL; | ||
129 | int curidx = 0; | ||
130 | |||
131 | while (bp != NULL) { | ||
132 | struct bbc_i2c_bus *next = bp->next; | ||
133 | int i; | ||
134 | |||
135 | for (i = 0; i < NUM_CHILDREN; i++) { | ||
136 | if (!(echild = bp->devs[i].device)) | ||
137 | break; | ||
138 | if (curidx == index) | ||
139 | goto out; | ||
140 | echild = NULL; | ||
141 | curidx++; | ||
142 | } | ||
143 | bp = next; | ||
144 | } | ||
145 | out: | ||
146 | if (curidx == index) | ||
147 | return echild; | ||
148 | return NULL; | ||
149 | } | ||
150 | |||
151 | struct bbc_i2c_client *bbc_i2c_attach(struct linux_ebus_child *echild) | ||
152 | { | ||
153 | struct bbc_i2c_bus *bp = find_bus_for_device(echild); | ||
154 | struct bbc_i2c_client *client; | ||
155 | |||
156 | if (!bp) | ||
157 | return NULL; | ||
158 | client = kmalloc(sizeof(*client), GFP_KERNEL); | ||
159 | if (!client) | ||
160 | return NULL; | ||
161 | memset(client, 0, sizeof(*client)); | ||
162 | client->bp = bp; | ||
163 | client->echild = echild; | ||
164 | client->bus = echild->resource[0].start; | ||
165 | client->address = echild->resource[1].start; | ||
166 | |||
167 | claim_device(bp, echild); | ||
168 | |||
169 | return client; | ||
170 | } | ||
171 | |||
172 | void bbc_i2c_detach(struct bbc_i2c_client *client) | ||
173 | { | ||
174 | struct bbc_i2c_bus *bp = client->bp; | ||
175 | struct linux_ebus_child *echild = client->echild; | ||
176 | |||
177 | release_device(bp, echild); | ||
178 | kfree(client); | ||
179 | } | ||
180 | |||
181 | static int wait_for_pin(struct bbc_i2c_bus *bp, u8 *status) | ||
182 | { | ||
183 | DECLARE_WAITQUEUE(wait, current); | ||
184 | int limit = 32; | ||
185 | int ret = 1; | ||
186 | |||
187 | bp->waiting = 1; | ||
188 | add_wait_queue(&bp->wq, &wait); | ||
189 | while (limit-- > 0) { | ||
190 | u8 val; | ||
191 | |||
192 | set_current_state(TASK_INTERRUPTIBLE); | ||
193 | *status = val = readb(bp->i2c_control_regs + 0); | ||
194 | if ((val & I2C_PCF_PIN) == 0) { | ||
195 | ret = 0; | ||
196 | break; | ||
197 | } | ||
198 | msleep_interruptible(250); | ||
199 | } | ||
200 | remove_wait_queue(&bp->wq, &wait); | ||
201 | bp->waiting = 0; | ||
202 | current->state = TASK_RUNNING; | ||
203 | |||
204 | return ret; | ||
205 | } | ||
206 | |||
207 | int bbc_i2c_writeb(struct bbc_i2c_client *client, unsigned char val, int off) | ||
208 | { | ||
209 | struct bbc_i2c_bus *bp = client->bp; | ||
210 | int address = client->address; | ||
211 | u8 status; | ||
212 | int ret = -1; | ||
213 | |||
214 | if (bp->i2c_bussel_reg != NULL) | ||
215 | writeb(client->bus, bp->i2c_bussel_reg); | ||
216 | |||
217 | writeb(address, bp->i2c_control_regs + 0x1); | ||
218 | writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0); | ||
219 | if (wait_for_pin(bp, &status)) | ||
220 | goto out; | ||
221 | |||
222 | writeb(off, bp->i2c_control_regs + 0x1); | ||
223 | if (wait_for_pin(bp, &status) || | ||
224 | (status & I2C_PCF_LRB) != 0) | ||
225 | goto out; | ||
226 | |||
227 | writeb(val, bp->i2c_control_regs + 0x1); | ||
228 | if (wait_for_pin(bp, &status)) | ||
229 | goto out; | ||
230 | |||
231 | ret = 0; | ||
232 | |||
233 | out: | ||
234 | writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0); | ||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | int bbc_i2c_readb(struct bbc_i2c_client *client, unsigned char *byte, int off) | ||
239 | { | ||
240 | struct bbc_i2c_bus *bp = client->bp; | ||
241 | unsigned char address = client->address, status; | ||
242 | int ret = -1; | ||
243 | |||
244 | if (bp->i2c_bussel_reg != NULL) | ||
245 | writeb(client->bus, bp->i2c_bussel_reg); | ||
246 | |||
247 | writeb(address, bp->i2c_control_regs + 0x1); | ||
248 | writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0); | ||
249 | if (wait_for_pin(bp, &status)) | ||
250 | goto out; | ||
251 | |||
252 | writeb(off, bp->i2c_control_regs + 0x1); | ||
253 | if (wait_for_pin(bp, &status) || | ||
254 | (status & I2C_PCF_LRB) != 0) | ||
255 | goto out; | ||
256 | |||
257 | writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0); | ||
258 | |||
259 | address |= 0x1; /* READ */ | ||
260 | |||
261 | writeb(address, bp->i2c_control_regs + 0x1); | ||
262 | writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0); | ||
263 | if (wait_for_pin(bp, &status)) | ||
264 | goto out; | ||
265 | |||
266 | /* Set PIN back to one so the device sends the first | ||
267 | * byte. | ||
268 | */ | ||
269 | (void) readb(bp->i2c_control_regs + 0x1); | ||
270 | if (wait_for_pin(bp, &status)) | ||
271 | goto out; | ||
272 | |||
273 | writeb(I2C_PCF_ESO | I2C_PCF_ENI, bp->i2c_control_regs + 0x0); | ||
274 | *byte = readb(bp->i2c_control_regs + 0x1); | ||
275 | if (wait_for_pin(bp, &status)) | ||
276 | goto out; | ||
277 | |||
278 | ret = 0; | ||
279 | |||
280 | out: | ||
281 | writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0); | ||
282 | (void) readb(bp->i2c_control_regs + 0x1); | ||
283 | |||
284 | return ret; | ||
285 | } | ||
286 | |||
287 | int bbc_i2c_write_buf(struct bbc_i2c_client *client, | ||
288 | char *buf, int len, int off) | ||
289 | { | ||
290 | int ret = 0; | ||
291 | |||
292 | while (len > 0) { | ||
293 | int err = bbc_i2c_writeb(client, *buf, off); | ||
294 | |||
295 | if (err < 0) { | ||
296 | ret = err; | ||
297 | break; | ||
298 | } | ||
299 | |||
300 | len--; | ||
301 | buf++; | ||
302 | off++; | ||
303 | } | ||
304 | return ret; | ||
305 | } | ||
306 | |||
307 | int bbc_i2c_read_buf(struct bbc_i2c_client *client, | ||
308 | char *buf, int len, int off) | ||
309 | { | ||
310 | int ret = 0; | ||
311 | |||
312 | while (len > 0) { | ||
313 | int err = bbc_i2c_readb(client, buf, off); | ||
314 | if (err < 0) { | ||
315 | ret = err; | ||
316 | break; | ||
317 | } | ||
318 | len--; | ||
319 | buf++; | ||
320 | off++; | ||
321 | } | ||
322 | |||
323 | return ret; | ||
324 | } | ||
325 | |||
326 | EXPORT_SYMBOL(bbc_i2c_getdev); | ||
327 | EXPORT_SYMBOL(bbc_i2c_attach); | ||
328 | EXPORT_SYMBOL(bbc_i2c_detach); | ||
329 | EXPORT_SYMBOL(bbc_i2c_writeb); | ||
330 | EXPORT_SYMBOL(bbc_i2c_readb); | ||
331 | EXPORT_SYMBOL(bbc_i2c_write_buf); | ||
332 | EXPORT_SYMBOL(bbc_i2c_read_buf); | ||
333 | |||
334 | static irqreturn_t bbc_i2c_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
335 | { | ||
336 | struct bbc_i2c_bus *bp = dev_id; | ||
337 | |||
338 | /* PIN going from set to clear is the only event which | ||
339 | * makes the i2c assert an interrupt. | ||
340 | */ | ||
341 | if (bp->waiting && | ||
342 | !(readb(bp->i2c_control_regs + 0x0) & I2C_PCF_PIN)) | ||
343 | wake_up(&bp->wq); | ||
344 | |||
345 | return IRQ_HANDLED; | ||
346 | } | ||
347 | |||
348 | static void __init reset_one_i2c(struct bbc_i2c_bus *bp) | ||
349 | { | ||
350 | writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0); | ||
351 | writeb(bp->own, bp->i2c_control_regs + 0x1); | ||
352 | writeb(I2C_PCF_PIN | I2C_PCF_ES1, bp->i2c_control_regs + 0x0); | ||
353 | writeb(bp->clock, bp->i2c_control_regs + 0x1); | ||
354 | writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0); | ||
355 | } | ||
356 | |||
357 | static int __init attach_one_i2c(struct linux_ebus_device *edev, int index) | ||
358 | { | ||
359 | struct bbc_i2c_bus *bp = kmalloc(sizeof(*bp), GFP_KERNEL); | ||
360 | struct linux_ebus_child *echild; | ||
361 | int entry; | ||
362 | |||
363 | if (!bp) | ||
364 | return -ENOMEM; | ||
365 | memset(bp, 0, sizeof(*bp)); | ||
366 | |||
367 | bp->i2c_control_regs = ioremap(edev->resource[0].start, 0x2); | ||
368 | if (!bp->i2c_control_regs) | ||
369 | goto fail; | ||
370 | |||
371 | if (edev->num_addrs == 2) { | ||
372 | bp->i2c_bussel_reg = ioremap(edev->resource[1].start, 0x1); | ||
373 | if (!bp->i2c_bussel_reg) | ||
374 | goto fail; | ||
375 | } | ||
376 | |||
377 | bp->waiting = 0; | ||
378 | init_waitqueue_head(&bp->wq); | ||
379 | if (request_irq(edev->irqs[0], bbc_i2c_interrupt, | ||
380 | SA_SHIRQ, "bbc_i2c", bp)) | ||
381 | goto fail; | ||
382 | |||
383 | bp->index = index; | ||
384 | bp->bus_edev = edev; | ||
385 | |||
386 | spin_lock_init(&bp->lock); | ||
387 | bp->next = all_bbc_i2c; | ||
388 | all_bbc_i2c = bp; | ||
389 | |||
390 | entry = 0; | ||
391 | for (echild = edev->children; | ||
392 | echild && entry < 8; | ||
393 | echild = echild->next, entry++) { | ||
394 | bp->devs[entry].device = echild; | ||
395 | bp->devs[entry].client_claimed = 0; | ||
396 | } | ||
397 | |||
398 | writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0); | ||
399 | bp->own = readb(bp->i2c_control_regs + 0x01); | ||
400 | writeb(I2C_PCF_PIN | I2C_PCF_ES1, bp->i2c_control_regs + 0x0); | ||
401 | bp->clock = readb(bp->i2c_control_regs + 0x01); | ||
402 | |||
403 | printk(KERN_INFO "i2c-%d: Regs at %p, %d devices, own %02x, clock %02x.\n", | ||
404 | bp->index, bp->i2c_control_regs, entry, bp->own, bp->clock); | ||
405 | |||
406 | reset_one_i2c(bp); | ||
407 | |||
408 | return 0; | ||
409 | |||
410 | fail: | ||
411 | if (bp->i2c_bussel_reg) | ||
412 | iounmap(bp->i2c_bussel_reg); | ||
413 | if (bp->i2c_control_regs) | ||
414 | iounmap(bp->i2c_control_regs); | ||
415 | kfree(bp); | ||
416 | return -EINVAL; | ||
417 | } | ||
418 | |||
419 | static int __init bbc_present(void) | ||
420 | { | ||
421 | struct linux_ebus *ebus = NULL; | ||
422 | struct linux_ebus_device *edev = NULL; | ||
423 | |||
424 | for_each_ebus(ebus) { | ||
425 | for_each_ebusdev(edev, ebus) { | ||
426 | if (!strcmp(edev->prom_name, "bbc")) | ||
427 | return 1; | ||
428 | } | ||
429 | } | ||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | extern int bbc_envctrl_init(void); | ||
434 | extern void bbc_envctrl_cleanup(void); | ||
435 | static void bbc_i2c_cleanup(void); | ||
436 | |||
437 | static int __init bbc_i2c_init(void) | ||
438 | { | ||
439 | struct linux_ebus *ebus = NULL; | ||
440 | struct linux_ebus_device *edev = NULL; | ||
441 | int err, index = 0; | ||
442 | |||
443 | if (tlb_type != cheetah || !bbc_present()) | ||
444 | return -ENODEV; | ||
445 | |||
446 | for_each_ebus(ebus) { | ||
447 | for_each_ebusdev(edev, ebus) { | ||
448 | if (!strcmp(edev->prom_name, "i2c")) { | ||
449 | if (!attach_one_i2c(edev, index)) | ||
450 | index++; | ||
451 | } | ||
452 | } | ||
453 | } | ||
454 | |||
455 | if (!index) | ||
456 | return -ENODEV; | ||
457 | |||
458 | err = bbc_envctrl_init(); | ||
459 | if (err) | ||
460 | bbc_i2c_cleanup(); | ||
461 | return err; | ||
462 | } | ||
463 | |||
464 | static void bbc_i2c_cleanup(void) | ||
465 | { | ||
466 | struct bbc_i2c_bus *bp = all_bbc_i2c; | ||
467 | |||
468 | bbc_envctrl_cleanup(); | ||
469 | |||
470 | while (bp != NULL) { | ||
471 | struct bbc_i2c_bus *next = bp->next; | ||
472 | |||
473 | free_irq(bp->bus_edev->irqs[0], bp); | ||
474 | |||
475 | if (bp->i2c_bussel_reg) | ||
476 | iounmap(bp->i2c_bussel_reg); | ||
477 | if (bp->i2c_control_regs) | ||
478 | iounmap(bp->i2c_control_regs); | ||
479 | |||
480 | kfree(bp); | ||
481 | |||
482 | bp = next; | ||
483 | } | ||
484 | all_bbc_i2c = NULL; | ||
485 | } | ||
486 | |||
487 | module_init(bbc_i2c_init); | ||
488 | module_exit(bbc_i2c_cleanup); | ||
diff --git a/drivers/sbus/char/bbc_i2c.h b/drivers/sbus/char/bbc_i2c.h new file mode 100644 index 00000000000..fb01bd17704 --- /dev/null +++ b/drivers/sbus/char/bbc_i2c.h | |||
@@ -0,0 +1,20 @@ | |||
1 | /* $Id: bbc_i2c.h,v 1.2 2001/04/02 09:59:25 davem Exp $ */ | ||
2 | #ifndef _BBC_I2C_H | ||
3 | #define _BBC_I2C_H | ||
4 | |||
5 | #include <asm/ebus.h> | ||
6 | |||
7 | struct bbc_i2c_client; | ||
8 | |||
9 | /* Probing and attachment. */ | ||
10 | extern struct linux_ebus_child *bbc_i2c_getdev(int); | ||
11 | extern struct bbc_i2c_client *bbc_i2c_attach(struct linux_ebus_child *); | ||
12 | extern void bbc_i2c_detach(struct bbc_i2c_client *); | ||
13 | |||
14 | /* Register read/write. NOTE: Blocking! */ | ||
15 | extern int bbc_i2c_writeb(struct bbc_i2c_client *, unsigned char val, int off); | ||
16 | extern int bbc_i2c_readb(struct bbc_i2c_client *, unsigned char *byte, int off); | ||
17 | extern int bbc_i2c_write_buf(struct bbc_i2c_client *, char *buf, int len, int off); | ||
18 | extern int bbc_i2c_read_buf(struct bbc_i2c_client *, char *buf, int len, int off); | ||
19 | |||
20 | #endif /* _BBC_I2C_H */ | ||
diff --git a/drivers/sbus/char/bpp.c b/drivers/sbus/char/bpp.c new file mode 100644 index 00000000000..8f0f46907a8 --- /dev/null +++ b/drivers/sbus/char/bpp.c | |||
@@ -0,0 +1,1079 @@ | |||
1 | /* | ||
2 | * drivers/sbus/char/bpp.c | ||
3 | * | ||
4 | * Copyright (c) 1995 Picture Elements | ||
5 | * Stephen Williams (steve@icarus.com) | ||
6 | * Gus Baldauf (gbaldauf@ix.netcom.com) | ||
7 | * | ||
8 | * Linux/SPARC port by Peter Zaitcev. | ||
9 | * Integration into SPARC tree by Tom Dyas. | ||
10 | */ | ||
11 | |||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/fs.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/smp_lock.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | #include <linux/timer.h> | ||
21 | #include <linux/ioport.h> | ||
22 | #include <linux/major.h> | ||
23 | #include <linux/devfs_fs_kernel.h> | ||
24 | |||
25 | #include <asm/uaccess.h> | ||
26 | #include <asm/io.h> | ||
27 | |||
28 | #if defined(__i386__) | ||
29 | # include <asm/system.h> | ||
30 | #endif | ||
31 | |||
32 | #if defined(__sparc__) | ||
33 | # include <linux/init.h> | ||
34 | # include <linux/delay.h> /* udelay() */ | ||
35 | |||
36 | # include <asm/oplib.h> /* OpenProm Library */ | ||
37 | # include <asm/sbus.h> | ||
38 | #endif | ||
39 | |||
40 | #include <asm/bpp.h> | ||
41 | |||
42 | #define BPP_PROBE_CODE 0x55 | ||
43 | #define BPP_DELAY 100 | ||
44 | |||
45 | static const unsigned BPP_MAJOR = LP_MAJOR; | ||
46 | static const char* dev_name = "bpp"; | ||
47 | |||
48 | /* When switching from compatibility to a mode where I can read, try | ||
49 | the following mode first. */ | ||
50 | |||
51 | /* const unsigned char DEFAULT_ECP = 0x10; */ | ||
52 | static const unsigned char DEFAULT_ECP = 0x30; | ||
53 | static const unsigned char DEFAULT_NIBBLE = 0x00; | ||
54 | |||
55 | /* | ||
56 | * These are 1284 time constraints, in units of jiffies. | ||
57 | */ | ||
58 | |||
59 | static const unsigned long TIME_PSetup = 1; | ||
60 | static const unsigned long TIME_PResponse = 6; | ||
61 | static const unsigned long TIME_IDLE_LIMIT = 2000; | ||
62 | |||
63 | /* | ||
64 | * One instance per supported subdevice... | ||
65 | */ | ||
66 | # define BPP_NO 3 | ||
67 | |||
68 | enum IEEE_Mode { COMPATIBILITY, NIBBLE, ECP, ECP_RLE, EPP }; | ||
69 | |||
70 | struct inst { | ||
71 | unsigned present : 1; /* True if the hardware exists */ | ||
72 | unsigned enhanced : 1; /* True if the hardware in "enhanced" */ | ||
73 | unsigned opened : 1; /* True if the device is opened already */ | ||
74 | unsigned run_flag : 1; /* True if waiting for a repeate byte */ | ||
75 | |||
76 | unsigned char direction; /* 0 --> out, 0x20 --> IN */ | ||
77 | unsigned char pp_state; /* State of host controlled pins. */ | ||
78 | enum IEEE_Mode mode; | ||
79 | |||
80 | unsigned char run_length; | ||
81 | unsigned char repeat_byte; | ||
82 | |||
83 | /* These members manage timeouts for programmed delays */ | ||
84 | wait_queue_head_t wait_queue; | ||
85 | struct timer_list timer_list; | ||
86 | }; | ||
87 | |||
88 | static struct inst instances[BPP_NO]; | ||
89 | |||
90 | #if defined(__i386__) | ||
91 | |||
92 | static const unsigned short base_addrs[BPP_NO] = { 0x278, 0x378, 0x3bc }; | ||
93 | |||
94 | /* | ||
95 | * These are for data access. | ||
96 | * Control lines accesses are hidden in set_bits() and get_bits(). | ||
97 | * The exception is the probe procedure, which is system-dependent. | ||
98 | */ | ||
99 | #define bpp_outb_p(data, base) outb_p((data), (base)) | ||
100 | #define bpp_inb(base) inb(base) | ||
101 | #define bpp_inb_p(base) inb_p(base) | ||
102 | |||
103 | /* | ||
104 | * This method takes the pin values mask and sets the hardware pins to | ||
105 | * the requested value: 1 == high voltage, 0 == low voltage. This | ||
106 | * burries the annoying PC bit inversion and preserves the direction | ||
107 | * flag. | ||
108 | */ | ||
109 | static void set_pins(unsigned short pins, unsigned minor) | ||
110 | { | ||
111 | unsigned char bits = instances[minor].direction; /* == 0x20 */ | ||
112 | |||
113 | if (! (pins & BPP_PP_nStrobe)) bits |= 1; | ||
114 | if (! (pins & BPP_PP_nAutoFd)) bits |= 2; | ||
115 | if ( pins & BPP_PP_nInit) bits |= 4; | ||
116 | if (! (pins & BPP_PP_nSelectIn)) bits |= 8; | ||
117 | |||
118 | instances[minor].pp_state = bits; | ||
119 | |||
120 | outb_p(bits, base_addrs[minor]+2); | ||
121 | } | ||
122 | |||
123 | static unsigned short get_pins(unsigned minor) | ||
124 | { | ||
125 | unsigned short bits = 0; | ||
126 | |||
127 | unsigned value = instances[minor].pp_state; | ||
128 | if (! (value & 0x01)) bits |= BPP_PP_nStrobe; | ||
129 | if (! (value & 0x02)) bits |= BPP_PP_nAutoFd; | ||
130 | if (value & 0x04) bits |= BPP_PP_nInit; | ||
131 | if (! (value & 0x08)) bits |= BPP_PP_nSelectIn; | ||
132 | |||
133 | value = inb_p(base_addrs[minor]+1); | ||
134 | if (value & 0x08) bits |= BPP_GP_nFault; | ||
135 | if (value & 0x10) bits |= BPP_GP_Select; | ||
136 | if (value & 0x20) bits |= BPP_GP_PError; | ||
137 | if (value & 0x40) bits |= BPP_GP_nAck; | ||
138 | if (! (value & 0x80)) bits |= BPP_GP_Busy; | ||
139 | |||
140 | return bits; | ||
141 | } | ||
142 | |||
143 | #endif /* __i386__ */ | ||
144 | |||
145 | #if defined(__sparc__) | ||
146 | |||
147 | /* | ||
148 | * Register block | ||
149 | */ | ||
150 | /* DMA registers */ | ||
151 | #define BPP_CSR 0x00 | ||
152 | #define BPP_ADDR 0x04 | ||
153 | #define BPP_BCNT 0x08 | ||
154 | #define BPP_TST_CSR 0x0C | ||
155 | /* Parallel Port registers */ | ||
156 | #define BPP_HCR 0x10 | ||
157 | #define BPP_OCR 0x12 | ||
158 | #define BPP_DR 0x14 | ||
159 | #define BPP_TCR 0x15 | ||
160 | #define BPP_OR 0x16 | ||
161 | #define BPP_IR 0x17 | ||
162 | #define BPP_ICR 0x18 | ||
163 | #define BPP_SIZE 0x1A | ||
164 | |||
165 | /* BPP_CSR. Bits of type RW1 are cleared with writting '1'. */ | ||
166 | #define P_DEV_ID_MASK 0xf0000000 /* R */ | ||
167 | #define P_DEV_ID_ZEBRA 0x40000000 | ||
168 | #define P_DEV_ID_L64854 0xa0000000 /* == NCR 89C100+89C105. Pity. */ | ||
169 | #define P_NA_LOADED 0x08000000 /* R NA wirtten but was not used */ | ||
170 | #define P_A_LOADED 0x04000000 /* R */ | ||
171 | #define P_DMA_ON 0x02000000 /* R DMA is not disabled */ | ||
172 | #define P_EN_NEXT 0x01000000 /* RW */ | ||
173 | #define P_TCI_DIS 0x00800000 /* RW TCI forbidden from interrupts */ | ||
174 | #define P_DIAG 0x00100000 /* RW Disables draining and resetting | ||
175 | of P-FIFO on loading of P_ADDR*/ | ||
176 | #define P_BURST_SIZE 0x000c0000 /* RW SBus burst size */ | ||
177 | #define P_BURST_8 0x00000000 | ||
178 | #define P_BURST_4 0x00040000 | ||
179 | #define P_BURST_1 0x00080000 /* "No burst" write */ | ||
180 | #define P_TC 0x00004000 /* RW1 Term Count, can be cleared when | ||
181 | P_EN_NEXT=1 */ | ||
182 | #define P_EN_CNT 0x00002000 /* RW */ | ||
183 | #define P_EN_DMA 0x00000200 /* RW */ | ||
184 | #define P_WRITE 0x00000100 /* R DMA dir, 1=to ram, 0=to port */ | ||
185 | #define P_RESET 0x00000080 /* RW */ | ||
186 | #define P_SLAVE_ERR 0x00000040 /* RW1 Access size error */ | ||
187 | #define P_INVALIDATE 0x00000020 /* W Drop P-FIFO */ | ||
188 | #define P_INT_EN 0x00000010 /* RW OK to P_INT_PEND||P_ERR_PEND */ | ||
189 | #define P_DRAINING 0x0000000c /* R P-FIFO is draining to memory */ | ||
190 | #define P_ERR_PEND 0x00000002 /* R */ | ||
191 | #define P_INT_PEND 0x00000001 /* R */ | ||
192 | |||
193 | /* BPP_HCR. Time is in increments of SBus clock. */ | ||
194 | #define P_HCR_TEST 0x8000 /* Allows buried counters to be read */ | ||
195 | #define P_HCR_DSW 0x7f00 /* Data strobe width (in ticks) */ | ||
196 | #define P_HCR_DDS 0x007f /* Data setup before strobe (in ticks) */ | ||
197 | |||
198 | /* BPP_OCR. */ | ||
199 | #define P_OCR_MEM_CLR 0x8000 | ||
200 | #define P_OCR_DATA_SRC 0x4000 /* ) */ | ||
201 | #define P_OCR_DS_DSEL 0x2000 /* ) Bidirectional */ | ||
202 | #define P_OCR_BUSY_DSEL 0x1000 /* ) selects */ | ||
203 | #define P_OCR_ACK_DSEL 0x0800 /* ) */ | ||
204 | #define P_OCR_EN_DIAG 0x0400 | ||
205 | #define P_OCR_BUSY_OP 0x0200 /* Busy operation */ | ||
206 | #define P_OCR_ACK_OP 0x0100 /* Ack operation */ | ||
207 | #define P_OCR_SRST 0x0080 /* Reset state machines. Not selfcleaning. */ | ||
208 | #define P_OCR_IDLE 0x0008 /* PP data transfer state machine is idle */ | ||
209 | #define P_OCR_V_ILCK 0x0002 /* Versatec faded. Zebra only. */ | ||
210 | #define P_OCR_EN_VER 0x0001 /* Enable Versatec (0 - enable). Zebra only. */ | ||
211 | |||
212 | /* BPP_TCR */ | ||
213 | #define P_TCR_DIR 0x08 | ||
214 | #define P_TCR_BUSY 0x04 | ||
215 | #define P_TCR_ACK 0x02 | ||
216 | #define P_TCR_DS 0x01 /* Strobe */ | ||
217 | |||
218 | /* BPP_OR */ | ||
219 | #define P_OR_V3 0x20 /* ) */ | ||
220 | #define P_OR_V2 0x10 /* ) on Zebra only */ | ||
221 | #define P_OR_V1 0x08 /* ) */ | ||
222 | #define P_OR_INIT 0x04 | ||
223 | #define P_OR_AFXN 0x02 /* Auto Feed */ | ||
224 | #define P_OR_SLCT_IN 0x01 | ||
225 | |||
226 | /* BPP_IR */ | ||
227 | #define P_IR_PE 0x04 | ||
228 | #define P_IR_SLCT 0x02 | ||
229 | #define P_IR_ERR 0x01 | ||
230 | |||
231 | /* BPP_ICR */ | ||
232 | #define P_DS_IRQ 0x8000 /* RW1 */ | ||
233 | #define P_ACK_IRQ 0x4000 /* RW1 */ | ||
234 | #define P_BUSY_IRQ 0x2000 /* RW1 */ | ||
235 | #define P_PE_IRQ 0x1000 /* RW1 */ | ||
236 | #define P_SLCT_IRQ 0x0800 /* RW1 */ | ||
237 | #define P_ERR_IRQ 0x0400 /* RW1 */ | ||
238 | #define P_DS_IRQ_EN 0x0200 /* RW Always on rising edge */ | ||
239 | #define P_ACK_IRQ_EN 0x0100 /* RW Always on rising edge */ | ||
240 | #define P_BUSY_IRP 0x0080 /* RW 1= rising edge */ | ||
241 | #define P_BUSY_IRQ_EN 0x0040 /* RW */ | ||
242 | #define P_PE_IRP 0x0020 /* RW 1= rising edge */ | ||
243 | #define P_PE_IRQ_EN 0x0010 /* RW */ | ||
244 | #define P_SLCT_IRP 0x0008 /* RW 1= rising edge */ | ||
245 | #define P_SLCT_IRQ_EN 0x0004 /* RW */ | ||
246 | #define P_ERR_IRP 0x0002 /* RW1 1= rising edge */ | ||
247 | #define P_ERR_IRQ_EN 0x0001 /* RW */ | ||
248 | |||
249 | static void __iomem *base_addrs[BPP_NO]; | ||
250 | |||
251 | #define bpp_outb_p(data, base) sbus_writeb(data, (base) + BPP_DR) | ||
252 | #define bpp_inb_p(base) sbus_readb((base) + BPP_DR) | ||
253 | #define bpp_inb(base) sbus_readb((base) + BPP_DR) | ||
254 | |||
255 | static void set_pins(unsigned short pins, unsigned minor) | ||
256 | { | ||
257 | void __iomem *base = base_addrs[minor]; | ||
258 | unsigned char bits_tcr = 0, bits_or = 0; | ||
259 | |||
260 | if (instances[minor].direction & 0x20) bits_tcr |= P_TCR_DIR; | ||
261 | if ( pins & BPP_PP_nStrobe) bits_tcr |= P_TCR_DS; | ||
262 | |||
263 | if ( pins & BPP_PP_nAutoFd) bits_or |= P_OR_AFXN; | ||
264 | if (! (pins & BPP_PP_nInit)) bits_or |= P_OR_INIT; | ||
265 | if (! (pins & BPP_PP_nSelectIn)) bits_or |= P_OR_SLCT_IN; | ||
266 | |||
267 | sbus_writeb(bits_or, base + BPP_OR); | ||
268 | sbus_writeb(bits_tcr, base + BPP_TCR); | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * i386 people read output pins from a software image. | ||
273 | * We may get them back from hardware. | ||
274 | * Again, inversion of pins must he buried here. | ||
275 | */ | ||
276 | static unsigned short get_pins(unsigned minor) | ||
277 | { | ||
278 | void __iomem *base = base_addrs[minor]; | ||
279 | unsigned short bits = 0; | ||
280 | unsigned value_tcr = sbus_readb(base + BPP_TCR); | ||
281 | unsigned value_ir = sbus_readb(base + BPP_IR); | ||
282 | unsigned value_or = sbus_readb(base + BPP_OR); | ||
283 | |||
284 | if (value_tcr & P_TCR_DS) bits |= BPP_PP_nStrobe; | ||
285 | if (value_or & P_OR_AFXN) bits |= BPP_PP_nAutoFd; | ||
286 | if (! (value_or & P_OR_INIT)) bits |= BPP_PP_nInit; | ||
287 | if (! (value_or & P_OR_SLCT_IN)) bits |= BPP_PP_nSelectIn; | ||
288 | |||
289 | if (value_ir & P_IR_ERR) bits |= BPP_GP_nFault; | ||
290 | if (! (value_ir & P_IR_SLCT)) bits |= BPP_GP_Select; | ||
291 | if (! (value_ir & P_IR_PE)) bits |= BPP_GP_PError; | ||
292 | if (! (value_tcr & P_TCR_ACK)) bits |= BPP_GP_nAck; | ||
293 | if (value_tcr & P_TCR_BUSY) bits |= BPP_GP_Busy; | ||
294 | |||
295 | return bits; | ||
296 | } | ||
297 | |||
298 | #endif /* __sparc__ */ | ||
299 | |||
300 | static void bpp_wake_up(unsigned long val) | ||
301 | { wake_up(&instances[val].wait_queue); } | ||
302 | |||
303 | static void snooze(unsigned long snooze_time, unsigned minor) | ||
304 | { | ||
305 | init_timer(&instances[minor].timer_list); | ||
306 | instances[minor].timer_list.expires = jiffies + snooze_time + 1; | ||
307 | instances[minor].timer_list.data = minor; | ||
308 | add_timer(&instances[minor].timer_list); | ||
309 | sleep_on (&instances[minor].wait_queue); | ||
310 | } | ||
311 | |||
312 | static int wait_for(unsigned short set, unsigned short clr, | ||
313 | unsigned long delay, unsigned minor) | ||
314 | { | ||
315 | unsigned short pins = get_pins(minor); | ||
316 | |||
317 | unsigned long extime = 0; | ||
318 | |||
319 | /* | ||
320 | * Try a real fast scan for the first jiffy, in case the device | ||
321 | * responds real good. The first while loop guesses an expire | ||
322 | * time accounting for possible wraparound of jiffies. | ||
323 | */ | ||
324 | while (time_after_eq(jiffies, extime)) extime = jiffies + 1; | ||
325 | while ( (time_before(jiffies, extime)) | ||
326 | && (((pins & set) != set) || ((pins & clr) != 0)) ) { | ||
327 | pins = get_pins(minor); | ||
328 | } | ||
329 | |||
330 | delay -= 1; | ||
331 | |||
332 | /* | ||
333 | * If my delay expired or the pins are still not where I want | ||
334 | * them, then resort to using the timer and greatly reduce my | ||
335 | * sample rate. If the peripheral is going to be slow, this will | ||
336 | * give the CPU up to some more worthy process. | ||
337 | */ | ||
338 | while ( delay && (((pins & set) != set) || ((pins & clr) != 0)) ) { | ||
339 | |||
340 | snooze(1, minor); | ||
341 | pins = get_pins(minor); | ||
342 | delay -= 1; | ||
343 | } | ||
344 | |||
345 | if (delay == 0) return -1; | ||
346 | else return pins; | ||
347 | } | ||
348 | |||
349 | /* | ||
350 | * Return ZERO(0) If the negotiation succeeds, an errno otherwise. An | ||
351 | * errno means something broke, and I do not yet know how to fix it. | ||
352 | */ | ||
353 | static int negotiate(unsigned char mode, unsigned minor) | ||
354 | { | ||
355 | int rc; | ||
356 | unsigned short pins = get_pins(minor); | ||
357 | if (pins & BPP_PP_nSelectIn) return -EIO; | ||
358 | |||
359 | |||
360 | /* Event 0: Write the mode to the data lines */ | ||
361 | bpp_outb_p(mode, base_addrs[minor]); | ||
362 | |||
363 | snooze(TIME_PSetup, minor); | ||
364 | |||
365 | /* Event 1: Strobe the mode code into the peripheral */ | ||
366 | set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nInit, minor); | ||
367 | |||
368 | /* Wait for Event 2: Peripheral responds as a 1284 device. */ | ||
369 | rc = wait_for(BPP_GP_PError|BPP_GP_Select|BPP_GP_nFault, | ||
370 | BPP_GP_nAck, | ||
371 | TIME_PResponse, | ||
372 | minor); | ||
373 | |||
374 | if (rc == -1) return -ETIMEDOUT; | ||
375 | |||
376 | /* Event 3: latch extensibility request */ | ||
377 | set_pins(BPP_PP_nSelectIn|BPP_PP_nInit, minor); | ||
378 | |||
379 | /* ... quick nap while peripheral ponders the byte i'm sending...*/ | ||
380 | snooze(1, minor); | ||
381 | |||
382 | /* Event 4: restore strobe, to ACK peripheral's response. */ | ||
383 | set_pins(BPP_PP_nSelectIn|BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor); | ||
384 | |||
385 | /* Wait for Event 6: Peripheral latches response bits */ | ||
386 | rc = wait_for(BPP_GP_nAck, 0, TIME_PSetup+TIME_PResponse, minor); | ||
387 | if (rc == -1) return -EIO; | ||
388 | |||
389 | /* A 1284 device cannot refuse nibble mode */ | ||
390 | if (mode == DEFAULT_NIBBLE) return 0; | ||
391 | |||
392 | if (pins & BPP_GP_Select) return 0; | ||
393 | |||
394 | return -EPROTONOSUPPORT; | ||
395 | } | ||
396 | |||
397 | static int terminate(unsigned minor) | ||
398 | { | ||
399 | int rc; | ||
400 | |||
401 | /* Event 22: Request termination of 1284 mode */ | ||
402 | set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor); | ||
403 | |||
404 | /* Wait for Events 23 and 24: ACK termination request. */ | ||
405 | rc = wait_for(BPP_GP_Busy|BPP_GP_nFault, | ||
406 | BPP_GP_nAck, | ||
407 | TIME_PSetup+TIME_PResponse, | ||
408 | minor); | ||
409 | |||
410 | instances[minor].direction = 0; | ||
411 | instances[minor].mode = COMPATIBILITY; | ||
412 | |||
413 | if (rc == -1) { | ||
414 | return -EIO; | ||
415 | } | ||
416 | |||
417 | /* Event 25: Handshake by lowering nAutoFd */ | ||
418 | set_pins(BPP_PP_nStrobe|BPP_PP_nInit, minor); | ||
419 | |||
420 | /* Event 26: Peripheral wiggles lines... */ | ||
421 | |||
422 | /* Event 27: Peripheral sets nAck HIGH to ack handshake */ | ||
423 | rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor); | ||
424 | if (rc == -1) { | ||
425 | set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor); | ||
426 | return -EIO; | ||
427 | } | ||
428 | |||
429 | /* Event 28: Finish phase by raising nAutoFd */ | ||
430 | set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor); | ||
431 | |||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | static DEFINE_SPINLOCK(bpp_open_lock); | ||
436 | |||
437 | /* | ||
438 | * Allow only one process to open the device at a time. | ||
439 | */ | ||
440 | static int bpp_open(struct inode *inode, struct file *f) | ||
441 | { | ||
442 | unsigned minor = iminor(inode); | ||
443 | int ret; | ||
444 | |||
445 | spin_lock(&bpp_open_lock); | ||
446 | ret = 0; | ||
447 | if (minor >= BPP_NO) { | ||
448 | ret = -ENODEV; | ||
449 | } else { | ||
450 | if (! instances[minor].present) { | ||
451 | ret = -ENODEV; | ||
452 | } else { | ||
453 | if (instances[minor].opened) | ||
454 | ret = -EBUSY; | ||
455 | else | ||
456 | instances[minor].opened = 1; | ||
457 | } | ||
458 | } | ||
459 | spin_unlock(&bpp_open_lock); | ||
460 | |||
461 | return ret; | ||
462 | } | ||
463 | |||
464 | /* | ||
465 | * When the process closes the device, this method is called to clean | ||
466 | * up and reset the hardware. Always leave the device in compatibility | ||
467 | * mode as this is a reasonable place to clean up from messes made by | ||
468 | * ioctls, or other mayhem. | ||
469 | */ | ||
470 | static int bpp_release(struct inode *inode, struct file *f) | ||
471 | { | ||
472 | unsigned minor = iminor(inode); | ||
473 | |||
474 | spin_lock(&bpp_open_lock); | ||
475 | instances[minor].opened = 0; | ||
476 | |||
477 | if (instances[minor].mode != COMPATIBILITY) | ||
478 | terminate(minor); | ||
479 | |||
480 | spin_unlock(&bpp_open_lock); | ||
481 | |||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | static long read_nibble(unsigned minor, char __user *c, unsigned long cnt) | ||
486 | { | ||
487 | unsigned long remaining = cnt; | ||
488 | long rc; | ||
489 | |||
490 | while (remaining > 0) { | ||
491 | unsigned char byte = 0; | ||
492 | int pins; | ||
493 | |||
494 | /* Event 7: request nibble */ | ||
495 | set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe, minor); | ||
496 | |||
497 | /* Wait for event 9: Peripher strobes first nibble */ | ||
498 | pins = wait_for(0, BPP_GP_nAck, TIME_IDLE_LIMIT, minor); | ||
499 | if (pins == -1) return -ETIMEDOUT; | ||
500 | |||
501 | /* Event 10: I handshake nibble */ | ||
502 | set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nAutoFd, minor); | ||
503 | if (pins & BPP_GP_nFault) byte |= 0x01; | ||
504 | if (pins & BPP_GP_Select) byte |= 0x02; | ||
505 | if (pins & BPP_GP_PError) byte |= 0x04; | ||
506 | if (pins & BPP_GP_Busy) byte |= 0x08; | ||
507 | |||
508 | /* Wait for event 11: Peripheral handshakes nibble */ | ||
509 | rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor); | ||
510 | |||
511 | /* Event 7: request nibble */ | ||
512 | set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe, minor); | ||
513 | |||
514 | /* Wait for event 9: Peripher strobes first nibble */ | ||
515 | pins = wait_for(0, BPP_GP_nAck, TIME_PResponse, minor); | ||
516 | if (rc == -1) return -ETIMEDOUT; | ||
517 | |||
518 | /* Event 10: I handshake nibble */ | ||
519 | set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nAutoFd, minor); | ||
520 | if (pins & BPP_GP_nFault) byte |= 0x10; | ||
521 | if (pins & BPP_GP_Select) byte |= 0x20; | ||
522 | if (pins & BPP_GP_PError) byte |= 0x40; | ||
523 | if (pins & BPP_GP_Busy) byte |= 0x80; | ||
524 | |||
525 | if (put_user(byte, c)) | ||
526 | return -EFAULT; | ||
527 | c += 1; | ||
528 | remaining -= 1; | ||
529 | |||
530 | /* Wait for event 11: Peripheral handshakes nibble */ | ||
531 | rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor); | ||
532 | if (rc == -1) return -EIO; | ||
533 | } | ||
534 | |||
535 | return cnt - remaining; | ||
536 | } | ||
537 | |||
538 | static long read_ecp(unsigned minor, char __user *c, unsigned long cnt) | ||
539 | { | ||
540 | unsigned long remaining; | ||
541 | long rc; | ||
542 | |||
543 | /* Turn ECP mode from forward to reverse if needed. */ | ||
544 | if (! instances[minor].direction) { | ||
545 | unsigned short pins = get_pins(minor); | ||
546 | |||
547 | /* Event 38: Turn the bus around */ | ||
548 | instances[minor].direction = 0x20; | ||
549 | pins &= ~BPP_PP_nAutoFd; | ||
550 | set_pins(pins, minor); | ||
551 | |||
552 | /* Event 39: Set pins for reverse mode. */ | ||
553 | snooze(TIME_PSetup, minor); | ||
554 | set_pins(BPP_PP_nStrobe|BPP_PP_nSelectIn, minor); | ||
555 | |||
556 | /* Wait for event 40: Peripheral ready to be strobed */ | ||
557 | rc = wait_for(0, BPP_GP_PError, TIME_PResponse, minor); | ||
558 | if (rc == -1) return -ETIMEDOUT; | ||
559 | } | ||
560 | |||
561 | remaining = cnt; | ||
562 | |||
563 | while (remaining > 0) { | ||
564 | |||
565 | /* If there is a run length for a repeated byte, repeat */ | ||
566 | /* that byte a few times. */ | ||
567 | if (instances[minor].run_length && !instances[minor].run_flag) { | ||
568 | |||
569 | char buffer[128]; | ||
570 | unsigned idx; | ||
571 | unsigned repeat = remaining < instances[minor].run_length | ||
572 | ? remaining | ||
573 | : instances[minor].run_length; | ||
574 | |||
575 | for (idx = 0 ; idx < repeat ; idx += 1) | ||
576 | buffer[idx] = instances[minor].repeat_byte; | ||
577 | |||
578 | if (copy_to_user(c, buffer, repeat)) | ||
579 | return -EFAULT; | ||
580 | remaining -= repeat; | ||
581 | c += repeat; | ||
582 | instances[minor].run_length -= repeat; | ||
583 | } | ||
584 | |||
585 | if (remaining == 0) break; | ||
586 | |||
587 | |||
588 | /* Wait for Event 43: Data active on the bus. */ | ||
589 | rc = wait_for(0, BPP_GP_nAck, TIME_IDLE_LIMIT, minor); | ||
590 | if (rc == -1) break; | ||
591 | |||
592 | if (rc & BPP_GP_Busy) { | ||
593 | /* OK, this is data. read it in. */ | ||
594 | unsigned char byte = bpp_inb(base_addrs[minor]); | ||
595 | if (put_user(byte, c)) | ||
596 | return -EFAULT; | ||
597 | c += 1; | ||
598 | remaining -= 1; | ||
599 | |||
600 | if (instances[minor].run_flag) { | ||
601 | instances[minor].repeat_byte = byte; | ||
602 | instances[minor].run_flag = 0; | ||
603 | } | ||
604 | |||
605 | } else { | ||
606 | unsigned char byte = bpp_inb(base_addrs[minor]); | ||
607 | if (byte & 0x80) { | ||
608 | printk("bpp%d: " | ||
609 | "Ignoring ECP channel %u from device.\n", | ||
610 | minor, byte & 0x7f); | ||
611 | } else { | ||
612 | instances[minor].run_length = byte; | ||
613 | instances[minor].run_flag = 1; | ||
614 | } | ||
615 | } | ||
616 | |||
617 | /* Event 44: I got it. */ | ||
618 | set_pins(BPP_PP_nStrobe|BPP_PP_nAutoFd|BPP_PP_nSelectIn, minor); | ||
619 | |||
620 | /* Wait for event 45: peripheral handshake */ | ||
621 | rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor); | ||
622 | if (rc == -1) return -ETIMEDOUT; | ||
623 | |||
624 | /* Event 46: Finish handshake */ | ||
625 | set_pins(BPP_PP_nStrobe|BPP_PP_nSelectIn, minor); | ||
626 | |||
627 | } | ||
628 | |||
629 | |||
630 | return cnt - remaining; | ||
631 | } | ||
632 | |||
633 | static ssize_t bpp_read(struct file *f, char __user *c, size_t cnt, loff_t * ppos) | ||
634 | { | ||
635 | long rc; | ||
636 | unsigned minor = iminor(f->f_dentry->d_inode); | ||
637 | if (minor >= BPP_NO) return -ENODEV; | ||
638 | if (!instances[minor].present) return -ENODEV; | ||
639 | |||
640 | switch (instances[minor].mode) { | ||
641 | |||
642 | default: | ||
643 | if (instances[minor].mode != COMPATIBILITY) | ||
644 | terminate(minor); | ||
645 | |||
646 | if (instances[minor].enhanced) { | ||
647 | /* For now, do all reads with ECP-RLE mode */ | ||
648 | unsigned short pins; | ||
649 | |||
650 | rc = negotiate(DEFAULT_ECP, minor); | ||
651 | if (rc < 0) break; | ||
652 | |||
653 | instances[minor].mode = ECP_RLE; | ||
654 | |||
655 | /* Event 30: set nAutoFd low to setup for ECP mode */ | ||
656 | pins = get_pins(minor); | ||
657 | pins &= ~BPP_PP_nAutoFd; | ||
658 | set_pins(pins, minor); | ||
659 | |||
660 | /* Wait for Event 31: peripheral ready */ | ||
661 | rc = wait_for(BPP_GP_PError, 0, TIME_PResponse, minor); | ||
662 | if (rc == -1) return -ETIMEDOUT; | ||
663 | |||
664 | rc = read_ecp(minor, c, cnt); | ||
665 | |||
666 | } else { | ||
667 | rc = negotiate(DEFAULT_NIBBLE, minor); | ||
668 | if (rc < 0) break; | ||
669 | |||
670 | instances[minor].mode = NIBBLE; | ||
671 | |||
672 | rc = read_nibble(minor, c, cnt); | ||
673 | } | ||
674 | break; | ||
675 | |||
676 | case NIBBLE: | ||
677 | rc = read_nibble(minor, c, cnt); | ||
678 | break; | ||
679 | |||
680 | case ECP: | ||
681 | case ECP_RLE: | ||
682 | rc = read_ecp(minor, c, cnt); | ||
683 | break; | ||
684 | |||
685 | } | ||
686 | |||
687 | |||
688 | return rc; | ||
689 | } | ||
690 | |||
691 | /* | ||
692 | * Compatibility mode handshaking is a matter of writing data, | ||
693 | * strobing it, and waiting for the printer to stop being busy. | ||
694 | */ | ||
695 | static long write_compat(unsigned minor, const char __user *c, unsigned long cnt) | ||
696 | { | ||
697 | long rc; | ||
698 | unsigned short pins = get_pins(minor); | ||
699 | |||
700 | unsigned long remaining = cnt; | ||
701 | |||
702 | |||
703 | while (remaining > 0) { | ||
704 | unsigned char byte; | ||
705 | |||
706 | if (get_user(byte, c)) | ||
707 | return -EFAULT; | ||
708 | c += 1; | ||
709 | |||
710 | rc = wait_for(BPP_GP_nAck, BPP_GP_Busy, TIME_IDLE_LIMIT, minor); | ||
711 | if (rc == -1) return -ETIMEDOUT; | ||
712 | |||
713 | bpp_outb_p(byte, base_addrs[minor]); | ||
714 | remaining -= 1; | ||
715 | /* snooze(1, minor); */ | ||
716 | |||
717 | pins &= ~BPP_PP_nStrobe; | ||
718 | set_pins(pins, minor); | ||
719 | |||
720 | rc = wait_for(BPP_GP_Busy, 0, TIME_PResponse, minor); | ||
721 | |||
722 | pins |= BPP_PP_nStrobe; | ||
723 | set_pins(pins, minor); | ||
724 | } | ||
725 | |||
726 | return cnt - remaining; | ||
727 | } | ||
728 | |||
729 | /* | ||
730 | * Write data using ECP mode. Watch out that the port may be set up | ||
731 | * for reading. If so, turn the port around. | ||
732 | */ | ||
733 | static long write_ecp(unsigned minor, const char __user *c, unsigned long cnt) | ||
734 | { | ||
735 | unsigned short pins = get_pins(minor); | ||
736 | unsigned long remaining = cnt; | ||
737 | |||
738 | if (instances[minor].direction) { | ||
739 | int rc; | ||
740 | |||
741 | /* Event 47 Request bus be turned around */ | ||
742 | pins |= BPP_PP_nInit; | ||
743 | set_pins(pins, minor); | ||
744 | |||
745 | /* Wait for Event 49: Peripheral relinquished bus */ | ||
746 | rc = wait_for(BPP_GP_PError, 0, TIME_PResponse, minor); | ||
747 | |||
748 | pins |= BPP_PP_nAutoFd; | ||
749 | instances[minor].direction = 0; | ||
750 | set_pins(pins, minor); | ||
751 | } | ||
752 | |||
753 | while (remaining > 0) { | ||
754 | unsigned char byte; | ||
755 | int rc; | ||
756 | |||
757 | if (get_user(byte, c)) | ||
758 | return -EFAULT; | ||
759 | |||
760 | rc = wait_for(0, BPP_GP_Busy, TIME_PResponse, minor); | ||
761 | if (rc == -1) return -ETIMEDOUT; | ||
762 | |||
763 | c += 1; | ||
764 | |||
765 | bpp_outb_p(byte, base_addrs[minor]); | ||
766 | |||
767 | pins &= ~BPP_PP_nStrobe; | ||
768 | set_pins(pins, minor); | ||
769 | |||
770 | pins |= BPP_PP_nStrobe; | ||
771 | rc = wait_for(BPP_GP_Busy, 0, TIME_PResponse, minor); | ||
772 | if (rc == -1) return -EIO; | ||
773 | |||
774 | set_pins(pins, minor); | ||
775 | } | ||
776 | |||
777 | return cnt - remaining; | ||
778 | } | ||
779 | |||
780 | /* | ||
781 | * Write to the peripheral. Be sensitive of the current mode. If I'm | ||
782 | * in a mode that can be turned around (ECP) then just do | ||
783 | * that. Otherwise, terminate and do my writing in compat mode. This | ||
784 | * is the safest course as any device can handle it. | ||
785 | */ | ||
786 | static ssize_t bpp_write(struct file *f, const char __user *c, size_t cnt, loff_t * ppos) | ||
787 | { | ||
788 | long errno = 0; | ||
789 | unsigned minor = iminor(f->f_dentry->d_inode); | ||
790 | if (minor >= BPP_NO) return -ENODEV; | ||
791 | if (!instances[minor].present) return -ENODEV; | ||
792 | |||
793 | switch (instances[minor].mode) { | ||
794 | |||
795 | case ECP: | ||
796 | case ECP_RLE: | ||
797 | errno = write_ecp(minor, c, cnt); | ||
798 | break; | ||
799 | case COMPATIBILITY: | ||
800 | errno = write_compat(minor, c, cnt); | ||
801 | break; | ||
802 | default: | ||
803 | terminate(minor); | ||
804 | errno = write_compat(minor, c, cnt); | ||
805 | } | ||
806 | |||
807 | return errno; | ||
808 | } | ||
809 | |||
810 | static int bpp_ioctl(struct inode *inode, struct file *f, unsigned int cmd, | ||
811 | unsigned long arg) | ||
812 | { | ||
813 | int errno = 0; | ||
814 | |||
815 | unsigned minor = iminor(inode); | ||
816 | if (minor >= BPP_NO) return -ENODEV; | ||
817 | if (!instances[minor].present) return -ENODEV; | ||
818 | |||
819 | |||
820 | switch (cmd) { | ||
821 | |||
822 | case BPP_PUT_PINS: | ||
823 | set_pins(arg, minor); | ||
824 | break; | ||
825 | |||
826 | case BPP_GET_PINS: | ||
827 | errno = get_pins(minor); | ||
828 | break; | ||
829 | |||
830 | case BPP_PUT_DATA: | ||
831 | bpp_outb_p(arg, base_addrs[minor]); | ||
832 | break; | ||
833 | |||
834 | case BPP_GET_DATA: | ||
835 | errno = bpp_inb_p(base_addrs[minor]); | ||
836 | break; | ||
837 | |||
838 | case BPP_SET_INPUT: | ||
839 | if (arg) | ||
840 | if (instances[minor].enhanced) { | ||
841 | unsigned short bits = get_pins(minor); | ||
842 | instances[minor].direction = 0x20; | ||
843 | set_pins(bits, minor); | ||
844 | } else { | ||
845 | errno = -ENOTTY; | ||
846 | } | ||
847 | else { | ||
848 | unsigned short bits = get_pins(minor); | ||
849 | instances[minor].direction = 0x00; | ||
850 | set_pins(bits, minor); | ||
851 | } | ||
852 | break; | ||
853 | |||
854 | default: | ||
855 | errno = -EINVAL; | ||
856 | } | ||
857 | |||
858 | return errno; | ||
859 | } | ||
860 | |||
861 | static struct file_operations bpp_fops = { | ||
862 | .owner = THIS_MODULE, | ||
863 | .read = bpp_read, | ||
864 | .write = bpp_write, | ||
865 | .ioctl = bpp_ioctl, | ||
866 | .open = bpp_open, | ||
867 | .release = bpp_release, | ||
868 | }; | ||
869 | |||
870 | #if defined(__i386__) | ||
871 | |||
872 | #define collectLptPorts() {} | ||
873 | |||
874 | static void probeLptPort(unsigned idx) | ||
875 | { | ||
876 | unsigned int testvalue; | ||
877 | const unsigned short lpAddr = base_addrs[idx]; | ||
878 | |||
879 | instances[idx].present = 0; | ||
880 | instances[idx].enhanced = 0; | ||
881 | instances[idx].direction = 0; | ||
882 | instances[idx].mode = COMPATIBILITY; | ||
883 | instances[idx].wait_queue = 0; | ||
884 | instances[idx].run_length = 0; | ||
885 | instances[idx].run_flag = 0; | ||
886 | init_timer(&instances[idx].timer_list); | ||
887 | instances[idx].timer_list.function = bpp_wake_up; | ||
888 | if (!request_region(lpAddr,3, dev_name)) return; | ||
889 | |||
890 | /* | ||
891 | * First, make sure the instance exists. Do this by writing to | ||
892 | * the data latch and reading the value back. If the port *is* | ||
893 | * present, test to see if it supports extended-mode | ||
894 | * operation. This will be required for IEEE1284 reverse | ||
895 | * transfers. | ||
896 | */ | ||
897 | |||
898 | outb_p(BPP_PROBE_CODE, lpAddr); | ||
899 | for (testvalue=0; testvalue<BPP_DELAY; testvalue++) | ||
900 | ; | ||
901 | testvalue = inb_p(lpAddr); | ||
902 | if (testvalue == BPP_PROBE_CODE) { | ||
903 | unsigned save; | ||
904 | instances[idx].present = 1; | ||
905 | |||
906 | save = inb_p(lpAddr+2); | ||
907 | for (testvalue=0; testvalue<BPP_DELAY; testvalue++) | ||
908 | ; | ||
909 | outb_p(save|0x20, lpAddr+2); | ||
910 | for (testvalue=0; testvalue<BPP_DELAY; testvalue++) | ||
911 | ; | ||
912 | outb_p(~BPP_PROBE_CODE, lpAddr); | ||
913 | for (testvalue=0; testvalue<BPP_DELAY; testvalue++) | ||
914 | ; | ||
915 | testvalue = inb_p(lpAddr); | ||
916 | if ((testvalue&0xff) == (0xff&~BPP_PROBE_CODE)) | ||
917 | instances[idx].enhanced = 0; | ||
918 | else | ||
919 | instances[idx].enhanced = 1; | ||
920 | outb_p(save, lpAddr+2); | ||
921 | } | ||
922 | else { | ||
923 | release_region(lpAddr,3); | ||
924 | } | ||
925 | /* | ||
926 | * Leave the port in compat idle mode. | ||
927 | */ | ||
928 | set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, idx); | ||
929 | |||
930 | printk("bpp%d: Port at 0x%03x: Enhanced mode %s\n", idx, base_addrs[idx], | ||
931 | instances[idx].enhanced? "SUPPORTED" : "UNAVAILABLE"); | ||
932 | } | ||
933 | |||
934 | static inline void freeLptPort(int idx) | ||
935 | { | ||
936 | release_region(base_addrs[idx], 3); | ||
937 | } | ||
938 | |||
939 | #endif | ||
940 | |||
941 | #if defined(__sparc__) | ||
942 | |||
943 | static void __iomem *map_bpp(struct sbus_dev *dev, int idx) | ||
944 | { | ||
945 | return sbus_ioremap(&dev->resource[0], 0, BPP_SIZE, "bpp"); | ||
946 | } | ||
947 | |||
948 | static int collectLptPorts(void) | ||
949 | { | ||
950 | struct sbus_bus *bus; | ||
951 | struct sbus_dev *dev; | ||
952 | int count; | ||
953 | |||
954 | count = 0; | ||
955 | for_all_sbusdev(dev, bus) { | ||
956 | if (strcmp(dev->prom_name, "SUNW,bpp") == 0) { | ||
957 | if (count >= BPP_NO) { | ||
958 | printk(KERN_NOTICE | ||
959 | "bpp: More than %d bpp ports," | ||
960 | " rest is ignored\n", BPP_NO); | ||
961 | return count; | ||
962 | } | ||
963 | base_addrs[count] = map_bpp(dev, count); | ||
964 | count++; | ||
965 | } | ||
966 | } | ||
967 | return count; | ||
968 | } | ||
969 | |||
970 | static void probeLptPort(unsigned idx) | ||
971 | { | ||
972 | void __iomem *rp = base_addrs[idx]; | ||
973 | __u32 csr; | ||
974 | char *brand; | ||
975 | |||
976 | instances[idx].present = 0; | ||
977 | instances[idx].enhanced = 0; | ||
978 | instances[idx].direction = 0; | ||
979 | instances[idx].mode = COMPATIBILITY; | ||
980 | init_waitqueue_head(&instances[idx].wait_queue); | ||
981 | instances[idx].run_length = 0; | ||
982 | instances[idx].run_flag = 0; | ||
983 | init_timer(&instances[idx].timer_list); | ||
984 | instances[idx].timer_list.function = bpp_wake_up; | ||
985 | |||
986 | if (!rp) return; | ||
987 | |||
988 | instances[idx].present = 1; | ||
989 | instances[idx].enhanced = 1; /* Sure */ | ||
990 | |||
991 | csr = sbus_readl(rp + BPP_CSR); | ||
992 | if ((csr & P_DRAINING) != 0 && (csr & P_ERR_PEND) == 0) { | ||
993 | udelay(20); | ||
994 | csr = sbus_readl(rp + BPP_CSR); | ||
995 | if ((csr & P_DRAINING) != 0 && (csr & P_ERR_PEND) == 0) { | ||
996 | printk("bpp%d: DRAINING still active (0x%08x)\n", idx, csr); | ||
997 | } | ||
998 | } | ||
999 | printk("bpp%d: reset with 0x%08x ..", idx, csr); | ||
1000 | sbus_writel((csr | P_RESET) & ~P_INT_EN, rp + BPP_CSR); | ||
1001 | udelay(500); | ||
1002 | sbus_writel(sbus_readl(rp + BPP_CSR) & ~P_RESET, rp + BPP_CSR); | ||
1003 | csr = sbus_readl(rp + BPP_CSR); | ||
1004 | printk(" done with csr=0x%08x ocr=0x%04x\n", | ||
1005 | csr, sbus_readw(rp + BPP_OCR)); | ||
1006 | |||
1007 | switch (csr & P_DEV_ID_MASK) { | ||
1008 | case P_DEV_ID_ZEBRA: | ||
1009 | brand = "Zebra"; | ||
1010 | break; | ||
1011 | case P_DEV_ID_L64854: | ||
1012 | brand = "DMA2"; | ||
1013 | break; | ||
1014 | default: | ||
1015 | brand = "Unknown"; | ||
1016 | } | ||
1017 | printk("bpp%d: %s at %p\n", idx, brand, rp); | ||
1018 | |||
1019 | /* | ||
1020 | * Leave the port in compat idle mode. | ||
1021 | */ | ||
1022 | set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, idx); | ||
1023 | |||
1024 | return; | ||
1025 | } | ||
1026 | |||
1027 | static inline void freeLptPort(int idx) | ||
1028 | { | ||
1029 | sbus_iounmap(base_addrs[idx], BPP_SIZE); | ||
1030 | } | ||
1031 | |||
1032 | #endif | ||
1033 | |||
1034 | static int __init bpp_init(void) | ||
1035 | { | ||
1036 | int rc; | ||
1037 | unsigned idx; | ||
1038 | |||
1039 | rc = collectLptPorts(); | ||
1040 | if (rc == 0) | ||
1041 | return -ENODEV; | ||
1042 | |||
1043 | rc = register_chrdev(BPP_MAJOR, dev_name, &bpp_fops); | ||
1044 | if (rc < 0) | ||
1045 | return rc; | ||
1046 | |||
1047 | for (idx = 0; idx < BPP_NO; idx++) { | ||
1048 | instances[idx].opened = 0; | ||
1049 | probeLptPort(idx); | ||
1050 | } | ||
1051 | devfs_mk_dir("bpp"); | ||
1052 | for (idx = 0; idx < BPP_NO; idx++) { | ||
1053 | devfs_mk_cdev(MKDEV(BPP_MAJOR, idx), | ||
1054 | S_IFCHR | S_IRUSR | S_IWUSR, "bpp/%d", idx); | ||
1055 | } | ||
1056 | |||
1057 | return 0; | ||
1058 | } | ||
1059 | |||
1060 | static void __exit bpp_cleanup(void) | ||
1061 | { | ||
1062 | unsigned idx; | ||
1063 | |||
1064 | for (idx = 0; idx < BPP_NO; idx++) | ||
1065 | devfs_remove("bpp/%d", idx); | ||
1066 | devfs_remove("bpp"); | ||
1067 | unregister_chrdev(BPP_MAJOR, dev_name); | ||
1068 | |||
1069 | for (idx = 0; idx < BPP_NO; idx++) { | ||
1070 | if (instances[idx].present) | ||
1071 | freeLptPort(idx); | ||
1072 | } | ||
1073 | } | ||
1074 | |||
1075 | module_init(bpp_init); | ||
1076 | module_exit(bpp_cleanup); | ||
1077 | |||
1078 | MODULE_LICENSE("GPL"); | ||
1079 | |||
diff --git a/drivers/sbus/char/cd180.h b/drivers/sbus/char/cd180.h new file mode 100644 index 00000000000..445b86cc65e --- /dev/null +++ b/drivers/sbus/char/cd180.h | |||
@@ -0,0 +1,240 @@ | |||
1 | |||
2 | /* Definitions for Cirrus Logic CL-CD180 8-port async mux chip */ | ||
3 | #define CD180_NCH 8 /* Total number of channels */ | ||
4 | #define CD180_TPC 16 /* Ticks per character */ | ||
5 | #define CD180_NFIFO 8 /* TX FIFO size */ | ||
6 | |||
7 | /* Global registers */ | ||
8 | #define CD180_GFRCR 0x6b /* Global Firmware Revision Code Register */ | ||
9 | #define CD180_SRCR 0x66 /* Service Request Configuration Register */ | ||
10 | #define CD180_PPRH 0x70 /* Prescaler Period Register High */ | ||
11 | #define CD180_PPRL 0x71 /* Prescaler Period Register Low */ | ||
12 | #define CD180_MSMR 0x61 /* Modem Service Match Register */ | ||
13 | #define CD180_TSMR 0x62 /* Transmit Service Match Register */ | ||
14 | #define CD180_RSMR 0x63 /* Receive Service Match Register */ | ||
15 | #define CD180_GSVR 0x40 /* Global Service Vector Register */ | ||
16 | #define CD180_SRSR 0x65 /* Service Request Status Register */ | ||
17 | #define CD180_GSCR 0x41 /* Global Service Channel Register */ | ||
18 | #define CD180_CAR 0x64 /* Channel Access Register */ | ||
19 | |||
20 | /* Indexed registers */ | ||
21 | #define CD180_RDCR 0x07 /* Receive Data Count Register */ | ||
22 | #define CD180_RDR 0x78 /* Receiver Data Register */ | ||
23 | #define CD180_RCSR 0x7a /* Receiver Character Status Register */ | ||
24 | #define CD180_TDR 0x7b /* Transmit Data Register */ | ||
25 | #define CD180_EOSRR 0x7f /* End of Service Request Register */ | ||
26 | |||
27 | /* Channel Registers */ | ||
28 | #define CD180_SRER 0x02 /* Service Request Enable Register */ | ||
29 | #define CD180_CCR 0x01 /* Channel Command Register */ | ||
30 | #define CD180_COR1 0x03 /* Channel Option Register 1 */ | ||
31 | #define CD180_COR2 0x04 /* Channel Option Register 2 */ | ||
32 | #define CD180_COR3 0x05 /* Channel Option Register 3 */ | ||
33 | #define CD180_CCSR 0x06 /* Channel Control Status Register */ | ||
34 | #define CD180_RTPR 0x18 /* Receive Timeout Period Register */ | ||
35 | #define CD180_RBPRH 0x31 /* Receive Bit Rate Period Register High */ | ||
36 | #define CD180_RBPRL 0x32 /* Receive Bit Rate Period Register Low */ | ||
37 | #define CD180_TBPRH 0x39 /* Transmit Bit Rate Period Register High */ | ||
38 | #define CD180_TBPRL 0x3a /* Transmit Bit Rate Period Register Low */ | ||
39 | #define CD180_SCHR1 0x09 /* Special Character Register 1 */ | ||
40 | #define CD180_SCHR2 0x0a /* Special Character Register 2 */ | ||
41 | #define CD180_SCHR3 0x0b /* Special Character Register 3 */ | ||
42 | #define CD180_SCHR4 0x0c /* Special Character Register 4 */ | ||
43 | #define CD180_MCR 0x12 /* Modem Change Register */ | ||
44 | #define CD180_MCOR1 0x10 /* Modem Change Option 1 Register */ | ||
45 | #define CD180_MCOR2 0x11 /* Modem Change Option 2 Register */ | ||
46 | #define CD180_MSVR 0x28 /* Modem Signal Value Register */ | ||
47 | #define CD180_MSVRTS 0x29 /* Modem Signal Value RTS */ | ||
48 | #define CD180_MSVDTR 0x2a /* Modem Signal Value DTR */ | ||
49 | |||
50 | /* Global Interrupt Vector Register (R/W) */ | ||
51 | |||
52 | #define GSVR_ITMASK 0x07 /* Interrupt type mask */ | ||
53 | #define GSVR_IT_MDM 0x01 /* Modem Signal Change Interrupt */ | ||
54 | #define GSVR_IT_TX 0x02 /* Transmit Data Interrupt */ | ||
55 | #define GSVR_IT_RGD 0x03 /* Receive Good Data Interrupt */ | ||
56 | #define GSVR_IT_REXC 0x07 /* Receive Exception Interrupt */ | ||
57 | |||
58 | |||
59 | /* Global Interrupt Channel Register (R/W) */ | ||
60 | |||
61 | #define GSCR_CHAN 0x1c /* Channel Number Mask */ | ||
62 | #define GSCR_CHAN_OFF 2 /* Channel Number Offset */ | ||
63 | |||
64 | |||
65 | /* Channel Address Register (R/W) */ | ||
66 | |||
67 | #define CAR_CHAN 0x07 /* Channel Number Mask */ | ||
68 | |||
69 | |||
70 | /* Receive Character Status Register (R/O) */ | ||
71 | |||
72 | #define RCSR_TOUT 0x80 /* Rx Timeout */ | ||
73 | #define RCSR_SCDET 0x70 /* Special Character Detected Mask */ | ||
74 | #define RCSR_NO_SC 0x00 /* No Special Characters Detected */ | ||
75 | #define RCSR_SC_1 0x10 /* Special Char 1 (or 1 & 3) Detected */ | ||
76 | #define RCSR_SC_2 0x20 /* Special Char 2 (or 2 & 4) Detected */ | ||
77 | #define RCSR_SC_3 0x30 /* Special Char 3 Detected */ | ||
78 | #define RCSR_SC_4 0x40 /* Special Char 4 Detected */ | ||
79 | #define RCSR_BREAK 0x08 /* Break has been detected */ | ||
80 | #define RCSR_PE 0x04 /* Parity Error */ | ||
81 | #define RCSR_FE 0x02 /* Frame Error */ | ||
82 | #define RCSR_OE 0x01 /* Overrun Error */ | ||
83 | |||
84 | |||
85 | /* Channel Command Register (R/W) (commands in groups can be OR-ed) */ | ||
86 | |||
87 | #define CCR_HARDRESET 0x81 /* Reset the chip */ | ||
88 | |||
89 | #define CCR_SOFTRESET 0x80 /* Soft Channel Reset */ | ||
90 | |||
91 | #define CCR_CORCHG1 0x42 /* Channel Option Register 1 Changed */ | ||
92 | #define CCR_CORCHG2 0x44 /* Channel Option Register 2 Changed */ | ||
93 | #define CCR_CORCHG3 0x48 /* Channel Option Register 3 Changed */ | ||
94 | |||
95 | #define CCR_SSCH1 0x21 /* Send Special Character 1 */ | ||
96 | |||
97 | #define CCR_SSCH2 0x22 /* Send Special Character 2 */ | ||
98 | |||
99 | #define CCR_SSCH3 0x23 /* Send Special Character 3 */ | ||
100 | |||
101 | #define CCR_SSCH4 0x24 /* Send Special Character 4 */ | ||
102 | |||
103 | #define CCR_TXEN 0x18 /* Enable Transmitter */ | ||
104 | #define CCR_RXEN 0x12 /* Enable Receiver */ | ||
105 | |||
106 | #define CCR_TXDIS 0x14 /* Disable Transmitter */ | ||
107 | #define CCR_RXDIS 0x11 /* Disable Receiver */ | ||
108 | |||
109 | |||
110 | /* Service Request Enable Register (R/W) */ | ||
111 | |||
112 | #define SRER_DSR 0x80 /* Enable interrupt on DSR change */ | ||
113 | #define SRER_CD 0x40 /* Enable interrupt on CD change */ | ||
114 | #define SRER_CTS 0x20 /* Enable interrupt on CTS change */ | ||
115 | #define SRER_RXD 0x10 /* Enable interrupt on Receive Data */ | ||
116 | #define SRER_RXSC 0x08 /* Enable interrupt on Receive Spec. Char */ | ||
117 | #define SRER_TXRDY 0x04 /* Enable interrupt on TX FIFO empty */ | ||
118 | #define SRER_TXEMPTY 0x02 /* Enable interrupt on TX completely empty */ | ||
119 | #define SRER_RET 0x01 /* Enable interrupt on RX Exc. Timeout */ | ||
120 | |||
121 | |||
122 | /* Channel Option Register 1 (R/W) */ | ||
123 | |||
124 | #define COR1_ODDP 0x80 /* Odd Parity */ | ||
125 | #define COR1_PARMODE 0x60 /* Parity Mode mask */ | ||
126 | #define COR1_NOPAR 0x00 /* No Parity */ | ||
127 | #define COR1_FORCEPAR 0x20 /* Force Parity */ | ||
128 | #define COR1_NORMPAR 0x40 /* Normal Parity */ | ||
129 | #define COR1_IGNORE 0x10 /* Ignore Parity on RX */ | ||
130 | #define COR1_STOPBITS 0x0c /* Number of Stop Bits */ | ||
131 | #define COR1_1SB 0x00 /* 1 Stop Bit */ | ||
132 | #define COR1_15SB 0x04 /* 1.5 Stop Bits */ | ||
133 | #define COR1_2SB 0x08 /* 2 Stop Bits */ | ||
134 | #define COR1_CHARLEN 0x03 /* Character Length */ | ||
135 | #define COR1_5BITS 0x00 /* 5 bits */ | ||
136 | #define COR1_6BITS 0x01 /* 6 bits */ | ||
137 | #define COR1_7BITS 0x02 /* 7 bits */ | ||
138 | #define COR1_8BITS 0x03 /* 8 bits */ | ||
139 | |||
140 | |||
141 | /* Channel Option Register 2 (R/W) */ | ||
142 | |||
143 | #define COR2_IXM 0x80 /* Implied XON mode */ | ||
144 | #define COR2_TXIBE 0x40 /* Enable In-Band (XON/XOFF) Flow Control */ | ||
145 | #define COR2_ETC 0x20 /* Embedded Tx Commands Enable */ | ||
146 | #define COR2_LLM 0x10 /* Local Loopback Mode */ | ||
147 | #define COR2_RLM 0x08 /* Remote Loopback Mode */ | ||
148 | #define COR2_RTSAO 0x04 /* RTS Automatic Output Enable */ | ||
149 | #define COR2_CTSAE 0x02 /* CTS Automatic Enable */ | ||
150 | #define COR2_DSRAE 0x01 /* DSR Automatic Enable */ | ||
151 | |||
152 | |||
153 | /* Channel Option Register 3 (R/W) */ | ||
154 | |||
155 | #define COR3_XONCH 0x80 /* XON is a pair of characters (1 & 3) */ | ||
156 | #define COR3_XOFFCH 0x40 /* XOFF is a pair of characters (2 & 4) */ | ||
157 | #define COR3_FCT 0x20 /* Flow-Control Transparency Mode */ | ||
158 | #define COR3_SCDE 0x10 /* Special Character Detection Enable */ | ||
159 | #define COR3_RXTH 0x0f /* RX FIFO Threshold value (1-8) */ | ||
160 | |||
161 | |||
162 | /* Channel Control Status Register (R/O) */ | ||
163 | |||
164 | #define CCSR_RXEN 0x80 /* Receiver Enabled */ | ||
165 | #define CCSR_RXFLOFF 0x40 /* Receive Flow Off (XOFF was sent) */ | ||
166 | #define CCSR_RXFLON 0x20 /* Receive Flow On (XON was sent) */ | ||
167 | #define CCSR_TXEN 0x08 /* Transmitter Enabled */ | ||
168 | #define CCSR_TXFLOFF 0x04 /* Transmit Flow Off (got XOFF) */ | ||
169 | #define CCSR_TXFLON 0x02 /* Transmit Flow On (got XON) */ | ||
170 | |||
171 | |||
172 | /* Modem Change Option Register 1 (R/W) */ | ||
173 | |||
174 | #define MCOR1_DSRZD 0x80 /* Detect 0->1 transition of DSR */ | ||
175 | #define MCOR1_CDZD 0x40 /* Detect 0->1 transition of CD */ | ||
176 | #define MCOR1_CTSZD 0x20 /* Detect 0->1 transition of CTS */ | ||
177 | #define MCOR1_DTRTH 0x0f /* Auto DTR flow control Threshold (1-8) */ | ||
178 | #define MCOR1_NODTRFC 0x0 /* Automatic DTR flow control disabled */ | ||
179 | |||
180 | |||
181 | /* Modem Change Option Register 2 (R/W) */ | ||
182 | |||
183 | #define MCOR2_DSROD 0x80 /* Detect 1->0 transition of DSR */ | ||
184 | #define MCOR2_CDOD 0x40 /* Detect 1->0 transition of CD */ | ||
185 | #define MCOR2_CTSOD 0x20 /* Detect 1->0 transition of CTS */ | ||
186 | |||
187 | |||
188 | /* Modem Change Register (R/W) */ | ||
189 | |||
190 | #define MCR_DSRCHG 0x80 /* DSR Changed */ | ||
191 | #define MCR_CDCHG 0x40 /* CD Changed */ | ||
192 | #define MCR_CTSCHG 0x20 /* CTS Changed */ | ||
193 | |||
194 | |||
195 | /* Modem Signal Value Register (R/W) */ | ||
196 | |||
197 | #define MSVR_DSR 0x80 /* Current state of DSR input */ | ||
198 | #define MSVR_CD 0x40 /* Current state of CD input */ | ||
199 | #define MSVR_CTS 0x20 /* Current state of CTS input */ | ||
200 | #define MSVR_DTR 0x02 /* Current state of DTR output */ | ||
201 | #define MSVR_RTS 0x01 /* Current state of RTS output */ | ||
202 | |||
203 | |||
204 | /* Service Request Status Register */ | ||
205 | |||
206 | #define SRSR_CMASK 0xC0 /* Current Service Context Mask */ | ||
207 | #define SRSR_CNONE 0x00 /* Not in a service context */ | ||
208 | #define SRSR_CRX 0x40 /* Rx Context */ | ||
209 | #define SRSR_CTX 0x80 /* Tx Context */ | ||
210 | #define SRSR_CMDM 0xC0 /* Modem Context */ | ||
211 | #define SRSR_ANYINT 0x6F /* Any interrupt flag */ | ||
212 | #define SRSR_RINT 0x10 /* Receive Interrupt */ | ||
213 | #define SRSR_TINT 0x04 /* Transmit Interrupt */ | ||
214 | #define SRSR_MINT 0x01 /* Modem Interrupt */ | ||
215 | #define SRSR_REXT 0x20 /* Receive External Interrupt */ | ||
216 | #define SRSR_TEXT 0x08 /* Transmit External Interrupt */ | ||
217 | #define SRSR_MEXT 0x02 /* Modem External Interrupt */ | ||
218 | |||
219 | |||
220 | /* Service Request Configuration Register */ | ||
221 | |||
222 | #define SRCR_PKGTYPE 0x80 | ||
223 | #define SRCR_REGACKEN 0x40 | ||
224 | #define SRCR_DAISYEN 0x20 | ||
225 | #define SRCR_GLOBPRI 0x10 | ||
226 | #define SRCR_UNFAIR 0x08 | ||
227 | #define SRCR_AUTOPRI 0x02 | ||
228 | #define SRCR_PRISEL 0x01 | ||
229 | |||
230 | /* Values for register-based Interrupt ACKs */ | ||
231 | #define CD180_ACK_MINT 0x75 /* goes to MSMR */ | ||
232 | #define CD180_ACK_TINT 0x76 /* goes to TSMR */ | ||
233 | #define CD180_ACK_RINT 0x77 /* goes to RSMR */ | ||
234 | |||
235 | /* Escape characters */ | ||
236 | |||
237 | #define CD180_C_ESC 0x00 /* Escape character */ | ||
238 | #define CD180_C_SBRK 0x81 /* Start sending BREAK */ | ||
239 | #define CD180_C_DELAY 0x82 /* Delay output */ | ||
240 | #define CD180_C_EBRK 0x83 /* Stop sending BREAK */ | ||
diff --git a/drivers/sbus/char/cpwatchdog.c b/drivers/sbus/char/cpwatchdog.c new file mode 100644 index 00000000000..c82abeb59d3 --- /dev/null +++ b/drivers/sbus/char/cpwatchdog.c | |||
@@ -0,0 +1,832 @@ | |||
1 | /* cpwatchdog.c - driver implementation for hardware watchdog | ||
2 | * timers found on Sun Microsystems CP1400 and CP1500 boards. | ||
3 | * | ||
4 | * This device supports both the generic Linux watchdog | ||
5 | * interface and Solaris-compatible ioctls as best it is | ||
6 | * able. | ||
7 | * | ||
8 | * NOTE: CP1400 systems appear to have a defective intr_mask | ||
9 | * register on the PLD, preventing the disabling of | ||
10 | * timer interrupts. We use a timer to periodically | ||
11 | * reset 'stopped' watchdogs on affected platforms. | ||
12 | * | ||
13 | * TODO: DevFS support (/dev/watchdogs/0 ... /dev/watchdogs/2) | ||
14 | * | ||
15 | * Copyright (c) 2000 Eric Brower (ebrower@usa.net) | ||
16 | */ | ||
17 | |||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/fs.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/major.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/miscdevice.h> | ||
25 | #include <linux/sched.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/ioport.h> | ||
28 | #include <linux/timer.h> | ||
29 | #include <asm/irq.h> | ||
30 | #include <asm/ebus.h> | ||
31 | #include <asm/oplib.h> | ||
32 | #include <asm/uaccess.h> | ||
33 | |||
34 | #include <asm/watchdog.h> | ||
35 | |||
36 | #define WD_OBPNAME "watchdog" | ||
37 | #define WD_BADMODEL "SUNW,501-5336" | ||
38 | #define WD_BTIMEOUT (jiffies + (HZ * 1000)) | ||
39 | #define WD_BLIMIT 0xFFFF | ||
40 | |||
41 | #define WD0_DEVNAME "watchdog0" | ||
42 | #define WD1_DEVNAME "watchdog1" | ||
43 | #define WD2_DEVNAME "watchdog2" | ||
44 | |||
45 | #define WD0_MINOR 212 | ||
46 | #define WD1_MINOR 213 | ||
47 | #define WD2_MINOR 214 | ||
48 | |||
49 | |||
50 | /* Internal driver definitions | ||
51 | */ | ||
52 | #define WD0_ID 0 /* Watchdog0 */ | ||
53 | #define WD1_ID 1 /* Watchdog1 */ | ||
54 | #define WD2_ID 2 /* Watchdog2 */ | ||
55 | #define WD_NUMDEVS 3 /* Device contains 3 timers */ | ||
56 | |||
57 | #define WD_INTR_OFF 0 /* Interrupt disable value */ | ||
58 | #define WD_INTR_ON 1 /* Interrupt enable value */ | ||
59 | |||
60 | #define WD_STAT_INIT 0x01 /* Watchdog timer is initialized */ | ||
61 | #define WD_STAT_BSTOP 0x02 /* Watchdog timer is brokenstopped */ | ||
62 | #define WD_STAT_SVCD 0x04 /* Watchdog interrupt occurred */ | ||
63 | |||
64 | /* Register value definitions | ||
65 | */ | ||
66 | #define WD0_INTR_MASK 0x01 /* Watchdog device interrupt masks */ | ||
67 | #define WD1_INTR_MASK 0x02 | ||
68 | #define WD2_INTR_MASK 0x04 | ||
69 | |||
70 | #define WD_S_RUNNING 0x01 /* Watchdog device status running */ | ||
71 | #define WD_S_EXPIRED 0x02 /* Watchdog device status expired */ | ||
72 | |||
73 | /* Sun uses Altera PLD EPF8820ATC144-4 | ||
74 | * providing three hardware watchdogs: | ||
75 | * | ||
76 | * 1) RIC - sends an interrupt when triggered | ||
77 | * 2) XIR - asserts XIR_B_RESET when triggered, resets CPU | ||
78 | * 3) POR - asserts POR_B_RESET when triggered, resets CPU, backplane, board | ||
79 | * | ||
80 | *** Timer register block definition (struct wd_timer_regblk) | ||
81 | * | ||
82 | * dcntr and limit registers (halfword access): | ||
83 | * ------------------- | ||
84 | * | 15 | ...| 1 | 0 | | ||
85 | * ------------------- | ||
86 | * |- counter val -| | ||
87 | * ------------------- | ||
88 | * dcntr - Current 16-bit downcounter value. | ||
89 | * When downcounter reaches '0' watchdog expires. | ||
90 | * Reading this register resets downcounter with 'limit' value. | ||
91 | * limit - 16-bit countdown value in 1/10th second increments. | ||
92 | * Writing this register begins countdown with input value. | ||
93 | * Reading from this register does not affect counter. | ||
94 | * NOTES: After watchdog reset, dcntr and limit contain '1' | ||
95 | * | ||
96 | * status register (byte access): | ||
97 | * --------------------------- | ||
98 | * | 7 | ... | 2 | 1 | 0 | | ||
99 | * --------------+------------ | ||
100 | * |- UNUSED -| EXP | RUN | | ||
101 | * --------------------------- | ||
102 | * status- Bit 0 - Watchdog is running | ||
103 | * Bit 1 - Watchdog has expired | ||
104 | * | ||
105 | *** PLD register block definition (struct wd_pld_regblk) | ||
106 | * | ||
107 | * intr_mask register (byte access): | ||
108 | * --------------------------------- | ||
109 | * | 7 | ... | 3 | 2 | 1 | 0 | | ||
110 | * +-------------+------------------ | ||
111 | * |- UNUSED -| WD3 | WD2 | WD1 | | ||
112 | * --------------------------------- | ||
113 | * WD3 - 1 == Interrupt disabled for watchdog 3 | ||
114 | * WD2 - 1 == Interrupt disabled for watchdog 2 | ||
115 | * WD1 - 1 == Interrupt disabled for watchdog 1 | ||
116 | * | ||
117 | * pld_status register (byte access): | ||
118 | * UNKNOWN, MAGICAL MYSTERY REGISTER | ||
119 | * | ||
120 | */ | ||
121 | #define WD_TIMER_REGSZ 16 | ||
122 | #define WD0_OFF 0 | ||
123 | #define WD1_OFF (WD_TIMER_REGSZ * 1) | ||
124 | #define WD2_OFF (WD_TIMER_REGSZ * 2) | ||
125 | #define PLD_OFF (WD_TIMER_REGSZ * 3) | ||
126 | |||
127 | #define WD_DCNTR 0x00 | ||
128 | #define WD_LIMIT 0x04 | ||
129 | #define WD_STATUS 0x08 | ||
130 | |||
131 | #define PLD_IMASK (PLD_OFF + 0x00) | ||
132 | #define PLD_STATUS (PLD_OFF + 0x04) | ||
133 | |||
134 | /* Individual timer structure | ||
135 | */ | ||
136 | struct wd_timer { | ||
137 | __u16 timeout; | ||
138 | __u8 intr_mask; | ||
139 | unsigned char runstatus; | ||
140 | void __iomem *regs; | ||
141 | }; | ||
142 | |||
143 | /* Device structure | ||
144 | */ | ||
145 | struct wd_device { | ||
146 | int irq; | ||
147 | spinlock_t lock; | ||
148 | unsigned char isbaddoggie; /* defective PLD */ | ||
149 | unsigned char opt_enable; | ||
150 | unsigned char opt_reboot; | ||
151 | unsigned short opt_timeout; | ||
152 | unsigned char initialized; | ||
153 | struct wd_timer watchdog[WD_NUMDEVS]; | ||
154 | void __iomem *regs; | ||
155 | }; | ||
156 | |||
157 | static struct wd_device wd_dev = { | ||
158 | 0, SPIN_LOCK_UNLOCKED, 0, 0, 0, 0, | ||
159 | }; | ||
160 | |||
161 | static struct timer_list wd_timer; | ||
162 | |||
163 | static int wd0_timeout = 0; | ||
164 | static int wd1_timeout = 0; | ||
165 | static int wd2_timeout = 0; | ||
166 | |||
167 | #ifdef MODULE | ||
168 | module_param (wd0_timeout, int, 0); | ||
169 | MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs"); | ||
170 | module_param (wd1_timeout, int, 0); | ||
171 | MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs"); | ||
172 | module_param (wd2_timeout, int, 0); | ||
173 | MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs"); | ||
174 | |||
175 | MODULE_AUTHOR | ||
176 | ("Eric Brower <ebrower@usa.net>"); | ||
177 | MODULE_DESCRIPTION | ||
178 | ("Hardware watchdog driver for Sun Microsystems CP1400/1500"); | ||
179 | MODULE_LICENSE("GPL"); | ||
180 | MODULE_SUPPORTED_DEVICE | ||
181 | ("watchdog"); | ||
182 | #endif /* ifdef MODULE */ | ||
183 | |||
184 | /* Forward declarations of internal methods | ||
185 | */ | ||
186 | #ifdef WD_DEBUG | ||
187 | static void wd_dumpregs(void); | ||
188 | #endif | ||
189 | static irqreturn_t wd_interrupt(int irq, void *dev_id, struct pt_regs *regs); | ||
190 | static void wd_toggleintr(struct wd_timer* pTimer, int enable); | ||
191 | static void wd_pingtimer(struct wd_timer* pTimer); | ||
192 | static void wd_starttimer(struct wd_timer* pTimer); | ||
193 | static void wd_resetbrokentimer(struct wd_timer* pTimer); | ||
194 | static void wd_stoptimer(struct wd_timer* pTimer); | ||
195 | static void wd_brokentimer(unsigned long data); | ||
196 | static int wd_getstatus(struct wd_timer* pTimer); | ||
197 | |||
198 | /* PLD expects words to be written in LSB format, | ||
199 | * so we must flip all words prior to writing them to regs | ||
200 | */ | ||
201 | static inline unsigned short flip_word(unsigned short word) | ||
202 | { | ||
203 | return ((word & 0xff) << 8) | ((word >> 8) & 0xff); | ||
204 | } | ||
205 | |||
206 | #define wd_writew(val, addr) (writew(flip_word(val), addr)) | ||
207 | #define wd_readw(addr) (flip_word(readw(addr))) | ||
208 | #define wd_writeb(val, addr) (writeb(val, addr)) | ||
209 | #define wd_readb(addr) (readb(addr)) | ||
210 | |||
211 | |||
212 | /* CP1400s seem to have broken PLD implementations-- | ||
213 | * the interrupt_mask register cannot be written, so | ||
214 | * no timer interrupts can be masked within the PLD. | ||
215 | */ | ||
216 | static inline int wd_isbroken(void) | ||
217 | { | ||
218 | /* we could test this by read/write/read/restore | ||
219 | * on the interrupt mask register only if OBP | ||
220 | * 'watchdog-enable?' == FALSE, but it seems | ||
221 | * ubiquitous on CP1400s | ||
222 | */ | ||
223 | char val[32]; | ||
224 | prom_getproperty(prom_root_node, "model", val, sizeof(val)); | ||
225 | return((!strcmp(val, WD_BADMODEL)) ? 1 : 0); | ||
226 | } | ||
227 | |||
228 | /* Retrieve watchdog-enable? option from OBP | ||
229 | * Returns 0 if false, 1 if true | ||
230 | */ | ||
231 | static inline int wd_opt_enable(void) | ||
232 | { | ||
233 | int opt_node; | ||
234 | |||
235 | opt_node = prom_getchild(prom_root_node); | ||
236 | opt_node = prom_searchsiblings(opt_node, "options"); | ||
237 | return((-1 == prom_getint(opt_node, "watchdog-enable?")) ? 0 : 1); | ||
238 | } | ||
239 | |||
240 | /* Retrieve watchdog-reboot? option from OBP | ||
241 | * Returns 0 if false, 1 if true | ||
242 | */ | ||
243 | static inline int wd_opt_reboot(void) | ||
244 | { | ||
245 | int opt_node; | ||
246 | |||
247 | opt_node = prom_getchild(prom_root_node); | ||
248 | opt_node = prom_searchsiblings(opt_node, "options"); | ||
249 | return((-1 == prom_getint(opt_node, "watchdog-reboot?")) ? 0 : 1); | ||
250 | } | ||
251 | |||
252 | /* Retrieve watchdog-timeout option from OBP | ||
253 | * Returns OBP value, or 0 if not located | ||
254 | */ | ||
255 | static inline int wd_opt_timeout(void) | ||
256 | { | ||
257 | int opt_node; | ||
258 | char value[32]; | ||
259 | char *p = value; | ||
260 | |||
261 | opt_node = prom_getchild(prom_root_node); | ||
262 | opt_node = prom_searchsiblings(opt_node, "options"); | ||
263 | opt_node = prom_getproperty(opt_node, | ||
264 | "watchdog-timeout", | ||
265 | value, | ||
266 | sizeof(value)); | ||
267 | if(-1 != opt_node) { | ||
268 | /* atoi implementation */ | ||
269 | for(opt_node = 0; /* nop */; p++) { | ||
270 | if(*p >= '0' && *p <= '9') { | ||
271 | opt_node = (10*opt_node)+(*p-'0'); | ||
272 | } | ||
273 | else { | ||
274 | break; | ||
275 | } | ||
276 | } | ||
277 | } | ||
278 | return((-1 == opt_node) ? (0) : (opt_node)); | ||
279 | } | ||
280 | |||
281 | static int wd_open(struct inode *inode, struct file *f) | ||
282 | { | ||
283 | switch(iminor(inode)) | ||
284 | { | ||
285 | case WD0_MINOR: | ||
286 | f->private_data = &wd_dev.watchdog[WD0_ID]; | ||
287 | break; | ||
288 | case WD1_MINOR: | ||
289 | f->private_data = &wd_dev.watchdog[WD1_ID]; | ||
290 | break; | ||
291 | case WD2_MINOR: | ||
292 | f->private_data = &wd_dev.watchdog[WD2_ID]; | ||
293 | break; | ||
294 | default: | ||
295 | return(-ENODEV); | ||
296 | } | ||
297 | |||
298 | /* Register IRQ on first open of device */ | ||
299 | if(0 == wd_dev.initialized) | ||
300 | { | ||
301 | if (request_irq(wd_dev.irq, | ||
302 | &wd_interrupt, | ||
303 | SA_SHIRQ, | ||
304 | WD_OBPNAME, | ||
305 | (void *)wd_dev.regs)) { | ||
306 | printk("%s: Cannot register IRQ %s\n", | ||
307 | WD_OBPNAME, __irq_itoa(wd_dev.irq)); | ||
308 | return(-EBUSY); | ||
309 | } | ||
310 | wd_dev.initialized = 1; | ||
311 | } | ||
312 | |||
313 | return(nonseekable_open(inode, f)); | ||
314 | } | ||
315 | |||
316 | static int wd_release(struct inode *inode, struct file *file) | ||
317 | { | ||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | static int wd_ioctl(struct inode *inode, struct file *file, | ||
322 | unsigned int cmd, unsigned long arg) | ||
323 | { | ||
324 | int setopt = 0; | ||
325 | struct wd_timer* pTimer = (struct wd_timer*)file->private_data; | ||
326 | void __user *argp = (void __user *)arg; | ||
327 | struct watchdog_info info = { | ||
328 | 0, | ||
329 | 0, | ||
330 | "Altera EPF8820ATC144-4" | ||
331 | }; | ||
332 | |||
333 | if(NULL == pTimer) { | ||
334 | return(-EINVAL); | ||
335 | } | ||
336 | |||
337 | switch(cmd) | ||
338 | { | ||
339 | /* Generic Linux IOCTLs */ | ||
340 | case WDIOC_GETSUPPORT: | ||
341 | if(copy_to_user(argp, &info, sizeof(struct watchdog_info))) { | ||
342 | return(-EFAULT); | ||
343 | } | ||
344 | break; | ||
345 | case WDIOC_GETSTATUS: | ||
346 | case WDIOC_GETBOOTSTATUS: | ||
347 | if (put_user(0, (int __user *)argp)) | ||
348 | return -EFAULT; | ||
349 | break; | ||
350 | case WDIOC_KEEPALIVE: | ||
351 | wd_pingtimer(pTimer); | ||
352 | break; | ||
353 | case WDIOC_SETOPTIONS: | ||
354 | if(copy_from_user(&setopt, argp, sizeof(unsigned int))) { | ||
355 | return -EFAULT; | ||
356 | } | ||
357 | if(setopt & WDIOS_DISABLECARD) { | ||
358 | if(wd_dev.opt_enable) { | ||
359 | printk( | ||
360 | "%s: cannot disable watchdog in ENABLED mode\n", | ||
361 | WD_OBPNAME); | ||
362 | return(-EINVAL); | ||
363 | } | ||
364 | wd_stoptimer(pTimer); | ||
365 | } | ||
366 | else if(setopt & WDIOS_ENABLECARD) { | ||
367 | wd_starttimer(pTimer); | ||
368 | } | ||
369 | else { | ||
370 | return(-EINVAL); | ||
371 | } | ||
372 | break; | ||
373 | /* Solaris-compatible IOCTLs */ | ||
374 | case WIOCGSTAT: | ||
375 | setopt = wd_getstatus(pTimer); | ||
376 | if(copy_to_user(argp, &setopt, sizeof(unsigned int))) { | ||
377 | return(-EFAULT); | ||
378 | } | ||
379 | break; | ||
380 | case WIOCSTART: | ||
381 | wd_starttimer(pTimer); | ||
382 | break; | ||
383 | case WIOCSTOP: | ||
384 | if(wd_dev.opt_enable) { | ||
385 | printk("%s: cannot disable watchdog in ENABLED mode\n", | ||
386 | WD_OBPNAME); | ||
387 | return(-EINVAL); | ||
388 | } | ||
389 | wd_stoptimer(pTimer); | ||
390 | break; | ||
391 | default: | ||
392 | return(-EINVAL); | ||
393 | } | ||
394 | return(0); | ||
395 | } | ||
396 | |||
397 | static ssize_t wd_write(struct file *file, | ||
398 | const char __user *buf, | ||
399 | size_t count, | ||
400 | loff_t *ppos) | ||
401 | { | ||
402 | struct wd_timer* pTimer = (struct wd_timer*)file->private_data; | ||
403 | |||
404 | if(NULL == pTimer) { | ||
405 | return(-EINVAL); | ||
406 | } | ||
407 | |||
408 | if (count) { | ||
409 | wd_pingtimer(pTimer); | ||
410 | return 1; | ||
411 | } | ||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | static ssize_t wd_read(struct file * file, char __user *buffer, | ||
416 | size_t count, loff_t *ppos) | ||
417 | { | ||
418 | #ifdef WD_DEBUG | ||
419 | wd_dumpregs(); | ||
420 | return(0); | ||
421 | #else | ||
422 | return(-EINVAL); | ||
423 | #endif /* ifdef WD_DEBUG */ | ||
424 | } | ||
425 | |||
426 | static irqreturn_t wd_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
427 | { | ||
428 | /* Only WD0 will interrupt-- others are NMI and we won't | ||
429 | * see them here.... | ||
430 | */ | ||
431 | spin_lock_irq(&wd_dev.lock); | ||
432 | if((unsigned long)wd_dev.regs == (unsigned long)dev_id) | ||
433 | { | ||
434 | wd_stoptimer(&wd_dev.watchdog[WD0_ID]); | ||
435 | wd_dev.watchdog[WD0_ID].runstatus |= WD_STAT_SVCD; | ||
436 | } | ||
437 | spin_unlock_irq(&wd_dev.lock); | ||
438 | return IRQ_HANDLED; | ||
439 | } | ||
440 | |||
441 | static struct file_operations wd_fops = { | ||
442 | .owner = THIS_MODULE, | ||
443 | .ioctl = wd_ioctl, | ||
444 | .open = wd_open, | ||
445 | .write = wd_write, | ||
446 | .read = wd_read, | ||
447 | .release = wd_release, | ||
448 | }; | ||
449 | |||
450 | static struct miscdevice wd0_miscdev = { WD0_MINOR, WD0_DEVNAME, &wd_fops }; | ||
451 | static struct miscdevice wd1_miscdev = { WD1_MINOR, WD1_DEVNAME, &wd_fops }; | ||
452 | static struct miscdevice wd2_miscdev = { WD2_MINOR, WD2_DEVNAME, &wd_fops }; | ||
453 | |||
454 | #ifdef WD_DEBUG | ||
455 | static void wd_dumpregs(void) | ||
456 | { | ||
457 | /* Reading from downcounters initiates watchdog countdown-- | ||
458 | * Example is included below for illustration purposes. | ||
459 | */ | ||
460 | int i; | ||
461 | printk("%s: dumping register values\n", WD_OBPNAME); | ||
462 | for(i = WD0_ID; i < WD_NUMDEVS; ++i) { | ||
463 | /* printk("\t%s%i: dcntr at 0x%lx: 0x%x\n", | ||
464 | * WD_OBPNAME, | ||
465 | * i, | ||
466 | * (unsigned long)(&wd_dev.watchdog[i].regs->dcntr), | ||
467 | * readw(&wd_dev.watchdog[i].regs->dcntr)); | ||
468 | */ | ||
469 | printk("\t%s%i: limit at 0x%lx: 0x%x\n", | ||
470 | WD_OBPNAME, | ||
471 | i, | ||
472 | (unsigned long)(&wd_dev.watchdog[i].regs->limit), | ||
473 | readw(&wd_dev.watchdog[i].regs->limit)); | ||
474 | printk("\t%s%i: status at 0x%lx: 0x%x\n", | ||
475 | WD_OBPNAME, | ||
476 | i, | ||
477 | (unsigned long)(&wd_dev.watchdog[i].regs->status), | ||
478 | readb(&wd_dev.watchdog[i].regs->status)); | ||
479 | printk("\t%s%i: driver status: 0x%x\n", | ||
480 | WD_OBPNAME, | ||
481 | i, | ||
482 | wd_getstatus(&wd_dev.watchdog[i])); | ||
483 | } | ||
484 | printk("\tintr_mask at %p: 0x%x\n", | ||
485 | wd_dev.regs + PLD_IMASK, | ||
486 | readb(wd_dev.regs + PLD_IMASK)); | ||
487 | printk("\tpld_status at %p: 0x%x\n", | ||
488 | wd_dev.regs + PLD_STATUS, | ||
489 | readb(wd_dev.regs + PLD_STATUS)); | ||
490 | } | ||
491 | #endif | ||
492 | |||
493 | /* Enable or disable watchdog interrupts | ||
494 | * Because of the CP1400 defect this should only be | ||
495 | * called during initialzation or by wd_[start|stop]timer() | ||
496 | * | ||
497 | * pTimer - pointer to timer device, or NULL to indicate all timers | ||
498 | * enable - non-zero to enable interrupts, zero to disable | ||
499 | */ | ||
500 | static void wd_toggleintr(struct wd_timer* pTimer, int enable) | ||
501 | { | ||
502 | unsigned char curregs = wd_readb(wd_dev.regs + PLD_IMASK); | ||
503 | unsigned char setregs = | ||
504 | (NULL == pTimer) ? | ||
505 | (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) : | ||
506 | (pTimer->intr_mask); | ||
507 | |||
508 | (WD_INTR_ON == enable) ? | ||
509 | (curregs &= ~setregs): | ||
510 | (curregs |= setregs); | ||
511 | |||
512 | wd_writeb(curregs, wd_dev.regs + PLD_IMASK); | ||
513 | return; | ||
514 | } | ||
515 | |||
516 | /* Reset countdown timer with 'limit' value and continue countdown. | ||
517 | * This will not start a stopped timer. | ||
518 | * | ||
519 | * pTimer - pointer to timer device | ||
520 | */ | ||
521 | static void wd_pingtimer(struct wd_timer* pTimer) | ||
522 | { | ||
523 | if (wd_readb(pTimer->regs + WD_STATUS) & WD_S_RUNNING) { | ||
524 | wd_readw(pTimer->regs + WD_DCNTR); | ||
525 | } | ||
526 | } | ||
527 | |||
528 | /* Stop a running watchdog timer-- the timer actually keeps | ||
529 | * running, but the interrupt is masked so that no action is | ||
530 | * taken upon expiration. | ||
531 | * | ||
532 | * pTimer - pointer to timer device | ||
533 | */ | ||
534 | static void wd_stoptimer(struct wd_timer* pTimer) | ||
535 | { | ||
536 | if(wd_readb(pTimer->regs + WD_STATUS) & WD_S_RUNNING) { | ||
537 | wd_toggleintr(pTimer, WD_INTR_OFF); | ||
538 | |||
539 | if(wd_dev.isbaddoggie) { | ||
540 | pTimer->runstatus |= WD_STAT_BSTOP; | ||
541 | wd_brokentimer((unsigned long)&wd_dev); | ||
542 | } | ||
543 | } | ||
544 | } | ||
545 | |||
546 | /* Start a watchdog timer with the specified limit value | ||
547 | * If the watchdog is running, it will be restarted with | ||
548 | * the provided limit value. | ||
549 | * | ||
550 | * This function will enable interrupts on the specified | ||
551 | * watchdog. | ||
552 | * | ||
553 | * pTimer - pointer to timer device | ||
554 | * limit - limit (countdown) value in 1/10th seconds | ||
555 | */ | ||
556 | static void wd_starttimer(struct wd_timer* pTimer) | ||
557 | { | ||
558 | if(wd_dev.isbaddoggie) { | ||
559 | pTimer->runstatus &= ~WD_STAT_BSTOP; | ||
560 | } | ||
561 | pTimer->runstatus &= ~WD_STAT_SVCD; | ||
562 | |||
563 | wd_writew(pTimer->timeout, pTimer->regs + WD_LIMIT); | ||
564 | wd_toggleintr(pTimer, WD_INTR_ON); | ||
565 | } | ||
566 | |||
567 | /* Restarts timer with maximum limit value and | ||
568 | * does not unset 'brokenstop' value. | ||
569 | */ | ||
570 | static void wd_resetbrokentimer(struct wd_timer* pTimer) | ||
571 | { | ||
572 | wd_toggleintr(pTimer, WD_INTR_ON); | ||
573 | wd_writew(WD_BLIMIT, pTimer->regs + WD_LIMIT); | ||
574 | } | ||
575 | |||
576 | /* Timer device initialization helper. | ||
577 | * Returns 0 on success, other on failure | ||
578 | */ | ||
579 | static int wd_inittimer(int whichdog) | ||
580 | { | ||
581 | struct miscdevice *whichmisc; | ||
582 | void __iomem *whichregs; | ||
583 | char whichident[8]; | ||
584 | int whichmask; | ||
585 | __u16 whichlimit; | ||
586 | |||
587 | switch(whichdog) | ||
588 | { | ||
589 | case WD0_ID: | ||
590 | whichmisc = &wd0_miscdev; | ||
591 | strcpy(whichident, "RIC"); | ||
592 | whichregs = wd_dev.regs + WD0_OFF; | ||
593 | whichmask = WD0_INTR_MASK; | ||
594 | whichlimit= (0 == wd0_timeout) ? | ||
595 | (wd_dev.opt_timeout): | ||
596 | (wd0_timeout); | ||
597 | break; | ||
598 | case WD1_ID: | ||
599 | whichmisc = &wd1_miscdev; | ||
600 | strcpy(whichident, "XIR"); | ||
601 | whichregs = wd_dev.regs + WD1_OFF; | ||
602 | whichmask = WD1_INTR_MASK; | ||
603 | whichlimit= (0 == wd1_timeout) ? | ||
604 | (wd_dev.opt_timeout): | ||
605 | (wd1_timeout); | ||
606 | break; | ||
607 | case WD2_ID: | ||
608 | whichmisc = &wd2_miscdev; | ||
609 | strcpy(whichident, "POR"); | ||
610 | whichregs = wd_dev.regs + WD2_OFF; | ||
611 | whichmask = WD2_INTR_MASK; | ||
612 | whichlimit= (0 == wd2_timeout) ? | ||
613 | (wd_dev.opt_timeout): | ||
614 | (wd2_timeout); | ||
615 | break; | ||
616 | default: | ||
617 | printk("%s: %s: invalid watchdog id: %i\n", | ||
618 | WD_OBPNAME, __FUNCTION__, whichdog); | ||
619 | return(1); | ||
620 | } | ||
621 | if(0 != misc_register(whichmisc)) | ||
622 | { | ||
623 | return(1); | ||
624 | } | ||
625 | wd_dev.watchdog[whichdog].regs = whichregs; | ||
626 | wd_dev.watchdog[whichdog].timeout = whichlimit; | ||
627 | wd_dev.watchdog[whichdog].intr_mask = whichmask; | ||
628 | wd_dev.watchdog[whichdog].runstatus &= ~WD_STAT_BSTOP; | ||
629 | wd_dev.watchdog[whichdog].runstatus |= WD_STAT_INIT; | ||
630 | |||
631 | printk("%s%i: %s hardware watchdog [%01i.%i sec] %s\n", | ||
632 | WD_OBPNAME, | ||
633 | whichdog, | ||
634 | whichident, | ||
635 | wd_dev.watchdog[whichdog].timeout / 10, | ||
636 | wd_dev.watchdog[whichdog].timeout % 10, | ||
637 | (0 != wd_dev.opt_enable) ? "in ENABLED mode" : ""); | ||
638 | return(0); | ||
639 | } | ||
640 | |||
641 | /* Timer method called to reset stopped watchdogs-- | ||
642 | * because of the PLD bug on CP1400, we cannot mask | ||
643 | * interrupts within the PLD so me must continually | ||
644 | * reset the timers ad infinitum. | ||
645 | */ | ||
646 | static void wd_brokentimer(unsigned long data) | ||
647 | { | ||
648 | struct wd_device* pDev = (struct wd_device*)data; | ||
649 | int id, tripped = 0; | ||
650 | |||
651 | /* kill a running timer instance, in case we | ||
652 | * were called directly instead of by kernel timer | ||
653 | */ | ||
654 | if(timer_pending(&wd_timer)) { | ||
655 | del_timer(&wd_timer); | ||
656 | } | ||
657 | |||
658 | for(id = WD0_ID; id < WD_NUMDEVS; ++id) { | ||
659 | if(pDev->watchdog[id].runstatus & WD_STAT_BSTOP) { | ||
660 | ++tripped; | ||
661 | wd_resetbrokentimer(&pDev->watchdog[id]); | ||
662 | } | ||
663 | } | ||
664 | |||
665 | if(tripped) { | ||
666 | /* there is at least one timer brokenstopped-- reschedule */ | ||
667 | init_timer(&wd_timer); | ||
668 | wd_timer.expires = WD_BTIMEOUT; | ||
669 | add_timer(&wd_timer); | ||
670 | } | ||
671 | } | ||
672 | |||
673 | static int wd_getstatus(struct wd_timer* pTimer) | ||
674 | { | ||
675 | unsigned char stat = wd_readb(pTimer->regs + WD_STATUS); | ||
676 | unsigned char intr = wd_readb(wd_dev.regs + PLD_IMASK); | ||
677 | unsigned char ret = WD_STOPPED; | ||
678 | |||
679 | /* determine STOPPED */ | ||
680 | if(0 == stat ) { | ||
681 | return(ret); | ||
682 | } | ||
683 | /* determine EXPIRED vs FREERUN vs RUNNING */ | ||
684 | else if(WD_S_EXPIRED & stat) { | ||
685 | ret = WD_EXPIRED; | ||
686 | } | ||
687 | else if(WD_S_RUNNING & stat) { | ||
688 | if(intr & pTimer->intr_mask) { | ||
689 | ret = WD_FREERUN; | ||
690 | } | ||
691 | else { | ||
692 | /* Fudge WD_EXPIRED status for defective CP1400-- | ||
693 | * IF timer is running | ||
694 | * AND brokenstop is set | ||
695 | * AND an interrupt has been serviced | ||
696 | * we are WD_EXPIRED. | ||
697 | * | ||
698 | * IF timer is running | ||
699 | * AND brokenstop is set | ||
700 | * AND no interrupt has been serviced | ||
701 | * we are WD_FREERUN. | ||
702 | */ | ||
703 | if(wd_dev.isbaddoggie && (pTimer->runstatus & WD_STAT_BSTOP)) { | ||
704 | if(pTimer->runstatus & WD_STAT_SVCD) { | ||
705 | ret = WD_EXPIRED; | ||
706 | } | ||
707 | else { | ||
708 | /* we could as well pretend we are expired */ | ||
709 | ret = WD_FREERUN; | ||
710 | } | ||
711 | } | ||
712 | else { | ||
713 | ret = WD_RUNNING; | ||
714 | } | ||
715 | } | ||
716 | } | ||
717 | |||
718 | /* determine SERVICED */ | ||
719 | if(pTimer->runstatus & WD_STAT_SVCD) { | ||
720 | ret |= WD_SERVICED; | ||
721 | } | ||
722 | |||
723 | return(ret); | ||
724 | } | ||
725 | |||
726 | static int __init wd_init(void) | ||
727 | { | ||
728 | int id; | ||
729 | struct linux_ebus *ebus = NULL; | ||
730 | struct linux_ebus_device *edev = NULL; | ||
731 | |||
732 | for_each_ebus(ebus) { | ||
733 | for_each_ebusdev(edev, ebus) { | ||
734 | if (!strcmp(edev->prom_name, WD_OBPNAME)) | ||
735 | goto ebus_done; | ||
736 | } | ||
737 | } | ||
738 | |||
739 | ebus_done: | ||
740 | if(!edev) { | ||
741 | printk("%s: unable to locate device\n", WD_OBPNAME); | ||
742 | return -ENODEV; | ||
743 | } | ||
744 | |||
745 | wd_dev.regs = | ||
746 | ioremap(edev->resource[0].start, 4 * WD_TIMER_REGSZ); /* ? */ | ||
747 | |||
748 | if(NULL == wd_dev.regs) { | ||
749 | printk("%s: unable to map registers\n", WD_OBPNAME); | ||
750 | return(-ENODEV); | ||
751 | } | ||
752 | |||
753 | /* initialize device structure from OBP parameters */ | ||
754 | wd_dev.irq = edev->irqs[0]; | ||
755 | wd_dev.opt_enable = wd_opt_enable(); | ||
756 | wd_dev.opt_reboot = wd_opt_reboot(); | ||
757 | wd_dev.opt_timeout = wd_opt_timeout(); | ||
758 | wd_dev.isbaddoggie = wd_isbroken(); | ||
759 | |||
760 | /* disable all interrupts unless watchdog-enabled? == true */ | ||
761 | if(! wd_dev.opt_enable) { | ||
762 | wd_toggleintr(NULL, WD_INTR_OFF); | ||
763 | } | ||
764 | |||
765 | /* register miscellaneous devices */ | ||
766 | for(id = WD0_ID; id < WD_NUMDEVS; ++id) { | ||
767 | if(0 != wd_inittimer(id)) { | ||
768 | printk("%s%i: unable to initialize\n", WD_OBPNAME, id); | ||
769 | } | ||
770 | } | ||
771 | |||
772 | /* warn about possible defective PLD */ | ||
773 | if(wd_dev.isbaddoggie) { | ||
774 | init_timer(&wd_timer); | ||
775 | wd_timer.function = wd_brokentimer; | ||
776 | wd_timer.data = (unsigned long)&wd_dev; | ||
777 | wd_timer.expires = WD_BTIMEOUT; | ||
778 | |||
779 | printk("%s: PLD defect workaround enabled for model %s\n", | ||
780 | WD_OBPNAME, WD_BADMODEL); | ||
781 | } | ||
782 | return(0); | ||
783 | } | ||
784 | |||
785 | static void __exit wd_cleanup(void) | ||
786 | { | ||
787 | int id; | ||
788 | |||
789 | /* if 'watchdog-enable?' == TRUE, timers are not stopped | ||
790 | * when module is unloaded. All brokenstopped timers will | ||
791 | * also now eventually trip. | ||
792 | */ | ||
793 | for(id = WD0_ID; id < WD_NUMDEVS; ++id) { | ||
794 | if(WD_S_RUNNING == wd_readb(wd_dev.watchdog[id].regs + WD_STATUS)) { | ||
795 | if(wd_dev.opt_enable) { | ||
796 | printk(KERN_WARNING "%s%i: timer not stopped at release\n", | ||
797 | WD_OBPNAME, id); | ||
798 | } | ||
799 | else { | ||
800 | wd_stoptimer(&wd_dev.watchdog[id]); | ||
801 | if(wd_dev.watchdog[id].runstatus & WD_STAT_BSTOP) { | ||
802 | wd_resetbrokentimer(&wd_dev.watchdog[id]); | ||
803 | printk(KERN_WARNING | ||
804 | "%s%i: defect workaround disabled at release, "\ | ||
805 | "timer expires in ~%01i sec\n", | ||
806 | WD_OBPNAME, id, | ||
807 | wd_readw(wd_dev.watchdog[id].regs + WD_LIMIT) / 10); | ||
808 | } | ||
809 | } | ||
810 | } | ||
811 | } | ||
812 | |||
813 | if(wd_dev.isbaddoggie && timer_pending(&wd_timer)) { | ||
814 | del_timer(&wd_timer); | ||
815 | } | ||
816 | if(0 != (wd_dev.watchdog[WD0_ID].runstatus & WD_STAT_INIT)) { | ||
817 | misc_deregister(&wd0_miscdev); | ||
818 | } | ||
819 | if(0 != (wd_dev.watchdog[WD1_ID].runstatus & WD_STAT_INIT)) { | ||
820 | misc_deregister(&wd1_miscdev); | ||
821 | } | ||
822 | if(0 != (wd_dev.watchdog[WD2_ID].runstatus & WD_STAT_INIT)) { | ||
823 | misc_deregister(&wd2_miscdev); | ||
824 | } | ||
825 | if(0 != wd_dev.initialized) { | ||
826 | free_irq(wd_dev.irq, (void *)wd_dev.regs); | ||
827 | } | ||
828 | iounmap(wd_dev.regs); | ||
829 | } | ||
830 | |||
831 | module_init(wd_init); | ||
832 | module_exit(wd_cleanup); | ||
diff --git a/drivers/sbus/char/display7seg.c b/drivers/sbus/char/display7seg.c new file mode 100644 index 00000000000..dbad7f35eb0 --- /dev/null +++ b/drivers/sbus/char/display7seg.c | |||
@@ -0,0 +1,234 @@ | |||
1 | /* $Id: display7seg.c,v 1.6 2002/01/08 16:00:16 davem Exp $ | ||
2 | * | ||
3 | * display7seg - Driver implementation for the 7-segment display | ||
4 | * present on Sun Microsystems CP1400 and CP1500 | ||
5 | * | ||
6 | * Copyright (c) 2000 Eric Brower (ebrower@usa.net) | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/fs.h> | ||
13 | #include <linux/errno.h> | ||
14 | #include <linux/major.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/miscdevice.h> | ||
17 | #include <linux/ioport.h> /* request_region, check_region */ | ||
18 | #include <asm/atomic.h> | ||
19 | #include <asm/ebus.h> /* EBus device */ | ||
20 | #include <asm/oplib.h> /* OpenProm Library */ | ||
21 | #include <asm/uaccess.h> /* put_/get_user */ | ||
22 | |||
23 | #include <asm/display7seg.h> | ||
24 | |||
25 | #define D7S_MINOR 193 | ||
26 | #define D7S_OBPNAME "display7seg" | ||
27 | #define D7S_DEVNAME "d7s" | ||
28 | |||
29 | static int sol_compat = 0; /* Solaris compatibility mode */ | ||
30 | |||
31 | #ifdef MODULE | ||
32 | |||
33 | /* Solaris compatibility flag - | ||
34 | * The Solaris implementation omits support for several | ||
35 | * documented driver features (ref Sun doc 806-0180-03). | ||
36 | * By default, this module supports the documented driver | ||
37 | * abilities, rather than the Solaris implementation: | ||
38 | * | ||
39 | * 1) Device ALWAYS reverts to OBP-specified FLIPPED mode | ||
40 | * upon closure of device or module unload. | ||
41 | * 2) Device ioctls D7SIOCRD/D7SIOCWR honor toggling of | ||
42 | * FLIP bit | ||
43 | * | ||
44 | * If you wish the device to operate as under Solaris, | ||
45 | * omitting above features, set this parameter to non-zero. | ||
46 | */ | ||
47 | module_param | ||
48 | (sol_compat, int, 0); | ||
49 | MODULE_PARM_DESC | ||
50 | (sol_compat, | ||
51 | "Disables documented functionality omitted from Solaris driver"); | ||
52 | |||
53 | MODULE_AUTHOR | ||
54 | ("Eric Brower <ebrower@usa.net>"); | ||
55 | MODULE_DESCRIPTION | ||
56 | ("7-Segment Display driver for Sun Microsystems CP1400/1500"); | ||
57 | MODULE_LICENSE("GPL"); | ||
58 | MODULE_SUPPORTED_DEVICE | ||
59 | ("d7s"); | ||
60 | #endif /* ifdef MODULE */ | ||
61 | |||
62 | /* | ||
63 | * Register block address- see header for details | ||
64 | * ----------------------------------------- | ||
65 | * | DP | ALARM | FLIP | 4 | 3 | 2 | 1 | 0 | | ||
66 | * ----------------------------------------- | ||
67 | * | ||
68 | * DP - Toggles decimal point on/off | ||
69 | * ALARM - Toggles "Alarm" LED green/red | ||
70 | * FLIP - Inverts display for upside-down mounted board | ||
71 | * bits 0-4 - 7-segment display contents | ||
72 | */ | ||
73 | static void __iomem* d7s_regs; | ||
74 | |||
75 | static inline void d7s_free(void) | ||
76 | { | ||
77 | iounmap(d7s_regs); | ||
78 | } | ||
79 | |||
80 | static inline int d7s_obpflipped(void) | ||
81 | { | ||
82 | int opt_node; | ||
83 | |||
84 | opt_node = prom_getchild(prom_root_node); | ||
85 | opt_node = prom_searchsiblings(opt_node, "options"); | ||
86 | return ((-1 != prom_getintdefault(opt_node, "d7s-flipped?", -1)) ? 0 : 1); | ||
87 | } | ||
88 | |||
89 | static atomic_t d7s_users = ATOMIC_INIT(0); | ||
90 | |||
91 | static int d7s_open(struct inode *inode, struct file *f) | ||
92 | { | ||
93 | if (D7S_MINOR != iminor(inode)) | ||
94 | return -ENODEV; | ||
95 | atomic_inc(&d7s_users); | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static int d7s_release(struct inode *inode, struct file *f) | ||
100 | { | ||
101 | /* Reset flipped state to OBP default only if | ||
102 | * no other users have the device open and we | ||
103 | * are not operating in solaris-compat mode | ||
104 | */ | ||
105 | if (atomic_dec_and_test(&d7s_users) && !sol_compat) { | ||
106 | int regval = 0; | ||
107 | |||
108 | regval = readb(d7s_regs); | ||
109 | (0 == d7s_obpflipped()) ? | ||
110 | writeb(regval |= D7S_FLIP, d7s_regs): | ||
111 | writeb(regval &= ~D7S_FLIP, d7s_regs); | ||
112 | } | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static int d7s_ioctl(struct inode *inode, struct file *f, | ||
118 | unsigned int cmd, unsigned long arg) | ||
119 | { | ||
120 | __u8 regs = readb(d7s_regs); | ||
121 | __u8 ireg = 0; | ||
122 | |||
123 | if (D7S_MINOR != iminor(inode)) | ||
124 | return -ENODEV; | ||
125 | |||
126 | switch (cmd) { | ||
127 | case D7SIOCWR: | ||
128 | /* assign device register values | ||
129 | * we mask-out D7S_FLIP if in sol_compat mode | ||
130 | */ | ||
131 | if (get_user(ireg, (int __user *) arg)) | ||
132 | return -EFAULT; | ||
133 | if (0 != sol_compat) { | ||
134 | (regs & D7S_FLIP) ? | ||
135 | (ireg |= D7S_FLIP) : (ireg &= ~D7S_FLIP); | ||
136 | } | ||
137 | writeb(ireg, d7s_regs); | ||
138 | break; | ||
139 | |||
140 | case D7SIOCRD: | ||
141 | /* retrieve device register values | ||
142 | * NOTE: Solaris implementation returns D7S_FLIP bit | ||
143 | * as toggled by user, even though it does not honor it. | ||
144 | * This driver will not misinform you about the state | ||
145 | * of your hardware while in sol_compat mode | ||
146 | */ | ||
147 | if (put_user(regs, (int __user *) arg)) | ||
148 | return -EFAULT; | ||
149 | break; | ||
150 | |||
151 | case D7SIOCTM: | ||
152 | /* toggle device mode-- flip display orientation */ | ||
153 | (regs & D7S_FLIP) ? | ||
154 | (regs &= ~D7S_FLIP) : (regs |= D7S_FLIP); | ||
155 | writeb(regs, d7s_regs); | ||
156 | break; | ||
157 | }; | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static struct file_operations d7s_fops = { | ||
163 | .owner = THIS_MODULE, | ||
164 | .ioctl = d7s_ioctl, | ||
165 | .open = d7s_open, | ||
166 | .release = d7s_release, | ||
167 | }; | ||
168 | |||
169 | static struct miscdevice d7s_miscdev = { D7S_MINOR, D7S_DEVNAME, &d7s_fops }; | ||
170 | |||
171 | static int __init d7s_init(void) | ||
172 | { | ||
173 | struct linux_ebus *ebus = NULL; | ||
174 | struct linux_ebus_device *edev = NULL; | ||
175 | int iTmp = 0, regs = 0; | ||
176 | |||
177 | for_each_ebus(ebus) { | ||
178 | for_each_ebusdev(edev, ebus) { | ||
179 | if (!strcmp(edev->prom_name, D7S_OBPNAME)) | ||
180 | goto ebus_done; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | ebus_done: | ||
185 | if(!edev) { | ||
186 | printk("%s: unable to locate device\n", D7S_DEVNAME); | ||
187 | return -ENODEV; | ||
188 | } | ||
189 | |||
190 | d7s_regs = ioremap(edev->resource[0].start, sizeof(__u8)); | ||
191 | |||
192 | iTmp = misc_register(&d7s_miscdev); | ||
193 | if (0 != iTmp) { | ||
194 | printk("%s: unable to acquire miscdevice minor %i\n", | ||
195 | D7S_DEVNAME, D7S_MINOR); | ||
196 | iounmap(d7s_regs); | ||
197 | return iTmp; | ||
198 | } | ||
199 | |||
200 | /* OBP option "d7s-flipped?" is honored as default | ||
201 | * for the device, and reset default when detached | ||
202 | */ | ||
203 | regs = readb(d7s_regs); | ||
204 | iTmp = d7s_obpflipped(); | ||
205 | (0 == iTmp) ? | ||
206 | writeb(regs |= D7S_FLIP, d7s_regs): | ||
207 | writeb(regs &= ~D7S_FLIP, d7s_regs); | ||
208 | |||
209 | printk("%s: 7-Segment Display%s at 0x%lx %s\n", | ||
210 | D7S_DEVNAME, | ||
211 | (0 == iTmp) ? (" (FLIPPED)") : (""), | ||
212 | edev->resource[0].start, | ||
213 | (0 != sol_compat) ? ("in sol_compat mode") : ("")); | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static void __exit d7s_cleanup(void) | ||
219 | { | ||
220 | int regs = readb(d7s_regs); | ||
221 | |||
222 | /* Honor OBP d7s-flipped? unless operating in solaris-compat mode */ | ||
223 | if (0 == sol_compat) { | ||
224 | (0 == d7s_obpflipped()) ? | ||
225 | writeb(regs |= D7S_FLIP, d7s_regs): | ||
226 | writeb(regs &= ~D7S_FLIP, d7s_regs); | ||
227 | } | ||
228 | |||
229 | misc_deregister(&d7s_miscdev); | ||
230 | d7s_free(); | ||
231 | } | ||
232 | |||
233 | module_init(d7s_init); | ||
234 | module_exit(d7s_cleanup); | ||
diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c new file mode 100644 index 00000000000..f6ed35b24f4 --- /dev/null +++ b/drivers/sbus/char/envctrl.c | |||
@@ -0,0 +1,1181 @@ | |||
1 | /* $Id: envctrl.c,v 1.25 2002/01/15 09:01:26 davem Exp $ | ||
2 | * envctrl.c: Temperature and Fan monitoring on Machines providing it. | ||
3 | * | ||
4 | * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) | ||
5 | * Copyright (C) 2000 Vinh Truong (vinh.truong@eng.sun.com) | ||
6 | * VT - The implementation is to support Sun Microelectronics (SME) platform | ||
7 | * environment monitoring. SME platforms use pcf8584 as the i2c bus | ||
8 | * controller to access pcf8591 (8-bit A/D and D/A converter) and | ||
9 | * pcf8571 (256 x 8-bit static low-voltage RAM with I2C-bus interface). | ||
10 | * At board level, it follows SME Firmware I2C Specification. Reference: | ||
11 | * http://www-eu2.semiconductors.com/pip/PCF8584P | ||
12 | * http://www-eu2.semiconductors.com/pip/PCF8574AP | ||
13 | * http://www-eu2.semiconductors.com/pip/PCF8591P | ||
14 | * | ||
15 | * EB - Added support for CP1500 Global Address and PS/Voltage monitoring. | ||
16 | * Eric Brower <ebrower@usa.net> | ||
17 | * | ||
18 | * DB - Audit every copy_to_user in envctrl_read. | ||
19 | * Daniele Bellucci <bellucda@tiscali.it> | ||
20 | */ | ||
21 | |||
22 | #include <linux/config.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/sched.h> | ||
25 | #include <linux/errno.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/ioport.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/miscdevice.h> | ||
30 | #include <linux/mm.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/kernel.h> | ||
33 | |||
34 | #include <asm/ebus.h> | ||
35 | #include <asm/uaccess.h> | ||
36 | #include <asm/envctrl.h> | ||
37 | |||
38 | #define __KERNEL_SYSCALLS__ | ||
39 | static int errno; | ||
40 | #include <asm/unistd.h> | ||
41 | |||
42 | #define ENVCTRL_MINOR 162 | ||
43 | |||
44 | #define PCF8584_ADDRESS 0x55 | ||
45 | |||
46 | #define CONTROL_PIN 0x80 | ||
47 | #define CONTROL_ES0 0x40 | ||
48 | #define CONTROL_ES1 0x20 | ||
49 | #define CONTROL_ES2 0x10 | ||
50 | #define CONTROL_ENI 0x08 | ||
51 | #define CONTROL_STA 0x04 | ||
52 | #define CONTROL_STO 0x02 | ||
53 | #define CONTROL_ACK 0x01 | ||
54 | |||
55 | #define STATUS_PIN 0x80 | ||
56 | #define STATUS_STS 0x20 | ||
57 | #define STATUS_BER 0x10 | ||
58 | #define STATUS_LRB 0x08 | ||
59 | #define STATUS_AD0 0x08 | ||
60 | #define STATUS_AAB 0x04 | ||
61 | #define STATUS_LAB 0x02 | ||
62 | #define STATUS_BB 0x01 | ||
63 | |||
64 | /* | ||
65 | * CLK Mode Register. | ||
66 | */ | ||
67 | #define BUS_CLK_90 0x00 | ||
68 | #define BUS_CLK_45 0x01 | ||
69 | #define BUS_CLK_11 0x02 | ||
70 | #define BUS_CLK_1_5 0x03 | ||
71 | |||
72 | #define CLK_3 0x00 | ||
73 | #define CLK_4_43 0x10 | ||
74 | #define CLK_6 0x14 | ||
75 | #define CLK_8 0x18 | ||
76 | #define CLK_12 0x1c | ||
77 | |||
78 | #define OBD_SEND_START 0xc5 /* value to generate I2c_bus START condition */ | ||
79 | #define OBD_SEND_STOP 0xc3 /* value to generate I2c_bus STOP condition */ | ||
80 | |||
81 | /* Monitor type of i2c child device. | ||
82 | * Firmware definitions. | ||
83 | */ | ||
84 | #define PCF8584_MAX_CHANNELS 8 | ||
85 | #define PCF8584_GLOBALADDR_TYPE 6 /* global address monitor */ | ||
86 | #define PCF8584_FANSTAT_TYPE 3 /* fan status monitor */ | ||
87 | #define PCF8584_VOLTAGE_TYPE 2 /* voltage monitor */ | ||
88 | #define PCF8584_TEMP_TYPE 1 /* temperature monitor*/ | ||
89 | |||
90 | /* Monitor type of i2c child device. | ||
91 | * Driver definitions. | ||
92 | */ | ||
93 | #define ENVCTRL_NOMON 0 | ||
94 | #define ENVCTRL_CPUTEMP_MON 1 /* cpu temperature monitor */ | ||
95 | #define ENVCTRL_CPUVOLTAGE_MON 2 /* voltage monitor */ | ||
96 | #define ENVCTRL_FANSTAT_MON 3 /* fan status monitor */ | ||
97 | #define ENVCTRL_ETHERTEMP_MON 4 /* ethernet temperarture */ | ||
98 | /* monitor */ | ||
99 | #define ENVCTRL_VOLTAGESTAT_MON 5 /* voltage status monitor */ | ||
100 | #define ENVCTRL_MTHRBDTEMP_MON 6 /* motherboard temperature */ | ||
101 | #define ENVCTRL_SCSITEMP_MON 7 /* scsi temperarture */ | ||
102 | #define ENVCTRL_GLOBALADDR_MON 8 /* global address */ | ||
103 | |||
104 | /* Child device type. | ||
105 | * Driver definitions. | ||
106 | */ | ||
107 | #define I2C_ADC 0 /* pcf8591 */ | ||
108 | #define I2C_GPIO 1 /* pcf8571 */ | ||
109 | |||
110 | /* Data read from child device may need to decode | ||
111 | * through a data table and a scale. | ||
112 | * Translation type as defined by firmware. | ||
113 | */ | ||
114 | #define ENVCTRL_TRANSLATE_NO 0 | ||
115 | #define ENVCTRL_TRANSLATE_PARTIAL 1 | ||
116 | #define ENVCTRL_TRANSLATE_COMBINED 2 | ||
117 | #define ENVCTRL_TRANSLATE_FULL 3 /* table[data] */ | ||
118 | #define ENVCTRL_TRANSLATE_SCALE 4 /* table[data]/scale */ | ||
119 | |||
120 | /* Driver miscellaneous definitions. */ | ||
121 | #define ENVCTRL_MAX_CPU 4 | ||
122 | #define CHANNEL_DESC_SZ 256 | ||
123 | |||
124 | /* Mask values for combined GlobalAddress/PowerStatus node */ | ||
125 | #define ENVCTRL_GLOBALADDR_ADDR_MASK 0x1F | ||
126 | #define ENVCTRL_GLOBALADDR_PSTAT_MASK 0x60 | ||
127 | |||
128 | /* Node 0x70 ignored on CompactPCI CP1400/1500 platforms | ||
129 | * (see envctrl_init_i2c_child) | ||
130 | */ | ||
131 | #define ENVCTRL_CPCI_IGNORED_NODE 0x70 | ||
132 | |||
133 | #define PCF8584_DATA 0x00 | ||
134 | #define PCF8584_CSR 0x01 | ||
135 | |||
136 | /* Each child device can be monitored by up to PCF8584_MAX_CHANNELS. | ||
137 | * Property of a port or channel as defined by the firmware. | ||
138 | */ | ||
139 | struct pcf8584_channel { | ||
140 | unsigned char chnl_no; | ||
141 | unsigned char io_direction; | ||
142 | unsigned char type; | ||
143 | unsigned char last; | ||
144 | }; | ||
145 | |||
146 | /* Each child device may have one or more tables of bytes to help decode | ||
147 | * data. Table property as defined by the firmware. | ||
148 | */ | ||
149 | struct pcf8584_tblprop { | ||
150 | unsigned int type; | ||
151 | unsigned int scale; | ||
152 | unsigned int offset; /* offset from the beginning of the table */ | ||
153 | unsigned int size; | ||
154 | }; | ||
155 | |||
156 | /* i2c child */ | ||
157 | struct i2c_child_t { | ||
158 | /* Either ADC or GPIO. */ | ||
159 | unsigned char i2ctype; | ||
160 | unsigned long addr; | ||
161 | struct pcf8584_channel chnl_array[PCF8584_MAX_CHANNELS]; | ||
162 | |||
163 | /* Channel info. */ | ||
164 | unsigned int total_chnls; /* Number of monitor channels. */ | ||
165 | unsigned char fan_mask; /* Byte mask for fan status channels. */ | ||
166 | unsigned char voltage_mask; /* Byte mask for voltage status channels. */ | ||
167 | struct pcf8584_tblprop tblprop_array[PCF8584_MAX_CHANNELS]; | ||
168 | |||
169 | /* Properties of all monitor channels. */ | ||
170 | unsigned int total_tbls; /* Number of monitor tables. */ | ||
171 | char *tables; /* Pointer to table(s). */ | ||
172 | char chnls_desc[CHANNEL_DESC_SZ]; /* Channel description. */ | ||
173 | char mon_type[PCF8584_MAX_CHANNELS]; | ||
174 | }; | ||
175 | |||
176 | static void __iomem *i2c; | ||
177 | static struct i2c_child_t i2c_childlist[ENVCTRL_MAX_CPU*2]; | ||
178 | static unsigned char chnls_mask[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; | ||
179 | static unsigned int warning_temperature = 0; | ||
180 | static unsigned int shutdown_temperature = 0; | ||
181 | static char read_cpu; | ||
182 | |||
183 | /* Forward declarations. */ | ||
184 | static struct i2c_child_t *envctrl_get_i2c_child(unsigned char); | ||
185 | |||
186 | /* Function Description: Test the PIN bit (Pending Interrupt Not) | ||
187 | * to test when serial transmission is completed . | ||
188 | * Return : None. | ||
189 | */ | ||
190 | static void envtrl_i2c_test_pin(void) | ||
191 | { | ||
192 | int limit = 1000000; | ||
193 | |||
194 | while (--limit > 0) { | ||
195 | if (!(readb(i2c + PCF8584_CSR) & STATUS_PIN)) | ||
196 | break; | ||
197 | udelay(1); | ||
198 | } | ||
199 | |||
200 | if (limit <= 0) | ||
201 | printk(KERN_INFO "envctrl: Pin status will not clear.\n"); | ||
202 | } | ||
203 | |||
204 | /* Function Description: Test busy bit. | ||
205 | * Return : None. | ||
206 | */ | ||
207 | static void envctrl_i2c_test_bb(void) | ||
208 | { | ||
209 | int limit = 1000000; | ||
210 | |||
211 | while (--limit > 0) { | ||
212 | /* Busy bit 0 means busy. */ | ||
213 | if (readb(i2c + PCF8584_CSR) & STATUS_BB) | ||
214 | break; | ||
215 | udelay(1); | ||
216 | } | ||
217 | |||
218 | if (limit <= 0) | ||
219 | printk(KERN_INFO "envctrl: Busy bit will not clear.\n"); | ||
220 | } | ||
221 | |||
222 | /* Function Description: Send the address for a read access. | ||
223 | * Return : 0 if not acknowledged, otherwise acknowledged. | ||
224 | */ | ||
225 | static int envctrl_i2c_read_addr(unsigned char addr) | ||
226 | { | ||
227 | envctrl_i2c_test_bb(); | ||
228 | |||
229 | /* Load address. */ | ||
230 | writeb(addr + 1, i2c + PCF8584_DATA); | ||
231 | |||
232 | envctrl_i2c_test_bb(); | ||
233 | |||
234 | writeb(OBD_SEND_START, i2c + PCF8584_CSR); | ||
235 | |||
236 | /* Wait for PIN. */ | ||
237 | envtrl_i2c_test_pin(); | ||
238 | |||
239 | /* CSR 0 means acknowledged. */ | ||
240 | if (!(readb(i2c + PCF8584_CSR) & STATUS_LRB)) { | ||
241 | return readb(i2c + PCF8584_DATA); | ||
242 | } else { | ||
243 | writeb(OBD_SEND_STOP, i2c + PCF8584_CSR); | ||
244 | return 0; | ||
245 | } | ||
246 | } | ||
247 | |||
248 | /* Function Description: Send the address for write mode. | ||
249 | * Return : None. | ||
250 | */ | ||
251 | static void envctrl_i2c_write_addr(unsigned char addr) | ||
252 | { | ||
253 | envctrl_i2c_test_bb(); | ||
254 | writeb(addr, i2c + PCF8584_DATA); | ||
255 | |||
256 | /* Generate Start condition. */ | ||
257 | writeb(OBD_SEND_START, i2c + PCF8584_CSR); | ||
258 | } | ||
259 | |||
260 | /* Function Description: Read 1 byte of data from addr | ||
261 | * set by envctrl_i2c_read_addr() | ||
262 | * Return : Data from address set by envctrl_i2c_read_addr(). | ||
263 | */ | ||
264 | static unsigned char envctrl_i2c_read_data(void) | ||
265 | { | ||
266 | envtrl_i2c_test_pin(); | ||
267 | writeb(CONTROL_ES0, i2c + PCF8584_CSR); /* Send neg ack. */ | ||
268 | return readb(i2c + PCF8584_DATA); | ||
269 | } | ||
270 | |||
271 | /* Function Description: Instruct the device which port to read data from. | ||
272 | * Return : None. | ||
273 | */ | ||
274 | static void envctrl_i2c_write_data(unsigned char port) | ||
275 | { | ||
276 | envtrl_i2c_test_pin(); | ||
277 | writeb(port, i2c + PCF8584_DATA); | ||
278 | } | ||
279 | |||
280 | /* Function Description: Generate Stop condition after last byte is sent. | ||
281 | * Return : None. | ||
282 | */ | ||
283 | static void envctrl_i2c_stop(void) | ||
284 | { | ||
285 | envtrl_i2c_test_pin(); | ||
286 | writeb(OBD_SEND_STOP, i2c + PCF8584_CSR); | ||
287 | } | ||
288 | |||
289 | /* Function Description: Read adc device. | ||
290 | * Return : Data at address and port. | ||
291 | */ | ||
292 | static unsigned char envctrl_i2c_read_8591(unsigned char addr, unsigned char port) | ||
293 | { | ||
294 | /* Send address. */ | ||
295 | envctrl_i2c_write_addr(addr); | ||
296 | |||
297 | /* Setup port to read. */ | ||
298 | envctrl_i2c_write_data(port); | ||
299 | envctrl_i2c_stop(); | ||
300 | |||
301 | /* Read port. */ | ||
302 | envctrl_i2c_read_addr(addr); | ||
303 | |||
304 | /* Do a single byte read and send stop. */ | ||
305 | envctrl_i2c_read_data(); | ||
306 | envctrl_i2c_stop(); | ||
307 | |||
308 | return readb(i2c + PCF8584_DATA); | ||
309 | } | ||
310 | |||
311 | /* Function Description: Read gpio device. | ||
312 | * Return : Data at address. | ||
313 | */ | ||
314 | static unsigned char envctrl_i2c_read_8574(unsigned char addr) | ||
315 | { | ||
316 | unsigned char rd; | ||
317 | |||
318 | envctrl_i2c_read_addr(addr); | ||
319 | |||
320 | /* Do a single byte read and send stop. */ | ||
321 | rd = envctrl_i2c_read_data(); | ||
322 | envctrl_i2c_stop(); | ||
323 | return rd; | ||
324 | } | ||
325 | |||
326 | /* Function Description: Decode data read from an adc device using firmware | ||
327 | * table. | ||
328 | * Return: Number of read bytes. Data is stored in bufdata in ascii format. | ||
329 | */ | ||
330 | static int envctrl_i2c_data_translate(unsigned char data, int translate_type, | ||
331 | int scale, char *tbl, char *bufdata) | ||
332 | { | ||
333 | int len = 0; | ||
334 | |||
335 | switch (translate_type) { | ||
336 | case ENVCTRL_TRANSLATE_NO: | ||
337 | /* No decode necessary. */ | ||
338 | len = 1; | ||
339 | bufdata[0] = data; | ||
340 | break; | ||
341 | |||
342 | case ENVCTRL_TRANSLATE_FULL: | ||
343 | /* Decode this way: data = table[data]. */ | ||
344 | len = 1; | ||
345 | bufdata[0] = tbl[data]; | ||
346 | break; | ||
347 | |||
348 | case ENVCTRL_TRANSLATE_SCALE: | ||
349 | /* Decode this way: data = table[data]/scale */ | ||
350 | sprintf(bufdata,"%d ", (tbl[data] * 10) / (scale)); | ||
351 | len = strlen(bufdata); | ||
352 | bufdata[len - 1] = bufdata[len - 2]; | ||
353 | bufdata[len - 2] = '.'; | ||
354 | break; | ||
355 | |||
356 | default: | ||
357 | break; | ||
358 | }; | ||
359 | |||
360 | return len; | ||
361 | } | ||
362 | |||
363 | /* Function Description: Read cpu-related data such as cpu temperature, voltage. | ||
364 | * Return: Number of read bytes. Data is stored in bufdata in ascii format. | ||
365 | */ | ||
366 | static int envctrl_read_cpu_info(int cpu, struct i2c_child_t *pchild, | ||
367 | char mon_type, unsigned char *bufdata) | ||
368 | { | ||
369 | unsigned char data; | ||
370 | int i; | ||
371 | char *tbl, j = -1; | ||
372 | |||
373 | /* Find the right monitor type and channel. */ | ||
374 | for (i = 0; i < PCF8584_MAX_CHANNELS; i++) { | ||
375 | if (pchild->mon_type[i] == mon_type) { | ||
376 | if (++j == cpu) { | ||
377 | break; | ||
378 | } | ||
379 | } | ||
380 | } | ||
381 | |||
382 | if (j != cpu) | ||
383 | return 0; | ||
384 | |||
385 | /* Read data from address and port. */ | ||
386 | data = envctrl_i2c_read_8591((unsigned char)pchild->addr, | ||
387 | (unsigned char)pchild->chnl_array[i].chnl_no); | ||
388 | |||
389 | /* Find decoding table. */ | ||
390 | tbl = pchild->tables + pchild->tblprop_array[i].offset; | ||
391 | |||
392 | return envctrl_i2c_data_translate(data, pchild->tblprop_array[i].type, | ||
393 | pchild->tblprop_array[i].scale, | ||
394 | tbl, bufdata); | ||
395 | } | ||
396 | |||
397 | /* Function Description: Read noncpu-related data such as motherboard | ||
398 | * temperature. | ||
399 | * Return: Number of read bytes. Data is stored in bufdata in ascii format. | ||
400 | */ | ||
401 | static int envctrl_read_noncpu_info(struct i2c_child_t *pchild, | ||
402 | char mon_type, unsigned char *bufdata) | ||
403 | { | ||
404 | unsigned char data; | ||
405 | int i; | ||
406 | char *tbl = NULL; | ||
407 | |||
408 | for (i = 0; i < PCF8584_MAX_CHANNELS; i++) { | ||
409 | if (pchild->mon_type[i] == mon_type) | ||
410 | break; | ||
411 | } | ||
412 | |||
413 | if (i >= PCF8584_MAX_CHANNELS) | ||
414 | return 0; | ||
415 | |||
416 | /* Read data from address and port. */ | ||
417 | data = envctrl_i2c_read_8591((unsigned char)pchild->addr, | ||
418 | (unsigned char)pchild->chnl_array[i].chnl_no); | ||
419 | |||
420 | /* Find decoding table. */ | ||
421 | tbl = pchild->tables + pchild->tblprop_array[i].offset; | ||
422 | |||
423 | return envctrl_i2c_data_translate(data, pchild->tblprop_array[i].type, | ||
424 | pchild->tblprop_array[i].scale, | ||
425 | tbl, bufdata); | ||
426 | } | ||
427 | |||
428 | /* Function Description: Read fan status. | ||
429 | * Return : Always 1 byte. Status stored in bufdata. | ||
430 | */ | ||
431 | static int envctrl_i2c_fan_status(struct i2c_child_t *pchild, | ||
432 | unsigned char data, | ||
433 | char *bufdata) | ||
434 | { | ||
435 | unsigned char tmp, ret = 0; | ||
436 | int i, j = 0; | ||
437 | |||
438 | tmp = data & pchild->fan_mask; | ||
439 | |||
440 | if (tmp == pchild->fan_mask) { | ||
441 | /* All bits are on. All fans are functioning. */ | ||
442 | ret = ENVCTRL_ALL_FANS_GOOD; | ||
443 | } else if (tmp == 0) { | ||
444 | /* No bits are on. No fans are functioning. */ | ||
445 | ret = ENVCTRL_ALL_FANS_BAD; | ||
446 | } else { | ||
447 | /* Go through all channels, mark 'on' the matched bits. | ||
448 | * Notice that fan_mask may have discontiguous bits but | ||
449 | * return mask are always contiguous. For example if we | ||
450 | * monitor 4 fans at channels 0,1,2,4, the return mask | ||
451 | * should be 00010000 if only fan at channel 4 is working. | ||
452 | */ | ||
453 | for (i = 0; i < PCF8584_MAX_CHANNELS;i++) { | ||
454 | if (pchild->fan_mask & chnls_mask[i]) { | ||
455 | if (!(chnls_mask[i] & tmp)) | ||
456 | ret |= chnls_mask[j]; | ||
457 | |||
458 | j++; | ||
459 | } | ||
460 | } | ||
461 | } | ||
462 | |||
463 | bufdata[0] = ret; | ||
464 | return 1; | ||
465 | } | ||
466 | |||
467 | /* Function Description: Read global addressing line. | ||
468 | * Return : Always 1 byte. Status stored in bufdata. | ||
469 | */ | ||
470 | static int envctrl_i2c_globaladdr(struct i2c_child_t *pchild, | ||
471 | unsigned char data, | ||
472 | char *bufdata) | ||
473 | { | ||
474 | /* Translatation table is not necessary, as global | ||
475 | * addr is the integer value of the GA# bits. | ||
476 | * | ||
477 | * NOTE: MSB is documented as zero, but I see it as '1' always.... | ||
478 | * | ||
479 | * ----------------------------------------------- | ||
480 | * | 0 | FAL | DEG | GA4 | GA3 | GA2 | GA1 | GA0 | | ||
481 | * ----------------------------------------------- | ||
482 | * GA0 - GA4 integer value of Global Address (backplane slot#) | ||
483 | * DEG 0 = cPCI Power supply output is starting to degrade | ||
484 | * 1 = cPCI Power supply output is OK | ||
485 | * FAL 0 = cPCI Power supply has failed | ||
486 | * 1 = cPCI Power supply output is OK | ||
487 | */ | ||
488 | bufdata[0] = (data & ENVCTRL_GLOBALADDR_ADDR_MASK); | ||
489 | return 1; | ||
490 | } | ||
491 | |||
492 | /* Function Description: Read standard voltage and power supply status. | ||
493 | * Return : Always 1 byte. Status stored in bufdata. | ||
494 | */ | ||
495 | static unsigned char envctrl_i2c_voltage_status(struct i2c_child_t *pchild, | ||
496 | unsigned char data, | ||
497 | char *bufdata) | ||
498 | { | ||
499 | unsigned char tmp, ret = 0; | ||
500 | int i, j = 0; | ||
501 | |||
502 | tmp = data & pchild->voltage_mask; | ||
503 | |||
504 | /* Two channels are used to monitor voltage and power supply. */ | ||
505 | if (tmp == pchild->voltage_mask) { | ||
506 | /* All bits are on. Voltage and power supply are okay. */ | ||
507 | ret = ENVCTRL_VOLTAGE_POWERSUPPLY_GOOD; | ||
508 | } else if (tmp == 0) { | ||
509 | /* All bits are off. Voltage and power supply are bad */ | ||
510 | ret = ENVCTRL_VOLTAGE_POWERSUPPLY_BAD; | ||
511 | } else { | ||
512 | /* Either voltage or power supply has problem. */ | ||
513 | for (i = 0; i < PCF8584_MAX_CHANNELS; i++) { | ||
514 | if (pchild->voltage_mask & chnls_mask[i]) { | ||
515 | j++; | ||
516 | |||
517 | /* Break out when there is a mismatch. */ | ||
518 | if (!(chnls_mask[i] & tmp)) | ||
519 | break; | ||
520 | } | ||
521 | } | ||
522 | |||
523 | /* Make a wish that hardware will always use the | ||
524 | * first channel for voltage and the second for | ||
525 | * power supply. | ||
526 | */ | ||
527 | if (j == 1) | ||
528 | ret = ENVCTRL_VOLTAGE_BAD; | ||
529 | else | ||
530 | ret = ENVCTRL_POWERSUPPLY_BAD; | ||
531 | } | ||
532 | |||
533 | bufdata[0] = ret; | ||
534 | return 1; | ||
535 | } | ||
536 | |||
537 | /* Function Description: Read a byte from /dev/envctrl. Mapped to user read(). | ||
538 | * Return: Number of read bytes. 0 for error. | ||
539 | */ | ||
540 | static ssize_t | ||
541 | envctrl_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | ||
542 | { | ||
543 | struct i2c_child_t *pchild; | ||
544 | unsigned char data[10]; | ||
545 | int ret = 0; | ||
546 | |||
547 | /* Get the type of read as decided in ioctl() call. | ||
548 | * Find the appropriate i2c child. | ||
549 | * Get the data and put back to the user buffer. | ||
550 | */ | ||
551 | |||
552 | switch ((int)(long)file->private_data) { | ||
553 | case ENVCTRL_RD_WARNING_TEMPERATURE: | ||
554 | if (warning_temperature == 0) | ||
555 | return 0; | ||
556 | |||
557 | data[0] = (unsigned char)(warning_temperature); | ||
558 | ret = 1; | ||
559 | if (copy_to_user(buf, data, ret)) | ||
560 | ret = -EFAULT; | ||
561 | break; | ||
562 | |||
563 | case ENVCTRL_RD_SHUTDOWN_TEMPERATURE: | ||
564 | if (shutdown_temperature == 0) | ||
565 | return 0; | ||
566 | |||
567 | data[0] = (unsigned char)(shutdown_temperature); | ||
568 | ret = 1; | ||
569 | if (copy_to_user(buf, data, ret)) | ||
570 | ret = -EFAULT; | ||
571 | break; | ||
572 | |||
573 | case ENVCTRL_RD_MTHRBD_TEMPERATURE: | ||
574 | if (!(pchild = envctrl_get_i2c_child(ENVCTRL_MTHRBDTEMP_MON))) | ||
575 | return 0; | ||
576 | ret = envctrl_read_noncpu_info(pchild, ENVCTRL_MTHRBDTEMP_MON, data); | ||
577 | if (copy_to_user(buf, data, ret)) | ||
578 | ret = -EFAULT; | ||
579 | break; | ||
580 | |||
581 | case ENVCTRL_RD_CPU_TEMPERATURE: | ||
582 | if (!(pchild = envctrl_get_i2c_child(ENVCTRL_CPUTEMP_MON))) | ||
583 | return 0; | ||
584 | ret = envctrl_read_cpu_info(read_cpu, pchild, ENVCTRL_CPUTEMP_MON, data); | ||
585 | |||
586 | /* Reset cpu to the default cpu0. */ | ||
587 | if (copy_to_user(buf, data, ret)) | ||
588 | ret = -EFAULT; | ||
589 | break; | ||
590 | |||
591 | case ENVCTRL_RD_CPU_VOLTAGE: | ||
592 | if (!(pchild = envctrl_get_i2c_child(ENVCTRL_CPUVOLTAGE_MON))) | ||
593 | return 0; | ||
594 | ret = envctrl_read_cpu_info(read_cpu, pchild, ENVCTRL_CPUVOLTAGE_MON, data); | ||
595 | |||
596 | /* Reset cpu to the default cpu0. */ | ||
597 | if (copy_to_user(buf, data, ret)) | ||
598 | ret = -EFAULT; | ||
599 | break; | ||
600 | |||
601 | case ENVCTRL_RD_SCSI_TEMPERATURE: | ||
602 | if (!(pchild = envctrl_get_i2c_child(ENVCTRL_SCSITEMP_MON))) | ||
603 | return 0; | ||
604 | ret = envctrl_read_noncpu_info(pchild, ENVCTRL_SCSITEMP_MON, data); | ||
605 | if (copy_to_user(buf, data, ret)) | ||
606 | ret = -EFAULT; | ||
607 | break; | ||
608 | |||
609 | case ENVCTRL_RD_ETHERNET_TEMPERATURE: | ||
610 | if (!(pchild = envctrl_get_i2c_child(ENVCTRL_ETHERTEMP_MON))) | ||
611 | return 0; | ||
612 | ret = envctrl_read_noncpu_info(pchild, ENVCTRL_ETHERTEMP_MON, data); | ||
613 | if (copy_to_user(buf, data, ret)) | ||
614 | ret = -EFAULT; | ||
615 | break; | ||
616 | |||
617 | case ENVCTRL_RD_FAN_STATUS: | ||
618 | if (!(pchild = envctrl_get_i2c_child(ENVCTRL_FANSTAT_MON))) | ||
619 | return 0; | ||
620 | data[0] = envctrl_i2c_read_8574(pchild->addr); | ||
621 | ret = envctrl_i2c_fan_status(pchild,data[0], data); | ||
622 | if (copy_to_user(buf, data, ret)) | ||
623 | ret = -EFAULT; | ||
624 | break; | ||
625 | |||
626 | case ENVCTRL_RD_GLOBALADDRESS: | ||
627 | if (!(pchild = envctrl_get_i2c_child(ENVCTRL_GLOBALADDR_MON))) | ||
628 | return 0; | ||
629 | data[0] = envctrl_i2c_read_8574(pchild->addr); | ||
630 | ret = envctrl_i2c_globaladdr(pchild, data[0], data); | ||
631 | if (copy_to_user(buf, data, ret)) | ||
632 | ret = -EFAULT; | ||
633 | break; | ||
634 | |||
635 | case ENVCTRL_RD_VOLTAGE_STATUS: | ||
636 | if (!(pchild = envctrl_get_i2c_child(ENVCTRL_VOLTAGESTAT_MON))) | ||
637 | /* If voltage monitor not present, check for CPCI equivalent */ | ||
638 | if (!(pchild = envctrl_get_i2c_child(ENVCTRL_GLOBALADDR_MON))) | ||
639 | return 0; | ||
640 | data[0] = envctrl_i2c_read_8574(pchild->addr); | ||
641 | ret = envctrl_i2c_voltage_status(pchild, data[0], data); | ||
642 | if (copy_to_user(buf, data, ret)) | ||
643 | ret = -EFAULT; | ||
644 | break; | ||
645 | |||
646 | default: | ||
647 | break; | ||
648 | |||
649 | }; | ||
650 | |||
651 | return ret; | ||
652 | } | ||
653 | |||
654 | /* Function Description: Command what to read. Mapped to user ioctl(). | ||
655 | * Return: Gives 0 for implemented commands, -EINVAL otherwise. | ||
656 | */ | ||
657 | static int | ||
658 | envctrl_ioctl(struct inode *inode, struct file *file, | ||
659 | unsigned int cmd, unsigned long arg) | ||
660 | { | ||
661 | char __user *infobuf; | ||
662 | |||
663 | switch (cmd) { | ||
664 | case ENVCTRL_RD_WARNING_TEMPERATURE: | ||
665 | case ENVCTRL_RD_SHUTDOWN_TEMPERATURE: | ||
666 | case ENVCTRL_RD_MTHRBD_TEMPERATURE: | ||
667 | case ENVCTRL_RD_FAN_STATUS: | ||
668 | case ENVCTRL_RD_VOLTAGE_STATUS: | ||
669 | case ENVCTRL_RD_ETHERNET_TEMPERATURE: | ||
670 | case ENVCTRL_RD_SCSI_TEMPERATURE: | ||
671 | case ENVCTRL_RD_GLOBALADDRESS: | ||
672 | file->private_data = (void *)(long)cmd; | ||
673 | break; | ||
674 | |||
675 | case ENVCTRL_RD_CPU_TEMPERATURE: | ||
676 | case ENVCTRL_RD_CPU_VOLTAGE: | ||
677 | /* Check to see if application passes in any cpu number, | ||
678 | * the default is cpu0. | ||
679 | */ | ||
680 | infobuf = (char __user *) arg; | ||
681 | if (infobuf == NULL) { | ||
682 | read_cpu = 0; | ||
683 | }else { | ||
684 | get_user(read_cpu, infobuf); | ||
685 | } | ||
686 | |||
687 | /* Save the command for use when reading. */ | ||
688 | file->private_data = (void *)(long)cmd; | ||
689 | break; | ||
690 | |||
691 | default: | ||
692 | return -EINVAL; | ||
693 | }; | ||
694 | |||
695 | return 0; | ||
696 | } | ||
697 | |||
698 | /* Function Description: open device. Mapped to user open(). | ||
699 | * Return: Always 0. | ||
700 | */ | ||
701 | static int | ||
702 | envctrl_open(struct inode *inode, struct file *file) | ||
703 | { | ||
704 | file->private_data = NULL; | ||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | /* Function Description: Open device. Mapped to user close(). | ||
709 | * Return: Always 0. | ||
710 | */ | ||
711 | static int | ||
712 | envctrl_release(struct inode *inode, struct file *file) | ||
713 | { | ||
714 | return 0; | ||
715 | } | ||
716 | |||
717 | static struct file_operations envctrl_fops = { | ||
718 | .owner = THIS_MODULE, | ||
719 | .read = envctrl_read, | ||
720 | .ioctl = envctrl_ioctl, | ||
721 | .open = envctrl_open, | ||
722 | .release = envctrl_release, | ||
723 | }; | ||
724 | |||
725 | static struct miscdevice envctrl_dev = { | ||
726 | ENVCTRL_MINOR, | ||
727 | "envctrl", | ||
728 | &envctrl_fops | ||
729 | }; | ||
730 | |||
731 | /* Function Description: Set monitor type based on firmware description. | ||
732 | * Return: None. | ||
733 | */ | ||
734 | static void envctrl_set_mon(struct i2c_child_t *pchild, | ||
735 | char *chnl_desc, | ||
736 | int chnl_no) | ||
737 | { | ||
738 | /* Firmware only has temperature type. It does not distinguish | ||
739 | * different kinds of temperatures. We use channel description | ||
740 | * to disinguish them. | ||
741 | */ | ||
742 | if (!(strcmp(chnl_desc,"temp,cpu")) || | ||
743 | !(strcmp(chnl_desc,"temp,cpu0")) || | ||
744 | !(strcmp(chnl_desc,"temp,cpu1")) || | ||
745 | !(strcmp(chnl_desc,"temp,cpu2")) || | ||
746 | !(strcmp(chnl_desc,"temp,cpu3"))) | ||
747 | pchild->mon_type[chnl_no] = ENVCTRL_CPUTEMP_MON; | ||
748 | |||
749 | if (!(strcmp(chnl_desc,"vddcore,cpu0")) || | ||
750 | !(strcmp(chnl_desc,"vddcore,cpu1")) || | ||
751 | !(strcmp(chnl_desc,"vddcore,cpu2")) || | ||
752 | !(strcmp(chnl_desc,"vddcore,cpu3"))) | ||
753 | pchild->mon_type[chnl_no] = ENVCTRL_CPUVOLTAGE_MON; | ||
754 | |||
755 | if (!(strcmp(chnl_desc,"temp,motherboard"))) | ||
756 | pchild->mon_type[chnl_no] = ENVCTRL_MTHRBDTEMP_MON; | ||
757 | |||
758 | if (!(strcmp(chnl_desc,"temp,scsi"))) | ||
759 | pchild->mon_type[chnl_no] = ENVCTRL_SCSITEMP_MON; | ||
760 | |||
761 | if (!(strcmp(chnl_desc,"temp,ethernet"))) | ||
762 | pchild->mon_type[chnl_no] = ENVCTRL_ETHERTEMP_MON; | ||
763 | } | ||
764 | |||
765 | /* Function Description: Initialize monitor channel with channel desc, | ||
766 | * decoding tables, monitor type, optional properties. | ||
767 | * Return: None. | ||
768 | */ | ||
769 | static void envctrl_init_adc(struct i2c_child_t *pchild, int node) | ||
770 | { | ||
771 | char chnls_desc[CHANNEL_DESC_SZ]; | ||
772 | int i = 0, len; | ||
773 | char *pos = chnls_desc; | ||
774 | |||
775 | /* Firmware describe channels into a stream separated by a '\0'. */ | ||
776 | len = prom_getproperty(node, "channels-description", chnls_desc, | ||
777 | CHANNEL_DESC_SZ); | ||
778 | chnls_desc[CHANNEL_DESC_SZ - 1] = '\0'; | ||
779 | |||
780 | while (len > 0) { | ||
781 | int l = strlen(pos) + 1; | ||
782 | envctrl_set_mon(pchild, pos, i++); | ||
783 | len -= l; | ||
784 | pos += l; | ||
785 | } | ||
786 | |||
787 | /* Get optional properties. */ | ||
788 | len = prom_getproperty(node, "warning-temp", (char *)&warning_temperature, | ||
789 | sizeof(warning_temperature)); | ||
790 | len = prom_getproperty(node, "shutdown-temp", (char *)&shutdown_temperature, | ||
791 | sizeof(shutdown_temperature)); | ||
792 | } | ||
793 | |||
794 | /* Function Description: Initialize child device monitoring fan status. | ||
795 | * Return: None. | ||
796 | */ | ||
797 | static void envctrl_init_fanstat(struct i2c_child_t *pchild) | ||
798 | { | ||
799 | int i; | ||
800 | |||
801 | /* Go through all channels and set up the mask. */ | ||
802 | for (i = 0; i < pchild->total_chnls; i++) | ||
803 | pchild->fan_mask |= chnls_mask[(pchild->chnl_array[i]).chnl_no]; | ||
804 | |||
805 | /* We only need to know if this child has fan status monitored. | ||
806 | * We don't care which channels since we have the mask already. | ||
807 | */ | ||
808 | pchild->mon_type[0] = ENVCTRL_FANSTAT_MON; | ||
809 | } | ||
810 | |||
811 | /* Function Description: Initialize child device for global addressing line. | ||
812 | * Return: None. | ||
813 | */ | ||
814 | static void envctrl_init_globaladdr(struct i2c_child_t *pchild) | ||
815 | { | ||
816 | int i; | ||
817 | |||
818 | /* Voltage/PowerSupply monitoring is piggybacked | ||
819 | * with Global Address on CompactPCI. See comments | ||
820 | * within envctrl_i2c_globaladdr for bit assignments. | ||
821 | * | ||
822 | * The mask is created here by assigning mask bits to each | ||
823 | * bit position that represents PCF8584_VOLTAGE_TYPE data. | ||
824 | * Channel numbers are not consecutive within the globaladdr | ||
825 | * node (why?), so we use the actual counter value as chnls_mask | ||
826 | * index instead of the chnl_array[x].chnl_no value. | ||
827 | * | ||
828 | * NOTE: This loop could be replaced with a constant representing | ||
829 | * a mask of bits 5&6 (ENVCTRL_GLOBALADDR_PSTAT_MASK). | ||
830 | */ | ||
831 | for (i = 0; i < pchild->total_chnls; i++) { | ||
832 | if (PCF8584_VOLTAGE_TYPE == pchild->chnl_array[i].type) { | ||
833 | pchild->voltage_mask |= chnls_mask[i]; | ||
834 | } | ||
835 | } | ||
836 | |||
837 | /* We only need to know if this child has global addressing | ||
838 | * line monitored. We don't care which channels since we know | ||
839 | * the mask already (ENVCTRL_GLOBALADDR_ADDR_MASK). | ||
840 | */ | ||
841 | pchild->mon_type[0] = ENVCTRL_GLOBALADDR_MON; | ||
842 | } | ||
843 | |||
844 | /* Initialize child device monitoring voltage status. */ | ||
845 | static void envctrl_init_voltage_status(struct i2c_child_t *pchild) | ||
846 | { | ||
847 | int i; | ||
848 | |||
849 | /* Go through all channels and set up the mask. */ | ||
850 | for (i = 0; i < pchild->total_chnls; i++) | ||
851 | pchild->voltage_mask |= chnls_mask[(pchild->chnl_array[i]).chnl_no]; | ||
852 | |||
853 | /* We only need to know if this child has voltage status monitored. | ||
854 | * We don't care which channels since we have the mask already. | ||
855 | */ | ||
856 | pchild->mon_type[0] = ENVCTRL_VOLTAGESTAT_MON; | ||
857 | } | ||
858 | |||
859 | /* Function Description: Initialize i2c child device. | ||
860 | * Return: None. | ||
861 | */ | ||
862 | static void envctrl_init_i2c_child(struct linux_ebus_child *edev_child, | ||
863 | struct i2c_child_t *pchild) | ||
864 | { | ||
865 | int node, len, i, tbls_size = 0; | ||
866 | |||
867 | node = edev_child->prom_node; | ||
868 | |||
869 | /* Get device address. */ | ||
870 | len = prom_getproperty(node, "reg", | ||
871 | (char *) &(pchild->addr), | ||
872 | sizeof(pchild->addr)); | ||
873 | |||
874 | /* Get tables property. Read firmware temperature tables. */ | ||
875 | len = prom_getproperty(node, "translation", | ||
876 | (char *) pchild->tblprop_array, | ||
877 | (PCF8584_MAX_CHANNELS * | ||
878 | sizeof(struct pcf8584_tblprop))); | ||
879 | if (len > 0) { | ||
880 | pchild->total_tbls = len / sizeof(struct pcf8584_tblprop); | ||
881 | for (i = 0; i < pchild->total_tbls; i++) { | ||
882 | if ((pchild->tblprop_array[i].size + pchild->tblprop_array[i].offset) > tbls_size) { | ||
883 | tbls_size = pchild->tblprop_array[i].size + pchild->tblprop_array[i].offset; | ||
884 | } | ||
885 | } | ||
886 | |||
887 | pchild->tables = kmalloc(tbls_size, GFP_KERNEL); | ||
888 | if (pchild->tables == NULL){ | ||
889 | printk("envctrl: Failed to allocate table.\n"); | ||
890 | return; | ||
891 | } | ||
892 | len = prom_getproperty(node, "tables", | ||
893 | (char *) pchild->tables, tbls_size); | ||
894 | if (len <= 0) { | ||
895 | printk("envctrl: Failed to get table.\n"); | ||
896 | return; | ||
897 | } | ||
898 | } | ||
899 | |||
900 | /* SPARCengine ASM Reference Manual (ref. SMI doc 805-7581-04) | ||
901 | * sections 2.5, 3.5, 4.5 state node 0x70 for CP1400/1500 is | ||
902 | * "For Factory Use Only." | ||
903 | * | ||
904 | * We ignore the node on these platforms by assigning the | ||
905 | * 'NULL' monitor type. | ||
906 | */ | ||
907 | if (ENVCTRL_CPCI_IGNORED_NODE == pchild->addr) { | ||
908 | int len; | ||
909 | char prop[56]; | ||
910 | |||
911 | len = prom_getproperty(prom_root_node, "name", prop, sizeof(prop)); | ||
912 | if (0 < len && (0 == strncmp(prop, "SUNW,UltraSPARC-IIi-cEngine", len))) | ||
913 | { | ||
914 | for (len = 0; len < PCF8584_MAX_CHANNELS; ++len) { | ||
915 | pchild->mon_type[len] = ENVCTRL_NOMON; | ||
916 | } | ||
917 | return; | ||
918 | } | ||
919 | } | ||
920 | |||
921 | /* Get the monitor channels. */ | ||
922 | len = prom_getproperty(node, "channels-in-use", | ||
923 | (char *) pchild->chnl_array, | ||
924 | (PCF8584_MAX_CHANNELS * | ||
925 | sizeof(struct pcf8584_channel))); | ||
926 | pchild->total_chnls = len / sizeof(struct pcf8584_channel); | ||
927 | |||
928 | for (i = 0; i < pchild->total_chnls; i++) { | ||
929 | switch (pchild->chnl_array[i].type) { | ||
930 | case PCF8584_TEMP_TYPE: | ||
931 | envctrl_init_adc(pchild, node); | ||
932 | break; | ||
933 | |||
934 | case PCF8584_GLOBALADDR_TYPE: | ||
935 | envctrl_init_globaladdr(pchild); | ||
936 | i = pchild->total_chnls; | ||
937 | break; | ||
938 | |||
939 | case PCF8584_FANSTAT_TYPE: | ||
940 | envctrl_init_fanstat(pchild); | ||
941 | i = pchild->total_chnls; | ||
942 | break; | ||
943 | |||
944 | case PCF8584_VOLTAGE_TYPE: | ||
945 | if (pchild->i2ctype == I2C_ADC) { | ||
946 | envctrl_init_adc(pchild,node); | ||
947 | } else { | ||
948 | envctrl_init_voltage_status(pchild); | ||
949 | } | ||
950 | i = pchild->total_chnls; | ||
951 | break; | ||
952 | |||
953 | default: | ||
954 | break; | ||
955 | }; | ||
956 | } | ||
957 | } | ||
958 | |||
959 | /* Function Description: Search the child device list for a device. | ||
960 | * Return : The i2c child if found. NULL otherwise. | ||
961 | */ | ||
962 | static struct i2c_child_t *envctrl_get_i2c_child(unsigned char mon_type) | ||
963 | { | ||
964 | int i, j; | ||
965 | |||
966 | for (i = 0; i < ENVCTRL_MAX_CPU*2; i++) { | ||
967 | for (j = 0; j < PCF8584_MAX_CHANNELS; j++) { | ||
968 | if (i2c_childlist[i].mon_type[j] == mon_type) { | ||
969 | return (struct i2c_child_t *)(&(i2c_childlist[i])); | ||
970 | } | ||
971 | } | ||
972 | } | ||
973 | return NULL; | ||
974 | } | ||
975 | |||
976 | static void envctrl_do_shutdown(void) | ||
977 | { | ||
978 | static int inprog = 0; | ||
979 | static char *envp[] = { | ||
980 | "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; | ||
981 | char *argv[] = { | ||
982 | "/sbin/shutdown", "-h", "now", NULL }; | ||
983 | |||
984 | if (inprog != 0) | ||
985 | return; | ||
986 | |||
987 | inprog = 1; | ||
988 | printk(KERN_CRIT "kenvctrld: WARNING: Shutting down the system now.\n"); | ||
989 | if (0 > execve("/sbin/shutdown", argv, envp)) { | ||
990 | printk(KERN_CRIT "kenvctrld: WARNING: system shutdown failed!\n"); | ||
991 | inprog = 0; /* unlikely to succeed, but we could try again */ | ||
992 | } | ||
993 | } | ||
994 | |||
995 | static struct task_struct *kenvctrld_task; | ||
996 | |||
997 | static int kenvctrld(void *__unused) | ||
998 | { | ||
999 | int poll_interval; | ||
1000 | int whichcpu; | ||
1001 | char tempbuf[10]; | ||
1002 | struct i2c_child_t *cputemp; | ||
1003 | |||
1004 | if (NULL == (cputemp = envctrl_get_i2c_child(ENVCTRL_CPUTEMP_MON))) { | ||
1005 | printk(KERN_ERR | ||
1006 | "envctrl: kenvctrld unable to monitor CPU temp-- exiting\n"); | ||
1007 | return -ENODEV; | ||
1008 | } | ||
1009 | |||
1010 | poll_interval = 5 * HZ; /* TODO env_mon_interval */ | ||
1011 | |||
1012 | daemonize("kenvctrld"); | ||
1013 | allow_signal(SIGKILL); | ||
1014 | |||
1015 | kenvctrld_task = current; | ||
1016 | |||
1017 | printk(KERN_INFO "envctrl: %s starting...\n", current->comm); | ||
1018 | for (;;) { | ||
1019 | current->state = TASK_INTERRUPTIBLE; | ||
1020 | schedule_timeout(poll_interval); | ||
1021 | |||
1022 | if(signal_pending(current)) | ||
1023 | break; | ||
1024 | |||
1025 | for (whichcpu = 0; whichcpu < ENVCTRL_MAX_CPU; ++whichcpu) { | ||
1026 | if (0 < envctrl_read_cpu_info(whichcpu, cputemp, | ||
1027 | ENVCTRL_CPUTEMP_MON, | ||
1028 | tempbuf)) { | ||
1029 | if (tempbuf[0] >= shutdown_temperature) { | ||
1030 | printk(KERN_CRIT | ||
1031 | "%s: WARNING: CPU%i temperature %i C meets or exceeds "\ | ||
1032 | "shutdown threshold %i C\n", | ||
1033 | current->comm, whichcpu, | ||
1034 | tempbuf[0], shutdown_temperature); | ||
1035 | envctrl_do_shutdown(); | ||
1036 | } | ||
1037 | } | ||
1038 | } | ||
1039 | } | ||
1040 | printk(KERN_INFO "envctrl: %s exiting...\n", current->comm); | ||
1041 | return 0; | ||
1042 | } | ||
1043 | |||
1044 | static int __init envctrl_init(void) | ||
1045 | { | ||
1046 | #ifdef CONFIG_PCI | ||
1047 | struct linux_ebus *ebus = NULL; | ||
1048 | struct linux_ebus_device *edev = NULL; | ||
1049 | struct linux_ebus_child *edev_child = NULL; | ||
1050 | int err, i = 0; | ||
1051 | |||
1052 | for_each_ebus(ebus) { | ||
1053 | for_each_ebusdev(edev, ebus) { | ||
1054 | if (!strcmp(edev->prom_name, "bbc")) { | ||
1055 | /* If we find a boot-bus controller node, | ||
1056 | * then this envctrl driver is not for us. | ||
1057 | */ | ||
1058 | return -ENODEV; | ||
1059 | } | ||
1060 | } | ||
1061 | } | ||
1062 | |||
1063 | /* Traverse through ebus and ebus device list for i2c device and | ||
1064 | * adc and gpio nodes. | ||
1065 | */ | ||
1066 | for_each_ebus(ebus) { | ||
1067 | for_each_ebusdev(edev, ebus) { | ||
1068 | if (!strcmp(edev->prom_name, "i2c")) { | ||
1069 | i2c = ioremap(edev->resource[0].start, 0x2); | ||
1070 | for_each_edevchild(edev, edev_child) { | ||
1071 | if (!strcmp("gpio", edev_child->prom_name)) { | ||
1072 | i2c_childlist[i].i2ctype = I2C_GPIO; | ||
1073 | envctrl_init_i2c_child(edev_child, &(i2c_childlist[i++])); | ||
1074 | } | ||
1075 | if (!strcmp("adc", edev_child->prom_name)) { | ||
1076 | i2c_childlist[i].i2ctype = I2C_ADC; | ||
1077 | envctrl_init_i2c_child(edev_child, &(i2c_childlist[i++])); | ||
1078 | } | ||
1079 | } | ||
1080 | goto done; | ||
1081 | } | ||
1082 | } | ||
1083 | } | ||
1084 | |||
1085 | done: | ||
1086 | if (!edev) { | ||
1087 | printk("envctrl: I2C device not found.\n"); | ||
1088 | return -ENODEV; | ||
1089 | } | ||
1090 | |||
1091 | /* Set device address. */ | ||
1092 | writeb(CONTROL_PIN, i2c + PCF8584_CSR); | ||
1093 | writeb(PCF8584_ADDRESS, i2c + PCF8584_DATA); | ||
1094 | |||
1095 | /* Set system clock and SCL frequencies. */ | ||
1096 | writeb(CONTROL_PIN | CONTROL_ES1, i2c + PCF8584_CSR); | ||
1097 | writeb(CLK_4_43 | BUS_CLK_90, i2c + PCF8584_DATA); | ||
1098 | |||
1099 | /* Enable serial interface. */ | ||
1100 | writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_ACK, i2c + PCF8584_CSR); | ||
1101 | udelay(200); | ||
1102 | |||
1103 | /* Register the device as a minor miscellaneous device. */ | ||
1104 | err = misc_register(&envctrl_dev); | ||
1105 | if (err) { | ||
1106 | printk("envctrl: Unable to get misc minor %d\n", | ||
1107 | envctrl_dev.minor); | ||
1108 | goto out_iounmap; | ||
1109 | } | ||
1110 | |||
1111 | /* Note above traversal routine post-incremented 'i' to accommodate | ||
1112 | * a next child device, so we decrement before reverse-traversal of | ||
1113 | * child devices. | ||
1114 | */ | ||
1115 | printk("envctrl: initialized "); | ||
1116 | for (--i; i >= 0; --i) { | ||
1117 | printk("[%s 0x%lx]%s", | ||
1118 | (I2C_ADC == i2c_childlist[i].i2ctype) ? ("adc") : | ||
1119 | ((I2C_GPIO == i2c_childlist[i].i2ctype) ? ("gpio") : ("unknown")), | ||
1120 | i2c_childlist[i].addr, (0 == i) ? ("\n") : (" ")); | ||
1121 | } | ||
1122 | |||
1123 | err = kernel_thread(kenvctrld, NULL, CLONE_FS | CLONE_FILES); | ||
1124 | if (err < 0) | ||
1125 | goto out_deregister; | ||
1126 | |||
1127 | return 0; | ||
1128 | |||
1129 | out_deregister: | ||
1130 | misc_deregister(&envctrl_dev); | ||
1131 | out_iounmap: | ||
1132 | iounmap(i2c); | ||
1133 | for (i = 0; i < ENVCTRL_MAX_CPU * 2; i++) { | ||
1134 | if (i2c_childlist[i].tables) | ||
1135 | kfree(i2c_childlist[i].tables); | ||
1136 | } | ||
1137 | return err; | ||
1138 | #else | ||
1139 | return -ENODEV; | ||
1140 | #endif | ||
1141 | } | ||
1142 | |||
1143 | static void __exit envctrl_cleanup(void) | ||
1144 | { | ||
1145 | int i; | ||
1146 | |||
1147 | if (NULL != kenvctrld_task) { | ||
1148 | force_sig(SIGKILL, kenvctrld_task); | ||
1149 | for (;;) { | ||
1150 | struct task_struct *p; | ||
1151 | int found = 0; | ||
1152 | |||
1153 | read_lock(&tasklist_lock); | ||
1154 | for_each_process(p) { | ||
1155 | if (p == kenvctrld_task) { | ||
1156 | found = 1; | ||
1157 | break; | ||
1158 | } | ||
1159 | } | ||
1160 | read_unlock(&tasklist_lock); | ||
1161 | |||
1162 | if (!found) | ||
1163 | break; | ||
1164 | |||
1165 | msleep(1000); | ||
1166 | } | ||
1167 | kenvctrld_task = NULL; | ||
1168 | } | ||
1169 | |||
1170 | iounmap(i2c); | ||
1171 | misc_deregister(&envctrl_dev); | ||
1172 | |||
1173 | for (i = 0; i < ENVCTRL_MAX_CPU * 2; i++) { | ||
1174 | if (i2c_childlist[i].tables) | ||
1175 | kfree(i2c_childlist[i].tables); | ||
1176 | } | ||
1177 | } | ||
1178 | |||
1179 | module_init(envctrl_init); | ||
1180 | module_exit(envctrl_cleanup); | ||
1181 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c new file mode 100644 index 00000000000..6bdd768b731 --- /dev/null +++ b/drivers/sbus/char/flash.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* $Id: flash.c,v 1.25 2001/12/21 04:56:16 davem Exp $ | ||
2 | * flash.c: Allow mmap access to the OBP Flash, for OBP updates. | ||
3 | * | ||
4 | * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) | ||
5 | */ | ||
6 | |||
7 | #include <linux/config.h> | ||
8 | #include <linux/module.h> | ||
9 | #include <linux/types.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/miscdevice.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/fcntl.h> | ||
14 | #include <linux/poll.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/smp_lock.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | |||
19 | #include <asm/system.h> | ||
20 | #include <asm/uaccess.h> | ||
21 | #include <asm/pgtable.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/sbus.h> | ||
24 | #include <asm/ebus.h> | ||
25 | #include <asm/upa.h> | ||
26 | |||
27 | static DEFINE_SPINLOCK(flash_lock); | ||
28 | static struct { | ||
29 | unsigned long read_base; /* Physical read address */ | ||
30 | unsigned long write_base; /* Physical write address */ | ||
31 | unsigned long read_size; /* Size of read area */ | ||
32 | unsigned long write_size; /* Size of write area */ | ||
33 | unsigned long busy; /* In use? */ | ||
34 | } flash; | ||
35 | |||
36 | #define FLASH_MINOR 152 | ||
37 | |||
38 | static int | ||
39 | flash_mmap(struct file *file, struct vm_area_struct *vma) | ||
40 | { | ||
41 | unsigned long addr; | ||
42 | unsigned long size; | ||
43 | |||
44 | spin_lock(&flash_lock); | ||
45 | if (flash.read_base == flash.write_base) { | ||
46 | addr = flash.read_base; | ||
47 | size = flash.read_size; | ||
48 | } else { | ||
49 | if ((vma->vm_flags & VM_READ) && | ||
50 | (vma->vm_flags & VM_WRITE)) { | ||
51 | spin_unlock(&flash_lock); | ||
52 | return -EINVAL; | ||
53 | } | ||
54 | if (vma->vm_flags & VM_READ) { | ||
55 | addr = flash.read_base; | ||
56 | size = flash.read_size; | ||
57 | } else if (vma->vm_flags & VM_WRITE) { | ||
58 | addr = flash.write_base; | ||
59 | size = flash.write_size; | ||
60 | } else { | ||
61 | spin_unlock(&flash_lock); | ||
62 | return -ENXIO; | ||
63 | } | ||
64 | } | ||
65 | spin_unlock(&flash_lock); | ||
66 | |||
67 | if ((vma->vm_pgoff << PAGE_SHIFT) > size) | ||
68 | return -ENXIO; | ||
69 | addr = vma->vm_pgoff + (addr >> PAGE_SHIFT); | ||
70 | |||
71 | if (vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT)) > size) | ||
72 | size = vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT)); | ||
73 | |||
74 | pgprot_val(vma->vm_page_prot) &= ~(_PAGE_CACHE); | ||
75 | pgprot_val(vma->vm_page_prot) |= _PAGE_E; | ||
76 | vma->vm_flags |= (VM_SHM | VM_LOCKED); | ||
77 | |||
78 | if (io_remap_pfn_range(vma, vma->vm_start, addr, size, vma->vm_page_prot)) | ||
79 | return -EAGAIN; | ||
80 | |||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | static long long | ||
85 | flash_llseek(struct file *file, long long offset, int origin) | ||
86 | { | ||
87 | lock_kernel(); | ||
88 | switch (origin) { | ||
89 | case 0: | ||
90 | file->f_pos = offset; | ||
91 | break; | ||
92 | case 1: | ||
93 | file->f_pos += offset; | ||
94 | if (file->f_pos > flash.read_size) | ||
95 | file->f_pos = flash.read_size; | ||
96 | break; | ||
97 | case 2: | ||
98 | file->f_pos = flash.read_size; | ||
99 | break; | ||
100 | default: | ||
101 | unlock_kernel(); | ||
102 | return -EINVAL; | ||
103 | } | ||
104 | unlock_kernel(); | ||
105 | return file->f_pos; | ||
106 | } | ||
107 | |||
108 | static ssize_t | ||
109 | flash_read(struct file * file, char __user * buf, | ||
110 | size_t count, loff_t *ppos) | ||
111 | { | ||
112 | unsigned long p = file->f_pos; | ||
113 | int i; | ||
114 | |||
115 | if (count > flash.read_size - p) | ||
116 | count = flash.read_size - p; | ||
117 | |||
118 | for (i = 0; i < count; i++) { | ||
119 | u8 data = upa_readb(flash.read_base + p + i); | ||
120 | if (put_user(data, buf)) | ||
121 | return -EFAULT; | ||
122 | buf++; | ||
123 | } | ||
124 | |||
125 | file->f_pos += count; | ||
126 | return count; | ||
127 | } | ||
128 | |||
129 | static int | ||
130 | flash_open(struct inode *inode, struct file *file) | ||
131 | { | ||
132 | if (test_and_set_bit(0, (void *)&flash.busy) != 0) | ||
133 | return -EBUSY; | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static int | ||
139 | flash_release(struct inode *inode, struct file *file) | ||
140 | { | ||
141 | spin_lock(&flash_lock); | ||
142 | flash.busy = 0; | ||
143 | spin_unlock(&flash_lock); | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | static struct file_operations flash_fops = { | ||
149 | /* no write to the Flash, use mmap | ||
150 | * and play flash dependent tricks. | ||
151 | */ | ||
152 | .owner = THIS_MODULE, | ||
153 | .llseek = flash_llseek, | ||
154 | .read = flash_read, | ||
155 | .mmap = flash_mmap, | ||
156 | .open = flash_open, | ||
157 | .release = flash_release, | ||
158 | }; | ||
159 | |||
160 | static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops }; | ||
161 | |||
162 | static int __init flash_init(void) | ||
163 | { | ||
164 | struct sbus_bus *sbus; | ||
165 | struct sbus_dev *sdev = NULL; | ||
166 | #ifdef CONFIG_PCI | ||
167 | struct linux_ebus *ebus; | ||
168 | struct linux_ebus_device *edev = NULL; | ||
169 | struct linux_prom_registers regs[2]; | ||
170 | int len, nregs; | ||
171 | #endif | ||
172 | int err; | ||
173 | |||
174 | for_all_sbusdev(sdev, sbus) { | ||
175 | if (!strcmp(sdev->prom_name, "flashprom")) { | ||
176 | if (sdev->reg_addrs[0].phys_addr == sdev->reg_addrs[1].phys_addr) { | ||
177 | flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) | | ||
178 | (((unsigned long)sdev->reg_addrs[0].which_io)<<32UL); | ||
179 | flash.read_size = sdev->reg_addrs[0].reg_size; | ||
180 | flash.write_base = flash.read_base; | ||
181 | flash.write_size = flash.read_size; | ||
182 | } else { | ||
183 | flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) | | ||
184 | (((unsigned long)sdev->reg_addrs[0].which_io)<<32UL); | ||
185 | flash.read_size = sdev->reg_addrs[0].reg_size; | ||
186 | flash.write_base = ((unsigned long)sdev->reg_addrs[1].phys_addr) | | ||
187 | (((unsigned long)sdev->reg_addrs[1].which_io)<<32UL); | ||
188 | flash.write_size = sdev->reg_addrs[1].reg_size; | ||
189 | } | ||
190 | flash.busy = 0; | ||
191 | break; | ||
192 | } | ||
193 | } | ||
194 | if (!sdev) { | ||
195 | #ifdef CONFIG_PCI | ||
196 | for_each_ebus(ebus) { | ||
197 | for_each_ebusdev(edev, ebus) { | ||
198 | if (!strcmp(edev->prom_name, "flashprom")) | ||
199 | goto ebus_done; | ||
200 | } | ||
201 | } | ||
202 | ebus_done: | ||
203 | if (!edev) | ||
204 | return -ENODEV; | ||
205 | |||
206 | len = prom_getproperty(edev->prom_node, "reg", (void *)regs, sizeof(regs)); | ||
207 | if ((len % sizeof(regs[0])) != 0) { | ||
208 | printk("flash: Strange reg property size %d\n", len); | ||
209 | return -ENODEV; | ||
210 | } | ||
211 | |||
212 | nregs = len / sizeof(regs[0]); | ||
213 | |||
214 | flash.read_base = edev->resource[0].start; | ||
215 | flash.read_size = regs[0].reg_size; | ||
216 | |||
217 | if (nregs == 1) { | ||
218 | flash.write_base = edev->resource[0].start; | ||
219 | flash.write_size = regs[0].reg_size; | ||
220 | } else if (nregs == 2) { | ||
221 | flash.write_base = edev->resource[1].start; | ||
222 | flash.write_size = regs[1].reg_size; | ||
223 | } else { | ||
224 | printk("flash: Strange number of regs %d\n", nregs); | ||
225 | return -ENODEV; | ||
226 | } | ||
227 | |||
228 | flash.busy = 0; | ||
229 | |||
230 | #else | ||
231 | return -ENODEV; | ||
232 | #endif | ||
233 | } | ||
234 | |||
235 | printk("OBP Flash: RD %lx[%lx] WR %lx[%lx]\n", | ||
236 | flash.read_base, flash.read_size, | ||
237 | flash.write_base, flash.write_size); | ||
238 | |||
239 | err = misc_register(&flash_dev); | ||
240 | if (err) { | ||
241 | printk(KERN_ERR "flash: unable to get misc minor\n"); | ||
242 | return err; | ||
243 | } | ||
244 | |||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | static void __exit flash_cleanup(void) | ||
249 | { | ||
250 | misc_deregister(&flash_dev); | ||
251 | } | ||
252 | |||
253 | module_init(flash_init); | ||
254 | module_exit(flash_cleanup); | ||
255 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c new file mode 100644 index 00000000000..c12c5046e2f --- /dev/null +++ b/drivers/sbus/char/jsflash.c | |||
@@ -0,0 +1,627 @@ | |||
1 | /* | ||
2 | * drivers/sbus/char/jsflash.c | ||
3 | * | ||
4 | * Copyright (C) 1991, 1992 Linus Torvalds (drivers/char/mem.c) | ||
5 | * Copyright (C) 1997 Eddie C. Dost (drivers/sbus/char/flash.c) | ||
6 | * Copyright (C) 1997-2000 Pavel Machek <pavel@ucw.cz> (drivers/block/nbd.c) | ||
7 | * Copyright (C) 1999-2000 Pete Zaitcev | ||
8 | * | ||
9 | * This driver is used to program OS into a Flash SIMM on | ||
10 | * Krups and Espresso platforms. | ||
11 | * | ||
12 | * TODO: do not allow erase/programming if file systems are mounted. | ||
13 | * TODO: Erase/program both banks of a 8MB SIMM. | ||
14 | * | ||
15 | * It is anticipated that programming an OS Flash will be a routine | ||
16 | * procedure. In the same time it is exeedingly dangerous because | ||
17 | * a user can program its OBP flash with OS image and effectively | ||
18 | * kill the machine. | ||
19 | * | ||
20 | * This driver uses an interface different from Eddie's flash.c | ||
21 | * as a silly safeguard. | ||
22 | * | ||
23 | * XXX The flash.c manipulates page caching characteristics in a certain | ||
24 | * dubious way; also it assumes that remap_pfn_range() can remap | ||
25 | * PCI bus locations, which may be false. ioremap() must be used | ||
26 | * instead. We should discuss this. | ||
27 | */ | ||
28 | |||
29 | #include <linux/module.h> | ||
30 | #include <linux/types.h> | ||
31 | #include <linux/errno.h> | ||
32 | #include <linux/miscdevice.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/fcntl.h> | ||
35 | #include <linux/poll.h> | ||
36 | #include <linux/init.h> | ||
37 | #include <linux/string.h> | ||
38 | #include <linux/smp_lock.h> | ||
39 | #include <linux/genhd.h> | ||
40 | #include <linux/blkdev.h> | ||
41 | |||
42 | #define MAJOR_NR JSFD_MAJOR | ||
43 | |||
44 | #include <asm/uaccess.h> | ||
45 | #include <asm/pgtable.h> | ||
46 | #include <asm/io.h> | ||
47 | #include <asm/pcic.h> | ||
48 | #include <asm/oplib.h> | ||
49 | |||
50 | #include <asm/jsflash.h> /* ioctl arguments. <linux/> ?? */ | ||
51 | #define JSFIDSZ (sizeof(struct jsflash_ident_arg)) | ||
52 | #define JSFPRGSZ (sizeof(struct jsflash_program_arg)) | ||
53 | |||
54 | /* | ||
55 | * Our device numbers have no business in system headers. | ||
56 | * The only thing a user knows is the device name /dev/jsflash. | ||
57 | * | ||
58 | * Block devices are laid out like this: | ||
59 | * minor+0 - Bootstrap, for 8MB SIMM 0x20400000[0x800000] | ||
60 | * minor+1 - Filesystem to mount, normally 0x20400400[0x7ffc00] | ||
61 | * minor+2 - Whole flash area for any case... 0x20000000[0x01000000] | ||
62 | * Total 3 minors per flash device. | ||
63 | * | ||
64 | * It is easier to have static size vectors, so we define | ||
65 | * a total minor range JSF_MAX, which must cover all minors. | ||
66 | */ | ||
67 | /* character device */ | ||
68 | #define JSF_MINOR 178 /* 178 is registered with hpa */ | ||
69 | /* block device */ | ||
70 | #define JSF_MAX 3 /* 3 minors wasted total so far. */ | ||
71 | #define JSF_NPART 3 /* 3 minors per flash device */ | ||
72 | #define JSF_PART_BITS 2 /* 2 bits of minors to cover JSF_NPART */ | ||
73 | #define JSF_PART_MASK 0x3 /* 2 bits mask */ | ||
74 | |||
75 | /* | ||
76 | * Access functions. | ||
77 | * We could ioremap(), but it's easier this way. | ||
78 | */ | ||
79 | static unsigned int jsf_inl(unsigned long addr) | ||
80 | { | ||
81 | unsigned long retval; | ||
82 | |||
83 | __asm__ __volatile__("lda [%1] %2, %0\n\t" : | ||
84 | "=r" (retval) : | ||
85 | "r" (addr), "i" (ASI_M_BYPASS)); | ||
86 | return retval; | ||
87 | } | ||
88 | |||
89 | static void jsf_outl(unsigned long addr, __u32 data) | ||
90 | { | ||
91 | |||
92 | __asm__ __volatile__("sta %0, [%1] %2\n\t" : : | ||
93 | "r" (data), "r" (addr), "i" (ASI_M_BYPASS) : | ||
94 | "memory"); | ||
95 | } | ||
96 | |||
97 | /* | ||
98 | * soft carrier | ||
99 | */ | ||
100 | |||
101 | struct jsfd_part { | ||
102 | unsigned long dbase; | ||
103 | unsigned long dsize; | ||
104 | }; | ||
105 | |||
106 | struct jsflash { | ||
107 | unsigned long base; | ||
108 | unsigned long size; | ||
109 | unsigned long busy; /* In use? */ | ||
110 | struct jsflash_ident_arg id; | ||
111 | /* int mbase; */ /* Minor base, typically zero */ | ||
112 | struct jsfd_part dv[JSF_NPART]; | ||
113 | }; | ||
114 | |||
115 | /* | ||
116 | * We do not map normal memory or obio as a safety precaution. | ||
117 | * But offsets are real, for ease of userland programming. | ||
118 | */ | ||
119 | #define JSF_BASE_TOP 0x30000000 | ||
120 | #define JSF_BASE_ALL 0x20000000 | ||
121 | |||
122 | #define JSF_BASE_JK 0x20400000 | ||
123 | |||
124 | /* | ||
125 | */ | ||
126 | static struct gendisk *jsfd_disk[JSF_MAX]; | ||
127 | |||
128 | /* | ||
129 | * Let's pretend we may have several of these... | ||
130 | */ | ||
131 | static struct jsflash jsf0; | ||
132 | |||
133 | /* | ||
134 | * Wait for AMD to finish its embedded algorithm. | ||
135 | * We use the Toggle bit DQ6 (0x40) because it does not | ||
136 | * depend on the data value as /DATA bit DQ7 does. | ||
137 | * | ||
138 | * XXX Do we need any timeout here? So far it never hanged, beware broken hw. | ||
139 | */ | ||
140 | static void jsf_wait(unsigned long p) { | ||
141 | unsigned int x1, x2; | ||
142 | |||
143 | for (;;) { | ||
144 | x1 = jsf_inl(p); | ||
145 | x2 = jsf_inl(p); | ||
146 | if ((x1 & 0x40404040) == (x2 & 0x40404040)) return; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | /* | ||
151 | * Programming will only work if Flash is clean, | ||
152 | * we leave it to the programmer application. | ||
153 | * | ||
154 | * AMD must be programmed one byte at a time; | ||
155 | * thus, Simple Tech SIMM must be written 4 bytes at a time. | ||
156 | * | ||
157 | * Write waits for the chip to become ready after the write | ||
158 | * was finished. This is done so that application would read | ||
159 | * consistent data after the write is done. | ||
160 | */ | ||
161 | static void jsf_write4(unsigned long fa, u32 data) { | ||
162 | |||
163 | jsf_outl(fa, 0xAAAAAAAA); /* Unlock 1 Write 1 */ | ||
164 | jsf_outl(fa, 0x55555555); /* Unlock 1 Write 2 */ | ||
165 | jsf_outl(fa, 0xA0A0A0A0); /* Byte Program */ | ||
166 | jsf_outl(fa, data); | ||
167 | |||
168 | jsf_wait(fa); | ||
169 | } | ||
170 | |||
171 | /* | ||
172 | */ | ||
173 | static void jsfd_read(char *buf, unsigned long p, size_t togo) { | ||
174 | union byte4 { | ||
175 | char s[4]; | ||
176 | unsigned int n; | ||
177 | } b; | ||
178 | |||
179 | while (togo >= 4) { | ||
180 | togo -= 4; | ||
181 | b.n = jsf_inl(p); | ||
182 | memcpy(buf, b.s, 4); | ||
183 | p += 4; | ||
184 | buf += 4; | ||
185 | } | ||
186 | } | ||
187 | |||
188 | static void jsfd_do_request(request_queue_t *q) | ||
189 | { | ||
190 | struct request *req; | ||
191 | |||
192 | while ((req = elv_next_request(q)) != NULL) { | ||
193 | struct jsfd_part *jdp = req->rq_disk->private_data; | ||
194 | unsigned long offset = req->sector << 9; | ||
195 | size_t len = req->current_nr_sectors << 9; | ||
196 | |||
197 | if ((offset + len) > jdp->dsize) { | ||
198 | end_request(req, 0); | ||
199 | continue; | ||
200 | } | ||
201 | |||
202 | if (rq_data_dir(req) != READ) { | ||
203 | printk(KERN_ERR "jsfd: write\n"); | ||
204 | end_request(req, 0); | ||
205 | continue; | ||
206 | } | ||
207 | |||
208 | if ((jdp->dbase & 0xff000000) != 0x20000000) { | ||
209 | printk(KERN_ERR "jsfd: bad base %x\n", (int)jdp->dbase); | ||
210 | end_request(req, 0); | ||
211 | continue; | ||
212 | } | ||
213 | |||
214 | jsfd_read(req->buffer, jdp->dbase + offset, len); | ||
215 | |||
216 | end_request(req, 1); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * The memory devices use the full 32/64 bits of the offset, and so we cannot | ||
222 | * check against negative addresses: they are ok. The return value is weird, | ||
223 | * though, in that case (0). | ||
224 | * | ||
225 | * also note that seeking relative to the "end of file" isn't supported: | ||
226 | * it has no meaning, so it returns -EINVAL. | ||
227 | */ | ||
228 | static loff_t jsf_lseek(struct file * file, loff_t offset, int orig) | ||
229 | { | ||
230 | loff_t ret; | ||
231 | |||
232 | lock_kernel(); | ||
233 | switch (orig) { | ||
234 | case 0: | ||
235 | file->f_pos = offset; | ||
236 | ret = file->f_pos; | ||
237 | break; | ||
238 | case 1: | ||
239 | file->f_pos += offset; | ||
240 | ret = file->f_pos; | ||
241 | break; | ||
242 | default: | ||
243 | ret = -EINVAL; | ||
244 | } | ||
245 | unlock_kernel(); | ||
246 | return ret; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * OS SIMM Cannot be read in other size but a 32bits word. | ||
251 | */ | ||
252 | static ssize_t jsf_read(struct file * file, char * buf, | ||
253 | size_t togo, loff_t *ppos) | ||
254 | { | ||
255 | unsigned long p = *ppos; | ||
256 | char *tmp = buf; | ||
257 | |||
258 | union byte4 { | ||
259 | char s[4]; | ||
260 | unsigned int n; | ||
261 | } b; | ||
262 | |||
263 | if (p < JSF_BASE_ALL || p >= JSF_BASE_TOP) { | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | if ((p + togo) < p /* wrap */ | ||
268 | || (p + togo) >= JSF_BASE_TOP) { | ||
269 | togo = JSF_BASE_TOP - p; | ||
270 | } | ||
271 | |||
272 | if (p < JSF_BASE_ALL && togo != 0) { | ||
273 | #if 0 /* __bzero XXX */ | ||
274 | size_t x = JSF_BASE_ALL - p; | ||
275 | if (x > togo) x = togo; | ||
276 | clear_user(tmp, x); | ||
277 | tmp += x; | ||
278 | p += x; | ||
279 | togo -= x; | ||
280 | #else | ||
281 | /* | ||
282 | * Implementation of clear_user() calls __bzero | ||
283 | * without regard to modversions, | ||
284 | * so we cannot build a module. | ||
285 | */ | ||
286 | return 0; | ||
287 | #endif | ||
288 | } | ||
289 | |||
290 | while (togo >= 4) { | ||
291 | togo -= 4; | ||
292 | b.n = jsf_inl(p); | ||
293 | if (copy_to_user(tmp, b.s, 4)) | ||
294 | return -EFAULT; | ||
295 | tmp += 4; | ||
296 | p += 4; | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * XXX Small togo may remain if 1 byte is ordered. | ||
301 | * It would be nice if we did a word size read and unpacked it. | ||
302 | */ | ||
303 | |||
304 | *ppos = p; | ||
305 | return tmp-buf; | ||
306 | } | ||
307 | |||
308 | static ssize_t jsf_write(struct file * file, const char * buf, | ||
309 | size_t count, loff_t *ppos) | ||
310 | { | ||
311 | return -ENOSPC; | ||
312 | } | ||
313 | |||
314 | /* | ||
315 | */ | ||
316 | static int jsf_ioctl_erase(unsigned long arg) | ||
317 | { | ||
318 | unsigned long p; | ||
319 | |||
320 | /* p = jsf0.base; hits wrong bank */ | ||
321 | p = 0x20400000; | ||
322 | |||
323 | jsf_outl(p, 0xAAAAAAAA); /* Unlock 1 Write 1 */ | ||
324 | jsf_outl(p, 0x55555555); /* Unlock 1 Write 2 */ | ||
325 | jsf_outl(p, 0x80808080); /* Erase setup */ | ||
326 | jsf_outl(p, 0xAAAAAAAA); /* Unlock 2 Write 1 */ | ||
327 | jsf_outl(p, 0x55555555); /* Unlock 2 Write 2 */ | ||
328 | jsf_outl(p, 0x10101010); /* Chip erase */ | ||
329 | |||
330 | #if 0 | ||
331 | /* | ||
332 | * This code is ok, except that counter based timeout | ||
333 | * has no place in this world. Let's just drop timeouts... | ||
334 | */ | ||
335 | { | ||
336 | int i; | ||
337 | __u32 x; | ||
338 | for (i = 0; i < 1000000; i++) { | ||
339 | x = jsf_inl(p); | ||
340 | if ((x & 0x80808080) == 0x80808080) break; | ||
341 | } | ||
342 | if ((x & 0x80808080) != 0x80808080) { | ||
343 | printk("jsf0: erase timeout with 0x%08x\n", x); | ||
344 | } else { | ||
345 | printk("jsf0: erase done with 0x%08x\n", x); | ||
346 | } | ||
347 | } | ||
348 | #else | ||
349 | jsf_wait(p); | ||
350 | #endif | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | /* | ||
356 | * Program a block of flash. | ||
357 | * Very simple because we can do it byte by byte anyway. | ||
358 | */ | ||
359 | static int jsf_ioctl_program(unsigned long arg) | ||
360 | { | ||
361 | struct jsflash_program_arg abuf; | ||
362 | char *uptr; | ||
363 | unsigned long p; | ||
364 | unsigned int togo; | ||
365 | union { | ||
366 | unsigned int n; | ||
367 | char s[4]; | ||
368 | } b; | ||
369 | |||
370 | if (copy_from_user(&abuf, (char *)arg, JSFPRGSZ)) | ||
371 | return -EFAULT; | ||
372 | p = abuf.off; | ||
373 | togo = abuf.size; | ||
374 | if ((togo & 3) || (p & 3)) return -EINVAL; | ||
375 | |||
376 | uptr = (char *) (unsigned long) abuf.data; | ||
377 | while (togo != 0) { | ||
378 | togo -= 4; | ||
379 | if (copy_from_user(&b.s[0], uptr, 4)) | ||
380 | return -EFAULT; | ||
381 | jsf_write4(p, b.n); | ||
382 | p += 4; | ||
383 | uptr += 4; | ||
384 | } | ||
385 | |||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | static int jsf_ioctl(struct inode *inode, struct file *f, unsigned int cmd, | ||
390 | unsigned long arg) | ||
391 | { | ||
392 | int error = -ENOTTY; | ||
393 | |||
394 | if (!capable(CAP_SYS_ADMIN)) | ||
395 | return -EPERM; | ||
396 | switch (cmd) { | ||
397 | case JSFLASH_IDENT: | ||
398 | if (copy_to_user((void *)arg, &jsf0.id, JSFIDSZ)) | ||
399 | return -EFAULT; | ||
400 | break; | ||
401 | case JSFLASH_ERASE: | ||
402 | error = jsf_ioctl_erase(arg); | ||
403 | break; | ||
404 | case JSFLASH_PROGRAM: | ||
405 | error = jsf_ioctl_program(arg); | ||
406 | break; | ||
407 | } | ||
408 | |||
409 | return error; | ||
410 | } | ||
411 | |||
412 | static int jsf_mmap(struct file * file, struct vm_area_struct * vma) | ||
413 | { | ||
414 | return -ENXIO; | ||
415 | } | ||
416 | |||
417 | static int jsf_open(struct inode * inode, struct file * filp) | ||
418 | { | ||
419 | |||
420 | if (jsf0.base == 0) return -ENXIO; | ||
421 | if (test_and_set_bit(0, (void *)&jsf0.busy) != 0) | ||
422 | return -EBUSY; | ||
423 | |||
424 | return 0; /* XXX What security? */ | ||
425 | } | ||
426 | |||
427 | static int jsf_release(struct inode *inode, struct file *file) | ||
428 | { | ||
429 | jsf0.busy = 0; | ||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | static struct file_operations jsf_fops = { | ||
434 | .owner = THIS_MODULE, | ||
435 | .llseek = jsf_lseek, | ||
436 | .read = jsf_read, | ||
437 | .write = jsf_write, | ||
438 | .ioctl = jsf_ioctl, | ||
439 | .mmap = jsf_mmap, | ||
440 | .open = jsf_open, | ||
441 | .release = jsf_release, | ||
442 | }; | ||
443 | |||
444 | static struct miscdevice jsf_dev = { JSF_MINOR, "jsflash", &jsf_fops }; | ||
445 | |||
446 | static struct block_device_operations jsfd_fops = { | ||
447 | .owner = THIS_MODULE, | ||
448 | }; | ||
449 | |||
450 | static int jsflash_init(void) | ||
451 | { | ||
452 | int rc; | ||
453 | struct jsflash *jsf; | ||
454 | int node; | ||
455 | char banner[128]; | ||
456 | struct linux_prom_registers reg0; | ||
457 | |||
458 | node = prom_getchild(prom_root_node); | ||
459 | node = prom_searchsiblings(node, "flash-memory"); | ||
460 | if (node != 0 && node != -1) { | ||
461 | if (prom_getproperty(node, "reg", | ||
462 | (char *)®0, sizeof(reg0)) == -1) { | ||
463 | printk("jsflash: no \"reg\" property\n"); | ||
464 | return -ENXIO; | ||
465 | } | ||
466 | if (reg0.which_io != 0) { | ||
467 | printk("jsflash: bus number nonzero: 0x%x:%x\n", | ||
468 | reg0.which_io, reg0.phys_addr); | ||
469 | return -ENXIO; | ||
470 | } | ||
471 | /* | ||
472 | * Flash may be somewhere else, for instance on Ebus. | ||
473 | * So, don't do the following check for IIep flash space. | ||
474 | */ | ||
475 | #if 0 | ||
476 | if ((reg0.phys_addr >> 24) != 0x20) { | ||
477 | printk("jsflash: suspicious address: 0x%x:%x\n", | ||
478 | reg0.which_io, reg0.phys_addr); | ||
479 | return -ENXIO; | ||
480 | } | ||
481 | #endif | ||
482 | if ((int)reg0.reg_size <= 0) { | ||
483 | printk("jsflash: bad size 0x%x\n", (int)reg0.reg_size); | ||
484 | return -ENXIO; | ||
485 | } | ||
486 | } else { | ||
487 | /* XXX Remove this code once PROLL ID12 got widespread */ | ||
488 | printk("jsflash: no /flash-memory node, use PROLL >= 12\n"); | ||
489 | prom_getproperty(prom_root_node, "banner-name", banner, 128); | ||
490 | if (strcmp (banner, "JavaStation-NC") != 0 && | ||
491 | strcmp (banner, "JavaStation-E") != 0) { | ||
492 | return -ENXIO; | ||
493 | } | ||
494 | reg0.which_io = 0; | ||
495 | reg0.phys_addr = 0x20400000; | ||
496 | reg0.reg_size = 0x00800000; | ||
497 | } | ||
498 | |||
499 | /* Let us be really paranoid for modifications to probing code. */ | ||
500 | /* extern enum sparc_cpu sparc_cpu_model; */ /* in <asm/system.h> */ | ||
501 | if (sparc_cpu_model != sun4m) { | ||
502 | /* We must be on sun4m because we use MMU Bypass ASI. */ | ||
503 | return -ENXIO; | ||
504 | } | ||
505 | |||
506 | if (jsf0.base == 0) { | ||
507 | jsf = &jsf0; | ||
508 | |||
509 | jsf->base = reg0.phys_addr; | ||
510 | jsf->size = reg0.reg_size; | ||
511 | |||
512 | /* XXX Redo the userland interface. */ | ||
513 | jsf->id.off = JSF_BASE_ALL; | ||
514 | jsf->id.size = 0x01000000; /* 16M - all segments */ | ||
515 | strcpy(jsf->id.name, "Krups_all"); | ||
516 | |||
517 | jsf->dv[0].dbase = jsf->base; | ||
518 | jsf->dv[0].dsize = jsf->size; | ||
519 | jsf->dv[1].dbase = jsf->base + 1024; | ||
520 | jsf->dv[1].dsize = jsf->size - 1024; | ||
521 | jsf->dv[2].dbase = JSF_BASE_ALL; | ||
522 | jsf->dv[2].dsize = 0x01000000; | ||
523 | |||
524 | printk("Espresso Flash @0x%lx [%d MB]\n", jsf->base, | ||
525 | (int) (jsf->size / (1024*1024))); | ||
526 | } | ||
527 | |||
528 | if ((rc = misc_register(&jsf_dev)) != 0) { | ||
529 | printk(KERN_ERR "jsf: unable to get misc minor %d\n", | ||
530 | JSF_MINOR); | ||
531 | jsf0.base = 0; | ||
532 | return rc; | ||
533 | } | ||
534 | |||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | static struct request_queue *jsf_queue; | ||
539 | |||
540 | static int jsfd_init(void) | ||
541 | { | ||
542 | static DEFINE_SPINLOCK(lock); | ||
543 | struct jsflash *jsf; | ||
544 | struct jsfd_part *jdp; | ||
545 | int err; | ||
546 | int i; | ||
547 | |||
548 | if (jsf0.base == 0) | ||
549 | return -ENXIO; | ||
550 | |||
551 | err = -ENOMEM; | ||
552 | for (i = 0; i < JSF_MAX; i++) { | ||
553 | struct gendisk *disk = alloc_disk(1); | ||
554 | if (!disk) | ||
555 | goto out; | ||
556 | jsfd_disk[i] = disk; | ||
557 | } | ||
558 | |||
559 | if (register_blkdev(JSFD_MAJOR, "jsfd")) { | ||
560 | err = -EIO; | ||
561 | goto out; | ||
562 | } | ||
563 | |||
564 | jsf_queue = blk_init_queue(jsfd_do_request, &lock); | ||
565 | if (!jsf_queue) { | ||
566 | err = -ENOMEM; | ||
567 | unregister_blkdev(JSFD_MAJOR, "jsfd"); | ||
568 | goto out; | ||
569 | } | ||
570 | |||
571 | for (i = 0; i < JSF_MAX; i++) { | ||
572 | struct gendisk *disk = jsfd_disk[i]; | ||
573 | if ((i & JSF_PART_MASK) >= JSF_NPART) continue; | ||
574 | jsf = &jsf0; /* actually, &jsfv[i >> JSF_PART_BITS] */ | ||
575 | jdp = &jsf->dv[i&JSF_PART_MASK]; | ||
576 | |||
577 | disk->major = JSFD_MAJOR; | ||
578 | disk->first_minor = i; | ||
579 | sprintf(disk->disk_name, "jsfd%d", i); | ||
580 | disk->fops = &jsfd_fops; | ||
581 | set_capacity(disk, jdp->dsize >> 9); | ||
582 | disk->private_data = jdp; | ||
583 | disk->queue = jsf_queue; | ||
584 | add_disk(disk); | ||
585 | set_disk_ro(disk, 1); | ||
586 | } | ||
587 | return 0; | ||
588 | out: | ||
589 | while (i--) | ||
590 | put_disk(jsfd_disk[i]); | ||
591 | return err; | ||
592 | } | ||
593 | |||
594 | MODULE_LICENSE("GPL"); | ||
595 | |||
596 | static int __init jsflash_init_module(void) { | ||
597 | int rc; | ||
598 | |||
599 | if ((rc = jsflash_init()) == 0) { | ||
600 | jsfd_init(); | ||
601 | return 0; | ||
602 | } | ||
603 | return rc; | ||
604 | } | ||
605 | |||
606 | static void __exit jsflash_cleanup_module(void) | ||
607 | { | ||
608 | int i; | ||
609 | |||
610 | for (i = 0; i < JSF_MAX; i++) { | ||
611 | if ((i & JSF_PART_MASK) >= JSF_NPART) continue; | ||
612 | del_gendisk(jsfd_disk[i]); | ||
613 | put_disk(jsfd_disk[i]); | ||
614 | } | ||
615 | if (jsf0.busy) | ||
616 | printk("jsf0: cleaning busy unit\n"); | ||
617 | jsf0.base = 0; | ||
618 | jsf0.busy = 0; | ||
619 | |||
620 | misc_deregister(&jsf_dev); | ||
621 | if (unregister_blkdev(JSFD_MAJOR, "jsfd") != 0) | ||
622 | printk("jsfd: cleanup_module failed\n"); | ||
623 | blk_cleanup_queue(jsf_queue); | ||
624 | } | ||
625 | |||
626 | module_init(jsflash_init_module); | ||
627 | module_exit(jsflash_cleanup_module); | ||
diff --git a/drivers/sbus/char/max1617.h b/drivers/sbus/char/max1617.h new file mode 100644 index 00000000000..0bb09c286cb --- /dev/null +++ b/drivers/sbus/char/max1617.h | |||
@@ -0,0 +1,27 @@ | |||
1 | /* $Id: max1617.h,v 1.1 2001/04/02 09:59:08 davem Exp $ */ | ||
2 | #ifndef _MAX1617_H | ||
3 | #define _MAX1617_H | ||
4 | |||
5 | #define MAX1617_AMB_TEMP 0x00 /* Ambient temp in C */ | ||
6 | #define MAX1617_CPU_TEMP 0x01 /* Processor die temp in C */ | ||
7 | #define MAX1617_STATUS 0x02 /* Chip status bits */ | ||
8 | |||
9 | /* Read-only versions of changable registers. */ | ||
10 | #define MAX1617_RD_CFG_BYTE 0x03 /* Config register */ | ||
11 | #define MAX1617_RD_CVRATE_BYTE 0x04 /* Temp conversion rate */ | ||
12 | #define MAX1617_RD_AMB_HIGHLIM 0x05 /* Ambient high limit */ | ||
13 | #define MAX1617_RD_AMB_LOWLIM 0x06 /* Ambient low limit */ | ||
14 | #define MAX1617_RD_CPU_HIGHLIM 0x07 /* Processor high limit */ | ||
15 | #define MAX1617_RD_CPU_LOWLIM 0x08 /* Processor low limit */ | ||
16 | |||
17 | /* Write-only versions of the same. */ | ||
18 | #define MAX1617_WR_CFG_BYTE 0x09 | ||
19 | #define MAX1617_WR_CVRATE_BYTE 0x0a | ||
20 | #define MAX1617_WR_AMB_HIGHLIM 0x0b | ||
21 | #define MAX1617_WR_AMB_LOWLIM 0x0c | ||
22 | #define MAX1617_WR_CPU_HIGHLIM 0x0d | ||
23 | #define MAX1617_WR_CPU_LOWLIM 0x0e | ||
24 | |||
25 | #define MAX1617_ONESHOT 0x0f | ||
26 | |||
27 | #endif /* _MAX1617_H */ | ||
diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c new file mode 100644 index 00000000000..58ed3374957 --- /dev/null +++ b/drivers/sbus/char/openprom.c | |||
@@ -0,0 +1,630 @@ | |||
1 | /* | ||
2 | * Linux/SPARC PROM Configuration Driver | ||
3 | * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu) | ||
4 | * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) | ||
5 | * | ||
6 | * This character device driver allows user programs to access the | ||
7 | * PROM device tree. It is compatible with the SunOS /dev/openprom | ||
8 | * driver and the NetBSD /dev/openprom driver. The SunOS eeprom | ||
9 | * utility works without any modifications. | ||
10 | * | ||
11 | * The driver uses a minor number under the misc device major. The | ||
12 | * file read/write mode determines the type of access to the PROM. | ||
13 | * Interrupts are disabled whenever the driver calls into the PROM for | ||
14 | * sanity's sake. | ||
15 | */ | ||
16 | |||
17 | /* This program is free software; you can redistribute it and/or | ||
18 | * modify it under the terms of the GNU General Public License as | ||
19 | * published by the Free Software Foundation; either version 2 of the | ||
20 | * License, or (at your option) any later version. | ||
21 | * | ||
22 | * This program is distributed in the hope that it will be useful, but | ||
23 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
25 | * General Public License for more details. | ||
26 | * | ||
27 | * You should have received a copy of the GNU General Public License | ||
28 | * along with this program; if not, write to the Free Software | ||
29 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
30 | */ | ||
31 | |||
32 | #define PROMLIB_INTERNAL | ||
33 | |||
34 | #include <linux/config.h> | ||
35 | #include <linux/module.h> | ||
36 | #include <linux/kernel.h> | ||
37 | #include <linux/sched.h> | ||
38 | #include <linux/errno.h> | ||
39 | #include <linux/slab.h> | ||
40 | #include <linux/string.h> | ||
41 | #include <linux/miscdevice.h> | ||
42 | #include <linux/init.h> | ||
43 | #include <linux/fs.h> | ||
44 | #include <asm/oplib.h> | ||
45 | #include <asm/system.h> | ||
46 | #include <asm/uaccess.h> | ||
47 | #include <asm/openpromio.h> | ||
48 | #ifdef CONFIG_PCI | ||
49 | #include <linux/pci.h> | ||
50 | #include <asm/pbm.h> | ||
51 | #endif | ||
52 | |||
53 | /* Private data kept by the driver for each descriptor. */ | ||
54 | typedef struct openprom_private_data | ||
55 | { | ||
56 | int current_node; /* Current node for SunOS ioctls. */ | ||
57 | int lastnode; /* Last valid node used by BSD ioctls. */ | ||
58 | } DATA; | ||
59 | |||
60 | /* ID of the PROM node containing all of the EEPROM options. */ | ||
61 | static int options_node = 0; | ||
62 | |||
63 | /* | ||
64 | * Copy an openpromio structure into kernel space from user space. | ||
65 | * This routine does error checking to make sure that all memory | ||
66 | * accesses are within bounds. A pointer to the allocated openpromio | ||
67 | * structure will be placed in "*opp_p". Return value is the length | ||
68 | * of the user supplied buffer. | ||
69 | */ | ||
70 | static int copyin(struct openpromio __user *info, struct openpromio **opp_p) | ||
71 | { | ||
72 | unsigned int bufsize; | ||
73 | |||
74 | if (!info || !opp_p) | ||
75 | return -EFAULT; | ||
76 | |||
77 | if (get_user(bufsize, &info->oprom_size)) | ||
78 | return -EFAULT; | ||
79 | |||
80 | if (bufsize == 0) | ||
81 | return -EINVAL; | ||
82 | |||
83 | /* If the bufsize is too large, just limit it. | ||
84 | * Fix from Jason Rappleye. | ||
85 | */ | ||
86 | if (bufsize > OPROMMAXPARAM) | ||
87 | bufsize = OPROMMAXPARAM; | ||
88 | |||
89 | if (!(*opp_p = kmalloc(sizeof(int) + bufsize + 1, GFP_KERNEL))) | ||
90 | return -ENOMEM; | ||
91 | memset(*opp_p, 0, sizeof(int) + bufsize + 1); | ||
92 | |||
93 | if (copy_from_user(&(*opp_p)->oprom_array, | ||
94 | &info->oprom_array, bufsize)) { | ||
95 | kfree(*opp_p); | ||
96 | return -EFAULT; | ||
97 | } | ||
98 | return bufsize; | ||
99 | } | ||
100 | |||
101 | static int getstrings(struct openpromio __user *info, struct openpromio **opp_p) | ||
102 | { | ||
103 | int n, bufsize; | ||
104 | char c; | ||
105 | |||
106 | if (!info || !opp_p) | ||
107 | return -EFAULT; | ||
108 | |||
109 | if (!(*opp_p = kmalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL))) | ||
110 | return -ENOMEM; | ||
111 | |||
112 | memset(*opp_p, 0, sizeof(int) + OPROMMAXPARAM + 1); | ||
113 | (*opp_p)->oprom_size = 0; | ||
114 | |||
115 | n = bufsize = 0; | ||
116 | while ((n < 2) && (bufsize < OPROMMAXPARAM)) { | ||
117 | if (get_user(c, &info->oprom_array[bufsize])) { | ||
118 | kfree(*opp_p); | ||
119 | return -EFAULT; | ||
120 | } | ||
121 | if (c == '\0') | ||
122 | n++; | ||
123 | (*opp_p)->oprom_array[bufsize++] = c; | ||
124 | } | ||
125 | if (!n) { | ||
126 | kfree(*opp_p); | ||
127 | return -EINVAL; | ||
128 | } | ||
129 | return bufsize; | ||
130 | } | ||
131 | |||
132 | /* | ||
133 | * Copy an openpromio structure in kernel space back to user space. | ||
134 | */ | ||
135 | static int copyout(void __user *info, struct openpromio *opp, int len) | ||
136 | { | ||
137 | if (copy_to_user(info, opp, len)) | ||
138 | return -EFAULT; | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * SunOS and Solaris /dev/openprom ioctl calls. | ||
144 | */ | ||
145 | static int openprom_sunos_ioctl(struct inode * inode, struct file * file, | ||
146 | unsigned int cmd, unsigned long arg, int node) | ||
147 | { | ||
148 | DATA *data = (DATA *) file->private_data; | ||
149 | char buffer[OPROMMAXPARAM+1], *buf; | ||
150 | struct openpromio *opp; | ||
151 | int bufsize, len, error = 0; | ||
152 | static int cnt; | ||
153 | void __user *argp = (void __user *)arg; | ||
154 | |||
155 | if (cmd == OPROMSETOPT) | ||
156 | bufsize = getstrings(argp, &opp); | ||
157 | else | ||
158 | bufsize = copyin(argp, &opp); | ||
159 | |||
160 | if (bufsize < 0) | ||
161 | return bufsize; | ||
162 | |||
163 | switch (cmd) { | ||
164 | case OPROMGETOPT: | ||
165 | case OPROMGETPROP: | ||
166 | len = prom_getproplen(node, opp->oprom_array); | ||
167 | |||
168 | if (len <= 0 || len > bufsize) { | ||
169 | error = copyout(argp, opp, sizeof(int)); | ||
170 | break; | ||
171 | } | ||
172 | |||
173 | len = prom_getproperty(node, opp->oprom_array, buffer, bufsize); | ||
174 | |||
175 | memcpy(opp->oprom_array, buffer, len); | ||
176 | opp->oprom_array[len] = '\0'; | ||
177 | opp->oprom_size = len; | ||
178 | |||
179 | error = copyout(argp, opp, sizeof(int) + bufsize); | ||
180 | break; | ||
181 | |||
182 | case OPROMNXTOPT: | ||
183 | case OPROMNXTPROP: | ||
184 | buf = prom_nextprop(node, opp->oprom_array, buffer); | ||
185 | |||
186 | len = strlen(buf); | ||
187 | if (len == 0 || len + 1 > bufsize) { | ||
188 | error = copyout(argp, opp, sizeof(int)); | ||
189 | break; | ||
190 | } | ||
191 | |||
192 | memcpy(opp->oprom_array, buf, len); | ||
193 | opp->oprom_array[len] = '\0'; | ||
194 | opp->oprom_size = ++len; | ||
195 | |||
196 | error = copyout(argp, opp, sizeof(int) + bufsize); | ||
197 | break; | ||
198 | |||
199 | case OPROMSETOPT: | ||
200 | case OPROMSETOPT2: | ||
201 | buf = opp->oprom_array + strlen(opp->oprom_array) + 1; | ||
202 | len = opp->oprom_array + bufsize - buf; | ||
203 | |||
204 | error = prom_setprop(options_node, opp->oprom_array, | ||
205 | buf, len); | ||
206 | |||
207 | if (error < 0) | ||
208 | error = -EINVAL; | ||
209 | break; | ||
210 | |||
211 | case OPROMNEXT: | ||
212 | case OPROMCHILD: | ||
213 | case OPROMSETCUR: | ||
214 | if (bufsize < sizeof(int)) { | ||
215 | error = -EINVAL; | ||
216 | break; | ||
217 | } | ||
218 | |||
219 | node = *((int *) opp->oprom_array); | ||
220 | |||
221 | switch (cmd) { | ||
222 | case OPROMNEXT: node = __prom_getsibling(node); break; | ||
223 | case OPROMCHILD: node = __prom_getchild(node); break; | ||
224 | case OPROMSETCUR: break; | ||
225 | } | ||
226 | |||
227 | data->current_node = node; | ||
228 | *((int *)opp->oprom_array) = node; | ||
229 | opp->oprom_size = sizeof(int); | ||
230 | |||
231 | error = copyout(argp, opp, bufsize + sizeof(int)); | ||
232 | break; | ||
233 | |||
234 | case OPROMPCI2NODE: | ||
235 | error = -EINVAL; | ||
236 | |||
237 | if (bufsize >= 2*sizeof(int)) { | ||
238 | #ifdef CONFIG_PCI | ||
239 | struct pci_dev *pdev; | ||
240 | struct pcidev_cookie *pcp; | ||
241 | pdev = pci_find_slot (((int *) opp->oprom_array)[0], | ||
242 | ((int *) opp->oprom_array)[1]); | ||
243 | |||
244 | pcp = pdev->sysdata; | ||
245 | if (pcp != NULL && pcp->prom_node != -1 && pcp->prom_node) { | ||
246 | node = pcp->prom_node; | ||
247 | data->current_node = node; | ||
248 | *((int *)opp->oprom_array) = node; | ||
249 | opp->oprom_size = sizeof(int); | ||
250 | error = copyout(argp, opp, bufsize + sizeof(int)); | ||
251 | } | ||
252 | #endif | ||
253 | } | ||
254 | break; | ||
255 | |||
256 | case OPROMPATH2NODE: | ||
257 | node = prom_finddevice(opp->oprom_array); | ||
258 | data->current_node = node; | ||
259 | *((int *)opp->oprom_array) = node; | ||
260 | opp->oprom_size = sizeof(int); | ||
261 | |||
262 | error = copyout(argp, opp, bufsize + sizeof(int)); | ||
263 | break; | ||
264 | |||
265 | case OPROMGETBOOTARGS: | ||
266 | buf = saved_command_line; | ||
267 | |||
268 | len = strlen(buf); | ||
269 | |||
270 | if (len > bufsize) { | ||
271 | error = -EINVAL; | ||
272 | break; | ||
273 | } | ||
274 | |||
275 | strcpy(opp->oprom_array, buf); | ||
276 | opp->oprom_size = len; | ||
277 | |||
278 | error = copyout(argp, opp, bufsize + sizeof(int)); | ||
279 | break; | ||
280 | |||
281 | case OPROMU2P: | ||
282 | case OPROMGETCONS: | ||
283 | case OPROMGETFBNAME: | ||
284 | if (cnt++ < 10) | ||
285 | printk(KERN_INFO "openprom_sunos_ioctl: unimplemented ioctl\n"); | ||
286 | error = -EINVAL; | ||
287 | break; | ||
288 | default: | ||
289 | if (cnt++ < 10) | ||
290 | printk(KERN_INFO "openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg); | ||
291 | error = -EINVAL; | ||
292 | break; | ||
293 | } | ||
294 | |||
295 | kfree(opp); | ||
296 | return error; | ||
297 | } | ||
298 | |||
299 | |||
300 | /* Return nonzero if a specific node is in the PROM device tree. */ | ||
301 | static int intree(int root, int node) | ||
302 | { | ||
303 | for (; root != 0; root = prom_getsibling(root)) | ||
304 | if (root == node || intree(prom_getchild(root),node)) | ||
305 | return 1; | ||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | /* Return nonzero if a specific node is "valid". */ | ||
310 | static int goodnode(int n, DATA *data) | ||
311 | { | ||
312 | if (n == data->lastnode || n == prom_root_node || n == options_node) | ||
313 | return 1; | ||
314 | if (n == 0 || n == -1 || !intree(prom_root_node,n)) | ||
315 | return 0; | ||
316 | data->lastnode = n; | ||
317 | return 1; | ||
318 | } | ||
319 | |||
320 | /* Copy in a whole string from userspace into kernelspace. */ | ||
321 | static int copyin_string(char __user *user, size_t len, char **ptr) | ||
322 | { | ||
323 | char *tmp; | ||
324 | |||
325 | if ((ssize_t)len < 0 || (ssize_t)(len + 1) < 0) | ||
326 | return -EINVAL; | ||
327 | |||
328 | tmp = kmalloc(len + 1, GFP_KERNEL); | ||
329 | if (!tmp) | ||
330 | return -ENOMEM; | ||
331 | |||
332 | if(copy_from_user(tmp, user, len)) { | ||
333 | kfree(tmp); | ||
334 | return -EFAULT; | ||
335 | } | ||
336 | |||
337 | tmp[len] = '\0'; | ||
338 | |||
339 | *ptr = tmp; | ||
340 | |||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | /* | ||
345 | * NetBSD /dev/openprom ioctl calls. | ||
346 | */ | ||
347 | static int openprom_bsd_ioctl(struct inode * inode, struct file * file, | ||
348 | unsigned int cmd, unsigned long arg) | ||
349 | { | ||
350 | DATA *data = (DATA *) file->private_data; | ||
351 | void __user *argp = (void __user *)arg; | ||
352 | struct opiocdesc op; | ||
353 | int error, node, len; | ||
354 | char *str, *tmp; | ||
355 | char buffer[64]; | ||
356 | static int cnt; | ||
357 | |||
358 | switch (cmd) { | ||
359 | case OPIOCGET: | ||
360 | if (copy_from_user(&op, argp, sizeof(op))) | ||
361 | return -EFAULT; | ||
362 | |||
363 | if (!goodnode(op.op_nodeid,data)) | ||
364 | return -EINVAL; | ||
365 | |||
366 | error = copyin_string(op.op_name, op.op_namelen, &str); | ||
367 | if (error) | ||
368 | return error; | ||
369 | |||
370 | len = prom_getproplen(op.op_nodeid,str); | ||
371 | |||
372 | if (len > op.op_buflen) { | ||
373 | kfree(str); | ||
374 | return -ENOMEM; | ||
375 | } | ||
376 | |||
377 | op.op_buflen = len; | ||
378 | |||
379 | if (len <= 0) { | ||
380 | kfree(str); | ||
381 | /* Verified by the above copy_from_user */ | ||
382 | if (__copy_to_user(argp, &op, | ||
383 | sizeof(op))) | ||
384 | return -EFAULT; | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | tmp = kmalloc(len + 1, GFP_KERNEL); | ||
389 | if (!tmp) { | ||
390 | kfree(str); | ||
391 | return -ENOMEM; | ||
392 | } | ||
393 | |||
394 | prom_getproperty(op.op_nodeid, str, tmp, len); | ||
395 | |||
396 | tmp[len] = '\0'; | ||
397 | |||
398 | if (__copy_to_user(argp, &op, sizeof(op)) != 0 | ||
399 | || copy_to_user(op.op_buf, tmp, len) != 0) | ||
400 | error = -EFAULT; | ||
401 | |||
402 | kfree(tmp); | ||
403 | kfree(str); | ||
404 | |||
405 | return error; | ||
406 | |||
407 | case OPIOCNEXTPROP: | ||
408 | if (copy_from_user(&op, argp, sizeof(op))) | ||
409 | return -EFAULT; | ||
410 | |||
411 | if (!goodnode(op.op_nodeid,data)) | ||
412 | return -EINVAL; | ||
413 | |||
414 | error = copyin_string(op.op_name, op.op_namelen, &str); | ||
415 | if (error) | ||
416 | return error; | ||
417 | |||
418 | tmp = prom_nextprop(op.op_nodeid,str,buffer); | ||
419 | |||
420 | if (tmp) { | ||
421 | len = strlen(tmp); | ||
422 | if (len > op.op_buflen) | ||
423 | len = op.op_buflen; | ||
424 | else | ||
425 | op.op_buflen = len; | ||
426 | } else { | ||
427 | len = op.op_buflen = 0; | ||
428 | } | ||
429 | |||
430 | if (!access_ok(VERIFY_WRITE, argp, sizeof(op))) { | ||
431 | kfree(str); | ||
432 | return -EFAULT; | ||
433 | } | ||
434 | |||
435 | if (!access_ok(VERIFY_WRITE, op.op_buf, len)) { | ||
436 | kfree(str); | ||
437 | return -EFAULT; | ||
438 | } | ||
439 | |||
440 | error = __copy_to_user(argp, &op, sizeof(op)); | ||
441 | if (!error) error = __copy_to_user(op.op_buf, tmp, len); | ||
442 | |||
443 | kfree(str); | ||
444 | |||
445 | return error; | ||
446 | |||
447 | case OPIOCSET: | ||
448 | if (copy_from_user(&op, argp, sizeof(op))) | ||
449 | return -EFAULT; | ||
450 | |||
451 | if (!goodnode(op.op_nodeid,data)) | ||
452 | return -EINVAL; | ||
453 | |||
454 | error = copyin_string(op.op_name, op.op_namelen, &str); | ||
455 | if (error) | ||
456 | return error; | ||
457 | |||
458 | error = copyin_string(op.op_buf, op.op_buflen, &tmp); | ||
459 | if (error) { | ||
460 | kfree(str); | ||
461 | return error; | ||
462 | } | ||
463 | |||
464 | len = prom_setprop(op.op_nodeid,str,tmp,op.op_buflen+1); | ||
465 | |||
466 | if (len != op.op_buflen) | ||
467 | return -EINVAL; | ||
468 | |||
469 | kfree(str); | ||
470 | kfree(tmp); | ||
471 | |||
472 | return 0; | ||
473 | |||
474 | case OPIOCGETOPTNODE: | ||
475 | if (copy_to_user(argp, &options_node, sizeof(int))) | ||
476 | return -EFAULT; | ||
477 | return 0; | ||
478 | |||
479 | case OPIOCGETNEXT: | ||
480 | case OPIOCGETCHILD: | ||
481 | if (copy_from_user(&node, argp, sizeof(int))) | ||
482 | return -EFAULT; | ||
483 | |||
484 | if (cmd == OPIOCGETNEXT) | ||
485 | node = __prom_getsibling(node); | ||
486 | else | ||
487 | node = __prom_getchild(node); | ||
488 | |||
489 | if (__copy_to_user(argp, &node, sizeof(int))) | ||
490 | return -EFAULT; | ||
491 | |||
492 | return 0; | ||
493 | |||
494 | default: | ||
495 | if (cnt++ < 10) | ||
496 | printk(KERN_INFO "openprom_bsd_ioctl: cmd 0x%X\n", cmd); | ||
497 | return -EINVAL; | ||
498 | |||
499 | } | ||
500 | } | ||
501 | |||
502 | |||
503 | /* | ||
504 | * Handoff control to the correct ioctl handler. | ||
505 | */ | ||
506 | static int openprom_ioctl(struct inode * inode, struct file * file, | ||
507 | unsigned int cmd, unsigned long arg) | ||
508 | { | ||
509 | DATA *data = (DATA *) file->private_data; | ||
510 | static int cnt; | ||
511 | |||
512 | switch (cmd) { | ||
513 | case OPROMGETOPT: | ||
514 | case OPROMNXTOPT: | ||
515 | if ((file->f_mode & FMODE_READ) == 0) | ||
516 | return -EPERM; | ||
517 | return openprom_sunos_ioctl(inode, file, cmd, arg, | ||
518 | options_node); | ||
519 | |||
520 | case OPROMSETOPT: | ||
521 | case OPROMSETOPT2: | ||
522 | if ((file->f_mode & FMODE_WRITE) == 0) | ||
523 | return -EPERM; | ||
524 | return openprom_sunos_ioctl(inode, file, cmd, arg, | ||
525 | options_node); | ||
526 | |||
527 | case OPROMNEXT: | ||
528 | case OPROMCHILD: | ||
529 | case OPROMGETPROP: | ||
530 | case OPROMNXTPROP: | ||
531 | if ((file->f_mode & FMODE_READ) == 0) | ||
532 | return -EPERM; | ||
533 | return openprom_sunos_ioctl(inode, file, cmd, arg, | ||
534 | data->current_node); | ||
535 | |||
536 | case OPROMU2P: | ||
537 | case OPROMGETCONS: | ||
538 | case OPROMGETFBNAME: | ||
539 | case OPROMGETBOOTARGS: | ||
540 | case OPROMSETCUR: | ||
541 | case OPROMPCI2NODE: | ||
542 | case OPROMPATH2NODE: | ||
543 | if ((file->f_mode & FMODE_READ) == 0) | ||
544 | return -EPERM; | ||
545 | return openprom_sunos_ioctl(inode, file, cmd, arg, 0); | ||
546 | |||
547 | case OPIOCGET: | ||
548 | case OPIOCNEXTPROP: | ||
549 | case OPIOCGETOPTNODE: | ||
550 | case OPIOCGETNEXT: | ||
551 | case OPIOCGETCHILD: | ||
552 | if ((file->f_mode & FMODE_READ) == 0) | ||
553 | return -EBADF; | ||
554 | return openprom_bsd_ioctl(inode,file,cmd,arg); | ||
555 | |||
556 | case OPIOCSET: | ||
557 | if ((file->f_mode & FMODE_WRITE) == 0) | ||
558 | return -EBADF; | ||
559 | return openprom_bsd_ioctl(inode,file,cmd,arg); | ||
560 | |||
561 | default: | ||
562 | if (cnt++ < 10) | ||
563 | printk("openprom_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg); | ||
564 | return -EINVAL; | ||
565 | } | ||
566 | } | ||
567 | |||
568 | static int openprom_open(struct inode * inode, struct file * file) | ||
569 | { | ||
570 | DATA *data; | ||
571 | |||
572 | data = (DATA *) kmalloc(sizeof(DATA), GFP_KERNEL); | ||
573 | if (!data) | ||
574 | return -ENOMEM; | ||
575 | |||
576 | data->current_node = prom_root_node; | ||
577 | data->lastnode = prom_root_node; | ||
578 | file->private_data = (void *)data; | ||
579 | |||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | static int openprom_release(struct inode * inode, struct file * file) | ||
584 | { | ||
585 | kfree(file->private_data); | ||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | static struct file_operations openprom_fops = { | ||
590 | .owner = THIS_MODULE, | ||
591 | .llseek = no_llseek, | ||
592 | .ioctl = openprom_ioctl, | ||
593 | .open = openprom_open, | ||
594 | .release = openprom_release, | ||
595 | }; | ||
596 | |||
597 | static struct miscdevice openprom_dev = { | ||
598 | SUN_OPENPROM_MINOR, "openprom", &openprom_fops | ||
599 | }; | ||
600 | |||
601 | static int __init openprom_init(void) | ||
602 | { | ||
603 | int error; | ||
604 | |||
605 | error = misc_register(&openprom_dev); | ||
606 | if (error) { | ||
607 | printk(KERN_ERR "openprom: unable to get misc minor\n"); | ||
608 | return error; | ||
609 | } | ||
610 | |||
611 | options_node = prom_getchild(prom_root_node); | ||
612 | options_node = prom_searchsiblings(options_node,"options"); | ||
613 | |||
614 | if (options_node == 0 || options_node == -1) { | ||
615 | printk(KERN_ERR "openprom: unable to find options node\n"); | ||
616 | misc_deregister(&openprom_dev); | ||
617 | return -EIO; | ||
618 | } | ||
619 | |||
620 | return 0; | ||
621 | } | ||
622 | |||
623 | static void __exit openprom_cleanup(void) | ||
624 | { | ||
625 | misc_deregister(&openprom_dev); | ||
626 | } | ||
627 | |||
628 | module_init(openprom_init); | ||
629 | module_exit(openprom_cleanup); | ||
630 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/sbus/char/riowatchdog.c b/drivers/sbus/char/riowatchdog.c new file mode 100644 index 00000000000..d1babff6a53 --- /dev/null +++ b/drivers/sbus/char/riowatchdog.c | |||
@@ -0,0 +1,293 @@ | |||
1 | /* $Id: riowatchdog.c,v 1.3.2.2 2002/01/23 18:48:02 davem Exp $ | ||
2 | * riowatchdog.c - driver for hw watchdog inside Super I/O of RIO | ||
3 | * | ||
4 | * Copyright (C) 2001 David S. Miller (davem@redhat.com) | ||
5 | */ | ||
6 | |||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/module.h> | ||
9 | #include <linux/types.h> | ||
10 | #include <linux/fs.h> | ||
11 | #include <linux/errno.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/miscdevice.h> | ||
14 | |||
15 | #include <asm/io.h> | ||
16 | #include <asm/ebus.h> | ||
17 | #include <asm/bbc.h> | ||
18 | #include <asm/oplib.h> | ||
19 | #include <asm/uaccess.h> | ||
20 | |||
21 | #include <asm/watchdog.h> | ||
22 | |||
23 | /* RIO uses the NatSemi Super I/O power management logical device | ||
24 | * as its' watchdog. | ||
25 | * | ||
26 | * When the watchdog triggers, it asserts a line to the BBC (Boot Bus | ||
27 | * Controller) of the machine. The BBC can only be configured to | ||
28 | * trigger a power-on reset when the signal is asserted. The BBC | ||
29 | * can be configured to ignore the signal entirely as well. | ||
30 | * | ||
31 | * The only Super I/O device register we care about is at index | ||
32 | * 0x05 (WDTO_INDEX) which is the watchdog time-out in minutes (1-255). | ||
33 | * If set to zero, this disables the watchdog. When set, the system | ||
34 | * must periodically (before watchdog expires) clear (set to zero) and | ||
35 | * re-set the watchdog else it will trigger. | ||
36 | * | ||
37 | * There are two other indexed watchdog registers inside this Super I/O | ||
38 | * logical device, but they are unused. The first, at index 0x06 is | ||
39 | * the watchdog control and can be used to make the watchdog timer re-set | ||
40 | * when the PS/2 mouse or serial lines show activity. The second, at | ||
41 | * index 0x07 is merely a sampling of the line from the watchdog to the | ||
42 | * BBC. | ||
43 | * | ||
44 | * The watchdog device generates no interrupts. | ||
45 | */ | ||
46 | |||
47 | MODULE_AUTHOR("David S. Miller <davem@redhat.com>"); | ||
48 | MODULE_DESCRIPTION("Hardware watchdog driver for Sun RIO"); | ||
49 | MODULE_SUPPORTED_DEVICE("watchdog"); | ||
50 | MODULE_LICENSE("GPL"); | ||
51 | |||
52 | #define RIOWD_NAME "pmc" | ||
53 | #define RIOWD_MINOR 215 | ||
54 | |||
55 | static DEFINE_SPINLOCK(riowd_lock); | ||
56 | |||
57 | static void __iomem *bbc_regs; | ||
58 | static void __iomem *riowd_regs; | ||
59 | #define WDTO_INDEX 0x05 | ||
60 | |||
61 | static int riowd_timeout = 1; /* in minutes */ | ||
62 | module_param(riowd_timeout, int, 0); | ||
63 | MODULE_PARM_DESC(riowd_timeout, "Watchdog timeout in minutes"); | ||
64 | |||
65 | #if 0 /* Currently unused. */ | ||
66 | static u8 riowd_readreg(int index) | ||
67 | { | ||
68 | unsigned long flags; | ||
69 | u8 ret; | ||
70 | |||
71 | spin_lock_irqsave(&riowd_lock, flags); | ||
72 | writeb(index, riowd_regs + 0); | ||
73 | ret = readb(riowd_regs + 1); | ||
74 | spin_unlock_irqrestore(&riowd_lock, flags); | ||
75 | |||
76 | return ret; | ||
77 | } | ||
78 | #endif | ||
79 | |||
80 | static void riowd_writereg(u8 val, int index) | ||
81 | { | ||
82 | unsigned long flags; | ||
83 | |||
84 | spin_lock_irqsave(&riowd_lock, flags); | ||
85 | writeb(index, riowd_regs + 0); | ||
86 | writeb(val, riowd_regs + 1); | ||
87 | spin_unlock_irqrestore(&riowd_lock, flags); | ||
88 | } | ||
89 | |||
90 | static void riowd_pingtimer(void) | ||
91 | { | ||
92 | riowd_writereg(riowd_timeout, WDTO_INDEX); | ||
93 | } | ||
94 | |||
95 | static void riowd_stoptimer(void) | ||
96 | { | ||
97 | u8 val; | ||
98 | |||
99 | riowd_writereg(0, WDTO_INDEX); | ||
100 | |||
101 | val = readb(bbc_regs + BBC_WDACTION); | ||
102 | val &= ~BBC_WDACTION_RST; | ||
103 | writeb(val, bbc_regs + BBC_WDACTION); | ||
104 | } | ||
105 | |||
106 | static void riowd_starttimer(void) | ||
107 | { | ||
108 | u8 val; | ||
109 | |||
110 | riowd_writereg(riowd_timeout, WDTO_INDEX); | ||
111 | |||
112 | val = readb(bbc_regs + BBC_WDACTION); | ||
113 | val |= BBC_WDACTION_RST; | ||
114 | writeb(val, bbc_regs + BBC_WDACTION); | ||
115 | } | ||
116 | |||
117 | static int riowd_open(struct inode *inode, struct file *filp) | ||
118 | { | ||
119 | nonseekable_open(inode, filp); | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static int riowd_release(struct inode *inode, struct file *filp) | ||
124 | { | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int riowd_ioctl(struct inode *inode, struct file *filp, | ||
129 | unsigned int cmd, unsigned long arg) | ||
130 | { | ||
131 | static struct watchdog_info info = { | ||
132 | WDIOF_SETTIMEOUT, 0, "Natl. Semiconductor PC97317" | ||
133 | }; | ||
134 | void __user *argp = (void __user *)arg; | ||
135 | unsigned int options; | ||
136 | int new_margin; | ||
137 | |||
138 | switch (cmd) { | ||
139 | case WDIOC_GETSUPPORT: | ||
140 | if (copy_to_user(argp, &info, sizeof(info))) | ||
141 | return -EFAULT; | ||
142 | break; | ||
143 | |||
144 | case WDIOC_GETSTATUS: | ||
145 | case WDIOC_GETBOOTSTATUS: | ||
146 | if (put_user(0, (int __user *)argp)) | ||
147 | return -EFAULT; | ||
148 | break; | ||
149 | |||
150 | case WDIOC_KEEPALIVE: | ||
151 | riowd_pingtimer(); | ||
152 | break; | ||
153 | |||
154 | case WDIOC_SETOPTIONS: | ||
155 | if (copy_from_user(&options, argp, sizeof(options))) | ||
156 | return -EFAULT; | ||
157 | |||
158 | if (options & WDIOS_DISABLECARD) | ||
159 | riowd_stoptimer(); | ||
160 | else if (options & WDIOS_ENABLECARD) | ||
161 | riowd_starttimer(); | ||
162 | else | ||
163 | return -EINVAL; | ||
164 | |||
165 | break; | ||
166 | |||
167 | case WDIOC_SETTIMEOUT: | ||
168 | if (get_user(new_margin, (int __user *)argp)) | ||
169 | return -EFAULT; | ||
170 | if ((new_margin < 60) || (new_margin > (255 * 60))) | ||
171 | return -EINVAL; | ||
172 | riowd_timeout = (new_margin + 59) / 60; | ||
173 | riowd_pingtimer(); | ||
174 | /* Fall */ | ||
175 | |||
176 | case WDIOC_GETTIMEOUT: | ||
177 | return put_user(riowd_timeout * 60, (int __user *)argp); | ||
178 | |||
179 | default: | ||
180 | return -EINVAL; | ||
181 | }; | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static ssize_t riowd_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | ||
187 | { | ||
188 | if (count) { | ||
189 | riowd_pingtimer(); | ||
190 | return 1; | ||
191 | } | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static struct file_operations riowd_fops = { | ||
197 | .owner = THIS_MODULE, | ||
198 | .ioctl = riowd_ioctl, | ||
199 | .open = riowd_open, | ||
200 | .write = riowd_write, | ||
201 | .release = riowd_release, | ||
202 | }; | ||
203 | |||
204 | static struct miscdevice riowd_miscdev = { RIOWD_MINOR, RIOWD_NAME, &riowd_fops }; | ||
205 | |||
206 | static int __init riowd_bbc_init(void) | ||
207 | { | ||
208 | struct linux_ebus *ebus = NULL; | ||
209 | struct linux_ebus_device *edev = NULL; | ||
210 | u8 val; | ||
211 | |||
212 | for_each_ebus(ebus) { | ||
213 | for_each_ebusdev(edev, ebus) { | ||
214 | if (!strcmp(edev->prom_name, "bbc")) | ||
215 | goto found_bbc; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | found_bbc: | ||
220 | if (!edev) | ||
221 | return -ENODEV; | ||
222 | bbc_regs = ioremap(edev->resource[0].start, BBC_REGS_SIZE); | ||
223 | if (!bbc_regs) | ||
224 | return -ENODEV; | ||
225 | |||
226 | /* Turn it off. */ | ||
227 | val = readb(bbc_regs + BBC_WDACTION); | ||
228 | val &= ~BBC_WDACTION_RST; | ||
229 | writeb(val, bbc_regs + BBC_WDACTION); | ||
230 | |||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | static int __init riowd_init(void) | ||
235 | { | ||
236 | struct linux_ebus *ebus = NULL; | ||
237 | struct linux_ebus_device *edev = NULL; | ||
238 | |||
239 | for_each_ebus(ebus) { | ||
240 | for_each_ebusdev(edev, ebus) { | ||
241 | if (!strcmp(edev->prom_name, RIOWD_NAME)) | ||
242 | goto ebus_done; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | ebus_done: | ||
247 | if (!edev) | ||
248 | goto fail; | ||
249 | |||
250 | riowd_regs = ioremap(edev->resource[0].start, 2); | ||
251 | if (riowd_regs == NULL) { | ||
252 | printk(KERN_ERR "pmc: Cannot map registers.\n"); | ||
253 | return -ENODEV; | ||
254 | } | ||
255 | |||
256 | if (riowd_bbc_init()) { | ||
257 | printk(KERN_ERR "pmc: Failure initializing BBC config.\n"); | ||
258 | goto fail; | ||
259 | } | ||
260 | |||
261 | if (misc_register(&riowd_miscdev)) { | ||
262 | printk(KERN_ERR "pmc: Cannot register watchdog misc device.\n"); | ||
263 | goto fail; | ||
264 | } | ||
265 | |||
266 | printk(KERN_INFO "pmc: Hardware watchdog [%i minutes], " | ||
267 | "regs at %p\n", riowd_timeout, riowd_regs); | ||
268 | |||
269 | return 0; | ||
270 | |||
271 | fail: | ||
272 | if (riowd_regs) { | ||
273 | iounmap(riowd_regs); | ||
274 | riowd_regs = NULL; | ||
275 | } | ||
276 | if (bbc_regs) { | ||
277 | iounmap(bbc_regs); | ||
278 | bbc_regs = NULL; | ||
279 | } | ||
280 | return -ENODEV; | ||
281 | } | ||
282 | |||
283 | static void __exit riowd_cleanup(void) | ||
284 | { | ||
285 | misc_deregister(&riowd_miscdev); | ||
286 | iounmap(riowd_regs); | ||
287 | riowd_regs = NULL; | ||
288 | iounmap(bbc_regs); | ||
289 | bbc_regs = NULL; | ||
290 | } | ||
291 | |||
292 | module_init(riowd_init); | ||
293 | module_exit(riowd_cleanup); | ||
diff --git a/drivers/sbus/char/rtc.c b/drivers/sbus/char/rtc.c new file mode 100644 index 00000000000..bf3273eb1c8 --- /dev/null +++ b/drivers/sbus/char/rtc.c | |||
@@ -0,0 +1,178 @@ | |||
1 | /* $Id: rtc.c,v 1.28 2001/10/08 22:19:51 davem Exp $ | ||
2 | * | ||
3 | * Linux/SPARC Real Time Clock Driver | ||
4 | * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) | ||
5 | * | ||
6 | * This is a little driver that lets a user-level program access | ||
7 | * the SPARC Mostek real time clock chip. It is no use unless you | ||
8 | * use the modified clock utility. | ||
9 | * | ||
10 | * Get the modified clock utility from: | ||
11 | * ftp://vger.kernel.org/pub/linux/Sparc/userland/clock.c | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/miscdevice.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/fcntl.h> | ||
20 | #include <linux/poll.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/smp_lock.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <asm/mostek.h> | ||
25 | #include <asm/system.h> | ||
26 | #include <asm/uaccess.h> | ||
27 | #include <asm/rtc.h> | ||
28 | |||
29 | static int rtc_busy = 0; | ||
30 | |||
31 | /* Retrieve the current date and time from the real time clock. */ | ||
32 | static void get_rtc_time(struct rtc_time *t) | ||
33 | { | ||
34 | void * __iomem regs = mstk48t02_regs; | ||
35 | u8 tmp; | ||
36 | |||
37 | spin_lock_irq(&mostek_lock); | ||
38 | |||
39 | tmp = mostek_read(regs + MOSTEK_CREG); | ||
40 | tmp |= MSTK_CREG_READ; | ||
41 | mostek_write(regs + MOSTEK_CREG, tmp); | ||
42 | |||
43 | t->sec = MSTK_REG_SEC(regs); | ||
44 | t->min = MSTK_REG_MIN(regs); | ||
45 | t->hour = MSTK_REG_HOUR(regs); | ||
46 | t->dow = MSTK_REG_DOW(regs); | ||
47 | t->dom = MSTK_REG_DOM(regs); | ||
48 | t->month = MSTK_REG_MONTH(regs); | ||
49 | t->year = MSTK_CVT_YEAR( MSTK_REG_YEAR(regs) ); | ||
50 | |||
51 | tmp = mostek_read(regs + MOSTEK_CREG); | ||
52 | tmp &= ~MSTK_CREG_READ; | ||
53 | mostek_write(regs + MOSTEK_CREG, tmp); | ||
54 | |||
55 | spin_unlock_irq(&mostek_lock); | ||
56 | } | ||
57 | |||
58 | /* Set the current date and time inthe real time clock. */ | ||
59 | void set_rtc_time(struct rtc_time *t) | ||
60 | { | ||
61 | void * __iomem regs = mstk48t02_regs; | ||
62 | u8 tmp; | ||
63 | |||
64 | spin_lock_irq(&mostek_lock); | ||
65 | |||
66 | tmp = mostek_read(regs + MOSTEK_CREG); | ||
67 | tmp |= MSTK_CREG_WRITE; | ||
68 | mostek_write(regs + MOSTEK_CREG, tmp); | ||
69 | |||
70 | MSTK_SET_REG_SEC(regs,t->sec); | ||
71 | MSTK_SET_REG_MIN(regs,t->min); | ||
72 | MSTK_SET_REG_HOUR(regs,t->hour); | ||
73 | MSTK_SET_REG_DOW(regs,t->dow); | ||
74 | MSTK_SET_REG_DOM(regs,t->dom); | ||
75 | MSTK_SET_REG_MONTH(regs,t->month); | ||
76 | MSTK_SET_REG_YEAR(regs,t->year - MSTK_YEAR_ZERO); | ||
77 | |||
78 | tmp = mostek_read(regs + MOSTEK_CREG); | ||
79 | tmp &= ~MSTK_CREG_WRITE; | ||
80 | mostek_write(regs + MOSTEK_CREG, tmp); | ||
81 | |||
82 | spin_unlock_irq(&mostek_lock); | ||
83 | } | ||
84 | |||
85 | static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | ||
86 | unsigned long arg) | ||
87 | { | ||
88 | struct rtc_time rtc_tm; | ||
89 | void __user *argp = (void __user *)arg; | ||
90 | |||
91 | switch (cmd) | ||
92 | { | ||
93 | case RTCGET: | ||
94 | memset(&rtc_tm, 0, sizeof(struct rtc_time)); | ||
95 | get_rtc_time(&rtc_tm); | ||
96 | |||
97 | if (copy_to_user(argp, &rtc_tm, sizeof(struct rtc_time))) | ||
98 | return -EFAULT; | ||
99 | |||
100 | return 0; | ||
101 | |||
102 | |||
103 | case RTCSET: | ||
104 | if (!capable(CAP_SYS_TIME)) | ||
105 | return -EPERM; | ||
106 | |||
107 | if (copy_from_user(&rtc_tm, argp, sizeof(struct rtc_time))) | ||
108 | return -EFAULT; | ||
109 | |||
110 | set_rtc_time(&rtc_tm); | ||
111 | |||
112 | return 0; | ||
113 | |||
114 | default: | ||
115 | return -EINVAL; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | static int rtc_open(struct inode *inode, struct file *file) | ||
120 | { | ||
121 | int ret; | ||
122 | |||
123 | spin_lock_irq(&mostek_lock); | ||
124 | if (rtc_busy) { | ||
125 | ret = -EBUSY; | ||
126 | } else { | ||
127 | rtc_busy = 1; | ||
128 | ret = 0; | ||
129 | } | ||
130 | spin_unlock_irq(&mostek_lock); | ||
131 | |||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | static int rtc_release(struct inode *inode, struct file *file) | ||
136 | { | ||
137 | rtc_busy = 0; | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static struct file_operations rtc_fops = { | ||
143 | .owner = THIS_MODULE, | ||
144 | .llseek = no_llseek, | ||
145 | .ioctl = rtc_ioctl, | ||
146 | .open = rtc_open, | ||
147 | .release = rtc_release, | ||
148 | }; | ||
149 | |||
150 | static struct miscdevice rtc_dev = { RTC_MINOR, "rtc", &rtc_fops }; | ||
151 | |||
152 | static int __init rtc_sun_init(void) | ||
153 | { | ||
154 | int error; | ||
155 | |||
156 | /* It is possible we are being driven by some other RTC chip | ||
157 | * and thus another RTC driver is handling things. | ||
158 | */ | ||
159 | if (mstk48t02_regs == 0) | ||
160 | return -ENODEV; | ||
161 | |||
162 | error = misc_register(&rtc_dev); | ||
163 | if (error) { | ||
164 | printk(KERN_ERR "rtc: unable to get misc minor for Mostek\n"); | ||
165 | return error; | ||
166 | } | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | static void __exit rtc_sun_cleanup(void) | ||
172 | { | ||
173 | misc_deregister(&rtc_dev); | ||
174 | } | ||
175 | |||
176 | module_init(rtc_sun_init); | ||
177 | module_exit(rtc_sun_cleanup); | ||
178 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c new file mode 100644 index 00000000000..858cc683f85 --- /dev/null +++ b/drivers/sbus/char/uctrl.c | |||
@@ -0,0 +1,422 @@ | |||
1 | /* $Id: uctrl.c,v 1.12 2001/10/08 22:19:51 davem Exp $ | ||
2 | * uctrl.c: TS102 Microcontroller interface on Tadpole Sparcbook 3 | ||
3 | * | ||
4 | * Copyright 1999 Derrick J Brashear (shadow@dementia.org) | ||
5 | */ | ||
6 | |||
7 | #include <linux/module.h> | ||
8 | #include <linux/sched.h> | ||
9 | #include <linux/errno.h> | ||
10 | #include <linux/delay.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/ioport.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/miscdevice.h> | ||
16 | #include <linux/mm.h> | ||
17 | |||
18 | #include <asm/openprom.h> | ||
19 | #include <asm/oplib.h> | ||
20 | #include <asm/system.h> | ||
21 | #include <asm/irq.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/pgtable.h> | ||
24 | #include <asm/sbus.h> | ||
25 | |||
26 | #define UCTRL_MINOR 174 | ||
27 | |||
28 | #define DEBUG 1 | ||
29 | #ifdef DEBUG | ||
30 | #define dprintk(x) printk x | ||
31 | #else | ||
32 | #define dprintk(x) | ||
33 | #endif | ||
34 | |||
35 | struct uctrl_regs { | ||
36 | volatile u32 uctrl_intr; | ||
37 | volatile u32 uctrl_data; | ||
38 | volatile u32 uctrl_stat; | ||
39 | volatile u32 uctrl_xxx[5]; | ||
40 | }; | ||
41 | |||
42 | struct ts102_regs { | ||
43 | volatile u32 card_a_intr; | ||
44 | volatile u32 card_a_stat; | ||
45 | volatile u32 card_a_ctrl; | ||
46 | volatile u32 card_a_xxx; | ||
47 | volatile u32 card_b_intr; | ||
48 | volatile u32 card_b_stat; | ||
49 | volatile u32 card_b_ctrl; | ||
50 | volatile u32 card_b_xxx; | ||
51 | volatile u32 uctrl_intr; | ||
52 | volatile u32 uctrl_data; | ||
53 | volatile u32 uctrl_stat; | ||
54 | volatile u32 uctrl_xxx; | ||
55 | volatile u32 ts102_xxx[4]; | ||
56 | }; | ||
57 | |||
58 | /* Bits for uctrl_intr register */ | ||
59 | #define UCTRL_INTR_TXE_REQ 0x01 /* transmit FIFO empty int req */ | ||
60 | #define UCTRL_INTR_TXNF_REQ 0x02 /* transmit FIFO not full int req */ | ||
61 | #define UCTRL_INTR_RXNE_REQ 0x04 /* receive FIFO not empty int req */ | ||
62 | #define UCTRL_INTR_RXO_REQ 0x08 /* receive FIFO overflow int req */ | ||
63 | #define UCTRL_INTR_TXE_MSK 0x10 /* transmit FIFO empty mask */ | ||
64 | #define UCTRL_INTR_TXNF_MSK 0x20 /* transmit FIFO not full mask */ | ||
65 | #define UCTRL_INTR_RXNE_MSK 0x40 /* receive FIFO not empty mask */ | ||
66 | #define UCTRL_INTR_RXO_MSK 0x80 /* receive FIFO overflow mask */ | ||
67 | |||
68 | /* Bits for uctrl_stat register */ | ||
69 | #define UCTRL_STAT_TXE_STA 0x01 /* transmit FIFO empty status */ | ||
70 | #define UCTRL_STAT_TXNF_STA 0x02 /* transmit FIFO not full status */ | ||
71 | #define UCTRL_STAT_RXNE_STA 0x04 /* receive FIFO not empty status */ | ||
72 | #define UCTRL_STAT_RXO_STA 0x08 /* receive FIFO overflow status */ | ||
73 | |||
74 | static const char *uctrl_extstatus[16] = { | ||
75 | "main power available", | ||
76 | "internal battery attached", | ||
77 | "external battery attached", | ||
78 | "external VGA attached", | ||
79 | "external keyboard attached", | ||
80 | "external mouse attached", | ||
81 | "lid down", | ||
82 | "internal battery currently charging", | ||
83 | "external battery currently charging", | ||
84 | "internal battery currently discharging", | ||
85 | "external battery currently discharging", | ||
86 | }; | ||
87 | |||
88 | /* Everything required for one transaction with the uctrl */ | ||
89 | struct uctrl_txn { | ||
90 | u8 opcode; | ||
91 | u8 inbits; | ||
92 | u8 outbits; | ||
93 | u8 *inbuf; | ||
94 | u8 *outbuf; | ||
95 | }; | ||
96 | |||
97 | struct uctrl_status { | ||
98 | u8 current_temp; /* 0x07 */ | ||
99 | u8 reset_status; /* 0x0b */ | ||
100 | u16 event_status; /* 0x0c */ | ||
101 | u16 error_status; /* 0x10 */ | ||
102 | u16 external_status; /* 0x11, 0x1b */ | ||
103 | u8 internal_charge; /* 0x18 */ | ||
104 | u8 external_charge; /* 0x19 */ | ||
105 | u16 control_lcd; /* 0x20 */ | ||
106 | u8 control_bitport; /* 0x21 */ | ||
107 | u8 speaker_volume; /* 0x23 */ | ||
108 | u8 control_tft_brightness; /* 0x24 */ | ||
109 | u8 control_kbd_repeat_delay; /* 0x28 */ | ||
110 | u8 control_kbd_repeat_period; /* 0x29 */ | ||
111 | u8 control_screen_contrast; /* 0x2F */ | ||
112 | }; | ||
113 | |||
114 | enum uctrl_opcode { | ||
115 | READ_SERIAL_NUMBER=0x1, | ||
116 | READ_ETHERNET_ADDRESS=0x2, | ||
117 | READ_HARDWARE_VERSION=0x3, | ||
118 | READ_MICROCONTROLLER_VERSION=0x4, | ||
119 | READ_MAX_TEMPERATURE=0x5, | ||
120 | READ_MIN_TEMPERATURE=0x6, | ||
121 | READ_CURRENT_TEMPERATURE=0x7, | ||
122 | READ_SYSTEM_VARIANT=0x8, | ||
123 | READ_POWERON_CYCLES=0x9, | ||
124 | READ_POWERON_SECONDS=0xA, | ||
125 | READ_RESET_STATUS=0xB, | ||
126 | READ_EVENT_STATUS=0xC, | ||
127 | READ_REAL_TIME_CLOCK=0xD, | ||
128 | READ_EXTERNAL_VGA_PORT=0xE, | ||
129 | READ_MICROCONTROLLER_ROM_CHECKSUM=0xF, | ||
130 | READ_ERROR_STATUS=0x10, | ||
131 | READ_EXTERNAL_STATUS=0x11, | ||
132 | READ_USER_CONFIGURATION_AREA=0x12, | ||
133 | READ_MICROCONTROLLER_VOLTAGE=0x13, | ||
134 | READ_INTERNAL_BATTERY_VOLTAGE=0x14, | ||
135 | READ_DCIN_VOLTAGE=0x15, | ||
136 | READ_HORIZONTAL_POINTER_VOLTAGE=0x16, | ||
137 | READ_VERTICAL_POINTER_VOLTAGE=0x17, | ||
138 | READ_INTERNAL_BATTERY_CHARGE_LEVEL=0x18, | ||
139 | READ_EXTERNAL_BATTERY_CHARGE_LEVEL=0x19, | ||
140 | READ_REAL_TIME_CLOCK_ALARM=0x1A, | ||
141 | READ_EVENT_STATUS_NO_RESET=0x1B, | ||
142 | READ_INTERNAL_KEYBOARD_LAYOUT=0x1C, | ||
143 | READ_EXTERNAL_KEYBOARD_LAYOUT=0x1D, | ||
144 | READ_EEPROM_STATUS=0x1E, | ||
145 | CONTROL_LCD=0x20, | ||
146 | CONTROL_BITPORT=0x21, | ||
147 | SPEAKER_VOLUME=0x23, | ||
148 | CONTROL_TFT_BRIGHTNESS=0x24, | ||
149 | CONTROL_WATCHDOG=0x25, | ||
150 | CONTROL_FACTORY_EEPROM_AREA=0x26, | ||
151 | CONTROL_KBD_TIME_UNTIL_REPEAT=0x28, | ||
152 | CONTROL_KBD_TIME_BETWEEN_REPEATS=0x29, | ||
153 | CONTROL_TIMEZONE=0x2A, | ||
154 | CONTROL_MARK_SPACE_RATIO=0x2B, | ||
155 | CONTROL_DIAGNOSTIC_MODE=0x2E, | ||
156 | CONTROL_SCREEN_CONTRAST=0x2F, | ||
157 | RING_BELL=0x30, | ||
158 | SET_DIAGNOSTIC_STATUS=0x32, | ||
159 | CLEAR_KEY_COMBINATION_TABLE=0x33, | ||
160 | PERFORM_SOFTWARE_RESET=0x34, | ||
161 | SET_REAL_TIME_CLOCK=0x35, | ||
162 | RECALIBRATE_POINTING_STICK=0x36, | ||
163 | SET_BELL_FREQUENCY=0x37, | ||
164 | SET_INTERNAL_BATTERY_CHARGE_RATE=0x39, | ||
165 | SET_EXTERNAL_BATTERY_CHARGE_RATE=0x3A, | ||
166 | SET_REAL_TIME_CLOCK_ALARM=0x3B, | ||
167 | READ_EEPROM=0x40, | ||
168 | WRITE_EEPROM=0x41, | ||
169 | WRITE_TO_STATUS_DISPLAY=0x42, | ||
170 | DEFINE_SPECIAL_CHARACTER=0x43, | ||
171 | DEFINE_KEY_COMBINATION_ENTRY=0x50, | ||
172 | DEFINE_STRING_TABLE_ENTRY=0x51, | ||
173 | DEFINE_STATUS_SCREEN_DISPLAY=0x52, | ||
174 | PERFORM_EMU_COMMANDS=0x64, | ||
175 | READ_EMU_REGISTER=0x65, | ||
176 | WRITE_EMU_REGISTER=0x66, | ||
177 | READ_EMU_RAM=0x67, | ||
178 | WRITE_EMU_RAM=0x68, | ||
179 | READ_BQ_REGISTER=0x69, | ||
180 | WRITE_BQ_REGISTER=0x6A, | ||
181 | SET_USER_PASSWORD=0x70, | ||
182 | VERIFY_USER_PASSWORD=0x71, | ||
183 | GET_SYSTEM_PASSWORD_KEY=0x72, | ||
184 | VERIFY_SYSTEM_PASSWORD=0x73, | ||
185 | POWER_OFF=0x82, | ||
186 | POWER_RESTART=0x83, | ||
187 | }; | ||
188 | |||
189 | struct uctrl_driver { | ||
190 | struct uctrl_regs *regs; | ||
191 | int irq; | ||
192 | int pending; | ||
193 | struct uctrl_status status; | ||
194 | }; | ||
195 | |||
196 | static struct uctrl_driver drv; | ||
197 | |||
198 | void uctrl_get_event_status(void); | ||
199 | void uctrl_get_external_status(void); | ||
200 | |||
201 | static int | ||
202 | uctrl_ioctl(struct inode *inode, struct file *file, | ||
203 | unsigned int cmd, unsigned long arg) | ||
204 | { | ||
205 | switch (cmd) { | ||
206 | default: | ||
207 | return -EINVAL; | ||
208 | } | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static int | ||
213 | uctrl_open(struct inode *inode, struct file *file) | ||
214 | { | ||
215 | uctrl_get_event_status(); | ||
216 | uctrl_get_external_status(); | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static irqreturn_t uctrl_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
221 | { | ||
222 | struct uctrl_driver *driver = (struct uctrl_driver *)dev_id; | ||
223 | printk("in uctrl_interrupt\n"); | ||
224 | return IRQ_HANDLED; | ||
225 | } | ||
226 | |||
227 | static struct file_operations uctrl_fops = { | ||
228 | .owner = THIS_MODULE, | ||
229 | .llseek = no_llseek, | ||
230 | .ioctl = uctrl_ioctl, | ||
231 | .open = uctrl_open, | ||
232 | }; | ||
233 | |||
234 | static struct miscdevice uctrl_dev = { | ||
235 | UCTRL_MINOR, | ||
236 | "uctrl", | ||
237 | &uctrl_fops | ||
238 | }; | ||
239 | |||
240 | /* Wait for space to write, then write to it */ | ||
241 | #define WRITEUCTLDATA(value) \ | ||
242 | { \ | ||
243 | unsigned int i; \ | ||
244 | for (i = 0; i < 10000; i++) { \ | ||
245 | if (UCTRL_STAT_TXNF_STA & driver->regs->uctrl_stat) \ | ||
246 | break; \ | ||
247 | } \ | ||
248 | dprintk(("write data 0x%02x\n", value)); \ | ||
249 | driver->regs->uctrl_data = value; \ | ||
250 | } | ||
251 | |||
252 | /* Wait for something to read, read it, then clear the bit */ | ||
253 | #define READUCTLDATA(value) \ | ||
254 | { \ | ||
255 | unsigned int i; \ | ||
256 | value = 0; \ | ||
257 | for (i = 0; i < 10000; i++) { \ | ||
258 | if ((UCTRL_STAT_RXNE_STA & driver->regs->uctrl_stat) == 0) \ | ||
259 | break; \ | ||
260 | udelay(1); \ | ||
261 | } \ | ||
262 | value = driver->regs->uctrl_data; \ | ||
263 | dprintk(("read data 0x%02x\n", value)); \ | ||
264 | driver->regs->uctrl_stat = UCTRL_STAT_RXNE_STA; \ | ||
265 | } | ||
266 | |||
267 | void uctrl_set_video(int status) | ||
268 | { | ||
269 | struct uctrl_driver *driver = &drv; | ||
270 | |||
271 | } | ||
272 | |||
273 | static void uctrl_do_txn(struct uctrl_txn *txn) | ||
274 | { | ||
275 | struct uctrl_driver *driver = &drv; | ||
276 | int stat, incnt, outcnt, bytecnt, intr; | ||
277 | u32 byte; | ||
278 | |||
279 | stat = driver->regs->uctrl_stat; | ||
280 | intr = driver->regs->uctrl_intr; | ||
281 | driver->regs->uctrl_stat = stat; | ||
282 | |||
283 | dprintk(("interrupt stat 0x%x int 0x%x\n", stat, intr)); | ||
284 | |||
285 | incnt = txn->inbits; | ||
286 | outcnt = txn->outbits; | ||
287 | byte = (txn->opcode << 8); | ||
288 | WRITEUCTLDATA(byte); | ||
289 | |||
290 | bytecnt = 0; | ||
291 | while (incnt > 0) { | ||
292 | byte = (txn->inbuf[bytecnt] << 8); | ||
293 | WRITEUCTLDATA(byte); | ||
294 | incnt--; | ||
295 | bytecnt++; | ||
296 | } | ||
297 | |||
298 | /* Get the ack */ | ||
299 | READUCTLDATA(byte); | ||
300 | dprintk(("ack was %x\n", (byte >> 8))); | ||
301 | |||
302 | bytecnt = 0; | ||
303 | while (outcnt > 0) { | ||
304 | READUCTLDATA(byte); | ||
305 | txn->outbuf[bytecnt] = (byte >> 8); | ||
306 | dprintk(("set byte to %02x\n", byte)); | ||
307 | outcnt--; | ||
308 | bytecnt++; | ||
309 | } | ||
310 | } | ||
311 | |||
312 | void uctrl_get_event_status() | ||
313 | { | ||
314 | struct uctrl_driver *driver = &drv; | ||
315 | struct uctrl_txn txn; | ||
316 | u8 outbits[2]; | ||
317 | |||
318 | txn.opcode = READ_EVENT_STATUS; | ||
319 | txn.inbits = 0; | ||
320 | txn.outbits = 2; | ||
321 | txn.inbuf = 0; | ||
322 | txn.outbuf = outbits; | ||
323 | |||
324 | uctrl_do_txn(&txn); | ||
325 | |||
326 | dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff))); | ||
327 | driver->status.event_status = | ||
328 | ((outbits[0] & 0xff) << 8) | (outbits[1] & 0xff); | ||
329 | dprintk(("ev is %x\n", driver->status.event_status)); | ||
330 | } | ||
331 | |||
332 | void uctrl_get_external_status() | ||
333 | { | ||
334 | struct uctrl_driver *driver = &drv; | ||
335 | struct uctrl_txn txn; | ||
336 | u8 outbits[2]; | ||
337 | int i, v; | ||
338 | |||
339 | txn.opcode = READ_EXTERNAL_STATUS; | ||
340 | txn.inbits = 0; | ||
341 | txn.outbits = 2; | ||
342 | txn.inbuf = 0; | ||
343 | txn.outbuf = outbits; | ||
344 | |||
345 | uctrl_do_txn(&txn); | ||
346 | |||
347 | dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff))); | ||
348 | driver->status.external_status = | ||
349 | ((outbits[0] * 256) + (outbits[1])); | ||
350 | dprintk(("ex is %x\n", driver->status.external_status)); | ||
351 | v = driver->status.external_status; | ||
352 | for (i = 0; v != 0; i++, v >>= 1) { | ||
353 | if (v & 1) { | ||
354 | dprintk(("%s%s", " ", uctrl_extstatus[i])); | ||
355 | } | ||
356 | } | ||
357 | dprintk(("\n")); | ||
358 | |||
359 | } | ||
360 | |||
361 | static int __init ts102_uctrl_init(void) | ||
362 | { | ||
363 | struct uctrl_driver *driver = &drv; | ||
364 | int len, i; | ||
365 | struct linux_prom_irqs tmp_irq[2]; | ||
366 | unsigned int vaddr[2] = { 0, 0 }; | ||
367 | int tmpnode, uctrlnode = prom_getchild(prom_root_node); | ||
368 | |||
369 | tmpnode = prom_searchsiblings(uctrlnode, "obio"); | ||
370 | |||
371 | if (tmpnode) | ||
372 | uctrlnode = prom_getchild(tmpnode); | ||
373 | |||
374 | uctrlnode = prom_searchsiblings(uctrlnode, "uctrl"); | ||
375 | |||
376 | if (!uctrlnode) | ||
377 | return -ENODEV; | ||
378 | |||
379 | /* the prom mapped it for us */ | ||
380 | len = prom_getproperty(uctrlnode, "address", (void *) vaddr, | ||
381 | sizeof(vaddr)); | ||
382 | driver->regs = (struct uctrl_regs *)vaddr[0]; | ||
383 | |||
384 | len = prom_getproperty(uctrlnode, "intr", (char *) tmp_irq, | ||
385 | sizeof(tmp_irq)); | ||
386 | |||
387 | /* Flush device */ | ||
388 | READUCTLDATA(len); | ||
389 | |||
390 | if(!driver->irq) | ||
391 | driver->irq = tmp_irq[0].pri; | ||
392 | |||
393 | request_irq(driver->irq, uctrl_interrupt, 0, "uctrl", driver); | ||
394 | |||
395 | if (misc_register(&uctrl_dev)) { | ||
396 | printk("%s: unable to get misc minor %d\n", | ||
397 | __FUNCTION__, uctrl_dev.minor); | ||
398 | free_irq(driver->irq, driver); | ||
399 | return -ENODEV; | ||
400 | } | ||
401 | |||
402 | driver->regs->uctrl_intr = UCTRL_INTR_RXNE_REQ|UCTRL_INTR_RXNE_MSK; | ||
403 | printk("uctrl: 0x%x (irq %s)\n", driver->regs, __irq_itoa(driver->irq)); | ||
404 | uctrl_get_event_status(); | ||
405 | uctrl_get_external_status(); | ||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | static void __exit ts102_uctrl_cleanup(void) | ||
410 | { | ||
411 | struct uctrl_driver *driver = &drv; | ||
412 | |||
413 | misc_deregister(&uctrl_dev); | ||
414 | if (driver->irq) | ||
415 | free_irq(driver->irq, driver); | ||
416 | if (driver->regs) | ||
417 | driver->regs = 0; | ||
418 | } | ||
419 | |||
420 | module_init(ts102_uctrl_init); | ||
421 | module_exit(ts102_uctrl_cleanup); | ||
422 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/sbus/char/vfc.h b/drivers/sbus/char/vfc.h new file mode 100644 index 00000000000..e56a43af0f6 --- /dev/null +++ b/drivers/sbus/char/vfc.h | |||
@@ -0,0 +1,179 @@ | |||
1 | #ifndef _LINUX_VFC_H_ | ||
2 | #define _LINUX_VFC_H_ | ||
3 | |||
4 | #include <linux/devfs_fs_kernel.h> | ||
5 | |||
6 | /* | ||
7 | * The control register for the vfc is at offset 0x4000 | ||
8 | * The first field ram bank is located at offset 0x5000 | ||
9 | * The second field ram bank is at offset 0x7000 | ||
10 | * i2c_reg address the Phillips PCF8584(see notes in vfc_i2c.c) | ||
11 | * data and transmit register. | ||
12 | * i2c_s1 controls register s1 of the PCF8584 | ||
13 | * i2c_write seems to be similar to i2c_write but I am not | ||
14 | * quite sure why sun uses it | ||
15 | * | ||
16 | * I am also not sure whether or not you can read the fram bank as a | ||
17 | * whole or whether you must read each word individually from offset | ||
18 | * 0x5000 as soon as I figure it out I will update this file */ | ||
19 | |||
20 | struct vfc_regs { | ||
21 | char pad1[0x4000]; | ||
22 | unsigned int control; /* Offset 0x4000 */ | ||
23 | char pad2[0xffb]; /* from offset 0x4004 to 0x5000 */ | ||
24 | unsigned int fram_bank1; /* Offset 0x5000 */ | ||
25 | char pad3[0xffb]; /* from offset 0x5004 to 0x6000 */ | ||
26 | unsigned int i2c_reg; /* Offset 0x6000 */ | ||
27 | unsigned int i2c_magic2; /* Offset 0x6004 */ | ||
28 | unsigned int i2c_s1; /* Offset 0x6008 */ | ||
29 | unsigned int i2c_write; /* Offset 0x600c */ | ||
30 | char pad4[0xff0]; /* from offset 0x6010 to 0x7000 */ | ||
31 | unsigned int fram_bank2; /* Offset 0x7000 */ | ||
32 | char pad5[0x1000]; | ||
33 | }; | ||
34 | |||
35 | #define VFC_SAA9051_NR (13) | ||
36 | #define VFC_SAA9051_ADDR (0x8a) | ||
37 | /* The saa9051 returns the following for its status | ||
38 | * bit 0 - 0 | ||
39 | * bit 1 - SECAM color detected (1=found,0=not found) | ||
40 | * bit 2 - COLOR detected (1=found,0=not found) | ||
41 | * bit 3 - 0 | ||
42 | * bit 4 - Field frequency bit (1=60Hz (NTSC), 0=50Hz (PAL)) | ||
43 | * bit 5 - 1 | ||
44 | * bit 6 - horizontal frequency lock (1=transmitter found, | ||
45 | * 0=no transmitter) | ||
46 | * bit 7 - Power on reset bit (1=reset,0=at least one successful | ||
47 | * read of the status byte) | ||
48 | */ | ||
49 | |||
50 | #define VFC_SAA9051_PONRES (0x80) | ||
51 | #define VFC_SAA9051_HLOCK (0x40) | ||
52 | #define VFC_SAA9051_FD (0x10) | ||
53 | #define VFC_SAA9051_CD (0x04) | ||
54 | #define VFC_SAA9051_CS (0x02) | ||
55 | |||
56 | |||
57 | /* The various saa9051 sub addresses */ | ||
58 | |||
59 | #define VFC_SAA9051_IDEL (0) | ||
60 | #define VFC_SAA9051_HSY_START (1) | ||
61 | #define VFC_SAA9051_HSY_STOP (2) | ||
62 | #define VFC_SAA9051_HC_START (3) | ||
63 | #define VFC_SAA9051_HC_STOP (4) | ||
64 | #define VFC_SAA9051_HS_START (5) | ||
65 | #define VFC_SAA9051_HORIZ_PEAK (6) | ||
66 | #define VFC_SAA9051_HUE (7) | ||
67 | #define VFC_SAA9051_C1 (8) | ||
68 | #define VFC_SAA9051_C2 (9) | ||
69 | #define VFC_SAA9051_C3 (0xa) | ||
70 | #define VFC_SAA9051_SECAM_DELAY (0xb) | ||
71 | |||
72 | |||
73 | /* Bit settings for saa9051 sub address 0x06 */ | ||
74 | |||
75 | #define VFC_SAA9051_AP1 (0x01) | ||
76 | #define VFC_SAA9051_AP2 (0x02) | ||
77 | #define VFC_SAA9051_COR1 (0x04) | ||
78 | #define VFC_SAA9051_COR2 (0x08) | ||
79 | #define VFC_SAA9051_BP1 (0x10) | ||
80 | #define VFC_SAA9051_BP2 (0x20) | ||
81 | #define VFC_SAA9051_PF (0x40) | ||
82 | #define VFC_SAA9051_BY (0x80) | ||
83 | |||
84 | |||
85 | /* Bit settings for saa9051 sub address 0x08 */ | ||
86 | |||
87 | #define VFC_SAA9051_CCFR0 (0x01) | ||
88 | #define VFC_SAA9051_CCFR1 (0x02) | ||
89 | #define VFC_SAA9051_YPN (0x04) | ||
90 | #define VFC_SAA9051_ALT (0x08) | ||
91 | #define VFC_SAA9051_CO (0x10) | ||
92 | #define VFC_SAA9051_VTR (0x20) | ||
93 | #define VFC_SAA9051_FS (0x40) | ||
94 | #define VFC_SAA9051_HPLL (0x80) | ||
95 | |||
96 | |||
97 | /* Bit settings for saa9051 sub address 9 */ | ||
98 | |||
99 | #define VFC_SAA9051_SS0 (0x01) | ||
100 | #define VFC_SAA9051_SS1 (0x02) | ||
101 | #define VFC_SAA9051_AFCC (0x04) | ||
102 | #define VFC_SAA9051_CI (0x08) | ||
103 | #define VFC_SAA9051_SA9D4 (0x10) /* Don't care bit */ | ||
104 | #define VFC_SAA9051_OEC (0x20) | ||
105 | #define VFC_SAA9051_OEY (0x40) | ||
106 | #define VFC_SAA9051_VNL (0x80) | ||
107 | |||
108 | |||
109 | /* Bit settings for saa9051 sub address 0x0A */ | ||
110 | |||
111 | #define VFC_SAA9051_YDL0 (0x01) | ||
112 | #define VFC_SAA9051_YDL1 (0x02) | ||
113 | #define VFC_SAA9051_YDL2 (0x04) | ||
114 | #define VFC_SAA9051_SS2 (0x08) | ||
115 | #define VFC_SAA9051_SS3 (0x10) | ||
116 | #define VFC_SAA9051_YC (0x20) | ||
117 | #define VFC_SAA9051_CT (0x40) | ||
118 | #define VFC_SAA9051_SYC (0x80) | ||
119 | |||
120 | |||
121 | #define VFC_SAA9051_SA(a,b) ((a)->saa9051_state_array[(b)+1]) | ||
122 | #define vfc_update_saa9051(a) (vfc_i2c_sendbuf((a),VFC_SAA9051_ADDR,\ | ||
123 | (a)->saa9051_state_array,\ | ||
124 | VFC_SAA9051_NR)) | ||
125 | |||
126 | |||
127 | struct vfc_dev { | ||
128 | volatile struct vfc_regs *regs; | ||
129 | struct vfc_regs *phys_regs; | ||
130 | unsigned int control_reg; | ||
131 | struct semaphore device_lock_sem; | ||
132 | struct timer_list poll_timer; | ||
133 | wait_queue_head_t poll_wait; | ||
134 | int instance; | ||
135 | int busy; | ||
136 | unsigned long which_io; | ||
137 | unsigned char saa9051_state_array[VFC_SAA9051_NR]; | ||
138 | }; | ||
139 | |||
140 | extern struct vfc_dev **vfc_dev_lst; | ||
141 | |||
142 | void captstat_reset(struct vfc_dev *); | ||
143 | void memptr_reset(struct vfc_dev *); | ||
144 | |||
145 | int vfc_pcf8584_init(struct vfc_dev *); | ||
146 | void vfc_i2c_delay_no_busy(struct vfc_dev *, unsigned long); | ||
147 | void vfc_i2c_delay(struct vfc_dev *); | ||
148 | int vfc_i2c_sendbuf(struct vfc_dev *, unsigned char, char *, int) ; | ||
149 | int vfc_i2c_recvbuf(struct vfc_dev *, unsigned char, char *, int) ; | ||
150 | int vfc_i2c_reset_bus(struct vfc_dev *); | ||
151 | int vfc_init_i2c_bus(struct vfc_dev *); | ||
152 | void vfc_lock_device(struct vfc_dev *); | ||
153 | void vfc_unlock_device(struct vfc_dev *); | ||
154 | |||
155 | #define VFC_CONTROL_DIAGMODE 0x10000000 | ||
156 | #define VFC_CONTROL_MEMPTR 0x20000000 | ||
157 | #define VFC_CONTROL_CAPTURE 0x02000000 | ||
158 | #define VFC_CONTROL_CAPTRESET 0x04000000 | ||
159 | |||
160 | #define VFC_STATUS_CAPTURE 0x08000000 | ||
161 | |||
162 | #ifdef VFC_IOCTL_DEBUG | ||
163 | #define VFC_IOCTL_DEBUG_PRINTK(a) printk a | ||
164 | #else | ||
165 | #define VFC_IOCTL_DEBUG_PRINTK(a) | ||
166 | #endif | ||
167 | |||
168 | #ifdef VFC_I2C_DEBUG | ||
169 | #define VFC_I2C_DEBUG_PRINTK(a) printk a | ||
170 | #else | ||
171 | #define VFC_I2C_DEBUG_PRINTK(a) | ||
172 | #endif | ||
173 | |||
174 | #endif /* _LINUX_VFC_H_ */ | ||
175 | |||
176 | |||
177 | |||
178 | |||
179 | |||
diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c new file mode 100644 index 00000000000..86ce5413095 --- /dev/null +++ b/drivers/sbus/char/vfc_dev.c | |||
@@ -0,0 +1,742 @@ | |||
1 | /* | ||
2 | * drivers/sbus/char/vfc_dev.c | ||
3 | * | ||
4 | * Driver for the Videopix Frame Grabber. | ||
5 | * | ||
6 | * In order to use the VFC you need to program the video controller | ||
7 | * chip. This chip is the Phillips SAA9051. You need to call their | ||
8 | * documentation ordering line to get the docs. | ||
9 | * | ||
10 | * There is very little documentation on the VFC itself. There is | ||
11 | * some useful info that can be found in the manuals that come with | ||
12 | * the card. I will hopefully write some better docs at a later date. | ||
13 | * | ||
14 | * Copyright (C) 1996 Manish Vachharajani (mvachhar@noc.rutgers.edu) | ||
15 | * */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/fs.h> | ||
24 | #include <linux/smp_lock.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/spinlock.h> | ||
27 | #include <linux/mm.h> | ||
28 | |||
29 | #include <asm/openprom.h> | ||
30 | #include <asm/oplib.h> | ||
31 | #include <asm/io.h> | ||
32 | #include <asm/system.h> | ||
33 | #include <asm/sbus.h> | ||
34 | #include <asm/page.h> | ||
35 | #include <asm/pgtable.h> | ||
36 | #include <asm/uaccess.h> | ||
37 | |||
38 | #define VFC_MAJOR (60) | ||
39 | |||
40 | #if 0 | ||
41 | #define VFC_IOCTL_DEBUG | ||
42 | #endif | ||
43 | |||
44 | #include "vfc.h" | ||
45 | #include <asm/vfc_ioctls.h> | ||
46 | |||
47 | static struct file_operations vfc_fops; | ||
48 | struct vfc_dev **vfc_dev_lst; | ||
49 | static char vfcstr[]="vfc"; | ||
50 | static unsigned char saa9051_init_array[VFC_SAA9051_NR] = { | ||
51 | 0x00, 0x64, 0x72, 0x52, | ||
52 | 0x36, 0x18, 0xff, 0x20, | ||
53 | 0xfc, 0x77, 0xe3, 0x50, | ||
54 | 0x3e | ||
55 | }; | ||
56 | |||
57 | void vfc_lock_device(struct vfc_dev *dev) | ||
58 | { | ||
59 | down(&dev->device_lock_sem); | ||
60 | } | ||
61 | |||
62 | void vfc_unlock_device(struct vfc_dev *dev) | ||
63 | { | ||
64 | up(&dev->device_lock_sem); | ||
65 | } | ||
66 | |||
67 | |||
68 | void vfc_captstat_reset(struct vfc_dev *dev) | ||
69 | { | ||
70 | dev->control_reg |= VFC_CONTROL_CAPTRESET; | ||
71 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
72 | dev->control_reg &= ~VFC_CONTROL_CAPTRESET; | ||
73 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
74 | dev->control_reg |= VFC_CONTROL_CAPTRESET; | ||
75 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
76 | } | ||
77 | |||
78 | void vfc_memptr_reset(struct vfc_dev *dev) | ||
79 | { | ||
80 | dev->control_reg |= VFC_CONTROL_MEMPTR; | ||
81 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
82 | dev->control_reg &= ~VFC_CONTROL_MEMPTR; | ||
83 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
84 | dev->control_reg |= VFC_CONTROL_MEMPTR; | ||
85 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
86 | } | ||
87 | |||
88 | int vfc_csr_init(struct vfc_dev *dev) | ||
89 | { | ||
90 | dev->control_reg = 0x80000000; | ||
91 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
92 | udelay(200); | ||
93 | dev->control_reg &= ~0x80000000; | ||
94 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
95 | udelay(100); | ||
96 | sbus_writel(0x0f000000, &dev->regs->i2c_magic2); | ||
97 | |||
98 | vfc_memptr_reset(dev); | ||
99 | |||
100 | dev->control_reg &= ~VFC_CONTROL_DIAGMODE; | ||
101 | dev->control_reg &= ~VFC_CONTROL_CAPTURE; | ||
102 | dev->control_reg |= 0x40000000; | ||
103 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
104 | |||
105 | vfc_captstat_reset(dev); | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | int vfc_saa9051_init(struct vfc_dev *dev) | ||
111 | { | ||
112 | int i; | ||
113 | |||
114 | for (i = 0; i < VFC_SAA9051_NR; i++) | ||
115 | dev->saa9051_state_array[i] = saa9051_init_array[i]; | ||
116 | |||
117 | vfc_i2c_sendbuf(dev,VFC_SAA9051_ADDR, | ||
118 | dev->saa9051_state_array, VFC_SAA9051_NR); | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | int init_vfc_hw(struct vfc_dev *dev) | ||
123 | { | ||
124 | vfc_lock_device(dev); | ||
125 | vfc_csr_init(dev); | ||
126 | |||
127 | vfc_pcf8584_init(dev); | ||
128 | vfc_init_i2c_bus(dev); /* hopefully this doesn't undo the magic | ||
129 | sun code above*/ | ||
130 | vfc_saa9051_init(dev); | ||
131 | vfc_unlock_device(dev); | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | int init_vfc_devstruct(struct vfc_dev *dev, int instance) | ||
136 | { | ||
137 | dev->instance=instance; | ||
138 | init_MUTEX(&dev->device_lock_sem); | ||
139 | dev->control_reg=0; | ||
140 | init_waitqueue_head(&dev->poll_wait); | ||
141 | dev->busy=0; | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | int init_vfc_device(struct sbus_dev *sdev,struct vfc_dev *dev, int instance) | ||
146 | { | ||
147 | if(dev == NULL) { | ||
148 | printk(KERN_ERR "VFC: Bogus pointer passed\n"); | ||
149 | return -ENOMEM; | ||
150 | } | ||
151 | printk("Initializing vfc%d\n",instance); | ||
152 | dev->regs = NULL; | ||
153 | dev->regs = (volatile struct vfc_regs *) | ||
154 | sbus_ioremap(&sdev->resource[0], 0, | ||
155 | sizeof(struct vfc_regs), vfcstr); | ||
156 | dev->which_io = sdev->reg_addrs[0].which_io; | ||
157 | dev->phys_regs = (struct vfc_regs *) sdev->reg_addrs[0].phys_addr; | ||
158 | if (dev->regs == NULL) | ||
159 | return -EIO; | ||
160 | |||
161 | printk("vfc%d: registers mapped at phys_addr: 0x%lx\n virt_addr: 0x%lx\n", | ||
162 | instance,(unsigned long)sdev->reg_addrs[0].phys_addr,(unsigned long)dev->regs); | ||
163 | |||
164 | if (init_vfc_devstruct(dev, instance)) | ||
165 | return -EINVAL; | ||
166 | if (init_vfc_hw(dev)) | ||
167 | return -EIO; | ||
168 | |||
169 | devfs_mk_cdev(MKDEV(VFC_MAJOR, instance), | ||
170 | S_IFCHR | S_IRUSR | S_IWUSR, | ||
171 | "vfc/%d", instance); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | |||
176 | struct vfc_dev *vfc_get_dev_ptr(int instance) | ||
177 | { | ||
178 | return vfc_dev_lst[instance]; | ||
179 | } | ||
180 | |||
181 | static DEFINE_SPINLOCK(vfc_dev_lock); | ||
182 | |||
183 | static int vfc_open(struct inode *inode, struct file *file) | ||
184 | { | ||
185 | struct vfc_dev *dev; | ||
186 | |||
187 | spin_lock(&vfc_dev_lock); | ||
188 | dev = vfc_get_dev_ptr(iminor(inode)); | ||
189 | if (dev == NULL) { | ||
190 | spin_unlock(&vfc_dev_lock); | ||
191 | return -ENODEV; | ||
192 | } | ||
193 | if (dev->busy) { | ||
194 | spin_unlock(&vfc_dev_lock); | ||
195 | return -EBUSY; | ||
196 | } | ||
197 | |||
198 | dev->busy = 1; | ||
199 | spin_unlock(&vfc_dev_lock); | ||
200 | |||
201 | vfc_lock_device(dev); | ||
202 | |||
203 | vfc_csr_init(dev); | ||
204 | vfc_pcf8584_init(dev); | ||
205 | vfc_init_i2c_bus(dev); | ||
206 | vfc_saa9051_init(dev); | ||
207 | vfc_memptr_reset(dev); | ||
208 | vfc_captstat_reset(dev); | ||
209 | |||
210 | vfc_unlock_device(dev); | ||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static int vfc_release(struct inode *inode,struct file *file) | ||
215 | { | ||
216 | struct vfc_dev *dev; | ||
217 | |||
218 | spin_lock(&vfc_dev_lock); | ||
219 | dev = vfc_get_dev_ptr(iminor(inode)); | ||
220 | if (!dev || !dev->busy) { | ||
221 | spin_unlock(&vfc_dev_lock); | ||
222 | return -EINVAL; | ||
223 | } | ||
224 | dev->busy = 0; | ||
225 | spin_unlock(&vfc_dev_lock); | ||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static int vfc_debug(struct vfc_dev *dev, int cmd, void __user *argp) | ||
230 | { | ||
231 | struct vfc_debug_inout inout; | ||
232 | unsigned char *buffer; | ||
233 | |||
234 | if (!capable(CAP_SYS_ADMIN)) | ||
235 | return -EPERM; | ||
236 | |||
237 | switch(cmd) { | ||
238 | case VFC_I2C_SEND: | ||
239 | if(copy_from_user(&inout, argp, sizeof(inout))) | ||
240 | return -EFAULT; | ||
241 | |||
242 | buffer = kmalloc(inout.len, GFP_KERNEL); | ||
243 | if (buffer == NULL) | ||
244 | return -ENOMEM; | ||
245 | |||
246 | if(copy_from_user(buffer, inout.buffer, inout.len)) { | ||
247 | kfree(buffer); | ||
248 | return -EFAULT; | ||
249 | } | ||
250 | |||
251 | |||
252 | vfc_lock_device(dev); | ||
253 | inout.ret= | ||
254 | vfc_i2c_sendbuf(dev,inout.addr & 0xff, | ||
255 | buffer,inout.len); | ||
256 | |||
257 | if (copy_to_user(argp,&inout,sizeof(inout))) { | ||
258 | kfree(buffer); | ||
259 | return -EFAULT; | ||
260 | } | ||
261 | vfc_unlock_device(dev); | ||
262 | |||
263 | break; | ||
264 | case VFC_I2C_RECV: | ||
265 | if (copy_from_user(&inout, argp, sizeof(inout))) | ||
266 | return -EFAULT; | ||
267 | |||
268 | buffer = kmalloc(inout.len, GFP_KERNEL); | ||
269 | if (buffer == NULL) | ||
270 | return -ENOMEM; | ||
271 | |||
272 | memset(buffer,0,inout.len); | ||
273 | vfc_lock_device(dev); | ||
274 | inout.ret= | ||
275 | vfc_i2c_recvbuf(dev,inout.addr & 0xff | ||
276 | ,buffer,inout.len); | ||
277 | vfc_unlock_device(dev); | ||
278 | |||
279 | if (copy_to_user(inout.buffer, buffer, inout.len)) { | ||
280 | kfree(buffer); | ||
281 | return -EFAULT; | ||
282 | } | ||
283 | if (copy_to_user(argp,&inout,sizeof(inout))) { | ||
284 | kfree(buffer); | ||
285 | return -EFAULT; | ||
286 | } | ||
287 | kfree(buffer); | ||
288 | break; | ||
289 | default: | ||
290 | return -EINVAL; | ||
291 | }; | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | int vfc_capture_start(struct vfc_dev *dev) | ||
297 | { | ||
298 | vfc_captstat_reset(dev); | ||
299 | dev->control_reg = sbus_readl(&dev->regs->control); | ||
300 | if((dev->control_reg & VFC_STATUS_CAPTURE)) { | ||
301 | printk(KERN_ERR "vfc%d: vfc capture status not reset\n", | ||
302 | dev->instance); | ||
303 | return -EIO; | ||
304 | } | ||
305 | |||
306 | vfc_lock_device(dev); | ||
307 | dev->control_reg &= ~VFC_CONTROL_CAPTURE; | ||
308 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
309 | dev->control_reg |= VFC_CONTROL_CAPTURE; | ||
310 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
311 | dev->control_reg &= ~VFC_CONTROL_CAPTURE; | ||
312 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
313 | vfc_unlock_device(dev); | ||
314 | |||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | int vfc_capture_poll(struct vfc_dev *dev) | ||
319 | { | ||
320 | int timeout = 1000; | ||
321 | |||
322 | while (!timeout--) { | ||
323 | if (dev->regs->control & VFC_STATUS_CAPTURE) | ||
324 | break; | ||
325 | vfc_i2c_delay_no_busy(dev, 100); | ||
326 | } | ||
327 | if(!timeout) { | ||
328 | printk(KERN_WARNING "vfc%d: capture timed out\n", | ||
329 | dev->instance); | ||
330 | return -ETIMEDOUT; | ||
331 | } | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | |||
336 | |||
337 | static int vfc_set_control_ioctl(struct inode *inode, struct file *file, | ||
338 | struct vfc_dev *dev, unsigned long arg) | ||
339 | { | ||
340 | int setcmd, ret = 0; | ||
341 | |||
342 | if (copy_from_user(&setcmd,(void __user *)arg,sizeof(unsigned int))) | ||
343 | return -EFAULT; | ||
344 | |||
345 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCSCTRL) arg=0x%x\n", | ||
346 | dev->instance,setcmd)); | ||
347 | |||
348 | switch(setcmd) { | ||
349 | case MEMPRST: | ||
350 | vfc_lock_device(dev); | ||
351 | vfc_memptr_reset(dev); | ||
352 | vfc_unlock_device(dev); | ||
353 | ret=0; | ||
354 | break; | ||
355 | case CAPTRCMD: | ||
356 | vfc_capture_start(dev); | ||
357 | vfc_capture_poll(dev); | ||
358 | break; | ||
359 | case DIAGMODE: | ||
360 | if(capable(CAP_SYS_ADMIN)) { | ||
361 | vfc_lock_device(dev); | ||
362 | dev->control_reg |= VFC_CONTROL_DIAGMODE; | ||
363 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
364 | vfc_unlock_device(dev); | ||
365 | ret = 0; | ||
366 | } else { | ||
367 | ret = -EPERM; | ||
368 | } | ||
369 | break; | ||
370 | case NORMMODE: | ||
371 | vfc_lock_device(dev); | ||
372 | dev->control_reg &= ~VFC_CONTROL_DIAGMODE; | ||
373 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
374 | vfc_unlock_device(dev); | ||
375 | ret = 0; | ||
376 | break; | ||
377 | case CAPTRSTR: | ||
378 | vfc_capture_start(dev); | ||
379 | ret = 0; | ||
380 | break; | ||
381 | case CAPTRWAIT: | ||
382 | vfc_capture_poll(dev); | ||
383 | ret = 0; | ||
384 | break; | ||
385 | default: | ||
386 | ret = -EINVAL; | ||
387 | break; | ||
388 | }; | ||
389 | |||
390 | return ret; | ||
391 | } | ||
392 | |||
393 | |||
394 | int vfc_port_change_ioctl(struct inode *inode, struct file *file, | ||
395 | struct vfc_dev *dev, unsigned long arg) | ||
396 | { | ||
397 | int ret = 0; | ||
398 | int cmd; | ||
399 | |||
400 | if(copy_from_user(&cmd, (void __user *)arg, sizeof(unsigned int))) { | ||
401 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to " | ||
402 | "vfc_port_change_ioctl\n", | ||
403 | dev->instance)); | ||
404 | return -EFAULT; | ||
405 | } | ||
406 | |||
407 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCPORTCHG) arg=0x%x\n", | ||
408 | dev->instance, cmd)); | ||
409 | |||
410 | switch(cmd) { | ||
411 | case 1: | ||
412 | case 2: | ||
413 | VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_START) = 0x72; | ||
414 | VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_STOP) = 0x52; | ||
415 | VFC_SAA9051_SA(dev,VFC_SAA9051_HC_START) = 0x36; | ||
416 | VFC_SAA9051_SA(dev,VFC_SAA9051_HC_STOP) = 0x18; | ||
417 | VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) = VFC_SAA9051_BP2; | ||
418 | VFC_SAA9051_SA(dev,VFC_SAA9051_C3) = VFC_SAA9051_CT | VFC_SAA9051_SS3; | ||
419 | VFC_SAA9051_SA(dev,VFC_SAA9051_SECAM_DELAY) = 0x3e; | ||
420 | break; | ||
421 | case 3: | ||
422 | VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_START) = 0x3a; | ||
423 | VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_STOP) = 0x17; | ||
424 | VFC_SAA9051_SA(dev,VFC_SAA9051_HC_START) = 0xfa; | ||
425 | VFC_SAA9051_SA(dev,VFC_SAA9051_HC_STOP) = 0xde; | ||
426 | VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) = | ||
427 | VFC_SAA9051_BY | VFC_SAA9051_PF | VFC_SAA9051_BP2; | ||
428 | VFC_SAA9051_SA(dev,VFC_SAA9051_C3) = VFC_SAA9051_YC; | ||
429 | VFC_SAA9051_SA(dev,VFC_SAA9051_SECAM_DELAY) = 0; | ||
430 | VFC_SAA9051_SA(dev,VFC_SAA9051_C2) &= | ||
431 | ~(VFC_SAA9051_SS0 | VFC_SAA9051_SS1); | ||
432 | break; | ||
433 | default: | ||
434 | ret = -EINVAL; | ||
435 | return ret; | ||
436 | break; | ||
437 | } | ||
438 | |||
439 | switch(cmd) { | ||
440 | case 1: | ||
441 | VFC_SAA9051_SA(dev,VFC_SAA9051_C2) |= | ||
442 | (VFC_SAA9051_SS0 | VFC_SAA9051_SS1); | ||
443 | break; | ||
444 | case 2: | ||
445 | VFC_SAA9051_SA(dev,VFC_SAA9051_C2) &= | ||
446 | ~(VFC_SAA9051_SS0 | VFC_SAA9051_SS1); | ||
447 | VFC_SAA9051_SA(dev,VFC_SAA9051_C2) |= VFC_SAA9051_SS0; | ||
448 | break; | ||
449 | case 3: | ||
450 | break; | ||
451 | default: | ||
452 | ret = -EINVAL; | ||
453 | return ret; | ||
454 | break; | ||
455 | } | ||
456 | VFC_SAA9051_SA(dev,VFC_SAA9051_C3) &= ~(VFC_SAA9051_SS2); | ||
457 | ret=vfc_update_saa9051(dev); | ||
458 | udelay(500); | ||
459 | VFC_SAA9051_SA(dev,VFC_SAA9051_C3) |= (VFC_SAA9051_SS2); | ||
460 | ret=vfc_update_saa9051(dev); | ||
461 | return ret; | ||
462 | } | ||
463 | |||
464 | int vfc_set_video_ioctl(struct inode *inode, struct file *file, | ||
465 | struct vfc_dev *dev, unsigned long arg) | ||
466 | { | ||
467 | int ret = 0; | ||
468 | int cmd; | ||
469 | |||
470 | if(copy_from_user(&cmd, (void __user *)arg, sizeof(unsigned int))) { | ||
471 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to " | ||
472 | "vfc_set_video_ioctl\n", | ||
473 | dev->instance)); | ||
474 | return ret; | ||
475 | } | ||
476 | |||
477 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCSVID) arg=0x%x\n", | ||
478 | dev->instance, cmd)); | ||
479 | switch(cmd) { | ||
480 | case STD_NTSC: | ||
481 | VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~VFC_SAA9051_ALT; | ||
482 | VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_YPN | | ||
483 | VFC_SAA9051_CCFR0 | VFC_SAA9051_CCFR1 | VFC_SAA9051_FS; | ||
484 | ret = vfc_update_saa9051(dev); | ||
485 | break; | ||
486 | case STD_PAL: | ||
487 | VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~(VFC_SAA9051_YPN | | ||
488 | VFC_SAA9051_CCFR1 | | ||
489 | VFC_SAA9051_CCFR0 | | ||
490 | VFC_SAA9051_FS); | ||
491 | VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_ALT; | ||
492 | ret = vfc_update_saa9051(dev); | ||
493 | break; | ||
494 | |||
495 | case COLOR_ON: | ||
496 | VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_CO; | ||
497 | VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) &= | ||
498 | ~(VFC_SAA9051_BY | VFC_SAA9051_PF); | ||
499 | ret = vfc_update_saa9051(dev); | ||
500 | break; | ||
501 | case MONO: | ||
502 | VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~(VFC_SAA9051_CO); | ||
503 | VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) |= | ||
504 | (VFC_SAA9051_BY | VFC_SAA9051_PF); | ||
505 | ret = vfc_update_saa9051(dev); | ||
506 | break; | ||
507 | default: | ||
508 | ret = -EINVAL; | ||
509 | break; | ||
510 | }; | ||
511 | |||
512 | return ret; | ||
513 | } | ||
514 | |||
515 | int vfc_get_video_ioctl(struct inode *inode, struct file *file, | ||
516 | struct vfc_dev *dev, unsigned long arg) | ||
517 | { | ||
518 | int ret = 0; | ||
519 | unsigned int status = NO_LOCK; | ||
520 | unsigned char buf[1]; | ||
521 | |||
522 | if(vfc_i2c_recvbuf(dev, VFC_SAA9051_ADDR, buf, 1)) { | ||
523 | printk(KERN_ERR "vfc%d: Unable to get status\n", | ||
524 | dev->instance); | ||
525 | return -EIO; | ||
526 | } | ||
527 | |||
528 | if(buf[0] & VFC_SAA9051_HLOCK) { | ||
529 | status = NO_LOCK; | ||
530 | } else if(buf[0] & VFC_SAA9051_FD) { | ||
531 | if(buf[0] & VFC_SAA9051_CD) | ||
532 | status = NTSC_COLOR; | ||
533 | else | ||
534 | status = NTSC_NOCOLOR; | ||
535 | } else { | ||
536 | if(buf[0] & VFC_SAA9051_CD) | ||
537 | status = PAL_COLOR; | ||
538 | else | ||
539 | status = PAL_NOCOLOR; | ||
540 | } | ||
541 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCGVID) returning status 0x%x; " | ||
542 | "buf[0]=%x\n", dev->instance, status, buf[0])); | ||
543 | |||
544 | if (copy_to_user((void __user *)arg,&status,sizeof(unsigned int))) { | ||
545 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to " | ||
546 | "vfc_get_video_ioctl\n", | ||
547 | dev->instance)); | ||
548 | return ret; | ||
549 | } | ||
550 | return ret; | ||
551 | } | ||
552 | |||
553 | static int vfc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | ||
554 | unsigned long arg) | ||
555 | { | ||
556 | int ret = 0; | ||
557 | unsigned int tmp; | ||
558 | struct vfc_dev *dev; | ||
559 | void __user *argp = (void __user *)arg; | ||
560 | |||
561 | dev = vfc_get_dev_ptr(iminor(inode)); | ||
562 | if(dev == NULL) | ||
563 | return -ENODEV; | ||
564 | |||
565 | switch(cmd & 0x0000ffff) { | ||
566 | case VFCGCTRL: | ||
567 | #if 0 | ||
568 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCGCTRL)\n", dev->instance)); | ||
569 | #endif | ||
570 | tmp = sbus_readl(&dev->regs->control); | ||
571 | if(copy_to_user(argp, &tmp, sizeof(unsigned int))) { | ||
572 | ret = -EFAULT; | ||
573 | break; | ||
574 | } | ||
575 | ret = 0; | ||
576 | break; | ||
577 | case VFCSCTRL: | ||
578 | ret = vfc_set_control_ioctl(inode, file, dev, arg); | ||
579 | break; | ||
580 | case VFCGVID: | ||
581 | ret = vfc_get_video_ioctl(inode, file, dev, arg); | ||
582 | break; | ||
583 | case VFCSVID: | ||
584 | ret = vfc_set_video_ioctl(inode, file, dev, arg); | ||
585 | break; | ||
586 | case VFCHUE: | ||
587 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCHUE)\n", dev->instance)); | ||
588 | if(copy_from_user(&tmp,argp,sizeof(unsigned int))) { | ||
589 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer " | ||
590 | "to IOCTL(VFCHUE)", dev->instance)); | ||
591 | ret = -EFAULT; | ||
592 | } else { | ||
593 | VFC_SAA9051_SA(dev,VFC_SAA9051_HUE) = tmp; | ||
594 | vfc_update_saa9051(dev); | ||
595 | ret = 0; | ||
596 | } | ||
597 | break; | ||
598 | case VFCPORTCHG: | ||
599 | ret = vfc_port_change_ioctl(inode, file, dev, arg); | ||
600 | break; | ||
601 | case VFCRDINFO: | ||
602 | ret = -EINVAL; | ||
603 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCRDINFO)\n", dev->instance)); | ||
604 | break; | ||
605 | default: | ||
606 | ret = vfc_debug(vfc_get_dev_ptr(iminor(inode)), cmd, argp); | ||
607 | break; | ||
608 | }; | ||
609 | |||
610 | return ret; | ||
611 | } | ||
612 | |||
613 | static int vfc_mmap(struct file *file, struct vm_area_struct *vma) | ||
614 | { | ||
615 | unsigned int map_size, ret, map_offset; | ||
616 | struct vfc_dev *dev; | ||
617 | |||
618 | dev = vfc_get_dev_ptr(iminor(file->f_dentry->d_inode)); | ||
619 | if(dev == NULL) | ||
620 | return -ENODEV; | ||
621 | |||
622 | map_size = vma->vm_end - vma->vm_start; | ||
623 | if(map_size > sizeof(struct vfc_regs)) | ||
624 | map_size = sizeof(struct vfc_regs); | ||
625 | |||
626 | vma->vm_flags |= | ||
627 | (VM_SHM | VM_LOCKED | VM_IO | VM_MAYREAD | VM_MAYWRITE | VM_MAYSHARE); | ||
628 | map_offset = (unsigned int) (long)dev->phys_regs; | ||
629 | ret = io_remap_pfn_range(vma, vma->vm_start, | ||
630 | MK_IOSPACE_PFN(dev->which_io, | ||
631 | map_offset >> PAGE_SHIFT), | ||
632 | map_size, vma->vm_page_prot); | ||
633 | |||
634 | if(ret) | ||
635 | return -EAGAIN; | ||
636 | |||
637 | return 0; | ||
638 | } | ||
639 | |||
640 | |||
641 | static struct file_operations vfc_fops = { | ||
642 | .owner = THIS_MODULE, | ||
643 | .llseek = no_llseek, | ||
644 | .ioctl = vfc_ioctl, | ||
645 | .mmap = vfc_mmap, | ||
646 | .open = vfc_open, | ||
647 | .release = vfc_release, | ||
648 | }; | ||
649 | |||
650 | static int vfc_probe(void) | ||
651 | { | ||
652 | struct sbus_bus *sbus; | ||
653 | struct sbus_dev *sdev = NULL; | ||
654 | int ret; | ||
655 | int instance = 0, cards = 0; | ||
656 | |||
657 | for_all_sbusdev(sdev, sbus) { | ||
658 | if (strcmp(sdev->prom_name, "vfc") == 0) { | ||
659 | cards++; | ||
660 | continue; | ||
661 | } | ||
662 | } | ||
663 | |||
664 | if (!cards) | ||
665 | return -ENODEV; | ||
666 | |||
667 | vfc_dev_lst = (struct vfc_dev **)kmalloc(sizeof(struct vfc_dev *) * | ||
668 | (cards+1), | ||
669 | GFP_KERNEL); | ||
670 | if (vfc_dev_lst == NULL) | ||
671 | return -ENOMEM; | ||
672 | memset(vfc_dev_lst, 0, sizeof(struct vfc_dev *) * (cards + 1)); | ||
673 | vfc_dev_lst[cards] = NULL; | ||
674 | |||
675 | ret = register_chrdev(VFC_MAJOR, vfcstr, &vfc_fops); | ||
676 | if(ret) { | ||
677 | printk(KERN_ERR "Unable to get major number %d\n", VFC_MAJOR); | ||
678 | kfree(vfc_dev_lst); | ||
679 | return -EIO; | ||
680 | } | ||
681 | devfs_mk_dir("vfc"); | ||
682 | instance = 0; | ||
683 | for_all_sbusdev(sdev, sbus) { | ||
684 | if (strcmp(sdev->prom_name, "vfc") == 0) { | ||
685 | vfc_dev_lst[instance]=(struct vfc_dev *) | ||
686 | kmalloc(sizeof(struct vfc_dev), GFP_KERNEL); | ||
687 | if (vfc_dev_lst[instance] == NULL) | ||
688 | return -ENOMEM; | ||
689 | ret = init_vfc_device(sdev, | ||
690 | vfc_dev_lst[instance], | ||
691 | instance); | ||
692 | if(ret) { | ||
693 | printk(KERN_ERR "Unable to initialize" | ||
694 | " vfc%d device\n", | ||
695 | instance); | ||
696 | } else { | ||
697 | } | ||
698 | |||
699 | instance++; | ||
700 | continue; | ||
701 | } | ||
702 | } | ||
703 | |||
704 | return 0; | ||
705 | } | ||
706 | |||
707 | #ifdef MODULE | ||
708 | int init_module(void) | ||
709 | #else | ||
710 | int vfc_init(void) | ||
711 | #endif | ||
712 | { | ||
713 | return vfc_probe(); | ||
714 | } | ||
715 | |||
716 | #ifdef MODULE | ||
717 | static void deinit_vfc_device(struct vfc_dev *dev) | ||
718 | { | ||
719 | if(dev == NULL) | ||
720 | return; | ||
721 | devfs_remove("vfc/%d", dev->instance); | ||
722 | sbus_iounmap((unsigned long)dev->regs, sizeof(struct vfc_regs)); | ||
723 | kfree(dev); | ||
724 | } | ||
725 | |||
726 | void cleanup_module(void) | ||
727 | { | ||
728 | struct vfc_dev **devp; | ||
729 | |||
730 | unregister_chrdev(VFC_MAJOR,vfcstr); | ||
731 | |||
732 | for (devp = vfc_dev_lst; *devp; devp++) | ||
733 | deinit_vfc_device(*devp); | ||
734 | |||
735 | devfs_remove("vfc"); | ||
736 | kfree(vfc_dev_lst); | ||
737 | return; | ||
738 | } | ||
739 | #endif | ||
740 | |||
741 | MODULE_LICENSE("GPL"); | ||
742 | |||
diff --git a/drivers/sbus/char/vfc_i2c.c b/drivers/sbus/char/vfc_i2c.c new file mode 100644 index 00000000000..95e3cebf792 --- /dev/null +++ b/drivers/sbus/char/vfc_i2c.c | |||
@@ -0,0 +1,347 @@ | |||
1 | /* | ||
2 | * drivers/sbus/char/vfc_i2c.c | ||
3 | * | ||
4 | * Driver for the Videopix Frame Grabber. | ||
5 | * | ||
6 | * Functions that support the Phillips i2c(I squared C) bus on the vfc | ||
7 | * Documentation for the Phillips I2C bus can be found on the | ||
8 | * phillips home page | ||
9 | * | ||
10 | * Copyright (C) 1996 Manish Vachharajani (mvachhar@noc.rutgers.edu) | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | /* NOTE: It seems to me that the documentation regarding the | ||
15 | pcd8584t/pcf8584 does not show the correct way to address the i2c bus. | ||
16 | Based on the information on the I2C bus itself and the remainder of | ||
17 | the Phillips docs the following algorithims apper to be correct. I am | ||
18 | fairly certain that the flowcharts in the phillips docs are wrong. */ | ||
19 | |||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/string.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/errno.h> | ||
25 | #include <linux/sched.h> | ||
26 | #include <linux/wait.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <asm/openprom.h> | ||
29 | #include <asm/oplib.h> | ||
30 | #include <asm/io.h> | ||
31 | #include <asm/system.h> | ||
32 | #include <asm/sbus.h> | ||
33 | |||
34 | #if 0 | ||
35 | #define VFC_I2C_DEBUG | ||
36 | #endif | ||
37 | |||
38 | #include "vfc.h" | ||
39 | #include "vfc_i2c.h" | ||
40 | |||
41 | #define WRITE_S1(__val) \ | ||
42 | sbus_writel(__val, &dev->regs->i2c_s1) | ||
43 | #define WRITE_REG(__val) \ | ||
44 | sbus_writel(__val, &dev->regs->i2c_reg) | ||
45 | |||
46 | #define VFC_I2C_READ (0x1) | ||
47 | #define VFC_I2C_WRITE (0x0) | ||
48 | |||
49 | /****** | ||
50 | The i2c bus controller chip on the VFC is a pcd8584t, but | ||
51 | phillips claims it doesn't exist. As far as I can tell it is | ||
52 | identical to the PCF8584 so I treat it like it is the pcf8584. | ||
53 | |||
54 | NOTE: The pcf8584 only cares | ||
55 | about the msb of the word you feed it | ||
56 | *****/ | ||
57 | |||
58 | int vfc_pcf8584_init(struct vfc_dev *dev) | ||
59 | { | ||
60 | /* This will also choose register S0_OWN so we can set it. */ | ||
61 | WRITE_S1(RESET); | ||
62 | |||
63 | /* The pcf8584 shifts this value left one bit and uses | ||
64 | * it as its i2c bus address. | ||
65 | */ | ||
66 | WRITE_REG(0x55000000); | ||
67 | |||
68 | /* This will set the i2c bus at the same speed sun uses, | ||
69 | * and set another magic bit. | ||
70 | */ | ||
71 | WRITE_S1(SELECT(S2)); | ||
72 | WRITE_REG(0x14000000); | ||
73 | |||
74 | /* Enable the serial port, idle the i2c bus and set | ||
75 | * the data reg to s0. | ||
76 | */ | ||
77 | WRITE_S1(CLEAR_I2C_BUS); | ||
78 | udelay(100); | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | void vfc_i2c_delay_wakeup(struct vfc_dev *dev) | ||
83 | { | ||
84 | /* Used to profile code and eliminate too many delays */ | ||
85 | VFC_I2C_DEBUG_PRINTK(("vfc%d: Delaying\n", dev->instance)); | ||
86 | wake_up(&dev->poll_wait); | ||
87 | } | ||
88 | |||
89 | void vfc_i2c_delay_no_busy(struct vfc_dev *dev, unsigned long usecs) | ||
90 | { | ||
91 | init_timer(&dev->poll_timer); | ||
92 | dev->poll_timer.expires = jiffies + | ||
93 | ((unsigned long)usecs*(HZ))/1000000; | ||
94 | dev->poll_timer.data=(unsigned long)dev; | ||
95 | dev->poll_timer.function=(void *)(unsigned long)vfc_i2c_delay_wakeup; | ||
96 | add_timer(&dev->poll_timer); | ||
97 | sleep_on(&dev->poll_wait); | ||
98 | del_timer(&dev->poll_timer); | ||
99 | } | ||
100 | |||
101 | void inline vfc_i2c_delay(struct vfc_dev *dev) | ||
102 | { | ||
103 | vfc_i2c_delay_no_busy(dev, 100); | ||
104 | } | ||
105 | |||
106 | int vfc_init_i2c_bus(struct vfc_dev *dev) | ||
107 | { | ||
108 | WRITE_S1(ENABLE_SERIAL | SELECT(S0) | ACK); | ||
109 | vfc_i2c_reset_bus(dev); | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | int vfc_i2c_reset_bus(struct vfc_dev *dev) | ||
114 | { | ||
115 | VFC_I2C_DEBUG_PRINTK((KERN_DEBUG "vfc%d: Resetting the i2c bus\n", | ||
116 | dev->instance)); | ||
117 | if(dev == NULL) | ||
118 | return -EINVAL; | ||
119 | if(dev->regs == NULL) | ||
120 | return -EINVAL; | ||
121 | WRITE_S1(SEND_I2C_STOP); | ||
122 | WRITE_S1(SEND_I2C_STOP | ACK); | ||
123 | vfc_i2c_delay(dev); | ||
124 | WRITE_S1(CLEAR_I2C_BUS); | ||
125 | VFC_I2C_DEBUG_PRINTK((KERN_DEBUG "vfc%d: I2C status %x\n", | ||
126 | dev->instance, | ||
127 | sbus_readl(&dev->regs->i2c_s1))); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | int vfc_i2c_wait_for_bus(struct vfc_dev *dev) | ||
132 | { | ||
133 | int timeout = 1000; | ||
134 | |||
135 | while(!(sbus_readl(&dev->regs->i2c_s1) & BB)) { | ||
136 | if(!(timeout--)) | ||
137 | return -ETIMEDOUT; | ||
138 | vfc_i2c_delay(dev); | ||
139 | } | ||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | int vfc_i2c_wait_for_pin(struct vfc_dev *dev, int ack) | ||
144 | { | ||
145 | int timeout = 1000; | ||
146 | int s1; | ||
147 | |||
148 | while ((s1 = sbus_readl(&dev->regs->i2c_s1)) & PIN) { | ||
149 | if (!(timeout--)) | ||
150 | return -ETIMEDOUT; | ||
151 | vfc_i2c_delay(dev); | ||
152 | } | ||
153 | if (ack == VFC_I2C_ACK_CHECK) { | ||
154 | if(s1 & LRB) | ||
155 | return -EIO; | ||
156 | } | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | #define SHIFT(a) ((a) << 24) | ||
161 | int vfc_i2c_xmit_addr(struct vfc_dev *dev, unsigned char addr, char mode) | ||
162 | { | ||
163 | int ret, raddr; | ||
164 | #if 1 | ||
165 | WRITE_S1(SEND_I2C_STOP | ACK); | ||
166 | WRITE_S1(SELECT(S0) | ENABLE_SERIAL); | ||
167 | vfc_i2c_delay(dev); | ||
168 | #endif | ||
169 | |||
170 | switch(mode) { | ||
171 | case VFC_I2C_READ: | ||
172 | raddr = SHIFT(((unsigned int)addr | 0x1)); | ||
173 | WRITE_REG(raddr); | ||
174 | VFC_I2C_DEBUG_PRINTK(("vfc%d: receiving from i2c addr 0x%x\n", | ||
175 | dev->instance, addr | 0x1)); | ||
176 | break; | ||
177 | case VFC_I2C_WRITE: | ||
178 | raddr = SHIFT((unsigned int)addr & ~0x1); | ||
179 | WRITE_REG(raddr); | ||
180 | VFC_I2C_DEBUG_PRINTK(("vfc%d: sending to i2c addr 0x%x\n", | ||
181 | dev->instance, addr & ~0x1)); | ||
182 | break; | ||
183 | default: | ||
184 | return -EINVAL; | ||
185 | }; | ||
186 | |||
187 | WRITE_S1(SEND_I2C_START); | ||
188 | vfc_i2c_delay(dev); | ||
189 | ret = vfc_i2c_wait_for_pin(dev,VFC_I2C_ACK_CHECK); /* We wait | ||
190 | for the | ||
191 | i2c send | ||
192 | to finish | ||
193 | here but | ||
194 | Sun | ||
195 | doesn't, | ||
196 | hmm */ | ||
197 | if (ret) { | ||
198 | printk(KERN_ERR "vfc%d: VFC xmit addr timed out or no ack\n", | ||
199 | dev->instance); | ||
200 | return ret; | ||
201 | } else if (mode == VFC_I2C_READ) { | ||
202 | if ((ret = sbus_readl(&dev->regs->i2c_reg) & 0xff000000) != raddr) { | ||
203 | printk(KERN_WARNING | ||
204 | "vfc%d: returned slave address " | ||
205 | "mismatch(%x,%x)\n", | ||
206 | dev->instance, raddr, ret); | ||
207 | } | ||
208 | } | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | int vfc_i2c_xmit_byte(struct vfc_dev *dev,unsigned char *byte) | ||
213 | { | ||
214 | int ret; | ||
215 | u32 val = SHIFT((unsigned int)*byte); | ||
216 | |||
217 | WRITE_REG(val); | ||
218 | |||
219 | ret = vfc_i2c_wait_for_pin(dev, VFC_I2C_ACK_CHECK); | ||
220 | switch(ret) { | ||
221 | case -ETIMEDOUT: | ||
222 | printk(KERN_ERR "vfc%d: VFC xmit byte timed out or no ack\n", | ||
223 | dev->instance); | ||
224 | break; | ||
225 | case -EIO: | ||
226 | ret = XMIT_LAST_BYTE; | ||
227 | break; | ||
228 | default: | ||
229 | break; | ||
230 | }; | ||
231 | |||
232 | return ret; | ||
233 | } | ||
234 | |||
235 | int vfc_i2c_recv_byte(struct vfc_dev *dev, unsigned char *byte, int last) | ||
236 | { | ||
237 | int ret; | ||
238 | |||
239 | if (last) { | ||
240 | WRITE_REG(NEGATIVE_ACK); | ||
241 | VFC_I2C_DEBUG_PRINTK(("vfc%d: sending negative ack\n", | ||
242 | dev->instance)); | ||
243 | } else { | ||
244 | WRITE_S1(ACK); | ||
245 | } | ||
246 | |||
247 | ret = vfc_i2c_wait_for_pin(dev, VFC_I2C_NO_ACK_CHECK); | ||
248 | if(ret) { | ||
249 | printk(KERN_ERR "vfc%d: " | ||
250 | "VFC recv byte timed out\n", | ||
251 | dev->instance); | ||
252 | } | ||
253 | *byte = (sbus_readl(&dev->regs->i2c_reg)) >> 24; | ||
254 | return ret; | ||
255 | } | ||
256 | |||
257 | int vfc_i2c_recvbuf(struct vfc_dev *dev, unsigned char addr, | ||
258 | char *buf, int count) | ||
259 | { | ||
260 | int ret, last; | ||
261 | |||
262 | if(!(count && buf && dev && dev->regs) ) | ||
263 | return -EINVAL; | ||
264 | |||
265 | if ((ret = vfc_i2c_wait_for_bus(dev))) { | ||
266 | printk(KERN_ERR "vfc%d: VFC I2C bus busy\n", dev->instance); | ||
267 | return ret; | ||
268 | } | ||
269 | |||
270 | if ((ret = vfc_i2c_xmit_addr(dev, addr, VFC_I2C_READ))) { | ||
271 | WRITE_S1(SEND_I2C_STOP); | ||
272 | vfc_i2c_delay(dev); | ||
273 | return ret; | ||
274 | } | ||
275 | |||
276 | last = 0; | ||
277 | while (count--) { | ||
278 | if (!count) | ||
279 | last = 1; | ||
280 | if ((ret = vfc_i2c_recv_byte(dev, buf, last))) { | ||
281 | printk(KERN_ERR "vfc%d: " | ||
282 | "VFC error while receiving byte\n", | ||
283 | dev->instance); | ||
284 | WRITE_S1(SEND_I2C_STOP); | ||
285 | ret = -EINVAL; | ||
286 | } | ||
287 | buf++; | ||
288 | } | ||
289 | WRITE_S1(SEND_I2C_STOP | ACK); | ||
290 | vfc_i2c_delay(dev); | ||
291 | return ret; | ||
292 | } | ||
293 | |||
294 | int vfc_i2c_sendbuf(struct vfc_dev *dev, unsigned char addr, | ||
295 | char *buf, int count) | ||
296 | { | ||
297 | int ret; | ||
298 | |||
299 | if (!(buf && dev && dev->regs)) | ||
300 | return -EINVAL; | ||
301 | |||
302 | if ((ret = vfc_i2c_wait_for_bus(dev))) { | ||
303 | printk(KERN_ERR "vfc%d: VFC I2C bus busy\n", dev->instance); | ||
304 | return ret; | ||
305 | } | ||
306 | |||
307 | if ((ret = vfc_i2c_xmit_addr(dev, addr, VFC_I2C_WRITE))) { | ||
308 | WRITE_S1(SEND_I2C_STOP); | ||
309 | vfc_i2c_delay(dev); | ||
310 | return ret; | ||
311 | } | ||
312 | |||
313 | while(count--) { | ||
314 | ret = vfc_i2c_xmit_byte(dev, buf); | ||
315 | switch(ret) { | ||
316 | case XMIT_LAST_BYTE: | ||
317 | VFC_I2C_DEBUG_PRINTK(("vfc%d: " | ||
318 | "Receiver ended transmission with " | ||
319 | " %d bytes remaining\n", | ||
320 | dev->instance, count)); | ||
321 | ret = 0; | ||
322 | goto done; | ||
323 | break; | ||
324 | case 0: | ||
325 | break; | ||
326 | default: | ||
327 | printk(KERN_ERR "vfc%d: " | ||
328 | "VFC error while sending byte\n", dev->instance); | ||
329 | break; | ||
330 | }; | ||
331 | |||
332 | buf++; | ||
333 | } | ||
334 | done: | ||
335 | WRITE_S1(SEND_I2C_STOP | ACK); | ||
336 | vfc_i2c_delay(dev); | ||
337 | return ret; | ||
338 | } | ||
339 | |||
340 | |||
341 | |||
342 | |||
343 | |||
344 | |||
345 | |||
346 | |||
347 | |||
diff --git a/drivers/sbus/char/vfc_i2c.h b/drivers/sbus/char/vfc_i2c.h new file mode 100644 index 00000000000..a2e6973209d --- /dev/null +++ b/drivers/sbus/char/vfc_i2c.h | |||
@@ -0,0 +1,44 @@ | |||
1 | #ifndef _LINUX_VFC_I2C_H_ | ||
2 | #define _LINUX_VFC_I2C_H_ | ||
3 | |||
4 | /* control bits */ | ||
5 | #define PIN (0x80000000) | ||
6 | #define ESO (0x40000000) | ||
7 | #define ES1 (0x20000000) | ||
8 | #define ES2 (0x10000000) | ||
9 | #define ENI (0x08000000) | ||
10 | #define STA (0x04000000) | ||
11 | #define STO (0x02000000) | ||
12 | #define ACK (0x01000000) | ||
13 | |||
14 | /* status bits */ | ||
15 | #define STS (0x20000000) | ||
16 | #define BER (0x10000000) | ||
17 | #define LRB (0x08000000) | ||
18 | #define AAS (0x04000000) | ||
19 | #define LAB (0x02000000) | ||
20 | #define BB (0x01000000) | ||
21 | |||
22 | #define SEND_I2C_START (PIN | ESO | STA) | ||
23 | #define SEND_I2C_STOP (PIN | ESO | STO) | ||
24 | #define CLEAR_I2C_BUS (PIN | ESO | ACK) | ||
25 | #define NEGATIVE_ACK ((ESO) & ~ACK) | ||
26 | |||
27 | #define SELECT(a) (a) | ||
28 | #define S0 (PIN | ESO | ES1) | ||
29 | #define S0_OWN (PIN) | ||
30 | #define S2 (PIN | ES1) | ||
31 | #define S3 (PIN | ES2) | ||
32 | |||
33 | #define ENABLE_SERIAL (PIN | ESO) | ||
34 | #define DISABLE_SERIAL (PIN) | ||
35 | #define RESET (PIN) | ||
36 | |||
37 | #define XMIT_LAST_BYTE (1) | ||
38 | #define VFC_I2C_ACK_CHECK (1) | ||
39 | #define VFC_I2C_NO_ACK_CHECK (0) | ||
40 | |||
41 | #endif /* _LINUX_VFC_I2C_H_ */ | ||
42 | |||
43 | |||
44 | |||