It looks like setting TOUTn on the LEDs will cause serious issues for me. Not only did it create failures with reading the battery, but I believe it also was the reason for my modem failures I reported earlier. I am including 2 patches:

1) Clean up gta02 code to eliminate timer logic. This appears to be remnants of gta01 support within gta02 specific code.

2) Add ac and usb power_supply classes, reduce battery access to a predetermined rate (currently 10 seconds), notify userspace of battery update, remove online status result for battery as it belong in ac/usb.

The battery class needs a little more work. It should only notify when there is a change in values and I think ac should check to see if charging at 1000mV to distinguish between using charger or plugging into usb.

Sean

diff --git a/drivers/leds/leds-neo1973-gta02.c b/drivers/leds/leds-neo1973-gta02.c
index 8fe817b..9e490ac 100644
--- a/drivers/leds/leds-neo1973-gta02.c
+++ b/drivers/leds/leds-neo1973-gta02.c
@@ -17,7 +17,6 @@
 #include <linux/leds.h>
 #include <asm/hardware.h>
 #include <asm/mach-types.h>
-#include <asm/arch/pwm.h>
 #include <asm/arch/gta02.h>
 #include <asm/plat-s3c/regs-timer.h>
 #include <asm/plat-s3c24xx/neo1973.h>
@@ -29,9 +28,7 @@ struct gta02_led_priv
 {
 	spinlock_t lock;
 	struct led_classdev cdev;
-	struct s3c2410_pwm pwm;
 	unsigned int gpio;
-	unsigned int has_pwm;
 };
 
 struct gta02_led_bundle
@@ -56,19 +53,8 @@ static void gta02led_set(struct led_classdev *led_cdev,
 	unsigned long flags;
 	struct gta02_led_priv *lp = to_priv(led_cdev);
 
-	/*
- 	 * value == 255 -> 99% duty cycle (full power)
- 	 * value == 128 -> 50% duty cycle (medium power)
- 	 * value == 0 -> 0% duty cycle (zero power)
- 	 */
 	spin_lock_irqsave(&lp->lock, flags);
-
-	if (lp->has_pwm) {
-		s3c2410_pwm_duty_cycle(value, &lp->pwm);
-	} else {
-		neo1973_gpb_setpin(lp->gpio, value ? 1 : 0);
-	}
-
+	neo1973_gpb_setpin(lp->gpio, value ? 1 : 0);
 	spin_unlock_irqrestore(&lp->lock, flags);
 }
 
@@ -128,42 +114,8 @@ static int __init gta02led_probe(struct platform_device *pdev)
 
 		switch (lp->gpio) {
 		case S3C2410_GPB0:
-			lp->has_pwm = 1;
-			lp->pwm.timerid = PWM0;
-			s3c2410_gpio_cfgpin(lp->gpio, S3C2410_GPB0_TOUT0);
-			break;
 		case S3C2410_GPB1:
-			lp->has_pwm = 1;
-			lp->pwm.timerid = PWM1;
-			s3c2410_gpio_cfgpin(lp->gpio, S3C2410_GPB1_TOUT1);
-			break;
 		case S3C2410_GPB2:
-			lp->has_pwm = 1;
-			lp->pwm.timerid = PWM2;
-			s3c2410_gpio_cfgpin(lp->gpio, S3C2410_GPB2_TOUT2);
-			break;
-		case S3C2410_GPB3:
-			lp->has_pwm = 1;
-			lp->pwm.timerid = PWM3;
-			s3c2410_gpio_cfgpin(lp->gpio, S3C2410_GPB3_TOUT3);
-			break;
-		default:
-			break;
-		}
-
-		lp->pwm.prescaler = 0;
-		lp->pwm.divider = S3C2410_TCFG1_MUX3_DIV8;
-		lp->pwm.counter = COUNTER;
-		lp->pwm.comparer = COUNTER;
-		s3c2410_pwm_enable(&lp->pwm);
-		s3c2410_pwm_start(&lp->pwm);
-
-		switch (lp->gpio) {
-		case S3C2410_GPB0:
-		case S3C2410_GPB1:
-		case S3C2410_GPB2:
-		case S3C2410_GPB3:
-			lp->has_pwm = 0;
 			s3c2410_gpio_cfgpin(lp->gpio, S3C2410_GPIO_OUTPUT);
 			neo1973_gpb_add_shadow_gpio(lp->gpio);
 			break;
@@ -187,11 +139,7 @@ static int gta02led_remove(struct platform_device *pdev)
 
 	for (i = 0; i < bundle->num_leds; i++) {
 		struct gta02_led_priv *lp = &bundle->led[i];
-		if (lp->has_pwm)
-			s3c2410_pwm_disable(&lp->pwm);
-		else
-			gta02led_set(&lp->cdev, 0);
-
+		gta02led_set(&lp->cdev, 0);
 		led_classdev_unregister(&lp->cdev);
 	}
 
diff --git a/drivers/power/bq27000_battery.c b/drivers/power/bq27000_battery.c
index cd4968e..46c2cd2 100644
--- a/drivers/power/bq27000_battery.c
+++ b/drivers/power/bq27000_battery.c
@@ -29,6 +29,7 @@
 #include <linux/jiffies.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
+#include <linux/workqueue.h>
 #include <linux/platform_device.h>
 #include <linux/power_supply.h>
 #include <linux/bq27000_battery.h>
@@ -113,9 +114,25 @@ enum bq27000_status_flags {
 struct bq27000_device_info {
 	struct device *dev;
 	struct power_supply bat;
+	struct power_supply ac;
+	struct power_supply usb;
+	struct delayed_work work;
 	struct bq27000_platform_data *pdata;
+
+	int		ai;
+	int		flags;
+	int		lmd;
+	int		rsoc;
+	int		temp;
+	int		tte;
+	int		ttf;
+	int		volt;
 };
 
+static unsigned int cache_time = 10000;
+module_param(cache_time, uint, 0644);
+MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
+
 /*
  * reading 16 bit values over HDQ has a special hazard where the
  * hdq device firmware can update the 16-bit register during the time we
@@ -147,13 +164,9 @@ static int hdq_read16(struct bq27000_device_info *di, int address)
 	return -ETIME;
 }
 
-#define to_bq27000_device_info(x) container_of((x), \
-					       struct bq27000_device_info, \
-					       bat);
-
 static void bq27000_battery_external_power_changed(struct power_supply *psy)
 {
-	struct bq27000_device_info *di = to_bq27000_device_info(psy);
+	struct bq27000_device_info *di = container_of(psy, struct bq27000_device_info, bat);
 
 	dev_dbg(di->dev, "%s\n", __FUNCTION__);
 }
@@ -162,11 +175,8 @@ static int bq27000_battery_get_property(struct power_supply *psy,
 				       enum power_supply_property psp,
 				       union power_supply_propval *val)
 {
-	int v, n;
-	struct bq27000_device_info *di = to_bq27000_device_info(psy);
-
-	if (!(di->pdata->hdq_initialized)())
-		return -EINVAL;
+	int n;
+	struct bq27000_device_info *di = container_of(psy, struct bq27000_device_info, bat);
 
 	switch (psp) {
 	case POWER_SUPPLY_PROP_STATUS:
@@ -207,85 +217,122 @@ use_bat:
 		 * but... battery state can be out of date by 4 seconds or
 		 * so... use the platform callbacks if possible.
 		 */
-		v = hdq_read16(di, BQ27000_AI_L);
-		if (v < 0)
-			return v;
 
 		/* no real activity on the battery */
-		if (v < 2) {
-			if (!hdq_read16(di, BQ27000_TTF_L))
+		if (di->ai < 2) {
+			if (!di->ttf)
 				val->intval = POWER_SUPPLY_STATUS_FULL;
 			else
 				val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
 			break;
 		}
 		/* power is actually going in or out... */
-		v = (di->pdata->hdq_read)(BQ27000_FLAGS);
-		if (v < 0)
-			return v;
-		if (v & BQ27000_STATUS_CHGS)
+		if (di->flags < 0)
+			return di->flags;
+		if (di->flags & BQ27000_STATUS_CHGS)
 			val->intval = POWER_SUPPLY_STATUS_CHARGING;
 		else
 			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
 		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
+		/* Do we have accurate readings... */
+		if (di->flags < 0)
+			return di->flags;
+		if (di->flags & BQ27000_STATUS_VDQ)
+			val->intval = POWER_SUPPLY_HEALTH_GOOD;
+		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-		v = hdq_read16(di, BQ27000_VOLT_L);
-		if (v < 0)
-			return v;
+		if (di->volt < 0)
+			return di->volt;
 		/* mV -> uV */
-		val->intval = v * 1000;
+		val->intval = di->volt * 1000;
 		break;
 	case POWER_SUPPLY_PROP_CURRENT_NOW:
-		v = (di->pdata->hdq_read)(BQ27000_FLAGS);
-		if (v < 0)
-			return v;
-		if (v & BQ27000_STATUS_CHGS)
+		if (di->flags < 0)
+			return di->flags;
+		if (di->flags & BQ27000_STATUS_CHGS)
 			n = -NANOVOLTS_UNIT;
 		else
 			n = NANOVOLTS_UNIT;
-		v = hdq_read16(di, BQ27000_AI_L);
-		if (v < 0)
-			return v;
-		val->intval = (v * n) / di->pdata->rsense_mohms;
+		if (di->ai < 0)
+			return di->ai;
+		val->intval = (di->ai * n) / di->pdata->rsense_mohms;
 		break;
 	case POWER_SUPPLY_PROP_CHARGE_FULL:
-		v = hdq_read16(di, BQ27000_LMD_L);
-		if (v < 0)
-			return v;
-		val->intval = (v * 3570) / di->pdata->rsense_mohms;
+		if (di->lmd < 0)
+			return di->lmd;
+		val->intval = (di->lmd * 3570) / di->pdata->rsense_mohms;
 		break;
 	case POWER_SUPPLY_PROP_TEMP:
-		v = hdq_read16(di, BQ27000_TEMP_L);
-		if (v < 0)
-			return v;
+		if (di->temp < 0)
+			return di->temp;
 		/* K (in 0.25K units) is 273.15 up from C (in 0.1C)*/
 		/* 10926 = 27315 * 4 / 10 */
-		val->intval = (((long)v * 10l) - 10926) / 4;
+		val->intval = (((long)di->temp * 10l) - 10926) / 4;
 		break;
 	case POWER_SUPPLY_PROP_TECHNOLOGY:
 		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
 		break;
 	case POWER_SUPPLY_PROP_CAPACITY:
-		val->intval = (di->pdata->hdq_read)(BQ27000_RSOC);
+		val->intval = di->rsoc;
 		if (val->intval < 0)
 			return val->intval;
 		break;
 	case POWER_SUPPLY_PROP_PRESENT:
-		v = (di->pdata->hdq_read)(BQ27000_RSOC);
-		val->intval = !(v < 0);
+		val->intval = !(di->rsoc < 0);
 		break;
 	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
-		v = hdq_read16(di, BQ27000_TTE_L);
-		if (v < 0)
-			return v;
-		val->intval = 60 * v;
+		if (di->tte < 0)
+			return di->tte;
+		val->intval = 60 * di->tte;
 		break;
 	case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
-		v = hdq_read16(di, BQ27000_TTF_L);
-		if (v < 0)
-			return v;
-		val->intval = 60 * v;
+		if (di->ttf < 0)
+			return di->ttf;
+		val->intval = 60 * di->ttf;
 		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void bq27000_battery_work(struct work_struct *work)
+{
+	struct bq27000_device_info *di =
+		container_of(work, struct bq27000_device_info, work.work);
+
+	if ((di->pdata->hdq_initialized)()) {
+
+		di->ai    = hdq_read16(di, BQ27000_AI_L);
+		di->flags = (di->pdata->hdq_read)(BQ27000_FLAGS);
+		di->lmd   = hdq_read16(di, BQ27000_LMD_L);
+		di->rsoc  = (di->pdata->hdq_read)(BQ27000_RSOC);
+		di->temp  = hdq_read16(di, BQ27000_TEMP_L);
+		di->tte   = hdq_read16(di, BQ27000_TTE_L);
+		di->ttf   = hdq_read16(di, BQ27000_TTF_L);
+		di->volt  = hdq_read16(di, BQ27000_VOLT_L);
+
+		power_supply_changed(&di->bat);
+	}
+
+	if (!schedule_delayed_work(&di->work, cache_time))
+		dev_err(di->dev, "battery service reschedule failed\n");
+}
+
+static int ac_get_property(struct power_supply *psy,
+			enum power_supply_property psp,
+			union power_supply_propval *val)
+{
+	int ret = 0;
+	struct bq27000_device_info *di = container_of(psy, struct bq27000_device_info, ac);
+
+	if (!(di->pdata->hdq_initialized)())
+		return -EINVAL;
+
+	switch (psp) {
 	case POWER_SUPPLY_PROP_ONLINE:
 		if (di->pdata->get_charger_online_status)
 			val->intval = (di->pdata->get_charger_online_status)();
@@ -293,14 +340,39 @@ use_bat:
 			return -EINVAL;
 		break;
 	default:
-		return -EINVAL;
+		ret = -EINVAL;
+		break;
 	}
+	return ret;
+}
 
-	return 0;
+static int usb_get_property(struct power_supply *psy,
+			enum power_supply_property psp,
+			union power_supply_propval *val)
+{
+	int ret = 0;
+	struct bq27000_device_info *di = container_of(psy, struct bq27000_device_info, usb);
+
+	if (!(di->pdata->hdq_initialized)())
+		return -EINVAL;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		if (di->pdata->get_charger_online_status)
+			val->intval = (di->pdata->get_charger_online_status)();
+		else
+			return -EINVAL;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
 }
 
 static enum power_supply_property bq27000_battery_props[] = {
 	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_HEALTH,
 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
 	POWER_SUPPLY_PROP_CURRENT_NOW,
 	POWER_SUPPLY_PROP_CHARGE_FULL,
@@ -309,8 +381,11 @@ static enum power_supply_property bq27000_battery_props[] = {
 	POWER_SUPPLY_PROP_PRESENT,
 	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
 	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
-	POWER_SUPPLY_PROP_CAPACITY,
-	POWER_SUPPLY_PROP_ONLINE
+	POWER_SUPPLY_PROP_CAPACITY
+};
+
+static enum power_supply_property power_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
 };
 
 static int bq27000_battery_probe(struct platform_device *pdev)
@@ -342,14 +417,47 @@ static int bq27000_battery_probe(struct platform_device *pdev)
 	di->bat.use_for_apm = 1;
 	di->pdata = pdata;
 
+	di->ac.name            = "ac";
+	di->ac.type            = POWER_SUPPLY_TYPE_MAINS;
+	di->ac.properties      = power_props;
+	di->ac.num_properties  = ARRAY_SIZE(power_props);
+	di->ac.get_property    = ac_get_property;
+
+	di->usb.name           = "usb";
+	di->usb.type           = POWER_SUPPLY_TYPE_USB;
+	di->usb.properties     = power_props;
+	di->usb.num_properties = ARRAY_SIZE(power_props);
+	di->usb.get_property   = usb_get_property;
+
 	retval = power_supply_register(&pdev->dev, &di->bat);
 	if (retval) {
 		dev_err(di->dev, "failed to register battery\n");
 		goto batt_failed;
 	}
 
+	retval = power_supply_register(&pdev->dev, &di->ac);
+	if (retval) {
+		dev_err(di->dev, "failed to register ac\n");
+		goto ac_failed;
+	}
+
+	retval = power_supply_register(&pdev->dev, &di->usb);
+	if (retval) {
+		dev_err(di->dev, "failed to register usb\n");
+		goto usb_failed;
+	}
+
+	INIT_DELAYED_WORK(&di->work, bq27000_battery_work);
+
+	if (!schedule_delayed_work(&di->work, 0))
+		dev_err(di->dev, "failed to schedule bq27000_battery_work\n");
+
 	return 0;
 
+usb_failed:
+	power_supply_unregister(&di->ac);
+ac_failed:
+	power_supply_unregister(&di->bat);
 batt_failed:
 	kfree(di);
 di_alloc_failed:
@@ -360,7 +468,11 @@ static int bq27000_battery_remove(struct platform_device *pdev)
 {
 	struct bq27000_device_info *di = platform_get_drvdata(pdev);
 
+	cancel_delayed_work(&di->work);
+
 	power_supply_unregister(&di->bat);
+	power_supply_unregister(&di->ac);
+	power_supply_unregister(&di->usb);
 
 	return 0;
 }
@@ -372,7 +484,8 @@ void bq27000_charging_state_change(struct platform_device *pdev)
 	if (!di)
 	    return;
 
-	power_supply_changed(&di->bat);
+	power_supply_changed(&di->ac);
+	power_supply_changed(&di->usb);
 }
 EXPORT_SYMBOL_GPL(bq27000_charging_state_change);
 
@@ -381,11 +494,17 @@ EXPORT_SYMBOL_GPL(bq27000_charging_state_change);
 static int bq27000_battery_suspend(struct platform_device *pdev,
 				  pm_message_t state)
 {
+	struct bq27000_device_info *di = platform_get_drvdata(pdev);
+
+	cancel_delayed_work(&di->work);
 	return 0;
 }
 
 static int bq27000_battery_resume(struct platform_device *pdev)
 {
+	struct bq27000_device_info *di = platform_get_drvdata(pdev);
+
+	schedule_delayed_work(&di->work, 0);
 	return 0;
 }
 

Reply via email to