aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorAndy Walls <awalls@radix.net>2008-11-07 21:57:46 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2008-12-30 06:38:03 -0500
commitac50441720332f22a9d85ac03151d6acb7bc55d6 (patch)
tree4c7e9b213cccf24f6cff3f614c61c80a7b96932a /drivers/media
parentd670b6ff4ef32d3a0804ec26ad53a2a7712cec98 (diff)
V4L/DVB (9595): cx18: Improve handling of outgoing mailboxes detected to be busy
cx18: Improve handling of outgoing mailboxes detected to be busy. When encountering a busy mailbox, sleep instead of polling, and wait for interrupt or timeout. If the mailbox is still busy, force it free. When sending commands, make sure we never create a situation where we mark the mailbox busy upon sending, and ensure we always have a method to cleanly recover from a busy mailbox. Signed-off-by: Andy Walls <awalls@radix.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/video/cx18/cx18-irq.c3
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c130
2 files changed, 64 insertions, 69 deletions
diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c
index 37b931055d3a..4912b3c8e6a5 100644
--- a/drivers/media/video/cx18/cx18-irq.c
+++ b/drivers/media/video/cx18/cx18-irq.c
@@ -167,7 +167,8 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id)
167 cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2); 167 cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2);
168 168
169 if (sw1 || sw2 || hw2) 169 if (sw1 || sw2 || hw2)
170 CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); 170 CX18_DEBUG_HI_IRQ("received interrupts "
171 "SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2);
171 172
172 /* To do: interrupt-based I2C handling 173 /* To do: interrupt-based I2C handling
173 if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) { 174 if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) {
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index d2975a2e6cb7..0de4b9a7bbca 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -92,48 +92,6 @@ static const struct cx18_api_info *find_api_info(u32 cmd)
92 return NULL; 92 return NULL;
93} 93}
94 94
95static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu,
96 u32 *state, u32 *irq, u32 *req)
97{
98 struct cx18_mailbox __iomem *mb = NULL;
99 int wait_count = 0;
100 u32 ack;
101
102 switch (rpu) {
103 case APU:
104 mb = &cx->scb->epu2apu_mb;
105 *state = cx18_readl(cx, &cx->scb->apu_state);
106 *irq = cx18_readl(cx, &cx->scb->epu2apu_irq);
107 break;
108
109 case CPU:
110 mb = &cx->scb->epu2cpu_mb;
111 *state = cx18_readl(cx, &cx->scb->cpu_state);
112 *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq);
113 break;
114
115 default:
116 break;
117 }
118
119 if (mb == NULL)
120 return mb;
121
122 do {
123 *req = cx18_readl(cx, &mb->request);
124 ack = cx18_readl(cx, &mb->ack);
125 wait_count++;
126 } while (*req != ack && wait_count < 600);
127
128 if (*req == ack) {
129 (*req)++;
130 if (*req == 0 || *req == 0xffffffff)
131 *req = 1;
132 return mb;
133 }
134 return NULL;
135}
136
137long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu) 95long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu)
138{ 96{
139 struct cx18_mailbox __iomem *ack_mb; 97 struct cx18_mailbox __iomem *ack_mb;
@@ -164,12 +122,13 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu)
164static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) 122static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
165{ 123{
166 const struct cx18_api_info *info = find_api_info(cmd); 124 const struct cx18_api_info *info = find_api_info(cmd);
167 u32 state = 0, irq = 0, req, oldreq, err; 125 u32 state, irq, req, ack, err;
168 struct cx18_mailbox __iomem *mb; 126 struct cx18_mailbox __iomem *mb;
127 u32 __iomem *xpu_state;
169 wait_queue_head_t *waitq; 128 wait_queue_head_t *waitq;
170 struct mutex *mb_lock; 129 struct mutex *mb_lock;
171 int timeout = 100; 130 int timeout = 100;
172 int sig = 0; 131 long unsigned int j, ret;
173 int i; 132 int i;
174 133
175 if (info == NULL) { 134 if (info == NULL) {
@@ -186,10 +145,16 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
186 case APU: 145 case APU:
187 waitq = &cx->mb_apu_waitq; 146 waitq = &cx->mb_apu_waitq;
188 mb_lock = &cx->epu2apu_mb_lock; 147 mb_lock = &cx->epu2apu_mb_lock;
148 irq = IRQ_EPU_TO_APU;
149 mb = &cx->scb->epu2apu_mb;
150 xpu_state = &cx->scb->apu_state;
189 break; 151 break;
190 case CPU: 152 case CPU:
191 waitq = &cx->mb_cpu_waitq; 153 waitq = &cx->mb_cpu_waitq;
192 mb_lock = &cx->epu2cpu_mb_lock; 154 mb_lock = &cx->epu2cpu_mb_lock;
155 irq = IRQ_EPU_TO_CPU;
156 mb = &cx->scb->epu2cpu_mb;
157 xpu_state = &cx->scb->cpu_state;
193 break; 158 break;
194 default: 159 default:
195 CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu); 160 CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu);
@@ -198,51 +163,85 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
198 163
199 mutex_lock(mb_lock); 164 mutex_lock(mb_lock);
200 cx18_setup_page(cx, SCB_OFFSET); 165 cx18_setup_page(cx, SCB_OFFSET);
201 mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req);
202 166
203 if (mb == NULL) { 167 /*
204 mutex_unlock(mb_lock); 168 * Wait for an in-use mailbox to complete
205 CX18_ERR("mb %s busy\n", info->name); 169 *
206 return -EBUSY; 170 * If the XPU is responding with Ack's, the mailbox shouldn't be in
207 } 171 * a busy state, since we serialize access to it on our end.
172 *
173 * If the wait for ack after sending a previous command was interrupted
174 * by a signal, we may get here and find a busy mailbox. After waiting,
175 * mark it "not busy" from our end, if the XPU hasn't ack'ed it still.
176 */
177 state = cx18_readl(cx, xpu_state);
178 req = cx18_readl(cx, &mb->request);
179 j = msecs_to_jiffies(timeout);
180 ret = wait_event_timeout(*waitq,
181 (ack = cx18_readl(cx, &mb->ack)) == req,
182 j);
183 if (req != ack) {
184 /* waited long enough, make the mbox "not busy" from our end */
185 cx18_writel(cx, req, &mb->ack);
186 CX18_ERR("mbox was found stuck busy when setting up for %s; "
187 "clearing busy and trying to proceed\n", info->name);
188 } else if (ret != j)
189 CX18_DEBUG_API("waited %u usecs for busy mbox to be acked\n",
190 jiffies_to_usecs(j-ret));
191
192 /* Build the outgoing mailbox */
193 req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1;
208 194
209 oldreq = req - 1;
210 cx18_writel(cx, cmd, &mb->cmd); 195 cx18_writel(cx, cmd, &mb->cmd);
211 for (i = 0; i < args; i++) 196 for (i = 0; i < args; i++)
212 cx18_writel(cx, data[i], &mb->args[i]); 197 cx18_writel(cx, data[i], &mb->args[i]);
213 cx18_writel(cx, 0, &mb->error); 198 cx18_writel(cx, 0, &mb->error);
214 cx18_writel(cx, req, &mb->request); 199 cx18_writel(cx, req, &mb->request);
200 cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */
215 201
202 /* Notify the XPU and wait for it to send an Ack back */
216 if (info->flags & API_FAST) 203 if (info->flags & API_FAST)
217 timeout /= 2; 204 timeout /= 2;
205 j = msecs_to_jiffies(timeout);
206
207 CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n",
208 irq, info->name);
218 cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq); 209 cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq);
219 210
220 sig = wait_event_interruptible_timeout( 211 ret = wait_event_interruptible_timeout(
221 *waitq, 212 *waitq,
222 cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request), 213 cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request),
223 msecs_to_jiffies(timeout)); 214 j);
224 if (sig == 0) { 215 if (ret == 0) {
225 /* Timed out */ 216 /* Timed out */
226 cx18_writel(cx, oldreq, &mb->request);
227 mutex_unlock(mb_lock); 217 mutex_unlock(mb_lock);
228 CX18_ERR("sending %s timed out waiting for RPU to respond\n", 218 CX18_ERR("sending %s timed out waiting for RPU "
229 info->name); 219 "acknowledgement\n", info->name);
230 return -EINVAL; 220 return -EINVAL;
231 } else if (sig < 0) { 221 } else if (ret < 0) {
232 /* Interrupted */ 222 /* Interrupted */
233 cx18_writel(cx, oldreq, &mb->request);
234 mutex_unlock(mb_lock); 223 mutex_unlock(mb_lock);
235 CX18_WARN("sending %s interrupted waiting for RPU to respond\n", 224 CX18_WARN("sending %s was interrupted waiting for RPU"
236 info->name); 225 "acknowledgement\n", info->name);
237 return -EINTR; 226 return -EINTR;
238 } 227 } else if (ret != j)
228 CX18_DEBUG_HI_API("waited %u usecs for %s to be acked\n",
229 jiffies_to_usecs(j-ret), info->name);
239 230
231 /* Collect data returned by the XPU */
240 for (i = 0; i < MAX_MB_ARGUMENTS; i++) 232 for (i = 0; i < MAX_MB_ARGUMENTS; i++)
241 data[i] = cx18_readl(cx, &mb->args[i]); 233 data[i] = cx18_readl(cx, &mb->args[i]);
242 err = cx18_readl(cx, &mb->error); 234 err = cx18_readl(cx, &mb->error);
243 mutex_unlock(mb_lock); 235 mutex_unlock(mb_lock);
236
237 /*
238 * Wait for XPU to perform extra actions for the caller in some cases.
239 * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers
240 * back in a burst shortly thereafter
241 */
244 if (info->flags & API_SLOW) 242 if (info->flags & API_SLOW)
245 cx18_msleep_timeout(300, 0); 243 cx18_msleep_timeout(300, 0);
244
246 if (err) 245 if (err)
247 CX18_DEBUG_API("mailbox error %08x for command %s\n", err, 246 CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
248 info->name); 247 info->name);
@@ -251,12 +250,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
251 250
252int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]) 251int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[])
253{ 252{
254 int res = cx18_api_call(cx, cmd, args, data); 253 return cx18_api_call(cx, cmd, args, data);
255
256 /* Allow a single retry, probably already too late though.
257 If there is no free mailbox then that is usually an indication
258 of a more serious problem. */
259 return (res == -EBUSY) ? cx18_api_call(cx, cmd, args, data) : res;
260} 254}
261 255
262static int cx18_set_filter_param(struct cx18_stream *s) 256static int cx18_set_filter_param(struct cx18_stream *s)