1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2d9eda9baSHeikki Krogerus /* 3d9eda9baSHeikki Krogerus * 8250_mid.c - Driver for UART on Intel Penwell and various other Intel SOCs 4d9eda9baSHeikki Krogerus * 5d9eda9baSHeikki Krogerus * Copyright (C) 2015 Intel Corporation 6d9eda9baSHeikki Krogerus * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 7d9eda9baSHeikki Krogerus */ 8d9eda9baSHeikki Krogerus 9dea5ac3aSAndy Shevchenko #include <linux/bitops.h> 10d9eda9baSHeikki Krogerus #include <linux/module.h> 11d9eda9baSHeikki Krogerus #include <linux/pci.h> 12dea5ac3aSAndy Shevchenko #include <linux/rational.h> 13d9eda9baSHeikki Krogerus 14d9eda9baSHeikki Krogerus #include <linux/dma/hsu.h> 15107e15fcSAndy Shevchenko #include <linux/8250_pci.h> 16d9eda9baSHeikki Krogerus 17d9eda9baSHeikki Krogerus #include "8250.h" 18d9eda9baSHeikki Krogerus 19d9eda9baSHeikki Krogerus #define PCI_DEVICE_ID_INTEL_PNW_UART1 0x081b 20d9eda9baSHeikki Krogerus #define PCI_DEVICE_ID_INTEL_PNW_UART2 0x081c 21d9eda9baSHeikki Krogerus #define PCI_DEVICE_ID_INTEL_PNW_UART3 0x081d 22d9eda9baSHeikki Krogerus #define PCI_DEVICE_ID_INTEL_TNG_UART 0x1191 23daf3930cSAndy Shevchenko #define PCI_DEVICE_ID_INTEL_CDF_UART 0x18d8 246ede6dcdSHeikki Krogerus #define PCI_DEVICE_ID_INTEL_DNV_UART 0x19d8 25d9eda9baSHeikki Krogerus 26d9eda9baSHeikki Krogerus /* Intel MID Specific registers */ 27daf3930cSAndy Shevchenko #define INTEL_MID_UART_FISR 0x08 28d9eda9baSHeikki Krogerus #define INTEL_MID_UART_PS 0x30 29d9eda9baSHeikki Krogerus #define INTEL_MID_UART_MUL 0x34 30d9eda9baSHeikki Krogerus #define INTEL_MID_UART_DIV 0x38 31d9eda9baSHeikki Krogerus 32d9eda9baSHeikki Krogerus struct mid8250; 33d9eda9baSHeikki Krogerus 34d9eda9baSHeikki Krogerus struct mid8250_board { 35107e15fcSAndy Shevchenko unsigned int flags; 36d9eda9baSHeikki Krogerus unsigned long freq; 37d9eda9baSHeikki Krogerus unsigned int base_baud; 38d9eda9baSHeikki Krogerus int (*setup)(struct mid8250 *, struct uart_port *p); 396ede6dcdSHeikki Krogerus void (*exit)(struct mid8250 *); 40d9eda9baSHeikki Krogerus }; 41d9eda9baSHeikki Krogerus 42d9eda9baSHeikki Krogerus struct mid8250 { 43d9eda9baSHeikki Krogerus int line; 44d9eda9baSHeikki Krogerus int dma_index; 45d9eda9baSHeikki Krogerus struct pci_dev *dma_dev; 46d9eda9baSHeikki Krogerus struct uart_8250_dma dma; 47d9eda9baSHeikki Krogerus struct mid8250_board *board; 486ede6dcdSHeikki Krogerus struct hsu_dma_chip dma_chip; 49d9eda9baSHeikki Krogerus }; 50d9eda9baSHeikki Krogerus 51d9eda9baSHeikki Krogerus /*****************************************************************************/ 52d9eda9baSHeikki Krogerus 53d9eda9baSHeikki Krogerus static int pnw_setup(struct mid8250 *mid, struct uart_port *p) 54d9eda9baSHeikki Krogerus { 55d9eda9baSHeikki Krogerus struct pci_dev *pdev = to_pci_dev(p->dev); 56d9eda9baSHeikki Krogerus 57d9eda9baSHeikki Krogerus switch (pdev->device) { 58d9eda9baSHeikki Krogerus case PCI_DEVICE_ID_INTEL_PNW_UART1: 59d9eda9baSHeikki Krogerus mid->dma_index = 0; 60d9eda9baSHeikki Krogerus break; 61d9eda9baSHeikki Krogerus case PCI_DEVICE_ID_INTEL_PNW_UART2: 62d9eda9baSHeikki Krogerus mid->dma_index = 1; 63d9eda9baSHeikki Krogerus break; 64d9eda9baSHeikki Krogerus case PCI_DEVICE_ID_INTEL_PNW_UART3: 65d9eda9baSHeikki Krogerus mid->dma_index = 2; 66d9eda9baSHeikki Krogerus break; 67d9eda9baSHeikki Krogerus default: 68d9eda9baSHeikki Krogerus return -EINVAL; 69d9eda9baSHeikki Krogerus } 70d9eda9baSHeikki Krogerus 71d9eda9baSHeikki Krogerus mid->dma_dev = pci_get_slot(pdev->bus, 72d9eda9baSHeikki Krogerus PCI_DEVFN(PCI_SLOT(pdev->devfn), 3)); 73d9eda9baSHeikki Krogerus return 0; 74d9eda9baSHeikki Krogerus } 75d9eda9baSHeikki Krogerus 7667ec6dd0SAndy Shevchenko static void pnw_exit(struct mid8250 *mid) 7767ec6dd0SAndy Shevchenko { 7867ec6dd0SAndy Shevchenko pci_dev_put(mid->dma_dev); 7967ec6dd0SAndy Shevchenko } 8067ec6dd0SAndy Shevchenko 814831e0d9SAndy Shevchenko static int tng_handle_irq(struct uart_port *p) 824831e0d9SAndy Shevchenko { 834831e0d9SAndy Shevchenko struct mid8250 *mid = p->private_data; 844831e0d9SAndy Shevchenko struct uart_8250_port *up = up_to_u8250p(p); 854831e0d9SAndy Shevchenko struct hsu_dma_chip *chip; 864831e0d9SAndy Shevchenko u32 status; 874831e0d9SAndy Shevchenko int ret = 0; 884831e0d9SAndy Shevchenko int err; 894831e0d9SAndy Shevchenko 904831e0d9SAndy Shevchenko chip = pci_get_drvdata(mid->dma_dev); 914831e0d9SAndy Shevchenko 924831e0d9SAndy Shevchenko /* Rx DMA */ 934831e0d9SAndy Shevchenko err = hsu_dma_get_status(chip, mid->dma_index * 2 + 1, &status); 944831e0d9SAndy Shevchenko if (err > 0) { 954831e0d9SAndy Shevchenko serial8250_rx_dma_flush(up); 964831e0d9SAndy Shevchenko ret |= 1; 974831e0d9SAndy Shevchenko } else if (err == 0) 984831e0d9SAndy Shevchenko ret |= hsu_dma_do_irq(chip, mid->dma_index * 2 + 1, status); 994831e0d9SAndy Shevchenko 1004831e0d9SAndy Shevchenko /* Tx DMA */ 1014831e0d9SAndy Shevchenko err = hsu_dma_get_status(chip, mid->dma_index * 2, &status); 1024831e0d9SAndy Shevchenko if (err > 0) 1034831e0d9SAndy Shevchenko ret |= 1; 1044831e0d9SAndy Shevchenko else if (err == 0) 1054831e0d9SAndy Shevchenko ret |= hsu_dma_do_irq(chip, mid->dma_index * 2, status); 1064831e0d9SAndy Shevchenko 1074831e0d9SAndy Shevchenko /* UART */ 1084831e0d9SAndy Shevchenko ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR)); 1094831e0d9SAndy Shevchenko return IRQ_RETVAL(ret); 1104831e0d9SAndy Shevchenko } 1114831e0d9SAndy Shevchenko 112d9eda9baSHeikki Krogerus static int tng_setup(struct mid8250 *mid, struct uart_port *p) 113d9eda9baSHeikki Krogerus { 114d9eda9baSHeikki Krogerus struct pci_dev *pdev = to_pci_dev(p->dev); 115d9eda9baSHeikki Krogerus int index = PCI_FUNC(pdev->devfn); 116d9eda9baSHeikki Krogerus 117ceeafb8eSAndy Shevchenko /* 118ceeafb8eSAndy Shevchenko * Device 0000:00:04.0 is not a real HSU port. It provides a global 119ceeafb8eSAndy Shevchenko * register set for all HSU ports, although it has the same PCI ID. 120ceeafb8eSAndy Shevchenko * Skip it here. 121ceeafb8eSAndy Shevchenko */ 122d9eda9baSHeikki Krogerus if (index-- == 0) 123d9eda9baSHeikki Krogerus return -ENODEV; 124d9eda9baSHeikki Krogerus 125d9eda9baSHeikki Krogerus mid->dma_index = index; 126d9eda9baSHeikki Krogerus mid->dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(5, 0)); 1274831e0d9SAndy Shevchenko 1284831e0d9SAndy Shevchenko p->handle_irq = tng_handle_irq; 129d9eda9baSHeikki Krogerus return 0; 130d9eda9baSHeikki Krogerus } 131d9eda9baSHeikki Krogerus 13267ec6dd0SAndy Shevchenko static void tng_exit(struct mid8250 *mid) 13367ec6dd0SAndy Shevchenko { 13467ec6dd0SAndy Shevchenko pci_dev_put(mid->dma_dev); 13567ec6dd0SAndy Shevchenko } 13667ec6dd0SAndy Shevchenko 1376ede6dcdSHeikki Krogerus static int dnv_handle_irq(struct uart_port *p) 1386ede6dcdSHeikki Krogerus { 1396ede6dcdSHeikki Krogerus struct mid8250 *mid = p->private_data; 140692aa190SChuah, Kim Tatt struct uart_8250_port *up = up_to_u8250p(p); 141daf3930cSAndy Shevchenko unsigned int fisr = serial_port_in(p, INTEL_MID_UART_FISR); 142c6f82787SChuah, Kim Tatt u32 status; 143d2f5a731SAndy Shevchenko int ret = 0; 144c6f82787SChuah, Kim Tatt int err; 1456ede6dcdSHeikki Krogerus 146c6f82787SChuah, Kim Tatt if (fisr & BIT(2)) { 147c6f82787SChuah, Kim Tatt err = hsu_dma_get_status(&mid->dma_chip, 1, &status); 148692aa190SChuah, Kim Tatt if (err > 0) { 149692aa190SChuah, Kim Tatt serial8250_rx_dma_flush(up); 150d2f5a731SAndy Shevchenko ret |= 1; 151692aa190SChuah, Kim Tatt } else if (err == 0) 152c6f82787SChuah, Kim Tatt ret |= hsu_dma_do_irq(&mid->dma_chip, 1, status); 153c6f82787SChuah, Kim Tatt } 154c6f82787SChuah, Kim Tatt if (fisr & BIT(1)) { 155c6f82787SChuah, Kim Tatt err = hsu_dma_get_status(&mid->dma_chip, 0, &status); 156c6f82787SChuah, Kim Tatt if (err > 0) 157d2f5a731SAndy Shevchenko ret |= 1; 158c6f82787SChuah, Kim Tatt else if (err == 0) 159c6f82787SChuah, Kim Tatt ret |= hsu_dma_do_irq(&mid->dma_chip, 0, status); 160c6f82787SChuah, Kim Tatt } 161c42850f1SAndy Shevchenko if (fisr & BIT(0)) 162c42850f1SAndy Shevchenko ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR)); 163d2f5a731SAndy Shevchenko return IRQ_RETVAL(ret); 1646ede6dcdSHeikki Krogerus } 1656ede6dcdSHeikki Krogerus 1666ede6dcdSHeikki Krogerus #define DNV_DMA_CHAN_OFFSET 0x80 1676ede6dcdSHeikki Krogerus 1686ede6dcdSHeikki Krogerus static int dnv_setup(struct mid8250 *mid, struct uart_port *p) 1696ede6dcdSHeikki Krogerus { 1706ede6dcdSHeikki Krogerus struct hsu_dma_chip *chip = &mid->dma_chip; 1716ede6dcdSHeikki Krogerus struct pci_dev *pdev = to_pci_dev(p->dev); 172107e15fcSAndy Shevchenko unsigned int bar = FL_GET_BASE(mid->board->flags); 1736ede6dcdSHeikki Krogerus int ret; 1746ede6dcdSHeikki Krogerus 175216e234dSAndy Shevchenko pci_set_master(pdev); 176216e234dSAndy Shevchenko 1776d59225fSAndy Shevchenko ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); 1786d59225fSAndy Shevchenko if (ret < 0) 1796d59225fSAndy Shevchenko return ret; 1806d59225fSAndy Shevchenko 1816d59225fSAndy Shevchenko p->irq = pci_irq_vector(pdev, 0); 1826d59225fSAndy Shevchenko 1836ede6dcdSHeikki Krogerus chip->dev = &pdev->dev; 1846d59225fSAndy Shevchenko chip->irq = pci_irq_vector(pdev, 0); 1856ede6dcdSHeikki Krogerus chip->regs = p->membase; 186107e15fcSAndy Shevchenko chip->length = pci_resource_len(pdev, bar); 1876ede6dcdSHeikki Krogerus chip->offset = DNV_DMA_CHAN_OFFSET; 1886ede6dcdSHeikki Krogerus 1896ede6dcdSHeikki Krogerus /* Falling back to PIO mode if DMA probing fails */ 1906ede6dcdSHeikki Krogerus ret = hsu_dma_probe(chip); 1916ede6dcdSHeikki Krogerus if (ret) 1926ede6dcdSHeikki Krogerus return 0; 1936ede6dcdSHeikki Krogerus 1946ede6dcdSHeikki Krogerus mid->dma_dev = pdev; 1956ede6dcdSHeikki Krogerus 1966ede6dcdSHeikki Krogerus p->handle_irq = dnv_handle_irq; 1976ede6dcdSHeikki Krogerus return 0; 1986ede6dcdSHeikki Krogerus } 1996ede6dcdSHeikki Krogerus 2006ede6dcdSHeikki Krogerus static void dnv_exit(struct mid8250 *mid) 2016ede6dcdSHeikki Krogerus { 2026ede6dcdSHeikki Krogerus if (!mid->dma_dev) 2036ede6dcdSHeikki Krogerus return; 2046ede6dcdSHeikki Krogerus hsu_dma_remove(&mid->dma_chip); 2056ede6dcdSHeikki Krogerus } 2066ede6dcdSHeikki Krogerus 207d9eda9baSHeikki Krogerus /*****************************************************************************/ 208d9eda9baSHeikki Krogerus 209d9eda9baSHeikki Krogerus static void mid8250_set_termios(struct uart_port *p, 210d9eda9baSHeikki Krogerus struct ktermios *termios, 211d9eda9baSHeikki Krogerus struct ktermios *old) 212d9eda9baSHeikki Krogerus { 213d9eda9baSHeikki Krogerus unsigned int baud = tty_termios_baud_rate(termios); 214d9eda9baSHeikki Krogerus struct mid8250 *mid = p->private_data; 215d9eda9baSHeikki Krogerus unsigned short ps = 16; 216d9eda9baSHeikki Krogerus unsigned long fuart = baud * ps; 217d9eda9baSHeikki Krogerus unsigned long w = BIT(24) - 1; 218d9eda9baSHeikki Krogerus unsigned long mul, div; 219d9eda9baSHeikki Krogerus 22047b34d2eSAndy Shevchenko /* Gracefully handle the B0 case: fall back to B9600 */ 22147b34d2eSAndy Shevchenko fuart = fuart ? fuart : 9600 * 16; 22247b34d2eSAndy Shevchenko 223d9eda9baSHeikki Krogerus if (mid->board->freq < fuart) { 224d9eda9baSHeikki Krogerus /* Find prescaler value that satisfies Fuart < Fref */ 225d9eda9baSHeikki Krogerus if (mid->board->freq > baud) 226d9eda9baSHeikki Krogerus ps = mid->board->freq / baud; /* baud rate too high */ 227d9eda9baSHeikki Krogerus else 228d9eda9baSHeikki Krogerus ps = 1; /* PLL case */ 229d9eda9baSHeikki Krogerus fuart = baud * ps; 230d9eda9baSHeikki Krogerus } else { 231d9eda9baSHeikki Krogerus /* Get Fuart closer to Fref */ 232d9eda9baSHeikki Krogerus fuart *= rounddown_pow_of_two(mid->board->freq / fuart); 233d9eda9baSHeikki Krogerus } 234d9eda9baSHeikki Krogerus 235d9eda9baSHeikki Krogerus rational_best_approximation(fuart, mid->board->freq, w, w, &mul, &div); 236d9eda9baSHeikki Krogerus p->uartclk = fuart * 16 / ps; /* core uses ps = 16 always */ 237d9eda9baSHeikki Krogerus 238d9eda9baSHeikki Krogerus writel(ps, p->membase + INTEL_MID_UART_PS); /* set PS */ 239d9eda9baSHeikki Krogerus writel(mul, p->membase + INTEL_MID_UART_MUL); /* set MUL */ 240d9eda9baSHeikki Krogerus writel(div, p->membase + INTEL_MID_UART_DIV); 241d9eda9baSHeikki Krogerus 242d9eda9baSHeikki Krogerus serial8250_do_set_termios(p, termios, old); 243d9eda9baSHeikki Krogerus } 244d9eda9baSHeikki Krogerus 245d9eda9baSHeikki Krogerus static bool mid8250_dma_filter(struct dma_chan *chan, void *param) 246d9eda9baSHeikki Krogerus { 247d9eda9baSHeikki Krogerus struct hsu_dma_slave *s = param; 248d9eda9baSHeikki Krogerus 249d9eda9baSHeikki Krogerus if (s->dma_dev != chan->device->dev || s->chan_id != chan->chan_id) 250d9eda9baSHeikki Krogerus return false; 251d9eda9baSHeikki Krogerus 252d9eda9baSHeikki Krogerus chan->private = s; 253d9eda9baSHeikki Krogerus return true; 254d9eda9baSHeikki Krogerus } 255d9eda9baSHeikki Krogerus 256d9eda9baSHeikki Krogerus static int mid8250_dma_setup(struct mid8250 *mid, struct uart_8250_port *port) 257d9eda9baSHeikki Krogerus { 258d9eda9baSHeikki Krogerus struct uart_8250_dma *dma = &mid->dma; 259d9eda9baSHeikki Krogerus struct device *dev = port->port.dev; 260d9eda9baSHeikki Krogerus struct hsu_dma_slave *rx_param; 261d9eda9baSHeikki Krogerus struct hsu_dma_slave *tx_param; 262d9eda9baSHeikki Krogerus 2636ede6dcdSHeikki Krogerus if (!mid->dma_dev) 2646ede6dcdSHeikki Krogerus return 0; 2656ede6dcdSHeikki Krogerus 266d9eda9baSHeikki Krogerus rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL); 267d9eda9baSHeikki Krogerus if (!rx_param) 268d9eda9baSHeikki Krogerus return -ENOMEM; 269d9eda9baSHeikki Krogerus 270d9eda9baSHeikki Krogerus tx_param = devm_kzalloc(dev, sizeof(*tx_param), GFP_KERNEL); 271d9eda9baSHeikki Krogerus if (!tx_param) 272d9eda9baSHeikki Krogerus return -ENOMEM; 273d9eda9baSHeikki Krogerus 274d9eda9baSHeikki Krogerus rx_param->chan_id = mid->dma_index * 2 + 1; 275d9eda9baSHeikki Krogerus tx_param->chan_id = mid->dma_index * 2; 276d9eda9baSHeikki Krogerus 277d9eda9baSHeikki Krogerus dma->rxconf.src_maxburst = 64; 278d9eda9baSHeikki Krogerus dma->txconf.dst_maxburst = 64; 279d9eda9baSHeikki Krogerus 280d9eda9baSHeikki Krogerus rx_param->dma_dev = &mid->dma_dev->dev; 281d9eda9baSHeikki Krogerus tx_param->dma_dev = &mid->dma_dev->dev; 282d9eda9baSHeikki Krogerus 283d9eda9baSHeikki Krogerus dma->fn = mid8250_dma_filter; 284d9eda9baSHeikki Krogerus dma->rx_param = rx_param; 285d9eda9baSHeikki Krogerus dma->tx_param = tx_param; 286d9eda9baSHeikki Krogerus 287d9eda9baSHeikki Krogerus port->dma = dma; 288d9eda9baSHeikki Krogerus return 0; 289d9eda9baSHeikki Krogerus } 290d9eda9baSHeikki Krogerus 291d9eda9baSHeikki Krogerus static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) 292d9eda9baSHeikki Krogerus { 293d9eda9baSHeikki Krogerus struct uart_8250_port uart; 294d9eda9baSHeikki Krogerus struct mid8250 *mid; 295107e15fcSAndy Shevchenko unsigned int bar; 296d9eda9baSHeikki Krogerus int ret; 297d9eda9baSHeikki Krogerus 298d9eda9baSHeikki Krogerus ret = pcim_enable_device(pdev); 299d9eda9baSHeikki Krogerus if (ret) 300d9eda9baSHeikki Krogerus return ret; 301d9eda9baSHeikki Krogerus 302d9eda9baSHeikki Krogerus mid = devm_kzalloc(&pdev->dev, sizeof(*mid), GFP_KERNEL); 303d9eda9baSHeikki Krogerus if (!mid) 304d9eda9baSHeikki Krogerus return -ENOMEM; 305d9eda9baSHeikki Krogerus 306d9eda9baSHeikki Krogerus mid->board = (struct mid8250_board *)id->driver_data; 307107e15fcSAndy Shevchenko bar = FL_GET_BASE(mid->board->flags); 308d9eda9baSHeikki Krogerus 309d9eda9baSHeikki Krogerus memset(&uart, 0, sizeof(struct uart_8250_port)); 310d9eda9baSHeikki Krogerus 311d9eda9baSHeikki Krogerus uart.port.dev = &pdev->dev; 312d9eda9baSHeikki Krogerus uart.port.irq = pdev->irq; 313d9eda9baSHeikki Krogerus uart.port.private_data = mid; 314d9eda9baSHeikki Krogerus uart.port.type = PORT_16750; 315d9eda9baSHeikki Krogerus uart.port.iotype = UPIO_MEM; 316d9eda9baSHeikki Krogerus uart.port.uartclk = mid->board->base_baud * 16; 317d9eda9baSHeikki Krogerus uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE; 318d9eda9baSHeikki Krogerus uart.port.set_termios = mid8250_set_termios; 319d9eda9baSHeikki Krogerus 320107e15fcSAndy Shevchenko uart.port.mapbase = pci_resource_start(pdev, bar); 321107e15fcSAndy Shevchenko uart.port.membase = pcim_iomap(pdev, bar, 0); 322d9eda9baSHeikki Krogerus if (!uart.port.membase) 323d9eda9baSHeikki Krogerus return -ENOMEM; 324d9eda9baSHeikki Krogerus 325d9eda9baSHeikki Krogerus ret = mid->board->setup(mid, &uart.port); 326d9eda9baSHeikki Krogerus if (ret) 327d9eda9baSHeikki Krogerus return ret; 328d9eda9baSHeikki Krogerus 329d9eda9baSHeikki Krogerus ret = mid8250_dma_setup(mid, &uart); 330d9eda9baSHeikki Krogerus if (ret) 3316ede6dcdSHeikki Krogerus goto err; 332d9eda9baSHeikki Krogerus 333d9eda9baSHeikki Krogerus ret = serial8250_register_8250_port(&uart); 334d9eda9baSHeikki Krogerus if (ret < 0) 3356ede6dcdSHeikki Krogerus goto err; 336d9eda9baSHeikki Krogerus 337d9eda9baSHeikki Krogerus mid->line = ret; 338d9eda9baSHeikki Krogerus 339d9eda9baSHeikki Krogerus pci_set_drvdata(pdev, mid); 340d9eda9baSHeikki Krogerus return 0; 34167ec6dd0SAndy Shevchenko 3426ede6dcdSHeikki Krogerus err: 3436ede6dcdSHeikki Krogerus mid->board->exit(mid); 3446ede6dcdSHeikki Krogerus return ret; 345d9eda9baSHeikki Krogerus } 346d9eda9baSHeikki Krogerus 347d9eda9baSHeikki Krogerus static void mid8250_remove(struct pci_dev *pdev) 348d9eda9baSHeikki Krogerus { 349d9eda9baSHeikki Krogerus struct mid8250 *mid = pci_get_drvdata(pdev); 350d9eda9baSHeikki Krogerus 351a9b01b58SLiwei Song serial8250_unregister_port(mid->line); 352a9b01b58SLiwei Song 3536ede6dcdSHeikki Krogerus mid->board->exit(mid); 354d9eda9baSHeikki Krogerus } 355d9eda9baSHeikki Krogerus 356d9eda9baSHeikki Krogerus static const struct mid8250_board pnw_board = { 357107e15fcSAndy Shevchenko .flags = FL_BASE0, 358d9eda9baSHeikki Krogerus .freq = 50000000, 359d9eda9baSHeikki Krogerus .base_baud = 115200, 360d9eda9baSHeikki Krogerus .setup = pnw_setup, 36167ec6dd0SAndy Shevchenko .exit = pnw_exit, 362d9eda9baSHeikki Krogerus }; 363d9eda9baSHeikki Krogerus 364d9eda9baSHeikki Krogerus static const struct mid8250_board tng_board = { 365107e15fcSAndy Shevchenko .flags = FL_BASE0, 366d9eda9baSHeikki Krogerus .freq = 38400000, 367d9eda9baSHeikki Krogerus .base_baud = 1843200, 368d9eda9baSHeikki Krogerus .setup = tng_setup, 36967ec6dd0SAndy Shevchenko .exit = tng_exit, 370d9eda9baSHeikki Krogerus }; 371d9eda9baSHeikki Krogerus 3726ede6dcdSHeikki Krogerus static const struct mid8250_board dnv_board = { 373107e15fcSAndy Shevchenko .flags = FL_BASE1, 3746ede6dcdSHeikki Krogerus .freq = 133333333, 3756ede6dcdSHeikki Krogerus .base_baud = 115200, 3766ede6dcdSHeikki Krogerus .setup = dnv_setup, 3776ede6dcdSHeikki Krogerus .exit = dnv_exit, 3786ede6dcdSHeikki Krogerus }; 3796ede6dcdSHeikki Krogerus 380d9eda9baSHeikki Krogerus static const struct pci_device_id pci_ids[] = { 3812394f359SAndy Shevchenko { PCI_DEVICE_DATA(INTEL, PNW_UART1, &pnw_board) }, 3822394f359SAndy Shevchenko { PCI_DEVICE_DATA(INTEL, PNW_UART2, &pnw_board) }, 3832394f359SAndy Shevchenko { PCI_DEVICE_DATA(INTEL, PNW_UART3, &pnw_board) }, 3842394f359SAndy Shevchenko { PCI_DEVICE_DATA(INTEL, TNG_UART, &tng_board) }, 3852394f359SAndy Shevchenko { PCI_DEVICE_DATA(INTEL, CDF_UART, &dnv_board) }, 3862394f359SAndy Shevchenko { PCI_DEVICE_DATA(INTEL, DNV_UART, &dnv_board) }, 3872394f359SAndy Shevchenko { } 388d9eda9baSHeikki Krogerus }; 389d9eda9baSHeikki Krogerus MODULE_DEVICE_TABLE(pci, pci_ids); 390d9eda9baSHeikki Krogerus 391d9eda9baSHeikki Krogerus static struct pci_driver mid8250_pci_driver = { 392d9eda9baSHeikki Krogerus .name = "8250_mid", 393d9eda9baSHeikki Krogerus .id_table = pci_ids, 394d9eda9baSHeikki Krogerus .probe = mid8250_probe, 395d9eda9baSHeikki Krogerus .remove = mid8250_remove, 396d9eda9baSHeikki Krogerus }; 397d9eda9baSHeikki Krogerus 398d9eda9baSHeikki Krogerus module_pci_driver(mid8250_pci_driver); 399d9eda9baSHeikki Krogerus 400d9eda9baSHeikki Krogerus MODULE_AUTHOR("Intel Corporation"); 401d9eda9baSHeikki Krogerus MODULE_LICENSE("GPL v2"); 402d9eda9baSHeikki Krogerus MODULE_DESCRIPTION("Intel MID UART driver"); 403