📄 via-pmu.c
字号:
udelay(10); }}/* New PMU seems to be very sensitive to those timings, so we make sure * PCI is flushed immediately */static void __openfirmwaresend_byte(int x){ volatile unsigned char *v = via; out_8(&v[ACR], in_8(&v[ACR]) | SR_OUT | SR_EXT); out_8(&v[SR], x); out_8(&v[B], in_8(&v[B]) & ~TREQ); /* assert TREQ */ (void)in_8(&v[B]);}static void __openfirmwarerecv_byte(){ volatile unsigned char *v = via; out_8(&v[ACR], (in_8(&v[ACR]) & ~SR_OUT) | SR_EXT); in_8(&v[SR]); /* resets SR */ out_8(&v[B], in_8(&v[B]) & ~TREQ); (void)in_8(&v[B]);}static volatile int disable_poll;static void __openfirmwarepmu_start(){ struct adb_request *req; /* assert pmu_state == idle */ /* get the packet to send */ req = current_req; if (req == 0 || pmu_state != idle || (/*req->reply_expected && */req_awaiting_reply)) return; pmu_state = sending; data_index = 1; data_len = pmu_data_len[req->data[0]][0]; /* Sounds safer to make sure ACK is high before writing. This helped * kill a problem with ADB and some iBooks */ wait_for_ack(); /* set the shift register to shift out and send a byte */ send_byte(req->data[0]);}void __openfirmwarepmu_poll(){ if (!via) return; if (disable_poll) return; /* Kicks ADB read when PMU is suspended */ if (pmu_suspended) adb_int_pending = 1; do { via_pmu_interrupt(0, 0, 0); } while (pmu_suspended && (adb_int_pending || pmu_state != idle || req_awaiting_reply));}/* This function loops until the PMU is idle and prevents it from * anwsering to ADB interrupts. pmu_request can still be called. * This is done to avoid spurrious shutdowns when we know we'll have * interrupts switched off for a long time */void __openfirmwarepmu_suspend(void){ unsigned long flags;#ifdef SUSPEND_USES_PMU struct adb_request *req;#endif if (!via) return; spin_lock_irqsave(&pmu_lock, flags); pmu_suspended++; if (pmu_suspended > 1) { spin_unlock_irqrestore(&pmu_lock, flags); return; } do { spin_unlock(&pmu_lock); via_pmu_interrupt(0, 0, 0); spin_lock(&pmu_lock); if (!adb_int_pending && pmu_state == idle && !req_awaiting_reply) {#ifdef SUSPEND_USES_PMU pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0); spin_unlock_irqrestore(&pmu_lock, flags); while(!req.complete) pmu_poll();#else /* SUSPEND_USES_PMU */ if (gpio_irq >= 0) disable_irq(gpio_irq); out_8(&via[IER], CB1_INT | IER_CLR); spin_unlock_irqrestore(&pmu_lock, flags);#endif /* SUSPEND_USES_PMU */ break; } } while (1);}void __openfirmwarepmu_resume(void){ unsigned long flags; if (!via || (pmu_suspended < 1)) return; spin_lock_irqsave(&pmu_lock, flags); pmu_suspended--; if (pmu_suspended > 0) { spin_unlock_irqrestore(&pmu_lock, flags); return; } adb_int_pending = 1;#ifdef SUSPEND_USES_PMU pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask); spin_unlock_irqrestore(&pmu_lock, flags); while(!req.complete) pmu_poll();#else /* SUSPEND_USES_PMU */ if (gpio_irq >= 0) enable_irq(gpio_irq); out_8(&via[IER], CB1_INT | IER_SET); spin_unlock_irqrestore(&pmu_lock, flags); pmu_poll();#endif /* SUSPEND_USES_PMU */}static void __openfirmwarevia_pmu_interrupt(int irq, void *arg, struct pt_regs *regs){ unsigned long flags; int intr; int nloop = 0; /* This is a bit brutal, we can probably do better */ spin_lock_irqsave(&pmu_lock, flags); ++disable_poll; for (;;) { intr = in_8(&via[IFR]) & (SR_INT | CB1_INT); if (intr == 0) break; if (++nloop > 1000) { printk(KERN_DEBUG "PMU: stuck in intr loop, " "intr=%x, ier=%x pmu_state=%d\n", intr, in_8(&via[IER]), pmu_state); break; } out_8(&via[IFR], intr); if (intr & SR_INT) pmu_sr_intr(regs); if (intr & CB1_INT) adb_int_pending = 1; } if (pmu_state == idle) { if (adb_int_pending) { pmu_state = intack; /* Sounds safer to make sure ACK is high before writing. * This helped kill a problem with ADB and some iBooks */ wait_for_ack(); send_byte(PMU_INT_ACK); adb_int_pending = 0; } else if (current_req) pmu_start(); } --disable_poll; spin_unlock_irqrestore(&pmu_lock, flags);}static void __openfirmwaregpio1_interrupt(int irq, void *arg, struct pt_regs *regs){ if ((in_8(gpio_reg + 0x9) & 0x02) == 0) { adb_int_pending = 1; via_pmu_interrupt(0, 0, 0); }}static void __openfirmwarepmu_sr_intr(struct pt_regs *regs){ struct adb_request *req; int bite; if (via[B] & TREQ) { printk(KERN_ERR "PMU: spurious SR intr (%x)\n", via[B]); out_8(&via[IFR], SR_INT); return; } /* The ack may not yet be low when we get the interrupt */ while ((in_8(&via[B]) & TACK) != 0) ; /* if reading grab the byte, and reset the interrupt */ if (pmu_state == reading || pmu_state == reading_intr) bite = in_8(&via[SR]); /* reset TREQ and wait for TACK to go high */ out_8(&via[B], in_8(&via[B]) | TREQ); wait_for_ack(); switch (pmu_state) { case sending: req = current_req; if (data_len < 0) { data_len = req->nbytes - 1; send_byte(data_len); break; } if (data_index <= data_len) { send_byte(req->data[data_index++]); break; } req->sent = 1; data_len = pmu_data_len[req->data[0]][1]; if (data_len == 0) { pmu_state = idle; current_req = req->next; if (req->reply_expected) req_awaiting_reply = req; else { spin_unlock(&pmu_lock); pmu_done(req); spin_lock(&pmu_lock); } } else { pmu_state = reading; data_index = 0; reply_ptr = req->reply + req->reply_len; recv_byte(); } break; case intack: data_index = 0; data_len = -1; pmu_state = reading_intr; reply_ptr = interrupt_data; recv_byte(); break; case reading: case reading_intr: if (data_len == -1) { data_len = bite; if (bite > 32) printk(KERN_ERR "PMU: bad reply len %d\n", bite); } else if (data_index < 32) { reply_ptr[data_index++] = bite; } if (data_index < data_len) { recv_byte(); break; } if (pmu_state == reading_intr) { spin_unlock(&pmu_lock); pmu_handle_data(interrupt_data, data_index, regs); spin_lock(&pmu_lock); } else { req = current_req; current_req = req->next; req->reply_len += data_index; spin_unlock(&pmu_lock); pmu_done(req); spin_lock(&pmu_lock); } pmu_state = idle; break; default: printk(KERN_ERR "via_pmu_interrupt: unknown state %d?\n", pmu_state); }}static void __openfirmwarepmu_done(struct adb_request *req){ req->complete = 1; if (req->done) (*req->done)(req);}/* Interrupt data could be the result data from an ADB cmd */static void __openfirmwarepmu_handle_data(unsigned char *data, int len, struct pt_regs *regs){ asleep = 0; if (drop_interrupts || len < 1) { adb_int_pending = 0; return; } /* Note: for some reason, we get an interrupt with len=1, * data[0]==0 after each normal ADB interrupt, at least * on the Pismo. Still investigating... --BenH */ if (data[0] & PMU_INT_ADB) { if ((data[0] & PMU_INT_ADB_AUTO) == 0) { struct adb_request *req = req_awaiting_reply; if (req == 0) { printk(KERN_ERR "PMU: extra ADB reply\n"); return; } req_awaiting_reply = 0; if (len <= 2) req->reply_len = 0; else { memcpy(req->reply, data + 1, len - 1); req->reply_len = len - 1; } pmu_done(req); } 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 { /* Sound/brightness button pressed */ if ((data[0] & PMU_INT_SNDBRT) && len == 3) {#ifdef CONFIG_PMAC_BACKLIGHT set_backlight_level(data[1] >> 4);#endif }#ifdef CONFIG_PMAC_PBOOK /* Environement or tick interrupt, query batteries */ if (pmu_battery_count && (data[0] & PMU_INT_TICK)) { if ((--query_batt_timer) == 0) { query_battery_state(); query_batt_timer = BATTERY_POLLING_COUNT; } } else if (pmu_battery_count && (data[0] & PMU_INT_ENVIRONMENT)) query_battery_state(); if (data[0]) pmu_pass_intr(data, len);#endif /* CONFIG_PMAC_PBOOK */ }}#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();}void __openfirmwarepmu_restart(void){ struct adb_request req; cli(); drop_interrupts = 1; if (pmu_kind != PMU_KEYLARGO_BASED) { 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(); drop_interrupts = 1; if (pmu_kind != PMU_KEYLARGO_BASED) { 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);#ifdef CONFIG_PMstatic intgeneric_notify_sleep(struct pmu_sleep_notifier *self, int when){ switch (when) { case PBOOK_SLEEP_NOW: if (pm_send_all(PM_SUSPEND, (void *)3)) return PBOOK_SLEEP_REJECT; break; case PBOOK_WAKE: (void) pm_send_all(PM_RESUME, (void *)0); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -