pm_direct.c revision 1.34 1 /* $NetBSD: pm_direct.c,v 1.34 2024/09/14 21:02:46 nat Exp $ */
2
3 /*
4 * Copyright (c) 2024 Nathanial Sloss <nathanialsloss (at) yahoo.com.au>
5 * All rights reserved.
6 *
7 * Copyright (C) 1997 Takashi Hamada
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Takashi Hamada
21 * 4. The name of the author may not be used to endorse or promote products
22 * derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35 /* From: pm_direct.c 1.3 03/18/98 Takashi Hamada */
36
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: pm_direct.c,v 1.34 2024/09/14 21:02:46 nat Exp $");
39
40 #include "opt_adb.h"
41
42 #ifdef DEBUG
43 #ifndef ADB_DEBUG
44 #define ADB_DEBUG
45 #endif
46 #endif
47
48 /* #define PM_GRAB_SI 1 */
49
50 #include <sys/types.h>
51 #include <sys/systm.h>
52
53 #include <machine/viareg.h>
54 #include <machine/param.h>
55 #include <machine/cpu.h>
56 #include <machine/adbsys.h>
57
58 #include <mac68k/mac68k/macrom.h>
59 #include <mac68k/dev/adbvar.h>
60 #include <mac68k/dev/pm_direct.h>
61
62 /* hardware dependent values */
63 extern u_short ADBDelay;
64 extern u_int32_t HwCfgFlags3;
65 extern struct mac68k_machine_S mac68k_machine;
66
67
68 /* define the types of the Power Manager */
69 #define PM_HW_UNKNOWN 0x00 /* don't know */
70 #define PM_HW_PB1XX 0x01 /* PowerBook 1XX series */
71 #define PM_HW_PB5XX 0x02 /* PowerBook Duo and 5XX series */
72
73 /* useful macros */
74 #define PM_SR() via_reg(VIA1, vSR)
75 #define PM_VIA_INTR_ENABLE() via_reg(VIA1, vIER) = 0x90
76 #define PM_VIA_INTR_DISABLE() via_reg(VIA1, vIER) = 0x10
77 #define PM_VIA_CLR_INTR() via_reg(VIA1, vIFR) = 0x90
78 #define PM_SET_STATE_ACKON() via_reg(VIA2, vBufB) |= 0x04
79 #define PM_SET_STATE_ACKOFF() via_reg(VIA2, vBufB) &= ~0x04
80 #define PM_IS_ON (0x02 == (via_reg(VIA2, vBufB) & 0x02))
81 #define PM_IS_OFF (0x00 == (via_reg(VIA2, vBufB) & 0x02))
82
83 /*
84 * Variables for internal use
85 */
86 int pmHardware = PM_HW_UNKNOWN;
87 u_short pm_existent_ADB_devices = 0x0; /* each bit expresses the existent ADB device */
88 u_int pm_LCD_brightness = 0x0;
89 u_int pm_LCD_contrast = 0x0;
90 u_int pm_counter = 0; /* clock count */
91
92 /* these values shows that number of data returned after 'send' cmd is sent */
93 char pm_send_cmd_type[] = {
94 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
95 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
96 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
97 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
98 0xff, 0x00, 0x02, 0x01, 0x01, 0xff, 0xff, 0xff,
99 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
100 0x04, 0x14, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
101 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff,
102 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
103 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
104 0x01, 0x00, 0x02, 0x02, 0xff, 0x01, 0x03, 0x01,
105 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
106 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
107 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
108 0x01, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
109 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x04, 0x04,
110 0x04, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
111 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
112 0x01, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
113 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
114 0x02, 0x02, 0x02, 0x04, 0xff, 0x00, 0xff, 0xff,
115 0x01, 0x01, 0x03, 0x02, 0xff, 0xff, 0xff, 0xff,
116 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
117 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
118 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
119 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
120 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
121 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff,
122 0xff, 0x04, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
123 0x03, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00,
124 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
125 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
126 };
127
128 /* these values shows that number of data returned after 'receive' cmd is sent */
129 char pm_receive_cmd_type[] = {
130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 0x02, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
134 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
136 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
137 0x05, 0x15, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
138 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
139 0x02, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
140 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
141 0x02, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff,
142 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
143 0x04, 0x04, 0x03, 0x09, 0xff, 0xff, 0xff, 0xff,
144 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
145 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01,
146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
147 0x06, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
149 0x02, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
151 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
152 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
153 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
154 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
157 0x02, 0x02, 0xff, 0xff, 0x02, 0xff, 0xff, 0xff,
158 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
159 0xff, 0xff, 0x02, 0xff, 0xff, 0xff, 0xff, 0x00,
160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
162 };
163
164
165 /*
166 * Define the private functions
167 */
168
169 /* for debugging */
170 #ifdef ADB_DEBUG
171 void pm_printerr(const char *, int, int, char *);
172 #endif
173
174 int pm_wait_busy(int);
175 int pm_wait_free(int);
176
177 /* these functions are for the PB1XX series */
178 int pm_receive_pm1(u_char *);
179 int pm_send_pm1(u_char, int);
180 int pm_pmgrop_pm1(PMData *);
181 void pm_intr_pm1(void *);
182
183 /* these functions are for the PB Duo series and the PB 5XX series */
184 int pm_receive_pm2(u_char *);
185 int pm_send_pm2(u_char);
186 int pm_pmgrop_pm2(PMData *);
187 void pm_intr_pm2(void *);
188
189 /* this function is MRG-Based (for testing) */
190 int pm_pmgrop_mrg(PMData *);
191
192 /* these functions are called from adb_direct.c */
193 void pm_setup_adb(void);
194 void pm_check_adb_devices(int);
195 void pm_intr(void *);
196 int pm_adb_op(u_char *, void *, void *, int);
197 void pm_hw_setup(void);
198
199 /* these functions also use the variables of adb_direct.c */
200 void pm_adb_get_TALK_result(PMData *);
201 void pm_adb_get_ADB_data(PMData *);
202 void pm_adb_poll_next_device_pm1(PMData *);
203
204
205 /*
206 * These variables are in adb_direct.c.
207 */
208 extern u_char *adbBuffer; /* pointer to user data area */
209 extern void *adbCompRout; /* pointer to the completion routine */
210 extern void *adbCompData; /* pointer to the completion routine data */
211 extern int adbWaiting; /* waiting for return data from the device */
212 extern int adbWaitingCmd; /* ADB command we are waiting for */
213 extern int adbStarting; /* doing ADB reinit, so do "polling" differently */
214
215 #define ADB_MAX_MSG_LENGTH 16
216 #define ADB_MAX_HDR_LENGTH 8
217 struct adbCommand {
218 u_char header[ADB_MAX_HDR_LENGTH]; /* not used yet */
219 u_char data[ADB_MAX_MSG_LENGTH]; /* packet data only */
220 u_char *saveBuf; /* where to save result */
221 u_char *compRout; /* completion routine pointer */
222 u_char *compData; /* completion routine data pointer */
223 u_int cmd; /* the original command for this data */
224 u_int unsol; /* 1 if packet was unsolicited */
225 u_int ack_only; /* 1 for no special processing */
226 };
227 extern void adb_pass_up(struct adbCommand *);
228
229 #ifdef ADB_DEBUG
230 /*
231 * This function dumps contents of the PMData
232 */
233 void
234 pm_printerr(const char *ttl, int rval, int num, char *data)
235 {
236 int i;
237
238 printf("pm: %s:%04x %02x ", ttl, rval, num);
239 for (i = 0; i < num; i++)
240 printf("%02x ", data[i]);
241 printf("\n");
242 }
243 #endif
244
245
246
247 /*
248 * Check the hardware type of the Power Manager
249 */
250 void
251 pm_setup_adb(void)
252 {
253 switch (mac68k_machine.machineid) {
254 case MACH_MACPB140:
255 case MACH_MACPB145:
256 case MACH_MACPB160:
257 case MACH_MACPB165:
258 case MACH_MACPB165C:
259 case MACH_MACPB170:
260 case MACH_MACPB180:
261 case MACH_MACPB180C:
262 pmHardware = PM_HW_PB1XX;
263 break;
264 case MACH_MACPB150:
265 case MACH_MACPB210:
266 case MACH_MACPB230:
267 case MACH_MACPB250:
268 case MACH_MACPB270:
269 case MACH_MACPB280:
270 case MACH_MACPB280C:
271 case MACH_MACPB500:
272 case MACH_MACPB190:
273 case MACH_MACPB190CS:
274 pmHardware = PM_HW_PB5XX;
275 break;
276 default:
277 break;
278 }
279 }
280
281
282 /*
283 * Check the existent ADB devices
284 */
285 void
286 pm_check_adb_devices(int id)
287 {
288 u_short ed = 0x1;
289
290 ed <<= id;
291 pm_existent_ADB_devices |= ed;
292 }
293
294
295 /*
296 * Wait until PM IC is busy
297 */
298 int
299 pm_wait_busy(int xdelay)
300 {
301 while (PM_IS_ON) {
302 #ifdef PM_GRAB_SI
303 (void)intr_dispatch(0x70); /* grab any serial interrupts */
304 #endif
305 if ((--xdelay) < 0)
306 return 1; /* timeout */
307 }
308 return 0;
309 }
310
311
312 /*
313 * Wait until PM IC is free
314 */
315 int
316 pm_wait_free(int xdelay)
317 {
318 while (PM_IS_OFF) {
319 #ifdef PM_GRAB_SI
320 (void)intr_dispatch(0x70); /* grab any serial interrupts */
321 #endif
322 if ((--xdelay) < 0)
323 return 0; /* timeout */
324 }
325 return 1;
326 }
327
328
329
330 /*
331 * Functions for the PB1XX series
332 */
333
334 /*
335 * Receive data from PM for the PB1XX series
336 */
337 int
338 pm_receive_pm1(u_char *data)
339 {
340 int rval = 0xffffcd34;
341
342 via_reg(VIA2, vDirA) = 0x00;
343
344 switch (1) {
345 default:
346 if (pm_wait_busy(0x40) != 0)
347 break; /* timeout */
348
349 PM_SET_STATE_ACKOFF();
350 *data = via_reg(VIA2, 0x200);
351
352 rval = 0xffffcd33;
353 if (pm_wait_free(0x40) == 0)
354 break; /* timeout */
355
356 rval = 0x00;
357 break;
358 }
359
360 PM_SET_STATE_ACKON();
361 via_reg(VIA2, vDirA) = 0x00;
362
363 return rval;
364 }
365
366
367
368 /*
369 * Send data to PM for the PB1XX series
370 */
371 int
372 pm_send_pm1(u_char data, int timo)
373 {
374 int rval;
375
376 via_reg(VIA2, vDirA) = 0xff;
377 via_reg(VIA2, 0x200) = data;
378
379 PM_SET_STATE_ACKOFF();
380 #if 0
381 if (pm_wait_busy(0x400) == 0) {
382 #else
383 if (pm_wait_busy(timo) == 0) {
384 #endif
385 PM_SET_STATE_ACKON();
386 if (pm_wait_free(0x40) != 0)
387 rval = 0x0;
388 else
389 rval = 0xffffcd35;
390 } else {
391 rval = 0xffffcd36;
392 }
393
394 PM_SET_STATE_ACKON();
395 via_reg(VIA2, vDirA) = 0x00;
396
397 return rval;
398 }
399
400
401 /*
402 * My PMgrOp routine for the PB1XX series
403 */
404 int
405 pm_pmgrop_pm1(PMData *pmdata)
406 {
407 int i;
408 int s = 0x81815963;
409 u_char via1_vIER, via1_vDirA;
410 int rval = 0;
411 int num_pm_data = 0;
412 u_char pm_cmd;
413 u_char pm_data;
414 u_char *pm_buf;
415
416 /* disable all interrupts but PM */
417 via1_vIER = via_reg(VIA1, vIER);
418 PM_VIA_INTR_DISABLE();
419
420 via1_vDirA = via_reg(VIA1, vDirA);
421
422 switch (pmdata->command) {
423 default:
424 for (i = 0; i < 7; i++) {
425 via_reg(VIA2, vDirA) = 0x00;
426
427 /* wait until PM is free */
428 if (pm_wait_free(ADBDelay) == 0) { /* timeout */
429 via_reg(VIA2, vDirA) = 0x00;
430 /* restore formar value */
431 via_reg(VIA1, vDirA) = via1_vDirA;
432 via_reg(VIA1, vIER) = via1_vIER;
433 return 0xffffcd38;
434 }
435
436 switch (mac68k_machine.machineid) {
437 case MACH_MACPB160:
438 case MACH_MACPB165:
439 case MACH_MACPB165C:
440 case MACH_MACPB170:
441 case MACH_MACPB180:
442 case MACH_MACPB180C:
443 {
444 int xdelay = ADBDelay * 16;
445
446 via_reg(VIA2, vDirA) = 0x00;
447 while ((via_reg(VIA2, 0x200) == 0x7f) && (xdelay >= 0))
448 xdelay--;
449
450 if (xdelay < 0) { /* timeout */
451 via_reg(VIA2, vDirA) = 0x00;
452 /* restore formar value */
453 via_reg(VIA1, vIER) = via1_vIER;
454 return 0xffffcd38;
455 }
456 }
457 } /* end switch */
458
459 s = splhigh();
460
461 via1_vDirA = via_reg(VIA1, vDirA);
462 via_reg(VIA1, vDirA) &= 0x7f;
463
464 pm_cmd = (u_char)(pmdata->command & 0xff);
465 if ((rval = pm_send_pm1(pm_cmd, ADBDelay * 8)) == 0)
466 break; /* send command succeeded */
467
468 via_reg(VIA1, vDirA) = via1_vDirA;
469 splx(s);
470 } /* end for */
471
472 /* failed to send a command */
473 if (i == 7) {
474 via_reg(VIA2, vDirA) = 0x00;
475 /* restore formar value */
476 via_reg(VIA1, vDirA) = via1_vDirA;
477 via_reg(VIA1, vIER) = via1_vIER;
478 if (s != 0x81815963)
479 splx(s);
480 return 0xffffcd38;
481 }
482
483 /* send # of PM data */
484 num_pm_data = pmdata->num_data;
485 if ((rval = pm_send_pm1((u_char)(num_pm_data & 0xff), ADBDelay * 8)) != 0)
486 break; /* timeout */
487
488 /* send PM data */
489 pm_buf = (u_char *)pmdata->s_buf;
490 for (i = 0; i < num_pm_data; i++)
491 if ((rval = pm_send_pm1(pm_buf[i], ADBDelay * 8)) != 0)
492 break; /* timeout */
493 if ((i != num_pm_data) && (num_pm_data != 0))
494 break; /* timeout */
495
496 /* Will PM IC return data? */
497 if ((pm_cmd & 0x08) == 0) {
498 rval = 0;
499 break; /* no returned data */
500 }
501
502 rval = 0xffffcd37;
503 if (pm_wait_busy(ADBDelay) != 0)
504 break; /* timeout */
505
506 /* receive PM command */
507 if ((rval = pm_receive_pm1(&pm_data)) != 0)
508 break;
509
510 pmdata->command = pm_data;
511
512 /* receive number of PM data */
513 if ((rval = pm_receive_pm1(&pm_data)) != 0)
514 break; /* timeout */
515 num_pm_data = pm_data;
516 pmdata->num_data = num_pm_data;
517
518 /* receive PM data */
519 pm_buf = (u_char *)pmdata->r_buf;
520 for (i = 0; i < num_pm_data; i++) {
521 if ((rval = pm_receive_pm1(&pm_data)) != 0)
522 break; /* timeout */
523 pm_buf[i] = pm_data;
524 }
525
526 rval = 0;
527 }
528
529 via_reg(VIA2, vDirA) = 0x00;
530
531 /* restore formar value */
532 via_reg(VIA1, vDirA) = via1_vDirA;
533 via_reg(VIA1, vIER) = via1_vIER;
534 if (s != 0x81815963)
535 splx(s);
536
537 return rval;
538 }
539
540
541 /*
542 * My PM interrupt routine for PB1XX series
543 */
544 void
545 pm_intr_pm1(void *arg)
546 {
547 int s;
548 int rval;
549 PMData pmdata;
550
551 s = splhigh();
552
553 PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */
554
555 /* ask PM what happened */
556 pmdata.command = 0x78;
557 pmdata.num_data = 0;
558 pmdata.data[0] = pmdata.data[1] = 0;
559 pmdata.s_buf = &pmdata.data[2];
560 pmdata.r_buf = &pmdata.data[2];
561 rval = pm_pmgrop_pm1(&pmdata);
562 if (rval != 0) {
563 #ifdef ADB_DEBUG
564 if (adb_debug)
565 printf("pm: PM is not ready. error code=%08x\n", rval);
566 #endif
567 splx(s);
568 return;
569 }
570
571 if ((pmdata.data[2] & 0x10) == 0x10) {
572 if ((pmdata.data[2] & 0x0f) == 0) {
573 /* ADB data that were requested by TALK command */
574 pm_adb_get_TALK_result(&pmdata);
575 } else if ((pmdata.data[2] & 0x08) == 0x8) {
576 /* PM is requesting to poll */
577 pm_adb_poll_next_device_pm1(&pmdata);
578 } else if ((pmdata.data[2] & 0x04) == 0x4) {
579 /* ADB device event */
580 pm_adb_get_ADB_data(&pmdata);
581 }
582 } else {
583 #ifdef ADB_DEBUG
584 if (adb_debug)
585 pm_printerr("driver does not supported this event.",
586 rval, pmdata.num_data, pmdata.data);
587 #endif
588 }
589
590 splx(s);
591 }
592
593
594
595 /*
596 * Functions for the PB Duo series and the PB 5XX series
597 */
598
599 /*
600 * Receive data from PM for the PB Duo series and the PB 5XX series
601 */
602 int
603 pm_receive_pm2(u_char *data)
604 {
605 int rval;
606
607 rval = 0xffffcd34;
608
609 switch (1) {
610 default:
611 /* set VIA SR to input mode */
612 via_reg(VIA1, vACR) |= 0x0c;
613 via_reg(VIA1, vACR) &= ~0x10;
614 PM_SR();
615
616 PM_SET_STATE_ACKOFF();
617 if (pm_wait_busy((int)ADBDelay*32) != 0)
618 break; /* timeout */
619
620 PM_SET_STATE_ACKON();
621 rval = 0xffffcd33;
622 if (pm_wait_free((int)ADBDelay*32) == 0)
623 break; /* timeout */
624
625 *data = PM_SR();
626 rval = 0;
627
628 break;
629 }
630
631 PM_SET_STATE_ACKON();
632 via_reg(VIA1, vACR) |= 0x1c;
633
634 return rval;
635 }
636
637
638
639 /*
640 * Send data to PM for the PB Duo series and the PB 5XX series
641 */
642 int
643 pm_send_pm2(u_char data)
644 {
645 int rval;
646
647 via_reg(VIA1, vACR) |= 0x1c;
648 PM_SR() = data;
649
650 PM_SET_STATE_ACKOFF();
651 if (pm_wait_busy((int)ADBDelay*32) == 0) {
652 PM_SET_STATE_ACKON();
653 if (pm_wait_free((int)ADBDelay*32) != 0)
654 rval = 0;
655 else
656 rval = 0xffffcd35;
657 } else {
658 rval = 0xffffcd36;
659 }
660
661 PM_SET_STATE_ACKON();
662 via_reg(VIA1, vACR) |= 0x1c;
663
664 return rval;
665 }
666
667
668
669 /*
670 * My PMgrOp routine for the PB Duo series and the PB 5XX series
671 */
672 int
673 pm_pmgrop_pm2(PMData *pmdata)
674 {
675 int i;
676 int s;
677 u_char via1_vIER;
678 int rval = 0;
679 int num_pm_data = 0;
680 u_char pm_cmd;
681 short pm_num_rx_data;
682 u_char pm_data;
683 u_char *pm_buf;
684
685 s = splhigh();
686
687 /* disable all interrupts but PM */
688 via1_vIER = 0x10;
689 via1_vIER &= via_reg(VIA1, vIER);
690 via_reg(VIA1, vIER) = via1_vIER;
691 if (via1_vIER != 0x0)
692 via1_vIER |= 0x80;
693
694 switch (pmdata->command) {
695 default:
696 /* wait until PM is free */
697 pm_cmd = (u_char)(pmdata->command & 0xff);
698 rval = 0xcd38;
699 if (pm_wait_free(ADBDelay * 4) == 0)
700 break; /* timeout */
701
702 if (HwCfgFlags3 & 0x00200000) {
703 /* PB 160, PB 165(c), PB 180(c)? */
704 int xdelay = ADBDelay * 16;
705
706 via_reg(VIA2, vDirA) = 0x00;
707 while ((via_reg(VIA2, 0x200) == 0x07) &&
708 (xdelay >= 0))
709 xdelay--;
710
711 if (xdelay < 0) {
712 rval = 0xffffcd38;
713 break; /* timeout */
714 }
715 }
716
717 /* send PM command */
718 if ((rval = pm_send_pm2((u_char)(pm_cmd & 0xff))))
719 break; /* timeout */
720
721 /* send number of PM data */
722 num_pm_data = pmdata->num_data;
723 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */
724 if (pm_send_cmd_type[pm_cmd] < 0) {
725 if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0)
726 break; /* timeout */
727 pmdata->command = 0;
728 }
729 } else { /* PB 1XX series ? */
730 if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0)
731 break; /* timeout */
732 }
733 /* send PM data */
734 pm_buf = (u_char *)pmdata->s_buf;
735 for (i = 0 ; i < num_pm_data; i++)
736 if ((rval = pm_send_pm2(pm_buf[i])) != 0)
737 break; /* timeout */
738 if (i != num_pm_data)
739 break; /* timeout */
740
741
742 /* check if PM will send me data */
743 pm_num_rx_data = pm_receive_cmd_type[pm_cmd];
744 pmdata->num_data = pm_num_rx_data;
745 if (pm_num_rx_data == 0) {
746 rval = 0;
747 break; /* no return data */
748 }
749
750 /* receive PM command */
751 pm_data = pmdata->command;
752 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */
753 pm_num_rx_data--;
754 if (pm_num_rx_data == 0)
755 if ((rval = pm_receive_pm2(&pm_data)) != 0) {
756 rval = 0xffffcd37;
757 break;
758 }
759 pmdata->command = pm_data;
760 } else { /* PB 1XX series ? */
761 if ((rval = pm_receive_pm2(&pm_data)) != 0) {
762 rval = 0xffffcd37;
763 break;
764 }
765 pmdata->command = pm_data;
766 }
767
768 /* receive number of PM data */
769 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */
770 if (pm_num_rx_data < 0) {
771 if ((rval = pm_receive_pm2(&pm_data)) != 0)
772 break; /* timeout */
773 num_pm_data = pm_data;
774 } else
775 num_pm_data = pm_num_rx_data;
776 pmdata->num_data = num_pm_data;
777 } else { /* PB 1XX serias ? */
778 if ((rval = pm_receive_pm2(&pm_data)) != 0)
779 break; /* timeout */
780 num_pm_data = pm_data;
781 pmdata->num_data = num_pm_data;
782 }
783
784 /* receive PM data */
785 pm_buf = (u_char *)pmdata->r_buf;
786 for (i = 0; i < num_pm_data; i++) {
787 if ((rval = pm_receive_pm2(&pm_data)) != 0)
788 break; /* timeout */
789 pm_buf[i] = pm_data;
790 }
791
792 rval = 0;
793 }
794
795 /* restore former value */
796 via_reg(VIA1, vIER) = via1_vIER;
797 splx(s);
798
799 return rval;
800 }
801
802
803 /*
804 * My PM interrupt routine for the PB Duo series and the PB 5XX series
805 */
806 void
807 pm_intr_pm2(void *arg)
808 {
809 int s;
810 int rval;
811 PMData pmdata;
812
813 s = splhigh();
814
815 PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */
816 /* ask PM what happened */
817 pmdata.command = 0x78;
818 pmdata.num_data = 0;
819 pmdata.s_buf = &pmdata.data[2];
820 pmdata.r_buf = &pmdata.data[2];
821 rval = pm_pmgrop_pm2(&pmdata);
822 if (rval != 0) {
823 #ifdef ADB_DEBUG
824 if (adb_debug)
825 printf("pm: PM is not ready. error code: %08x\n", rval);
826 #endif
827 splx(s);
828 return;
829 }
830
831 switch ((u_int)(pmdata.data[2] & 0xff)) {
832 case 0x00: /* 1 sec interrupt? */
833 break;
834 case 0x80: /* 1 sec interrupt? */
835 pm_counter++;
836 break;
837 case 0x08: /* Brightness/Contrast button on LCD panel */
838 /* get brightness and contrast of the LCD */
839 pm_LCD_brightness = (u_int)pmdata.data[3] & 0xff;
840 pm_LCD_contrast = (u_int)pmdata.data[4] & 0xff;
841 /*
842 pm_printerr("#08", rval, pmdata.num_data, pmdata.data);
843 pmdata.command = 0x33;
844 pmdata.num_data = 1;
845 pmdata.s_buf = pmdata.data;
846 pmdata.r_buf = pmdata.data;
847 pmdata.data[0] = pm_LCD_contrast;
848 rval = pm_pmgrop_pm2(&pmdata);
849 pm_printerr("#33", rval, pmdata.num_data, pmdata.data);
850 */
851 pm_LCD_brightness =
852 pm_set_brightness(pm_LCD_brightness);
853 break;
854 case 0x10: /* ADB data that were requested by TALK command */
855 case 0x14:
856 pm_adb_get_TALK_result(&pmdata);
857 break;
858 case 0x16: /* ADB device event */
859 case 0x18:
860 case 0x1e:
861 pm_adb_get_ADB_data(&pmdata);
862 break;
863 default:
864 #ifdef ADB_DEBUG
865 if (adb_debug)
866 pm_printerr("driver does not supported this event.",
867 pmdata.data[2], pmdata.num_data,
868 pmdata.data);
869 #endif
870 break;
871 }
872
873 splx(s);
874 }
875
876
877 /*
878 * MRG-based PMgrOp routine
879 */
880 int
881 pm_pmgrop_mrg(PMData *pmdata)
882 {
883 u_int32_t rval=0;
884
885 __asm volatile(
886 " movl %1,%%a0 \n"
887 " .word 0xa085 \n"
888 " movl %%d0,%0"
889 : "=g" (rval)
890 : "g" (pmdata)
891 : "a0","d0");
892
893 return rval;
894 }
895
896
897 /*
898 * My PMgrOp routine
899 */
900 int
901 pmgrop(PMData *pmdata)
902 {
903 switch (pmHardware) {
904 case PM_HW_PB1XX:
905 return (pm_pmgrop_pm1(pmdata));
906 break;
907 case PM_HW_PB5XX:
908 return (pm_pmgrop_pm2(pmdata));
909 break;
910 default:
911 /* return (pmgrop_mrg(pmdata)); */
912 return 1;
913 }
914 }
915
916
917 /*
918 * My PM interrupt routine
919 */
920 void
921 pm_intr(void *arg)
922 {
923 switch (pmHardware) {
924 case PM_HW_PB1XX:
925 pm_intr_pm1(arg);
926 break;
927 case PM_HW_PB5XX:
928 pm_intr_pm2(arg);
929 break;
930 default:
931 break;
932 }
933 }
934
935
936 void
937 pm_hw_setup(void)
938 {
939 switch (pmHardware) {
940 case PM_HW_PB1XX:
941 via1_register_irq(4, pm_intr_pm1, (void *)0);
942 PM_VIA_CLR_INTR();
943 break;
944 case PM_HW_PB5XX:
945 via1_register_irq(4, pm_intr_pm2, (void *)0);
946 PM_VIA_CLR_INTR();
947 break;
948 default:
949 break;
950 }
951 }
952
953
954 /*
955 * Synchronous ADBOp routine for the Power Manager
956 */
957 int
958 pm_adb_op(u_char *buffer, void *compRout, void *data, int command)
959 {
960 int i;
961 int s;
962 int rval;
963 int xdelay;
964 PMData pmdata;
965 struct adbCommand packet;
966
967 if (adbWaiting == 1)
968 return 1;
969
970 s = splhigh();
971 via_reg(VIA1, vIER) = 0x10;
972
973 adbBuffer = buffer;
974 adbCompRout = compRout;
975 adbCompData = data;
976
977 pmdata.command = 0x20;
978 pmdata.s_buf = pmdata.data;
979 pmdata.r_buf = pmdata.data;
980
981 if ((command & 0xc) == 0x8) { /* if the command is LISTEN, add number of ADB data to number of PM data */
982 if (buffer != (u_char *)0)
983 pmdata.num_data = buffer[0] + 3;
984 } else {
985 pmdata.num_data = 3;
986 }
987
988 pmdata.data[0] = (u_char)(command & 0xff);
989 pmdata.data[1] = 0;
990 if ((command & 0xc) == 0x8) { /* if the command is LISTEN, copy ADB data to PM buffer */
991 if ((buffer != (u_char *)0) && (buffer[0] <= 24)) {
992 pmdata.data[2] = buffer[0]; /* number of data */
993 for (i = 0; i < buffer[0]; i++)
994 pmdata.data[3 + i] = buffer[1 + i];
995 } else
996 pmdata.data[2] = 0;
997 } else
998 pmdata.data[2] = 0;
999
1000 if ((command & 0xc) != 0xc) { /* if the command is not TALK */
1001 /* set up stuff fNULLor adb_pass_up */
1002 packet.data[0] = 1 + pmdata.data[2];
1003 packet.data[1] = command;
1004 for (i = 0; i < pmdata.data[2]; i++)
1005 packet.data[i+2] = pmdata.data[i+3];
1006 packet.saveBuf = adbBuffer;
1007 packet.compRout = adbCompRout;
1008 packet.compData = adbCompData;
1009 packet.cmd = command;
1010 packet.unsol = 0;
1011 packet.ack_only = 1;
1012 adb_polling = 1;
1013 adb_pass_up(&packet);
1014 adb_polling = 0;
1015 }
1016
1017 rval = pmgrop(&pmdata);
1018 if (rval != 0) {
1019 splx(s);
1020 return 1;
1021 }
1022
1023 adbWaiting = 1;
1024 adbWaitingCmd = command;
1025
1026 PM_VIA_INTR_ENABLE();
1027
1028 /* wait until the PM interrupt has occurred */
1029 xdelay = 0x80000;
1030 while (adbWaiting == 1) {
1031 switch (mac68k_machine.machineid) {
1032 case MACH_MACPB150:
1033 case MACH_MACPB210:
1034 case MACH_MACPB230: /* daishi tested with Duo230 */
1035 case MACH_MACPB250:
1036 case MACH_MACPB270:
1037 case MACH_MACPB280:
1038 case MACH_MACPB280C:
1039 case MACH_MACPB190:
1040 case MACH_MACPB190CS:
1041 pm_intr((void *)0);
1042 break;
1043 default:
1044 if ((via_reg(VIA1, vIFR) & 0x10) == 0x10)
1045 pm_intr((void *)0);
1046 break;
1047 }
1048 #ifdef PM_GRAB_SI
1049 (void)intr_dispatch(0x70); /* grab any serial interrupts */
1050 #endif
1051 if ((--xdelay) < 0) {
1052 splx(s);
1053 return 1;
1054 }
1055 }
1056
1057 /* this command enables the interrupt by operating ADB devices */
1058 if (HwCfgFlags3 & 0x00020000) { /* PB Duo series, PB 5XX series */
1059 pmdata.command = 0x20;
1060 pmdata.num_data = 4;
1061 pmdata.s_buf = pmdata.data;
1062 pmdata.r_buf = pmdata.data;
1063 pmdata.data[0] = 0x00;
1064 pmdata.data[1] = 0x86; /* magic spell for awaking the PM */
1065 pmdata.data[2] = 0x00;
1066 pmdata.data[3] = 0x0c; /* each bit may express the existent ADB device */
1067 } else { /* PB 1XX series */
1068 pmdata.command = 0x20;
1069 pmdata.num_data = 3;
1070 pmdata.s_buf = pmdata.data;
1071 pmdata.r_buf = pmdata.data;
1072 pmdata.data[0] = (u_char)(command & 0xf0) | 0xc;
1073 pmdata.data[1] = 0x04;
1074 pmdata.data[2] = 0x00;
1075 }
1076 rval = pmgrop(&pmdata);
1077
1078 splx(s);
1079 return rval;
1080 }
1081
1082
1083 void
1084 pm_adb_get_TALK_result(PMData *pmdata)
1085 {
1086 int i;
1087 struct adbCommand packet;
1088
1089 /* set up data for adb_pass_up */
1090 packet.data[0] = pmdata->num_data-1;
1091 packet.data[1] = pmdata->data[3];
1092 for (i = 0; i <packet.data[0]-1; i++)
1093 packet.data[i+2] = pmdata->data[i+4];
1094
1095 packet.saveBuf = adbBuffer;
1096 packet.compRout = adbCompRout;
1097 packet.compData = adbCompData;
1098 packet.unsol = 0;
1099 packet.ack_only = 0;
1100 adb_polling = 1;
1101 adb_pass_up(&packet);
1102 adb_polling = 0;
1103
1104 adbWaiting = 0;
1105 adbBuffer = (long)0;
1106 adbCompRout = (long)0;
1107 adbCompData = (long)0;
1108 }
1109
1110
1111 void
1112 pm_adb_get_ADB_data(PMData *pmdata)
1113 {
1114 int i;
1115 struct adbCommand packet;
1116
1117 /* set up data for adb_pass_up */
1118 packet.data[0] = pmdata->num_data-1; /* number of raw data */
1119 packet.data[1] = pmdata->data[3]; /* ADB command */
1120 for (i = 0; i <packet.data[0]-1; i++)
1121 packet.data[i+2] = pmdata->data[i+4];
1122 packet.unsol = 1;
1123 packet.ack_only = 0;
1124 adb_pass_up(&packet);
1125 }
1126
1127
1128 void
1129 pm_adb_poll_next_device_pm1(PMData *pmdata)
1130 {
1131 int i;
1132 int ndid;
1133 u_short bendid = 0x1;
1134 PMData tmp_pmdata;
1135
1136 /* find another existent ADB device to poll */
1137 for (i = 1; i < 16; i++) {
1138 ndid = (ADB_CMDADDR(pmdata->data[3]) + i) & 0xf;
1139 bendid <<= ndid;
1140 if ((pm_existent_ADB_devices & bendid) != 0)
1141 break;
1142 }
1143
1144 /* poll the other device */
1145 tmp_pmdata.command = 0x20;
1146 tmp_pmdata.num_data = 3;
1147 tmp_pmdata.s_buf = tmp_pmdata.data;
1148 tmp_pmdata.r_buf = tmp_pmdata.data;
1149 tmp_pmdata.data[0] = (u_char)(ndid << 4) | 0xc;
1150 tmp_pmdata.data[1] = 0x04; /* magic spell for awaking the PM */
1151 tmp_pmdata.data[2] = 0x00;
1152 pmgrop(&tmp_pmdata);
1153 }
1154
1155 void
1156 pm_poweroff(void)
1157 {
1158 PMData pmdata;
1159 int attempt = 3;
1160
1161 while (pmHardware == PM_HW_PB1XX && attempt > 0) {
1162 pmdata.command = 0x7e;
1163 pmdata.num_data = 0;
1164 pmdata.data[0] = pmdata.data[1] = 0;
1165 pmdata.s_buf = &pmdata.data[2];
1166 pmdata.r_buf = &pmdata.data[2];
1167 (void)pm_pmgrop_pm1(&pmdata);
1168 attempt--;
1169 }
1170
1171 return;
1172 }
1173
1174 u_int
1175 pm_set_brightness(u_int brightness)
1176 {
1177 PMData pmdata;
1178
1179 pmdata.num_data = 1;
1180 pmdata.s_buf = pmdata.data;
1181 pmdata.r_buf = pmdata.data;
1182
1183 switch (pmHardware) {
1184 case PM_HW_PB5XX:
1185 /* this is an experimental code */
1186 pmdata.command = 0x41;
1187 brightness = 0x7f - brightness / 2;
1188 if (brightness < 0x25)
1189 brightness = 0x25;
1190 if (brightness > 0x5a)
1191 brightness = 0x7f;
1192 pmdata.data[0] = brightness;
1193 (void)pm_pmgrop_pm2(&pmdata);
1194 break;
1195 case PM_HW_PB1XX:
1196 /* this is an experimental code also */
1197 pmdata.command = 0x40;
1198 if ((int)brightness < 0)
1199 brightness = 0;
1200 if ((int)brightness > 31)
1201 brightness = 31;
1202 pmdata.data[0] = 31 - brightness;
1203 (void)pm_pmgrop_pm1(&pmdata);
1204 break;
1205 default:
1206
1207 return 0;
1208 break;
1209 }
1210
1211 return brightness;
1212 }
1213