Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mfd: Add driver for Atmel Microcontroller on iPaq h3xxx

This adds a driver for the Atmel Microcontroller found on the
iPAQ h3xxx series. This device handles some keys, the
touchscreen, and the battery monitoring.

This is a port of a driver from handhelds.org 2.6.21 kernel,
written by Alessandro Gardich based on Andrew Christians
original HAL-driver. It has been heavily cleaned and
converted to mfd-core by Dmitry Artamonow and rewritten
again for the v3.x series kernels by Linus Walleij,
bringing back some of the functionality lost from Andrew's
original driver.

Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Alessandro Gardich <gremlin@gremlin.it>
Signed-off-by: Dmitry Artamonow <mad_soft@inbox.ru>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>

authored by

Linus Walleij and committed by
Lee Jones
dcc21cc0 3d237990

+641
+10
drivers/mfd/Kconfig
··· 262 262 Passage) chip. This chip embeds audio, battery, GPIO, etc. 263 263 devices used in Intel Medfield platforms. 264 264 265 + config 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 + 265 275 config MFD_JANZ_CMODIO 266 276 tristate "Janz CMOD-IO PCI MODULbus Carrier Board" 267 277 select MFD_CORE
+1
drivers/mfd/Makefile
··· 168 168 obj-$(CONFIG_MFD_AS3711) += as3711.o 169 169 obj-$(CONFIG_MFD_AS3722) += as3722.o 170 170 obj-$(CONFIG_MFD_STW481X) += stw481x.o 171 + obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o
+482
drivers/mfd/ipaq-micro.c
··· 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 + 34 + static 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 + 65 + int 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 + } 82 + EXPORT_SYMBOL(ipaq_micro_tx_msg); 83 + 84 + static 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 + 147 + static 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 + 177 + static 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 + 193 + static 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 + 213 + static 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 + 230 + static 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 + 240 + static u16 ipaq_micro_to_u16(u8 *data) 241 + { 242 + return data[1] << 8 | data[0]; 243 + } 244 + 245 + static 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 + 289 + static 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 + 306 + static 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 + 340 + static 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 + 371 + static 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 + 379 + static 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 + 389 + static 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 + 451 + static 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 + 466 + static const struct dev_pm_ops micro_dev_pm_ops = { 467 + SET_SYSTEM_SLEEP_PM_OPS(NULL, micro_resume) 468 + }; 469 + 470 + static 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 + }; 479 + module_platform_driver(micro_device_driver); 480 + 481 + MODULE_LICENSE("GPL"); 482 + MODULE_DESCRIPTION("driver for iPAQ Atmel micro core and backlight");
+148
include/linux/mfd/ipaq-micro.h
··· 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 */ 35 + enum 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 + */ 48 + struct 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 + */ 63 + struct 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 + */ 82 + struct 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 + */ 108 + struct 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 + 124 + extern int 125 + ipaq_micro_tx_msg(struct ipaq_micro *micro, struct ipaq_micro_msg *msg); 126 + 127 + static inline int 128 + ipaq_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 + 140 + static inline int 141 + ipaq_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_ */