1 From 9bed4563ebd6ac99594f39ab004bf4411b904434 Mon Sep 17 00:00:00 2001
2 From: Andy Green <andy@openmoko.com>
3 Date: Fri, 25 Jul 2008 23:06:13 +0100
4 Subject: [PATCH] fix-pcf50633-usb-curlim-workqueue-migration.patch
6 pcf50633 needs to take responsibility for managing current limit
7 changes asycnhrnously, ie, from USB stack enumeration. It's a feature of
8 pcf50633 not mach-gta02.c, and we can do better with taking care about
9 keeping it from firing at a bad time in there too.
11 Signed-off-by: Andy Green <andy@openmoko.com>
13 arch/arm/mach-s3c2440/mach-gta02.c | 23 +--------
14 drivers/i2c/chips/pcf50633.c | 96 ++++++++++++++++++++++++++++++++++++
15 include/linux/pcf50633.h | 5 ++
16 3 files changed, 103 insertions(+), 21 deletions(-)
18 diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
19 index 64d275d..accdbc5 100644
20 --- a/arch/arm/mach-s3c2440/mach-gta02.c
21 +++ b/arch/arm/mach-s3c2440/mach-gta02.c
22 @@ -813,29 +813,11 @@ static void gta02_udc_command(enum s3c2410_udc_cmd_e cmd)
26 -/* use a work queue, since I2C API inherently schedules
27 - * and we get called in hardirq context from UDC driver */
30 - struct work_struct work;
33 -static struct vbus_draw gta02_udc_vbus_drawer;
35 -static void __gta02_udc_vbus_draw(struct work_struct *work)
37 - if (!pcf50633_global) {
38 - printk(KERN_ERR "pcf50633 not initialized yet, can't change "
42 - pcf50633_usb_curlim_set(pcf50633_global, gta02_udc_vbus_drawer.ma);
44 +/* get PMU to set USB current limit accordingly */
46 static void gta02_udc_vbus_draw(unsigned int ma)
48 - gta02_udc_vbus_drawer.ma = ma;
49 - schedule_work(>a02_udc_vbus_drawer.work);
50 + pcf50633_notify_usb_current_limit_change(pcf50633_global, ma);
53 static struct s3c2410_udc_mach_info gta02_udc_cfg = {
54 @@ -1478,7 +1460,6 @@ static void __init gta02_machine_init(void)
55 s3c2410_gpio_setpin(S3C2410_GPD13, 1);
56 s3c2410_gpio_cfgpin(S3C2410_GPD13, S3C2410_GPIO_OUTPUT);
58 - INIT_WORK(>a02_udc_vbus_drawer.work, __gta02_udc_vbus_draw);
59 s3c24xx_udc_set_platdata(>a02_udc_cfg);
60 set_s3c2410ts_info(>a02_ts_cfg);
62 diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
63 index 645cbbb..58f6ffc 100644
64 --- a/drivers/i2c/chips/pcf50633.c
65 +++ b/drivers/i2c/chips/pcf50633.c
66 @@ -125,6 +125,7 @@ struct pcf50633_data {
67 int have_been_suspended;
68 int usb_removal_count;
69 unsigned char pcfirq_resume[5];
70 + int probe_completed;
72 /* if he pulls battery while charging, we notice that and correctly
73 * report that the charger is idle. But there is no interrupt that
74 @@ -138,6 +139,12 @@ struct pcf50633_data {
75 int usb_removal_count_nobat;
76 int jiffies_last_bat_ins;
78 + /* current limit notification handler stuff */
79 + struct mutex working_lock_usb_curlimit;
80 + struct work_struct work_usb_curlimit;
81 + int pending_curlimit;
82 + int usb_removal_count_usb_curlimit;
86 int coldplug_done; /* cleared by probe, set by first work service */
87 @@ -603,6 +610,92 @@ static void add_request_to_adc_queue(struct pcf50633_data *pcf,
88 trigger_next_adc_job_if_any(pcf);
92 + * we get run to handle servicing the async notification from USB stack that
93 + * we got enumerated and allowed to draw a particular amount of current
96 +static void pcf50633_work_usbcurlim(struct work_struct *work)
98 + struct pcf50633_data *pcf =
99 + container_of(work, struct pcf50633_data, work_usb_curlimit);
101 + mutex_lock(&pcf->working_lock_usb_curlimit);
103 + dev_info(&pcf->client.dev, "pcf50633_work_usbcurlim\n");
105 + if (!pcf->probe_completed)
108 + /* we got a notification from USB stack before we completed resume...
109 + * that can only make trouble, reschedule for a retry
111 + if (pcf->have_been_suspended && (pcf->have_been_suspended < 3))
115 + * did he pull USB before we managed to set the limit?
117 + if (pcf->usb_removal_count_usb_curlimit != pcf->usb_removal_count)
120 + /* OK let's set the requested limit and finish */
122 + dev_info(&pcf->client.dev, "pcf50633_work_usbcurlim setting %dmA\n",
123 + pcf->pending_curlimit);
124 + pcf50633_usb_curlim_set(pcf, pcf->pending_curlimit);
127 + mutex_unlock(&pcf->working_lock_usb_curlimit);
131 + dev_info(&pcf->client.dev, "pcf50633_work_usbcurlim rescheduling\n");
132 + if (!schedule_work(&pcf->work_usb_curlimit))
133 + dev_err(&pcf->client.dev, "work item may be lost\n");
135 + mutex_unlock(&pcf->working_lock_usb_curlimit);
136 + /* don't spew, delaying whatever else is happening */
141 +/* this is an export to allow machine to set USB current limit according to
142 + * notifications of USB stack about enumeration state. We spawn a work
143 + * function to handle the actual setting, because suspend / resume and such
144 + * can be in a bad state since this gets called externally asychronous to
145 + * anything else going on in pcf50633.
148 +int pcf50633_notify_usb_current_limit_change(struct pcf50633_data *pcf,
151 + /* can happen if he calls with pcf50633_global before probe
152 + * have to bail with error since we can't even schedule the work
155 + dev_err(&pcf->client.dev,
156 + "pcf50633_notify_usb_current_limit_change "
157 + "called with NULL pcf\n");
161 + dev_info(&pcf->client.dev,
162 + "pcf50633_notify_usb_current_limit_change %dmA\n", ma);
164 + /* prepare to detect USB power removal before we complete */
165 + pcf->usb_removal_count_usb_curlimit = pcf->usb_removal_count;
167 + pcf->pending_curlimit = ma;
169 + if (!schedule_work(&pcf->work_usb_curlimit))
170 + dev_err(&pcf->client.dev, "work item may be lost\n");
174 +EXPORT_SYMBOL_GPL(pcf50633_notify_usb_current_limit_change);
177 /* we are run when we see a NOBAT situation, because there is no interrupt
178 * source in pcf50633 that triggers on resuming charging. It watches to see
179 * if charging resumes, it reassesses the charging source if it does. If the
180 @@ -1960,8 +2053,10 @@ static int pcf50633_detect(struct i2c_adapter *adapter, int address, int kind)
181 mutex_init(&data->lock);
182 mutex_init(&data->working_lock);
183 mutex_init(&data->working_lock_nobat);
184 + mutex_init(&data->working_lock_usb_curlimit);
185 INIT_WORK(&data->work, pcf50633_work);
186 INIT_WORK(&data->work_nobat, pcf50633_work_nobat);
187 + INIT_WORK(&data->work_usb_curlimit, pcf50633_work_usbcurlim);
190 data->onkey_seconds = -1;
191 @@ -2060,6 +2155,7 @@ static int pcf50633_detect(struct i2c_adapter *adapter, int address, int kind)
194 apm_get_power_status = pcf50633_get_power_status;
195 + data->probe_completed = 1;
197 #ifdef CONFIG_MACH_NEO1973_GTA02
198 if (machine_is_neo1973_gta02()) {
199 diff --git a/include/linux/pcf50633.h b/include/linux/pcf50633.h
200 index 0522d92..fa1c7e8 100644
201 --- a/include/linux/pcf50633.h
202 +++ b/include/linux/pcf50633.h
203 @@ -129,6 +129,11 @@ extern void
204 pcf50633_register_resume_dependency(struct pcf50633_data *pcf,
205 struct resume_dependency *dep);
208 +pcf50633_notify_usb_current_limit_change(struct pcf50633_data *pcf,
212 /* 0 = initialized and resumed and ready to roll, !=0 = either not
213 * initialized or not resumed yet