Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | ||
fec607ff | 312 | void uctrl_get_event_status(void) |
1da177e4 LT |
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; | |
fec607ff | 321 | txn.inbuf = NULL; |
1da177e4 LT |
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 | ||
fec607ff | 332 | void uctrl_get_external_status(void) |
1da177e4 LT |
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; | |
fec607ff | 342 | txn.inbuf = NULL; |
1da177e4 LT |
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; | |
c6387a48 | 403 | printk("uctrl: 0x%x (irq %d)\n", driver->regs, driver->irq); |
1da177e4 LT |
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) | |
fec607ff | 417 | driver->regs = NULL; |
1da177e4 LT |
418 | } |
419 | ||
420 | module_init(ts102_uctrl_init); | |
421 | module_exit(ts102_uctrl_cleanup); | |
422 | MODULE_LICENSE("GPL"); |