diff options
Diffstat (limited to 'drivers/serial/sunsab.c')
-rw-r--r-- | drivers/serial/sunsab.c | 301 |
1 files changed, 144 insertions, 157 deletions
diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c index e4c0fd2d6a9d..0dbd4df44c05 100644 --- a/drivers/serial/sunsab.c +++ b/drivers/serial/sunsab.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* sunsab.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. | 1 | /* sunsab.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. |
2 | * | 2 | * |
3 | * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) | 3 | * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) |
4 | * Copyright (C) 2002 David S. Miller (davem@redhat.com) | 4 | * Copyright (C) 2002, 2006 David S. Miller (davem@davemloft.net) |
5 | * | 5 | * |
6 | * Rewrote buffer handling to use CIRC(Circular Buffer) macros. | 6 | * Rewrote buffer handling to use CIRC(Circular Buffer) macros. |
7 | * Maxim Krasnyanskiy <maxk@qualcomm.com> | 7 | * Maxim Krasnyanskiy <maxk@qualcomm.com> |
@@ -12,10 +12,9 @@ | |||
12 | * Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12 | 12 | * Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12 |
13 | * | 13 | * |
14 | * Ported to new 2.5.x UART layer. | 14 | * Ported to new 2.5.x UART layer. |
15 | * David S. Miller <davem@redhat.com> | 15 | * David S. Miller <davem@davemloft.net> |
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include <linux/config.h> | ||
19 | #include <linux/module.h> | 18 | #include <linux/module.h> |
20 | #include <linux/kernel.h> | 19 | #include <linux/kernel.h> |
21 | #include <linux/sched.h> | 20 | #include <linux/sched.h> |
@@ -37,8 +36,8 @@ | |||
37 | 36 | ||
38 | #include <asm/io.h> | 37 | #include <asm/io.h> |
39 | #include <asm/irq.h> | 38 | #include <asm/irq.h> |
40 | #include <asm/oplib.h> | 39 | #include <asm/prom.h> |
41 | #include <asm/ebus.h> | 40 | #include <asm/of_device.h> |
42 | 41 | ||
43 | #if defined(CONFIG_SERIAL_SUNZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) | 42 | #if defined(CONFIG_SERIAL_SUNZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) |
44 | #define SUPPORT_SYSRQ | 43 | #define SUPPORT_SYSRQ |
@@ -851,7 +850,6 @@ static struct uart_ops sunsab_pops = { | |||
851 | static struct uart_driver sunsab_reg = { | 850 | static struct uart_driver sunsab_reg = { |
852 | .owner = THIS_MODULE, | 851 | .owner = THIS_MODULE, |
853 | .driver_name = "serial", | 852 | .driver_name = "serial", |
854 | .devfs_name = "tts/", | ||
855 | .dev_name = "ttyS", | 853 | .dev_name = "ttyS", |
856 | .major = TTY_MAJOR, | 854 | .major = TTY_MAJOR, |
857 | }; | 855 | }; |
@@ -977,199 +975,188 @@ static inline struct console *SUNSAB_CONSOLE(void) | |||
977 | #define sunsab_console_init() do { } while (0) | 975 | #define sunsab_console_init() do { } while (0) |
978 | #endif | 976 | #endif |
979 | 977 | ||
980 | static void __init for_each_sab_edev(void (*callback)(struct linux_ebus_device *, void *), void *arg) | 978 | static int __devinit sunsab_init_one(struct uart_sunsab_port *up, |
979 | struct of_device *op, | ||
980 | unsigned long offset, | ||
981 | int line) | ||
981 | { | 982 | { |
982 | struct linux_ebus *ebus; | 983 | up->port.line = line; |
983 | struct linux_ebus_device *edev = NULL; | 984 | up->port.dev = &op->dev; |
984 | 985 | ||
985 | for_each_ebus(ebus) { | 986 | up->port.mapbase = op->resource[0].start + offset; |
986 | for_each_ebusdev(edev, ebus) { | 987 | up->port.membase = of_ioremap(&op->resource[0], offset, |
987 | if (!strcmp(edev->prom_node->name, "se")) { | 988 | sizeof(union sab82532_async_regs), |
988 | callback(edev, arg); | 989 | "sab"); |
989 | continue; | 990 | if (!up->port.membase) |
990 | } else if (!strcmp(edev->prom_node->name, "serial")) { | 991 | return -ENOMEM; |
991 | char *compat; | 992 | up->regs = (union sab82532_async_regs __iomem *) up->port.membase; |
992 | int clen; | ||
993 | |||
994 | /* On RIO this can be an SE, check it. We could | ||
995 | * just check ebus->is_rio, but this is more portable. | ||
996 | */ | ||
997 | compat = of_get_property(edev->prom_node, | ||
998 | "compatible", &clen); | ||
999 | if (compat && clen > 0) { | ||
1000 | if (strncmp(compat, "sab82532", 8) == 0) { | ||
1001 | callback(edev, arg); | ||
1002 | continue; | ||
1003 | } | ||
1004 | } | ||
1005 | } | ||
1006 | } | ||
1007 | } | ||
1008 | } | ||
1009 | 993 | ||
1010 | static void __init sab_count_callback(struct linux_ebus_device *edev, void *arg) | 994 | up->port.irq = op->irqs[0]; |
1011 | { | ||
1012 | int *count_p = arg; | ||
1013 | 995 | ||
1014 | (*count_p)++; | 996 | up->port.fifosize = SAB82532_XMIT_FIFO_SIZE; |
1015 | } | 997 | up->port.iotype = UPIO_MEM; |
1016 | 998 | ||
1017 | static void __init sab_attach_callback(struct linux_ebus_device *edev, void *arg) | 999 | writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc); |
1018 | { | ||
1019 | int *instance_p = arg; | ||
1020 | struct uart_sunsab_port *up; | ||
1021 | unsigned long regs, offset; | ||
1022 | int i; | ||
1023 | 1000 | ||
1024 | /* Note: ports are located in reverse order */ | 1001 | up->port.ops = &sunsab_pops; |
1025 | regs = edev->resource[0].start; | 1002 | up->port.type = PORT_SUNSAB; |
1026 | offset = sizeof(union sab82532_async_regs); | 1003 | up->port.uartclk = SAB_BASE_BAUD; |
1027 | for (i = 0; i < 2; i++) { | ||
1028 | up = &sunsab_ports[(*instance_p * 2) + 1 - i]; | ||
1029 | 1004 | ||
1030 | memset(up, 0, sizeof(*up)); | 1005 | up->type = readb(&up->regs->r.vstr) & 0x0f; |
1031 | up->regs = ioremap(regs + offset, sizeof(union sab82532_async_regs)); | 1006 | writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &up->regs->w.pcr); |
1032 | up->port.irq = edev->irqs[0]; | 1007 | writeb(0xff, &up->regs->w.pim); |
1033 | up->port.fifosize = SAB82532_XMIT_FIFO_SIZE; | 1008 | if ((up->port.line & 0x1) == 0) { |
1034 | up->port.mapbase = (unsigned long)up->regs; | 1009 | up->pvr_dsr_bit = (1 << 0); |
1035 | up->port.iotype = UPIO_MEM; | 1010 | up->pvr_dtr_bit = (1 << 1); |
1011 | } else { | ||
1012 | up->pvr_dsr_bit = (1 << 3); | ||
1013 | up->pvr_dtr_bit = (1 << 2); | ||
1014 | } | ||
1015 | up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4); | ||
1016 | writeb(up->cached_pvr, &up->regs->w.pvr); | ||
1017 | up->cached_mode = readb(&up->regs->rw.mode); | ||
1018 | up->cached_mode |= SAB82532_MODE_FRTS; | ||
1019 | writeb(up->cached_mode, &up->regs->rw.mode); | ||
1020 | up->cached_mode |= SAB82532_MODE_RTS; | ||
1021 | writeb(up->cached_mode, &up->regs->rw.mode); | ||
1036 | 1022 | ||
1037 | writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc); | 1023 | up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT; |
1024 | up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT; | ||
1038 | 1025 | ||
1039 | offset -= sizeof(union sab82532_async_regs); | 1026 | if (!(up->port.line & 0x01)) { |
1027 | int err; | ||
1028 | |||
1029 | err = request_irq(up->port.irq, sunsab_interrupt, | ||
1030 | IRQF_SHARED, "sab", up); | ||
1031 | if (err) { | ||
1032 | of_iounmap(up->port.membase, | ||
1033 | sizeof(union sab82532_async_regs)); | ||
1034 | return err; | ||
1035 | } | ||
1040 | } | 1036 | } |
1041 | 1037 | ||
1042 | (*instance_p)++; | 1038 | return 0; |
1043 | } | 1039 | } |
1044 | 1040 | ||
1045 | static int __init probe_for_sabs(void) | 1041 | static int __devinit sab_probe(struct of_device *op, const struct of_device_id *match) |
1046 | { | 1042 | { |
1047 | int this_sab = 0; | 1043 | static int inst; |
1044 | struct uart_sunsab_port *up; | ||
1045 | int err; | ||
1046 | |||
1047 | up = &sunsab_ports[inst * 2]; | ||
1048 | |||
1049 | err = sunsab_init_one(&up[0], op, | ||
1050 | sizeof(union sab82532_async_regs), | ||
1051 | (inst * 2) + 0); | ||
1052 | if (err) | ||
1053 | return err; | ||
1054 | |||
1055 | err = sunsab_init_one(&up[0], op, 0, | ||
1056 | (inst * 2) + 1); | ||
1057 | if (err) { | ||
1058 | of_iounmap(up[0].port.membase, | ||
1059 | sizeof(union sab82532_async_regs)); | ||
1060 | free_irq(up[0].port.irq, &up[0]); | ||
1061 | return err; | ||
1062 | } | ||
1048 | 1063 | ||
1049 | /* Find device instances. */ | 1064 | uart_add_one_port(&sunsab_reg, &up[0].port); |
1050 | for_each_sab_edev(&sab_count_callback, &this_sab); | 1065 | uart_add_one_port(&sunsab_reg, &up[1].port); |
1051 | if (!this_sab) | ||
1052 | return -ENODEV; | ||
1053 | 1066 | ||
1054 | /* Allocate tables. */ | 1067 | dev_set_drvdata(&op->dev, &up[0]); |
1055 | sunsab_ports = kmalloc(sizeof(struct uart_sunsab_port) * this_sab * 2, | ||
1056 | GFP_KERNEL); | ||
1057 | if (!sunsab_ports) | ||
1058 | return -ENOMEM; | ||
1059 | 1068 | ||
1060 | num_channels = this_sab * 2; | 1069 | inst++; |
1061 | 1070 | ||
1062 | this_sab = 0; | ||
1063 | for_each_sab_edev(&sab_attach_callback, &this_sab); | ||
1064 | return 0; | 1071 | return 0; |
1065 | } | 1072 | } |
1066 | 1073 | ||
1067 | static void __init sunsab_init_hw(void) | 1074 | static void __devexit sab_remove_one(struct uart_sunsab_port *up) |
1068 | { | 1075 | { |
1069 | int i; | 1076 | uart_remove_one_port(&sunsab_reg, &up->port); |
1070 | 1077 | if (!(up->port.line & 1)) | |
1071 | for (i = 0; i < num_channels; i++) { | 1078 | free_irq(up->port.irq, up); |
1072 | struct uart_sunsab_port *up = &sunsab_ports[i]; | 1079 | of_iounmap(up->port.membase, |
1073 | 1080 | sizeof(union sab82532_async_regs)); | |
1074 | up->port.line = i; | ||
1075 | up->port.ops = &sunsab_pops; | ||
1076 | up->port.type = PORT_SUNSAB; | ||
1077 | up->port.uartclk = SAB_BASE_BAUD; | ||
1078 | |||
1079 | up->type = readb(&up->regs->r.vstr) & 0x0f; | ||
1080 | writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &up->regs->w.pcr); | ||
1081 | writeb(0xff, &up->regs->w.pim); | ||
1082 | if (up->port.line == 0) { | ||
1083 | up->pvr_dsr_bit = (1 << 0); | ||
1084 | up->pvr_dtr_bit = (1 << 1); | ||
1085 | } else { | ||
1086 | up->pvr_dsr_bit = (1 << 3); | ||
1087 | up->pvr_dtr_bit = (1 << 2); | ||
1088 | } | ||
1089 | up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4); | ||
1090 | writeb(up->cached_pvr, &up->regs->w.pvr); | ||
1091 | up->cached_mode = readb(&up->regs->rw.mode); | ||
1092 | up->cached_mode |= SAB82532_MODE_FRTS; | ||
1093 | writeb(up->cached_mode, &up->regs->rw.mode); | ||
1094 | up->cached_mode |= SAB82532_MODE_RTS; | ||
1095 | writeb(up->cached_mode, &up->regs->rw.mode); | ||
1096 | |||
1097 | up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT; | ||
1098 | up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT; | ||
1099 | |||
1100 | if (!(up->port.line & 0x01)) { | ||
1101 | if (request_irq(up->port.irq, sunsab_interrupt, | ||
1102 | SA_SHIRQ, "serial(sab82532)", up)) { | ||
1103 | printk("sunsab%d: can't get IRQ %x\n", | ||
1104 | i, up->port.irq); | ||
1105 | continue; | ||
1106 | } | ||
1107 | } | ||
1108 | } | ||
1109 | } | 1081 | } |
1110 | 1082 | ||
1111 | static int __init sunsab_init(void) | 1083 | static int __devexit sab_remove(struct of_device *op) |
1112 | { | 1084 | { |
1113 | int ret = probe_for_sabs(); | 1085 | struct uart_sunsab_port *up = dev_get_drvdata(&op->dev); |
1114 | int i; | ||
1115 | |||
1116 | if (ret < 0) | ||
1117 | return ret; | ||
1118 | 1086 | ||
1119 | sunsab_init_hw(); | 1087 | sab_remove_one(&up[0]); |
1088 | sab_remove_one(&up[1]); | ||
1120 | 1089 | ||
1121 | sunsab_reg.minor = sunserial_current_minor; | 1090 | dev_set_drvdata(&op->dev, NULL); |
1122 | sunsab_reg.nr = num_channels; | ||
1123 | 1091 | ||
1124 | ret = uart_register_driver(&sunsab_reg); | 1092 | return 0; |
1125 | if (ret < 0) { | 1093 | } |
1126 | int i; | ||
1127 | 1094 | ||
1128 | for (i = 0; i < num_channels; i++) { | 1095 | static struct of_device_id sab_match[] = { |
1129 | struct uart_sunsab_port *up = &sunsab_ports[i]; | 1096 | { |
1097 | .name = "se", | ||
1098 | }, | ||
1099 | { | ||
1100 | .name = "serial", | ||
1101 | .compatible = "sab82532", | ||
1102 | }, | ||
1103 | {}, | ||
1104 | }; | ||
1105 | MODULE_DEVICE_TABLE(of, sab_match); | ||
1130 | 1106 | ||
1131 | if (!(up->port.line & 0x01)) | 1107 | static struct of_platform_driver sab_driver = { |
1132 | free_irq(up->port.irq, up); | 1108 | .name = "sab", |
1133 | iounmap(up->regs); | 1109 | .match_table = sab_match, |
1134 | } | 1110 | .probe = sab_probe, |
1135 | kfree(sunsab_ports); | 1111 | .remove = __devexit_p(sab_remove), |
1136 | sunsab_ports = NULL; | 1112 | }; |
1137 | 1113 | ||
1138 | return ret; | 1114 | static int __init sunsab_init(void) |
1115 | { | ||
1116 | struct device_node *dp; | ||
1117 | int err; | ||
1118 | |||
1119 | num_channels = 0; | ||
1120 | for_each_node_by_name(dp, "su") | ||
1121 | num_channels += 2; | ||
1122 | for_each_node_by_name(dp, "serial") { | ||
1123 | if (of_device_is_compatible(dp, "sab82532")) | ||
1124 | num_channels += 2; | ||
1139 | } | 1125 | } |
1140 | 1126 | ||
1141 | sunsab_reg.tty_driver->name_base = sunsab_reg.minor - 64; | 1127 | if (num_channels) { |
1128 | sunsab_ports = kzalloc(sizeof(struct uart_sunsab_port) * | ||
1129 | num_channels, GFP_KERNEL); | ||
1130 | if (!sunsab_ports) | ||
1131 | return -ENOMEM; | ||
1142 | 1132 | ||
1143 | sunsab_reg.cons = SUNSAB_CONSOLE(); | 1133 | sunsab_reg.minor = sunserial_current_minor; |
1134 | sunsab_reg.nr = num_channels; | ||
1144 | 1135 | ||
1145 | sunserial_current_minor += num_channels; | 1136 | err = uart_register_driver(&sunsab_reg); |
1146 | 1137 | if (err) { | |
1147 | for (i = 0; i < num_channels; i++) { | 1138 | kfree(sunsab_ports); |
1148 | struct uart_sunsab_port *up = &sunsab_ports[i]; | 1139 | sunsab_ports = NULL; |
1149 | 1140 | ||
1150 | uart_add_one_port(&sunsab_reg, &up->port); | 1141 | return err; |
1142 | } | ||
1143 | |||
1144 | sunsab_reg.tty_driver->name_base = sunsab_reg.minor - 64; | ||
1145 | sunsab_reg.cons = SUNSAB_CONSOLE(); | ||
1146 | sunserial_current_minor += num_channels; | ||
1151 | } | 1147 | } |
1152 | 1148 | ||
1153 | return 0; | 1149 | return of_register_driver(&sab_driver, &of_bus_type); |
1154 | } | 1150 | } |
1155 | 1151 | ||
1156 | static void __exit sunsab_exit(void) | 1152 | static void __exit sunsab_exit(void) |
1157 | { | 1153 | { |
1158 | int i; | 1154 | of_unregister_driver(&sab_driver); |
1159 | 1155 | if (num_channels) { | |
1160 | for (i = 0; i < num_channels; i++) { | 1156 | sunserial_current_minor -= num_channels; |
1161 | struct uart_sunsab_port *up = &sunsab_ports[i]; | 1157 | uart_unregister_driver(&sunsab_reg); |
1162 | |||
1163 | uart_remove_one_port(&sunsab_reg, &up->port); | ||
1164 | |||
1165 | if (!(up->port.line & 0x01)) | ||
1166 | free_irq(up->port.irq, up); | ||
1167 | iounmap(up->regs); | ||
1168 | } | 1158 | } |
1169 | 1159 | ||
1170 | sunserial_current_minor -= num_channels; | ||
1171 | uart_unregister_driver(&sunsab_reg); | ||
1172 | |||
1173 | kfree(sunsab_ports); | 1160 | kfree(sunsab_ports); |
1174 | sunsab_ports = NULL; | 1161 | sunsab_ports = NULL; |
1175 | } | 1162 | } |