aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAndy Green <andy@openmoko.com>2008-11-19 17:09:48 +0000
committerAndy Green <agreen@pads.home.warmcat.com>2008-11-19 17:09:48 +0000
commit8ea2da6e862ac3cd234cf2645645267a1d7fcde9 (patch)
tree331ee6214f839496c7306a28bdabcc832390d57f /drivers
parent3608fada489ebc4abedcc5754d9644a52b372492 (diff)
fix-pcf50633-usb-curlim-workqueue-migration.patch
pcf50633 needs to take responsibility for managing current limit changes asycnhrnously, ie, from USB stack enumeration. It's a feature of pcf50633 not mach-gta02.c, and we can do better with taking care about keeping it from firing at a bad time in there too. Signed-off-by: Andy Green <andy@openmoko.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/i2c/chips/pcf50633.c96
1 files changed, 96 insertions, 0 deletions
diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
index 645cbbbc2f5..58f6ffc2ab8 100644
--- a/drivers/i2c/chips/pcf50633.c
+++ b/drivers/i2c/chips/pcf50633.c
@@ -125,6 +125,7 @@ struct pcf50633_data {
int have_been_suspended;
int usb_removal_count;
unsigned char pcfirq_resume[5];
+ int probe_completed;
/* if he pulls battery while charging, we notice that and correctly
* report that the charger is idle. But there is no interrupt that
@@ -138,6 +139,12 @@ struct pcf50633_data {
int usb_removal_count_nobat;
int jiffies_last_bat_ins;
+ /* current limit notification handler stuff */
+ struct mutex working_lock_usb_curlimit;
+ struct work_struct work_usb_curlimit;
+ int pending_curlimit;
+ int usb_removal_count_usb_curlimit;
+
int last_curlim_set;
int coldplug_done; /* cleared by probe, set by first work service */
@@ -603,6 +610,92 @@ static void add_request_to_adc_queue(struct pcf50633_data *pcf,
trigger_next_adc_job_if_any(pcf);
}
+/*
+ * we get run to handle servicing the async notification from USB stack that
+ * we got enumerated and allowed to draw a particular amount of current
+ */
+
+static void pcf50633_work_usbcurlim(struct work_struct *work)
+{
+ struct pcf50633_data *pcf =
+ container_of(work, struct pcf50633_data, work_usb_curlimit);
+
+ mutex_lock(&pcf->working_lock_usb_curlimit);
+
+ dev_info(&pcf->client.dev, "pcf50633_work_usbcurlim\n");
+
+ if (!pcf->probe_completed)
+ goto reschedule;
+
+ /* we got a notification from USB stack before we completed resume...
+ * that can only make trouble, reschedule for a retry
+ */
+ if (pcf->have_been_suspended && (pcf->have_been_suspended < 3))
+ goto reschedule;
+
+ /*
+ * did he pull USB before we managed to set the limit?
+ */
+ if (pcf->usb_removal_count_usb_curlimit != pcf->usb_removal_count)
+ goto bail;
+
+ /* OK let's set the requested limit and finish */
+
+ dev_info(&pcf->client.dev, "pcf50633_work_usbcurlim setting %dmA\n",
+ pcf->pending_curlimit);
+ pcf50633_usb_curlim_set(pcf, pcf->pending_curlimit);
+
+bail:
+ mutex_unlock(&pcf->working_lock_usb_curlimit);
+ return;
+
+reschedule:
+ dev_info(&pcf->client.dev, "pcf50633_work_usbcurlim rescheduling\n");
+ if (!schedule_work(&pcf->work_usb_curlimit))
+ dev_err(&pcf->client.dev, "work item may be lost\n");
+
+ mutex_unlock(&pcf->working_lock_usb_curlimit);
+ /* don't spew, delaying whatever else is happening */
+ msleep(1);
+}
+
+
+/* this is an export to allow machine to set USB current limit according to
+ * notifications of USB stack about enumeration state. We spawn a work
+ * function to handle the actual setting, because suspend / resume and such
+ * can be in a bad state since this gets called externally asychronous to
+ * anything else going on in pcf50633.
+ */
+
+int pcf50633_notify_usb_current_limit_change(struct pcf50633_data *pcf,
+ unsigned int ma)
+{
+ /* can happen if he calls with pcf50633_global before probe
+ * have to bail with error since we can't even schedule the work
+ */
+ if (!pcf) {
+ dev_err(&pcf->client.dev,
+ "pcf50633_notify_usb_current_limit_change "
+ "called with NULL pcf\n");
+ return -EBUSY;
+ }
+
+ dev_info(&pcf->client.dev,
+ "pcf50633_notify_usb_current_limit_change %dmA\n", ma);
+
+ /* prepare to detect USB power removal before we complete */
+ pcf->usb_removal_count_usb_curlimit = pcf->usb_removal_count;
+
+ pcf->pending_curlimit = ma;
+
+ if (!schedule_work(&pcf->work_usb_curlimit))
+ dev_err(&pcf->client.dev, "work item may be lost\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pcf50633_notify_usb_current_limit_change);
+
+
/* we are run when we see a NOBAT situation, because there is no interrupt
* source in pcf50633 that triggers on resuming charging. It watches to see
* if charging resumes, it reassesses the charging source if it does. If the
@@ -1960,8 +2053,10 @@ static int pcf50633_detect(struct i2c_adapter *adapter, int address, int kind)
mutex_init(&data->lock);
mutex_init(&data->working_lock);
mutex_init(&data->working_lock_nobat);
+ mutex_init(&data->working_lock_usb_curlimit);
INIT_WORK(&data->work, pcf50633_work);
INIT_WORK(&data->work_nobat, pcf50633_work_nobat);
+ INIT_WORK(&data->work_usb_curlimit, pcf50633_work_usbcurlim);
data->irq = irq;
data->working = 0;
data->onkey_seconds = -1;
@@ -2060,6 +2155,7 @@ static int pcf50633_detect(struct i2c_adapter *adapter, int address, int kind)
}
apm_get_power_status = pcf50633_get_power_status;
+ data->probe_completed = 1;
#ifdef CONFIG_MACH_NEO1973_GTA02
if (machine_is_neo1973_gta02()) {