Skip to content

Commit 7cac59d

Browse files
maciej-w-rozyckigregkh
authored andcommitted
serial: zs: Convert to use a platform device
Prevent a crash from happening as the first serial port is initialised: Console: switching to mono frame buffer device 160x64 fb0: PMAG-AA frame buffer device at tc0 DECstation Z85C30 serial driver version 0.10 CPU 0 Unable to handle kernel paging request at virtual address 0000002c, epc == 803ab00c, ra == 803aafe0 Oops[#1]: CPU: 0 PID: 1 Comm: swapper Not tainted 6.4.0-rc3-00031-g84a9582fd203-dirty #57 $ 0 : 00000000 10012c00 803aaeb0 00000000 $ 4 : 80e12f60 80e12f50 80e12f58 81000030 $ 8 : 00000000 805ff37c 00000000 33433538 $12 : 65732030 00000006 80c2915d 6c616972 $16 : 80e12f00 807b7630 00000000 00000000 $20 : 00000004 00000348 000001a0 807623b8 $24 : 00000018 00000000 $28 : 80c24000 80c25d60 8078b148 803aafe0 Hi : 00000000 Lo : 00000000 epc : 803ab00c serial_base_ctrl_add+0x78/0xf4 ra : 803aafe0 serial_base_ctrl_add+0x4c/0xf4 Status: 10012c03 KERNEL EXL IE Cause : 00000008 (ExcCode 02) BadVA : 0000002c PrId : 00000440 (R4400SC) Modules linked in: Process swapper (pid: 1, threadinfo=(ptrval), task=(ptrval), tls=00000000) Stack : 80760000 00000cc0 00400044 00400040 803aa02c 80d61ab8 00000000 807b7630 80760000 807623b8 807b7628 803aa644 80386998 00000000 80e17780 80220f68 80e17780 80d61ab8 80c17d80 80e17780 80e17780 8063c798 80e17780 80383fa0 00000010 80e17780 00000000 80386998 807a0000 00000000 00400040 8038f848 807623b8 80d61ab8 00000004 80e17780 00000000 803a68e4 80c25e2c 803bb884 ... Call Trace: [<803ab00c>] serial_base_ctrl_add+0x78/0xf4 [<803aa644>] serial_core_register_port+0x174/0x69c [<8077e9ac>] zs_init+0xc8/0xfc [<800404d4>] do_one_initcall+0x40/0x2ac [<8076cecc>] kernel_init_freeable+0x1e4/0x270 [<80605bec>] kernel_init+0x20/0x108 [<800431e8>] ret_from_kernel_thread+0x14/0x1c Code: 2442aeb0 ae120024 ae0200d0 <8c67002c> 50e00001 8c670000 3c06806 3c05806e afb30010 ---[ end trace 0000000000000000 ]--- (report at the offending commit) -- where a pointer is dereferenced that has been derived from a null pointer to the port's parent device. Since no device is available with legacy probing and it's not anymore a preferable way to discover devices anyway, switch the driver to using a platform device and use it as the port's parent device. Update resource handling accordingly and only request the actual span of addresses used within the slot, which will have had its resource already requested by generic platform device code. Use platform_driver_probe() not just because SCC devices are fixed with solder on board and not straightforward to remove, but foremost because the associated TTY's major device number is the same as used by the dz driver and the first driver to claim it will prevent the other one from using it. Either one DZ device or some SCC devices will be present in a given system but never both at a time, and therefore we want the major device number to be claimed by the first driver to actually successfully bind to its device and platform_driver_probe() is a way to fulfil that. An unfortunate consequence of the switch to a platform device is we now hand the console over from the bootconsole much later in the bootstrap. The firmware console handler appears good enough though to work so late and in particular with interrupts enabled. Since there is one way only remaining to reach zs_reset() now, remove the port initialisation marker as no longer needed and go through the channel reset unconditionally. Fixes: 84a9582 ("serial: core: Start managing serial controllers to enable runtime PM") Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk> Cc: stable@vger.kernel.org # needs to use .remove_new for <= 6.10 Link: https://patch.msgid.link/alpine.DEB.2.21.2605062328480.46195@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 5d7a49d commit 7cac59d

3 files changed

Lines changed: 129 additions & 124 deletions

File tree

arch/mips/dec/platform.c

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <asm/bootinfo.h>
1414

1515
#include <asm/dec/interrupts.h>
16+
#include <asm/dec/ioasic_addrs.h>
1617
#include <asm/dec/kn01.h>
1718
#include <asm/dec/kn02.h>
1819
#include <asm/dec/system.h>
@@ -53,10 +54,37 @@ static struct platform_device *dec_dz_devices[] __initdata = {
5354
&dec_dz_device,
5455
};
5556

57+
static struct resource dec_zs_resources[][2] = {
58+
{
59+
{ .name = "scc0", .flags = IORESOURCE_MEM, },
60+
{ .name = "scc0", .flags = IORESOURCE_IRQ, },
61+
},
62+
{
63+
{ .name = "scc1", .flags = IORESOURCE_MEM, },
64+
{ .name = "scc1", .flags = IORESOURCE_IRQ, },
65+
},
66+
};
67+
68+
static struct platform_device dec_zs_device[] = {
69+
{
70+
.name = "zs",
71+
.id = 0,
72+
.resource = dec_zs_resources[0],
73+
.num_resources = ARRAY_SIZE(dec_zs_resources[0]),
74+
},
75+
{
76+
.name = "zs",
77+
.id = 1,
78+
.resource = dec_zs_resources[1],
79+
.num_resources = ARRAY_SIZE(dec_zs_resources[1]),
80+
},
81+
};
82+
5683
static int __init dec_add_devices(void)
5784
{
58-
int ret1, ret2;
59-
int num_dz;
85+
struct platform_device *dec_zs_devices[ARRAY_SIZE(dec_zs_device)];
86+
int ret1, ret2, ret3;
87+
int num_dz, num_zs;
6088
int irq, i;
6189

6290
dec_rtc_resources[0].start = RTC_PORT(0);
@@ -84,10 +112,36 @@ static int __init dec_add_devices(void)
84112
}
85113
num_dz = i;
86114

115+
i = 0;
116+
irq = dec_interrupt[DEC_IRQ_SCC0];
117+
if (irq >= 0) {
118+
resource_size_t base = dec_kn_slot_base + IOASIC_SCC0;
119+
120+
dec_zs_device[i].resource[0].start = base;
121+
dec_zs_device[i].resource[0].end = base + dec_kn_slot_size - 1;
122+
dec_zs_device[i].resource[1].start = irq;
123+
dec_zs_device[i].resource[1].end = irq;
124+
dec_zs_devices[i] = &dec_zs_device[i];
125+
i++;
126+
}
127+
irq = dec_interrupt[DEC_IRQ_SCC1];
128+
if (irq >= 0) {
129+
resource_size_t base = dec_kn_slot_base + IOASIC_SCC1;
130+
131+
dec_zs_device[i].resource[0].start = base;
132+
dec_zs_device[i].resource[0].end = base + dec_kn_slot_size - 1;
133+
dec_zs_device[i].resource[1].start = irq;
134+
dec_zs_device[i].resource[1].end = irq;
135+
dec_zs_devices[i] = &dec_zs_device[i];
136+
i++;
137+
}
138+
num_zs = i;
139+
87140
ret1 = platform_device_register(&dec_rtc_device);
88141
ret2 = IS_ENABLED(CONFIG_32BIT) ?
89142
platform_add_devices(dec_dz_devices, num_dz) : 0;
90-
return ret1 ? ret1 : ret2;
143+
ret3 = platform_add_devices(dec_zs_devices, num_zs);
144+
return ret1 ? ret1 : ret2 ? ret2 : ret3;
91145
}
92146

