diff options
Diffstat (limited to 'drivers/serial/sunsab.c')
-rw-r--r-- | drivers/serial/sunsab.c | 311 |
1 files changed, 154 insertions, 157 deletions
diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c index bfbe9dc90cca..cfe20f730436 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 | }; |
@@ -888,6 +886,15 @@ static int sunsab_console_setup(struct console *con, char *options) | |||
888 | unsigned long flags; | 886 | unsigned long flags; |
889 | unsigned int baud, quot; | 887 | unsigned int baud, quot; |
890 | 888 | ||
889 | /* | ||
890 | * The console framework calls us for each and every port | ||
891 | * registered. Defer the console setup until the requested | ||
892 | * port has been properly discovered. A bit of a hack, | ||
893 | * though... | ||
894 | */ | ||
895 | if (up->port.type != PORT_SUNSAB) | ||
896 | return -1; | ||
897 | |||
891 | printk("Console: ttyS%d (SAB82532)\n", | 898 | printk("Console: ttyS%d (SAB82532)\n", |
892 | (sunsab_reg.minor - 64) + con->index); | 899 | (sunsab_reg.minor - 64) + con->index); |
893 | 900 | ||
@@ -977,199 +984,189 @@ static inline struct console *SUNSAB_CONSOLE(void) | |||
977 | #define sunsab_console_init() do { } while (0) | 984 | #define sunsab_console_init() do { } while (0) |
978 | #endif | 985 | #endif |
979 | 986 | ||
980 | static void __init for_each_sab_edev(void (*callback)(struct linux_ebus_device *, void *), void *arg) | 987 | static int __devinit sunsab_init_one(struct uart_sunsab_port *up, |
988 | struct of_device *op, | ||
989 | unsigned long offset, | ||
990 | int line) | ||
981 | { | 991 | { |
982 | struct linux_ebus *ebus; | 992 | up->port.line = line; |
983 | struct linux_ebus_device *edev = NULL; | 993 | up->port.dev = &op->dev; |
984 | 994 | ||
985 | for_each_ebus(ebus) { | 995 | up->port.mapbase = op->resource[0].start + offset; |
986 | for_each_ebusdev(edev, ebus) { | 996 | up->port.membase = of_ioremap(&op->resource[0], offset, |
987 | if (!strcmp(edev->prom_name, "se")) { | 997 | sizeof(union sab82532_async_regs), |
988 | callback(edev, arg); | 998 | "sab"); |
989 | continue; | 999 | if (!up->port.membase) |
990 | } else if (!strcmp(edev->prom_name, "serial")) { | 1000 | return -ENOMEM; |
991 | char compat[32]; | 1001 | 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 | clen = prom_getproperty(edev->prom_node, "compatible", | ||
998 | compat, sizeof(compat)); | ||
999 | if (clen > 0) { | ||
1000 | if (strncmp(compat, "sab82532", 8) == 0) { | ||
1001 | callback(edev, arg); | ||
1002 | continue; | ||
1003 | } | ||
1004 | } | ||
1005 | } | ||
1006 | } | ||
1007 | } | ||
1008 | } | ||
1009 | 1002 | ||
1010 | static void __init sab_count_callback(struct linux_ebus_device *edev, void *arg) | 1003 | up->port.irq = op->irqs[0]; |
1011 | { | ||
1012 | int *count_p = arg; | ||
1013 | 1004 | ||
1014 | (*count_p)++; | 1005 | up->port.fifosize = SAB82532_XMIT_FIFO_SIZE; |
1015 | } | 1006 | up->port.iotype = UPIO_MEM; |
1016 | 1007 | ||
1017 | static void __init sab_attach_callback(struct linux_ebus_device *edev, void *arg) | 1008 | 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 | 1009 | ||
1024 | /* Note: ports are located in reverse order */ | 1010 | up->port.ops = &sunsab_pops; |
1025 | regs = edev->resource[0].start; | 1011 | up->port.type = PORT_SUNSAB; |
1026 | offset = sizeof(union sab82532_async_regs); | 1012 | up->port.uartclk = SAB_BASE_BAUD; |
1027 | for (i = 0; i < 2; i++) { | ||
1028 | up = &sunsab_ports[(*instance_p * 2) + 1 - i]; | ||
1029 | 1013 | ||
1030 | memset(up, 0, sizeof(*up)); | 1014 | up->type = readb(&up->regs->r.vstr) & 0x0f; |
1031 | up->regs = ioremap(regs + offset, sizeof(union sab82532_async_regs)); | 1015 | writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &up->regs->w.pcr); |
1032 | up->port.irq = edev->irqs[0]; | 1016 | writeb(0xff, &up->regs->w.pim); |
1033 | up->port.fifosize = SAB82532_XMIT_FIFO_SIZE; | 1017 | if ((up->port.line & 0x1) == 0) { |
1034 | up->port.mapbase = (unsigned long)up->regs; | 1018 | up->pvr_dsr_bit = (1 << 0); |
1035 | up->port.iotype = UPIO_MEM; | 1019 | up->pvr_dtr_bit = (1 << 1); |
1020 | } else { | ||
1021 | up->pvr_dsr_bit = (1 << 3); | ||
1022 | up->pvr_dtr_bit = (1 << 2); | ||
1023 | } | ||
1024 | up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4); | ||
1025 | writeb(up->cached_pvr, &up->regs->w.pvr); | ||
1026 | up->cached_mode = readb(&up->regs->rw.mode); | ||
1027 | up->cached_mode |= SAB82532_MODE_FRTS; | ||
1028 | writeb(up->cached_mode, &up->regs->rw.mode); | ||
1029 | up->cached_mode |= SAB82532_MODE_RTS; | ||
1030 | writeb(up->cached_mode, &up->regs->rw.mode); | ||
1036 | 1031 | ||
1037 | writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc); | 1032 | up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT; |
1033 | up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT; | ||
1038 | 1034 | ||
1039 | offset -= sizeof(union sab82532_async_regs); | 1035 | if (!(up->port.line & 0x01)) { |
1036 | int err; | ||
1037 | |||
1038 | err = request_irq(up->port.irq, sunsab_interrupt, | ||
1039 | IRQF_SHARED, "sab", up); | ||
1040 | if (err) { | ||
1041 | of_iounmap(up->port.membase, | ||
1042 | sizeof(union sab82532_async_regs)); | ||
1043 | return err; | ||
1044 | } | ||
1040 | } | 1045 | } |
1041 | 1046 | ||
1042 | (*instance_p)++; | 1047 | return 0; |
1043 | } | 1048 | } |
1044 | 1049 | ||
1045 | static int __init probe_for_sabs(void) | 1050 | static int __devinit sab_probe(struct of_device *op, const struct of_device_id *match) |
1046 | { | 1051 | { |
1047 | int this_sab = 0; | 1052 | static int inst; |
1053 | struct uart_sunsab_port *up; | ||
1054 | int err; | ||
1055 | |||
1056 | up = &sunsab_ports[inst * 2]; | ||
1057 | |||
1058 | err = sunsab_init_one(&up[0], op, | ||
1059 | 0, | ||
1060 | (inst * 2) + 0); | ||
1061 | if (err) | ||
1062 | return err; | ||
1063 | |||
1064 | err = sunsab_init_one(&up[1], op, | ||
1065 | sizeof(union sab82532_async_regs), | ||
1066 | (inst * 2) + 1); | ||
1067 | if (err) { | ||
1068 | of_iounmap(up[0].port.membase, | ||
1069 | sizeof(union sab82532_async_regs)); | ||
1070 | free_irq(up[0].port.irq, &up[0]); | ||
1071 | return err; | ||
1072 | } | ||
1048 | 1073 | ||
1049 | /* Find device instances. */ | 1074 | uart_add_one_port(&sunsab_reg, &up[0].port); |
1050 | for_each_sab_edev(&sab_count_callback, &this_sab); | 1075 | uart_add_one_port(&sunsab_reg, &up[1].port); |
1051 | if (!this_sab) | ||
1052 | return -ENODEV; | ||
1053 | 1076 | ||
1054 | /* Allocate tables. */ | 1077 | 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 | 1078 | ||
1060 | num_channels = this_sab * 2; | 1079 | inst++; |
1061 | 1080 | ||
1062 | this_sab = 0; | ||
1063 | for_each_sab_edev(&sab_attach_callback, &this_sab); | ||
1064 | return 0; | 1081 | return 0; |
1065 | } | 1082 | } |
1066 | 1083 | ||
1067 | static void __init sunsab_init_hw(void) | 1084 | static void __devexit sab_remove_one(struct uart_sunsab_port *up) |
1068 | { | 1085 | { |
1069 | int i; | 1086 | uart_remove_one_port(&sunsab_reg, &up->port); |
1070 | 1087 | if (!(up->port.line & 1)) | |
1071 | for (i = 0; i < num_channels; i++) { | 1088 | free_irq(up->port.irq, up); |
1072 | struct uart_sunsab_port *up = &sunsab_ports[i]; | 1089 | of_iounmap(up->port.membase, |
1073 | 1090 | 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 | } | 1091 | } |
1110 | 1092 | ||
1111 | static int __init sunsab_init(void) | 1093 | static int __devexit sab_remove(struct of_device *op) |
1112 | { | 1094 | { |
1113 | int ret = probe_for_sabs(); | 1095 | struct uart_sunsab_port *up = dev_get_drvdata(&op->dev); |
1114 | int i; | ||
1115 | 1096 | ||
1116 | if (ret < 0) | 1097 | sab_remove_one(&up[0]); |
1117 | return ret; | 1098 | sab_remove_one(&up[1]); |
1118 | 1099 | ||
1119 | sunsab_init_hw(); | 1100 | dev_set_drvdata(&op->dev, NULL); |
1120 | 1101 | ||
1121 | sunsab_reg.minor = sunserial_current_minor; | 1102 | return 0; |
1122 | sunsab_reg.nr = num_channels; | 1103 | } |
1123 | |||
1124 | ret = uart_register_driver(&sunsab_reg); | ||
1125 | if (ret < 0) { | ||
1126 | int i; | ||
1127 | 1104 | ||
1128 | for (i = 0; i < num_channels; i++) { | 1105 | static struct of_device_id sab_match[] = { |
1129 | struct uart_sunsab_port *up = &sunsab_ports[i]; | 1106 | { |
1107 | .name = "se", | ||
1108 | }, | ||
1109 | { | ||
1110 | .name = "serial", | ||
1111 | .compatible = "sab82532", | ||
1112 | }, | ||
1113 | {}, | ||
1114 | }; | ||
1115 | MODULE_DEVICE_TABLE(of, sab_match); | ||
1130 | 1116 | ||
1131 | if (!(up->port.line & 0x01)) | 1117 | static struct of_platform_driver sab_driver = { |
1132 | free_irq(up->port.irq, up); | 1118 | .name = "sab", |
1133 | iounmap(up->regs); | 1119 | .match_table = sab_match, |
1134 | } | 1120 | .probe = sab_probe, |
1135 | kfree(sunsab_ports); | 1121 | .remove = __devexit_p(sab_remove), |
1136 | sunsab_ports = NULL; | 1122 | }; |
1137 | 1123 | ||
1138 | return ret; | 1124 | static int __init sunsab_init(void) |
1125 | { | ||
1126 | struct device_node *dp; | ||
1127 | int err; | ||
1128 | |||
1129 | num_channels = 0; | ||
1130 | for_each_node_by_name(dp, "se") | ||
1131 | num_channels += 2; | ||
1132 | for_each_node_by_name(dp, "serial") { | ||
1133 | if (of_device_is_compatible(dp, "sab82532")) | ||
1134 | num_channels += 2; | ||
1139 | } | 1135 | } |
1140 | 1136 | ||
1141 | sunsab_reg.tty_driver->name_base = sunsab_reg.minor - 64; | 1137 | if (num_channels) { |
1138 | sunsab_ports = kzalloc(sizeof(struct uart_sunsab_port) * | ||
1139 | num_channels, GFP_KERNEL); | ||
1140 | if (!sunsab_ports) | ||
1141 | return -ENOMEM; | ||
1142 | 1142 | ||
1143 | sunsab_reg.cons = SUNSAB_CONSOLE(); | 1143 | sunsab_reg.minor = sunserial_current_minor; |
1144 | sunsab_reg.nr = num_channels; | ||
1144 | 1145 | ||
1145 | sunserial_current_minor += num_channels; | 1146 | err = uart_register_driver(&sunsab_reg); |
1146 | 1147 | if (err) { | |
1147 | for (i = 0; i < num_channels; i++) { | 1148 | kfree(sunsab_ports); |
1148 | struct uart_sunsab_port *up = &sunsab_ports[i]; | 1149 | sunsab_ports = NULL; |
1150 | |||
1151 | return err; | ||
1152 | } | ||
1149 | 1153 | ||
1150 | uart_add_one_port(&sunsab_reg, &up->port); | 1154 | sunsab_reg.tty_driver->name_base = sunsab_reg.minor - 64; |
1155 | sunsab_reg.cons = SUNSAB_CONSOLE(); | ||
1156 | sunserial_current_minor += num_channels; | ||
1151 | } | 1157 | } |
1152 | 1158 | ||
1153 | return 0; | 1159 | return of_register_driver(&sab_driver, &of_bus_type); |
1154 | } | 1160 | } |
1155 | 1161 | ||
1156 | static void __exit sunsab_exit(void) | 1162 | static void __exit sunsab_exit(void) |
1157 | { | 1163 | { |
1158 | int i; | 1164 | of_unregister_driver(&sab_driver); |
1159 | 1165 | if (num_channels) { | |
1160 | for (i = 0; i < num_channels; i++) { | 1166 | sunserial_current_minor -= num_channels; |
1161 | struct uart_sunsab_port *up = &sunsab_ports[i]; | 1167 | 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 | } | 1168 | } |
1169 | 1169 | ||
1170 | sunserial_current_minor -= num_channels; | ||
1171 | uart_unregister_driver(&sunsab_reg); | ||
1172 | |||
1173 | kfree(sunsab_ports); | 1170 | kfree(sunsab_ports); |
1174 | sunsab_ports = NULL; | 1171 | sunsab_ports = NULL; |
1175 | } | 1172 | } |