📄 via-pmu.c
字号:
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 {#ifndef HACKED_PCI_SAVE u16 command; u16 cache_lat; u16 intr; u32 rom_address;#else u32 config[16];#endif } *pbook_pci_saves;static int pbook_npci_saves;static void __pmacpbook_alloc_pci_save(void){ int npci; struct pci_dev *pd = NULL; npci = 0; while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) { ++npci; } if (npci == 0) return; pbook_pci_saves = (struct pci_save *) kmalloc(npci * sizeof(struct pci_save), GFP_KERNEL); pbook_npci_saves = npci;}static void __pmacpbook_free_pci_save(void){ if (pbook_pci_saves == NULL) return; kfree(pbook_pci_saves); pbook_pci_saves = NULL; pbook_npci_saves = 0;}static void __pmacpbook_pci_save(void){ struct pci_save *ps = pbook_pci_saves; struct pci_dev *pd = NULL; int npci = pbook_npci_saves; if (ps == NULL) return; while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) { if (npci-- == 0) return;#ifndef HACKED_PCI_SAVE 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);#else int i; for (i=1;i<16;i++) pci_read_config_dword(pd, i<<4, &ps->config[i]);#endif ++ps; }}/* For this to work, we must take care of a few things: If gmac was enabled * during boot, it will be in the pci dev list. If it's disabled at this point * (and it will probably be), then you can't access it's config space. */static void __pmacpbook_pci_restore(void){ u16 cmd; struct pci_save *ps = pbook_pci_saves - 1; struct pci_dev *pd = NULL; int npci = pbook_npci_saves; int j; while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {#ifdef HACKED_PCI_SAVE int i; if (npci-- == 0) return; ps++; for (i=2;i<16;i++) pci_write_config_dword(pd, i<<4, ps->config[i]); pci_write_config_dword(pd, 4, ps->config[1]);#else if (npci-- == 0) return; 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; }#endif }}#ifdef DEBUG_SLEEP/* N.B. This doesn't work on the 3400 */void __pmacpmu_blink(int n){ struct adb_request req; memset(&req, 0, sizeof(req)); for (; n > 0; --n) { req.nbytes = 4; req.done = NULL; req.data[0] = 0xee; req.data[1] = 4; req.data[2] = 0; req.data[3] = 1; req.reply[0] = ADB_RET_OK; req.reply_len = 1; req.reply_expected = 0; pmu_polled_request(&req); mdelay(50); req.nbytes = 4; req.done = NULL; req.data[0] = 0xee; req.data[1] = 4; req.data[2] = 0; req.data[3] = 0; req.reply[0] = ADB_RET_OK; req.reply_len = 1; req.reply_expected = 0; pmu_polled_request(&req); mdelay(50); } mdelay(50);}#endif/* * Put the powerbook to sleep. */ static u32 save_via[8] __pmacdata;static void __pmacsave_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 __pmacrestore_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);}static int __pmacpmac_suspend_devices(void){ int ret; pm_prepare_console(); /* Notify old-style device drivers & userland */ ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, PBOOK_SLEEP_REJECT); if (ret != PBOOK_SLEEP_OK) { printk(KERN_ERR "Sleep rejected by drivers\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. That * could be acheived using the refrigerator for processes * that swsusp uses */ sys_sync(); /* 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(KERN_ERR "Driver sleep failed\n"); return -EBUSY; } /* Send suspend call to devices, hold the device core's dpm_sem */ ret = device_suspend(PM_SUSPEND_MEM); if (ret) { printk(KERN_ERR "Driver sleep failed\n"); broadcast_wake(); return -EBUSY; } preempt_disable(); /* Make sure the decrementer won't interrupt us */ asm volatile("mtdec %0" : : "r" (0x7fffffff)); /* Make sure any pending DEC interrupt occurring while we did * the above didn't re-enable the DEC */ mb(); asm volatile("mtdec %0" : : "r" (0x7fffffff)); /* We can now disable MSR_EE. This code of course works properly only * on UP machines... For SMP, if we ever implement sleep, we'll have to * stop the "other" CPUs way before we do all that stuff. */ local_irq_disable(); /* Broadcast power down irq * This isn't that useful in most cases (only directly wired devices can * use this but still... This will take care of sysdev's as well, so * we exit from here with local irqs disabled and PIC off. */ ret = device_power_down(PM_SUSPEND_MEM); if (ret) { wakeup_decrementer(); local_irq_enable(); preempt_enable(); device_resume(); broadcast_wake(); printk(KERN_ERR "Driver powerdown failed\n"); return -EBUSY; } /* Wait for completion of async backlight requests */ while (!bright_req_1.complete || !bright_req_2.complete || !batt_req.complete) pmu_poll(); /* Giveup the lazy FPU & vec so we don't have to back them * up from the low level code */ enable_kernel_fp();#ifdef CONFIG_ALTIVEC if (cur_cpu_spec[0]->cpu_features & CPU_FTR_ALTIVEC) enable_kernel_altivec();#endif /* CONFIG_ALTIVEC */ return 0;}static int __pmacpmac_wakeup_devices(void){ mdelay(100); /* Power back up system devices (including the PIC) */ device_power_up(); pmu_blink(1); /* Force a poll of ADB interrupts */ adb_int_pending = 1; via_pmu_interrupt(0, NULL, NULL); /* Restart jiffies & scheduling */ wakeup_decrementer(); /* Re-enable local CPU interrupts */ local_irq_enable(); pmu_blink(1); preempt_enable(); /* Resume devices */ device_resume(); /* Notify old style drivers */ broadcast_wake(); pm_restore_console(); return 0;}#define GRACKLE_PM (1<<7)#define GRACKLE_DOZE (1<<5)#define GRACKLE_NAP (1<<4)#define GRACKLE_SLEEP (1<<3)int __pmacpowerbook_sleep_grackle(void){ unsigned long save_l2cr; unsigned short pmcr1; struct adb_request req; int ret; struct pci_dev *grackle; grackle = pci_find_slot(0, 0); if (!grackle) return -ENODEV; ret = pmac_suspend_devices(); if (ret) { printk(KERN_ERR "Sleep rejected by devices\n"); return ret; } /* 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); pmu_wait_complete(&req); pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_OFF|PMU_POW_BACKLIGHT|PMU_POW_IRLED|PMU_POW_MEDIABAY); pmu_wait_complete(&req); /* For 750, save backside cache setting and disable it */ save_l2cr = _get_L2CR(); /* (returns -1 if not available) */ if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) _set_L2CR(save_l2cr & 0x7fffffff); if (!__fake_sleep) { /* Ask the PMU to put us to sleep */ pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); pmu_wait_complete(&req); } /* The VIA is supposed not to be restored correctly*/ save_via_state(); /* We shut down some HW */ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1); pci_read_config_word(grackle, 0x70, &pmcr1); /* Apparently, MacOS uses NAP mode for Grackle ??? */ pmcr1 &= ~(GRACKLE_DOZE|GRACKLE_SLEEP); pmcr1 |= GRACKLE_PM|GRACKLE_NAP; pci_write_config_word(grackle, 0x70, pmcr1); /* Call low-level ASM sleep handler */ if (__fake_sleep) mdelay(5000); else low_sleep_handler(); /* We're awake again, stop grackle PM */ pci_read_config_word(grackle, 0x70, &pmcr1); pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP); pci_write_config_word(grackle, 0x70, pmcr1); /* Make sure the PMU is idle */ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0); restore_via_state(); /* Restore L2 cache */ if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) _set_L2CR(save_l2cr); /* Restore userland MMU context */ set_context(current->active_mm->context, current->active_mm->pgd); /* Power things up */ pmu_unlock(); pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask); pmu_wait_complete(&req); pmu_request(&req, NULL, 2, PMU_POWER_CTRL0, PMU_POW0_ON|PMU_POW0_HARD_DRIVE); pmu_wait_complete(&req); pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_ON|PMU_POW_BACKLIGHT|PMU_POW_CHARGER|PMU_POW_IRLED|PMU_POW_MEDIABAY); pmu_wait_complete(&req); pmac_wakeup_devices(); return 0;}static int __pmacpowerbook_sleep_Core99(void){ unsigned long save_l2cr; unsigned long save_l3cr; struct adb_request req; int ret; if (!can_sleep) { printk(KERN_ERR "Sleep mode not supported on this machine\n"); return -ENOSYS; } ret = pmac_suspend_devices(); if (ret) { printk(KERN_ERR "Sleep rejected by devices\n"); return ret; } /* Tell PMU what events will wake us up */ pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_CLR_WAKEUP_EVENTS, 0xff, 0xff); pmu_wait_complete(&req); pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_SET_WAKEUP_EVENTS, 0, PMU_PWR_WAKEUP_KEY | (option_lid_wakeup ? PMU_PWR_WAKEUP_LID_OPEN : 0)); pmu_wait_complete(&req); /* Save & disable L2 and L3 caches*/ save_l3cr = _get_L3CR(); /* (returns -1 if not available) */ save_l2cr = _get_L2CR(); /* (returns -1 if not available) */ if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0) _set_L3CR(save_l3cr & 0x7fffffff); if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) _set_L2CR(save_l2cr & 0x7fffffff); /* Save the state of PCI config space for some slots */ //pbook_pci_save(); if (!__fake_sleep) { /* Ask the PMU to put us to sleep */ pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); pmu_wait_complete(&req); } /* The VIA is supposed not to be restored correctly*/ save_via_state(); /* Shut down various ASICs. There's a chance that we can no longer * talk to the PMU after this, so I moved it to _after_ sending the * sleep command to it. Still need to be checked. */ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1); /* Call low-level ASM sleep handler */ if (__fake_sleep) mdelay(5000); else low_sleep_handler(); /* Restore Apple core ASICs state */ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0); /* Restore VIA */ restore_via_state(); /* Restore PCI config space. This should be overridable by PCI device * drivers as some of them may need special restore code. That's yet * another issue that should be handled by the common code properly, * maybe one day ? */ /* Don't restore PCI for now, it crashes. Maybe unnecessary on pbook */ //pbook_pci_restore(); /* Restore L2 cache */ if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) _set_L2CR(save_l2cr); /* Restore L3 cache */ if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0) _set_L3CR(save_l3cr); /* Restore userland MMU context */ set_context(current->active_mm->context, current->active_mm->pgd); /* Tell PMU we are ready */ pmu_unlock(); pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2); pmu_wait_complete(&req); pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask); pmu_wait_complete(&req); pmu_blink(1); pmac_wakeup_devices(); return 0;}#define PB3400_MEM_CTRL 0xf8000000#define PB3400_MEM_CTRL_SLEEP 0x70static int __pmacpowerbook_sleep_3400(void){ int ret, i, x; unsigned int hid0; unsigned long p; struct adb_request sleep_req; char *mem_ctrl; unsigned int *mem_ctrl_sleep; /* first map in the memory contro
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -