aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mfd/Kconfig10
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/ipaq-micro.c482
-rw-r--r--include/linux/mfd/ipaq-micro.h148
4 files changed, 641 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index e56fb3749bca..60cef41b0af4 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -262,6 +262,16 @@ config MFD_INTEL_MSIC
262 Passage) chip. This chip embeds audio, battery, GPIO, etc. 262 Passage) chip. This chip embeds audio, battery, GPIO, etc.
263 devices used in Intel Medfield platforms. 263 devices used in Intel Medfield platforms.
264 264
265config MFD_IPAQ_MICRO
266 bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
267 depends on SA1100_H3100 || SA1100_H3600
268 select MFD_CORE
269 help
270 Select this to get support for the Microcontroller found in
271 the Compaq iPAQ handheld computers. This is an Atmel
272 AT90LS8535 microcontroller flashed with a special iPAQ
273 firmware using the custom protocol implemented in this driver.
274
265config MFD_JANZ_CMODIO 275config MFD_JANZ_CMODIO
266 tristate "Janz CMOD-IO PCI MODULbus Carrier Board" 276 tristate "Janz CMOD-IO PCI MODULbus Carrier Board"
267 select MFD_CORE 277 select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index df7823cae5af..5dec445ab139 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -168,3 +168,4 @@ obj-$(CONFIG_MFD_RETU) += retu-mfd.o
168obj-$(CONFIG_MFD_AS3711) += as3711.o 168obj-$(CONFIG_MFD_AS3711) += as3711.o
169obj-$(CONFIG_MFD_AS3722) += as3722.o 169obj-$(CONFIG_MFD_AS3722) += as3722.o
170obj-$(CONFIG_MFD_STW481X) += stw481x.o 170obj-$(CONFIG_MFD_STW481X) += stw481x.o
171obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o
diff --git a/drivers/mfd/ipaq-micro.c b/drivers/mfd/ipaq-micro.c
new file mode 100644
index 000000000000..1763d6db346e
--- /dev/null
+++ b/drivers/mfd/ipaq-micro.c
@@ -0,0 +1,482 @@
1/*
2 * Compaq iPAQ h3xxx Atmel microcontroller companion support
3 *
4 * This is an Atmel AT90LS8535 with a special flashed-in firmware that
5 * implements the special protocol used by this driver.
6 *
7 * based on previous kernel 2.4 version by Andrew Christian
8 * Author : Alessandro Gardich <gremlin@gremlin.it>
9 * Author : Dmitry Artamonow <mad_soft@inbox.ru>
10 * Author : Linus Walleij <linus.walleij@linaro.org>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License version 2 as
14 * published by the Free Software Foundation.
15 */
16
17#include <linux/module.h>
18#include <linux/init.h>
19#include <linux/interrupt.h>
20#include <linux/pm.h>
21#include <linux/delay.h>
22#include <linux/device.h>
23#include <linux/platform_device.h>
24#include <linux/io.h>
25#include <linux/mfd/core.h>
26#include <linux/mfd/ipaq-micro.h>
27#include <linux/string.h>
28#include <linux/random.h>
29#include <linux/slab.h>
30#include <linux/list.h>
31
32#include <mach/hardware.h>
33
34static void ipaq_micro_trigger_tx(struct ipaq_micro *micro)
35{
36 struct ipaq_micro_txdev *tx = &micro->tx;
37 struct ipaq_micro_msg *msg = micro->msg;
38 int i, bp;
39 u8 checksum;
40 u32 val;
41
42 bp = 0;
43 tx->buf[bp++] = CHAR_SOF;
44
45 checksum = ((msg->id & 0x0f) << 4) | (msg->tx_len & 0x0f);
46 tx->buf[bp++] = checksum;
47
48 for (i = 0; i < msg->tx_len; i++) {
49 tx->buf[bp++] = msg->tx_data[i];
50 checksum += msg->tx_data[i];
51 }
52
53 tx->buf[bp++] = checksum;
54 tx->len = bp;
55 tx->index = 0;
56 print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_OFFSET, 16, 1,
57 tx->buf, tx->len, true);
58
59 /* Enable interrupt */
60 val = readl(micro->base + UTCR3);
61 val |= UTCR3_TIE;
62 writel(val, micro->base + UTCR3);
63}
64
65int ipaq_micro_tx_msg(struct ipaq_micro *micro, struct ipaq_micro_msg *msg)
66{
67 unsigned long flags;
68
69 dev_dbg(micro->dev, "TX msg: %02x, %d bytes\n", msg->id, msg->tx_len);
70
71 spin_lock_irqsave(&micro->lock, flags);
72 if (micro->msg) {
73 list_add_tail(&msg->node, &micro->queue);
74 spin_unlock_irqrestore(&micro->lock, flags);
75 return 0;
76 }
77 micro->msg = msg;
78 ipaq_micro_trigger_tx(micro);
79 spin_unlock_irqrestore(&micro->lock, flags);
80 return 0;
81}
82EXPORT_SYMBOL(ipaq_micro_tx_msg);
83
84static void micro_rx_msg(struct ipaq_micro *micro, u8 id, int len, u8 *data)
85{
86 int i;
87
88 dev_dbg(micro->dev, "RX msg: %02x, %d bytes\n", id, len);
89
90 spin_lock(&micro->lock);
91 switch (id) {
92 case MSG_VERSION:
93 case MSG_EEPROM_READ:
94 case MSG_EEPROM_WRITE:
95 case MSG_BACKLIGHT:
96 case MSG_NOTIFY_LED:
97 case MSG_THERMAL_SENSOR:
98 case MSG_BATTERY:
99 /* Handle synchronous messages */
100 if (micro->msg && micro->msg->id == id) {
101 struct ipaq_micro_msg *msg = micro->msg;
102
103 memcpy(msg->rx_data, data, len);
104 msg->rx_len = len;
105 complete(&micro->msg->ack);
106 if (!list_empty(&micro->queue)) {
107 micro->msg = list_entry(micro->queue.next,
108 struct ipaq_micro_msg,
109 node);
110 list_del_init(&micro->msg->node);
111 ipaq_micro_trigger_tx(micro);
112 } else
113 micro->msg = NULL;
114 dev_dbg(micro->dev, "OK RX message 0x%02x\n", id);
115 } else {
116 dev_err(micro->dev,
117 "out of band RX message 0x%02x\n", id);
118 if(!micro->msg)
119 dev_info(micro->dev, "no message queued\n");
120 else
121 dev_info(micro->dev, "expected message %02x\n",
122 micro->msg->id);
123 }
124 break;
125 case MSG_KEYBOARD:
126 if (micro->key)
127 micro->key(micro->key_data, len, data);
128 else
129 dev_dbg(micro->dev, "key message ignored, no handle \n");
130 break;
131 case MSG_TOUCHSCREEN:
132 if (micro->ts)
133 micro->ts(micro->ts_data, len, data);
134 else
135 dev_dbg(micro->dev, "touchscreen message ignored, no handle \n");
136 break;
137 default:
138 dev_err(micro->dev,
139 "unknown msg %d [%d] ", id, len);
140 for (i = 0; i < len; ++i)
141 pr_cont("0x%02x ", data[i]);
142 pr_cont("\n");
143 }
144 spin_unlock(&micro->lock);
145}
146
147static void micro_process_char(struct ipaq_micro *micro, u8 ch)
148{
149 struct ipaq_micro_rxdev *rx = &micro->rx;
150
151 switch (rx->state) {
152 case STATE_SOF: /* Looking for SOF */
153 if (ch == CHAR_SOF)
154 rx->state = STATE_ID; /* Next byte is the id and len */
155 break;
156 case STATE_ID: /* Looking for id and len byte */
157 rx->id = (ch & 0xf0) >> 4 ;
158 rx->len = (ch & 0x0f);
159 rx->index = 0;
160 rx->chksum = ch;
161 rx->state = (rx->len > 0) ? STATE_DATA : STATE_CHKSUM;
162 break;
163 case STATE_DATA: /* Looking for 'len' data bytes */
164 rx->chksum += ch;
165 rx->buf[rx->index] = ch;
166 if (++rx->index == rx->len)
167 rx->state = STATE_CHKSUM;
168 break;
169 case STATE_CHKSUM: /* Looking for the checksum */
170 if (ch == rx->chksum)
171 micro_rx_msg(micro, rx->id, rx->len, rx->buf);
172 rx->state = STATE_SOF;
173 break;
174 }
175}
176
177static void micro_rx_chars(struct ipaq_micro *micro)
178{
179 u32 status, ch;
180
181 while ((status = readl(micro->base + UTSR1)) & UTSR1_RNE) {
182 ch = readl(micro->base + UTDR);
183 if (status & UTSR1_PRE)
184 dev_err(micro->dev, "rx: parity error\n");
185 else if (status & UTSR1_FRE)
186 dev_err(micro->dev, "rx: framing error\n");
187 else if (status & UTSR1_ROR)
188 dev_err(micro->dev, "rx: overrun error\n");
189 micro_process_char(micro, ch);
190 }
191}
192
193static void ipaq_micro_get_version(struct ipaq_micro *micro)
194{
195 struct ipaq_micro_msg msg = {
196 .id = MSG_VERSION,
197 };
198
199 ipaq_micro_tx_msg_sync(micro, &msg);
200 if (msg.rx_len == 4) {
201 memcpy(micro->version, msg.rx_data, 4);
202 micro->version[4] = '\0';
203 } else if (msg.rx_len == 9) {
204 memcpy(micro->version, msg.rx_data, 4);
205 micro->version[4] = '\0';
206 /* Bytes 4-7 are "pack", byte 8 is "boot type" */
207 } else {
208 dev_err(micro->dev,
209 "illegal version message %d bytes\n", msg.rx_len);
210 }
211}
212
213static void ipaq_micro_eeprom_read(struct ipaq_micro *micro,
214 u8 address, u8 len, u8 *data)
215{
216 struct ipaq_micro_msg msg = {
217 .id = MSG_EEPROM_READ,
218 };
219 u8 i;
220
221 for (i = 0; i < len; i++) {
222 msg.tx_data[0] = address + i;
223 msg.tx_data[1] = 1;
224 msg.tx_len = 2;
225 ipaq_micro_tx_msg_sync(micro, &msg);
226 memcpy(data + (i * 2), msg.rx_data, 2);
227 }
228}
229
230static char *ipaq_micro_str(u8 *wchar, u8 len)
231{
232 char retstr[256];
233 u8 i;
234
235 for (i = 0; i < len / 2; i++)
236 retstr[i] = wchar[i * 2];
237 return kstrdup(retstr, GFP_KERNEL);
238}
239
240static u16 ipaq_micro_to_u16(u8 *data)
241{
242 return data[1] << 8 | data[0];
243}
244
245static void ipaq_micro_eeprom_dump(struct ipaq_micro *micro)
246{
247 u8 dump[256];
248 char *str;
249
250 ipaq_micro_eeprom_read(micro, 0, 128, dump);
251 str = ipaq_micro_str(dump, 10);
252 if (str) {
253 dev_info(micro->dev, "HM version %s\n", str);
254 kfree(str);
255 }
256 str = ipaq_micro_str(dump+10, 40);
257 if (str) {
258 dev_info(micro->dev, "serial number: %s\n", str);
259 /* Feed the random pool with this */
260 add_device_randomness(str, strlen(str));
261 kfree(str);
262 }
263 str = ipaq_micro_str(dump+50, 20);
264 if (str) {
265 dev_info(micro->dev, "module ID: %s\n", str);
266 kfree(str);
267 }
268 str = ipaq_micro_str(dump+70, 10);
269 if (str) {
270 dev_info(micro->dev, "product revision: %s\n", str);
271 kfree(str);
272 }
273 dev_info(micro->dev, "product ID: %u\n", ipaq_micro_to_u16(dump+80));
274 dev_info(micro->dev, "frame rate: %u fps\n",
275 ipaq_micro_to_u16(dump+82));
276 dev_info(micro->dev, "page mode: %u\n", ipaq_micro_to_u16(dump+84));
277 dev_info(micro->dev, "country ID: %u\n", ipaq_micro_to_u16(dump+86));
278 dev_info(micro->dev, "color display: %s\n",
279 ipaq_micro_to_u16(dump+88) ? "yes" : "no");
280 dev_info(micro->dev, "ROM size: %u MiB\n", ipaq_micro_to_u16(dump+90));
281 dev_info(micro->dev, "RAM size: %u KiB\n", ipaq_micro_to_u16(dump+92));
282 dev_info(micro->dev, "screen: %u x %u\n",
283 ipaq_micro_to_u16(dump+94), ipaq_micro_to_u16(dump+96));
284 print_hex_dump(KERN_DEBUG, "eeprom: ", DUMP_PREFIX_OFFSET, 16, 1,
285 dump, 256, true);
286
287}
288
289static void micro_tx_chars(struct ipaq_micro *micro)
290{
291 struct ipaq_micro_txdev *tx = &micro->tx;
292 u32 val;
293
294 while ((tx->index < tx->len) &&
295 (readl(micro->base + UTSR1) & UTSR1_TNF)) {
296 writel(tx->buf[tx->index], micro->base + UTDR);
297 tx->index++;
298 }
299
300 /* Stop interrupts */
301 val = readl(micro->base + UTCR3);
302 val &= ~UTCR3_TIE;
303 writel(val, micro->base + UTCR3);
304}
305
306static void micro_reset_comm(struct ipaq_micro *micro)
307{
308 struct ipaq_micro_rxdev *rx = &micro->rx;
309 u32 val;
310
311 if (micro->msg)
312 complete(&micro->msg->ack);
313
314 /* Initialize Serial channel protocol frame */
315 rx->state = STATE_SOF; /* Reset the state machine */
316
317 /* Set up interrupts */
318 writel(0x01, micro->sdlc + 0x0); /* Select UART mode */
319
320 /* Clean up CR3 */
321 writel(0x0, micro->base + UTCR3);
322
323 /* Format: 8N1 */
324 writel(UTCR0_8BitData | UTCR0_1StpBit, micro->base + UTCR0);
325
326 /* Baud rate: 115200 */
327 writel(0x0, micro->base + UTCR1);
328 writel(0x1, micro->base + UTCR2);
329
330 /* Clear SR0 */
331 writel(0xff, micro->base + UTSR0);
332
333 /* Enable RX int, disable TX int */
334 writel(UTCR3_TXE | UTCR3_RXE | UTCR3_RIE, micro->base + UTCR3);
335 val = readl(micro->base + UTCR3);
336 val &= ~UTCR3_TIE;
337 writel(val, micro->base + UTCR3);
338}
339
340static irqreturn_t micro_serial_isr(int irq, void *dev_id)
341{
342 struct ipaq_micro *micro = dev_id;
343 struct ipaq_micro_txdev *tx = &micro->tx;
344 u32 status;
345
346 status = readl(micro->base + UTSR0);
347 do {
348 if (status & (UTSR0_RID | UTSR0_RFS)) {
349 if (status & UTSR0_RID)
350 /* Clear the Receiver IDLE bit */
351 writel(UTSR0_RID, micro->base + UTSR0);
352 micro_rx_chars(micro);
353 }
354
355 /* Clear break bits */
356 if (status & (UTSR0_RBB | UTSR0_REB))
357 writel(status & (UTSR0_RBB | UTSR0_REB),
358 micro->base + UTSR0);
359
360 if (status & UTSR0_TFS)
361 micro_tx_chars(micro);
362
363 status = readl(micro->base + UTSR0);
364
365 } while (((tx->index < tx->len) && (status & UTSR0_TFS)) ||
366 (status & (UTSR0_RFS | UTSR0_RID)));
367
368 return IRQ_HANDLED;
369}
370
371static struct mfd_cell micro_cells[] = {
372 { .name = "ipaq-micro-backlight", },
373 { .name = "ipaq-micro-battery", },
374 { .name = "ipaq-micro-keys", },
375 { .name = "ipaq-micro-ts", },
376 { .name = "ipaq-micro-leds", },
377};
378
379static int micro_resume(struct device *dev)
380{
381 struct ipaq_micro *micro = dev_get_drvdata(dev);
382
383 micro_reset_comm(micro);
384 mdelay(10);
385
386 return 0;
387}
388
389static int micro_probe(struct platform_device *pdev)
390{
391 struct ipaq_micro *micro;
392 struct resource *res;
393 int ret;
394 int irq;
395
396 micro = devm_kzalloc(&pdev->dev, sizeof(*micro), GFP_KERNEL);
397 if (!micro)
398 return -ENOMEM;
399
400 micro->dev = &pdev->dev;
401
402 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
403 if (!res)
404 return -EINVAL;
405
406 micro->base = devm_request_and_ioremap(&pdev->dev, res);
407 if (!micro->base)
408 return -ENOMEM;
409
410 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
411 if (!res)
412 return -EINVAL;
413
414 micro->sdlc = devm_request_and_ioremap(&pdev->dev, res);
415 if (!micro->sdlc)
416 return -ENOMEM;
417
418 micro_reset_comm(micro);
419
420 irq = platform_get_irq(pdev, 0);
421 if (!irq)
422 return -EINVAL;
423 ret = devm_request_irq(&pdev->dev, irq, micro_serial_isr,
424 IRQF_SHARED, "ipaq-micro",
425 micro);
426 if (ret) {
427 dev_err(&pdev->dev, "unable to grab serial port IRQ\n");
428 return ret;
429 } else
430 dev_info(&pdev->dev, "grabbed serial port IRQ\n");
431
432 spin_lock_init(&micro->lock);
433 INIT_LIST_HEAD(&micro->queue);
434 platform_set_drvdata(pdev, micro);
435
436 ret = mfd_add_devices(&pdev->dev, pdev->id, micro_cells,
437 ARRAY_SIZE(micro_cells), NULL, 0, NULL);
438 if (ret) {
439 dev_err(&pdev->dev, "error adding MFD cells");
440 return ret;
441 }
442
443 /* Check version */
444 ipaq_micro_get_version(micro);
445 dev_info(&pdev->dev, "Atmel micro ASIC version %s\n", micro->version);
446 ipaq_micro_eeprom_dump(micro);
447
448 return 0;
449}
450
451static int micro_remove(struct platform_device *pdev)
452{
453 struct ipaq_micro *micro = platform_get_drvdata(pdev);
454 u32 val;
455
456 mfd_remove_devices(&pdev->dev);
457
458 val = readl(micro->base + UTCR3);
459 val &= ~(UTCR3_RXE | UTCR3_RIE); /* disable receive interrupt */
460 val &= ~(UTCR3_TXE | UTCR3_TIE); /* disable transmit interrupt */
461 writel(val, micro->base + UTCR3);
462
463 return 0;
464}
465
466static const struct dev_pm_ops micro_dev_pm_ops = {
467 SET_SYSTEM_SLEEP_PM_OPS(NULL, micro_resume)
468};
469
470static struct platform_driver micro_device_driver = {
471 .driver = {
472 .name = "ipaq-h3xxx-micro",
473 .pm = &micro_dev_pm_ops,
474 },
475 .probe = micro_probe,
476 .remove = micro_remove,
477 /* .shutdown = micro_suspend, // FIXME */
478};
479module_platform_driver(micro_device_driver);
480
481MODULE_LICENSE("GPL");
482MODULE_DESCRIPTION("driver for iPAQ Atmel micro core and backlight");
diff --git a/include/linux/mfd/ipaq-micro.h b/include/linux/mfd/ipaq-micro.h
new file mode 100644
index 000000000000..5c4d29f6674f
--- /dev/null
+++ b/include/linux/mfd/ipaq-micro.h
@@ -0,0 +1,148 @@
1/*
2 * Header file for the compaq Micro MFD
3 */
4
5#ifndef _MFD_IPAQ_MICRO_H_
6#define _MFD_IPAQ_MICRO_H_
7
8#include <linux/spinlock.h>
9#include <linux/completion.h>
10#include <linux/list.h>
11
12#define TX_BUF_SIZE 32
13#define RX_BUF_SIZE 16
14#define CHAR_SOF 0x02
15
16/*
17 * These are the different messages that can be sent to the microcontroller
18 * to control various aspects.
19 */
20#define MSG_VERSION 0x0
21#define MSG_KEYBOARD 0x2
22#define MSG_TOUCHSCREEN 0x3
23#define MSG_EEPROM_READ 0x4
24#define MSG_EEPROM_WRITE 0x5
25#define MSG_THERMAL_SENSOR 0x6
26#define MSG_NOTIFY_LED 0x8
27#define MSG_BATTERY 0x9
28#define MSG_SPI_READ 0xb
29#define MSG_SPI_WRITE 0xc
30#define MSG_BACKLIGHT 0xd /* H3600 only */
31#define MSG_CODEC_CTRL 0xe /* H3100 only */
32#define MSG_DISPLAY_CTRL 0xf /* H3100 only */
33
34/* state of receiver parser */
35enum rx_state {
36 STATE_SOF = 0, /* Next byte should be start of frame */
37 STATE_ID, /* Next byte is ID & message length */
38 STATE_DATA, /* Next byte is a data byte */
39 STATE_CHKSUM /* Next byte should be checksum */
40};
41
42/**
43 * struct ipaq_micro_txdev - TX state
44 * @len: length of message in TX buffer
45 * @index: current index into TX buffer
46 * @buf: TX buffer
47 */
48struct ipaq_micro_txdev {
49 u8 len;
50 u8 index;
51 u8 buf[TX_BUF_SIZE];
52};
53
54/**
55 * struct ipaq_micro_rxdev - RX state
56 * @state: context of RX state machine
57 * @chksum: calculated checksum
58 * @id: message ID from packet
59 * @len: RX buffer length
60 * @index: RX buffer index
61 * @buf: RX buffer
62 */
63struct ipaq_micro_rxdev {
64 enum rx_state state;
65 unsigned char chksum;
66 u8 id;
67 unsigned int len;
68 unsigned int index;
69 u8 buf[RX_BUF_SIZE];
70};
71
72/**
73 * struct ipaq_micro_msg - message to the iPAQ microcontroller
74 * @id: 4-bit ID of the message
75 * @tx_len: length of TX data
76 * @tx_data: TX data to send
77 * @rx_len: length of receieved RX data
78 * @rx_data: RX data to recieve
79 * @ack: a completion that will be completed when RX is complete
80 * @node: list node if message gets queued
81 */
82struct ipaq_micro_msg {
83 u8 id;
84 u8 tx_len;
85 u8 tx_data[TX_BUF_SIZE];
86 u8 rx_len;
87 u8 rx_data[RX_BUF_SIZE];
88 struct completion ack;
89 struct list_head node;
90};
91
92/**
93 * struct ipaq_micro - iPAQ microcontroller state
94 * @dev: corresponding platform device
95 * @base: virtual memory base for underlying serial device
96 * @sdlc: virtual memory base for Synchronous Data Link Controller
97 * @version: version string
98 * @tx: TX state
99 * @rx: RX state
100 * @lock: lock for this state container
101 * @msg: current message
102 * @queue: message queue
103 * @key: callback for asynchronous key events
104 * @key_data: data to pass along with key events
105 * @ts: callback for asynchronous touchscreen events
106 * @ts_data: data to pass along with key events
107 */
108struct ipaq_micro {
109 struct device *dev;
110 void __iomem *base;
111 void __iomem *sdlc;
112 char version[5];
113 struct ipaq_micro_txdev tx; /* transmit ISR state */
114 struct ipaq_micro_rxdev rx; /* receive ISR state */
115 spinlock_t lock;
116 struct ipaq_micro_msg *msg;
117 struct list_head queue;
118 void (*key) (void *data, int len, unsigned char *rxdata);
119 void *key_data;
120 void (*ts) (void *data, int len, unsigned char *rxdata);
121 void *ts_data;
122};
123
124extern int
125ipaq_micro_tx_msg(struct ipaq_micro *micro, struct ipaq_micro_msg *msg);
126
127static inline int
128ipaq_micro_tx_msg_sync(struct ipaq_micro *micro,
129 struct ipaq_micro_msg *msg)
130{
131 int ret;
132
133 init_completion(&msg->ack);
134 ret = ipaq_micro_tx_msg(micro, msg);
135 wait_for_completion(&msg->ack);
136
137 return ret;
138}
139
140static inline int
141ipaq_micro_tx_msg_async(struct ipaq_micro *micro,
142 struct ipaq_micro_msg *msg)
143{
144 init_completion(&msg->ack);
145 return ipaq_micro_tx_msg(micro, msg);
146}
147
148#endif /* _MFD_IPAQ_MICRO_H_ */