diff options
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/hvc/Kconfig | 5 | ||||
-rw-r--r-- | drivers/tty/hvc/Makefile | 3 | ||||
-rw-r--r-- | drivers/tty/hvc/hvc_console.c | 23 | ||||
-rw-r--r-- | drivers/tty/hvc/hvc_console.h | 4 | ||||
-rw-r--r-- | drivers/tty/hvc/hvc_vio.c | 725 |
5 files changed, 725 insertions, 35 deletions
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index 6f2c9809f1fb..e371753ba921 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig | |||
@@ -19,6 +19,11 @@ config HVC_CONSOLE | |||
19 | console. This driver allows each pSeries partition to have a console | 19 | console. This driver allows each pSeries partition to have a console |
20 | which is accessed via the HMC. | 20 | which is accessed via the HMC. |
21 | 21 | ||
22 | config HVC_OLD_HVSI | ||
23 | bool "Old driver for pSeries serial port (/dev/hvsi*)" | ||
24 | depends on HVC_CONSOLE | ||
25 | default n | ||
26 | |||
22 | config HVC_ISERIES | 27 | config HVC_ISERIES |
23 | bool "iSeries Hypervisor Virtual Console support" | 28 | bool "iSeries Hypervisor Virtual Console support" |
24 | depends on PPC_ISERIES | 29 | depends on PPC_ISERIES |
diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile index 40a25d93fe52..69a444b71c63 100644 --- a/drivers/tty/hvc/Makefile +++ b/drivers/tty/hvc/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi.o | 1 | obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o |
2 | obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o | ||
2 | obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o | 3 | obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o |
3 | obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o | 4 | obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o |
4 | obj-$(CONFIG_HVC_TILE) += hvc_tile.o | 5 | obj-$(CONFIG_HVC_TILE) += hvc_tile.o |
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index e9cba13ee800..f8ff6f50fc35 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c | |||
@@ -184,7 +184,7 @@ static struct tty_driver *hvc_console_device(struct console *c, int *index) | |||
184 | } | 184 | } |
185 | 185 | ||
186 | static int __init hvc_console_setup(struct console *co, char *options) | 186 | static int __init hvc_console_setup(struct console *co, char *options) |
187 | { | 187 | { |
188 | if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES) | 188 | if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES) |
189 | return -ENODEV; | 189 | return -ENODEV; |
190 | 190 | ||
@@ -745,6 +745,25 @@ static int khvcd(void *unused) | |||
745 | return 0; | 745 | return 0; |
746 | } | 746 | } |
747 | 747 | ||
748 | static int hvc_tiocmget(struct tty_struct *tty) | ||
749 | { | ||
750 | struct hvc_struct *hp = tty->driver_data; | ||
751 | |||
752 | if (!hp || !hp->ops->tiocmget) | ||
753 | return -EINVAL; | ||
754 | return hp->ops->tiocmget(hp); | ||
755 | } | ||
756 | |||
757 | static int hvc_tiocmset(struct tty_struct *tty, | ||
758 | unsigned int set, unsigned int clear) | ||
759 | { | ||
760 | struct hvc_struct *hp = tty->driver_data; | ||
761 | |||
762 | if (!hp || !hp->ops->tiocmset) | ||
763 | return -EINVAL; | ||
764 | return hp->ops->tiocmset(hp, set, clear); | ||
765 | } | ||
766 | |||
748 | static const struct tty_operations hvc_ops = { | 767 | static const struct tty_operations hvc_ops = { |
749 | .open = hvc_open, | 768 | .open = hvc_open, |
750 | .close = hvc_close, | 769 | .close = hvc_close, |
@@ -753,6 +772,8 @@ static const struct tty_operations hvc_ops = { | |||
753 | .unthrottle = hvc_unthrottle, | 772 | .unthrottle = hvc_unthrottle, |
754 | .write_room = hvc_write_room, | 773 | .write_room = hvc_write_room, |
755 | .chars_in_buffer = hvc_chars_in_buffer, | 774 | .chars_in_buffer = hvc_chars_in_buffer, |
775 | .tiocmget = hvc_tiocmget, | ||
776 | .tiocmset = hvc_tiocmset, | ||
756 | }; | 777 | }; |
757 | 778 | ||
758 | struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, | 779 | struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, |
diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h index 54381eba4e4a..c335a1492a54 100644 --- a/drivers/tty/hvc/hvc_console.h +++ b/drivers/tty/hvc/hvc_console.h | |||
@@ -73,6 +73,10 @@ struct hv_ops { | |||
73 | int (*notifier_add)(struct hvc_struct *hp, int irq); | 73 | int (*notifier_add)(struct hvc_struct *hp, int irq); |
74 | void (*notifier_del)(struct hvc_struct *hp, int irq); | 74 | void (*notifier_del)(struct hvc_struct *hp, int irq); |
75 | void (*notifier_hangup)(struct hvc_struct *hp, int irq); | 75 | void (*notifier_hangup)(struct hvc_struct *hp, int irq); |
76 | |||
77 | /* tiocmget/set implementation */ | ||
78 | int (*tiocmget)(struct hvc_struct *hp); | ||
79 | int (*tiocmset)(struct hvc_struct *hp, unsigned int set, unsigned int clear); | ||
76 | }; | 80 | }; |
77 | 81 | ||
78 | /* Register a vterm and a slot index for use as a console (console_init) */ | 82 | /* Register a vterm and a slot index for use as a console (console_init) */ |
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index e6eea1485244..d4e0850e8051 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c | |||
@@ -27,15 +27,27 @@ | |||
27 | * You should have received a copy of the GNU General Public License | 27 | * You should have received a copy of the GNU General Public License |
28 | * along with this program; if not, write to the Free Software | 28 | * along with this program; if not, write to the Free Software |
29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
30 | * | ||
31 | * TODO: | ||
32 | * | ||
33 | * - handle error in sending hvsi protocol packets | ||
34 | * - retry nego on subsequent sends ? | ||
30 | */ | 35 | */ |
31 | 36 | ||
37 | #undef DEBUG | ||
38 | |||
32 | #include <linux/types.h> | 39 | #include <linux/types.h> |
33 | #include <linux/init.h> | 40 | #include <linux/init.h> |
41 | #include <linux/delay.h> | ||
42 | #include <linux/slab.h> | ||
43 | #include <linux/console.h> | ||
34 | 44 | ||
35 | #include <asm/hvconsole.h> | 45 | #include <asm/hvconsole.h> |
36 | #include <asm/vio.h> | 46 | #include <asm/vio.h> |
37 | #include <asm/prom.h> | 47 | #include <asm/prom.h> |
38 | #include <asm/firmware.h> | 48 | #include <asm/firmware.h> |
49 | #include <asm/hvsi.h> | ||
50 | #include <asm/udbg.h> | ||
39 | 51 | ||
40 | #include "hvc_console.h" | 52 | #include "hvc_console.h" |
41 | 53 | ||
@@ -43,14 +55,47 @@ static const char hvc_driver_name[] = "hvc_console"; | |||
43 | 55 | ||
44 | static struct vio_device_id hvc_driver_table[] __devinitdata = { | 56 | static struct vio_device_id hvc_driver_table[] __devinitdata = { |
45 | {"serial", "hvterm1"}, | 57 | {"serial", "hvterm1"}, |
58 | #ifndef HVC_OLD_HVSI | ||
59 | {"serial", "hvterm-protocol"}, | ||
60 | #endif | ||
46 | { "", "" } | 61 | { "", "" } |
47 | }; | 62 | }; |
48 | MODULE_DEVICE_TABLE(vio, hvc_driver_table); | 63 | MODULE_DEVICE_TABLE(vio, hvc_driver_table); |
49 | 64 | ||
50 | static int filtered_get_chars(uint32_t vtermno, char *buf, int count) | 65 | typedef enum hv_protocol { |
66 | HV_PROTOCOL_RAW, | ||
67 | HV_PROTOCOL_HVSI | ||
68 | } hv_protocol_t; | ||
69 | |||
70 | #define HV_INBUF_SIZE 255 | ||
71 | |||
72 | struct hvterm_priv { | ||
73 | u32 termno; /* HV term number */ | ||
74 | hv_protocol_t proto; /* Raw data or HVSI packets */ | ||
75 | unsigned int inbuf_len; /* Data in input buffer */ | ||
76 | unsigned char inbuf[HV_INBUF_SIZE]; | ||
77 | unsigned int inbuf_cur; /* Cursor in input buffer */ | ||
78 | unsigned int inbuf_pktlen; /* HVSI packet lenght from cursor */ | ||
79 | atomic_t seqno; /* HVSI packet sequence number */ | ||
80 | unsigned int opened:1; /* HVSI driver opened */ | ||
81 | unsigned int established:1; /* HVSI protocol established */ | ||
82 | unsigned int is_console:1; /* Used as a kernel console device */ | ||
83 | unsigned int mctrl_update:1; /* HVSI modem control updated */ | ||
84 | unsigned short mctrl; /* HVSI modem control */ | ||
85 | struct tty_struct *tty; /* TTY structure */ | ||
86 | }; | ||
87 | static struct hvterm_priv *hvterm_privs[MAX_NR_HVC_CONSOLES]; | ||
88 | |||
89 | /* For early boot console */ | ||
90 | static struct hvterm_priv hvterm_priv0; | ||
91 | |||
92 | static int hvterm_raw_get_chars(uint32_t vtermno, char *buf, int count) | ||
51 | { | 93 | { |
52 | unsigned long got; | 94 | struct hvterm_priv *pv = hvterm_privs[vtermno]; |
53 | int i; | 95 | unsigned long got, i; |
96 | |||
97 | if (WARN_ON(!pv)) | ||
98 | return 0; | ||
54 | 99 | ||
55 | /* | 100 | /* |
56 | * Vio firmware will read up to SIZE_VIO_GET_CHARS at its own discretion | 101 | * Vio firmware will read up to SIZE_VIO_GET_CHARS at its own discretion |
@@ -60,7 +105,7 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count) | |||
60 | if (count < SIZE_VIO_GET_CHARS) | 105 | if (count < SIZE_VIO_GET_CHARS) |
61 | return -EAGAIN; | 106 | return -EAGAIN; |
62 | 107 | ||
63 | got = hvc_get_chars(vtermno, buf, count); | 108 | got = hvc_get_chars(pv->termno, buf, count); |
64 | 109 | ||
65 | /* | 110 | /* |
66 | * Work around a HV bug where it gives us a null | 111 | * Work around a HV bug where it gives us a null |
@@ -70,32 +115,527 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count) | |||
70 | if (buf[i] == 0 && buf[i-1] == '\r') { | 115 | if (buf[i] == 0 && buf[i-1] == '\r') { |
71 | --got; | 116 | --got; |
72 | if (i < got) | 117 | if (i < got) |
73 | memmove(&buf[i], &buf[i+1], | 118 | memmove(&buf[i], &buf[i+1], got - i); |
74 | got - i); | ||
75 | } | 119 | } |
76 | } | 120 | } |
77 | return got; | 121 | return got; |
78 | } | 122 | } |
79 | 123 | ||
80 | static const struct hv_ops hvc_get_put_ops = { | 124 | static int hvterm_raw_put_chars(uint32_t vtermno, const char *buf, int count) |
81 | .get_chars = filtered_get_chars, | 125 | { |
82 | .put_chars = hvc_put_chars, | 126 | struct hvterm_priv *pv = hvterm_privs[vtermno]; |
127 | |||
128 | if (WARN_ON(!pv)) | ||
129 | return 0; | ||
130 | |||
131 | return hvc_put_chars(pv->termno, buf, count); | ||
132 | } | ||
133 | |||
134 | static const struct hv_ops hvterm_raw_ops = { | ||
135 | .get_chars = hvterm_raw_get_chars, | ||
136 | .put_chars = hvterm_raw_put_chars, | ||
83 | .notifier_add = notifier_add_irq, | 137 | .notifier_add = notifier_add_irq, |
84 | .notifier_del = notifier_del_irq, | 138 | .notifier_del = notifier_del_irq, |
85 | .notifier_hangup = notifier_hangup_irq, | 139 | .notifier_hangup = notifier_hangup_irq, |
86 | }; | 140 | }; |
87 | 141 | ||
142 | static int hvterm_hvsi_send_packet(struct hvterm_priv *pv, struct hvsi_header *packet) | ||
143 | { | ||
144 | packet->seqno = atomic_inc_return(&pv->seqno); | ||
145 | |||
146 | /* Assumes that always succeeds, works in practice */ | ||
147 | return hvc_put_chars(pv->termno, (char *)packet, packet->len); | ||
148 | } | ||
149 | |||
150 | static void hvterm_hvsi_start_handshake(struct hvterm_priv *pv) | ||
151 | { | ||
152 | struct hvsi_query q; | ||
153 | |||
154 | /* Reset state */ | ||
155 | pv->established = 0; | ||
156 | atomic_set(&pv->seqno, 0); | ||
157 | |||
158 | pr_devel("HVSI@%x: Handshaking started\n", pv->termno); | ||
159 | |||
160 | /* Send version query */ | ||
161 | q.hdr.type = VS_QUERY_PACKET_HEADER; | ||
162 | q.hdr.len = sizeof(struct hvsi_query); | ||
163 | q.verb = VSV_SEND_VERSION_NUMBER; | ||
164 | hvterm_hvsi_send_packet(pv, &q.hdr); | ||
165 | } | ||
166 | |||
167 | static int hvterm_hvsi_send_close(struct hvterm_priv *pv) | ||
168 | { | ||
169 | struct hvsi_control ctrl; | ||
170 | |||
171 | pv->established = 0; | ||
172 | |||
173 | ctrl.hdr.type = VS_CONTROL_PACKET_HEADER; | ||
174 | ctrl.hdr.len = sizeof(struct hvsi_control); | ||
175 | ctrl.verb = VSV_CLOSE_PROTOCOL; | ||
176 | return hvterm_hvsi_send_packet(pv, &ctrl.hdr); | ||
177 | } | ||
178 | |||
179 | static void hvterm_cd_change(struct hvterm_priv *pv, int cd) | ||
180 | { | ||
181 | if (cd) | ||
182 | pv->mctrl |= TIOCM_CD; | ||
183 | else { | ||
184 | pv->mctrl &= ~TIOCM_CD; | ||
185 | |||
186 | /* We copy the existing hvsi driver semantics | ||
187 | * here which are to trigger a hangup when | ||
188 | * we get a carrier loss. | ||
189 | * Closing our connection to the server will | ||
190 | * do just that. | ||
191 | */ | ||
192 | if (!pv->is_console && pv->opened) { | ||
193 | pr_devel("HVSI@%x Carrier lost, hanging up !\n", | ||
194 | pv->termno); | ||
195 | hvterm_hvsi_send_close(pv); | ||
196 | } | ||
197 | } | ||
198 | } | ||
199 | |||
200 | static void hvterm_hvsi_got_control(struct hvterm_priv *pv) | ||
201 | { | ||
202 | struct hvsi_control *pkt = (struct hvsi_control *)pv->inbuf; | ||
203 | |||
204 | switch (pkt->verb) { | ||
205 | case VSV_CLOSE_PROTOCOL: | ||
206 | /* We restart the handshaking */ | ||
207 | hvterm_hvsi_start_handshake(pv); | ||
208 | break; | ||
209 | case VSV_MODEM_CTL_UPDATE: | ||
210 | /* Transition of carrier detect */ | ||
211 | hvterm_cd_change(pv, pkt->word & HVSI_TSCD); | ||
212 | break; | ||
213 | } | ||
214 | } | ||
215 | |||
216 | static void hvterm_hvsi_got_query(struct hvterm_priv *pv) | ||
217 | { | ||
218 | struct hvsi_query *pkt = (struct hvsi_query *)pv->inbuf; | ||
219 | struct hvsi_query_response r; | ||
220 | |||
221 | /* We only handle version queries */ | ||
222 | if (pkt->verb != VSV_SEND_VERSION_NUMBER) | ||
223 | return; | ||
224 | |||
225 | pr_devel("HVSI@%x: Got version query, sending response...\n", | ||
226 | pv->termno); | ||
227 | |||
228 | /* Send version response */ | ||
229 | r.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER; | ||
230 | r.hdr.len = sizeof(struct hvsi_query_response); | ||
231 | r.verb = VSV_SEND_VERSION_NUMBER; | ||
232 | r.u.version = HVSI_VERSION; | ||
233 | r.query_seqno = pkt->hdr.seqno; | ||
234 | hvterm_hvsi_send_packet(pv, &r.hdr); | ||
235 | |||
236 | /* Assume protocol is open now */ | ||
237 | pv->established = 1; | ||
238 | } | ||
239 | |||
240 | static void hvterm_hvsi_got_response(struct hvterm_priv *pv) | ||
241 | { | ||
242 | struct hvsi_query_response *r = (struct hvsi_query_response *)pv->inbuf; | ||
243 | |||
244 | switch(r->verb) { | ||
245 | case VSV_SEND_MODEM_CTL_STATUS: | ||
246 | hvterm_cd_change(pv, r->u.mctrl_word & HVSI_TSCD); | ||
247 | pv->mctrl_update = 1; | ||
248 | break; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | static int hvterm_hvsi_check_packet(struct hvterm_priv *pv) | ||
253 | { | ||
254 | u8 len, type; | ||
255 | |||
256 | /* Check header validity. If it's invalid, we ditch | ||
257 | * the whole buffer and hope we eventually resync | ||
258 | */ | ||
259 | if (pv->inbuf[0] < 0xfc) { | ||
260 | pv->inbuf_len = pv->inbuf_pktlen = 0; | ||
261 | return 0; | ||
262 | } | ||
263 | type = pv->inbuf[0]; | ||
264 | len = pv->inbuf[1]; | ||
265 | |||
266 | /* Packet incomplete ? */ | ||
267 | if (pv->inbuf_len < len) | ||
268 | return 0; | ||
269 | |||
270 | pr_devel("HVSI@%x: Got packet type %x len %d bytes:\n", | ||
271 | pv->termno, type, len); | ||
272 | |||
273 | /* We have a packet, yay ! Handle it */ | ||
274 | switch(type) { | ||
275 | case VS_DATA_PACKET_HEADER: | ||
276 | pv->inbuf_pktlen = len - 4; | ||
277 | pv->inbuf_cur = 4; | ||
278 | return 1; | ||
279 | case VS_CONTROL_PACKET_HEADER: | ||
280 | hvterm_hvsi_got_control(pv); | ||
281 | break; | ||
282 | case VS_QUERY_PACKET_HEADER: | ||
283 | hvterm_hvsi_got_query(pv); | ||
284 | break; | ||
285 | case VS_QUERY_RESPONSE_PACKET_HEADER: | ||
286 | hvterm_hvsi_got_response(pv); | ||
287 | break; | ||
288 | } | ||
289 | |||
290 | /* Swallow packet and retry */ | ||
291 | pv->inbuf_len -= len; | ||
292 | memmove(pv->inbuf, &pv->inbuf[len], pv->inbuf_len); | ||
293 | return 1; | ||
294 | } | ||
295 | |||
296 | static int hvterm_hvsi_get_packet(struct hvterm_priv *pv) | ||
297 | { | ||
298 | /* If we have room in the buffer, ask HV for more */ | ||
299 | if (pv->inbuf_len < HV_INBUF_SIZE) | ||
300 | pv->inbuf_len += hvc_get_chars(pv->termno, | ||
301 | &pv->inbuf[pv->inbuf_len], | ||
302 | HV_INBUF_SIZE - pv->inbuf_len); | ||
303 | /* | ||
304 | * If we have at least 4 bytes in the buffer, check for | ||
305 | * a full packet and retry | ||
306 | */ | ||
307 | if (pv->inbuf_len >= 4) | ||
308 | return hvterm_hvsi_check_packet(pv); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static int hvterm_hvsi_get_chars(uint32_t vtermno, char *buf, int count) | ||
313 | { | ||
314 | struct hvterm_priv *pv = hvterm_privs[vtermno]; | ||
315 | unsigned int tries, read = 0; | ||
316 | |||
317 | if (WARN_ON(!pv)) | ||
318 | return 0; | ||
319 | |||
320 | /* If we aren't open, dont do anything in order to avoid races | ||
321 | * with connection establishment. The hvc core will call this | ||
322 | * before we have returned from notifier_add(), and we need to | ||
323 | * avoid multiple users playing with the receive buffer | ||
324 | */ | ||
325 | if (!pv->opened) | ||
326 | return 0; | ||
327 | |||
328 | /* We try twice, once with what data we have and once more | ||
329 | * after we try to fetch some more from the hypervisor | ||
330 | */ | ||
331 | for (tries = 1; count && tries < 2; tries++) { | ||
332 | /* Consume existing data packet */ | ||
333 | if (pv->inbuf_pktlen) { | ||
334 | unsigned int l = min(count, (int)pv->inbuf_pktlen); | ||
335 | memcpy(&buf[read], &pv->inbuf[pv->inbuf_cur], l); | ||
336 | pv->inbuf_cur += l; | ||
337 | pv->inbuf_pktlen -= l; | ||
338 | count -= l; | ||
339 | read += l; | ||
340 | } | ||
341 | if (count == 0) | ||
342 | break; | ||
343 | |||
344 | /* Data packet fully consumed, move down remaning data */ | ||
345 | if (pv->inbuf_cur) { | ||
346 | pv->inbuf_len -= pv->inbuf_cur; | ||
347 | memmove(pv->inbuf, &pv->inbuf[pv->inbuf_cur], pv->inbuf_len); | ||
348 | pv->inbuf_cur = 0; | ||
349 | } | ||
350 | |||
351 | /* Try to get another packet */ | ||
352 | if (hvterm_hvsi_get_packet(pv)) | ||
353 | tries--; | ||
354 | } | ||
355 | if (!pv->established) { | ||
356 | pr_devel("HVSI@%x: returning -EPIPE\n", pv->termno); | ||
357 | return -EPIPE; | ||
358 | } | ||
359 | return read; | ||
360 | } | ||
361 | |||
362 | static int hvterm_hvsi_put_chars(uint32_t vtermno, const char *buf, int count) | ||
363 | { | ||
364 | struct hvterm_priv *pv = hvterm_privs[vtermno]; | ||
365 | struct hvsi_data dp; | ||
366 | int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA); | ||
367 | |||
368 | if (WARN_ON(!pv)) | ||
369 | return 0; | ||
370 | |||
371 | dp.hdr.type = VS_DATA_PACKET_HEADER; | ||
372 | dp.hdr.len = adjcount + sizeof(struct hvsi_header); | ||
373 | memcpy(dp.data, buf, adjcount); | ||
374 | rc = hvterm_hvsi_send_packet(pv, &dp.hdr); | ||
375 | if (rc <= 0) | ||
376 | return rc; | ||
377 | return adjcount; | ||
378 | } | ||
379 | |||
380 | static void maybe_msleep(unsigned long ms) | ||
381 | { | ||
382 | /* During early boot, IRQs are disabled, use mdelay */ | ||
383 | if (irqs_disabled()) | ||
384 | mdelay(ms); | ||
385 | else | ||
386 | msleep(ms); | ||
387 | } | ||
388 | |||
389 | static int hvterm_hvsi_read_mctrl(struct hvterm_priv *pv) | ||
390 | { | ||
391 | struct hvsi_query q; | ||
392 | int rc, timeout; | ||
393 | |||
394 | pr_devel("HVSI@%x: Querying modem control status...\n", | ||
395 | pv->termno); | ||
396 | |||
397 | pv->mctrl_update = 0; | ||
398 | q.hdr.type = VS_QUERY_PACKET_HEADER; | ||
399 | q.hdr.len = sizeof(struct hvsi_query); | ||
400 | q.hdr.seqno = atomic_inc_return(&pv->seqno); | ||
401 | q.verb = VSV_SEND_MODEM_CTL_STATUS; | ||
402 | rc = hvterm_hvsi_send_packet(pv, &q.hdr); | ||
403 | if (rc <= 0) { | ||
404 | pr_devel("HVSI@%x: Error %d...\n", pv->termno, rc); | ||
405 | return rc; | ||
406 | } | ||
407 | |||
408 | /* Try for up to 1s */ | ||
409 | for (timeout = 0; timeout < 1000; timeout++) { | ||
410 | if (!pv->established) | ||
411 | return -ENXIO; | ||
412 | if (pv->mctrl_update) | ||
413 | return 0; | ||
414 | if (!hvterm_hvsi_get_packet(pv)) | ||
415 | maybe_msleep(1); | ||
416 | } | ||
417 | return -EIO; | ||
418 | } | ||
419 | |||
420 | static int hvterm_hvsi_write_mctrl(struct hvterm_priv *pv, int dtr) | ||
421 | { | ||
422 | struct hvsi_control ctrl; | ||
423 | |||
424 | pr_devel("HVSI@%x: %s DTR...\n", pv->termno, | ||
425 | dtr ? "Setting" : "Clearing"); | ||
426 | |||
427 | ctrl.hdr.type = VS_CONTROL_PACKET_HEADER, | ||
428 | ctrl.hdr.len = sizeof(struct hvsi_control); | ||
429 | ctrl.verb = VSV_SET_MODEM_CTL; | ||
430 | ctrl.mask = HVSI_TSDTR; | ||
431 | ctrl.word = dtr ? HVSI_TSDTR : 0; | ||
432 | if (dtr) | ||
433 | pv->mctrl |= TIOCM_DTR; | ||
434 | else | ||
435 | pv->mctrl &= ~TIOCM_DTR; | ||
436 | return hvterm_hvsi_send_packet(pv, &ctrl.hdr); | ||
437 | } | ||
438 | |||
439 | static void hvterm_hvsi_establish(struct hvterm_priv *pv) | ||
440 | { | ||
441 | int timeout; | ||
442 | |||
443 | /* Try for up to 10ms, there can be a packet to | ||
444 | * start the process waiting for us... | ||
445 | */ | ||
446 | for (timeout = 0; timeout < 10; timeout++) { | ||
447 | if (pv->established) | ||
448 | goto established; | ||
449 | if (!hvterm_hvsi_get_packet(pv)) | ||
450 | maybe_msleep(1); | ||
451 | } | ||
452 | |||
453 | /* Failed, send a close connection packet just | ||
454 | * in case | ||
455 | */ | ||
456 | hvterm_hvsi_send_close(pv); | ||
457 | |||
458 | /* Then restart handshake */ | ||
459 | hvterm_hvsi_start_handshake(pv); | ||
460 | |||
461 | /* Try for up to 100ms */ | ||
462 | for (timeout = 0; timeout < 100; timeout++) { | ||
463 | if (pv->established) | ||
464 | goto established; | ||
465 | if (!hvterm_hvsi_get_packet(pv)) | ||
466 | maybe_msleep(1); | ||
467 | } | ||
468 | |||
469 | if (!pv->established) { | ||
470 | pr_devel("HVSI@%x: Timeout handshaking, giving up !\n", | ||
471 | pv->termno); | ||
472 | return; | ||
473 | } | ||
474 | established: | ||
475 | /* Query modem control lines */ | ||
476 | hvterm_hvsi_read_mctrl(pv); | ||
477 | |||
478 | /* Set our own DTR */ | ||
479 | hvterm_hvsi_write_mctrl(pv, 1); | ||
480 | |||
481 | /* Set the opened flag so reads are allowed */ | ||
482 | wmb(); | ||
483 | pv->opened = 1; | ||
484 | } | ||
485 | |||
486 | static int hvterm_hvsi_open(struct hvc_struct *hp, int data) | ||
487 | { | ||
488 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
489 | int rc; | ||
490 | |||
491 | pr_devel("HVSI@%x: open !\n", pv->termno); | ||
492 | |||
493 | rc = notifier_add_irq(hp, data); | ||
494 | if (rc) | ||
495 | return rc; | ||
496 | |||
497 | /* Keep track of the tty data structure */ | ||
498 | pv->tty = tty_kref_get(hp->tty); | ||
499 | |||
500 | hvterm_hvsi_establish(pv); | ||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | static void hvterm_hvsi_shutdown(struct hvc_struct *hp, struct hvterm_priv *pv) | ||
505 | { | ||
506 | unsigned long flags; | ||
507 | |||
508 | if (!pv->is_console) { | ||
509 | pr_devel("HVSI@%x: Not a console, tearing down\n", | ||
510 | pv->termno); | ||
511 | |||
512 | /* Clear opened, synchronize with khvcd */ | ||
513 | spin_lock_irqsave(&hp->lock, flags); | ||
514 | pv->opened = 0; | ||
515 | spin_unlock_irqrestore(&hp->lock, flags); | ||
516 | |||
517 | /* Clear our own DTR */ | ||
518 | if (!pv->tty || (pv->tty->termios->c_cflag & HUPCL)) | ||
519 | hvterm_hvsi_write_mctrl(pv, 0); | ||
520 | |||
521 | /* Tear down the connection */ | ||
522 | hvterm_hvsi_send_close(pv); | ||
523 | } | ||
524 | |||
525 | if (pv->tty) | ||
526 | tty_kref_put(pv->tty); | ||
527 | pv->tty = NULL; | ||
528 | } | ||
529 | |||
530 | static void hvterm_hvsi_close(struct hvc_struct *hp, int data) | ||
531 | { | ||
532 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
533 | |||
534 | pr_devel("HVSI@%x: close !\n", pv->termno); | ||
535 | |||
536 | hvterm_hvsi_shutdown(hp, pv); | ||
537 | |||
538 | notifier_del_irq(hp, data); | ||
539 | } | ||
540 | |||
541 | void hvterm_hvsi_hangup(struct hvc_struct *hp, int data) | ||
542 | { | ||
543 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
544 | |||
545 | pr_devel("HVSI@%x: hangup !\n", pv->termno); | ||
546 | |||
547 | hvterm_hvsi_shutdown(hp, pv); | ||
548 | |||
549 | notifier_hangup_irq(hp, data); | ||
550 | } | ||
551 | |||
552 | static int hvterm_hvsi_tiocmget(struct hvc_struct *hp) | ||
553 | { | ||
554 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
555 | |||
556 | if (!pv) | ||
557 | return -EINVAL; | ||
558 | return pv->mctrl; | ||
559 | } | ||
560 | |||
561 | static int hvterm_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set, | ||
562 | unsigned int clear) | ||
563 | { | ||
564 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
565 | |||
566 | pr_devel("HVSI@%x: Set modem control, set=%x,clr=%x\n", | ||
567 | pv->termno, set, clear); | ||
568 | |||
569 | if (set & TIOCM_DTR) | ||
570 | hvterm_hvsi_write_mctrl(pv, 1); | ||
571 | else if (clear & TIOCM_DTR) | ||
572 | hvterm_hvsi_write_mctrl(pv, 0); | ||
573 | |||
574 | return 0; | ||
575 | } | ||
576 | |||
577 | static const struct hv_ops hvterm_hvsi_ops = { | ||
578 | .get_chars = hvterm_hvsi_get_chars, | ||
579 | .put_chars = hvterm_hvsi_put_chars, | ||
580 | .notifier_add = hvterm_hvsi_open, | ||
581 | .notifier_del = hvterm_hvsi_close, | ||
582 | .notifier_hangup = hvterm_hvsi_hangup, | ||
583 | .tiocmget = hvterm_hvsi_tiocmget, | ||
584 | .tiocmset = hvterm_hvsi_tiocmset, | ||
585 | }; | ||
586 | |||
88 | static int __devinit hvc_vio_probe(struct vio_dev *vdev, | 587 | static int __devinit hvc_vio_probe(struct vio_dev *vdev, |
89 | const struct vio_device_id *id) | 588 | const struct vio_device_id *id) |
90 | { | 589 | { |
590 | const struct hv_ops *ops; | ||
91 | struct hvc_struct *hp; | 591 | struct hvc_struct *hp; |
592 | struct hvterm_priv *pv; | ||
593 | hv_protocol_t proto; | ||
594 | int i, termno = -1; | ||
92 | 595 | ||
93 | /* probed with invalid parameters. */ | 596 | /* probed with invalid parameters. */ |
94 | if (!vdev || !id) | 597 | if (!vdev || !id) |
95 | return -EPERM; | 598 | return -EPERM; |
96 | 599 | ||
97 | hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, | 600 | if (of_device_is_compatible(vdev->dev.of_node, "hvterm1")) { |
98 | MAX_VIO_PUT_CHARS); | 601 | proto = HV_PROTOCOL_RAW; |
602 | ops = &hvterm_raw_ops; | ||
603 | } else if (of_device_is_compatible(vdev->dev.of_node, "hvterm-protocol")) { | ||
604 | proto = HV_PROTOCOL_HVSI; | ||
605 | ops = &hvterm_hvsi_ops; | ||
606 | } else { | ||
607 | pr_err("hvc_vio: Unkown protocol for %s\n", vdev->dev.of_node->full_name); | ||
608 | return -ENXIO; | ||
609 | } | ||
610 | |||
611 | pr_devel("hvc_vio_probe() device %s, using %s protocol\n", | ||
612 | vdev->dev.of_node->full_name, | ||
613 | proto == HV_PROTOCOL_RAW ? "raw" : "hvsi"); | ||
614 | |||
615 | /* Is it our boot one ? */ | ||
616 | if (hvterm_privs[0] == &hvterm_priv0 && | ||
617 | vdev->unit_address == hvterm_priv0.termno) { | ||
618 | pv = hvterm_privs[0]; | ||
619 | termno = 0; | ||
620 | pr_devel("->boot console, using termno 0\n"); | ||
621 | } | ||
622 | /* nope, allocate a new one */ | ||
623 | else { | ||
624 | for (i = 0; i < MAX_NR_HVC_CONSOLES && termno < 0; i++) | ||
625 | if (!hvterm_privs[i]) | ||
626 | termno = i; | ||
627 | pr_devel("->non-boot console, using termno %d\n", termno); | ||
628 | if (termno < 0) | ||
629 | return -ENODEV; | ||
630 | pv = kzalloc(sizeof(struct hvterm_priv), GFP_KERNEL); | ||
631 | if (!pv) | ||
632 | return -ENOMEM; | ||
633 | pv->termno = vdev->unit_address; | ||
634 | pv->proto = proto; | ||
635 | hvterm_privs[termno] = pv; | ||
636 | } | ||
637 | |||
638 | hp = hvc_alloc(termno, vdev->irq, ops, MAX_VIO_PUT_CHARS); | ||
99 | if (IS_ERR(hp)) | 639 | if (IS_ERR(hp)) |
100 | return PTR_ERR(hp); | 640 | return PTR_ERR(hp); |
101 | dev_set_drvdata(&vdev->dev, hp); | 641 | dev_set_drvdata(&vdev->dev, hp); |
@@ -106,8 +646,16 @@ static int __devinit hvc_vio_probe(struct vio_dev *vdev, | |||
106 | static int __devexit hvc_vio_remove(struct vio_dev *vdev) | 646 | static int __devexit hvc_vio_remove(struct vio_dev *vdev) |
107 | { | 647 | { |
108 | struct hvc_struct *hp = dev_get_drvdata(&vdev->dev); | 648 | struct hvc_struct *hp = dev_get_drvdata(&vdev->dev); |
649 | int rc, termno; | ||
109 | 650 | ||
110 | return hvc_remove(hp); | 651 | termno = hp->vtermno; |
652 | rc = hvc_remove(hp); | ||
653 | if (rc == 0) { | ||
654 | if (hvterm_privs[termno] != &hvterm_priv0) | ||
655 | kfree(hvterm_privs[termno]); | ||
656 | hvterm_privs[termno] = NULL; | ||
657 | } | ||
658 | return rc; | ||
111 | } | 659 | } |
112 | 660 | ||
113 | static struct vio_driver hvc_vio_driver = { | 661 | static struct vio_driver hvc_vio_driver = { |
@@ -140,34 +688,145 @@ static void __exit hvc_vio_exit(void) | |||
140 | } | 688 | } |
141 | module_exit(hvc_vio_exit); | 689 | module_exit(hvc_vio_exit); |
142 | 690 | ||
143 | /* the device tree order defines our numbering */ | 691 | static void udbg_hvc_putc(char c) |
144 | static int hvc_find_vtys(void) | ||
145 | { | 692 | { |
146 | struct device_node *vty; | 693 | int count = -1; |
147 | int num_found = 0; | ||
148 | 694 | ||
149 | for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; | 695 | if (c == '\n') |
150 | vty = of_find_node_by_name(vty, "vty")) { | 696 | udbg_hvc_putc('\r'); |
151 | const uint32_t *vtermno; | ||
152 | 697 | ||
153 | /* We have statically defined space for only a certain number | 698 | do { |
154 | * of console adapters. | 699 | switch(hvterm_priv0.proto) { |
155 | */ | 700 | case HV_PROTOCOL_RAW: |
156 | if (num_found >= MAX_NR_HVC_CONSOLES) { | 701 | count = hvterm_raw_put_chars(0, &c, 1); |
157 | of_node_put(vty); | 702 | break; |
703 | case HV_PROTOCOL_HVSI: | ||
704 | count = hvterm_hvsi_put_chars(0, &c, 1); | ||
158 | break; | 705 | break; |
159 | } | 706 | } |
707 | } while(count == 0); | ||
708 | } | ||
709 | |||
710 | static int udbg_hvc_getc_poll(void) | ||
711 | { | ||
712 | int rc = 0; | ||
713 | char c; | ||
160 | 714 | ||
161 | vtermno = of_get_property(vty, "reg", NULL); | 715 | switch(hvterm_priv0.proto) { |
162 | if (!vtermno) | 716 | case HV_PROTOCOL_RAW: |
163 | continue; | 717 | rc = hvterm_raw_get_chars(0, &c, 1); |
718 | break; | ||
719 | case HV_PROTOCOL_HVSI: | ||
720 | rc = hvterm_hvsi_get_chars(0, &c, 1); | ||
721 | break; | ||
722 | } | ||
723 | if (!rc) | ||
724 | return -1; | ||
725 | return c; | ||
726 | } | ||
164 | 727 | ||
165 | if (of_device_is_compatible(vty, "hvterm1")) { | 728 | static int udbg_hvc_getc(void) |
166 | hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops); | 729 | { |
167 | ++num_found; | 730 | int ch; |
731 | for (;;) { | ||
732 | ch = udbg_hvc_getc_poll(); | ||
733 | if (ch == -1) { | ||
734 | /* This shouldn't be needed...but... */ | ||
735 | volatile unsigned long delay; | ||
736 | for (delay=0; delay < 2000000; delay++) | ||
737 | ; | ||
738 | } else { | ||
739 | return ch; | ||
168 | } | 740 | } |
169 | } | 741 | } |
742 | } | ||
743 | |||
744 | void __init hvc_vio_init_early(void) | ||
745 | { | ||
746 | struct device_node *stdout_node; | ||
747 | const u32 *termno; | ||
748 | const char *name; | ||
749 | const struct hv_ops *ops; | ||
750 | |||
751 | /* find the boot console from /chosen/stdout */ | ||
752 | if (!of_chosen) | ||
753 | return; | ||
754 | name = of_get_property(of_chosen, "linux,stdout-path", NULL); | ||
755 | if (name == NULL) | ||
756 | return; | ||
757 | stdout_node = of_find_node_by_path(name); | ||
758 | if (!stdout_node) | ||
759 | return; | ||
760 | name = of_get_property(stdout_node, "name", NULL); | ||
761 | if (!name) { | ||
762 | printk(KERN_WARNING "stdout node missing 'name' property!\n"); | ||
763 | goto out; | ||
764 | } | ||
765 | |||
766 | /* Check if it's a virtual terminal */ | ||
767 | if (strncmp(name, "vty", 3) != 0) | ||
768 | goto out; | ||
769 | termno = of_get_property(stdout_node, "reg", NULL); | ||
770 | if (termno == NULL) | ||
771 | goto out; | ||
772 | hvterm_priv0.termno = *termno; | ||
773 | hvterm_priv0.is_console = 1; | ||
774 | hvterm_privs[0] = &hvterm_priv0; | ||
775 | |||
776 | /* Check the protocol */ | ||
777 | if (of_device_is_compatible(stdout_node, "hvterm1")) { | ||
778 | hvterm_priv0.proto = HV_PROTOCOL_RAW; | ||
779 | ops = &hvterm_raw_ops; | ||
780 | } | ||
781 | else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) { | ||
782 | hvterm_priv0.proto = HV_PROTOCOL_HVSI; | ||
783 | ops = &hvterm_hvsi_ops; | ||
784 | /* HVSI, perform the handshake now */ | ||
785 | hvterm_hvsi_establish(&hvterm_priv0); | ||
786 | } else | ||
787 | goto out; | ||
788 | udbg_putc = udbg_hvc_putc; | ||
789 | udbg_getc = udbg_hvc_getc; | ||
790 | udbg_getc_poll = udbg_hvc_getc_poll; | ||
791 | #ifdef HVC_OLD_HVSI | ||
792 | /* When using the old HVSI driver don't register the HVC | ||
793 | * backend for HVSI, only do udbg | ||
794 | */ | ||
795 | if (hvterm_priv0.proto == HV_PROTOCOL_HVSI) | ||
796 | goto out; | ||
797 | #endif | ||
798 | add_preferred_console("hvc", 0, NULL); | ||
799 | hvc_instantiate(0, 0, ops); | ||
800 | out: | ||
801 | of_node_put(stdout_node); | ||
802 | } | ||
803 | |||
804 | /* call this from early_init() for a working debug console on | ||
805 | * vterm capable LPAR machines | ||
806 | */ | ||
807 | #ifdef CONFIG_PPC_EARLY_DEBUG_LPAR | ||
808 | void __init udbg_init_debug_lpar(void) | ||
809 | { | ||
810 | hvterm_privs[0] = &hvterm_priv0; | ||
811 | hvterm_priv0.termno = 0; | ||
812 | hvterm_priv0.proto = HV_PROTOCOL_RAW; | ||
813 | hvterm_priv0.is_console = 1; | ||
814 | udbg_putc = udbg_hvc_putc; | ||
815 | udbg_getc = udbg_hvc_getc; | ||
816 | udbg_getc_poll = udbg_hvc_getc_poll; | ||
817 | } | ||
818 | #endif /* CONFIG_PPC_EARLY_DEBUG_LPAR */ | ||
170 | 819 | ||
171 | return num_found; | 820 | #ifdef CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI |
821 | void __init udbg_init_debug_lpar_hvsi(void) | ||
822 | { | ||
823 | hvterm_privs[0] = &hvterm_priv0; | ||
824 | hvterm_priv0.termno = CONFIG_PPC_EARLY_DEBUG_HVSI_VTERMNO; | ||
825 | hvterm_priv0.proto = HV_PROTOCOL_HVSI; | ||
826 | hvterm_priv0.is_console = 1; | ||
827 | udbg_putc = udbg_hvc_putc; | ||
828 | udbg_getc = udbg_hvc_getc; | ||
829 | udbg_getc_poll = udbg_hvc_getc_poll; | ||
830 | hvterm_hvsi_establish(&hvterm_priv0); | ||
172 | } | 831 | } |
173 | console_initcall(hvc_find_vtys); | 832 | #endif /* CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI */ |