linux/drivers/mfd/ipaq-micro.c
<<
>>
Prefs
   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_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 __init 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, "HW 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_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 const 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 __maybe_unused 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 __init 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_ioremap_resource(&pdev->dev, res);
 407        if (IS_ERR(micro->base))
 408                return PTR_ERR(micro->base);
 409
 410        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 411        if (!res)
 412                return -EINVAL;
 413
 414        micro->sdlc = devm_ioremap_resource(&pdev->dev, res);
 415        if (IS_ERR(micro->sdlc))
 416                return PTR_ERR(micro->sdlc);
 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 const struct dev_pm_ops micro_dev_pm_ops = {
 452        SET_SYSTEM_SLEEP_PM_OPS(NULL, micro_resume)
 453};
 454
 455static struct platform_driver micro_device_driver = {
 456        .driver   = {
 457                .name   = "ipaq-h3xxx-micro",
 458                .pm     = &micro_dev_pm_ops,
 459                .suppress_bind_attrs = true,
 460        },
 461};
 462builtin_platform_driver_probe(micro_device_driver, micro_probe);
 463