patch-2.2.12 linux/drivers/macintosh/via-pmu.c
Next file: linux/drivers/net/Config.in
Previous file: linux/drivers/macintosh/mac_keyb.c
Back to the patch index
Back to the overall index
- Lines: 757
- Date:
Wed Aug 25 17:29:48 1999
- Orig file:
v2.2.11/linux/drivers/macintosh/via-pmu.c
- Orig date:
Mon Aug 9 16:05:56 1999
diff -u --recursive --new-file v2.2.11/linux/drivers/macintosh/via-pmu.c linux/drivers/macintosh/via-pmu.c
@@ -1,3 +1,4 @@
+
/*
* Device driver for the via-pmu on Apple Powermacs.
*
@@ -33,6 +34,7 @@
#include <asm/irq.h>
#include <asm/feature.h>
#include <asm/uaccess.h>
+#include <asm/mmu_context.h>
/* Misc minor number allocated for /dev/pmu */
#define PMU_MINOR 154
@@ -71,6 +73,7 @@
#define IER_SET 0x80 /* set bits in IER */
#define IER_CLR 0 /* clear bits in IER */
#define SR_INT 0x04 /* Shift register full/empty */
+#define CB2_INT 0x08
#define CB1_INT 0x10 /* transition on CB1 input */
static enum pmu_state {
@@ -125,6 +128,16 @@
pmu_poll
};
+extern void low_sleep_handler(void);
+extern void sleep_save_intrs(int);
+extern void sleep_restore_intrs(void);
+
+extern int grackle_pcibios_read_config_word(unsigned char bus,
+ unsigned char dev_fn, unsigned char offset, unsigned short *val);
+
+extern int grackle_pcibios_write_config_word(unsigned char bus,
+ unsigned char dev_fn, unsigned char offset, unsigned short val);
+
/*
* This table indicates for each PMU opcode:
* - the number of data bytes to be sent with the command, or -1
@@ -168,13 +181,21 @@
/*f8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
};
+static char *pbook_type[] = {
+ "Unknown PowerBook",
+ "PowerBook 2400/3400/3500(G3)",
+ "PowerBook G3 Series",
+ "1999 PowerBook G3",
+};
-void __openfirmware
+int __openfirmware
find_via_pmu()
{
+ if (via != 0)
+ return 1;
vias = find_devices("via-pmu");
if (vias == 0)
- return;
+ return 0;
if (vias->next != 0)
printk(KERN_WARNING "Warning: only using 1st via-pmu\n");
@@ -196,12 +217,14 @@
printk(KERN_ERR "via-pmu: %d addresses, %d interrupts!\n",
vias->n_addrs, vias->n_intrs);
if (vias->n_addrs < 1 || vias->n_intrs < 1)
- return;
+ return 0;
}
if (vias->parent->name && ((strcmp(vias->parent->name, "ohare") == 0)
|| device_is_compatible(vias->parent, "ohare")))
pmu_kind = PMU_OHARE_BASED;
+ else if (device_is_compatible(vias->parent, "paddington"))
+ pmu_kind = PMU_PADDINGTON_BASED;
else if (device_is_compatible(vias->parent, "heathrow"))
pmu_kind = PMU_HEATHROW_BASED;
else
@@ -220,9 +243,9 @@
if (via)
printk(KERN_INFO "PMU driver initialized for %s\n",
- (pmu_kind == PMU_OHARE_BASED) ? "PowerBook 2400/3400/3500(G3)" :
- ((pmu_kind == PMU_HEATHROW_BASED) ? "PowerBook G3 Series" :
- "Unknown PowerBook"));
+ pbook_type[pmu_kind]);
+
+ return via != 0;
}
void __openfirmware
@@ -517,19 +540,25 @@
static void __openfirmware
send_byte(int x)
{
- out_8(&via[ACR], 0x1c);
- out_8(&via[SR], x);
- out_8(&via[B], via[B] & ~0x10); /* assert TREQ */
+ 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 */
}
static void __openfirmware
recv_byte()
{
- out_8(&via[ACR], 0x0c);
- in_8(&via[SR]); /* resets SR */
- out_8(&via[B], via[B] & ~0x10);
+ 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]) & ~0x10);
}
+static int disable_poll;
+
static void __openfirmware
pmu_start()
{
@@ -549,7 +578,9 @@
data_len = pmu_data_len[req->data[0]][0];
/* set the shift register to shift out and send a byte */
+ ++disable_poll;
send_byte(req->data[0]);
+ --disable_poll;
out:
restore_flags(flags);
@@ -560,6 +591,8 @@
{
int ie;
+ if (disable_poll)
+ return;
ie = _disable_interrupts();
if (via[IFR] & (SR_INT | CB1_INT))
via_pmu_interrupt(0, 0, 0);
@@ -572,6 +605,7 @@
int intr;
int nloop = 0;
+ ++disable_poll;
while ((intr = in_8(&via[IFR])) != 0) {
if (++nloop > 1000) {
printk(KERN_DEBUG "PMU: stuck in intr loop, "
@@ -583,11 +617,9 @@
else if (intr & CB1_INT) {
adb_int_pending = 1;
out_8(&via[IFR], CB1_INT);
- } else
- {
- /* -- Disabled printk, will happen _really_ often on
- PowerBooks ((CB2 interrupts) --
- printk(KERN_DEBUG "PMU: spurrious interrupt intr=%x\n", intr); */
+ }
+ intr &= ~(SR_INT | CB1_INT);
+ if (intr != 0) {
out_8(&via[IFR], intr);
}
}
@@ -600,6 +632,7 @@
pmu_start();
}
}
+ --disable_poll;
}
static void __openfirmware
@@ -608,17 +641,17 @@
struct adb_request *req;
int bite, timeout;
+ if (via[B] & TREQ) {
+ printk(KERN_ERR "PMU: spurious SR intr (%x)\n", via[B]);
+ out_8(&via[IFR], SR_INT);
+ return;
+ }
if (via[B] & TACK)
- printk(KERN_DEBUG "PMU: sr_intr but ack still high! (%x)\n",
+ printk(KERN_ERR "PMU: sr_intr but ack still high! (%x)\n",
via[B]);
- /* if reading grab the byte, and reset the interrupt */
- if ((via[ACR] & SR_OUT) == 0)
- bite = in_8(&via[SR]);
- out_8(&via[IFR], SR_INT);
-
/* reset TREQ and wait for TACK to go high */
- out_8(&via[B], via[B] | TREQ);
+ out_8(&via[B], in_8(&via[B]) | TREQ);
timeout = 3200;
while ((in_8(&via[B]) & TACK) == 0) {
if (--timeout < 0) {
@@ -628,6 +661,11 @@
udelay(10);
}
+ /* 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;
@@ -710,8 +748,6 @@
static void __openfirmware
pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
{
- static int show_pmu_ints = 1;
-
asleep = 0;
if (len < 1) {
adb_int_pending = 0;
@@ -733,12 +769,23 @@
}
pmu_done(req);
} else {
+#ifdef CONFIG_XMON
+ if (len == 4 && data[1] == 0x2c) {
+ extern int xmon_wants_key, xmon_pmu_keycode;
+ if (xmon_wants_key) {
+ xmon_pmu_keycode = data[2];
+ return;
+ }
+ }
+#endif /* CONFIG_XMON */
/*
- * XXX 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.
+ * 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 (!(len == 4 && data[1] == 0x2c && data[3] == 0xff
+ 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);
}
@@ -749,15 +796,6 @@
} else {
#ifdef CONFIG_PMAC_PBOOK
pmu_pass_intr(data, len);
-#else
- if (show_pmu_ints
- && !(data[0] == PMU_INT_TICK && len == 1)) {
- int i;
- printk(KERN_DEBUG "pmu intr");
- for (i = 0; i < len; ++i)
- printk(" %.2x", data[i]);
- printk("\n");
- }
#endif
}
}
@@ -773,38 +811,46 @@
struct adb_request req;
if (vias == NULL)
- return ;
+ return;
- if (on) {
- /* first call: get current backlight value */
- if (backlight_level < 0) {
- switch(pmu_kind) {
- case PMU_OHARE_BASED:
+ /* first call: get current backlight value */
+ if (on && backlight_level < 0) {
+ switch (pmu_kind) {
+ case PMU_OHARE_BASED:
pmu_request(&req, NULL, 2, 0xd9, 0);
while (!req.complete)
pmu_poll();
backlight_level = req.reply[1] >> 3;
- printk(KERN_DEBUG "pmu: controls returned bright: %d\n", (int)req.reply[1]);
break;
- case PMU_HEATHROW_BASED:
+ case PMU_HEATHROW_BASED:
+ /* We cannot use nvram_read_byte here (not yet initialized) */
pmu_request(&req, NULL, 3, PMU_READ_NVRAM, 0x14, 0xe);
while (!req.complete)
pmu_poll();
- printk(KERN_DEBUG "pmu: nvram returned bright: %d\n", (int)req.reply[1]);
backlight_level = req.reply[1];
+ printk(KERN_DEBUG "pmu: nvram returned bright: %d\n", backlight_level);
+ break;
+ case PMU_PADDINGTON_BASED:
+ /* the G3 PB 1999 has a backlight node
+ and chrp-structured nvram */
+ /* XXX should read macos's "blkt" property in nvram
+ for this node. For now this ensures that the
+ backlight doesn't go off as soon as linux boots. */
+ backlight_level = 20;
break;
- default:
+ default:
backlight_enabled = 0;
return;
}
- }
- pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
- LEVEL_TO_BRIGHT(backlight_level));
- while (!req.complete)
- pmu_poll();
+ }
+ if (on) {
+ pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
+ LEVEL_TO_BRIGHT(backlight_level));
+ while (!req.complete)
+ pmu_poll();
}
pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
- PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF));
+ PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF));
while (!req.complete)
pmu_poll();
backlight_enabled = on;
@@ -903,6 +949,68 @@
#ifdef CONFIG_PMAC_PBOOK
+static LIST_HEAD(sleep_notifiers);
+
+int
+pmu_register_sleep_notifier(struct pmu_sleep_notifier *n)
+{
+ struct list_head *list;
+ struct pmu_sleep_notifier *current;
+
+ for (list = sleep_notifiers.next; list != &sleep_notifiers;
+ list = list->next) {
+ current = list_entry(list, struct pmu_sleep_notifier, list);
+ if (n->priority > current->priority)
+ break;
+ }
+ __list_add(&n->list, list->prev, list);
+ return 0;
+}
+
+int
+pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n)
+{
+ if (n->list.next == 0)
+ return -ENOENT;
+ list_del(&n->list);
+ n->list.next = 0;
+ return 0;
+}
+
+/* Sleep is broadcast last-to-first */
+static int
+broadcast_sleep(int when, int can_cancel)
+{
+ int ret = PBOOK_SLEEP_OK;
+ struct list_head *list;
+ struct pmu_sleep_notifier *current;
+
+ for (list = sleep_notifiers.prev; list != &sleep_notifiers;
+ list = list->prev) {
+ current = list_entry(list, struct pmu_sleep_notifier, list);
+ ret = current->notifier_call(current, when);
+ if (can_cancel && (ret != PBOOK_SLEEP_OK))
+ return ret;
+ }
+ return ret;
+}
+
+/* Wake is broadcast first-to-last */
+static int
+broadcast_wake(void)
+{
+ int ret = PBOOK_SLEEP_OK;
+ struct list_head *list;
+ struct pmu_sleep_notifier *current;
+
+ for (list = sleep_notifiers.next; list != &sleep_notifiers;
+ list = list->next) {
+ current = list_entry(list, struct pmu_sleep_notifier, list);
+ current->notifier_call(current, PBOOK_WAKE);
+ }
+ return ret;
+}
+
/*
* This struct is used to store config register values for
* PCI devices which may get powered off when we sleep.
@@ -914,7 +1022,7 @@
} *pbook_pci_saves;
static int n_pbook_pci_saves;
-static inline void __openfirmware
+static void __openfirmware
pbook_pci_save(void)
{
int npci;
@@ -941,7 +1049,7 @@
}
}
-static inline void __openfirmware
+static void __openfirmware
pbook_pci_restore(void)
{
u16 cmd;
@@ -974,46 +1082,168 @@
}
}
+#if 0
+/* N.B. This doesn't work on the 3400 */
+void pmu_blink(int n)
+{
+ struct adb_request req;
+
+ for (; n > 0; --n) {
+ pmu_request(&req, NULL, 4, 0xee, 4, 0, 1);
+ while (!req.complete) pmu_poll();
+ udelay(50000);
+ pmu_request(&req, NULL, 4, 0xee, 4, 0, 0);
+ while (!req.complete) pmu_poll();
+ udelay(50000);
+ }
+ udelay(50000);
+}
+#endif
+
/*
* Put the powerbook to sleep.
*/
-#define IRQ_ENABLE ((unsigned int *)0xf3000024)
-#define MEM_CTRL ((unsigned int *)0xf8000070)
+
+#define FEATURE_CTRL(base) ((unsigned int *)(base + 0x38))
+#define GRACKLE_PM (1<<7)
+#define GRACKLE_DOZE (1<<5)
+#define GRACKLE_NAP (1<<4)
+#define GRACKLE_SLEEP (1<<3)
+
+int __openfirmware powerbook_sleep_G3(void)
+{
+ int ret;
+ unsigned long save_l2cr;
+ unsigned long save_fcr;
+ unsigned long wait;
+ unsigned short pmcr1;
+ struct adb_request sleep_req;
+ struct device_node *macio;
+ unsigned long macio_base = 0;
+
+ macio = find_devices("mac-io");
+ if (macio != 0 && macio->n_addrs > 0)
+ macio_base = (unsigned long)
+ ioremap(macio->addrs[0].address, 0x40);
+
+ /* Sync the disks. */
+ /* XXX It would be nice to have some way to ensure that
+ * nobody is dirtying any new buffers while we wait. */
+ fsync_dev(0);
+
+ /* Notify device drivers */
+ ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, 1);
+ if (ret != PBOOK_SLEEP_OK) {
+ broadcast_sleep(PBOOK_SLEEP_REJECT, 0);
+ printk("pmu: sleep rejected\n");
+ return -EBUSY;
+ }
+ broadcast_sleep(PBOOK_SLEEP_NOW, 0);
+
+ /* 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);
-int __openfirmware powerbook_sleep(void)
+ /* Make sure the decrementer won't interrupt us */
+ asm volatile("mtdec %0" : : "r" (0x7fffffff));
+#if 0
+ /* Save the state of PCI config space for some slots */
+ pbook_pci_save();
+#endif
+ /* 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 (macio_base != 0) {
+ save_fcr = in_le32(FEATURE_CTRL(macio_base));
+ /* Check if this is still valid on older powerbooks */
+ out_le32(FEATURE_CTRL(macio_base), save_fcr & ~(0x00000140UL));
+ }
+
+ if (current->tss.regs && (current->tss.regs->msr & MSR_FP) != 0)
+ giveup_fpu(current);
+
+ grackle_pcibios_read_config_word(0,0,0x70,&pmcr1);
+ /* Apparently, MacOS uses NAP mode for Grackle ??? */
+ pmcr1 &= ~(GRACKLE_DOZE|GRACKLE_SLEEP);
+ pmcr1 |= GRACKLE_PM|GRACKLE_NAP;
+ grackle_pcibios_write_config_word(0, 0, 0x70, pmcr1);
+
+ /* 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();
+
+ cli();
+ while (pmu_state != idle)
+ pmu_poll();
+
+ /* Call low-level ASM sleep handler */
+ low_sleep_handler();
+
+ /* We're awake again, stop grackle PM */
+ grackle_pcibios_read_config_word(0, 0, 0x70, &pmcr1);
+ pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP);
+ grackle_pcibios_write_config_word(0, 0, 0x70, pmcr1);
+
+ sti();
+#if 0
+ /* According to someone from Apple, this should not be needed,
+ at least not for all devices. Let's keep it for now until we
+ have something that works. */
+ pbook_pci_restore();
+#endif
+ set_context(current->mm->context);
+
+ /* Restore L2 cache */
+ if (save_l2cr)
+ _set_L2CR(save_l2cr | 0x200000); /* set invalidate bit */
+
+ /* reenable interrupts */
+ sleep_restore_intrs();
+
+ /* Notify drivers */
+ broadcast_wake();
+
+ return 0;
+}
+
+#define PB3400_MEM_CTRL ((unsigned int *)0xf8000070)
+
+int __openfirmware powerbook_sleep_3400(void)
{
int ret, i, x;
- static int save_backlight;
- static unsigned int save_irqen;
unsigned long msr;
unsigned int hid0;
unsigned long p, wait;
struct adb_request sleep_req;
- /* Notify device drivers */
- ret = notifier_call_chain(&sleep_notifier_list, PBOOK_SLEEP, NULL);
- if (ret & NOTIFY_STOP_MASK)
- 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. */
fsync_dev(0);
- /* Turn off the display backlight */
- save_backlight = backlight_enabled;
- if (save_backlight)
- pmu_enable_backlight(0);
+ /* Notify device drivers */
+ ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, 1);
+ if (ret != PBOOK_SLEEP_OK) {
+ broadcast_sleep(PBOOK_SLEEP_REJECT, 0);
+ printk("pmu: sleep rejected\n");
+ return -EBUSY;
+ }
+ broadcast_sleep(PBOOK_SLEEP_NOW, 0);
/* 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 */
- save_irqen = in_le32(IRQ_ENABLE);
- for (i = 0; i < 32; ++i)
- if (i != vias->intrs[0].line && (save_irqen & (1 << i)))
- disable_irq(i);
+ 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 */
@@ -1022,9 +1252,9 @@
/* Set the memory controller to keep the memory refreshed
while we're asleep */
for (i = 0x403f; i >= 0x4000; --i) {
- out_be32(MEM_CTRL, i);
+ out_be32(PB3400_MEM_CTRL, i);
do {
- x = (in_be32(MEM_CTRL) >> 16) & 0x3ff;
+ x = (in_be32(PB3400_MEM_CTRL) >> 16) & 0x3ff;
} while (x == 0);
if (x >= 0x100)
break;
@@ -1034,6 +1264,7 @@
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;
@@ -1049,7 +1280,7 @@
udelay(10);
/* OK, we're awake again, start restoring things */
- out_be32(MEM_CTRL, 0x3f);
+ out_be32(PB3400_MEM_CTRL, 0x3f);
pbook_pci_restore();
/* wait for the PMU interrupt sequence to complete */
@@ -1057,21 +1288,10 @@
mb();
/* reenable interrupts */
- for (i = 0; i < 32; ++i)
- if (i != vias->intrs[0].line && (save_irqen & (1 << i)))
- enable_irq(i);
+ sleep_restore_intrs();
/* Notify drivers */
- notifier_call_chain(&sleep_notifier_list, PBOOK_WAKE, NULL);
-
- /* reenable ADB autopoll */
- pmu_adb_autopoll(adb_dev_map);
-
- /* Turn on the screen backlight, if it was on before */
- if (save_backlight)
- pmu_enable_backlight(1);
-
- /* Wait for the hard disk to spin up */
+ broadcast_wake();
return 0;
}
@@ -1224,7 +1444,7 @@
}
/* Note: removed __openfirmware here since it causes link errors */
-static int /*__openfirmware*/ pmu_ioctl(struct inode * inode, struct file *filp,
+static int pmu_ioctl(struct inode * inode, struct file *filp,
u_int cmd, u_long arg)
{
int error;
@@ -1232,9 +1452,18 @@
switch (cmd) {
case PMU_IOC_SLEEP:
- if (pmu_kind != PMU_OHARE_BASED)
- return -ENOSYS;
- return powerbook_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;
+ default:
+ error = ENOSYS;
+ }
+ return error;
case PMU_IOC_GET_BACKLIGHT:
return put_user(backlight_level, (__u32 *)arg);
case PMU_IOC_SET_BACKLIGHT:
@@ -1272,3 +1501,72 @@
}
#endif /* CONFIG_PMAC_PBOOK */
+#if 0
+static 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)
+{
+ xmon_printf("s%.2x", 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();
+ xmon_printf("r%.2x", x);
+ return x;
+}
+
+int
+pmu_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 */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)