root/drivers/mfd/ipaq-micro.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ipaq_micro_trigger_tx
  2. ipaq_micro_tx_msg
  3. micro_rx_msg
  4. micro_process_char
  5. micro_rx_chars
  6. ipaq_micro_get_version
  7. ipaq_micro_eeprom_read
  8. ipaq_micro_str
  9. ipaq_micro_to_u16
  10. ipaq_micro_eeprom_dump
  11. micro_tx_chars
  12. micro_reset_comm
  13. micro_serial_isr
  14. micro_resume
  15. micro_probe

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

/* [<][>][^][v][top][bottom][index][help] */