93147
device_initcall(dec_add_devices);

drivers/tty/serial/zs.c

Lines changed: 72 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#include <linux/kernel.h>
5757
#include <linux/module.h>
5858
#include <linux/major.h>
59+
#include <linux/platform_device.h>
5960
#include <linux/serial.h>
6061
#include <linux/serial_core.h>
6162
#include <linux/spinlock.h>
@@ -66,10 +67,6 @@
6667

6768
#include <linux/atomic.h>
6869

69-
#include <asm/dec/interrupts.h>
70-
#include <asm/dec/ioasic_addrs.h>
71-
#include <asm/dec/system.h>
72-
7370
#include "zs.h"
7471

7572

@@ -79,7 +76,7 @@ MODULE_LICENSE("GPL");
7976

8077

8178
static char zs_name[] __initdata = "DECstation Z85C30 serial driver version ";
82-
static char zs_version[] __initdata = "0.10";
79+
static char zs_version[] __initdata = "0.11";
8380

8481
/*
8582
* It would be nice to dynamically allocate everything that
@@ -98,12 +95,8 @@ static char zs_version[] __initdata = "0.10";
9895

9996
#define to_zport(uport) container_of(uport, struct zs_port, port)
10097

101-
struct zs_parms {
102-
resource_size_t scc[ZS_NUM_SCCS];
103-
int irq[ZS_NUM_SCCS];
104-
};
105-
10698
static struct zs_scc zs_sccs[ZS_NUM_SCCS];
99+
static struct uart_driver zs_reg;
107100

108101
/*
109102
* Set parameters in WR5, WR12, WR13 such as not to interfere
@@ -839,16 +832,15 @@ static void zs_reset(struct zs_port *zport)
839832

840833
spin_lock_irqsave(&scc->zlock, flags);
841834
irq = !irqs_disabled_flags(flags);
842-
if (!zport->initialised) {
843-
/* Reset the pointer first, just in case... */
844-
read_zsreg(zport, R0);
845-
/* And let the current transmission finish. */
846-
zs_line_drain(zport, irq);
847-
write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB);
848-
udelay(10);
849-
write_zsreg(zport, R9, 0);
850-
zport->initialised = 1;
851-
}
835+
836+
/* Reset the pointer first, just in case... */
837+
read_zsreg(zport, R0);
838+
/* And let the current transmission finish. */
839+
zs_line_drain(zport, irq);
840+
write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB);
841+
udelay(10);
842+
write_zsreg(zport, R9, 0);
843+
852844
load_zsregs(zport, zport->regs, irq);
853845
spin_unlock_irqrestore(&scc->zlock, flags);
854846
}
@@ -1055,63 +1047,62 @@ static const struct uart_ops zs_ops = {
10551047
/*
10561048
* Initialize Z85C30 port structures.
10571049
*/
1058-
static int __init zs_probe_sccs(void)
1050+
static int __init zs_probe(struct platform_device *pdev)
10591051
{
1060-
static int probed;
1061-
struct zs_parms zs_parms;
1062-
int chip, side, irq;
1063-
int n_chips = 0;
1052+
struct resource *mem_resource, *irq_resource;
1053+
int chip, side;
10641054
int i;
10651055

1066-
if (probed)
1067-
return 0;
1056+
mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1057+
irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
1058+
if (!mem_resource || !irq_resource)
1059+
return -ENODEV;
10681060

1069-
irq = dec_interrupt[DEC_IRQ_SCC0];
1070-
if (irq >= 0) {
1071-
zs_parms.scc[n_chips] = IOASIC_SCC0;
1072-
zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC0];
1073-
n_chips++;
1074-
}
1075-
irq = dec_interrupt[DEC_IRQ_SCC1];
1076-
if (irq >= 0) {
1077-
zs_parms.scc[n_chips] = IOASIC_SCC1;
1078-
zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC1];
1079-
n_chips++;
1080-
}
1081-
if (!n_chips)
1082-
return -ENXIO;
1083-
1084-
probed = 1;
1085-
1086-
for (chip = 0; chip < n_chips; chip++) {
1087-
spin_lock_init(&zs_sccs[chip].zlock);
1088-
for (side = 0; side < ZS_NUM_CHAN; side++) {
1089-
struct zs_port *zport = &zs_sccs[chip].zport[side];
1090-
struct uart_port *uport = &zport->port;
1091-
1092-
zport->scc = &zs_sccs[chip];
1093-
zport->clk_mode = 16;
1094-
1095-
uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE);
1096-
uport->irq = zs_parms.irq[chip];
1097-
uport->uartclk = ZS_CLOCK;
1098-
uport->fifosize = 1;
1099-
uport->iotype = UPIO_MEM;
1100-
uport->flags = UPF_BOOT_AUTOCONF;
1101-
uport->ops = &zs_ops;
1102-
uport->line = chip * ZS_NUM_CHAN + side;
1103-
uport->mapbase = dec_kn_slot_base +
1104-
zs_parms.scc[chip] +
1105-
(side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE;
1106-
1107-
for (i = 0; i < ZS_NUM_REGS; i++)
1108-
zport->regs[i] = zs_init_regs[i];
1109-
}
1061+
chip = pdev->id;
1062+
spin_lock_init(&zs_sccs[chip].zlock);
1063+
for (side = 0; side < ZS_NUM_CHAN; side++) {
1064+
struct zs_port *zport = &zs_sccs[chip].zport[side];
1065+
struct uart_port *uport = &zport->port;
1066+
1067+
zport->scc = &zs_sccs[chip];
1068+
zport->clk_mode = 16;
1069+
1070+
uport->dev = &pdev->dev;
1071+
uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE);
1072+
uport->irq = irq_resource->start;
1073+
uport->uartclk = ZS_CLOCK;
1074+
uport->fifosize = 1;
1075+
uport->iotype = UPIO_MEM;
1076+
uport->flags = UPF_BOOT_AUTOCONF;
1077+
uport->ops = &zs_ops;
1078+
uport->line = chip * ZS_NUM_CHAN + side;
1079+
uport->mapbase = mem_resource->start +
1080+
(side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE;
1081+
1082+
for (i = 0; i < ZS_NUM_REGS; i++)
1083+
zport->regs[i] = zs_init_regs[i];
1084+
1085+
if (uart_add_one_port(&zs_reg, uport))
1086+
uport->dev = NULL;
11101087
}
11111088

11121089
return 0;
11131090
}
11141091

1092+
static void __exit zs_remove(struct platform_device *pdev)
1093+
{
1094+
int chip, side;
1095+
1096+
chip = pdev->id;
1097+
for (side = ZS_NUM_CHAN - 1; side >= 0; side--) {
1098+
struct zs_port *zport = &zs_sccs[chip].zport[side];
1099+
struct uart_port *uport = &zport->port;
1100+
1101+
if (uport->dev)
1102+
uart_remove_one_port(&zs_reg, uport);
1103+
}
1104+
}
1105+
11151106

11161107
#ifdef CONFIG_SERIAL_ZS_CONSOLE
11171108
static void zs_console_putchar(struct uart_port *uport, unsigned char ch)
@@ -1192,20 +1183,14 @@ static int __init zs_console_setup(struct console *co, char *options)
11921183
int bits = 8;
11931184
int parity = 'n';
11941185
int flow = 'n';
1195-
int ret;
1196-
1197-
ret = zs_map_port(uport);
1198-
if (ret)
1199-
return ret;
1200-
1201-
zs_reset(zport);
12021186

1187+
if (!zport->scc)
1188+
return -ENODEV;
12031189
if (options)
12041190
uart_parse_options(options, &baud, &parity, &bits, &flow);
12051191
return uart_set_options(uport, co, baud, parity, bits, flow);
12061192
}
12071193

