]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
tty: serial: ip22zilog: Use platform device for probing
authorThomas Bogendoerfer <tsbogend@alpha.franken.de>
Fri, 25 Jul 2025 13:40:17 +0000 (15:40 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 14 Aug 2025 09:58:40 +0000 (11:58 +0200)
After commit 84a9582fd203 ("serial: core: Start managing serial controllers
to enable runtime PM") serial drivers need to provide a device in
struct uart_port.dev otherwise an oops happens. To fix this issue
for ip22zilog driver switch driver to a platform driver and setup
the serial device in sgi-ip22 code.

Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Link: https://lore.kernel.org/r/20250725134018.136113-1-tsbogend@alpha.franken.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/mips/sgi-ip22/ip22-platform.c
drivers/tty/serial/ip22zilog.c

index 0b2002e02a47735b92727de6a785c0bef704f1b5..3a53690b4b333ac3bb6379afecc8be14e42d8844 100644 (file)
@@ -221,3 +221,35 @@ static int __init sgi_ds1286_devinit(void)
 }
 
 device_initcall(sgi_ds1286_devinit);
+
+#define SGI_ZILOG_BASE (HPC3_CHIP0_BASE + \
+                        offsetof(struct hpc3_regs, pbus_extregs[6]) + \
+                        offsetof(struct sgioc_regs, uart))
+
+static struct resource sgi_zilog_resources[] = {
+       {
+               .start  = SGI_ZILOG_BASE,
+               .end    = SGI_ZILOG_BASE + 15,
+               .flags  = IORESOURCE_MEM
+       },
+       {
+               .start  = SGI_SERIAL_IRQ,
+               .end    = SGI_SERIAL_IRQ,
+               .flags  = IORESOURCE_IRQ
+       }
+};
+
+static struct platform_device zilog_device = {
+       .name           = "ip22zilog",
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(sgi_zilog_resources),
+       .resource       = sgi_zilog_resources,
+};
+
+
+static int __init sgi_zilog_devinit(void)
+{
+       return platform_device_register(&zilog_device);
+}
+
+device_initcall(sgi_zilog_devinit);
index c2cae50f06f335fbe314f648307e59460f5c1c16..6e19c6713849aef05e5a373f5e5c196ea5fc2417 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/console.h>
 #include <linux/spinlock.h>
 #include <linux/init.h>
+#include <linux/platform_device.h>
 
 #include <linux/io.h>
 #include <asm/irq.h>
@@ -50,8 +51,9 @@
 #define ZSDELAY_LONG()         udelay(20)
 #define ZS_WSYNC(channel)      do { } while (0)
 
-#define NUM_IP22ZILOG          1
-#define NUM_CHANNELS           (NUM_IP22ZILOG * 2)
+#define NUM_CHANNELS           2
+#define CHANNEL_B              0
+#define CHANNEL_A              1
 
 #define ZS_CLOCK               3672000 /* Zilog input clock rate. */
 #define ZS_CLOCK_DIVISOR       16      /* Divisor this driver uses. */
@@ -62,9 +64,6 @@
 struct uart_ip22zilog_port {
        struct uart_port                port;
 
-       /* IRQ servicing chain.  */
-       struct uart_ip22zilog_port      *next;
-
        /* Current values of Zilog write registers.  */
        unsigned char                   curregs[NUM_ZSREGS];
 
@@ -72,7 +71,6 @@ struct uart_ip22zilog_port {
 #define IP22ZILOG_FLAG_IS_CONS         0x00000004
 #define IP22ZILOG_FLAG_IS_KGDB         0x00000008
 #define IP22ZILOG_FLAG_MODEM_STATUS    0x00000010
-#define IP22ZILOG_FLAG_IS_CHANNEL_A    0x00000020
 #define IP22ZILOG_FLAG_REGS_HELD       0x00000040
 #define IP22ZILOG_FLAG_TX_STOPPED      0x00000080
 #define IP22ZILOG_FLAG_TX_ACTIVE       0x00000100
@@ -84,6 +82,8 @@ struct uart_ip22zilog_port {
        unsigned char                   prev_status;
 };
 
+static struct uart_ip22zilog_port ip22zilog_port_table[NUM_CHANNELS];
+
 #define ZILOG_CHANNEL_FROM_PORT(PORT)  ((struct zilog_channel *)((PORT)->membase))
 #define UART_ZILOG(PORT)               ((struct uart_ip22zilog_port *)(PORT))
 #define IP22ZILOG_GET_CURR_REG(PORT, REGNUM)           \
@@ -93,7 +93,6 @@ struct uart_ip22zilog_port {
 #define ZS_IS_CONS(UP) ((UP)->flags & IP22ZILOG_FLAG_IS_CONS)
 #define ZS_IS_KGDB(UP) ((UP)->flags & IP22ZILOG_FLAG_IS_KGDB)
 #define ZS_WANTS_MODEM_STATUS(UP)      ((UP)->flags & IP22ZILOG_FLAG_MODEM_STATUS)
-#define ZS_IS_CHANNEL_A(UP)    ((UP)->flags & IP22ZILOG_FLAG_IS_CHANNEL_A)
 #define ZS_REGS_HELD(UP)       ((UP)->flags & IP22ZILOG_FLAG_REGS_HELD)
 #define ZS_TX_STOPPED(UP)      ((UP)->flags & IP22ZILOG_FLAG_TX_STOPPED)
 #define ZS_TX_ACTIVE(UP)       ((UP)->flags & IP22ZILOG_FLAG_TX_ACTIVE)
@@ -423,60 +422,57 @@ ack_tx_int:
 
 static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id)
 {
-       struct uart_ip22zilog_port *up = dev_id;
-
-       while (up) {
-               struct zilog_channel *channel
-                       = ZILOG_CHANNEL_FROM_PORT(&up->port);
-               unsigned char r3;
-               bool push = false;
-
-               uart_port_lock(&up->port);
-               r3 = read_zsreg(channel, R3);
+       struct uart_ip22zilog_port *up;
+       struct zilog_channel *channel;
+       unsigned char r3;
+       bool push = false;
 
-               /* Channel A */
-               if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
-                       writeb(RES_H_IUS, &channel->control);
-                       ZSDELAY();
-                       ZS_WSYNC(channel);
+       up = &ip22zilog_port_table[CHANNEL_A];
+       channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
 
-                       if (r3 & CHARxIP)
-                               push = ip22zilog_receive_chars(up, channel);
-                       if (r3 & CHAEXT)
-                               ip22zilog_status_handle(up, channel);
-                       if (r3 & CHATxIP)
-                               ip22zilog_transmit_chars(up, channel);
-               }
-               uart_port_unlock(&up->port);
+       uart_port_lock(&up->port);
+       r3 = read_zsreg(channel, R3);
 
-               if (push)
-                       tty_flip_buffer_push(&up->port.state->port);
+       /* Channel A */
+       if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
+               writeb(RES_H_IUS, &channel->control);
+               ZSDELAY();
+               ZS_WSYNC(channel);
 
-               /* Channel B */
-               up = up->next;
-               channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
-               push = false;
+               if (r3 & CHARxIP)
+                       push = ip22zilog_receive_chars(up, channel);
+               if (r3 & CHAEXT)
+                       ip22zilog_status_handle(up, channel);
+               if (r3 & CHATxIP)
+                       ip22zilog_transmit_chars(up, channel);
+       }
+       uart_port_unlock(&up->port);
 
-               uart_port_lock(&up->port);
-               if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
-                       writeb(RES_H_IUS, &channel->control);
-                       ZSDELAY();
-                       ZS_WSYNC(channel);
+       if (push)
+               tty_flip_buffer_push(&up->port.state->port);
 
-                       if (r3 & CHBRxIP)
-                               push = ip22zilog_receive_chars(up, channel);
-                       if (r3 & CHBEXT)
-                               ip22zilog_status_handle(up, channel);
-                       if (r3 & CHBTxIP)
-                               ip22zilog_transmit_chars(up, channel);
-               }
-               uart_port_unlock(&up->port);
+       /* Channel B */
+       up = &ip22zilog_port_table[CHANNEL_B];
+       channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
+       push = false;
 
-               if (push)
-                       tty_flip_buffer_push(&up->port.state->port);
+       uart_port_lock(&up->port);
+       if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
+               writeb(RES_H_IUS, &channel->control);
+               ZSDELAY();
+               ZS_WSYNC(channel);
 
-               up = up->next;
+               if (r3 & CHBRxIP)
+                       push = ip22zilog_receive_chars(up, channel);
+               if (r3 & CHBEXT)
+                       ip22zilog_status_handle(up, channel);
+               if (r3 & CHBTxIP)
+                       ip22zilog_transmit_chars(up, channel);
        }
