📄 via-pmu.c
字号:
/* 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(); pmu_blink(2); /* Restore L2 cache */ if (save_l2cr) _set_L2CR(save_l2cr); /* Restore userland MMU context */ set_context(current->active_mm->context, current->active_mm->pgd); /* Tell PMU we are ready */ pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2); while (!req.complete) pmu_poll(); pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0xfc); 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 */ openpic_sleep_restore_intrs(); /* Leave some time for HW to settle down */ mdelay(100); /* Restart jiffies & scheduling */ wakeup_decrementer(); sti(); /* Notify drivers */ broadcast_wake(); return 0;}#define PB3400_MEM_CTRL 0xf8000000#define PB3400_MEM_CTRL_SLEEP 0x70int __openfirmware powerbook_sleep_3400(void){ int ret, i, x; unsigned int hid0; unsigned long p, wait; struct adb_request sleep_req; char *mem_ctrl; unsigned int *mem_ctrl_sleep; /* first map in the memory controller registers */ mem_ctrl = ioremap(PB3400_MEM_CTRL, 0x100); if (mem_ctrl == NULL) { printk("powerbook_sleep_3400: ioremap failed\n"); return -ENOMEM; } mem_ctrl_sleep = (unsigned int *) (mem_ctrl + PB3400_MEM_CTRL_SLEEP); /* 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); /* Give the disks a little time to actually finish writing */ for (wait = jiffies + (HZ/4); time_before(jiffies, wait); ) mb(); /* 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; } /* Wait for completion of async backlight requests */ while (!bright_req_1.complete || !bright_req_2.complete || !bright_req_3.complete || !batt_req.complete) pmu_poll(); /* Disable all interrupts except pmu */ pmac_sleep_save_intrs(vias->intrs[0].line); /* 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)); /* Save the state of PCI config space for some slots */ pbook_pci_save(); /* Set the memory controller to keep the memory refreshed while we're asleep */ for (i = 0x403f; i >= 0x4000; --i) { out_be32(mem_ctrl_sleep, i); do { x = (in_be32(mem_ctrl_sleep) >> 16) & 0x3ff; } while (x == 0); if (x >= 0x100) break; } /* Ask the PMU to put us to sleep */ pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); while (!sleep_req.complete) mb(); pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1); /* displacement-flush the L2 cache - necessary? */ for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000) i = *(volatile int *)p; asleep = 1; /* Put the CPU into sleep mode */ asm volatile("mfspr %0,1008" : "=r" (hid0) :); hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP; asm volatile("mtspr 1008,%0" : : "r" (hid0)); _nmask_and_or_msr(0, MSR_POW | MSR_EE); udelay(10); /* OK, we're awake again, start restoring things */ out_be32(mem_ctrl_sleep, 0x3f); pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0); pbook_pci_restore(); /* wait for the PMU interrupt sequence to complete */ while (asleep) mb(); /* reenable interrupts */ pmac_sleep_restore_intrs(); /* Leave some time for HW to settle down */ mdelay(100); /* Restart jiffies & scheduling */ wakeup_decrementer(); sti(); /* Notify drivers */ broadcast_wake(); iounmap(mem_ctrl); return 0;}/* * Support for /dev/pmu device */#define RB_SIZE 0x10struct pmu_private { struct list_head list; int rb_get; int rb_put; struct rb_entry { unsigned short len; unsigned char data[16]; } rb_buf[RB_SIZE]; wait_queue_head_t wait; spinlock_t lock;};static LIST_HEAD(all_pmu_pvt);static spinlock_t all_pvt_lock = SPIN_LOCK_UNLOCKED;static void pmu_pass_intr(unsigned char *data, int len){ struct pmu_private *pp; struct list_head *list; int i; unsigned long flags; if (len > sizeof(pp->rb_buf[0].data)) len = sizeof(pp->rb_buf[0].data); spin_lock_irqsave(&all_pvt_lock, flags); for (list = &all_pmu_pvt; (list = list->next) != &all_pmu_pvt; ) { pp = list_entry(list, struct pmu_private, list); spin_lock(&pp->lock); i = pp->rb_put + 1; if (i >= RB_SIZE) i = 0; if (i != pp->rb_get) { struct rb_entry *rp = &pp->rb_buf[pp->rb_put]; rp->len = len; memcpy(rp->data, data, len); pp->rb_put = i; wake_up_interruptible(&pp->wait); } spin_unlock(&pp->lock); } spin_unlock_irqrestore(&all_pvt_lock, flags);}static int __openfirmware pmu_open(struct inode *inode, struct file *file){ struct pmu_private *pp; unsigned long flags; pp = kmalloc(sizeof(struct pmu_private), GFP_KERNEL); if (pp == 0) return -ENOMEM; pp->rb_get = pp->rb_put = 0; spin_lock_init(&pp->lock); init_waitqueue_head(&pp->wait); spin_lock_irqsave(&all_pvt_lock, flags); list_add(&pp->list, &all_pmu_pvt); spin_unlock_irqrestore(&all_pvt_lock, flags); file->private_data = pp; return 0;}static ssize_t __openfirmware pmu_read(struct file *file, char *buf, size_t count, loff_t *ppos){ struct pmu_private *pp = file->private_data; DECLARE_WAITQUEUE(wait, current); unsigned long flags; int ret; if (count < 1 || pp == 0) return -EINVAL; ret = verify_area(VERIFY_WRITE, buf, count); if (ret) return ret; spin_lock_irqsave(&pp->lock, flags); add_wait_queue(&pp->wait, &wait); current->state = TASK_INTERRUPTIBLE; for (;;) { ret = -EAGAIN; if (pp->rb_get != pp->rb_put) { int i = pp->rb_get; struct rb_entry *rp = &pp->rb_buf[i]; ret = rp->len; spin_unlock_irqrestore(&pp->lock, flags); if (ret > count) ret = count; if (ret > 0 && copy_to_user(buf, rp->data, ret)) ret = -EFAULT; if (++i >= RB_SIZE) i = 0; spin_lock_irqsave(&pp->lock, flags); pp->rb_get = i; } if (ret >= 0) break; if (file->f_flags & O_NONBLOCK) break; ret = -ERESTARTSYS; if (signal_pending(current)) break; spin_unlock_irqrestore(&pp->lock, flags); schedule(); spin_lock_irqsave(&pp->lock, flags); } current->state = TASK_RUNNING; remove_wait_queue(&pp->wait, &wait); spin_unlock_irqrestore(&pp->lock, flags); return ret;}static ssize_t __openfirmware pmu_write(struct file *file, const char *buf, size_t count, loff_t *ppos){ return 0;}static unsigned int pmu_fpoll(struct file *filp, poll_table *wait){ struct pmu_private *pp = filp->private_data; unsigned int mask = 0; unsigned long flags; if (pp == 0) return 0; poll_wait(filp, &pp->wait, wait); spin_lock_irqsave(&pp->lock, flags); if (pp->rb_get != pp->rb_put) mask |= POLLIN; spin_unlock_irqrestore(&pp->lock, flags); return mask;}static int pmu_release(struct inode *inode, struct file *file){ struct pmu_private *pp = file->private_data; unsigned long flags; lock_kernel(); if (pp != 0) { file->private_data = 0; spin_lock_irqsave(&all_pvt_lock, flags); list_del(&pp->list); spin_unlock_irqrestore(&all_pvt_lock, flags); kfree(pp); } unlock_kernel(); return 0;}/* Note: removed __openfirmware here since it causes link errors */static int pmu_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg){ int error; switch (cmd) { case PMU_IOC_SLEEP: if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (sleep_in_progress) return -EBUSY; sleep_in_progress = 1; switch (pmu_kind) { case PMU_OHARE_BASED: error = powerbook_sleep_3400(); break; case PMU_HEATHROW_BASED: case PMU_PADDINGTON_BASED: error = powerbook_sleep_G3(); break; case PMU_KEYLARGO_BASED: error = powerbook_sleep_Core99(); break; default: error = -ENOSYS; } sleep_in_progress = 0; return error; case PMU_IOC_CAN_SLEEP: return put_user((u32)can_sleep, (__u32 *)arg);#ifdef CONFIG_PMAC_BACKLIGHT /* Backlight should have its own device or go via * the fbdev */ case PMU_IOC_GET_BACKLIGHT: if (sleep_in_progress) return -EBUSY; error = get_backlight_level(); if (error < 0) return error; return put_user(error, (__u32 *)arg); case PMU_IOC_SET_BACKLIGHT: { __u32 value; if (sleep_in_progress) return -EBUSY; error = get_user(value, (__u32 *)arg); if (!error) error = set_backlight_level(value); return error; }#endif /* CONFIG_PMAC_BACKLIGHT */ case PMU_IOC_GET_MODEL: return put_user(pmu_kind, (__u32 *)arg); case PMU_IOC_HAS_ADB: return put_user(pmu_has_adb, (__u32 *)arg); } return -EINVAL;}static struct file_operations pmu_device_fops = { read: pmu_read, write: pmu_write, poll: pmu_fpoll, ioctl: pmu_ioctl, open: pmu_open, release: pmu_release,};static struct miscdevice pmu_device = { PMU_MINOR, "pmu", &pmu_device_fops};void pmu_device_init(void){ if (via) misc_register(&pmu_device);}#endif /* CONFIG_PMAC_PBOOK */#ifdef DEBUG_SLEEPstatic inline void polled_handshake(volatile unsigned char *via){ via[B] &= ~TREQ; eieio(); while ((via[B] & TACK) != 0) ; via[B] |= TREQ; eieio(); while ((via[B] & TACK) == 0) ;}static inline void polled_send_byte(volatile unsigned char *via, int x){ via[ACR] |= SR_OUT | SR_EXT; eieio(); via[SR] = x; eieio(); polled_handshake(via);}static inline int polled_recv_byte(volatile unsigned char *via){ int x; via[ACR] = (via[ACR] & ~SR_OUT) | SR_EXT; eieio(); x = via[SR]; eieio(); polled_handshake(via); x = via[SR]; eieio(); return x;}intpmu_polled_request(struct adb_request *req){ unsigned long flags; int i, l, c; volatile unsigned char *v = via; req->complete = 1; c = req->data[0]; l = pmu_data_len[c][0]; if (l >= 0 && req->nbytes != l + 1) return -EINVAL; save_flags(flags); cli(); while (pmu_state != idle) pmu_poll(); while ((via[B] & TACK) == 0) ; polled_send_byte(v, c); if (l < 0) { l = req->nbytes - 1; polled_send_byte(v, l); } for (i = 1; i <= l; ++i) polled_send_byte(v, req->data[i]); l = pmu_data_len[c][1]; if (l < 0) l = polled_recv_byte(v); for (i = 0; i < l; ++i) req->reply[i + req->reply_len] = polled_recv_byte(v); if (req->done) (*req->done)(req); restore_flags(flags); return 0;}#endif /* DEBUG_SLEEP */EXPORT_SYMBOL(pmu_request);EXPORT_SYMBOL(pmu_poll);EXPORT_SYMBOL(pmu_suspend);EXPORT_SYMBOL(pmu_resume);#ifdef CONFIG_PMAC_PBOOKEXPORT_SYMBOL(pmu_register_sleep_notifier);EXPORT_SYMBOL(pmu_unregister_sleep_notifier);EXPORT_SYMBOL(pmu_enable_irled);EXPORT_SYMBOL(pmu_battery_count);EXPORT_SYMBOL(pmu_batteries);EXPORT_SYMBOL(pmu_power_flags);#endif /* CONFIG_PMAC_PBOOK */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -