Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  *  Serial Port driver for Aspeed VUART device
0004  *
0005  *    Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp.
0006  *    Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp.
0007  */
0008 #include <linux/device.h>
0009 #include <linux/module.h>
0010 #include <linux/of_address.h>
0011 #include <linux/of_irq.h>
0012 #include <linux/of_platform.h>
0013 #include <linux/regmap.h>
0014 #include <linux/mfd/syscon.h>
0015 #include <linux/tty.h>
0016 #include <linux/tty_flip.h>
0017 #include <linux/clk.h>
0018 
0019 #include "8250.h"
0020 
0021 #define ASPEED_VUART_GCRA       0x20
0022 #define ASPEED_VUART_GCRA_VUART_EN      BIT(0)
0023 #define ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY    BIT(1)
0024 #define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5)
0025 #define ASPEED_VUART_GCRB       0x24
0026 #define ASPEED_VUART_GCRB_HOST_SIRQ_MASK    GENMASK(7, 4)
0027 #define ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT   4
0028 #define ASPEED_VUART_ADDRL      0x28
0029 #define ASPEED_VUART_ADDRH      0x2c
0030 
0031 #define ASPEED_VUART_DEFAULT_LPC_ADDR   0x3f8
0032 #define ASPEED_VUART_DEFAULT_SIRQ   4
0033 #define ASPEED_VUART_DEFAULT_SIRQ_POLARITY  IRQ_TYPE_LEVEL_LOW
0034 
0035 struct aspeed_vuart {
0036     struct device       *dev;
0037     struct clk      *clk;
0038     int         line;
0039     struct timer_list   unthrottle_timer;
0040     struct uart_8250_port   *port;
0041 };
0042 
0043 /*
0044  * If we fill the tty flip buffers, we throttle the data ready interrupt
0045  * to prevent dropped characters. This timeout defines how long we wait
0046  * to (conditionally, depending on buffer state) unthrottle.
0047  */
0048 static const int unthrottle_timeout = HZ/10;
0049 
0050 /*
0051  * The VUART is basically two UART 'front ends' connected by their FIFO
0052  * (no actual serial line in between). One is on the BMC side (management
0053  * controller) and one is on the host CPU side.
0054  *
0055  * It allows the BMC to provide to the host a "UART" that pipes into
0056  * the BMC itself and can then be turned by the BMC into a network console
0057  * of some sort for example.
0058  *
0059  * This driver is for the BMC side. The sysfs files allow the BMC
0060  * userspace which owns the system configuration policy, to specify
0061  * at what IO port and interrupt number the host side will appear
0062  * to the host on the Host <-> BMC LPC bus. It could be different on a
0063  * different system (though most of them use 3f8/4).
0064  */
0065 
0066 static inline u8 aspeed_vuart_readb(struct aspeed_vuart *vuart, u8 reg)
0067 {
0068     return readb(vuart->port->port.membase + reg);
0069 }
0070 
0071 static inline void aspeed_vuart_writeb(struct aspeed_vuart *vuart, u8 val, u8 reg)
0072 {
0073     writeb(val, vuart->port->port.membase + reg);
0074 }
0075 
0076 static ssize_t lpc_address_show(struct device *dev,
0077                 struct device_attribute *attr, char *buf)
0078 {
0079     struct aspeed_vuart *vuart = dev_get_drvdata(dev);
0080     u16 addr;
0081 
0082     addr = (aspeed_vuart_readb(vuart, ASPEED_VUART_ADDRH) << 8) |
0083         (aspeed_vuart_readb(vuart, ASPEED_VUART_ADDRL));
0084 
0085     return sysfs_emit(buf, "0x%x\n", addr);
0086 }
0087 
0088 static int aspeed_vuart_set_lpc_address(struct aspeed_vuart *vuart, u32 addr)
0089 {
0090     if (addr > U16_MAX)
0091         return -EINVAL;
0092 
0093     aspeed_vuart_writeb(vuart, addr >> 8, ASPEED_VUART_ADDRH);
0094     aspeed_vuart_writeb(vuart, addr >> 0, ASPEED_VUART_ADDRL);
0095 
0096     return 0;
0097 }
0098 
0099 static ssize_t lpc_address_store(struct device *dev,
0100                  struct device_attribute *attr,
0101                  const char *buf, size_t count)
0102 {
0103     struct aspeed_vuart *vuart = dev_get_drvdata(dev);
0104     u32 val;
0105     int err;
0106 
0107     err = kstrtou32(buf, 0, &val);
0108     if (err)
0109         return err;
0110 
0111     err = aspeed_vuart_set_lpc_address(vuart, val);
0112     return err ? : count;
0113 }
0114 
0115 static DEVICE_ATTR_RW(lpc_address);
0116 
0117 static ssize_t sirq_show(struct device *dev,
0118              struct device_attribute *attr, char *buf)
0119 {
0120     struct aspeed_vuart *vuart = dev_get_drvdata(dev);
0121     u8 reg;
0122 
0123     reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRB);
0124     reg &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK;
0125     reg >>= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT;
0126 
0127     return sysfs_emit(buf, "%u\n", reg);
0128 }
0129 
0130 static int aspeed_vuart_set_sirq(struct aspeed_vuart *vuart, u32 sirq)
0131 {
0132     u8 reg;
0133 
0134     if (sirq > (ASPEED_VUART_GCRB_HOST_SIRQ_MASK >> ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT))
0135         return -EINVAL;
0136 
0137     sirq <<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT;
0138     sirq &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK;
0139 
0140     reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRB);
0141     reg &= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK;
0142     reg |= sirq;
0143     aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRB);
0144 
0145     return 0;
0146 }
0147 
0148 static ssize_t sirq_store(struct device *dev, struct device_attribute *attr,
0149               const char *buf, size_t count)
0150 {
0151     struct aspeed_vuart *vuart = dev_get_drvdata(dev);
0152     unsigned long val;
0153     int err;
0154 
0155     err = kstrtoul(buf, 0, &val);
0156     if (err)
0157         return err;
0158 
0159     err = aspeed_vuart_set_sirq(vuart, val);
0160     return err ? : count;
0161 }
0162 
0163 static DEVICE_ATTR_RW(sirq);
0164 
0165 static ssize_t sirq_polarity_show(struct device *dev,
0166                   struct device_attribute *attr, char *buf)
0167 {
0168     struct aspeed_vuart *vuart = dev_get_drvdata(dev);
0169     u8 reg;
0170 
0171     reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA);
0172     reg &= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
0173 
0174     return sysfs_emit(buf, "%u\n", reg ? 1 : 0);
0175 }
0176 
0177 static void aspeed_vuart_set_sirq_polarity(struct aspeed_vuart *vuart,
0178                        bool polarity)
0179 {
0180     u8 reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA);
0181 
0182     if (polarity)
0183         reg |= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
0184     else
0185         reg &= ~ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
0186 
0187     aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRA);
0188 }
0189 
0190 static ssize_t sirq_polarity_store(struct device *dev,
0191                    struct device_attribute *attr,
0192                    const char *buf, size_t count)
0193 {
0194     struct aspeed_vuart *vuart = dev_get_drvdata(dev);
0195     unsigned long val;
0196     int err;
0197 
0198     err = kstrtoul(buf, 0, &val);
0199     if (err)
0200         return err;
0201 
0202     aspeed_vuart_set_sirq_polarity(vuart, val != 0);
0203 
0204     return count;
0205 }
0206 
0207 static DEVICE_ATTR_RW(sirq_polarity);
0208 
0209 static struct attribute *aspeed_vuart_attrs[] = {
0210     &dev_attr_sirq.attr,
0211     &dev_attr_sirq_polarity.attr,
0212     &dev_attr_lpc_address.attr,
0213     NULL,
0214 };
0215 
0216 static const struct attribute_group aspeed_vuart_attr_group = {
0217     .attrs = aspeed_vuart_attrs,
0218 };
0219 
0220 static void aspeed_vuart_set_enabled(struct aspeed_vuart *vuart, bool enabled)
0221 {
0222     u8 reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA);
0223 
0224     if (enabled)
0225         reg |= ASPEED_VUART_GCRA_VUART_EN;
0226     else
0227         reg &= ~ASPEED_VUART_GCRA_VUART_EN;
0228 
0229     aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRA);
0230 }
0231 
0232 static void aspeed_vuart_set_host_tx_discard(struct aspeed_vuart *vuart,
0233                          bool discard)
0234 {
0235     u8 reg;
0236 
0237     reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA);
0238 
0239     /* If the DISABLE_HOST_TX_DISCARD bit is set, discard is disabled */
0240     if (!discard)
0241         reg |= ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD;
0242     else
0243         reg &= ~ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD;
0244 
0245     aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRA);
0246 }
0247 
0248 static int aspeed_vuart_startup(struct uart_port *uart_port)
0249 {
0250     struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port);
0251     struct aspeed_vuart *vuart = uart_8250_port->port.private_data;
0252     int rc;
0253 
0254     rc = serial8250_do_startup(uart_port);
0255     if (rc)
0256         return rc;
0257 
0258     aspeed_vuart_set_host_tx_discard(vuart, false);
0259 
0260     return 0;
0261 }
0262 
0263 static void aspeed_vuart_shutdown(struct uart_port *uart_port)
0264 {
0265     struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port);
0266     struct aspeed_vuart *vuart = uart_8250_port->port.private_data;
0267 
0268     aspeed_vuart_set_host_tx_discard(vuart, true);
0269 
0270     serial8250_do_shutdown(uart_port);
0271 }
0272 
0273 static void __aspeed_vuart_set_throttle(struct uart_8250_port *up,
0274         bool throttle)
0275 {
0276     unsigned char irqs = UART_IER_RLSI | UART_IER_RDI;
0277 
0278     up->ier &= ~irqs;
0279     if (!throttle)
0280         up->ier |= irqs;
0281     serial_out(up, UART_IER, up->ier);
0282 }
0283 static void aspeed_vuart_set_throttle(struct uart_port *port, bool throttle)
0284 {
0285     struct uart_8250_port *up = up_to_u8250p(port);
0286     unsigned long flags;
0287 
0288     spin_lock_irqsave(&port->lock, flags);
0289     __aspeed_vuart_set_throttle(up, throttle);
0290     spin_unlock_irqrestore(&port->lock, flags);
0291 }
0292 
0293 static void aspeed_vuart_throttle(struct uart_port *port)
0294 {
0295     aspeed_vuart_set_throttle(port, true);
0296 }
0297 
0298 static void aspeed_vuart_unthrottle(struct uart_port *port)
0299 {
0300     aspeed_vuart_set_throttle(port, false);
0301 }
0302 
0303 static void aspeed_vuart_unthrottle_exp(struct timer_list *timer)
0304 {
0305     struct aspeed_vuart *vuart = from_timer(vuart, timer, unthrottle_timer);
0306     struct uart_8250_port *up = vuart->port;
0307 
0308     if (!tty_buffer_space_avail(&up->port.state->port)) {
0309         mod_timer(&vuart->unthrottle_timer,
0310               jiffies + unthrottle_timeout);
0311         return;
0312     }
0313 
0314     aspeed_vuart_unthrottle(&up->port);
0315 }
0316 
0317 /*
0318  * Custom interrupt handler to manage finer-grained flow control. Although we
0319  * have throttle/unthrottle callbacks, we've seen that the VUART device can
0320  * deliver characters faster than the ldisc has a chance to check buffer space
0321  * against the throttle threshold. This results in dropped characters before
0322  * the throttle.
0323  *
0324  * We do this by checking for flip buffer space before RX. If we have no space,
0325  * throttle now and schedule an unthrottle for later, once the ldisc has had
0326  * a chance to drain the buffers.
0327  */
0328 static int aspeed_vuart_handle_irq(struct uart_port *port)
0329 {
0330     struct uart_8250_port *up = up_to_u8250p(port);
0331     unsigned int iir, lsr;
0332     unsigned long flags;
0333     unsigned int space, count;
0334 
0335     iir = serial_port_in(port, UART_IIR);
0336 
0337     if (iir & UART_IIR_NO_INT)
0338         return 0;
0339 
0340     spin_lock_irqsave(&port->lock, flags);
0341 
0342     lsr = serial_port_in(port, UART_LSR);
0343 
0344     if (lsr & (UART_LSR_DR | UART_LSR_BI)) {
0345         space = tty_buffer_space_avail(&port->state->port);
0346 
0347         if (!space) {
0348             /* throttle and schedule an unthrottle later */
0349             struct aspeed_vuart *vuart = port->private_data;
0350             __aspeed_vuart_set_throttle(up, true);
0351 
0352             if (!timer_pending(&vuart->unthrottle_timer))
0353                 mod_timer(&vuart->unthrottle_timer,
0354                       jiffies + unthrottle_timeout);
0355 
0356         } else {
0357             count = min(space, 256U);
0358 
0359             do {
0360                 serial8250_read_char(up, lsr);
0361                 lsr = serial_in(up, UART_LSR);
0362                 if (--count == 0)
0363                     break;
0364             } while (lsr & (UART_LSR_DR | UART_LSR_BI));
0365 
0366             tty_flip_buffer_push(&port->state->port);
0367         }
0368     }
0369 
0370     serial8250_modem_status(up);
0371     if (lsr & UART_LSR_THRE)
0372         serial8250_tx_chars(up);
0373 
0374     uart_unlock_and_check_sysrq_irqrestore(port, flags);
0375 
0376     return 1;
0377 }
0378 
0379 static void aspeed_vuart_auto_configure_sirq_polarity(
0380     struct aspeed_vuart *vuart, struct device_node *syscon_np,
0381     u32 reg_offset, u32 reg_mask)
0382 {
0383     struct regmap *regmap;
0384     u32 value;
0385 
0386     regmap = syscon_node_to_regmap(syscon_np);
0387     if (IS_ERR(regmap)) {
0388         dev_warn(vuart->dev,
0389              "could not get regmap for aspeed,sirq-polarity-sense\n");
0390         return;
0391     }
0392     if (regmap_read(regmap, reg_offset, &value)) {
0393         dev_warn(vuart->dev, "could not read hw strap table\n");
0394         return;
0395     }
0396 
0397     aspeed_vuart_set_sirq_polarity(vuart, (value & reg_mask) == 0);
0398 }
0399 
0400 static int aspeed_vuart_map_irq_polarity(u32 dt)
0401 {
0402     switch (dt) {
0403     case IRQ_TYPE_LEVEL_LOW:
0404         return 0;
0405     case IRQ_TYPE_LEVEL_HIGH:
0406         return 1;
0407     default:
0408         return -EINVAL;
0409     }
0410 }
0411 
0412 static int aspeed_vuart_probe(struct platform_device *pdev)
0413 {
0414     struct of_phandle_args sirq_polarity_sense_args;
0415     struct uart_8250_port port;
0416     struct aspeed_vuart *vuart;
0417     struct device_node *np;
0418     struct resource *res;
0419     u32 clk, prop, sirq[2];
0420     int rc, sirq_polarity;
0421 
0422     np = pdev->dev.of_node;
0423 
0424     vuart = devm_kzalloc(&pdev->dev, sizeof(*vuart), GFP_KERNEL);
0425     if (!vuart)
0426         return -ENOMEM;
0427 
0428     vuart->dev = &pdev->dev;
0429     timer_setup(&vuart->unthrottle_timer, aspeed_vuart_unthrottle_exp, 0);
0430 
0431     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0432     if (!res)
0433         return -EINVAL;
0434 
0435     memset(&port, 0, sizeof(port));
0436     port.port.private_data = vuart;
0437     port.port.mapbase = res->start;
0438     port.port.mapsize = resource_size(res);
0439     port.port.startup = aspeed_vuart_startup;
0440     port.port.shutdown = aspeed_vuart_shutdown;
0441     port.port.throttle = aspeed_vuart_throttle;
0442     port.port.unthrottle = aspeed_vuart_unthrottle;
0443     port.port.status = UPSTAT_SYNC_FIFO;
0444     port.port.dev = &pdev->dev;
0445     port.port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE);
0446     port.bugs |= UART_BUG_TXRACE;
0447 
0448     rc = sysfs_create_group(&vuart->dev->kobj, &aspeed_vuart_attr_group);
0449     if (rc < 0)
0450         return rc;
0451 
0452     if (of_property_read_u32(np, "clock-frequency", &clk)) {
0453         vuart->clk = devm_clk_get(&pdev->dev, NULL);
0454         if (IS_ERR(vuart->clk)) {
0455             dev_warn(&pdev->dev,
0456                 "clk or clock-frequency not defined\n");
0457             rc = PTR_ERR(vuart->clk);
0458             goto err_sysfs_remove;
0459         }
0460 
0461         rc = clk_prepare_enable(vuart->clk);
0462         if (rc < 0)
0463             goto err_sysfs_remove;
0464 
0465         clk = clk_get_rate(vuart->clk);
0466     }
0467 
0468     /* If current-speed was set, then try not to change it. */
0469     if (of_property_read_u32(np, "current-speed", &prop) == 0)
0470         port.port.custom_divisor = clk / (16 * prop);
0471 
0472     /* Check for shifted address mapping */
0473     if (of_property_read_u32(np, "reg-offset", &prop) == 0)
0474         port.port.mapbase += prop;
0475 
0476     /* Check for registers offset within the devices address range */
0477     if (of_property_read_u32(np, "reg-shift", &prop) == 0)
0478         port.port.regshift = prop;
0479 
0480     /* Check for fifo size */
0481     if (of_property_read_u32(np, "fifo-size", &prop) == 0)
0482         port.port.fifosize = prop;
0483 
0484     /* Check for a fixed line number */
0485     rc = of_alias_get_id(np, "serial");
0486     if (rc >= 0)
0487         port.port.line = rc;
0488 
0489     port.port.irq = irq_of_parse_and_map(np, 0);
0490     port.port.handle_irq = aspeed_vuart_handle_irq;
0491     port.port.iotype = UPIO_MEM;
0492     port.port.type = PORT_ASPEED_VUART;
0493     port.port.uartclk = clk;
0494     port.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP
0495         | UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_NO_THRE_TEST;
0496 
0497     if (of_property_read_bool(np, "no-loopback-test"))
0498         port.port.flags |= UPF_SKIP_TEST;
0499 
0500     if (port.port.fifosize)
0501         port.capabilities = UART_CAP_FIFO;
0502 
0503     if (of_property_read_bool(np, "auto-flow-control"))
0504         port.capabilities |= UART_CAP_AFE;
0505 
0506     rc = serial8250_register_8250_port(&port);
0507     if (rc < 0)
0508         goto err_clk_disable;
0509 
0510     vuart->line = rc;
0511     vuart->port = serial8250_get_port(vuart->line);
0512 
0513     rc = of_parse_phandle_with_fixed_args(
0514         np, "aspeed,sirq-polarity-sense", 2, 0,
0515         &sirq_polarity_sense_args);
0516     if (rc < 0) {
0517         dev_dbg(&pdev->dev,
0518             "aspeed,sirq-polarity-sense property not found\n");
0519     } else {
0520         aspeed_vuart_auto_configure_sirq_polarity(
0521             vuart, sirq_polarity_sense_args.np,
0522             sirq_polarity_sense_args.args[0],
0523             BIT(sirq_polarity_sense_args.args[1]));
0524         of_node_put(sirq_polarity_sense_args.np);
0525     }
0526 
0527     rc = of_property_read_u32(np, "aspeed,lpc-io-reg", &prop);
0528     if (rc < 0)
0529         prop = ASPEED_VUART_DEFAULT_LPC_ADDR;
0530 
0531     rc = aspeed_vuart_set_lpc_address(vuart, prop);
0532     if (rc < 0) {
0533         dev_err(&pdev->dev, "invalid value in aspeed,lpc-io-reg property\n");
0534         goto err_clk_disable;
0535     }
0536 
0537     rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts", sirq, 2);
0538     if (rc < 0) {
0539         sirq[0] = ASPEED_VUART_DEFAULT_SIRQ;
0540         sirq[1] = ASPEED_VUART_DEFAULT_SIRQ_POLARITY;
0541     }
0542 
0543     rc = aspeed_vuart_set_sirq(vuart, sirq[0]);
0544     if (rc < 0) {
0545         dev_err(&pdev->dev, "invalid sirq number in aspeed,lpc-interrupts property\n");
0546         goto err_clk_disable;
0547     }
0548 
0549     sirq_polarity = aspeed_vuart_map_irq_polarity(sirq[1]);
0550     if (sirq_polarity < 0) {
0551         dev_err(&pdev->dev, "invalid sirq polarity in aspeed,lpc-interrupts property\n");
0552         rc = sirq_polarity;
0553         goto err_clk_disable;
0554     }
0555 
0556     aspeed_vuart_set_sirq_polarity(vuart, sirq_polarity);
0557 
0558     aspeed_vuart_set_enabled(vuart, true);
0559     aspeed_vuart_set_host_tx_discard(vuart, true);
0560     platform_set_drvdata(pdev, vuart);
0561 
0562     return 0;
0563 
0564 err_clk_disable:
0565     clk_disable_unprepare(vuart->clk);
0566     irq_dispose_mapping(port.port.irq);
0567 err_sysfs_remove:
0568     sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group);
0569     return rc;
0570 }
0571 
0572 static int aspeed_vuart_remove(struct platform_device *pdev)
0573 {
0574     struct aspeed_vuart *vuart = platform_get_drvdata(pdev);
0575 
0576     del_timer_sync(&vuart->unthrottle_timer);
0577     aspeed_vuart_set_enabled(vuart, false);
0578     serial8250_unregister_port(vuart->line);
0579     sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group);
0580     clk_disable_unprepare(vuart->clk);
0581 
0582     return 0;
0583 }
0584 
0585 static const struct of_device_id aspeed_vuart_table[] = {
0586     { .compatible = "aspeed,ast2400-vuart" },
0587     { .compatible = "aspeed,ast2500-vuart" },
0588     { },
0589 };
0590 
0591 static struct platform_driver aspeed_vuart_driver = {
0592     .driver = {
0593         .name = "aspeed-vuart",
0594         .of_match_table = aspeed_vuart_table,
0595     },
0596     .probe = aspeed_vuart_probe,
0597     .remove = aspeed_vuart_remove,
0598 };
0599 
0600 module_platform_driver(aspeed_vuart_driver);
0601 
0602 MODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>");
0603 MODULE_LICENSE("GPL");
0604 MODULE_DESCRIPTION("Driver for Aspeed VUART device");