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

Next file: linux-2.4.22/drivers/md/md.c
Previous file: linux-2.4.22/drivers/macintosh/via-cuda.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.21/drivers/macintosh/via-pmu.c linux-2.4.22/drivers/macintosh/via-pmu.c
@@ -345,11 +345,6 @@
 	} else
 		pmu_kind = PMU_UNKNOWN;
 
-#ifdef CONFIG_PMAC_PBOOK
-	if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
-		can_sleep = 1;
-#endif /* CONFIG_PMAC_PBOOK */
-
 	via = (volatile unsigned char *) ioremap(vias->addrs->address, 0x2000);
 
 	out_8(&via[IER], IER_CLR | 0x7f);	/* disable all intrs */
@@ -410,6 +405,8 @@
 	bright_req_3.complete = 1;
 #ifdef CONFIG_PMAC_PBOOK
 	batt_req.complete = 1;
+	if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
+		can_sleep = 1;
 #endif
 
 	if (request_irq(vias->intrs[0].line, via_pmu_interrupt, 0, "VIA-PMU",
@@ -436,12 +433,20 @@
 
 #ifdef CONFIG_PMAC_PBOOK
   	if (machine_is_compatible("AAPL,3400/2400") ||
-  		machine_is_compatible("AAPL,3500"))
+  		machine_is_compatible("AAPL,3500")) {
+		int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO,
+			NULL, PMAC_MB_INFO_MODEL, 0);
 		pmu_battery_count = 1;
-	else if (machine_is_compatible("AAPL,PowerBook1998") ||
-		machine_is_compatible("PowerBook1,1"))
+		if (mb == PMAC_TYPE_COMET)
+			pmu_batteries[0].flags |= PMU_BATT_TYPE_COMET;
+		else
+			pmu_batteries[0].flags |= PMU_BATT_TYPE_HOOPER;
+	} else if (machine_is_compatible("AAPL,PowerBook1998") ||
+		machine_is_compatible("PowerBook1,1")) {
 		pmu_battery_count = 2;
-	else {
+		pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART;
+		pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART;
+	} else {
 		struct device_node* prim = find_devices("power-mgt");
 		u32 *prim_info = NULL;
 		if (prim)
@@ -449,6 +454,9 @@
 		if (prim_info) {
 			/* Other stuffs here yet unknown */
 			pmu_battery_count = (prim_info[6] >> 16) & 0xff;
+			pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART;
+			if (pmu_battery_count > 1)
+				pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART;
 		}
 	}
 #endif /* CONFIG_PMAC_PBOOK */
@@ -558,148 +566,12 @@
 
 #ifdef CONFIG_PMAC_PBOOK
 
-/* 
- * WARNING ! This code probably needs some debugging... -- BenH.
+/* This new version of the code for 2400/3400/3500 powerbooks
+ * is inspired from the implementation in gkrellm-pmu
  */
-#ifdef NEW_OHARE_CODE
-static void __pmac
-done_battery_state_ohare(struct adb_request* req)
-{
-	unsigned int bat_flags = 0;
-	int current = 0;
-	unsigned int capa, max, voltage, time;
-	int lrange[] = { 0, 275, 850, 1680, 2325, 
-				2765, 3160, 3500, 3830, 4115, 
-				4360, 4585, 4795, 4990, 5170, 
-				5340, 5510, 5710, 5930, 6150, 
-				6370, 6500
-				};
-	
-	if (req->reply[0] & 0x01)
-		pmu_power_flags |= PMU_PWR_AC_PRESENT;
-	else
-		pmu_power_flags &= ~PMU_PWR_AC_PRESENT;
-
-	if (req->reply[0] & 0x04) {
-		int vb, i, j, k, charge, pcharge;
-		bat_flags |= PMU_BATT_PRESENT;
-		vb = (req->reply[1] << 8) | req->reply[2];
-		voltage = ((vb * 2650) + 726650)/100;
-		vb *= 100;
-		current = req->reply[5];
-		if ((req->reply[0] & 0x01) == 0 && (current > 200))
-			vb += (current - 200) * 15;
-		else if (req->reply[0] & 0x02)
-			vb = (vb - 2000);
-	  	i = (33000 - vb) / 10;
-	  	j = i - (i % 100);
-		k = j/100;
-		if (k <= 0)
-	       		charge = 0;
-		else if (k >= 21)
-	       		charge = 650000;
-	  	else
-			charge = (lrange[k + 1] - lrange[k]) * (i - j) + (lrange[k] * 100);
-		charge = (1000 - charge / 650) / 10;
-		if (req->reply[0] & 0x40) {
-			pcharge = (req->reply[6] << 8) + req->reply[7];
-			if (pcharge > 6500)
-				pcharge = 6500;
-			pcharge *= 100;
-			pcharge = (1000 - pcharge / 650) / 10;
-			if (pcharge < charge)
-				charge = pcharge;
-		}
-		capa = charge;
-		max = 100;
-		time = (charge * 16440) / current;
-		current = -current;
-		
-	} else
-		capa = max = current = voltage = time = 0;
-
-	if (req->reply[0] & 0x02)
-		bat_flags |= PMU_BATT_CHARGING;
-
-	pmu_batteries[pmu_cur_battery].flags = bat_flags;
-	pmu_batteries[pmu_cur_battery].charge = capa;
-	pmu_batteries[pmu_cur_battery].max_charge = max;
-	pmu_batteries[pmu_cur_battery].current = current;
-	pmu_batteries[pmu_cur_battery].voltage = voltage;
-	pmu_batteries[pmu_cur_battery].time_remaining = time;
-}
-#else /* NEW_OHARE_CODE */
 static void __pmac
 done_battery_state_ohare(struct adb_request* req)
 {
-	unsigned int bat_flags = 0;
-	int current = 0;
-	unsigned int capa, max, voltage, time;
-	int lrange[] = { 0, 275, 850, 1680, 2325, 
-				2765, 3160, 3500, 3830, 4115, 
-				4360, 4585, 4795, 4990, 5170, 
-				5340, 5510, 5710, 5930, 6150, 
-				6370, 6500
-				};
-	
-	if (req->reply[0] & 0x01)
-		pmu_power_flags |= PMU_PWR_AC_PRESENT;
-	else
-		pmu_power_flags &= ~PMU_PWR_AC_PRESENT;
-
-	if (req->reply[0] & 0x04) {
-		int vb, i, j, charge, pcharge;
-		bat_flags |= PMU_BATT_PRESENT;
-		vb = (req->reply[1] << 8) | req->reply[2];
-		voltage = ((vb * 2650) + 726650)/100;
-		current = *((signed char *)&req->reply[5]);
-		if ((req->reply[0] & 0x01) == 0 && (current > 200))
-			vb += (current - 200) * 15;
-		else if (req->reply[0] & 0x02)
-			vb = (vb - 10) * 100;
-	  	i = (33000 - vb) / 10;
-	  	j = i - (i % 100);
-	  	if (j <= 0)
-	       		charge = 0;
-	  	else if (j >= 21)
-	       		charge = 650000;
-	  	else
-			charge = (lrange[j + 1] - lrange[j]) * (i - j) + (lrange[j] * 100);
-		charge = (1000 - charge / 650) / 10;
-		if (req->reply[0] & 0x40) {
-			pcharge = (req->reply[6] << 8) + req->reply[7];
-			if (pcharge > 6500)
-				pcharge = 6500;
-			pcharge *= 100;
-			pcharge = (1000 - pcharge / 650) / 10;
-			if (pcharge < charge)
-				charge = pcharge;
-		}
-		capa = charge;
-		max = 100;
-		time = (charge * 274) / current;
-		current = -current;
-		
-	} else
-		capa = max = current = voltage = time = 0;
-
-	if ((req->reply[0] & 0x02) && (current > 0))
-		bat_flags |= PMU_BATT_CHARGING;
-	if (req->reply[0] & 0x04) /* CHECK THIS ONE */
-		bat_flags |= PMU_BATT_PRESENT;
-
-	pmu_batteries[pmu_cur_battery].flags = bat_flags;
-	pmu_batteries[pmu_cur_battery].charge = capa;
-	pmu_batteries[pmu_cur_battery].max_charge = max;
-	pmu_batteries[pmu_cur_battery].current = current;
-	pmu_batteries[pmu_cur_battery].voltage = voltage;
-	pmu_batteries[pmu_cur_battery].time_remaining = time;
-}
-#endif /* NEW_OHARE_CODE */
-
- static void __pmac
-done_battery_state_comet(struct adb_request* req)
-{
 	/* format:
 	 *  [0]    :  flags
 	 *    0x01 :  AC indicator
@@ -718,57 +590,62 @@
 	 *  [6][7] :  pcharge
 	 *              --tkoba
 	 */
+	unsigned int bat_flags = PMU_BATT_TYPE_HOOPER;
+	long pcharge, charge, vb, vmax, lmax;
+	long vmax_charging, vmax_charged;
+	long current, voltage, time, max;
+	int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO,
+			NULL, PMAC_MB_INFO_MODEL, 0);
 
-	unsigned int bat_flags = 0;
-	int current = 0;
-	unsigned int max = 100;
-	unsigned int charge, voltage, time;
-	int lrange[] = { 0, 600, 750, 900, 1000, 1080, 
-				1180, 1250, 1300, 1340, 1360, 
-				1390, 1420, 1440, 1470, 1490, 
-				1520, 1550, 1580, 1610, 1650, 
-				1700
-				};
-	
 	if (req->reply[0] & 0x01)
 		pmu_power_flags |= PMU_PWR_AC_PRESENT;
 	else
 		pmu_power_flags &= ~PMU_PWR_AC_PRESENT;
+	
+	if (mb == PMAC_TYPE_COMET) {
+		vmax_charged = 189;
+		vmax_charging = 213;
+		lmax = 6500;
+	} else {
+		vmax_charged = 330;
+		vmax_charging = 330;
+		lmax = 6500;
+	}
+	vmax = vmax_charged;
 
-	if (req->reply[0] & 0x04) {	/* battery exist */
-		int vb, i;
+	/* If battery installed */
+	if (req->reply[0] & 0x04) {
 		bat_flags |= PMU_BATT_PRESENT;
+		if (req->reply[0] & 0x02)
+			bat_flags |= PMU_BATT_CHARGING;
 		vb = (req->reply[1] << 8) | req->reply[2];
-		voltage = ((vb * 2650) + 726650)/100;
-		vb *= 10;
+		voltage = (vb * 265 + 72665) / 10;
 		current = req->reply[5];
-		if ((req->reply[0] & 0x01) == 0 && (current > 200))
-			vb += ((current - 200) * 3);	/* vb = 500<->1800 */
-		else if (req->reply[0] & 0x02)
-			vb = ((vb - 800) * 1700/13)/100;	/*  in charging vb = 1300<->2130 */
-
-		if (req->reply[0] & 0x20) {	/* full charged */
-			charge = max;
-		} else {
-			if (lrange[21] < vb)
-				charge = max;
-			else {
-				if (vb < lrange[1])
-					charge = 0;
-				else {
-					for (i=21; vb < lrange[i]; --i);
-					charge = (i * 100)/21;
-				}
-			}
-			if (charge > max) charge = max;
+		if ((req->reply[0] & 0x01) == 0) {
+			if (current > 200)
+				vb += ((current - 200) * 15)/100;
+		} else if (req->reply[0] & 0x02) {
+			vb = (vb * 97) / 100;
+			vmax = vmax_charging;
 		}
-		time = (charge * 72);
+		charge = (100 * vb) / vmax;
+		if (req->reply[0] & 0x40) {
+			pcharge = (req->reply[6] << 8) + req->reply[7];
+			if (pcharge > lmax)
+				pcharge = lmax;
+			pcharge *= 100;
+			pcharge = 100 - pcharge / lmax;
+			if (pcharge < charge)
+				charge = pcharge;
+		}
+		if (current > 0)
+			time = (charge * 16440) / current;
+		else
+			time = 0;
+		max = 100;
 		current = -current;
 	} else
-		max = current = voltage = time = 0;
-
-	if (req->reply[0] & 0x02)
-		bat_flags |= PMU_BATT_CHARGING;
+		charge = max = current = voltage = time = 0;
 
 	pmu_batteries[pmu_cur_battery].flags = bat_flags;
 	pmu_batteries[pmu_cur_battery].charge = charge;
@@ -800,7 +677,7 @@
 	 *  [8][9] : voltage
 	 */
 	 
-	unsigned int bat_flags = 0;
+	unsigned int bat_flags = PMU_BATT_TYPE_SMART;
 	int current;
 	unsigned int capa, max, voltage;
 	
@@ -858,17 +735,10 @@
 {
 	if (!batt_req.complete)
 		return;
-	if (pmu_kind == PMU_OHARE_BASED) {
-		int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO,
-			NULL, PMAC_MB_INFO_MODEL, 0);
-
-		if (mb == PMAC_TYPE_COMET)
-			pmu_request(&batt_req, done_battery_state_comet,
-				1, PMU_BATTERY_STATE);
-		else
-			pmu_request(&batt_req, done_battery_state_ohare,
-				1, PMU_BATTERY_STATE);
-	} else
+	if (pmu_kind == PMU_OHARE_BASED)
+		pmu_request(&batt_req, done_battery_state_ohare,
+			1, PMU_BATTERY_STATE);
+	else
 		pmu_request(&batt_req, done_battery_state_smart,
 			2, PMU_SMART_BATTERY_STATE, pmu_cur_battery+1);
 }
@@ -1220,9 +1090,14 @@
 static inline void
 pmu_done(struct adb_request *req)
 {
+	void (*done)(struct adb_request *) = req->done;
+	mb();
 	req->complete = 1;
-	if (req->done)
-		(*req->done)(req);
+    	/* 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 void __openfirmware
@@ -1557,7 +1432,8 @@
 			wait_for_ack();
 			send_byte(PMU_INT_ACK);
 			adb_int_pending = 0;
-no_free_slot:			
+no_free_slot:
+			;	
 		} else if (current_req)
 			pmu_start();
 	}
@@ -1714,6 +1590,259 @@
 	return via != 0;
 }
 
+struct pmu_i2c_hdr {
+	u8	bus;
+	u8	mode;
+	u8	bus2;
+	u8	address;
+	u8	sub_addr;
+	u8	comb_addr;
+	u8	count;
+};
+
+int
+pmu_i2c_combined_read(int bus, int addr, int subaddr,  u8* data, int len)
+{
+	struct adb_request	req;
+	struct pmu_i2c_hdr	*hdr = (struct pmu_i2c_hdr *)&req.data[1];
+	int retry;
+	int rc;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		hdr->bus = bus;
+		hdr->address = addr & 0xfe;
+		hdr->mode = PMU_I2C_MODE_COMBINED;
+		hdr->bus2 = 0;
+		hdr->sub_addr = subaddr;
+		hdr->comb_addr = addr | 1;
+		hdr->count = len;
+		
+		req.nbytes = sizeof(struct pmu_i2c_hdr) + 1;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		req.reply[0] = 0xff;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			break;
+		mdelay(15);
+	}
+	if (req.reply[0] != PMU_I2C_STATUS_OK)
+		return -1;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		mdelay(15);
+
+		hdr->bus = PMU_I2C_BUS_STATUS;
+		req.reply[0] = 0xff;
+		
+		req.nbytes = 2;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_DATAREAD) {
+			memcpy(data, &req.reply[1], req.reply_len - 1);
+			return req.reply_len - 1;
+		}
+	}
+	return -1;
+}
+
+int
+pmu_i2c_stdsub_write(int bus, int addr, int subaddr,  u8* data, int len)
+{
+	struct adb_request	req;
+	struct pmu_i2c_hdr	*hdr = (struct pmu_i2c_hdr *)&req.data[1];
+	int retry;
+	int rc;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		hdr->bus = bus;
+		hdr->address = addr & 0xfe;
+		hdr->mode = PMU_I2C_MODE_STDSUB;
+		hdr->bus2 = 0;
+		hdr->sub_addr = subaddr;
+		hdr->comb_addr = addr & 0xfe;
+		hdr->count = len;
+
+		req.data[0] = PMU_I2C_CMD;
+		memcpy(&req.data[sizeof(struct pmu_i2c_hdr) + 1], data, len);
+		req.nbytes = sizeof(struct pmu_i2c_hdr) + len + 1;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.reply[0] = 0xff;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			break;
+		mdelay(15);
+	}
+	if (req.reply[0] != PMU_I2C_STATUS_OK)
+		return -1;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		mdelay(15);
+
+		hdr->bus = PMU_I2C_BUS_STATUS;
+		req.reply[0] = 0xff;
+		
+		req.nbytes = 2;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			return len;
+	}
+	return -1;
+}
+
+int
+pmu_i2c_simple_read(int bus, int addr,  u8* data, int len)
+{
+	struct adb_request	req;
+	struct pmu_i2c_hdr	*hdr = (struct pmu_i2c_hdr *)&req.data[1];
+	int retry;
+	int rc;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		hdr->bus = bus;
+		hdr->address = addr | 1;
+		hdr->mode = PMU_I2C_MODE_SIMPLE;
+		hdr->bus2 = 0;
+		hdr->sub_addr = 0;
+		hdr->comb_addr = 0;
+		hdr->count = len;
+
+		req.data[0] = PMU_I2C_CMD;
+		req.nbytes = sizeof(struct pmu_i2c_hdr) + 1;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.reply[0] = 0xff;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			break;
+		mdelay(15);
+	}
+	if (req.reply[0] != PMU_I2C_STATUS_OK)
+		return -1;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		mdelay(15);
+
+		hdr->bus = PMU_I2C_BUS_STATUS;
+		req.reply[0] = 0xff;
+		
+		req.nbytes = 2;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_DATAREAD) {
+			memcpy(data, &req.reply[1], req.reply_len - 1);
+			return req.reply_len - 1;
+		}
+	}
+	return -1;
+}
+
+int
+pmu_i2c_simple_write(int bus, int addr,  u8* data, int len)
+{
+	struct adb_request	req;
+	struct pmu_i2c_hdr	*hdr = (struct pmu_i2c_hdr *)&req.data[1];
+	int retry;
+	int rc;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		hdr->bus = bus;
+		hdr->address = addr & 0xfe;
+		hdr->mode = PMU_I2C_MODE_SIMPLE;
+		hdr->bus2 = 0;
+		hdr->sub_addr = 0;
+		hdr->comb_addr = 0;
+		hdr->count = len;
+
+		req.data[0] = PMU_I2C_CMD;
+		memcpy(&req.data[sizeof(struct pmu_i2c_hdr) + 1], data, len);
+		req.nbytes = sizeof(struct pmu_i2c_hdr) + len + 1;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.reply[0] = 0xff;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			break;
+		mdelay(15);
+	}
+	if (req.reply[0] != PMU_I2C_STATUS_OK)
+		return -1;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		mdelay(15);
+
+		hdr->bus = PMU_I2C_BUS_STATUS;
+		req.reply[0] = 0xff;
+		
+		req.nbytes = 2;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			return len;
+	}
+	return -1;
+}
+
+
 #if defined(DEBUG_SLEEP) || defined(DEBUG_FREQ)
 /* N.B. This doesn't work on the 3400 */
 void
@@ -2794,6 +2923,10 @@
 EXPORT_SYMBOL(pmu_poll);
 EXPORT_SYMBOL(pmu_suspend);
 EXPORT_SYMBOL(pmu_resume);
+EXPORT_SYMBOL(pmu_i2c_combined_read);
+EXPORT_SYMBOL(pmu_i2c_stdsub_write);
+EXPORT_SYMBOL(pmu_i2c_simple_read);
+EXPORT_SYMBOL(pmu_i2c_simple_write);
 #ifdef CONFIG_PMAC_PBOOK
 EXPORT_SYMBOL(pmu_register_sleep_notifier);
 EXPORT_SYMBOL(pmu_unregister_sleep_notifier);

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