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