patch-2.2.4 linux/drivers/macintosh/via-pmu.c

Next file: linux/drivers/misc/parport_ax.c
Previous file: linux/drivers/macintosh/macserial.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.3/linux/drivers/macintosh/via-pmu.c linux/drivers/macintosh/via-pmu.c
@@ -31,6 +31,7 @@
 #include <asm/init.h>
 #include <asm/irq.h>
 #include <asm/feature.h>
+#include <asm/uaccess.h>
 
 /* Misc minor number allocated for /dev/pmu */
 #define PMU_MINOR	154
@@ -89,8 +90,9 @@
 static int adb_int_pending;
 static int pmu_adb_flags;
 static int adb_dev_map = 0;
-static struct adb_request bright_req_1, bright_req_2;
+static struct adb_request bright_req_1, bright_req_2, bright_req_3;
 static struct device_node *vias;
+static int pmu_kind = PMU_UNKNOWN;
 
 int asleep;
 struct notifier_block *sleep_notifier_list;
@@ -108,7 +110,6 @@
 static void pmu_done(struct adb_request *req);
 static void pmu_handle_data(unsigned char *data, int len,
 			    struct pt_regs *regs);
-static void set_brightness(int level);
 static void set_volume(int level);
 
 /*
@@ -118,7 +119,7 @@
  * - the number of response bytes which the PMU will return, or
  *   -1 if it will send a length byte.
  */
-static s8 pmu_data_len[256][2] = {
+static s8 pmu_data_len[256][2] __openfirmwaredata = {
 /*	   0	   1	   2	   3	   4	   5	   6	   7  */
 /*00*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
 /*08*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
@@ -154,9 +155,8 @@
 /*f8*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
 };
 
-__openfirmware
 
-void
+void __openfirmware
 find_via_pmu()
 {
 	vias = find_devices("via-pmu");
@@ -185,6 +185,15 @@
 		if (vias->n_addrs < 1 || vias->n_intrs < 1)
 			return;
 	}
+
+	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, "heathrow"))
+		pmu_kind = PMU_HEATHROW_BASED;
+	else
+		pmu_kind = PMU_UNKNOWN;
+
 	via = (volatile unsigned char *) ioremap(vias->addrs->address, 0x2000);
 
 	out_8(&via[IER], IER_CLR | 0x7f);	/* disable all intrs */
@@ -195,9 +204,15 @@
 		via = NULL;
 
 	adb_hardware = ADB_VIAPMU;
+	
+	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"));
 }
 
-void
+void __openfirmware
 via_pmu_init(void)
 {
 	if (vias == NULL)
@@ -205,6 +220,7 @@
 
 	bright_req_1.complete = 1;
 	bright_req_2.complete = 1;
+	bright_req_3.complete = 1;
 
 	if (request_irq(vias->intrs[0].line, via_pmu_interrupt, 0, "VIA-PMU",
 			(void *)0)) {
@@ -220,9 +236,12 @@
 	adb_send_request = pmu_adb_send_request;
 	adb_autopoll = pmu_adb_autopoll;
 	adb_reset_bus = pmu_reset_bus;
+
+	/* Enable backlight */
+	pmu_enable_backlight(1);
 }
 
-static int
+static int __openfirmware
 init_pmu()
 {
 	int timeout;
@@ -232,7 +251,7 @@
 	out_8(&via[DIRB], (via[DIRB] | TREQ) & ~TACK);	/* TACK in, TREQ out */
 
 	pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0xff);
-	timeout = 100000;
+	timeout =  100000;
 	while (!req.complete) {
 		if (--timeout < 0) {
 			printk(KERN_ERR "init_pmu: no response from PMU\n");
@@ -259,8 +278,14 @@
 	return 1;
 }
 
+int
+pmu_get_model(void)
+{
+	return pmu_kind;
+}
+
 /* Send an ADB command */
-static int
+static int __openfirmware
 pmu_adb_send_request(struct adb_request *req, int sync)
 {
 	int i;
@@ -285,7 +310,7 @@
 }
 
 /* Enable/disable autopolling */
-static int
+static int __openfirmware
 pmu_adb_autopoll(int devs)
 {
 	struct adb_request req;
@@ -305,7 +330,7 @@
 }
 
 /* Reset the ADB bus */
-static int
+static int __openfirmware
 pmu_reset_bus(void)
 {
 	struct adb_request req;
@@ -348,7 +373,7 @@
 }
 
 /* Construct and send a pmu request */
-int
+int __openfirmware
 pmu_request(struct adb_request *req, void (*done)(struct adb_request *),
 	    int nbytes, ...)
 {
@@ -380,7 +405,7 @@
  * first byte is CUDA_PACKET or PMU_PACKET.  For CUDA_PACKET, we
  * emulate a few CUDA requests.
  */
-int
+int __openfirmware
 pmu_send_request(struct adb_request *req)
 {
 	int i;
@@ -426,7 +451,7 @@
 	return -EINVAL;
 }
 
-int
+int __openfirmware
 pmu_queue_request(struct adb_request *req)
 {
 	unsigned long flags;
@@ -465,7 +490,7 @@
 	return 0;
 }
 
-static void
+static void __openfirmware
 send_byte(int x)
 {
 	out_8(&via[ACR], 0x1c);
@@ -473,7 +498,7 @@
 	out_8(&via[B], via[B] & ~0x10);		/* assert TREQ */
 }
 
-static void
+static void __openfirmware
 recv_byte()
 {
 	out_8(&via[ACR], 0x0c);
@@ -481,7 +506,7 @@
 	out_8(&via[B], via[B] & ~0x10);
 }
 
-static void
+static void __openfirmware
 pmu_start()
 {
 	unsigned long flags;
@@ -506,7 +531,7 @@
 	restore_flags(flags);
 }
 
-void
+void __openfirmware
 pmu_poll()
 {
 	int ie;
@@ -517,7 +542,7 @@
 	_enable_interrupts(ie);
 }
 
-static void
+static void __openfirmware
 via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs)
 {
 	int intr;
@@ -553,7 +578,7 @@
 	}
 }
 
-static void
+static void __openfirmware
 pmu_sr_intr(struct pt_regs *regs)
 {
 	struct adb_request *req;
@@ -649,7 +674,7 @@
 	}
 }
 
-static void
+static void __openfirmware
 pmu_done(struct adb_request *req)
 {
 	req->complete = 1;
@@ -658,7 +683,7 @@
 }
 
 /* Interrupt data could be the result data from an ADB cmd */
-static void
+static void __openfirmware
 pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
 {
 	static int show_pmu_ints = 1;
@@ -689,7 +714,7 @@
 	} else {
 		if (data[0] == 0x08 && len == 3) {
 			/* sound/brightness buttons pressed */
-			set_brightness(data[1]);
+			pmu_set_brightness(data[1] >> 3);
 			set_volume(data[2]);
 		} else if (show_pmu_ints
 			   && !(data[0] == PMU_INT_TICK && len == 1)) {
@@ -702,54 +727,101 @@
 	}
 }
 
-int backlight_bright = -1;
+int backlight_level = -1;
 int backlight_enabled = 0;
 
-#define LEVEL_TO_BRIGHT(lev)	((lev) < 8? 0x7f: 0x4a - ((lev) >> 2))
+#define LEVEL_TO_BRIGHT(lev)	((lev) < 1? 0x7f: 0x4a - ((lev) << 1))
 
-void
+void __openfirmware
 pmu_enable_backlight(int on)
 {
 	struct adb_request req;
 
+	if (adb_hardware != ADB_VIAPMU)
+		return ;
+		
 	if (on) {
-		if (backlight_bright < 0) {
+	    /* first call: get current backlight value */
+	    if (backlight_level < 0) {
+		switch(pmu_kind) {
+		    case PMU_OHARE_BASED:
 			pmu_request(&req, NULL, 2, 0xd9, 0);
 			while (!req.complete)
 				pmu_poll();
-			backlight_bright = LEVEL_TO_BRIGHT(req.reply[1]);
+			backlight_level = req.reply[1] >> 3;
+			printk(KERN_DEBUG "pmu: controls returned bright: %d\n", (int)req.reply[1]);
+			break;
+		    case PMU_HEATHROW_BASED:
+			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];
+			break;
+		    default:
+		        backlight_enabled = 0;
+		        return;
 		}
-		pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
-			    backlight_bright);
-		while (!req.complete)
-			pmu_poll();
+	    }
+	    pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
+	    	LEVEL_TO_BRIGHT(backlight_level));
+	    while (!req.complete)
+		pmu_poll();
 	}
-	pmu_request(&req, NULL, 2, PMU_BACKLIGHT_CTRL, on? 0x81: 1);
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
+	    PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF));
 	while (!req.complete)
 		pmu_poll();
 	backlight_enabled = on;
 }
 
-static void
-set_brightness(int level)
+void __openfirmware
+pmu_set_brightness(int level)
 {
-	backlight_bright = LEVEL_TO_BRIGHT(level);
+	int bright;
+
+	if (adb_hardware != ADB_VIAPMU)
+		return ;
+
+	backlight_level = level;
+	bright = LEVEL_TO_BRIGHT(level);
 	if (!backlight_enabled)
 		return;
 	if (bright_req_1.complete)
 		pmu_request(&bright_req_1, NULL, 2, PMU_BACKLIGHT_BRIGHT,
-			    backlight_bright);
+		    bright);
 	if (bright_req_2.complete)
-		pmu_request(&bright_req_2, NULL, 2, PMU_BACKLIGHT_CTRL,
-			    backlight_bright < 0x7f? 0x81: 1);
+		pmu_request(&bright_req_2, NULL, 2, PMU_POWER_CTRL,
+		    PMU_POW_BACKLIGHT | (bright < 0x7f ? PMU_POW_ON : PMU_POW_OFF));
+
+	/* XXX nvram address is hard-coded and looks ok on wallstreet, please
+	   test on your machine. Note that newer MacOS system software may break
+	   the nvram layout. */
+	if ((pmu_kind == PMU_HEATHROW_BASED) && bright_req_3.complete)
+		pmu_request(&bright_req_3, NULL, 4, PMU_WRITE_NVRAM,
+			    0x14, 0xe, level);
 }
 
-static void
+void __openfirmware
+pmu_enable_irled(int on)
+{
+	struct adb_request req;
+
+	if (adb_hardware != ADB_VIAPMU)
+		return ;
+
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_IRLED |
+	    (on ? PMU_POW_ON : PMU_POW_OFF));
+	while (!req.complete)
+		pmu_poll();
+}
+
+static void __openfirmware
 set_volume(int level)
 {
 }
 
-void
+void __openfirmware
 pmu_restart(void)
 {
 	struct adb_request req;
@@ -768,7 +840,7 @@
 		;
 }
 
-void
+void __openfirmware
 pmu_shutdown(void)
 {
 	struct adb_request req;
@@ -802,7 +874,7 @@
 } *pbook_pci_saves;
 static int n_pbook_pci_saves;
 
-static inline void
+static inline void __openfirmware
 pbook_pci_save(void)
 {
 	int npci;
@@ -829,7 +901,7 @@
 	}
 }
 
-static inline void
+static inline void __openfirmware
 pbook_pci_restore(void)
 {
 	u16 cmd;
@@ -868,7 +940,7 @@
 #define IRQ_ENABLE	((unsigned int *)0xf3000024)
 #define MEM_CTRL	((unsigned int *)0xf8000070)
 
-int powerbook_sleep(void)
+int __openfirmware powerbook_sleep(void)
 {
 	int ret, i, x;
 	static int save_backlight;
@@ -967,29 +1039,44 @@
 /*
  * Support for /dev/pmu device
  */
-static int pmu_open(struct inode *inode, struct file *file)
+static int __openfirmware pmu_open(struct inode *inode, struct file *file)
 {
 	return 0;
 }
 
-static ssize_t pmu_read(struct file *file, char *buf,
+static ssize_t __openfirmware pmu_read(struct file *file, char *buf,
 			size_t count, loff_t *ppos)
 {
 	return 0;
 }
 
-static ssize_t pmu_write(struct file *file, const char *buf,
+static ssize_t __openfirmware pmu_write(struct file *file, const char *buf,
 			 size_t count, loff_t *ppos)
 {
 	return 0;
 }
 
-static int pmu_ioctl(struct inode * inode, struct file *filp,
+/* Note: removed __openfirmware here since it causes link errors */
+static int /*__openfirmware*/ pmu_ioctl(struct inode * inode, struct file *filp,
 		     u_int cmd, u_long arg)
 {
+	int error;
+	__u32 value;
+
 	switch (cmd) {
-	case PMU_IOC_SLEEP:
+	    case PMU_IOC_SLEEP:
+	    	if (pmu_kind != PMU_OHARE_BASED)
+	    		return -ENOSYS;
 		return powerbook_sleep();
+	    case PMU_IOC_GET_BACKLIGHT:
+		return put_user(backlight_level, (__u32 *)arg);
+	    case PMU_IOC_SET_BACKLIGHT:
+		error = get_user(value, (__u32 *)arg);
+		if (!error)
+			pmu_set_brightness(value);
+		return error;
+	    case PMU_IOC_GET_MODEL:
+	    	return put_user(pmu_kind, (__u32 *)arg);
 	}
 	return -EINVAL;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)