📄 via-pmu.c
字号:
} else {#ifdef CONFIG_XMON if (len == 4 && data[1] == 0x2c) { extern int xmon_wants_key, xmon_adb_keycode; if (xmon_wants_key) { xmon_adb_keycode = data[2]; return; } }#endif /* CONFIG_XMON */#ifdef CONFIG_ADB /* * XXX On the [23]400 the PMU gives us an up * event for keycodes 0x74 or 0x75 when the PC * card eject buttons are released, so we * ignore those events. */ if (!(pmu_kind == PMU_OHARE_BASED && len == 4 && data[1] == 0x2c && data[3] == 0xff && (data[2] & ~1) == 0xf4)) adb_input(data+1, len-1, regs, 1);#endif /* CONFIG_ADB */ } } else if (data[0] == 0x08 && len == 3) { /* sound/brightness buttons pressed */#ifdef CONFIG_PMAC_BACKLIGHT set_backlight_level(data[1] >> 4);#endif set_volume(data[2]); } else {#ifdef CONFIG_PMAC_PBOOK pmu_pass_intr(data, len);#endif }}#ifdef CONFIG_PMAC_BACKLIGHTstatic int backlight_to_bright[] = { 0x7f, 0x46, 0x42, 0x3e, 0x3a, 0x36, 0x32, 0x2e, 0x2a, 0x26, 0x22, 0x1e, 0x1a, 0x16, 0x12, 0x0e}; static int __openfirmwarepmu_set_backlight_enable(int on, int level, void* data){ struct adb_request req; if (vias == NULL) return -ENODEV; if (on) { pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, backlight_to_bright[level]); while (!req.complete) pmu_poll(); } pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF)); while (!req.complete) pmu_poll(); return 0;}static int __openfirmwarepmu_set_backlight_level(int level, void* data){ if (vias == NULL) return -ENODEV; if (!bright_req_1.complete) return -EAGAIN; pmu_request(&bright_req_1, NULL, 2, PMU_BACKLIGHT_BRIGHT, backlight_to_bright[level]); if (!bright_req_2.complete) return -EAGAIN; pmu_request(&bright_req_2, NULL, 2, PMU_POWER_CTRL, PMU_POW_BACKLIGHT | (level > BACKLIGHT_OFF ? PMU_POW_ON : PMU_POW_OFF)); return 0;}#endif /* CONFIG_PMAC_BACKLIGHT */void __openfirmwarepmu_enable_irled(int on){ struct adb_request req; if (vias == NULL) return ; if (pmu_kind == PMU_KEYLARGO_BASED) return ; pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_IRLED | (on ? PMU_POW_ON : PMU_POW_OFF)); while (!req.complete) pmu_poll();}static void __openfirmwareset_volume(int level){}void __openfirmwarepmu_restart(void){ struct adb_request req; cli(); pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB | PMU_INT_TICK ); while(!req.complete) pmu_poll(); pmu_request(&req, NULL, 1, PMU_RESET); while(!req.complete || (pmu_state != idle)) pmu_poll(); for (;;) ;}void __openfirmwarepmu_shutdown(void){ struct adb_request req; cli(); pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB | PMU_INT_TICK ); while(!req.complete) pmu_poll(); pmu_request(&req, NULL, 5, PMU_SHUTDOWN, 'M', 'A', 'T', 'T'); while(!req.complete || (pmu_state != idle)) pmu_poll(); for (;;) ;}intpmu_present(void){ return via != 0;}#ifdef CONFIG_PMAC_PBOOKstatic LIST_HEAD(sleep_notifiers);intpmu_register_sleep_notifier(struct pmu_sleep_notifier *n){ struct list_head *list; struct pmu_sleep_notifier *notifier; for (list = sleep_notifiers.next; list != &sleep_notifiers; list = list->next) { notifier = list_entry(list, struct pmu_sleep_notifier, list); if (n->priority > notifier->priority) break; } __list_add(&n->list, list->prev, list); return 0;}intpmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n){ if (n->list.next == 0) return -ENOENT; list_del(&n->list); n->list.next = 0; return 0;}/* Sleep is broadcast last-to-first */static intbroadcast_sleep(int when, int fallback){ int ret = PBOOK_SLEEP_OK; struct list_head *list; struct pmu_sleep_notifier *notifier; for (list = sleep_notifiers.prev; list != &sleep_notifiers; list = list->prev) { notifier = list_entry(list, struct pmu_sleep_notifier, list); ret = notifier->notifier_call(notifier, when); if (ret != PBOOK_SLEEP_OK) { printk(KERN_DEBUG "sleep %d rejected by %p (%p)\n", when, notifier, notifier->notifier_call); for (; list != &sleep_notifiers; list = list->next) { notifier = list_entry(list, struct pmu_sleep_notifier, list); notifier->notifier_call(notifier, fallback); } return ret; } } return ret;}/* Wake is broadcast first-to-last */static intbroadcast_wake(void){ int ret = PBOOK_SLEEP_OK; struct list_head *list; struct pmu_sleep_notifier *notifier; for (list = sleep_notifiers.next; list != &sleep_notifiers; list = list->next) { notifier = list_entry(list, struct pmu_sleep_notifier, list); notifier->notifier_call(notifier, PBOOK_WAKE); } return ret;}/* * This struct is used to store config register values for * PCI devices which may get powered off when we sleep. */static struct pci_save { u16 command; u16 cache_lat; u16 intr; u32 rom_address;} *pbook_pci_saves;static int n_pbook_pci_saves;static void __openfirmwarepbook_pci_save(void){ int npci; struct pci_dev *pd; struct pci_save *ps; npci = 0; pci_for_each_dev(pd) { ++npci; } n_pbook_pci_saves = npci; if (npci == 0) return; ps = (struct pci_save *) kmalloc(npci * sizeof(*ps), GFP_KERNEL); pbook_pci_saves = ps; if (ps == NULL) return; pci_for_each_dev(pd) { pci_read_config_word(pd, PCI_COMMAND, &ps->command); pci_read_config_word(pd, PCI_CACHE_LINE_SIZE, &ps->cache_lat); pci_read_config_word(pd, PCI_INTERRUPT_LINE, &ps->intr); pci_read_config_dword(pd, PCI_ROM_ADDRESS, &ps->rom_address); ++ps; }}static void __openfirmwarepbook_pci_restore(void){ u16 cmd; struct pci_save *ps = pbook_pci_saves - 1; struct pci_dev *pd; int j; pci_for_each_dev(pd) { ps++; if (ps->command == 0) continue; pci_read_config_word(pd, PCI_COMMAND, &cmd); if ((ps->command & ~cmd) == 0) continue; switch (pd->hdr_type) { case PCI_HEADER_TYPE_NORMAL: for (j = 0; j < 6; ++j) pci_write_config_dword(pd, PCI_BASE_ADDRESS_0 + j*4, pd->resource[j].start); pci_write_config_dword(pd, PCI_ROM_ADDRESS, ps->rom_address); pci_write_config_word(pd, PCI_CACHE_LINE_SIZE, ps->cache_lat); pci_write_config_word(pd, PCI_INTERRUPT_LINE, ps->intr); pci_write_config_word(pd, PCI_COMMAND, ps->command); break; /* other header types not restored at present */ } }}#if 0/* N.B. This doesn't work on the 3400 */void pmu_blink(int n){ struct adb_request req; for (; n > 0; --n) { pmu_request(&req, NULL, 4, 0xee, 4, 0, 1); while (!req.complete) pmu_poll(); udelay(50000); pmu_request(&req, NULL, 4, 0xee, 4, 0, 0); while (!req.complete) pmu_poll(); udelay(50000); } udelay(150000);}#endif/* * Put the powerbook to sleep. */ static u32 save_via[8];static void save_via_state(void){ save_via[0] = in_8(&via[ANH]); save_via[1] = in_8(&via[DIRA]); save_via[2] = in_8(&via[B]); save_via[3] = in_8(&via[DIRB]); save_via[4] = in_8(&via[PCR]); save_via[5] = in_8(&via[ACR]); save_via[6] = in_8(&via[T1CL]); save_via[7] = in_8(&via[T1CH]);}static void restore_via_state(void){ out_8(&via[ANH], save_via[0]); out_8(&via[DIRA], save_via[1]); out_8(&via[B], save_via[2]); out_8(&via[DIRB], save_via[3]); out_8(&via[PCR], save_via[4]); out_8(&via[ACR], save_via[5]); out_8(&via[T1CL], save_via[6]); out_8(&via[T1CH], save_via[7]); out_8(&via[IER], IER_CLR | 0x7f); /* disable all intrs */ out_8(&via[IFR], 0x7f); /* clear IFR */ out_8(&via[IER], IER_SET | SR_INT | CB1_INT);}#define FEATURE_CTRL(base) ((unsigned int *)(base + 0x38))#define GRACKLE_PM (1<<7)#define GRACKLE_DOZE (1<<5)#define GRACKLE_NAP (1<<4)#define GRACKLE_SLEEP (1<<3)int __openfirmware powerbook_sleep_G3(void){ unsigned long save_l2cr; unsigned long wait; unsigned short pmcr1; struct adb_request req; int ret, timeout; /* Notify device drivers */ ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, PBOOK_SLEEP_REJECT); if (ret != PBOOK_SLEEP_OK) { printk("pmu: sleep rejected\n"); return -EBUSY; } /* Sync the disks. */ /* XXX It would be nice to have some way to ensure that * nobody is dirtying any new buffers while we wait. * BenH: Moved to _after_ sleep request and changed video * drivers to vmalloc() during sleep request. This way, all * vmalloc's are done before actual sleep of block drivers */ fsync_dev(0); /* Sleep can fail now. May not be very robust but useful for debugging */ ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE); if (ret != PBOOK_SLEEP_OK) { printk("pmu: sleep failed\n"); return -EBUSY; } /* Give the disks a little time to actually finish writing */ for (wait = jiffies + (HZ/2); time_before(jiffies, wait); ) mb(); /* Wait for completion of async backlight requests */ while (!bright_req_1.complete || !bright_req_2.complete || !bright_req_3.complete) pmu_poll(); /* Turn off various things. Darwin does some retry tests here... */ pmu_request(&req, NULL, 2, PMU_POWER_CTRL0, PMU_POW0_OFF|PMU_POW0_HARD_DRIVE); while (!req.complete) pmu_poll(); pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_OFF|PMU_POW_BACKLIGHT|PMU_POW_IRLED|PMU_POW_MEDIABAY); while (!req.complete) pmu_poll(); /* Disable all interrupts except pmu */ sleep_save_intrs(vias->intrs[0].line); /* Make sure the PMU is idle */ while (pmu_state != idle) pmu_poll(); /* Make sure the decrementer won't interrupt us */ asm volatile("mtdec %0" : : "r" (0x7fffffff)); /* Make sure any pending DEC interrupt occuring while we did * the above didn't re-enable the DEC */ mb(); asm volatile("mtdec %0" : : "r" (0x7fffffff)); /* Giveup the FPU */ if (current->thread.regs && (current->thread.regs->msr & MSR_FP) != 0) giveup_fpu(current); /* For 750, save backside cache setting and disable it */ save_l2cr = _get_L2CR(); /* (returns 0 if not 750) */ if (save_l2cr) _set_L2CR(0); /* Ask the PMU to put us to sleep */ pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); while (!req.complete) pmu_poll(); /* The VIA is supposed not to be restored correctly*/ save_via_state(); /* We shut down some HW */ feature_prepare_for_sleep(); grackle_pcibios_read_config_word(0,0,0x70,&pmcr1); /* Apparently, MacOS uses NAP mode for Grackle ??? */ pmcr1 &= ~(GRACKLE_DOZE|GRACKLE_SLEEP); pmcr1 |= GRACKLE_PM|GRACKLE_NAP; grackle_pcibios_write_config_word(0, 0, 0x70, pmcr1); /* Call low-level ASM sleep handler */ low_sleep_handler(); /* We're awake again, stop grackle PM */ grackle_pcibios_read_config_word(0, 0, 0x70, &pmcr1); pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP); grackle_pcibios_write_config_word(0, 0, 0x70, pmcr1); /* Restore things */ feature_wake_up(); restore_via_state(); /* Restore L2 cache */ if (save_l2cr) _set_L2CR(save_l2cr); /* Restore userland MMU context */ set_context(current->mm->context, current->mm->pgd); /* Re-enable DEC interrupts and kick DEC */ asm volatile("mtdec %0" : : "r" (0x7fffffff)); sti(); asm volatile("mtdec %0" : : "r" (0x10000000)); /* Power things up */ pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0xfc); while (!req.complete) pmu_poll(); pmu_request(&req, NULL, 2, PMU_POWER_CTRL0, PMU_POW0_ON|PMU_POW0_HARD_DRIVE); while (!req.complete) pmu_poll(); pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_ON|PMU_POW_BACKLIGHT|PMU_POW_CHARGER|PMU_POW_IRLED|PMU_POW_MEDIABAY); while (!req.complete) pmu_poll(); /* ack all pending interrupts */ timeout = 100000; interrupt_data[0] = 1; while (interrupt_data[0] || pmu_state != idle) { if (--timeout < 0) break; if (pmu_state == idle) adb_int_pending = 1; via_pmu_interrupt(0, 0, 0); udelay(10); } /* reenable interrupt controller */ sleep_restore_intrs(); /* Leave some time for HW to settle down */ mdelay(100); /* Notify drivers */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -