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