📄 via-pmu.c
字号:
/* Construct and send a pmu request */intpmu_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); req->reply_len = 0; req->reply_expected = 0; return pmu_queue_request(req);}intpmu_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 = NULL; 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 inline voidwait_for_ack(void){ /* Sightly increased the delay, I had one occurrence 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 inline voidsend_byte(int x){ volatile unsigned char __iomem *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 inline voidrecv_byte(void){ volatile unsigned char __iomem *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 inline voidpmu_done(struct adb_request *req){ void (*done)(struct adb_request *) = req->done; mb(); req->complete = 1; /* Here, we assume that if the request has a done member, the * struct request will survive to setting req->complete to 1 */ if (done) (*done)(req);}static voidpmu_start(void){ 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]);}voidpmu_poll(void){ if (!via) return; if (disable_poll) return; via_pmu_interrupt(0, NULL);}voidpmu_poll_adb(void){ if (!via) return; if (disable_poll) return; /* Kicks ADB read when PMU is suspended */ adb_int_pending = 1; do { via_pmu_interrupt(0, NULL); } while (pmu_suspended && (adb_int_pending || pmu_state != idle || req_awaiting_reply));}voidpmu_wait_complete(struct adb_request *req){ if (!via) return; while((pmu_state != idle && pmu_state != locked) || !req->complete) via_pmu_interrupt(0, NULL);}/* 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 */voidpmu_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_irqrestore(&pmu_lock, flags); if (req_awaiting_reply) adb_int_pending = 1; via_pmu_interrupt(0, NULL); spin_lock_irqsave(&pmu_lock, flags); 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_nosync(gpio_irq); out_8(&via[IER], CB1_INT | IER_CLR); spin_unlock_irqrestore(&pmu_lock, flags);#endif /* SUSPEND_USES_PMU */ break; } } while (1);}voidpmu_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 */}/* Interrupt data could be the result data from an ADB cmd */static voidpmu_handle_data(unsigned char *data, int len){ unsigned char ints, pirq; int i = 0; asleep = 0; if (drop_interrupts || len < 1) { adb_int_pending = 0; pmu_irq_stats[8]++; return; } /* Get PMU interrupt mask */ ints = data[0]; /* Record zero interrupts for stats */ if (ints == 0) pmu_irq_stats[9]++; /* Hack to deal with ADB autopoll flag */ if (ints & PMU_INT_ADB) ints &= ~(PMU_INT_ADB_AUTO | PMU_INT_AUTO_SRQ_POLL);next: if (ints == 0) { if (i > pmu_irq_stats[10]) pmu_irq_stats[10] = i; return; } for (pirq = 0; pirq < 8; pirq++) if (ints & (1 << pirq)) break; pmu_irq_stats[pirq]++; i++; ints &= ~(1 << pirq); /* 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 ((1 << pirq) & 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 = NULL; if (len <= 2) req->reply_len = 0; else { memcpy(req->reply, data + 1, len - 1); req->reply_len = len - 1; } pmu_done(req); } else { if (len == 4 && data[1] == 0x2c) { extern int xmon_wants_key, xmon_adb_keycode; if (xmon_wants_key) { xmon_adb_keycode = data[2]; return; } }#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, 1);#endif /* CONFIG_ADB */ } } /* Sound/brightness button pressed */ else if ((1 << pirq) & PMU_INT_SNDBRT) {#ifdef CONFIG_PMAC_BACKLIGHT if (len == 3) pmac_backlight_set_legacy_brightness_pmu(data[1] >> 4);#endif } /* Tick interrupt */ else if ((1 << pirq) & PMU_INT_TICK) { /* Environement or tick interrupt, query batteries */ if (pmu_battery_count) { if ((--query_batt_timer) == 0) { query_battery_state(); query_batt_timer = BATTERY_POLLING_COUNT; } } } else if ((1 << pirq) & PMU_INT_ENVIRONMENT) { if (pmu_battery_count) query_battery_state(); pmu_pass_intr(data, len); /* len == 6 is probably a bad check. But how do I * know what PMU versions send what events here? */ if (len == 6) { via_pmu_event(PMU_EVT_POWER, !!(data[1]&8)); via_pmu_event(PMU_EVT_LID, data[1]&1); } } else { pmu_pass_intr(data, len); } goto next;}static struct adb_request*pmu_sr_intr(void){ struct adb_request *req; int bite = 0; if (via[B] & TREQ) { printk(KERN_ERR "PMU: spurious SR intr (%x)\n", via[B]); out_8(&via[IFR], SR_INT); return NULL; } /* 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 return req; } 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[int_data_last]; recv_byte(); if (gpio_irq >= 0 && !gpio_irq_enabled) { enable_irq(gpio_irq); gpio_irq_enabled = 1; } 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) { pmu_state = idle; int_data_state[int_data_last] = int_data_ready; interrupt_data_len[int_data_last] = data_len; } else { req = current_req; /* * For PMU sleep and freq change requests, we lock the * PMU until it's explicitly unlocked. This avoids any * spurrious event polling getting in */ current_req = req->next; req->reply_len += data_index; if (req->data[0] == PMU_SLEEP || req->data[0] == PMU_CPU_SPEED) pmu_state = locked; else pmu_state = idle; return req; } break; default: printk(KERN_ERR "via_pmu_interrupt: unknown state %d?\n", pmu_state); } return NULL;}static irqreturn_tvia_pmu_interrupt(int irq, void *arg){ unsigned long flags; int intr; int nloop = 0; int int_data = -1; struct adb_request *req = NULL; int handled = 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; handled = 1; 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 & CB1_INT) { adb_int_pending = 1; pmu_irq_stats[0]++; } if (intr & SR_INT) { req = pmu_sr_intr(); if (req) break; } }recheck: if (pmu_state == idle) { if (adb_int_pending) { if (int_data_state[0] == int_data_empty) int_data_last = 0; else if (int_data_state[1] == int_data_empty) int_data_last = 1; else goto no_free_slot; pmu_state = intack; int_data_state[int_data_last] = int_data_fill; /* 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(); }no_free_slot: /* Mark the oldest buffer for flushing */ if (int_data_state[!int_data_last] == int_data_ready) { int_data_state[!int_data_last] = int_data_flush; int_data = !int_data_last; } else if (int_data_state[int_data_last] == int_data_ready) { int_data_state[int_data_last] = int_data_flush; int_data = int_data_last;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -