diff options
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.26/1167-fix-pcf50633-usb-curlim-workqueue-migration.patch.patch')
-rwxr-xr-x | target/linux/s3c24xx/patches-2.6.26/1167-fix-pcf50633-usb-curlim-workqueue-migration.patch.patch | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.26/1167-fix-pcf50633-usb-curlim-workqueue-migration.patch.patch b/target/linux/s3c24xx/patches-2.6.26/1167-fix-pcf50633-usb-curlim-workqueue-migration.patch.patch new file mode 100755 index 0000000000..7b47f328fc --- /dev/null +++ b/target/linux/s3c24xx/patches-2.6.26/1167-fix-pcf50633-usb-curlim-workqueue-migration.patch.patch @@ -0,0 +1,217 @@ +From 9bed4563ebd6ac99594f39ab004bf4411b904434 Mon Sep 17 00:00:00 2001 +From: Andy Green <andy@openmoko.com> +Date: Fri, 25 Jul 2008 23:06:13 +0100 +Subject: [PATCH] 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> +--- + arch/arm/mach-s3c2440/mach-gta02.c | 23 +-------- + drivers/i2c/chips/pcf50633.c | 96 ++++++++++++++++++++++++++++++++++++ + include/linux/pcf50633.h | 5 ++ + 3 files changed, 103 insertions(+), 21 deletions(-) + +diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c +index 64d275d..accdbc5 100644 +--- a/arch/arm/mach-s3c2440/mach-gta02.c ++++ b/arch/arm/mach-s3c2440/mach-gta02.c +@@ -813,29 +813,11 @@ static void gta02_udc_command(enum s3c2410_udc_cmd_e cmd) + } + } + +-/* use a work queue, since I2C API inherently schedules +- * and we get called in hardirq context from UDC driver */ +- +-struct vbus_draw { +- struct work_struct work; +- int ma; +-}; +-static struct vbus_draw gta02_udc_vbus_drawer; +- +-static void __gta02_udc_vbus_draw(struct work_struct *work) +-{ +- if (!pcf50633_global) { +- printk(KERN_ERR "pcf50633 not initialized yet, can't change " +- "vbus_draw\n"); +- return; +- } +- pcf50633_usb_curlim_set(pcf50633_global, gta02_udc_vbus_drawer.ma); +-} ++/* get PMU to set USB current limit accordingly */ + + static void gta02_udc_vbus_draw(unsigned int ma) + { +- gta02_udc_vbus_drawer.ma = ma; +- schedule_work(>a02_udc_vbus_drawer.work); ++ pcf50633_notify_usb_current_limit_change(pcf50633_global, ma); + } + + static struct s3c2410_udc_mach_info gta02_udc_cfg = { +@@ -1478,7 +1460,6 @@ static void __init gta02_machine_init(void) + s3c2410_gpio_setpin(S3C2410_GPD13, 1); + s3c2410_gpio_cfgpin(S3C2410_GPD13, S3C2410_GPIO_OUTPUT); + +- INIT_WORK(>a02_udc_vbus_drawer.work, __gta02_udc_vbus_draw); + s3c24xx_udc_set_platdata(>a02_udc_cfg); + set_s3c2410ts_info(>a02_ts_cfg); + +diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c +index 645cbbb..58f6ffc 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()) { +diff --git a/include/linux/pcf50633.h b/include/linux/pcf50633.h +index 0522d92..fa1c7e8 100644 +--- a/include/linux/pcf50633.h ++++ b/include/linux/pcf50633.h +@@ -129,6 +129,11 @@ extern void + pcf50633_register_resume_dependency(struct pcf50633_data *pcf, + struct resume_dependency *dep); + ++extern int ++pcf50633_notify_usb_current_limit_change(struct pcf50633_data *pcf, ++ unsigned int ma); ++ ++ + /* 0 = initialized and resumed and ready to roll, !=0 = either not + * initialized or not resumed yet + */ +-- +1.5.6.3 + |