📄 via-pmu.c
字号:
while (!req->complete) pmu_poll(); return 0;}/* Enable/disable autopolling */static int __openfirmwarepmu_adb_autopoll(int devs){ struct adb_request req; if ((vias == NULL) || (!pmu_fully_inited) || !pmu_has_adb) return -ENXIO; if (devs) { adb_dev_map = devs; pmu_request(&req, NULL, 5, PMU_ADB_CMD, 0, 0x86, adb_dev_map >> 8, adb_dev_map); pmu_adb_flags = 2; } else { pmu_request(&req, NULL, 1, PMU_ADB_POLL_OFF); pmu_adb_flags = 0; } while (!req.complete) pmu_poll(); return 0;}/* Reset the ADB bus */static int __openfirmwarepmu_adb_reset_bus(void){ struct adb_request req; int save_autopoll = adb_dev_map; if ((vias == NULL) || (!pmu_fully_inited) || !pmu_has_adb) return -ENXIO; /* anyone got a better idea?? */ pmu_adb_autopoll(0); req.nbytes = 5; req.done = NULL; req.data[0] = PMU_ADB_CMD; req.data[1] = 0; req.data[2] = ADB_BUSRESET; /* 3 ??? */ req.data[3] = 0; req.data[4] = 0; req.reply_len = 0; req.reply_expected = 1; if (pmu_queue_request(&req) != 0) { printk(KERN_ERR "pmu_adb_reset_bus: pmu_queue_request failed\n"); return -EIO; } while (!req.complete) pmu_poll(); if (save_autopoll != 0) pmu_adb_autopoll(save_autopoll); return 0;}#endif /* CONFIG_ADB *//* Construct and send a pmu request */int __openfirmwarepmu_request(struct adb_request *req, void (*done)(struct adb_request *), int nbytes, ...){ va_list list; int i; if (vias == NULL) return -ENXIO; if (nbytes < 0 || nbytes > 32) { printk(KERN_ERR "pmu_request: bad nbytes (%d)\n", nbytes); req->complete = 1; return -EINVAL; } req->nbytes = nbytes; req->done = done; va_start(list, nbytes); for (i = 0; i < nbytes; ++i) req->data[i] = va_arg(list, int); va_end(list); if (pmu_data_len[req->data[0]][1] != 0) { req->reply[0] = ADB_RET_OK; req->reply_len = 1; } else req->reply_len = 0; req->reply_expected = 0; return pmu_queue_request(req);}int __openfirmwarepmu_queue_request(struct adb_request *req){ unsigned long flags; int nsend; if (via == NULL) { req->complete = 1; return -ENXIO; } if (req->nbytes <= 0) { req->complete = 1; return 0; } nsend = pmu_data_len[req->data[0]][0]; if (nsend >= 0 && req->nbytes != nsend + 1) { req->complete = 1; return -EINVAL; } req->next = 0; req->sent = 0; req->complete = 0; spin_lock_irqsave(&pmu_lock, flags); if (current_req != 0) { last_req->next = req; last_req = req; } else { current_req = req; last_req = req; if (pmu_state == idle) pmu_start(); } spin_unlock_irqrestore(&pmu_lock, flags); return 0;}static void __openfirmwarewait_for_ack(void){ /* Sightly increased the delay, I had one occurence of the message * reported */ int timeout = 4000; while ((in_8(&via[B]) & TACK) == 0) { if (--timeout < 0) { printk(KERN_ERR "PMU not responding (!ack)\n"); return; } 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, 0xfc); 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; while ((intr = in_8(&via[IFR])) != 0) { if (++nloop > 1000) { printk(KERN_DEBUG "PMU: stuck in intr loop, " "intr=%x pmu_state=%d\n", intr, pmu_state); break; } if (intr & SR_INT) pmu_sr_intr(regs); else if (intr & CB1_INT) { adb_int_pending = 1; out_8(&via[IFR], CB1_INT); } intr &= ~(SR_INT | CB1_INT); if (intr != 0) { out_8(&via[IFR], intr); } } /* This is not necessary except if synchronous ADB requests are done * with interrupts off, which should not happen. Since I'm not sure * this "wiring" will remain, I'm commenting it out for now. Please do * not remove. -- BenH. */#if 0 if (gpio_reg && !pmu_suspended && (in_8(gpio_reg + 0x9) & 0x02) == 0) adb_int_pending = 1;#endif 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){ 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; } /* This one seems to appear with PMU99. According to OF methods, * the protocol didn't change... */ if (via[B] & TACK) { while ((in_8(&via[B]) & TACK) != 0) ; } /* reset TREQ and wait for TACK to go high */ out_8(&via[B], in_8(&via[B]) | TREQ); wait_for_ack(); /* if reading grab the byte, and reset the interrupt */ if (pmu_state == reading || pmu_state == reading_intr) bite = in_8(&via[SR]); out_8(&via[IFR], SR_INT); 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 { 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 (len < 1) {// xmon_printk("empty ADB\n"); adb_int_pending = 0; return; } 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -