Allow getting memory resource (mapbase or iobase) as well as irq from
platform_device resources.

The UPF_DEV_RESOURCES flag must be set for devices where platform_device
resources are to be used.  When not set, driver behaves as before.

This allows use of the serial8250 driver together with devices with
resources added by platform_device_add_resources(), such as mfd child
devices added with mfd_add_devices().

When UPF_DEV_RESOURCES flag is set, the following platform_data fields should
not be used: mapbase, iobase, mapsize, and irq.  They are superseded by the
resources attached to the device.

Signed-off-by: Esben Haabendal <es...@geanix.com>
---
 drivers/tty/serial/8250/8250_core.c | 56 +++++++++++++++++++++++++++++++++----
 drivers/tty/serial/8250/8250_port.c | 15 ++++++----
 include/linux/serial_core.h         |  1 +
 3 files changed, 62 insertions(+), 10 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_core.c 
b/drivers/tty/serial/8250/8250_core.c
index e441221..9df6a98 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -788,6 +788,48 @@ void serial8250_resume_port(int line)
 }
 EXPORT_SYMBOL(serial8250_resume_port);
 
+static int serial8250_probe_resources(struct platform_device *pdev,
+                                     unsigned int num,
+                                     struct plat_serial8250_port *p,
+                                     struct uart_8250_port *uart)
+{
+       struct resource *r;
+       int irq;
+
+       switch (p->iotype) {
+       case UPIO_AU:
+       case UPIO_TSI:
+       case UPIO_MEM32:
+       case UPIO_MEM32BE:
+       case UPIO_MEM16:
+       case UPIO_MEM:
+               r = platform_get_resource(pdev, IORESOURCE_MEM, num);
+               if (!r)
+                       return -ENODEV;
+               uart->port.mapbase = r->start;
+               uart->port.mapsize = resource_size(r);
+               uart->port.flags |= UPF_IOREMAP;
+               break;
+       case UPIO_HUB6:
+       case UPIO_PORT:
+               r = platform_get_resource(pdev, IORESOURCE_IO, num);
+               if (!r)
+                       return -ENODEV;
+               uart->port.iobase = r->start;
+               uart->port.mapsize = resource_size(r);
+               break;
+       }
+
+       irq = platform_get_irq(pdev, num);
+       if (irq == -ENXIO)
+               uart->port.irq = 0; /* no interrupt -> use polling */
+       else if (irq < 0)
+               return irq;
+       uart->port.irq = irq;
+
+       return 0;
+}
+
 /*
  * Register a set of serial devices attached to a platform device.  The
  * list is terminated with a zero flags entry, which means we expect
@@ -805,15 +847,19 @@ static int serial8250_probe(struct platform_device *dev)
                irqflag = IRQF_SHARED;
 
        for (i = 0; p && p->flags != 0; p++, i++) {
-               uart.port.iobase        = p->iobase;
-               uart.port.membase       = p->membase;
-               uart.port.irq           = p->irq;
+               uart.port.flags         = p->flags;
+               if (p->flags & UPF_DEV_RESOURCES) {
+                       serial8250_probe_resources(dev, i, p, &uart);
+               } else {
+                       uart.port.iobase        = p->iobase;
+                       uart.port.mapbase       = p->mapbase;
+                       uart.port.membase       = p->membase;
+                       uart.port.irq           = p->irq;
+               }
                uart.port.irqflags      = p->irqflags;
                uart.port.uartclk       = p->uartclk;
                uart.port.regshift      = p->regshift;
                uart.port.iotype        = p->iotype;
-               uart.port.flags         = p->flags;
-               uart.port.mapbase       = p->mapbase;
                uart.port.hub6          = p->hub6;
                uart.port.private_data  = p->private_data;
                uart.port.type          = p->type;
diff --git a/drivers/tty/serial/8250/8250_port.c 
b/drivers/tty/serial/8250/8250_port.c
index d2f3310..7fa1e49 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -2863,7 +2863,8 @@ static int serial8250_request_std_resource(struct 
uart_8250_port *up)
                if (!port->mapbase)
                        break;
 
-               if (!request_mem_region(port->mapbase, size, "serial")) {
+               if (!(port->flags & UPF_DEV_RESOURCES) &&
+                   !request_mem_region(port->mapbase, size, "serial")) {
                        ret = -EBUSY;
                        break;
                }
@@ -2871,7 +2872,8 @@ static int serial8250_request_std_resource(struct 
uart_8250_port *up)
                if (port->flags & UPF_IOREMAP) {
                        port->membase = ioremap_nocache(port->mapbase, size);
                        if (!port->membase) {
-                               release_mem_region(port->mapbase, size);
+                               if (!(port->flags & UPF_DEV_RESOURCES))
+                                       release_mem_region(port->mapbase, size);
                                ret = -ENOMEM;
                        }
                }
@@ -2879,7 +2881,8 @@ static int serial8250_request_std_resource(struct 
uart_8250_port *up)
 
        case UPIO_HUB6:
        case UPIO_PORT:
-               if (!request_region(port->iobase, size, "serial"))
+               if (!(port->flags & UPF_DEV_RESOURCES) &&
+                   !request_region(port->iobase, size, "serial"))
                        ret = -EBUSY;
                break;
        }
@@ -2906,12 +2909,14 @@ static void serial8250_release_std_resource(struct 
uart_8250_port *up)
                        port->membase = NULL;
                }
 
-               release_mem_region(port->mapbase, size);
+               if (!(port->flags & UPF_DEV_RESOURCES))
+                       release_mem_region(port->mapbase, size);
                break;
 
        case UPIO_HUB6:
        case UPIO_PORT:
-               release_region(port->iobase, size);
+               if (!(port->flags & UPF_DEV_RESOURCES))
+                       release_region(port->iobase, size);
                break;
        }
 }
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 5fe2b03..87b4ed3 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -207,6 +207,7 @@ struct uart_port {
 #define UPF_BUGGY_UART         ((__force upf_t) ASYNC_BUGGY_UART     /* 14 */ )
 #define UPF_MAGIC_MULTIPLIER   ((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 
*/ )
 
+#define UPF_DEV_RESOURCES      ((__force upf_t) (1 << 18))
 #define UPF_NO_THRE_TEST       ((__force upf_t) (1 << 19))
 /* Port has hardware-assisted h/w flow control */
 #define UPF_AUTO_CTS           ((__force upf_t) (1 << 20))
-- 
2.4.11

Reply via email to