1208-
static struct uart_driver zs_reg;
12091194
static struct console zs_console = {
12101195
.name = "ttyS",
12111196
.write = zs_console_write,
@@ -1216,23 +1201,6 @@ static struct console zs_console = {
12161201
.data = &zs_reg,
12171202
};
12181203

1219-
/*
1220-
* Register console.
1221-
*/
1222-
static int __init zs_serial_console_init(void)
1223-
{
1224-
int ret;
1225-
1226-
ret = zs_probe_sccs();
1227-
if (ret)
1228-
return ret;
1229-
register_console(&zs_console);
1230-
1231-
return 0;
1232-
}
1233-
1234-
console_initcall(zs_serial_console_init);
1235-
12361204
#define SERIAL_ZS_CONSOLE &zs_console
12371205
#else
12381206
#define SERIAL_ZS_CONSOLE NULL
@@ -1248,47 +1216,31 @@ static struct uart_driver zs_reg = {
12481216
.cons = SERIAL_ZS_CONSOLE,
12491217
};
12501218

1219+
static struct platform_driver zs_driver = {
1220+
.remove = __exit_p(zs_remove),
1221+
.driver = { .name = "zs" },
1222+
};
1223+
12511224
/* zs_init inits the driver. */
12521225
static int __init zs_init(void)
12531226
{
1254-
int i, ret;
1227+
int ret;
12551228

12561229
pr_info("%s%s\n", zs_name, zs_version);
12571230

1258-
/* Find out how many Z85C30 SCCs we have. */
1259-
ret = zs_probe_sccs();
1260-
if (ret)
1261-
return ret;
1262-
12631231
ret = uart_register_driver(&zs_reg);
12641232
if (ret)
12651233
return ret;
1234+
ret = platform_driver_probe(&zs_driver, zs_probe);
1235+
if (ret)
1236+
uart_unregister_driver(&zs_reg);
12661237

1267-
for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) {
1268-
struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN];
1269-
struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN];
1270-
struct uart_port *uport = &zport->port;
1271-
1272-
if (zport->scc)
1273-
uart_add_one_port(&zs_reg, uport);
1274-
}
1275-
1276-
return 0;
1238+
return ret;
12771239
}
12781240

12791241
static void __exit zs_exit(void)
12801242
{
1281-
int i;
1282-
1283-
for (i = ZS_NUM_SCCS * ZS_NUM_CHAN - 1; i >= 0; i--) {
1284-
struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN];
1285-
struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN];
1286-
struct uart_port *uport = &zport->port;
1287-
1288-
if (zport->scc)
1289-
uart_remove_one_port(&zs_reg, uport);
1290-
}
1291-
1243+
platform_driver_unregister(&zs_driver);
12921244
uart_unregister_driver(&zs_reg);
12931245
}
12941246

drivers/tty/serial/zs.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
struct zs_port {
2323
struct zs_scc *scc; /* Containing SCC. */
2424
struct uart_port port; /* Underlying UART. */
25-
int initialised; /* For the console port. */
2625

2726
int clk_mode; /* May be 1, 16, 32, or 64. */
2827

0 commit comments

Comments
 (0)