162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2004 Hollis Blanchard <hollisb@us.ibm.com>, IBM
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/* Host Virtual Serial Interface (HVSI) is a protocol between the hosted OS
762306a36Sopenharmony_ci * and the service processor on IBM pSeries servers. On these servers, there
862306a36Sopenharmony_ci * are no serial ports under the OS's control, and sometimes there is no other
962306a36Sopenharmony_ci * console available either. However, the service processor has two standard
1062306a36Sopenharmony_ci * serial ports, so this over-complicated protocol allows the OS to control
1162306a36Sopenharmony_ci * those ports by proxy.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Besides data, the procotol supports the reading/writing of the serial
1462306a36Sopenharmony_ci * port's DTR line, and the reading of the CD line. This is to allow the OS to
1562306a36Sopenharmony_ci * control a modem attached to the service processor's serial port. Note that
1662306a36Sopenharmony_ci * the OS cannot change the speed of the port through this protocol.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#undef DEBUG
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/console.h>
2262306a36Sopenharmony_ci#include <linux/ctype.h>
2362306a36Sopenharmony_ci#include <linux/delay.h>
2462306a36Sopenharmony_ci#include <linux/init.h>
2562306a36Sopenharmony_ci#include <linux/interrupt.h>
2662306a36Sopenharmony_ci#include <linux/module.h>
2762306a36Sopenharmony_ci#include <linux/major.h>
2862306a36Sopenharmony_ci#include <linux/kernel.h>
2962306a36Sopenharmony_ci#include <linux/of_irq.h>
3062306a36Sopenharmony_ci#include <linux/spinlock.h>
3162306a36Sopenharmony_ci#include <linux/sysrq.h>
3262306a36Sopenharmony_ci#include <linux/tty.h>
3362306a36Sopenharmony_ci#include <linux/tty_flip.h>
3462306a36Sopenharmony_ci#include <asm/hvcall.h>
3562306a36Sopenharmony_ci#include <asm/hvconsole.h>
3662306a36Sopenharmony_ci#include <linux/uaccess.h>
3762306a36Sopenharmony_ci#include <asm/vio.h>
3862306a36Sopenharmony_ci#include <asm/param.h>
3962306a36Sopenharmony_ci#include <asm/hvsi.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define HVSI_MAJOR	229
4262306a36Sopenharmony_ci#define HVSI_MINOR	128
4362306a36Sopenharmony_ci#define MAX_NR_HVSI_CONSOLES 4
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define HVSI_TIMEOUT (5*HZ)
4662306a36Sopenharmony_ci#define HVSI_VERSION 1
4762306a36Sopenharmony_ci#define HVSI_MAX_PACKET 256
4862306a36Sopenharmony_ci#define HVSI_MAX_READ 16
4962306a36Sopenharmony_ci#define HVSI_MAX_OUTGOING_DATA 12
5062306a36Sopenharmony_ci#define N_OUTBUF 12
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/*
5362306a36Sopenharmony_ci * we pass data via two 8-byte registers, so we would like our char arrays
5462306a36Sopenharmony_ci * properly aligned for those loads.
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_ci#define __ALIGNED__	__attribute__((__aligned__(sizeof(long))))
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistruct hvsi_struct {
5962306a36Sopenharmony_ci	struct tty_port port;
6062306a36Sopenharmony_ci	struct delayed_work writer;
6162306a36Sopenharmony_ci	struct work_struct handshaker;
6262306a36Sopenharmony_ci	wait_queue_head_t emptyq; /* woken when outbuf is emptied */
6362306a36Sopenharmony_ci	wait_queue_head_t stateq; /* woken when HVSI state changes */
6462306a36Sopenharmony_ci	spinlock_t lock;
6562306a36Sopenharmony_ci	int index;
6662306a36Sopenharmony_ci	uint8_t throttle_buf[128];
6762306a36Sopenharmony_ci	uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */
6862306a36Sopenharmony_ci	/* inbuf is for packet reassembly. leave a little room for leftovers. */
6962306a36Sopenharmony_ci	uint8_t inbuf[HVSI_MAX_PACKET + HVSI_MAX_READ];
7062306a36Sopenharmony_ci	uint8_t *inbuf_end;
7162306a36Sopenharmony_ci	int n_throttle;
7262306a36Sopenharmony_ci	int n_outbuf;
7362306a36Sopenharmony_ci	uint32_t vtermno;
7462306a36Sopenharmony_ci	uint32_t virq;
7562306a36Sopenharmony_ci	atomic_t seqno; /* HVSI packet sequence number */
7662306a36Sopenharmony_ci	uint16_t mctrl;
7762306a36Sopenharmony_ci	uint8_t state;  /* HVSI protocol state */
7862306a36Sopenharmony_ci	uint8_t flags;
7962306a36Sopenharmony_ci#ifdef CONFIG_MAGIC_SYSRQ
8062306a36Sopenharmony_ci	uint8_t sysrq;
8162306a36Sopenharmony_ci#endif /* CONFIG_MAGIC_SYSRQ */
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_cistatic struct hvsi_struct hvsi_ports[MAX_NR_HVSI_CONSOLES];
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic struct tty_driver *hvsi_driver;
8662306a36Sopenharmony_cistatic int hvsi_count;
8762306a36Sopenharmony_cistatic int (*hvsi_wait)(struct hvsi_struct *hp, int state);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cienum HVSI_PROTOCOL_STATE {
9062306a36Sopenharmony_ci	HVSI_CLOSED,
9162306a36Sopenharmony_ci	HVSI_WAIT_FOR_VER_RESPONSE,
9262306a36Sopenharmony_ci	HVSI_WAIT_FOR_VER_QUERY,
9362306a36Sopenharmony_ci	HVSI_OPEN,
9462306a36Sopenharmony_ci	HVSI_WAIT_FOR_MCTRL_RESPONSE,
9562306a36Sopenharmony_ci	HVSI_FSP_DIED,
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ci#define HVSI_CONSOLE 0x1
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic inline int is_console(struct hvsi_struct *hp)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	return hp->flags & HVSI_CONSOLE;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic inline int is_open(struct hvsi_struct *hp)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	/* if we're waiting for an mctrl then we're already open */
10762306a36Sopenharmony_ci	return (hp->state == HVSI_OPEN)
10862306a36Sopenharmony_ci			|| (hp->state == HVSI_WAIT_FOR_MCTRL_RESPONSE);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic inline void print_state(struct hvsi_struct *hp)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci#ifdef DEBUG
11462306a36Sopenharmony_ci	static const char *state_names[] = {
11562306a36Sopenharmony_ci		"HVSI_CLOSED",
11662306a36Sopenharmony_ci		"HVSI_WAIT_FOR_VER_RESPONSE",
11762306a36Sopenharmony_ci		"HVSI_WAIT_FOR_VER_QUERY",
11862306a36Sopenharmony_ci		"HVSI_OPEN",
11962306a36Sopenharmony_ci		"HVSI_WAIT_FOR_MCTRL_RESPONSE",
12062306a36Sopenharmony_ci		"HVSI_FSP_DIED",
12162306a36Sopenharmony_ci	};
12262306a36Sopenharmony_ci	const char *name = (hp->state < ARRAY_SIZE(state_names))
12362306a36Sopenharmony_ci		? state_names[hp->state] : "UNKNOWN";
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	pr_debug("hvsi%i: state = %s\n", hp->index, name);
12662306a36Sopenharmony_ci#endif /* DEBUG */
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic inline void __set_state(struct hvsi_struct *hp, int state)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	hp->state = state;
13262306a36Sopenharmony_ci	print_state(hp);
13362306a36Sopenharmony_ci	wake_up_all(&hp->stateq);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic inline void set_state(struct hvsi_struct *hp, int state)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	unsigned long flags;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	spin_lock_irqsave(&hp->lock, flags);
14162306a36Sopenharmony_ci	__set_state(hp, state);
14262306a36Sopenharmony_ci	spin_unlock_irqrestore(&hp->lock, flags);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic inline int len_packet(const uint8_t *packet)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	return (int)((struct hvsi_header *)packet)->len;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic inline int is_header(const uint8_t *packet)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	struct hvsi_header *header = (struct hvsi_header *)packet;
15362306a36Sopenharmony_ci	return header->type >= VS_QUERY_RESPONSE_PACKET_HEADER;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic inline int got_packet(const struct hvsi_struct *hp, uint8_t *packet)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	if (hp->inbuf_end < packet + sizeof(struct hvsi_header))
15962306a36Sopenharmony_ci		return 0; /* don't even have the packet header */
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (hp->inbuf_end < (packet + len_packet(packet)))
16262306a36Sopenharmony_ci		return 0; /* don't have the rest of the packet */
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return 1;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/* shift remaining bytes in packetbuf down */
16862306a36Sopenharmony_cistatic void compact_inbuf(struct hvsi_struct *hp, uint8_t *read_to)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	int remaining = (int)(hp->inbuf_end - read_to);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	pr_debug("%s: %i chars remain\n", __func__, remaining);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (read_to != hp->inbuf)
17562306a36Sopenharmony_ci		memmove(hp->inbuf, read_to, remaining);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	hp->inbuf_end = hp->inbuf + remaining;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci#ifdef DEBUG
18162306a36Sopenharmony_ci#define dbg_dump_packet(packet) dump_packet(packet)
18262306a36Sopenharmony_ci#define dbg_dump_hex(data, len) dump_hex(data, len)
18362306a36Sopenharmony_ci#else
18462306a36Sopenharmony_ci#define dbg_dump_packet(packet) do { } while (0)
18562306a36Sopenharmony_ci#define dbg_dump_hex(data, len) do { } while (0)
18662306a36Sopenharmony_ci#endif
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic void dump_hex(const uint8_t *data, int len)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	int i;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	printk("    ");
19362306a36Sopenharmony_ci	for (i=0; i < len; i++)
19462306a36Sopenharmony_ci		printk("%.2x", data[i]);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	printk("\n    ");
19762306a36Sopenharmony_ci	for (i=0; i < len; i++) {
19862306a36Sopenharmony_ci		if (isprint(data[i]))
19962306a36Sopenharmony_ci			printk("%c", data[i]);
20062306a36Sopenharmony_ci		else
20162306a36Sopenharmony_ci			printk(".");
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci	printk("\n");
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic void dump_packet(uint8_t *packet)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct hvsi_header *header = (struct hvsi_header *)packet;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	printk("type 0x%x, len %i, seqno %i:\n", header->type, header->len,
21162306a36Sopenharmony_ci			header->seqno);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	dump_hex(packet, header->len);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic int hvsi_read(struct hvsi_struct *hp, char *buf, int count)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	unsigned long got;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	got = hvc_get_chars(hp->vtermno, buf, count);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	return got;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet,
22662306a36Sopenharmony_ci	struct tty_struct *tty, struct hvsi_struct **to_handshake)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct hvsi_control *header = (struct hvsi_control *)packet;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	switch (be16_to_cpu(header->verb)) {
23162306a36Sopenharmony_ci		case VSV_MODEM_CTL_UPDATE:
23262306a36Sopenharmony_ci			if ((be32_to_cpu(header->word) & HVSI_TSCD) == 0) {
23362306a36Sopenharmony_ci				/* CD went away; no more connection */
23462306a36Sopenharmony_ci				pr_debug("hvsi%i: CD dropped\n", hp->index);
23562306a36Sopenharmony_ci				hp->mctrl &= TIOCM_CD;
23662306a36Sopenharmony_ci				if (tty && !C_CLOCAL(tty))
23762306a36Sopenharmony_ci					tty_hangup(tty);
23862306a36Sopenharmony_ci			}
23962306a36Sopenharmony_ci			break;
24062306a36Sopenharmony_ci		case VSV_CLOSE_PROTOCOL:
24162306a36Sopenharmony_ci			pr_debug("hvsi%i: service processor came back\n", hp->index);
24262306a36Sopenharmony_ci			if (hp->state != HVSI_CLOSED) {
24362306a36Sopenharmony_ci				*to_handshake = hp;
24462306a36Sopenharmony_ci			}
24562306a36Sopenharmony_ci			break;
24662306a36Sopenharmony_ci		default:
24762306a36Sopenharmony_ci			printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ",
24862306a36Sopenharmony_ci				hp->index);
24962306a36Sopenharmony_ci			dump_packet(packet);
25062306a36Sopenharmony_ci			break;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	struct hvsi_query_response *resp = (struct hvsi_query_response *)packet;
25762306a36Sopenharmony_ci	uint32_t mctrl_word;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	switch (hp->state) {
26062306a36Sopenharmony_ci		case HVSI_WAIT_FOR_VER_RESPONSE:
26162306a36Sopenharmony_ci			__set_state(hp, HVSI_WAIT_FOR_VER_QUERY);
26262306a36Sopenharmony_ci			break;
26362306a36Sopenharmony_ci		case HVSI_WAIT_FOR_MCTRL_RESPONSE:
26462306a36Sopenharmony_ci			hp->mctrl = 0;
26562306a36Sopenharmony_ci			mctrl_word = be32_to_cpu(resp->u.mctrl_word);
26662306a36Sopenharmony_ci			if (mctrl_word & HVSI_TSDTR)
26762306a36Sopenharmony_ci				hp->mctrl |= TIOCM_DTR;
26862306a36Sopenharmony_ci			if (mctrl_word & HVSI_TSCD)
26962306a36Sopenharmony_ci				hp->mctrl |= TIOCM_CD;
27062306a36Sopenharmony_ci			__set_state(hp, HVSI_OPEN);
27162306a36Sopenharmony_ci			break;
27262306a36Sopenharmony_ci		default:
27362306a36Sopenharmony_ci			printk(KERN_ERR "hvsi%i: unexpected query response: ", hp->index);
27462306a36Sopenharmony_ci			dump_packet(packet);
27562306a36Sopenharmony_ci			break;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci/* respond to service processor's version query */
28062306a36Sopenharmony_cistatic int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct hvsi_query_response packet __ALIGNED__;
28362306a36Sopenharmony_ci	int wrote;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	packet.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER;
28662306a36Sopenharmony_ci	packet.hdr.len = sizeof(struct hvsi_query_response);
28762306a36Sopenharmony_ci	packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno));
28862306a36Sopenharmony_ci	packet.verb = cpu_to_be16(VSV_SEND_VERSION_NUMBER);
28962306a36Sopenharmony_ci	packet.u.version = HVSI_VERSION;
29062306a36Sopenharmony_ci	packet.query_seqno = cpu_to_be16(query_seqno+1);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len);
29362306a36Sopenharmony_ci	dbg_dump_hex((uint8_t*)&packet, packet.hdr.len);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len);
29662306a36Sopenharmony_ci	if (wrote != packet.hdr.len) {
29762306a36Sopenharmony_ci		printk(KERN_ERR "hvsi%i: couldn't send query response!\n",
29862306a36Sopenharmony_ci			hp->index);
29962306a36Sopenharmony_ci		return -EIO;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return 0;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct hvsi_query *query = (struct hvsi_query *)packet;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	switch (hp->state) {
31062306a36Sopenharmony_ci		case HVSI_WAIT_FOR_VER_QUERY:
31162306a36Sopenharmony_ci			hvsi_version_respond(hp, be16_to_cpu(query->hdr.seqno));
31262306a36Sopenharmony_ci			__set_state(hp, HVSI_OPEN);
31362306a36Sopenharmony_ci			break;
31462306a36Sopenharmony_ci		default:
31562306a36Sopenharmony_ci			printk(KERN_ERR "hvsi%i: unexpected query: ", hp->index);
31662306a36Sopenharmony_ci			dump_packet(packet);
31762306a36Sopenharmony_ci			break;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	int i;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	for (i=0; i < len; i++) {
32662306a36Sopenharmony_ci		char c = buf[i];
32762306a36Sopenharmony_ci#ifdef CONFIG_MAGIC_SYSRQ
32862306a36Sopenharmony_ci		if (c == '\0') {
32962306a36Sopenharmony_ci			hp->sysrq = 1;
33062306a36Sopenharmony_ci			continue;
33162306a36Sopenharmony_ci		} else if (hp->sysrq) {
33262306a36Sopenharmony_ci			handle_sysrq(c);
33362306a36Sopenharmony_ci			hp->sysrq = 0;
33462306a36Sopenharmony_ci			continue;
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci#endif /* CONFIG_MAGIC_SYSRQ */
33762306a36Sopenharmony_ci		tty_insert_flip_char(&hp->port, c, 0);
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci/*
34262306a36Sopenharmony_ci * We could get 252 bytes of data at once here. But the tty layer only
34362306a36Sopenharmony_ci * throttles us at TTY_THRESHOLD_THROTTLE (128) bytes, so we could overflow
34462306a36Sopenharmony_ci * it. Accordingly we won't send more than 128 bytes at a time to the flip
34562306a36Sopenharmony_ci * buffer, which will give the tty buffer a chance to throttle us. Should the
34662306a36Sopenharmony_ci * value of TTY_THRESHOLD_THROTTLE change in n_tty.c, this code should be
34762306a36Sopenharmony_ci * revisited.
34862306a36Sopenharmony_ci */
34962306a36Sopenharmony_ci#define TTY_THRESHOLD_THROTTLE 128
35062306a36Sopenharmony_cistatic bool hvsi_recv_data(struct hvsi_struct *hp, const uint8_t *packet)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	const struct hvsi_header *header = (const struct hvsi_header *)packet;
35362306a36Sopenharmony_ci	const uint8_t *data = packet + sizeof(struct hvsi_header);
35462306a36Sopenharmony_ci	int datalen = header->len - sizeof(struct hvsi_header);
35562306a36Sopenharmony_ci	int overflow = datalen - TTY_THRESHOLD_THROTTLE;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (datalen == 0)
36062306a36Sopenharmony_ci		return false;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (overflow > 0) {
36362306a36Sopenharmony_ci		pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __func__);
36462306a36Sopenharmony_ci		datalen = TTY_THRESHOLD_THROTTLE;
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	hvsi_insert_chars(hp, data, datalen);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (overflow > 0) {
37062306a36Sopenharmony_ci		/*
37162306a36Sopenharmony_ci		 * we still have more data to deliver, so we need to save off the
37262306a36Sopenharmony_ci		 * overflow and send it later
37362306a36Sopenharmony_ci		 */
37462306a36Sopenharmony_ci		pr_debug("%s: deferring overflow\n", __func__);
37562306a36Sopenharmony_ci		memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow);
37662306a36Sopenharmony_ci		hp->n_throttle = overflow;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return true;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci/*
38362306a36Sopenharmony_ci * Returns true/false indicating data successfully read from hypervisor.
38462306a36Sopenharmony_ci * Used both to get packets for tty connections and to advance the state
38562306a36Sopenharmony_ci * machine during console handshaking (in which case tty = NULL and we ignore
38662306a36Sopenharmony_ci * incoming data).
38762306a36Sopenharmony_ci */
38862306a36Sopenharmony_cistatic int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct *tty,
38962306a36Sopenharmony_ci		struct hvsi_struct **handshake)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	uint8_t *packet = hp->inbuf;
39262306a36Sopenharmony_ci	int chunklen;
39362306a36Sopenharmony_ci	bool flip = false;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	*handshake = NULL;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ);
39862306a36Sopenharmony_ci	if (chunklen == 0) {
39962306a36Sopenharmony_ci		pr_debug("%s: 0-length read\n", __func__);
40062306a36Sopenharmony_ci		return 0;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	pr_debug("%s: got %i bytes\n", __func__, chunklen);
40462306a36Sopenharmony_ci	dbg_dump_hex(hp->inbuf_end, chunklen);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	hp->inbuf_end += chunklen;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/* handle all completed packets */
40962306a36Sopenharmony_ci	while ((packet < hp->inbuf_end) && got_packet(hp, packet)) {
41062306a36Sopenharmony_ci		struct hvsi_header *header = (struct hvsi_header *)packet;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci		if (!is_header(packet)) {
41362306a36Sopenharmony_ci			printk(KERN_ERR "hvsi%i: got malformed packet\n", hp->index);
41462306a36Sopenharmony_ci			/* skip bytes until we find a header or run out of data */
41562306a36Sopenharmony_ci			while ((packet < hp->inbuf_end) && (!is_header(packet)))
41662306a36Sopenharmony_ci				packet++;
41762306a36Sopenharmony_ci			continue;
41862306a36Sopenharmony_ci		}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci		pr_debug("%s: handling %i-byte packet\n", __func__,
42162306a36Sopenharmony_ci				len_packet(packet));
42262306a36Sopenharmony_ci		dbg_dump_packet(packet);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		switch (header->type) {
42562306a36Sopenharmony_ci			case VS_DATA_PACKET_HEADER:
42662306a36Sopenharmony_ci				if (!is_open(hp))
42762306a36Sopenharmony_ci					break;
42862306a36Sopenharmony_ci				flip = hvsi_recv_data(hp, packet);
42962306a36Sopenharmony_ci				break;
43062306a36Sopenharmony_ci			case VS_CONTROL_PACKET_HEADER:
43162306a36Sopenharmony_ci				hvsi_recv_control(hp, packet, tty, handshake);
43262306a36Sopenharmony_ci				break;
43362306a36Sopenharmony_ci			case VS_QUERY_RESPONSE_PACKET_HEADER:
43462306a36Sopenharmony_ci				hvsi_recv_response(hp, packet);
43562306a36Sopenharmony_ci				break;
43662306a36Sopenharmony_ci			case VS_QUERY_PACKET_HEADER:
43762306a36Sopenharmony_ci				hvsi_recv_query(hp, packet);
43862306a36Sopenharmony_ci				break;
43962306a36Sopenharmony_ci			default:
44062306a36Sopenharmony_ci				printk(KERN_ERR "hvsi%i: unknown HVSI packet type 0x%x\n",
44162306a36Sopenharmony_ci						hp->index, header->type);
44262306a36Sopenharmony_ci				dump_packet(packet);
44362306a36Sopenharmony_ci				break;
44462306a36Sopenharmony_ci		}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci		packet += len_packet(packet);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		if (*handshake) {
44962306a36Sopenharmony_ci			pr_debug("%s: handshake\n", __func__);
45062306a36Sopenharmony_ci			break;
45162306a36Sopenharmony_ci		}
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	compact_inbuf(hp, packet);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (flip)
45762306a36Sopenharmony_ci		tty_flip_buffer_push(&hp->port);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	return 1;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic void hvsi_send_overflow(struct hvsi_struct *hp)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	pr_debug("%s: delivering %i bytes overflow\n", __func__,
46562306a36Sopenharmony_ci			hp->n_throttle);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle);
46862306a36Sopenharmony_ci	hp->n_throttle = 0;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci/*
47262306a36Sopenharmony_ci * must get all pending data because we only get an irq on empty->non-empty
47362306a36Sopenharmony_ci * transition
47462306a36Sopenharmony_ci */
47562306a36Sopenharmony_cistatic irqreturn_t hvsi_interrupt(int irq, void *arg)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct hvsi_struct *hp = (struct hvsi_struct *)arg;
47862306a36Sopenharmony_ci	struct hvsi_struct *handshake;
47962306a36Sopenharmony_ci	struct tty_struct *tty;
48062306a36Sopenharmony_ci	unsigned long flags;
48162306a36Sopenharmony_ci	int again = 1;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	pr_debug("%s\n", __func__);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	tty = tty_port_tty_get(&hp->port);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	while (again) {
48862306a36Sopenharmony_ci		spin_lock_irqsave(&hp->lock, flags);
48962306a36Sopenharmony_ci		again = hvsi_load_chunk(hp, tty, &handshake);
49062306a36Sopenharmony_ci		spin_unlock_irqrestore(&hp->lock, flags);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		if (handshake) {
49362306a36Sopenharmony_ci			pr_debug("hvsi%i: attempting re-handshake\n", handshake->index);
49462306a36Sopenharmony_ci			schedule_work(&handshake->handshaker);
49562306a36Sopenharmony_ci		}
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	spin_lock_irqsave(&hp->lock, flags);
49962306a36Sopenharmony_ci	if (tty && hp->n_throttle && !tty_throttled(tty)) {
50062306a36Sopenharmony_ci		/* we weren't hung up and we weren't throttled, so we can
50162306a36Sopenharmony_ci		 * deliver the rest now */
50262306a36Sopenharmony_ci		hvsi_send_overflow(hp);
50362306a36Sopenharmony_ci		tty_flip_buffer_push(&hp->port);
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci	spin_unlock_irqrestore(&hp->lock, flags);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	tty_kref_put(tty);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	return IRQ_HANDLED;
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci/* for boot console, before the irq handler is running */
51362306a36Sopenharmony_cistatic int __init poll_for_state(struct hvsi_struct *hp, int state)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	unsigned long end_jiffies = jiffies + HVSI_TIMEOUT;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	for (;;) {
51862306a36Sopenharmony_ci		hvsi_interrupt(hp->virq, (void *)hp); /* get pending data */
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		if (hp->state == state)
52162306a36Sopenharmony_ci			return 0;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		mdelay(5);
52462306a36Sopenharmony_ci		if (time_after(jiffies, end_jiffies))
52562306a36Sopenharmony_ci			return -EIO;
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci/* wait for irq handler to change our state */
53062306a36Sopenharmony_cistatic int wait_for_state(struct hvsi_struct *hp, int state)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	int ret = 0;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (!wait_event_timeout(hp->stateq, (hp->state == state), HVSI_TIMEOUT))
53562306a36Sopenharmony_ci		ret = -EIO;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	return ret;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic int hvsi_query(struct hvsi_struct *hp, uint16_t verb)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct hvsi_query packet __ALIGNED__;
54362306a36Sopenharmony_ci	int wrote;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	packet.hdr.type = VS_QUERY_PACKET_HEADER;
54662306a36Sopenharmony_ci	packet.hdr.len = sizeof(struct hvsi_query);
54762306a36Sopenharmony_ci	packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno));
54862306a36Sopenharmony_ci	packet.verb = cpu_to_be16(verb);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len);
55162306a36Sopenharmony_ci	dbg_dump_hex((uint8_t*)&packet, packet.hdr.len);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len);
55462306a36Sopenharmony_ci	if (wrote != packet.hdr.len) {
55562306a36Sopenharmony_ci		printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index,
55662306a36Sopenharmony_ci			wrote);
55762306a36Sopenharmony_ci		return -EIO;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return 0;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic int hvsi_get_mctrl(struct hvsi_struct *hp)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	int ret;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	set_state(hp, HVSI_WAIT_FOR_MCTRL_RESPONSE);
56862306a36Sopenharmony_ci	hvsi_query(hp, VSV_SEND_MODEM_CTL_STATUS);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	ret = hvsi_wait(hp, HVSI_OPEN);
57162306a36Sopenharmony_ci	if (ret < 0) {
57262306a36Sopenharmony_ci		printk(KERN_ERR "hvsi%i: didn't get modem flags\n", hp->index);
57362306a36Sopenharmony_ci		set_state(hp, HVSI_OPEN);
57462306a36Sopenharmony_ci		return ret;
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	pr_debug("%s: mctrl 0x%x\n", __func__, hp->mctrl);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	return 0;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci/* note that we can only set DTR */
58362306a36Sopenharmony_cistatic int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct hvsi_control packet __ALIGNED__;
58662306a36Sopenharmony_ci	int wrote;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	packet.hdr.type = VS_CONTROL_PACKET_HEADER;
58962306a36Sopenharmony_ci	packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno));
59062306a36Sopenharmony_ci	packet.hdr.len = sizeof(struct hvsi_control);
59162306a36Sopenharmony_ci	packet.verb = cpu_to_be16(VSV_SET_MODEM_CTL);
59262306a36Sopenharmony_ci	packet.mask = cpu_to_be32(HVSI_TSDTR);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (mctrl & TIOCM_DTR)
59562306a36Sopenharmony_ci		packet.word = cpu_to_be32(HVSI_TSDTR);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len);
59862306a36Sopenharmony_ci	dbg_dump_hex((uint8_t*)&packet, packet.hdr.len);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len);
60162306a36Sopenharmony_ci	if (wrote != packet.hdr.len) {
60262306a36Sopenharmony_ci		printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index);
60362306a36Sopenharmony_ci		return -EIO;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	return 0;
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic void hvsi_drain_input(struct hvsi_struct *hp)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	uint8_t buf[HVSI_MAX_READ] __ALIGNED__;
61262306a36Sopenharmony_ci	unsigned long end_jiffies = jiffies + HVSI_TIMEOUT;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	while (time_before(end_jiffies, jiffies))
61562306a36Sopenharmony_ci		if (0 == hvsi_read(hp, buf, HVSI_MAX_READ))
61662306a36Sopenharmony_ci			break;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic int hvsi_handshake(struct hvsi_struct *hp)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	int ret;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/*
62462306a36Sopenharmony_ci	 * We could have a CLOSE or other data waiting for us before we even try
62562306a36Sopenharmony_ci	 * to open; try to throw it all away so we don't get confused. (CLOSE
62662306a36Sopenharmony_ci	 * is the first message sent up the pipe when the FSP comes online. We
62762306a36Sopenharmony_ci	 * need to distinguish between "it came up a while ago and we're the first
62862306a36Sopenharmony_ci	 * user" and "it was just reset before it saw our handshake packet".)
62962306a36Sopenharmony_ci	 */
63062306a36Sopenharmony_ci	hvsi_drain_input(hp);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	set_state(hp, HVSI_WAIT_FOR_VER_RESPONSE);
63362306a36Sopenharmony_ci	ret = hvsi_query(hp, VSV_SEND_VERSION_NUMBER);
63462306a36Sopenharmony_ci	if (ret < 0) {
63562306a36Sopenharmony_ci		printk(KERN_ERR "hvsi%i: couldn't send version query\n", hp->index);
63662306a36Sopenharmony_ci		return ret;
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	ret = hvsi_wait(hp, HVSI_OPEN);
64062306a36Sopenharmony_ci	if (ret < 0)
64162306a36Sopenharmony_ci		return ret;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	return 0;
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic void hvsi_handshaker(struct work_struct *work)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	struct hvsi_struct *hp =
64962306a36Sopenharmony_ci		container_of(work, struct hvsi_struct, handshaker);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	if (hvsi_handshake(hp) >= 0)
65262306a36Sopenharmony_ci		return;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	printk(KERN_ERR "hvsi%i: re-handshaking failed\n", hp->index);
65562306a36Sopenharmony_ci	if (is_console(hp)) {
65662306a36Sopenharmony_ci		/*
65762306a36Sopenharmony_ci		 * ttys will re-attempt the handshake via hvsi_open, but
65862306a36Sopenharmony_ci		 * the console will not.
65962306a36Sopenharmony_ci		 */
66062306a36Sopenharmony_ci		printk(KERN_ERR "hvsi%i: lost console!\n", hp->index);
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	struct hvsi_data packet __ALIGNED__;
66762306a36Sopenharmony_ci	int ret;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	BUG_ON(count > HVSI_MAX_OUTGOING_DATA);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	packet.hdr.type = VS_DATA_PACKET_HEADER;
67262306a36Sopenharmony_ci	packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno));
67362306a36Sopenharmony_ci	packet.hdr.len = count + sizeof(struct hvsi_header);
67462306a36Sopenharmony_ci	memcpy(&packet.data, buf, count);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len);
67762306a36Sopenharmony_ci	if (ret == packet.hdr.len) {
67862306a36Sopenharmony_ci		/* return the number of chars written, not the packet length */
67962306a36Sopenharmony_ci		return count;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci	return ret; /* return any errors */
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic void hvsi_close_protocol(struct hvsi_struct *hp)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct hvsi_control packet __ALIGNED__;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	packet.hdr.type = VS_CONTROL_PACKET_HEADER;
68962306a36Sopenharmony_ci	packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno));
69062306a36Sopenharmony_ci	packet.hdr.len = 6;
69162306a36Sopenharmony_ci	packet.verb = cpu_to_be16(VSV_CLOSE_PROTOCOL);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len);
69462306a36Sopenharmony_ci	dbg_dump_hex((uint8_t*)&packet, packet.hdr.len);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len);
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic int hvsi_open(struct tty_struct *tty, struct file *filp)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	struct hvsi_struct *hp;
70262306a36Sopenharmony_ci	unsigned long flags;
70362306a36Sopenharmony_ci	int ret;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	pr_debug("%s\n", __func__);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	hp = &hvsi_ports[tty->index];
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	tty->driver_data = hp;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	mb();
71262306a36Sopenharmony_ci	if (hp->state == HVSI_FSP_DIED)
71362306a36Sopenharmony_ci		return -EIO;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	tty_port_tty_set(&hp->port, tty);
71662306a36Sopenharmony_ci	spin_lock_irqsave(&hp->lock, flags);
71762306a36Sopenharmony_ci	hp->port.count++;
71862306a36Sopenharmony_ci	atomic_set(&hp->seqno, 0);
71962306a36Sopenharmony_ci	h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE);
72062306a36Sopenharmony_ci	spin_unlock_irqrestore(&hp->lock, flags);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	if (is_console(hp))
72362306a36Sopenharmony_ci		return 0; /* this has already been handshaked as the console */
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	ret = hvsi_handshake(hp);
72662306a36Sopenharmony_ci	if (ret < 0) {
72762306a36Sopenharmony_ci		printk(KERN_ERR "%s: HVSI handshaking failed\n", tty->name);
72862306a36Sopenharmony_ci		return ret;
72962306a36Sopenharmony_ci	}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	ret = hvsi_get_mctrl(hp);
73262306a36Sopenharmony_ci	if (ret < 0) {
73362306a36Sopenharmony_ci		printk(KERN_ERR "%s: couldn't get initial modem flags\n", tty->name);
73462306a36Sopenharmony_ci		return ret;
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR);
73862306a36Sopenharmony_ci	if (ret < 0) {
73962306a36Sopenharmony_ci		printk(KERN_ERR "%s: couldn't set DTR\n", tty->name);
74062306a36Sopenharmony_ci		return ret;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	return 0;
74462306a36Sopenharmony_ci}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci/* wait for hvsi_write_worker to empty hp->outbuf */
74762306a36Sopenharmony_cistatic void hvsi_flush_output(struct hvsi_struct *hp)
74862306a36Sopenharmony_ci{
74962306a36Sopenharmony_ci	wait_event_timeout(hp->emptyq, (hp->n_outbuf <= 0), HVSI_TIMEOUT);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	/* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */
75262306a36Sopenharmony_ci	cancel_delayed_work_sync(&hp->writer);
75362306a36Sopenharmony_ci	flush_work(&hp->handshaker);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/*
75662306a36Sopenharmony_ci	 * it's also possible that our timeout expired and hvsi_write_worker
75762306a36Sopenharmony_ci	 * didn't manage to push outbuf. poof.
75862306a36Sopenharmony_ci	 */
75962306a36Sopenharmony_ci	hp->n_outbuf = 0;
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic void hvsi_close(struct tty_struct *tty, struct file *filp)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	struct hvsi_struct *hp = tty->driver_data;
76562306a36Sopenharmony_ci	unsigned long flags;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	pr_debug("%s\n", __func__);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	if (tty_hung_up_p(filp))
77062306a36Sopenharmony_ci		return;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	spin_lock_irqsave(&hp->lock, flags);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (--hp->port.count == 0) {
77562306a36Sopenharmony_ci		tty_port_tty_set(&hp->port, NULL);
77662306a36Sopenharmony_ci		hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci		/* only close down connection if it is not the console */
77962306a36Sopenharmony_ci		if (!is_console(hp)) {
78062306a36Sopenharmony_ci			h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); /* no more irqs */
78162306a36Sopenharmony_ci			__set_state(hp, HVSI_CLOSED);
78262306a36Sopenharmony_ci			/*
78362306a36Sopenharmony_ci			 * any data delivered to the tty layer after this will be
78462306a36Sopenharmony_ci			 * discarded (except for XON/XOFF)
78562306a36Sopenharmony_ci			 */
78662306a36Sopenharmony_ci			tty->closing = 1;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci			spin_unlock_irqrestore(&hp->lock, flags);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci			/* let any existing irq handlers finish. no more will start. */
79162306a36Sopenharmony_ci			synchronize_irq(hp->virq);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci			/* hvsi_write_worker will re-schedule until outbuf is empty. */
79462306a36Sopenharmony_ci			hvsi_flush_output(hp);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci			/* tell FSP to stop sending data */
79762306a36Sopenharmony_ci			hvsi_close_protocol(hp);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci			/*
80062306a36Sopenharmony_ci			 * drain anything FSP is still in the middle of sending, and let
80162306a36Sopenharmony_ci			 * hvsi_handshake drain the rest on the next open.
80262306a36Sopenharmony_ci			 */
80362306a36Sopenharmony_ci			hvsi_drain_input(hp);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci			spin_lock_irqsave(&hp->lock, flags);
80662306a36Sopenharmony_ci		}
80762306a36Sopenharmony_ci	} else if (hp->port.count < 0)
80862306a36Sopenharmony_ci		printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n",
80962306a36Sopenharmony_ci		       hp - hvsi_ports, hp->port.count);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	spin_unlock_irqrestore(&hp->lock, flags);
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic void hvsi_hangup(struct tty_struct *tty)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	struct hvsi_struct *hp = tty->driver_data;
81762306a36Sopenharmony_ci	unsigned long flags;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	pr_debug("%s\n", __func__);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	tty_port_tty_set(&hp->port, NULL);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	spin_lock_irqsave(&hp->lock, flags);
82462306a36Sopenharmony_ci	hp->port.count = 0;
82562306a36Sopenharmony_ci	hp->n_outbuf = 0;
82662306a36Sopenharmony_ci	spin_unlock_irqrestore(&hp->lock, flags);
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci/* called with hp->lock held */
83062306a36Sopenharmony_cistatic void hvsi_push(struct hvsi_struct *hp)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	int n;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	if (hp->n_outbuf <= 0)
83562306a36Sopenharmony_ci		return;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf);
83862306a36Sopenharmony_ci	if (n > 0) {
83962306a36Sopenharmony_ci		/* success */
84062306a36Sopenharmony_ci		pr_debug("%s: wrote %i chars\n", __func__, n);
84162306a36Sopenharmony_ci		hp->n_outbuf = 0;
84262306a36Sopenharmony_ci	} else if (n == -EIO) {
84362306a36Sopenharmony_ci		__set_state(hp, HVSI_FSP_DIED);
84462306a36Sopenharmony_ci		printk(KERN_ERR "hvsi%i: service processor died\n", hp->index);
84562306a36Sopenharmony_ci	}
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci/* hvsi_write_worker will keep rescheduling itself until outbuf is empty */
84962306a36Sopenharmony_cistatic void hvsi_write_worker(struct work_struct *work)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	struct hvsi_struct *hp =
85262306a36Sopenharmony_ci		container_of(work, struct hvsi_struct, writer.work);
85362306a36Sopenharmony_ci	unsigned long flags;
85462306a36Sopenharmony_ci#ifdef DEBUG
85562306a36Sopenharmony_ci	static long start_j = 0;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if (start_j == 0)
85862306a36Sopenharmony_ci		start_j = jiffies;
85962306a36Sopenharmony_ci#endif /* DEBUG */
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	spin_lock_irqsave(&hp->lock, flags);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	if (!is_open(hp)) {
86662306a36Sopenharmony_ci		/*
86762306a36Sopenharmony_ci		 * We could have a non-open connection if the service processor died
86862306a36Sopenharmony_ci		 * while we were busily scheduling ourselves. In that case, it could
86962306a36Sopenharmony_ci		 * be minutes before the service processor comes back, so only try
87062306a36Sopenharmony_ci		 * again once a second.
87162306a36Sopenharmony_ci		 */
87262306a36Sopenharmony_ci		schedule_delayed_work(&hp->writer, HZ);
87362306a36Sopenharmony_ci		goto out;
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	hvsi_push(hp);
87762306a36Sopenharmony_ci	if (hp->n_outbuf > 0)
87862306a36Sopenharmony_ci		schedule_delayed_work(&hp->writer, 10);
87962306a36Sopenharmony_ci	else {
88062306a36Sopenharmony_ci#ifdef DEBUG
88162306a36Sopenharmony_ci		pr_debug("%s: outbuf emptied after %li jiffies\n", __func__,
88262306a36Sopenharmony_ci				jiffies - start_j);
88362306a36Sopenharmony_ci		start_j = 0;
88462306a36Sopenharmony_ci#endif /* DEBUG */
88562306a36Sopenharmony_ci		wake_up_all(&hp->emptyq);
88662306a36Sopenharmony_ci		tty_port_tty_wakeup(&hp->port);
88762306a36Sopenharmony_ci	}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ciout:
89062306a36Sopenharmony_ci	spin_unlock_irqrestore(&hp->lock, flags);
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_cistatic unsigned int hvsi_write_room(struct tty_struct *tty)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	struct hvsi_struct *hp = tty->driver_data;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	return N_OUTBUF - hp->n_outbuf;
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cistatic unsigned int hvsi_chars_in_buffer(struct tty_struct *tty)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	struct hvsi_struct *hp = tty->driver_data;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	return hp->n_outbuf;
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_cistatic ssize_t hvsi_write(struct tty_struct *tty, const u8 *source,
90862306a36Sopenharmony_ci			  size_t count)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	struct hvsi_struct *hp = tty->driver_data;
91162306a36Sopenharmony_ci	unsigned long flags;
91262306a36Sopenharmony_ci	size_t total = 0;
91362306a36Sopenharmony_ci	size_t origcount = count;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	spin_lock_irqsave(&hp->lock, flags);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf);
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	if (!is_open(hp)) {
92062306a36Sopenharmony_ci		/* we're either closing or not yet open; don't accept data */
92162306a36Sopenharmony_ci		pr_debug("%s: not open\n", __func__);
92262306a36Sopenharmony_ci		goto out;
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	/*
92662306a36Sopenharmony_ci	 * when the hypervisor buffer (16K) fills, data will stay in hp->outbuf
92762306a36Sopenharmony_ci	 * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls
92862306a36Sopenharmony_ci	 * will see there is no room in outbuf and return.
92962306a36Sopenharmony_ci	 */
93062306a36Sopenharmony_ci	while ((count > 0) && (hvsi_write_room(tty) > 0)) {
93162306a36Sopenharmony_ci		size_t chunksize = min_t(size_t, count, hvsi_write_room(tty));
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci		BUG_ON(hp->n_outbuf < 0);
93462306a36Sopenharmony_ci		memcpy(hp->outbuf + hp->n_outbuf, source, chunksize);
93562306a36Sopenharmony_ci		hp->n_outbuf += chunksize;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci		total += chunksize;
93862306a36Sopenharmony_ci		source += chunksize;
93962306a36Sopenharmony_ci		count -= chunksize;
94062306a36Sopenharmony_ci		hvsi_push(hp);
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	if (hp->n_outbuf > 0) {
94462306a36Sopenharmony_ci		/*
94562306a36Sopenharmony_ci		 * we weren't able to write it all to the hypervisor.
94662306a36Sopenharmony_ci		 * schedule another push attempt.
94762306a36Sopenharmony_ci		 */
94862306a36Sopenharmony_ci		schedule_delayed_work(&hp->writer, 10);
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ciout:
95262306a36Sopenharmony_ci	spin_unlock_irqrestore(&hp->lock, flags);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	if (total != origcount)
95562306a36Sopenharmony_ci		pr_debug("%s: wanted %zu, only wrote %zu\n", __func__,
95662306a36Sopenharmony_ci			 origcount, total);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	return total;
95962306a36Sopenharmony_ci}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci/*
96262306a36Sopenharmony_ci * I have never seen throttle or unthrottle called, so this little throttle
96362306a36Sopenharmony_ci * buffering scheme may or may not work.
96462306a36Sopenharmony_ci */
96562306a36Sopenharmony_cistatic void hvsi_throttle(struct tty_struct *tty)
96662306a36Sopenharmony_ci{
96762306a36Sopenharmony_ci	struct hvsi_struct *hp = tty->driver_data;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	pr_debug("%s\n", __func__);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE);
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cistatic void hvsi_unthrottle(struct tty_struct *tty)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	struct hvsi_struct *hp = tty->driver_data;
97762306a36Sopenharmony_ci	unsigned long flags;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	pr_debug("%s\n", __func__);
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	spin_lock_irqsave(&hp->lock, flags);
98262306a36Sopenharmony_ci	if (hp->n_throttle) {
98362306a36Sopenharmony_ci		hvsi_send_overflow(hp);
98462306a36Sopenharmony_ci		tty_flip_buffer_push(&hp->port);
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci	spin_unlock_irqrestore(&hp->lock, flags);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE);
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_cistatic int hvsi_tiocmget(struct tty_struct *tty)
99362306a36Sopenharmony_ci{
99462306a36Sopenharmony_ci	struct hvsi_struct *hp = tty->driver_data;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	hvsi_get_mctrl(hp);
99762306a36Sopenharmony_ci	return hp->mctrl;
99862306a36Sopenharmony_ci}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_cistatic int hvsi_tiocmset(struct tty_struct *tty,
100162306a36Sopenharmony_ci				unsigned int set, unsigned int clear)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	struct hvsi_struct *hp = tty->driver_data;
100462306a36Sopenharmony_ci	unsigned long flags;
100562306a36Sopenharmony_ci	uint16_t new_mctrl;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	/* we can only alter DTR */
100862306a36Sopenharmony_ci	clear &= TIOCM_DTR;
100962306a36Sopenharmony_ci	set &= TIOCM_DTR;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	spin_lock_irqsave(&hp->lock, flags);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	new_mctrl = (hp->mctrl & ~clear) | set;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (hp->mctrl != new_mctrl) {
101662306a36Sopenharmony_ci		hvsi_set_mctrl(hp, new_mctrl);
101762306a36Sopenharmony_ci		hp->mctrl = new_mctrl;
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci	spin_unlock_irqrestore(&hp->lock, flags);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	return 0;
102262306a36Sopenharmony_ci}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_cistatic const struct tty_operations hvsi_ops = {
102662306a36Sopenharmony_ci	.open = hvsi_open,
102762306a36Sopenharmony_ci	.close = hvsi_close,
102862306a36Sopenharmony_ci	.write = hvsi_write,
102962306a36Sopenharmony_ci	.hangup = hvsi_hangup,
103062306a36Sopenharmony_ci	.write_room = hvsi_write_room,
103162306a36Sopenharmony_ci	.chars_in_buffer = hvsi_chars_in_buffer,
103262306a36Sopenharmony_ci	.throttle = hvsi_throttle,
103362306a36Sopenharmony_ci	.unthrottle = hvsi_unthrottle,
103462306a36Sopenharmony_ci	.tiocmget = hvsi_tiocmget,
103562306a36Sopenharmony_ci	.tiocmset = hvsi_tiocmset,
103662306a36Sopenharmony_ci};
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_cistatic int __init hvsi_init(void)
103962306a36Sopenharmony_ci{
104062306a36Sopenharmony_ci	struct tty_driver *driver;
104162306a36Sopenharmony_ci	int i, ret;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	driver = tty_alloc_driver(hvsi_count, TTY_DRIVER_REAL_RAW);
104462306a36Sopenharmony_ci	if (IS_ERR(driver))
104562306a36Sopenharmony_ci		return PTR_ERR(driver);
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	driver->driver_name = "hvsi";
104862306a36Sopenharmony_ci	driver->name = "hvsi";
104962306a36Sopenharmony_ci	driver->major = HVSI_MAJOR;
105062306a36Sopenharmony_ci	driver->minor_start = HVSI_MINOR;
105162306a36Sopenharmony_ci	driver->type = TTY_DRIVER_TYPE_SYSTEM;
105262306a36Sopenharmony_ci	driver->init_termios = tty_std_termios;
105362306a36Sopenharmony_ci	driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
105462306a36Sopenharmony_ci	driver->init_termios.c_ispeed = 9600;
105562306a36Sopenharmony_ci	driver->init_termios.c_ospeed = 9600;
105662306a36Sopenharmony_ci	tty_set_operations(driver, &hvsi_ops);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	for (i=0; i < hvsi_count; i++) {
105962306a36Sopenharmony_ci		struct hvsi_struct *hp = &hvsi_ports[i];
106062306a36Sopenharmony_ci		int ret = 1;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci		tty_port_link_device(&hp->port, driver, i);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci		ret = request_irq(hp->virq, hvsi_interrupt, 0, "hvsi", hp);
106562306a36Sopenharmony_ci		if (ret)
106662306a36Sopenharmony_ci			printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n",
106762306a36Sopenharmony_ci				hp->virq, ret);
106862306a36Sopenharmony_ci	}
106962306a36Sopenharmony_ci	hvsi_wait = wait_for_state; /* irqs active now */
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	ret = tty_register_driver(driver);
107262306a36Sopenharmony_ci	if (ret) {
107362306a36Sopenharmony_ci		pr_err("Couldn't register hvsi console driver\n");
107462306a36Sopenharmony_ci		goto err_free_irq;
107562306a36Sopenharmony_ci	}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	hvsi_driver = driver;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	printk(KERN_DEBUG "HVSI: registered %i devices\n", hvsi_count);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	return 0;
108262306a36Sopenharmony_cierr_free_irq:
108362306a36Sopenharmony_ci	hvsi_wait = poll_for_state;
108462306a36Sopenharmony_ci	for (i = 0; i < hvsi_count; i++) {
108562306a36Sopenharmony_ci		struct hvsi_struct *hp = &hvsi_ports[i];
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci		free_irq(hp->virq, hp);
108862306a36Sopenharmony_ci	}
108962306a36Sopenharmony_ci	tty_driver_kref_put(driver);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	return ret;
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_cidevice_initcall(hvsi_init);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci/***** console (not tty) code: *****/
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cistatic void hvsi_console_print(struct console *console, const char *buf,
109862306a36Sopenharmony_ci		unsigned int count)
109962306a36Sopenharmony_ci{
110062306a36Sopenharmony_ci	struct hvsi_struct *hp = &hvsi_ports[console->index];
110162306a36Sopenharmony_ci	char c[HVSI_MAX_OUTGOING_DATA] __ALIGNED__;
110262306a36Sopenharmony_ci	unsigned int i = 0, n = 0;
110362306a36Sopenharmony_ci	int ret, donecr = 0;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	mb();
110662306a36Sopenharmony_ci	if (!is_open(hp))
110762306a36Sopenharmony_ci		return;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	/*
111062306a36Sopenharmony_ci	 * ugh, we have to translate LF -> CRLF ourselves, in place.
111162306a36Sopenharmony_ci	 * copied from hvc_console.c:
111262306a36Sopenharmony_ci	 */
111362306a36Sopenharmony_ci	while (count > 0 || i > 0) {
111462306a36Sopenharmony_ci		if (count > 0 && i < sizeof(c)) {
111562306a36Sopenharmony_ci			if (buf[n] == '\n' && !donecr) {
111662306a36Sopenharmony_ci				c[i++] = '\r';
111762306a36Sopenharmony_ci				donecr = 1;
111862306a36Sopenharmony_ci			} else {
111962306a36Sopenharmony_ci				c[i++] = buf[n++];
112062306a36Sopenharmony_ci				donecr = 0;
112162306a36Sopenharmony_ci				--count;
112262306a36Sopenharmony_ci			}
112362306a36Sopenharmony_ci		} else {
112462306a36Sopenharmony_ci			ret = hvsi_put_chars(hp, c, i);
112562306a36Sopenharmony_ci			if (ret < 0)
112662306a36Sopenharmony_ci				i = 0;
112762306a36Sopenharmony_ci			i -= ret;
112862306a36Sopenharmony_ci		}
112962306a36Sopenharmony_ci	}
113062306a36Sopenharmony_ci}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_cistatic struct tty_driver *hvsi_console_device(struct console *console,
113362306a36Sopenharmony_ci	int *index)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	*index = console->index;
113662306a36Sopenharmony_ci	return hvsi_driver;
113762306a36Sopenharmony_ci}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_cistatic int __init hvsi_console_setup(struct console *console, char *options)
114062306a36Sopenharmony_ci{
114162306a36Sopenharmony_ci	struct hvsi_struct *hp;
114262306a36Sopenharmony_ci	int ret;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	if (console->index < 0 || console->index >= hvsi_count)
114562306a36Sopenharmony_ci		return -EINVAL;
114662306a36Sopenharmony_ci	hp = &hvsi_ports[console->index];
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	/* give the FSP a chance to change the baud rate when we re-open */
114962306a36Sopenharmony_ci	hvsi_close_protocol(hp);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	ret = hvsi_handshake(hp);
115262306a36Sopenharmony_ci	if (ret < 0)
115362306a36Sopenharmony_ci		return ret;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	ret = hvsi_get_mctrl(hp);
115662306a36Sopenharmony_ci	if (ret < 0)
115762306a36Sopenharmony_ci		return ret;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR);
116062306a36Sopenharmony_ci	if (ret < 0)
116162306a36Sopenharmony_ci		return ret;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	hp->flags |= HVSI_CONSOLE;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	return 0;
116662306a36Sopenharmony_ci}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_cistatic struct console hvsi_console = {
116962306a36Sopenharmony_ci	.name		= "hvsi",
117062306a36Sopenharmony_ci	.write		= hvsi_console_print,
117162306a36Sopenharmony_ci	.device		= hvsi_console_device,
117262306a36Sopenharmony_ci	.setup		= hvsi_console_setup,
117362306a36Sopenharmony_ci	.flags		= CON_PRINTBUFFER,
117462306a36Sopenharmony_ci	.index		= -1,
117562306a36Sopenharmony_ci};
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_cistatic int __init hvsi_console_init(void)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	struct device_node *vty;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	hvsi_wait = poll_for_state; /* no irqs yet; must poll */
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	/* search device tree for vty nodes */
118462306a36Sopenharmony_ci	for_each_compatible_node(vty, "serial", "hvterm-protocol") {
118562306a36Sopenharmony_ci		struct hvsi_struct *hp;
118662306a36Sopenharmony_ci		const __be32 *vtermno, *irq;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		vtermno = of_get_property(vty, "reg", NULL);
118962306a36Sopenharmony_ci		irq = of_get_property(vty, "interrupts", NULL);
119062306a36Sopenharmony_ci		if (!vtermno || !irq)
119162306a36Sopenharmony_ci			continue;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci		if (hvsi_count >= MAX_NR_HVSI_CONSOLES) {
119462306a36Sopenharmony_ci			of_node_put(vty);
119562306a36Sopenharmony_ci			break;
119662306a36Sopenharmony_ci		}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci		hp = &hvsi_ports[hvsi_count];
119962306a36Sopenharmony_ci		INIT_DELAYED_WORK(&hp->writer, hvsi_write_worker);
120062306a36Sopenharmony_ci		INIT_WORK(&hp->handshaker, hvsi_handshaker);
120162306a36Sopenharmony_ci		init_waitqueue_head(&hp->emptyq);
120262306a36Sopenharmony_ci		init_waitqueue_head(&hp->stateq);
120362306a36Sopenharmony_ci		spin_lock_init(&hp->lock);
120462306a36Sopenharmony_ci		tty_port_init(&hp->port);
120562306a36Sopenharmony_ci		hp->index = hvsi_count;
120662306a36Sopenharmony_ci		hp->inbuf_end = hp->inbuf;
120762306a36Sopenharmony_ci		hp->state = HVSI_CLOSED;
120862306a36Sopenharmony_ci		hp->vtermno = be32_to_cpup(vtermno);
120962306a36Sopenharmony_ci		hp->virq = irq_create_mapping(NULL, be32_to_cpup(irq));
121062306a36Sopenharmony_ci		if (hp->virq == 0) {
121162306a36Sopenharmony_ci			printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n",
121262306a36Sopenharmony_ci			       __func__, be32_to_cpup(irq));
121362306a36Sopenharmony_ci			tty_port_destroy(&hp->port);
121462306a36Sopenharmony_ci			continue;
121562306a36Sopenharmony_ci		}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci		hvsi_count++;
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	if (hvsi_count)
122162306a36Sopenharmony_ci		register_console(&hvsi_console);
122262306a36Sopenharmony_ci	return 0;
122362306a36Sopenharmony_ci}
122462306a36Sopenharmony_ciconsole_initcall(hvsi_console_init);
1225