linux/drivers/tty/serial/8250/8250_dw.c
<<
>>
Prefs
   1/*
   2 * Synopsys DesignWare 8250 driver.
   3 *
   4 * Copyright 2011 Picochip, Jamie Iles.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * The Synopsys DesignWare 8250 has an extra feature whereby it detects if the
  12 * LCR is written whilst busy.  If it is, then a busy detect interrupt is
  13 * raised, the LCR needs to be rewritten and the uart status register read.
  14 */
  15#include <linux/device.h>
  16#include <linux/init.h>
  17#include <linux/io.h>
  18#include <linux/module.h>
  19#include <linux/serial_8250.h>
  20#include <linux/serial_core.h>
  21#include <linux/serial_reg.h>
  22#include <linux/of.h>
  23#include <linux/of_irq.h>
  24#include <linux/of_platform.h>
  25#include <linux/platform_device.h>
  26#include <linux/slab.h>
  27
  28struct dw8250_data {
  29        int     last_lcr;
  30        int     line;
  31};
  32
  33static void dw8250_serial_out(struct uart_port *p, int offset, int value)
  34{
  35        struct dw8250_data *d = p->private_data;
  36
  37        if (offset == UART_LCR)
  38                d->last_lcr = value;
  39
  40        offset <<= p->regshift;
  41        writeb(value, p->membase + offset);
  42}
  43
  44static unsigned int dw8250_serial_in(struct uart_port *p, int offset)
  45{
  46        offset <<= p->regshift;
  47
  48        return readb(p->membase + offset);
  49}
  50
  51static void dw8250_serial_out32(struct uart_port *p, int offset, int value)
  52{
  53        struct dw8250_data *d = p->private_data;
  54
  55        if (offset == UART_LCR)
  56                d->last_lcr = value;
  57
  58        offset <<= p->regshift;
  59        writel(value, p->membase + offset);
  60}
  61
  62static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
  63{
  64        offset <<= p->regshift;
  65
  66        return readl(p->membase + offset);
  67}
  68
  69/* Offset for the DesignWare's UART Status Register. */
  70#define UART_USR        0x1f
  71
  72static int dw8250_handle_irq(struct uart_port *p)
  73{
  74        struct dw8250_data *d = p->private_data;
  75        unsigned int iir = p->serial_in(p, UART_IIR);
  76
  77        if (serial8250_handle_irq(p, iir)) {
  78                return 1;
  79        } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
  80                /* Clear the USR and write the LCR again. */
  81                (void)p->serial_in(p, UART_USR);
  82                p->serial_out(p, d->last_lcr, UART_LCR);
  83
  84                return 1;
  85        }
  86
  87        return 0;
  88}
  89
  90static int __devinit dw8250_probe(struct platform_device *pdev)
  91{
  92        struct uart_port port = {};
  93        struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  94        struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  95        struct device_node *np = pdev->dev.of_node;
  96        u32 val;
  97        struct dw8250_data *data;
  98
  99        if (!regs || !irq) {
 100                dev_err(&pdev->dev, "no registers/irq defined\n");
 101                return -EINVAL;
 102        }
 103
 104        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 105        if (!data)
 106                return -ENOMEM;
 107        port.private_data = data;
 108
 109        spin_lock_init(&port.lock);
 110        port.mapbase = regs->start;
 111        port.irq = irq->start;
 112        port.handle_irq = dw8250_handle_irq;
 113        port.type = PORT_8250;
 114        port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
 115                UPF_FIXED_PORT | UPF_FIXED_TYPE;
 116        port.dev = &pdev->dev;
 117
 118        port.iotype = UPIO_MEM;
 119        port.serial_in = dw8250_serial_in;
 120        port.serial_out = dw8250_serial_out;
 121        if (!of_property_read_u32(np, "reg-io-width", &val)) {
 122                switch (val) {
 123                case 1:
 124                        break;
 125                case 4:
 126                        port.iotype = UPIO_MEM32;
 127                        port.serial_in = dw8250_serial_in32;
 128                        port.serial_out = dw8250_serial_out32;
 129                        break;
 130                default:
 131                        dev_err(&pdev->dev, "unsupported reg-io-width (%u)\n",
 132                                val);
 133                        return -EINVAL;
 134                }
 135        }
 136
 137        if (!of_property_read_u32(np, "reg-shift", &val))
 138                port.regshift = val;
 139
 140        if (of_property_read_u32(np, "clock-frequency", &val)) {
 141                dev_err(&pdev->dev, "no clock-frequency property set\n");
 142                return -EINVAL;
 143        }
 144        port.uartclk = val;
 145
 146        data->line = serial8250_register_port(&port);
 147        if (data->line < 0)
 148                return data->line;
 149
 150        platform_set_drvdata(pdev, data);
 151
 152        return 0;
 153}
 154
 155static int __devexit dw8250_remove(struct platform_device *pdev)
 156{
 157        struct dw8250_data *data = platform_get_drvdata(pdev);
 158
 159        serial8250_unregister_port(data->line);
 160
 161        return 0;
 162}
 163
 164static const struct of_device_id dw8250_match[] = {
 165        { .compatible = "snps,dw-apb-uart" },
 166        { /* Sentinel */ }
 167};
 168MODULE_DEVICE_TABLE(of, dw8250_match);
 169
 170static struct platform_driver dw8250_platform_driver = {
 171        .driver = {
 172                .name           = "dw-apb-uart",
 173                .owner          = THIS_MODULE,
 174                .of_match_table = dw8250_match,
 175        },
 176        .probe                  = dw8250_probe,
 177        .remove                 = __devexit_p(dw8250_remove),
 178};
 179
 180module_platform_driver(dw8250_platform_driver);
 181
 182MODULE_AUTHOR("Jamie Iles");
 183MODULE_LICENSE("GPL");
 184MODULE_DESCRIPTION("Synopsys DesignWare 8250 serial port driver");
 185
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.