📄 via-pmu.c
字号:
mdelay(10); broadcast_wake(); return 0;}/* Not finished yet */int __openfirmware powerbook_sleep_Core99(void){ int ret; unsigned long save_l2cr; unsigned long wait; struct adb_request req; /* 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/4); time_before(jiffies, wait); ) mb(); /* Tell PMU what events will wake us up */ pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_CLR_WAKEUP_EVENTS, 0xff, 0xff); while (!req.complete) pmu_poll(); pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_SET_WAKEUP_EVENTS, 0, PMU_PWR_WAKEUP_KEY | PMU_PWR_WAKEUP_LID_OPEN); while (!req.complete) pmu_poll(); /* Disable all interrupts except pmu */ sleep_save_intrs(vias->intrs[0].line); /* Make sure the decrementer won't interrupt us */ asm volatile("mtdec %0" : : "r" (0x7fffffff)); /* Save the state of PCI config space for some slots */ pbook_pci_save(); feature_prepare_for_sleep(); /* For 750, save backside cache setting and disable it */ save_l2cr = _get_L2CR(); /* (returns 0 if not 750) */ if (save_l2cr) _set_L2CR(0); if (current->thread.regs && (current->thread.regs->msr & MSR_FP) != 0) giveup_fpu(current); /* Ask the PMU to put us to sleep */ pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); while (!req.complete) mb(); cli(); while (pmu_state != idle) pmu_poll(); /* Call low-level ASM sleep handler */ low_sleep_handler(); /* Make sure the PMU is idle */ while (pmu_state != idle) pmu_poll(); sti(); feature_wake_up(); pbook_pci_restore(); set_context(current->mm->context, current->mm->pgd); /* Restore L2 cache */ if (save_l2cr) _set_L2CR(save_l2cr | 0x200000); /* set invalidate bit */ /* reenable interrupts */ sleep_restore_intrs(); /* Tell PMU we are ready */ pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2); while (!req.complete) pmu_poll(); /* Notify drivers */ mdelay(10); broadcast_wake(); return 0;}#define PB3400_MEM_CTRL ((unsigned int *)0xf8000070)int __openfirmware powerbook_sleep_3400(void){ int ret, i, x; unsigned long msr; unsigned int hid0; unsigned long p, wait; struct adb_request sleep_req; /* 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/4); time_before(jiffies, wait); ) mb(); /* Disable all interrupts except pmu */ sleep_save_intrs(vias->intrs[0].line); /* Make sure the decrementer won't interrupt us */ 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(PB3400_MEM_CTRL, i); do { x = (in_be32(PB3400_MEM_CTRL) >> 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(); /* 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)); save_flags(msr); msr |= MSR_POW | MSR_EE; restore_flags(msr); udelay(10); /* OK, we're awake again, start restoring things */ out_be32(PB3400_MEM_CTRL, 0x3f); pbook_pci_restore(); /* wait for the PMU interrupt sequence to complete */ while (asleep) mb(); /* reenable interrupts */ sleep_restore_intrs(); /* Notify drivers */ broadcast_wake(); return 0;}/* * Support for /dev/pmu device */#define RB_SIZE 10struct 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); 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_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); int ret; if (count < 1 || pp == 0) return -EINVAL; ret = verify_area(VERIFY_WRITE, buf, count); if (ret) return ret; add_wait_queue(&pp->wait, &wait); current->state = TASK_INTERRUPTIBLE; for (;;) { ret = -EAGAIN; spin_lock(&pp->lock); if (pp->rb_get != pp->rb_put) { int i = pp->rb_get; struct rb_entry *rp = &pp->rb_buf[i]; ret = rp->len; if (ret > count) ret = count; if (ret > 0 && copy_to_user(buf, rp->data, ret)) ret = -EFAULT; if (++i >= RB_SIZE) i = 0; pp->rb_get = i; } spin_unlock(&pp->lock); if (ret >= 0) break; if (file->f_flags & O_NONBLOCK) break; ret = -ERESTARTSYS; if (signal_pending(current)) break; schedule(); } current->state = TASK_RUNNING; remove_wait_queue(&pp->wait, &wait); 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; if (pp == 0) return 0; poll_wait(filp, &pp->wait, wait); spin_lock(&pp->lock); if (pp->rb_get != pp->rb_put) mask |= POLLIN; spin_unlock(&pp->lock); 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: 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;#if 0 /* Not ready yet */ case PMU_KEYLARGO_BASED: error = powerbook_sleep_Core99(); break;#endif default: error = -ENOSYS; } return error;#ifdef CONFIG_PMAC_BACKLIGHT /* Backlight should have its own device or go via * the fbdev */ case PMU_IOC_GET_BACKLIGHT: error = get_backlight_level(); if (error < 0) return error; return put_user(error, (__u32 *)arg); case PMU_IOC_SET_BACKLIGHT: { __u32 value; 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 */#if 0static 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(); 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 /* 0 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -