diff options
Diffstat (limited to 'drivers/serial/serial_cs.c')
-rw-r--r-- | drivers/serial/serial_cs.c | 292 |
1 files changed, 215 insertions, 77 deletions
diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index cbf260bc225d..00f9ffd69489 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c | |||
@@ -80,23 +80,16 @@ module_param(buggy_uart, int, 0444); | |||
80 | 80 | ||
81 | /* Table of multi-port card ID's */ | 81 | /* Table of multi-port card ID's */ |
82 | 82 | ||
83 | struct multi_id { | 83 | struct serial_quirk { |
84 | u_short manfid; | 84 | unsigned int manfid; |
85 | u_short prodid; | 85 | unsigned int prodid; |
86 | int multi; /* 1 = multifunction, > 1 = # ports */ | 86 | int multi; /* 1 = multifunction, > 1 = # ports */ |
87 | void (*config)(struct pcmcia_device *); | ||
88 | void (*setup)(struct pcmcia_device *, struct uart_port *); | ||
89 | void (*wakeup)(struct pcmcia_device *); | ||
90 | int (*post)(struct pcmcia_device *); | ||
87 | }; | 91 | }; |
88 | 92 | ||
89 | static const struct multi_id multi_id[] = { | ||
90 | { MANFID_OMEGA, PRODID_OMEGA_QSP_100, 4 }, | ||
91 | { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232, 2 }, | ||
92 | { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D1, 2 }, | ||
93 | { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS232, 4 }, | ||
94 | { MANFID_SOCKET, PRODID_SOCKET_DUAL_RS232, 2 }, | ||
95 | { MANFID_INTEL, PRODID_INTEL_DUAL_RS232, 2 }, | ||
96 | { MANFID_NATINST, PRODID_NATINST_QUAD_RS232, 4 } | ||
97 | }; | ||
98 | #define MULTI_COUNT (sizeof(multi_id)/sizeof(struct multi_id)) | ||
99 | |||
100 | struct serial_info { | 93 | struct serial_info { |
101 | struct pcmcia_device *p_dev; | 94 | struct pcmcia_device *p_dev; |
102 | int ndev; | 95 | int ndev; |
@@ -107,6 +100,7 @@ struct serial_info { | |||
107 | int c950ctrl; | 100 | int c950ctrl; |
108 | dev_node_t node[4]; | 101 | dev_node_t node[4]; |
109 | int line[4]; | 102 | int line[4]; |
103 | const struct serial_quirk *quirk; | ||
110 | }; | 104 | }; |
111 | 105 | ||
112 | struct serial_cfg_mem { | 106 | struct serial_cfg_mem { |
@@ -115,37 +109,165 @@ struct serial_cfg_mem { | |||
115 | u_char buf[256]; | 109 | u_char buf[256]; |
116 | }; | 110 | }; |
117 | 111 | ||
112 | /* | ||
113 | * vers_1 5.0, "Brain Boxes", "2-Port RS232 card", "r6" | ||
114 | * manfid 0x0160, 0x0104 | ||
115 | * This card appears to have a 14.7456MHz clock. | ||
116 | */ | ||
117 | static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port) | ||
118 | { | ||
119 | port->uartclk = 14745600; | ||
120 | } | ||
118 | 121 | ||
119 | static int serial_config(struct pcmcia_device * link); | 122 | static int quirk_post_ibm(struct pcmcia_device *link) |
123 | { | ||
124 | conf_reg_t reg = { 0, CS_READ, 0x800, 0 }; | ||
125 | int last_ret, last_fn; | ||
120 | 126 | ||
127 | last_ret = pcmcia_access_configuration_register(link, ®); | ||
128 | if (last_ret) { | ||
129 | last_fn = AccessConfigurationRegister; | ||
130 | goto cs_failed; | ||
131 | } | ||
132 | reg.Action = CS_WRITE; | ||
133 | reg.Value = reg.Value | 1; | ||
134 | last_ret = pcmcia_access_configuration_register(link, ®); | ||
135 | if (last_ret) { | ||
136 | last_fn = AccessConfigurationRegister; | ||
137 | goto cs_failed; | ||
138 | } | ||
139 | return 0; | ||
121 | 140 | ||
122 | static void wakeup_card(struct serial_info *info) | 141 | cs_failed: |
142 | cs_error(link, last_fn, last_ret); | ||
143 | return -ENODEV; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * Nokia cards are not really multiport cards. Shouldn't this | ||
148 | * be handled by setting the quirk entry .multi = 0 | 1 ? | ||
149 | */ | ||
150 | static void quirk_config_nokia(struct pcmcia_device *link) | ||
123 | { | 151 | { |
124 | int ctrl = info->c950ctrl; | 152 | struct serial_info *info = link->priv; |
125 | 153 | ||
126 | if (info->manfid == MANFID_OXSEMI) { | 154 | if (info->multi > 1) |
127 | outb(12, ctrl + 1); | 155 | info->multi = 1; |
128 | } else if (info->manfid == MANFID_POSSIO && info->prodid == PRODID_POSSIO_GCC) { | 156 | } |
129 | /* request_region? oxsemi branch does no request_region too... */ | 157 | |
130 | /* This sequence is needed to properly initialize MC45 attached to OXCF950. | 158 | static void quirk_wakeup_oxsemi(struct pcmcia_device *link) |
131 | * I tried decreasing these msleep()s, but it worked properly (survived | 159 | { |
132 | * 1000 stop/start operations) with these timeouts (or bigger). */ | 160 | struct serial_info *info = link->priv; |
133 | outb(0xA, ctrl + 1); | 161 | |
134 | msleep(100); | 162 | outb(12, info->c950ctrl + 1); |
135 | outb(0xE, ctrl + 1); | 163 | } |
136 | msleep(300); | 164 | |
137 | outb(0xC, ctrl + 1); | 165 | /* request_region? oxsemi branch does no request_region too... */ |
138 | msleep(100); | 166 | /* |
139 | outb(0xE, ctrl + 1); | 167 | * This sequence is needed to properly initialize MC45 attached to OXCF950. |
140 | msleep(200); | 168 | * I tried decreasing these msleep()s, but it worked properly (survived |
141 | outb(0xF, ctrl + 1); | 169 | * 1000 stop/start operations) with these timeouts (or bigger). |
142 | msleep(100); | 170 | */ |
143 | outb(0xE, ctrl + 1); | 171 | static void quirk_wakeup_possio_gcc(struct pcmcia_device *link) |
144 | msleep(100); | 172 | { |
145 | outb(0xC, ctrl + 1); | 173 | struct serial_info *info = link->priv; |
174 | unsigned int ctrl = info->c950ctrl; | ||
175 | |||
176 | outb(0xA, ctrl + 1); | ||
177 | msleep(100); | ||
178 | outb(0xE, ctrl + 1); | ||
179 | msleep(300); | ||
180 | outb(0xC, ctrl + 1); | ||
181 | msleep(100); | ||
182 | outb(0xE, ctrl + 1); | ||
183 | msleep(200); | ||
184 | outb(0xF, ctrl + 1); | ||
185 | msleep(100); | ||
186 | outb(0xE, ctrl + 1); | ||
187 | msleep(100); | ||
188 | outb(0xC, ctrl + 1); | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * Socket Dual IO: this enables irq's for second port | ||
193 | */ | ||
194 | static void quirk_config_socket(struct pcmcia_device *link) | ||
195 | { | ||
196 | struct serial_info *info = link->priv; | ||
197 | |||
198 | if (info->multi) { | ||
199 | link->conf.Present |= PRESENT_EXT_STATUS; | ||
200 | link->conf.ExtStatus = ESR_REQ_ATTN_ENA; | ||
146 | } | 201 | } |
147 | } | 202 | } |
148 | 203 | ||
204 | static const struct serial_quirk quirks[] = { | ||
205 | { | ||
206 | .manfid = 0x0160, | ||
207 | .prodid = 0x0104, | ||
208 | .multi = -1, | ||
209 | .setup = quirk_setup_brainboxes_0104, | ||
210 | }, { | ||
211 | .manfid = MANFID_IBM, | ||
212 | .prodid = ~0, | ||
213 | .multi = -1, | ||
214 | .post = quirk_post_ibm, | ||
215 | }, { | ||
216 | .manfid = MANFID_INTEL, | ||
217 | .prodid = PRODID_INTEL_DUAL_RS232, | ||
218 | .multi = 2, | ||
219 | }, { | ||
220 | .manfid = MANFID_NATINST, | ||
221 | .prodid = PRODID_NATINST_QUAD_RS232, | ||
222 | .multi = 4, | ||
223 | }, { | ||
224 | .manfid = MANFID_NOKIA, | ||
225 | .prodid = ~0, | ||
226 | .multi = -1, | ||
227 | .config = quirk_config_nokia, | ||
228 | }, { | ||
229 | .manfid = MANFID_OMEGA, | ||
230 | .prodid = PRODID_OMEGA_QSP_100, | ||
231 | .multi = 4, | ||
232 | }, { | ||
233 | .manfid = MANFID_OXSEMI, | ||
234 | .prodid = ~0, | ||
235 | .multi = -1, | ||
236 | .wakeup = quirk_wakeup_oxsemi, | ||
237 | }, { | ||
238 | .manfid = MANFID_POSSIO, | ||
239 | .prodid = PRODID_POSSIO_GCC, | ||
240 | .multi = -1, | ||
241 | .wakeup = quirk_wakeup_possio_gcc, | ||
242 | }, { | ||
243 | .manfid = MANFID_QUATECH, | ||
244 | .prodid = PRODID_QUATECH_DUAL_RS232, | ||
245 | .multi = 2, | ||
246 | }, { | ||
247 | .manfid = MANFID_QUATECH, | ||
248 | .prodid = PRODID_QUATECH_DUAL_RS232_D1, | ||
249 | .multi = 2, | ||
250 | }, { | ||
251 | .manfid = MANFID_QUATECH, | ||
252 | .prodid = PRODID_QUATECH_QUAD_RS232, | ||
253 | .multi = 4, | ||
254 | }, { | ||
255 | .manfid = MANFID_SOCKET, | ||
256 | .prodid = PRODID_SOCKET_DUAL_RS232, | ||
257 | .multi = 2, | ||
258 | .config = quirk_config_socket, | ||
259 | }, { | ||
260 | .manfid = MANFID_SOCKET, | ||
261 | .prodid = ~0, | ||
262 | .multi = -1, | ||
263 | .config = quirk_config_socket, | ||
264 | } | ||
265 | }; | ||
266 | |||
267 | |||
268 | static int serial_config(struct pcmcia_device * link); | ||
269 | |||
270 | |||
149 | /*====================================================================== | 271 | /*====================================================================== |
150 | 272 | ||
151 | After a card is removed, serial_remove() will unregister | 273 | After a card is removed, serial_remove() will unregister |
@@ -185,14 +307,14 @@ static int serial_suspend(struct pcmcia_device *link) | |||
185 | 307 | ||
186 | static int serial_resume(struct pcmcia_device *link) | 308 | static int serial_resume(struct pcmcia_device *link) |
187 | { | 309 | { |
188 | if (pcmcia_dev_present(link)) { | 310 | struct serial_info *info = link->priv; |
189 | struct serial_info *info = link->priv; | 311 | int i; |
190 | int i; | ||
191 | 312 | ||
192 | for (i = 0; i < info->ndev; i++) | 313 | for (i = 0; i < info->ndev; i++) |
193 | serial8250_resume_port(info->line[i]); | 314 | serial8250_resume_port(info->line[i]); |
194 | wakeup_card(info); | 315 | |
195 | } | 316 | if (info->quirk && info->quirk->wakeup) |
317 | info->quirk->wakeup(link); | ||
196 | 318 | ||
197 | return 0; | 319 | return 0; |
198 | } | 320 | } |
@@ -278,6 +400,10 @@ static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, | |||
278 | port.dev = &handle_to_dev(handle); | 400 | port.dev = &handle_to_dev(handle); |
279 | if (buggy_uart) | 401 | if (buggy_uart) |
280 | port.flags |= UPF_BUGGY_UART; | 402 | port.flags |= UPF_BUGGY_UART; |
403 | |||
404 | if (info->quirk && info->quirk->setup) | ||
405 | info->quirk->setup(handle, &port); | ||
406 | |||
281 | line = serial8250_register_port(&port); | 407 | line = serial8250_register_port(&port); |
282 | if (line < 0) { | 408 | if (line < 0) { |
283 | printk(KERN_NOTICE "serial_cs: serial8250_register_port() at " | 409 | printk(KERN_NOTICE "serial_cs: serial8250_register_port() at " |
@@ -433,6 +559,13 @@ next_entry: | |||
433 | } | 559 | } |
434 | if (info->multi && (info->manfid == MANFID_3COM)) | 560 | if (info->multi && (info->manfid == MANFID_3COM)) |
435 | link->conf.ConfigIndex &= ~(0x08); | 561 | link->conf.ConfigIndex &= ~(0x08); |
562 | |||
563 | /* | ||
564 | * Apply any configuration quirks. | ||
565 | */ | ||
566 | if (info->quirk && info->quirk->config) | ||
567 | info->quirk->config(link); | ||
568 | |||
436 | i = pcmcia_request_configuration(link, &link->conf); | 569 | i = pcmcia_request_configuration(link, &link->conf); |
437 | if (i != CS_SUCCESS) { | 570 | if (i != CS_SUCCESS) { |
438 | cs_error(link, RequestConfiguration, i); | 571 | cs_error(link, RequestConfiguration, i); |
@@ -521,11 +654,13 @@ static int multi_config(struct pcmcia_device * link) | |||
521 | cs_error(link, RequestIRQ, i); | 654 | cs_error(link, RequestIRQ, i); |
522 | link->irq.AssignedIRQ = 0; | 655 | link->irq.AssignedIRQ = 0; |
523 | } | 656 | } |
524 | /* Socket Dual IO: this enables irq's for second port */ | 657 | |
525 | if (info->multi && (info->manfid == MANFID_SOCKET)) { | 658 | /* |
526 | link->conf.Present |= PRESENT_EXT_STATUS; | 659 | * Apply any configuration quirks. |
527 | link->conf.ExtStatus = ESR_REQ_ATTN_ENA; | 660 | */ |
528 | } | 661 | if (info->quirk && info->quirk->config) |
662 | info->quirk->config(link); | ||
663 | |||
529 | i = pcmcia_request_configuration(link, &link->conf); | 664 | i = pcmcia_request_configuration(link, &link->conf); |
530 | if (i != CS_SUCCESS) { | 665 | if (i != CS_SUCCESS) { |
531 | cs_error(link, RequestConfiguration, i); | 666 | cs_error(link, RequestConfiguration, i); |
@@ -550,17 +685,19 @@ static int multi_config(struct pcmcia_device * link) | |||
550 | link->irq.AssignedIRQ); | 685 | link->irq.AssignedIRQ); |
551 | } | 686 | } |
552 | info->c950ctrl = base2; | 687 | info->c950ctrl = base2; |
553 | wakeup_card(info); | 688 | |
689 | /* | ||
690 | * FIXME: We really should wake up the port prior to | ||
691 | * handing it over to the serial layer. | ||
692 | */ | ||
693 | if (info->quirk && info->quirk->wakeup) | ||
694 | info->quirk->wakeup(link); | ||
695 | |||
554 | rc = 0; | 696 | rc = 0; |
555 | goto free_cfg_mem; | 697 | goto free_cfg_mem; |
556 | } | 698 | } |
557 | 699 | ||
558 | setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ); | 700 | setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ); |
559 | /* The Nokia cards are not really multiport cards */ | ||
560 | if (info->manfid == MANFID_NOKIA) { | ||
561 | rc = 0; | ||
562 | goto free_cfg_mem; | ||
563 | } | ||
564 | for (i = 0; i < info->multi - 1; i++) | 701 | for (i = 0; i < info->multi - 1; i++) |
565 | setup_serial(link, info, base2 + (8 * i), | 702 | setup_serial(link, info, base2 + (8 * i), |
566 | link->irq.AssignedIRQ); | 703 | link->irq.AssignedIRQ); |
@@ -622,13 +759,16 @@ static int serial_config(struct pcmcia_device * link) | |||
622 | tuple->DesiredTuple = CISTPL_MANFID; | 759 | tuple->DesiredTuple = CISTPL_MANFID; |
623 | if (first_tuple(link, tuple, parse) == CS_SUCCESS) { | 760 | if (first_tuple(link, tuple, parse) == CS_SUCCESS) { |
624 | info->manfid = parse->manfid.manf; | 761 | info->manfid = parse->manfid.manf; |
625 | info->prodid = le16_to_cpu(buf[1]); | 762 | info->prodid = parse->manfid.card; |
626 | for (i = 0; i < MULTI_COUNT; i++) | 763 | |
627 | if ((info->manfid == multi_id[i].manfid) && | 764 | for (i = 0; i < ARRAY_SIZE(quirks); i++) |
628 | (parse->manfid.card == multi_id[i].prodid)) | 765 | if ((quirks[i].manfid == ~0 || |
766 | quirks[i].manfid == info->manfid) && | ||
767 | (quirks[i].prodid == ~0 || | ||
768 | quirks[i].prodid == info->prodid)) { | ||
769 | info->quirk = &quirks[i]; | ||
629 | break; | 770 | break; |
630 | if (i < MULTI_COUNT) | 771 | } |
631 | info->multi = multi_id[i].multi; | ||
632 | } | 772 | } |
633 | 773 | ||
634 | /* Another check for dual-serial cards: look for either serial or | 774 | /* Another check for dual-serial cards: look for either serial or |
@@ -648,6 +788,12 @@ static int serial_config(struct pcmcia_device * link) | |||
648 | } | 788 | } |
649 | } | 789 | } |
650 | 790 | ||
791 | /* | ||
792 | * Apply any multi-port quirk. | ||
793 | */ | ||
794 | if (info->quirk && info->quirk->multi != -1) | ||
795 | info->multi = info->quirk->multi; | ||
796 | |||
651 | if (info->multi > 1) | 797 | if (info->multi > 1) |
652 | multi_config(link); | 798 | multi_config(link); |
653 | else | 799 | else |
@@ -656,21 +802,13 @@ static int serial_config(struct pcmcia_device * link) | |||
656 | if (info->ndev == 0) | 802 | if (info->ndev == 0) |
657 | goto failed; | 803 | goto failed; |
658 | 804 | ||
659 | if (info->manfid == MANFID_IBM) { | 805 | /* |
660 | conf_reg_t reg = { 0, CS_READ, 0x800, 0 }; | 806 | * Apply any post-init quirk. FIXME: This should really happen |
661 | last_ret = pcmcia_access_configuration_register(link, ®); | 807 | * before we register the port, since it might already be in use. |
662 | if (last_ret) { | 808 | */ |
663 | last_fn = AccessConfigurationRegister; | 809 | if (info->quirk && info->quirk->post) |
664 | goto cs_failed; | 810 | if (info->quirk->post(link)) |
665 | } | 811 | goto failed; |
666 | reg.Action = CS_WRITE; | ||
667 | reg.Value = reg.Value | 1; | ||
668 | last_ret = pcmcia_access_configuration_register(link, ®); | ||
669 | if (last_ret) { | ||
670 | last_fn = AccessConfigurationRegister; | ||
671 | goto cs_failed; | ||
672 | } | ||
673 | } | ||
674 | 812 | ||
675 | link->dev_node = &info->node[0]; | 813 | link->dev_node = &info->node[0]; |
676 | kfree(cfg_mem); | 814 | kfree(cfg_mem); |