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