changed Makefile and profiles, added patches for kernel 2.6.24
[openwrt.git] / target / linux / s3c24xx / patches-2.6.24 / 1188-fix-pcf50633-usb-curlim-workqueue-migration.patch.patch
diff --git a/target/linux/s3c24xx/patches-2.6.24/1188-fix-pcf50633-usb-curlim-workqueue-migration.patch.patch b/target/linux/s3c24xx/patches-2.6.24/1188-fix-pcf50633-usb-curlim-workqueue-migration.patch.patch
new file mode 100644 (file)
index 0000000..72acf66
--- /dev/null
@@ -0,0 +1,227 @@
+From 553cc686ad357fc169a95733c51f7ae3466bb248 Mon Sep 17 00:00:00 2001
+From: Andy Green <andy@openmoko.com>
+Date: Wed, 2 Jul 2008 22:40:21 +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       |   99 +++++++++++++++++++++++++++++++++++-
+ include/linux/pcf50633.h           |    5 ++
+ 3 files changed, 104 insertions(+), 23 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(&gta02_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(&gta02_udc_vbus_drawer.work, __gta02_udc_vbus_draw);
+       s3c24xx_udc_set_platdata(&gta02_udc_cfg);
+       set_s3c2410ts_info(&gta02_ts_cfg);
+diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
+index 96857a3..e53bec0 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
+@@ -1967,8 +2060,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;
+@@ -2068,15 +2163,15 @@ static int pcf50633_detect(struct i2c_adapter *adapter, int address, int kind)
+               backlight_update_status(data->backlight);
+       }
++      data->probe_completed = 1;
+       if (machine_is_neo1973_gta02()) {
+               gta01_pm_gps_dev.dev.parent = &new_client->dev;
+               gta01_pm_bt_dev.dev.parent = &new_client->dev;
+               platform_device_register(&gta01_pm_bt_dev);
+               platform_device_register(&gta01_pm_gps_dev);
+-      } else {
++      } else
+               apm_get_power_status = pcf50633_get_power_status;
+-      }
+       return 0;
+ exit_rtc:
+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.5
+