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