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
1 From 553cc686ad357fc169a95733c51f7ae3466bb248 Mon Sep 17 00:00:00 2001
2 From: Andy Green <andy@openmoko.com>
3 Date: Wed, 2 Jul 2008 22:40:21 +0100
4 Subject: [PATCH] fix-pcf50633-usb-curlim-workqueue-migration.patch
5
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.
10
11 Signed-off-by: Andy Green <andy@openmoko.com>
12 ---
13  arch/arm/mach-s3c2440/mach-gta02.c |   23 +--------
14  drivers/i2c/chips/pcf50633.c       |   99 +++++++++++++++++++++++++++++++++++-
15  include/linux/pcf50633.h           |    5 ++
16  3 files changed, 104 insertions(+), 23 deletions(-)
17
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)
23         }
24  }
25  
26 -/* use a work queue, since I2C API inherently schedules
27 - * and we get called in hardirq context from UDC driver */
28 -
29 -struct vbus_draw {
30 -       struct work_struct work;
31 -       int ma;
32 -};
33 -static struct vbus_draw gta02_udc_vbus_drawer;
34 -
35 -static void __gta02_udc_vbus_draw(struct work_struct *work)
36 -{
37 -       if (!pcf50633_global) {
38 -               printk(KERN_ERR  "pcf50633 not initialized yet, can't change "
39 -                      "vbus_draw\n");
40 -               return;
41 -       }
42 -       pcf50633_usb_curlim_set(pcf50633_global, gta02_udc_vbus_drawer.ma);
43 -}
44 +/* get PMU to set USB current limit accordingly */
45  
46  static void gta02_udc_vbus_draw(unsigned int ma)
47  {
48 -       gta02_udc_vbus_drawer.ma = ma;
49 -       schedule_work(&gta02_udc_vbus_drawer.work);
50 +       pcf50633_notify_usb_current_limit_change(pcf50633_global, ma);
51  }
52  
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);
57  
58 -       INIT_WORK(&gta02_udc_vbus_drawer.work, __gta02_udc_vbus_draw);
59         s3c24xx_udc_set_platdata(&gta02_udc_cfg);
60         set_s3c2410ts_info(&gta02_ts_cfg);
61  
62 diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
63 index 96857a3..e53bec0 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;
71  
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;
77  
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;
83 +
84         int last_curlim_set;
85  
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);
89  }
90  
91 +/*
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
94 + */
95 +
96 +static void pcf50633_work_usbcurlim(struct work_struct *work)
97 +{
98 +       struct pcf50633_data *pcf =
99 +                   container_of(work, struct pcf50633_data, work_usb_curlimit);
100 +
101 +       mutex_lock(&pcf->working_lock_usb_curlimit);
102 +
103 +       dev_info(&pcf->client.dev, "pcf50633_work_usbcurlim\n");
104 +
105 +       if (!pcf->probe_completed)
106 +               goto reschedule;
107 +
108 +       /* we got a notification from USB stack before we completed resume...
109 +        * that can only make trouble, reschedule for a retry
110 +        */
111 +       if (pcf->have_been_suspended && (pcf->have_been_suspended < 3))
112 +               goto reschedule;
113 +
114 +       /*
115 +        * did he pull USB before we managed to set the limit?
116 +        */
117 +       if (pcf->usb_removal_count_usb_curlimit != pcf->usb_removal_count)
118 +               goto bail;
119 +
120 +       /* OK let's set the requested limit and finish */
121 +
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);
125 +
126 +bail:
127 +       mutex_unlock(&pcf->working_lock_usb_curlimit);
128 +       return;
129 +
130 +reschedule:
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");
134 +
135 +       mutex_unlock(&pcf->working_lock_usb_curlimit);
136 +       /* don't spew, delaying whatever else is happening */
137 +       msleep(1);
138 +}
139 +
140 +
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.
146 + */
147 +
148 +int pcf50633_notify_usb_current_limit_change(struct pcf50633_data *pcf,
149 +                                                               unsigned int ma)
150 +{
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
153 +        */
154 +       if (!pcf) {
155 +               dev_err(&pcf->client.dev,
156 +                       "pcf50633_notify_usb_current_limit_change "
157 +                       "called with NULL pcf\n");
158 +               return -EBUSY;
159 +       }
160 +
161 +       dev_info(&pcf->client.dev,
162 +                "pcf50633_notify_usb_current_limit_change %dmA\n", ma);
163 +
164 +       /* prepare to detect USB power removal before we complete */
165 +       pcf->usb_removal_count_usb_curlimit = pcf->usb_removal_count;
166 +
167 +       pcf->pending_curlimit = ma;
168 +
169 +       if (!schedule_work(&pcf->work_usb_curlimit))
170 +               dev_err(&pcf->client.dev, "work item may be lost\n");
171 +
172 +       return 0;
173 +}
174 +EXPORT_SYMBOL_GPL(pcf50633_notify_usb_current_limit_change);
175 +
176 +
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 @@ -1967,8 +2060,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);
188         data->irq = irq;
189         data->working = 0;
190         data->onkey_seconds = -1;
191 @@ -2068,15 +2163,15 @@ static int pcf50633_detect(struct i2c_adapter *adapter, int address, int kind)
192                 backlight_update_status(data->backlight);
193         }
194  
195 +       data->probe_completed = 1;
196  
197         if (machine_is_neo1973_gta02()) {
198                 gta01_pm_gps_dev.dev.parent = &new_client->dev;
199                 gta01_pm_bt_dev.dev.parent = &new_client->dev;
200                 platform_device_register(&gta01_pm_bt_dev);
201                 platform_device_register(&gta01_pm_gps_dev);
202 -       } else {
203 +       } else
204                 apm_get_power_status = pcf50633_get_power_status;
205 -       }
206  
207         return 0;
208  exit_rtc:
209 diff --git a/include/linux/pcf50633.h b/include/linux/pcf50633.h
210 index 0522d92..fa1c7e8 100644
211 --- a/include/linux/pcf50633.h
212 +++ b/include/linux/pcf50633.h
213 @@ -129,6 +129,11 @@ extern void
214  pcf50633_register_resume_dependency(struct pcf50633_data *pcf,
215                                         struct resume_dependency *dep);
216  
217 +extern int
218 +pcf50633_notify_usb_current_limit_change(struct pcf50633_data *pcf,
219 +                                                              unsigned int ma);
220 +
221 +
222  /* 0 = initialized and resumed and ready to roll, !=0 = either not
223   * initialized or not resumed yet
224   */
225 -- 
226 1.5.6.5
227