+       uart_port_unlock(&up->port);
+
+       if (push)
+               tty_flip_buffer_push(&up->port.state->port);
 
        return IRQ_HANDLED;
 }
@@ -692,16 +688,16 @@ static void __ip22zilog_reset(struct uart_ip22zilog_port *up)
                udelay(100);
        }
 
-       if (!ZS_IS_CHANNEL_A(up)) {
-               up++;
-               channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
-       }
+       up = &ip22zilog_port_table[CHANNEL_A];
+       channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
+
        write_zsreg(channel, R9, FHWRES);
        ZSDELAY_LONG();
        (void) read_zsreg(channel, R0);
 
        up->flags |= IP22ZILOG_FLAG_RESET_DONE;
-       up->next->flags |= IP22ZILOG_FLAG_RESET_DONE;
+       up = &ip22zilog_port_table[CHANNEL_B];
+       up->flags |= IP22ZILOG_FLAG_RESET_DONE;
 }
 
 static void __ip22zilog_startup(struct uart_ip22zilog_port *up)
@@ -942,47 +938,6 @@ static const struct uart_ops ip22zilog_pops = {
        .verify_port    =       ip22zilog_verify_port,
 };
 
-static struct uart_ip22zilog_port *ip22zilog_port_table;
-static struct zilog_layout **ip22zilog_chip_regs;
-
-static struct uart_ip22zilog_port *ip22zilog_irq_chain;
-static int zilog_irq = -1;
-
-static void * __init alloc_one_table(unsigned long size)
-{
-       return kzalloc(size, GFP_KERNEL);
-}
-
-static void __init ip22zilog_alloc_tables(void)
-{
-       ip22zilog_port_table = (struct uart_ip22zilog_port *)
-               alloc_one_table(NUM_CHANNELS * sizeof(struct uart_ip22zilog_port));
-       ip22zilog_chip_regs = (struct zilog_layout **)
-               alloc_one_table(NUM_IP22ZILOG * sizeof(struct zilog_layout *));
-
-       if (ip22zilog_port_table == NULL || ip22zilog_chip_regs == NULL) {
-               panic("IP22-Zilog: Cannot allocate IP22-Zilog tables.");
-       }
-}
-
-/* Get the address of the registers for IP22-Zilog instance CHIP.  */
-static struct zilog_layout * __init get_zs(int chip)
-{
-       unsigned long base;
-
-       if (chip < 0 || chip >= NUM_IP22ZILOG) {
-               panic("IP22-Zilog: Illegal chip number %d in get_zs.", chip);
-       }
-
-       /* Not probe-able, hard code it. */
-       base = (unsigned long) &sgioc->uart;
-
-       zilog_irq = SGI_SERIAL_IRQ;
-       request_mem_region(base, 8, "IP22-Zilog");
-
-       return (struct zilog_layout *) base;
-}
-
 #define ZS_PUT_CHAR_MAX_DELAY  2000    /* 10 ms */
 
 #ifdef CONFIG_SERIAL_IP22_ZILOG_CONSOLE
@@ -1070,144 +1025,123 @@ static struct uart_driver ip22zilog_reg = {
 #endif
 };
 
-static void __init ip22zilog_prepare(void)
+static void __init ip22zilog_prepare(struct uart_ip22zilog_port *up)
 {
        unsigned char sysrq_on = IS_ENABLED(CONFIG_SERIAL_IP22_ZILOG_CONSOLE);
+       int brg;
+
+       spin_lock_init(&up->port.lock);
+
+       up->port.iotype = UPIO_MEM;
+       up->port.uartclk = ZS_CLOCK;
+       up->port.fifosize = 1;
+       up->port.has_sysrq = sysrq_on;
+       up->port.ops = &ip22zilog_pops;
+       up->port.type = PORT_IP22ZILOG;
+
+       /* Normal serial TTY. */
+       up->parity_mask = 0xff;
+       up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB;
+       up->curregs[R4] = PAR_EVEN | X16CLK | SB1;
+       up->curregs[R3] = RxENAB | Rx8;
+       up->curregs[R5] = TxENAB | Tx8;
+       up->curregs[R9] = NV | MIE;
+       up->curregs[R10] = NRZ;
+       up->curregs[R11] = TCBR | RCBR;
+       brg = BPS_TO_BRG(9600, ZS_CLOCK / ZS_CLOCK_DIVISOR);
+       up->curregs[R12] = (brg & 0xff);
+       up->curregs[R13] = (brg >> 8) & 0xff;
+       up->curregs[R14] = BRENAB;
+}
+
+static int ip22zilog_probe(struct platform_device *pdev)
+{
        struct uart_ip22zilog_port *up;
-       struct zilog_layout *rp;
-       int channel, chip;
+       char __iomem *membase;
+       struct resource *res;
+       int irq;
+       int i;
 
-       /*
-        * Temporary fix.
-        */
-       for (channel = 0; channel < NUM_CHANNELS; channel++)
-               spin_lock_init(&ip22zilog_port_table[channel].port.lock);
-
-       ip22zilog_irq_chain = &ip22zilog_port_table[NUM_CHANNELS - 1];
-        up = &ip22zilog_port_table[0];
-       for (channel = NUM_CHANNELS - 1 ; channel > 0; channel--)
-               up[channel].next = &up[channel - 1];
-       up[channel].next = NULL;
-
-       for (chip = 0; chip < NUM_IP22ZILOG; chip++) {
-               if (!ip22zilog_chip_regs[chip]) {
-                       ip22zilog_chip_regs[chip] = rp = get_zs(chip);
-
-                       up[(chip * 2) + 0].port.membase = (char *) &rp->channelB;
-                       up[(chip * 2) + 1].port.membase = (char *) &rp->channelA;
-
-                       /* In theory mapbase is the physical address ...  */
-                       up[(chip * 2) + 0].port.mapbase =
-                               (unsigned long) ioremap((unsigned long) &rp->channelB, 8);
-                       up[(chip * 2) + 1].port.mapbase =
-                               (unsigned long) ioremap((unsigned long) &rp->channelA, 8);
-               }
+       up = &ip22zilog_port_table[CHANNEL_B];
+       if (up->port.dev)
+               return -ENOSPC;
 
-               /* Channel A */
-               up[(chip * 2) + 0].port.iotype = UPIO_MEM;
-               up[(chip * 2) + 0].port.irq = zilog_irq;
-               up[(chip * 2) + 0].port.uartclk = ZS_CLOCK;
-               up[(chip * 2) + 0].port.fifosize = 1;
-               up[(chip * 2) + 0].port.has_sysrq = sysrq_on;
-               up[(chip * 2) + 0].port.ops = &ip22zilog_pops;
-               up[(chip * 2) + 0].port.type = PORT_IP22ZILOG;
-               up[(chip * 2) + 0].port.flags = 0;
-               up[(chip * 2) + 0].port.line = (chip * 2) + 0;
-               up[(chip * 2) + 0].flags = 0;
-
-               /* Channel B */
-               up[(chip * 2) + 1].port.iotype = UPIO_MEM;
-               up[(chip * 2) + 1].port.irq = zilog_irq;
-               up[(chip * 2) + 1].port.uartclk = ZS_CLOCK;
-               up[(chip * 2) + 1].port.fifosize = 1;
-               up[(chip * 2) + 1].port.has_sysrq = sysrq_on;
-               up[(chip * 2) + 1].port.ops = &ip22zilog_pops;
-               up[(chip * 2) + 1].port.type = PORT_IP22ZILOG;
-               up[(chip * 2) + 1].port.line = (chip * 2) + 1;
-               up[(chip * 2) + 1].flags |= IP22ZILOG_FLAG_IS_CHANNEL_A;
-       }
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
 
-       for (channel = 0; channel < NUM_CHANNELS; channel++) {
-               struct uart_ip22zilog_port *up = &ip22zilog_port_table[channel];
-               int brg;
+       membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+       if (IS_ERR(membase))
+               return PTR_ERR(membase);
 
-               /* Normal serial TTY. */
-               up->parity_mask = 0xff;
-               up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB;
-               up->curregs[R4] = PAR_EVEN | X16CLK | SB1;
-               up->curregs[R3] = RxENAB | Rx8;
-               up->curregs[R5] = TxENAB | Tx8;
-               up->curregs[R9] = NV | MIE;
-               up->curregs[R10] = NRZ;
-               up->curregs[R11] = TCBR | RCBR;
-               brg = BPS_TO_BRG(9600, ZS_CLOCK / ZS_CLOCK_DIVISOR);
-               up->curregs[R12] = (brg & 0xff);
-               up->curregs[R13] = (brg >> 8) & 0xff;
-               up->curregs[R14] = BRENAB;
-       }
-}
+       ip22zilog_prepare(up);
 
-static int __init ip22zilog_ports_init(void)
-{
-       int ret;
+       up->port.mapbase = res->start + offsetof(struct zilog_layout, channelB);
+       up->port.membase = membase + offsetof(struct zilog_layout, channelB);
+       up->port.line = 0;
+       up->port.dev = &pdev->dev;
+       up->port.irq = irq;
 
-       printk(KERN_INFO "Serial: IP22 Zilog driver (%d chips).\n", NUM_IP22ZILOG);
+       up = &ip22zilog_port_table[CHANNEL_A];
+       ip22zilog_prepare(up);
 
-       ip22zilog_prepare();
+       up->port.mapbase = res->start + offsetof(struct zilog_layout, channelA);
+       up->port.membase = membase + offsetof(struct zilog_layout, channelA);
+       up->port.line = 1;
+       up->port.dev = &pdev->dev;
+       up->port.irq = irq;
 
-       if (request_irq(zilog_irq, ip22zilog_interrupt, 0,
-                       "IP22-Zilog", ip22zilog_irq_chain)) {
+       if (request_irq(irq, ip22zilog_interrupt, 0,
+                       "IP22-Zilog", NULL)) {
                panic("IP22-Zilog: Unable to register zs interrupt handler.\n");
        }
 
-       ret = uart_register_driver(&ip22zilog_reg);
-       if (ret == 0) {
-               int i;
-
-               for (i = 0; i < NUM_CHANNELS; i++) {
-                       struct uart_ip22zilog_port *up = &ip22zilog_port_table[i];
-
-                       uart_add_one_port(&ip22zilog_reg, &up->port);
-               }
-       }
-
-       return ret;
-}
-
-static int __init ip22zilog_init(void)
-{
-       /* IP22 Zilog setup is hard coded, no probing to do.  */
-       ip22zilog_alloc_tables();
-       ip22zilog_ports_init();
+       for (i = 0; i < NUM_CHANNELS; i++)
+               uart_add_one_port(&ip22zilog_reg,
+                                 &ip22zilog_port_table[i].port);
 
        return 0;
 }
 
-static void __exit ip22zilog_exit(void)
+static void ip22zilog_remove(struct platform_device *pdev)
 {
        int i;
-       struct uart_ip22zilog_port *up;
 
        for (i = 0; i < NUM_CHANNELS; i++) {
-               up = &ip22zilog_port_table[i];
-
-               uart_remove_one_port(&ip22zilog_reg, &up->port);
+               uart_remove_one_port(&ip22zilog_reg,
+                                    &ip22zilog_port_table[i].port);
+               ip22zilog_port_table[i].port.dev = NULL;
        }
+}
 
-       /* Free IO mem */
-       up = &ip22zilog_port_table[0];
-       for (i = 0; i < NUM_IP22ZILOG; i++) {
-               if (up[(i * 2) + 0].port.mapbase) {
-                  iounmap((void*)up[(i * 2) + 0].port.mapbase);
-                  up[(i * 2) + 0].port.mapbase = 0;
-               }
-               if (up[(i * 2) + 1].port.mapbase) {
-                       iounmap((void*)up[(i * 2) + 1].port.mapbase);
-                       up[(i * 2) + 1].port.mapbase = 0;
-               }
+static struct platform_driver ip22zilog_driver = {
+       .probe  = ip22zilog_probe,
+       .remove = ip22zilog_remove,
+       .driver = {
+               .name = "ip22zilog"
        }
+};
+
+static int __init ip22zilog_init(void)
+{
+       int ret;
+
+       ret = uart_register_driver(&ip22zilog_reg);
+       if (ret)
+               return ret;
+
+       ret = platform_driver_register(&ip22zilog_driver);
+       if (ret)
+               uart_unregister_driver(&ip22zilog_reg);
 
+       return ret;
+
+}
+
+static void __exit ip22zilog_exit(void)
+{
        uart_unregister_driver(&ip22zilog_reg);
+       platform_driver_unregister(&ip22zilog_driver);
 }
 
 module_init(ip22zilog_init);