1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 2728674a7SGreg Kroah-Hartman /* 3728674a7SGreg Kroah-Hartman * Copyright (C) 2004 Hollis Blanchard <hollisb@us.ibm.com>, IBM 4728674a7SGreg Kroah-Hartman */ 5728674a7SGreg Kroah-Hartman 6728674a7SGreg Kroah-Hartman /* Host Virtual Serial Interface (HVSI) is a protocol between the hosted OS 7728674a7SGreg Kroah-Hartman * and the service processor on IBM pSeries servers. On these servers, there 8728674a7SGreg Kroah-Hartman * are no serial ports under the OS's control, and sometimes there is no other 9728674a7SGreg Kroah-Hartman * console available either. However, the service processor has two standard 10728674a7SGreg Kroah-Hartman * serial ports, so this over-complicated protocol allows the OS to control 11728674a7SGreg Kroah-Hartman * those ports by proxy. 12728674a7SGreg Kroah-Hartman * 13728674a7SGreg Kroah-Hartman * Besides data, the procotol supports the reading/writing of the serial 14728674a7SGreg Kroah-Hartman * port's DTR line, and the reading of the CD line. This is to allow the OS to 15728674a7SGreg Kroah-Hartman * control a modem attached to the service processor's serial port. Note that 16728674a7SGreg Kroah-Hartman * the OS cannot change the speed of the port through this protocol. 17728674a7SGreg Kroah-Hartman */ 18728674a7SGreg Kroah-Hartman 19728674a7SGreg Kroah-Hartman #undef DEBUG 20728674a7SGreg Kroah-Hartman 21728674a7SGreg Kroah-Hartman #include <linux/console.h> 22728674a7SGreg Kroah-Hartman #include <linux/ctype.h> 23728674a7SGreg Kroah-Hartman #include <linux/delay.h> 24728674a7SGreg Kroah-Hartman #include <linux/init.h> 25728674a7SGreg Kroah-Hartman #include <linux/interrupt.h> 26728674a7SGreg Kroah-Hartman #include <linux/module.h> 27728674a7SGreg Kroah-Hartman #include <linux/major.h> 28728674a7SGreg Kroah-Hartman #include <linux/kernel.h> 298fbb3fc9SChristophe Leroy #include <linux/of_irq.h> 30728674a7SGreg Kroah-Hartman #include <linux/spinlock.h> 31728674a7SGreg Kroah-Hartman #include <linux/sysrq.h> 32728674a7SGreg Kroah-Hartman #include <linux/tty.h> 33728674a7SGreg Kroah-Hartman #include <linux/tty_flip.h> 34728674a7SGreg Kroah-Hartman #include <asm/hvcall.h> 35728674a7SGreg Kroah-Hartman #include <asm/hvconsole.h> 367c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 37728674a7SGreg Kroah-Hartman #include <asm/vio.h> 38728674a7SGreg Kroah-Hartman #include <asm/param.h> 39725e789fSBenjamin Herrenschmidt #include <asm/hvsi.h> 40728674a7SGreg Kroah-Hartman 41728674a7SGreg Kroah-Hartman #define HVSI_MAJOR 229 42728674a7SGreg Kroah-Hartman #define HVSI_MINOR 128 43728674a7SGreg Kroah-Hartman #define MAX_NR_HVSI_CONSOLES 4 44728674a7SGreg Kroah-Hartman 45728674a7SGreg Kroah-Hartman #define HVSI_TIMEOUT (5*HZ) 46728674a7SGreg Kroah-Hartman #define HVSI_VERSION 1 47728674a7SGreg Kroah-Hartman #define HVSI_MAX_PACKET 256 48728674a7SGreg Kroah-Hartman #define HVSI_MAX_READ 16 49728674a7SGreg Kroah-Hartman #define HVSI_MAX_OUTGOING_DATA 12 50728674a7SGreg Kroah-Hartman #define N_OUTBUF 12 51728674a7SGreg Kroah-Hartman 52728674a7SGreg Kroah-Hartman /* 53728674a7SGreg Kroah-Hartman * we pass data via two 8-byte registers, so we would like our char arrays 54728674a7SGreg Kroah-Hartman * properly aligned for those loads. 55728674a7SGreg Kroah-Hartman */ 56728674a7SGreg Kroah-Hartman #define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) 57728674a7SGreg Kroah-Hartman 58728674a7SGreg Kroah-Hartman struct hvsi_struct { 59d73a4e79SJiri Slaby struct tty_port port; 60728674a7SGreg Kroah-Hartman struct delayed_work writer; 61728674a7SGreg Kroah-Hartman struct work_struct handshaker; 62728674a7SGreg Kroah-Hartman wait_queue_head_t emptyq; /* woken when outbuf is emptied */ 63728674a7SGreg Kroah-Hartman wait_queue_head_t stateq; /* woken when HVSI state changes */ 64728674a7SGreg Kroah-Hartman spinlock_t lock; 65728674a7SGreg Kroah-Hartman int index; 66728674a7SGreg Kroah-Hartman uint8_t throttle_buf[128]; 67728674a7SGreg Kroah-Hartman uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */ 68728674a7SGreg Kroah-Hartman /* inbuf is for packet reassembly. leave a little room for leftovers. */ 69728674a7SGreg Kroah-Hartman uint8_t inbuf[HVSI_MAX_PACKET + HVSI_MAX_READ]; 70728674a7SGreg Kroah-Hartman uint8_t *inbuf_end; 71728674a7SGreg Kroah-Hartman int n_throttle; 72728674a7SGreg Kroah-Hartman int n_outbuf; 73728674a7SGreg Kroah-Hartman uint32_t vtermno; 74728674a7SGreg Kroah-Hartman uint32_t virq; 75728674a7SGreg Kroah-Hartman atomic_t seqno; /* HVSI packet sequence number */ 76728674a7SGreg Kroah-Hartman uint16_t mctrl; 77728674a7SGreg Kroah-Hartman uint8_t state; /* HVSI protocol state */ 78728674a7SGreg Kroah-Hartman uint8_t flags; 79728674a7SGreg Kroah-Hartman #ifdef CONFIG_MAGIC_SYSRQ 80728674a7SGreg Kroah-Hartman uint8_t sysrq; 81728674a7SGreg Kroah-Hartman #endif /* CONFIG_MAGIC_SYSRQ */ 82728674a7SGreg Kroah-Hartman }; 83728674a7SGreg Kroah-Hartman static struct hvsi_struct hvsi_ports[MAX_NR_HVSI_CONSOLES]; 84728674a7SGreg Kroah-Hartman 85728674a7SGreg Kroah-Hartman static struct tty_driver *hvsi_driver; 86728674a7SGreg Kroah-Hartman static int hvsi_count; 87728674a7SGreg Kroah-Hartman static int (*hvsi_wait)(struct hvsi_struct *hp, int state); 88728674a7SGreg Kroah-Hartman 89728674a7SGreg Kroah-Hartman enum HVSI_PROTOCOL_STATE { 90728674a7SGreg Kroah-Hartman HVSI_CLOSED, 91728674a7SGreg Kroah-Hartman HVSI_WAIT_FOR_VER_RESPONSE, 92728674a7SGreg Kroah-Hartman HVSI_WAIT_FOR_VER_QUERY, 93728674a7SGreg Kroah-Hartman HVSI_OPEN, 94728674a7SGreg Kroah-Hartman HVSI_WAIT_FOR_MCTRL_RESPONSE, 95728674a7SGreg Kroah-Hartman HVSI_FSP_DIED, 96728674a7SGreg Kroah-Hartman }; 97728674a7SGreg Kroah-Hartman #define HVSI_CONSOLE 0x1 98728674a7SGreg Kroah-Hartman 99728674a7SGreg Kroah-Hartman static inline int is_console(struct hvsi_struct *hp) 100728674a7SGreg Kroah-Hartman { 101728674a7SGreg Kroah-Hartman return hp->flags & HVSI_CONSOLE; 102728674a7SGreg Kroah-Hartman } 103728674a7SGreg Kroah-Hartman 104728674a7SGreg Kroah-Hartman static inline int is_open(struct hvsi_struct *hp) 105728674a7SGreg Kroah-Hartman { 106728674a7SGreg Kroah-Hartman /* if we're waiting for an mctrl then we're already open */ 107728674a7SGreg Kroah-Hartman return (hp->state == HVSI_OPEN) 108728674a7SGreg Kroah-Hartman || (hp->state == HVSI_WAIT_FOR_MCTRL_RESPONSE); 109728674a7SGreg Kroah-Hartman } 110728674a7SGreg Kroah-Hartman 111728674a7SGreg Kroah-Hartman static inline void print_state(struct hvsi_struct *hp) 112728674a7SGreg Kroah-Hartman { 113728674a7SGreg Kroah-Hartman #ifdef DEBUG 114728674a7SGreg Kroah-Hartman static const char *state_names[] = { 115728674a7SGreg Kroah-Hartman "HVSI_CLOSED", 116728674a7SGreg Kroah-Hartman "HVSI_WAIT_FOR_VER_RESPONSE", 117728674a7SGreg Kroah-Hartman "HVSI_WAIT_FOR_VER_QUERY", 118728674a7SGreg Kroah-Hartman "HVSI_OPEN", 119728674a7SGreg Kroah-Hartman "HVSI_WAIT_FOR_MCTRL_RESPONSE", 120728674a7SGreg Kroah-Hartman "HVSI_FSP_DIED", 121728674a7SGreg Kroah-Hartman }; 122728674a7SGreg Kroah-Hartman const char *name = (hp->state < ARRAY_SIZE(state_names)) 123728674a7SGreg Kroah-Hartman ? state_names[hp->state] : "UNKNOWN"; 124728674a7SGreg Kroah-Hartman 125728674a7SGreg Kroah-Hartman pr_debug("hvsi%i: state = %s\n", hp->index, name); 126728674a7SGreg Kroah-Hartman #endif /* DEBUG */ 127728674a7SGreg Kroah-Hartman } 128728674a7SGreg Kroah-Hartman 129728674a7SGreg Kroah-Hartman static inline void __set_state(struct hvsi_struct *hp, int state) 130728674a7SGreg Kroah-Hartman { 131728674a7SGreg Kroah-Hartman hp->state = state; 132728674a7SGreg Kroah-Hartman print_state(hp); 133728674a7SGreg Kroah-Hartman wake_up_all(&hp->stateq); 134728674a7SGreg Kroah-Hartman } 135728674a7SGreg Kroah-Hartman 136728674a7SGreg Kroah-Hartman static inline void set_state(struct hvsi_struct *hp, int state) 137728674a7SGreg Kroah-Hartman { 138728674a7SGreg Kroah-Hartman unsigned long flags; 139728674a7SGreg Kroah-Hartman 140728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 141728674a7SGreg Kroah-Hartman __set_state(hp, state); 142728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 143728674a7SGreg Kroah-Hartman } 144728674a7SGreg Kroah-Hartman 145728674a7SGreg Kroah-Hartman static inline int len_packet(const uint8_t *packet) 146728674a7SGreg Kroah-Hartman { 147728674a7SGreg Kroah-Hartman return (int)((struct hvsi_header *)packet)->len; 148728674a7SGreg Kroah-Hartman } 149728674a7SGreg Kroah-Hartman 150728674a7SGreg Kroah-Hartman static inline int is_header(const uint8_t *packet) 151728674a7SGreg Kroah-Hartman { 152728674a7SGreg Kroah-Hartman struct hvsi_header *header = (struct hvsi_header *)packet; 153728674a7SGreg Kroah-Hartman return header->type >= VS_QUERY_RESPONSE_PACKET_HEADER; 154728674a7SGreg Kroah-Hartman } 155728674a7SGreg Kroah-Hartman 156728674a7SGreg Kroah-Hartman static inline int got_packet(const struct hvsi_struct *hp, uint8_t *packet) 157728674a7SGreg Kroah-Hartman { 158728674a7SGreg Kroah-Hartman if (hp->inbuf_end < packet + sizeof(struct hvsi_header)) 159728674a7SGreg Kroah-Hartman return 0; /* don't even have the packet header */ 160728674a7SGreg Kroah-Hartman 161728674a7SGreg Kroah-Hartman if (hp->inbuf_end < (packet + len_packet(packet))) 162728674a7SGreg Kroah-Hartman return 0; /* don't have the rest of the packet */ 163728674a7SGreg Kroah-Hartman 164728674a7SGreg Kroah-Hartman return 1; 165728674a7SGreg Kroah-Hartman } 166728674a7SGreg Kroah-Hartman 167728674a7SGreg Kroah-Hartman /* shift remaining bytes in packetbuf down */ 168728674a7SGreg Kroah-Hartman static void compact_inbuf(struct hvsi_struct *hp, uint8_t *read_to) 169728674a7SGreg Kroah-Hartman { 170728674a7SGreg Kroah-Hartman int remaining = (int)(hp->inbuf_end - read_to); 171728674a7SGreg Kroah-Hartman 172728674a7SGreg Kroah-Hartman pr_debug("%s: %i chars remain\n", __func__, remaining); 173728674a7SGreg Kroah-Hartman 174728674a7SGreg Kroah-Hartman if (read_to != hp->inbuf) 175728674a7SGreg Kroah-Hartman memmove(hp->inbuf, read_to, remaining); 176728674a7SGreg Kroah-Hartman 177728674a7SGreg Kroah-Hartman hp->inbuf_end = hp->inbuf + remaining; 178728674a7SGreg Kroah-Hartman } 179728674a7SGreg Kroah-Hartman 180728674a7SGreg Kroah-Hartman #ifdef DEBUG 181728674a7SGreg Kroah-Hartman #define dbg_dump_packet(packet) dump_packet(packet) 182728674a7SGreg Kroah-Hartman #define dbg_dump_hex(data, len) dump_hex(data, len) 183728674a7SGreg Kroah-Hartman #else 184728674a7SGreg Kroah-Hartman #define dbg_dump_packet(packet) do { } while (0) 185728674a7SGreg Kroah-Hartman #define dbg_dump_hex(data, len) do { } while (0) 186728674a7SGreg Kroah-Hartman #endif 187728674a7SGreg Kroah-Hartman 188728674a7SGreg Kroah-Hartman static void dump_hex(const uint8_t *data, int len) 189728674a7SGreg Kroah-Hartman { 190728674a7SGreg Kroah-Hartman int i; 191728674a7SGreg Kroah-Hartman 192728674a7SGreg Kroah-Hartman printk(" "); 193728674a7SGreg Kroah-Hartman for (i=0; i < len; i++) 194728674a7SGreg Kroah-Hartman printk("%.2x", data[i]); 195728674a7SGreg Kroah-Hartman 196728674a7SGreg Kroah-Hartman printk("\n "); 197728674a7SGreg Kroah-Hartman for (i=0; i < len; i++) { 198728674a7SGreg Kroah-Hartman if (isprint(data[i])) 199728674a7SGreg Kroah-Hartman printk("%c", data[i]); 200728674a7SGreg Kroah-Hartman else 201728674a7SGreg Kroah-Hartman printk("."); 202728674a7SGreg Kroah-Hartman } 203728674a7SGreg Kroah-Hartman printk("\n"); 204728674a7SGreg Kroah-Hartman } 205728674a7SGreg Kroah-Hartman 206728674a7SGreg Kroah-Hartman static void dump_packet(uint8_t *packet) 207728674a7SGreg Kroah-Hartman { 208728674a7SGreg Kroah-Hartman struct hvsi_header *header = (struct hvsi_header *)packet; 209728674a7SGreg Kroah-Hartman 210728674a7SGreg Kroah-Hartman printk("type 0x%x, len %i, seqno %i:\n", header->type, header->len, 211728674a7SGreg Kroah-Hartman header->seqno); 212728674a7SGreg Kroah-Hartman 213728674a7SGreg Kroah-Hartman dump_hex(packet, header->len); 214728674a7SGreg Kroah-Hartman } 215728674a7SGreg Kroah-Hartman 216728674a7SGreg Kroah-Hartman static int hvsi_read(struct hvsi_struct *hp, char *buf, int count) 217728674a7SGreg Kroah-Hartman { 218728674a7SGreg Kroah-Hartman unsigned long got; 219728674a7SGreg Kroah-Hartman 220728674a7SGreg Kroah-Hartman got = hvc_get_chars(hp->vtermno, buf, count); 221728674a7SGreg Kroah-Hartman 222728674a7SGreg Kroah-Hartman return got; 223728674a7SGreg Kroah-Hartman } 224728674a7SGreg Kroah-Hartman 225728674a7SGreg Kroah-Hartman static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, 22628c0447dSJiri Slaby struct tty_struct *tty, struct hvsi_struct **to_handshake) 227728674a7SGreg Kroah-Hartman { 228728674a7SGreg Kroah-Hartman struct hvsi_control *header = (struct hvsi_control *)packet; 229728674a7SGreg Kroah-Hartman 23048079804SLaurent Dufour switch (be16_to_cpu(header->verb)) { 231728674a7SGreg Kroah-Hartman case VSV_MODEM_CTL_UPDATE: 23248079804SLaurent Dufour if ((be32_to_cpu(header->word) & HVSI_TSCD) == 0) { 233728674a7SGreg Kroah-Hartman /* CD went away; no more connection */ 234728674a7SGreg Kroah-Hartman pr_debug("hvsi%i: CD dropped\n", hp->index); 235728674a7SGreg Kroah-Hartman hp->mctrl &= TIOCM_CD; 23628c0447dSJiri Slaby if (tty && !C_CLOCAL(tty)) 23728c0447dSJiri Slaby tty_hangup(tty); 238728674a7SGreg Kroah-Hartman } 239728674a7SGreg Kroah-Hartman break; 240728674a7SGreg Kroah-Hartman case VSV_CLOSE_PROTOCOL: 241728674a7SGreg Kroah-Hartman pr_debug("hvsi%i: service processor came back\n", hp->index); 242728674a7SGreg Kroah-Hartman if (hp->state != HVSI_CLOSED) { 243728674a7SGreg Kroah-Hartman *to_handshake = hp; 244728674a7SGreg Kroah-Hartman } 245728674a7SGreg Kroah-Hartman break; 246728674a7SGreg Kroah-Hartman default: 247728674a7SGreg Kroah-Hartman printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ", 248728674a7SGreg Kroah-Hartman hp->index); 249728674a7SGreg Kroah-Hartman dump_packet(packet); 250728674a7SGreg Kroah-Hartman break; 251728674a7SGreg Kroah-Hartman } 252728674a7SGreg Kroah-Hartman } 253728674a7SGreg Kroah-Hartman 254728674a7SGreg Kroah-Hartman static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet) 255728674a7SGreg Kroah-Hartman { 256728674a7SGreg Kroah-Hartman struct hvsi_query_response *resp = (struct hvsi_query_response *)packet; 25748079804SLaurent Dufour uint32_t mctrl_word; 258728674a7SGreg Kroah-Hartman 259728674a7SGreg Kroah-Hartman switch (hp->state) { 260728674a7SGreg Kroah-Hartman case HVSI_WAIT_FOR_VER_RESPONSE: 261728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_WAIT_FOR_VER_QUERY); 262728674a7SGreg Kroah-Hartman break; 263728674a7SGreg Kroah-Hartman case HVSI_WAIT_FOR_MCTRL_RESPONSE: 264728674a7SGreg Kroah-Hartman hp->mctrl = 0; 26548079804SLaurent Dufour mctrl_word = be32_to_cpu(resp->u.mctrl_word); 26648079804SLaurent Dufour if (mctrl_word & HVSI_TSDTR) 267728674a7SGreg Kroah-Hartman hp->mctrl |= TIOCM_DTR; 26848079804SLaurent Dufour if (mctrl_word & HVSI_TSCD) 269728674a7SGreg Kroah-Hartman hp->mctrl |= TIOCM_CD; 270728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_OPEN); 271728674a7SGreg Kroah-Hartman break; 272728674a7SGreg Kroah-Hartman default: 273728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: unexpected query response: ", hp->index); 274728674a7SGreg Kroah-Hartman dump_packet(packet); 275728674a7SGreg Kroah-Hartman break; 276728674a7SGreg Kroah-Hartman } 277728674a7SGreg Kroah-Hartman } 278728674a7SGreg Kroah-Hartman 279728674a7SGreg Kroah-Hartman /* respond to service processor's version query */ 280728674a7SGreg Kroah-Hartman static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) 281728674a7SGreg Kroah-Hartman { 282728674a7SGreg Kroah-Hartman struct hvsi_query_response packet __ALIGNED__; 283728674a7SGreg Kroah-Hartman int wrote; 284728674a7SGreg Kroah-Hartman 285048bee77SBenjamin Herrenschmidt packet.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER; 286048bee77SBenjamin Herrenschmidt packet.hdr.len = sizeof(struct hvsi_query_response); 28748079804SLaurent Dufour packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 28848079804SLaurent Dufour packet.verb = cpu_to_be16(VSV_SEND_VERSION_NUMBER); 289728674a7SGreg Kroah-Hartman packet.u.version = HVSI_VERSION; 29048079804SLaurent Dufour packet.query_seqno = cpu_to_be16(query_seqno+1); 291728674a7SGreg Kroah-Hartman 292048bee77SBenjamin Herrenschmidt pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); 293048bee77SBenjamin Herrenschmidt dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); 294728674a7SGreg Kroah-Hartman 295048bee77SBenjamin Herrenschmidt wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 296048bee77SBenjamin Herrenschmidt if (wrote != packet.hdr.len) { 297728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: couldn't send query response!\n", 298728674a7SGreg Kroah-Hartman hp->index); 299728674a7SGreg Kroah-Hartman return -EIO; 300728674a7SGreg Kroah-Hartman } 301728674a7SGreg Kroah-Hartman 302728674a7SGreg Kroah-Hartman return 0; 303728674a7SGreg Kroah-Hartman } 304728674a7SGreg Kroah-Hartman 305728674a7SGreg Kroah-Hartman static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) 306728674a7SGreg Kroah-Hartman { 307728674a7SGreg Kroah-Hartman struct hvsi_query *query = (struct hvsi_query *)packet; 308728674a7SGreg Kroah-Hartman 309728674a7SGreg Kroah-Hartman switch (hp->state) { 310728674a7SGreg Kroah-Hartman case HVSI_WAIT_FOR_VER_QUERY: 31148079804SLaurent Dufour hvsi_version_respond(hp, be16_to_cpu(query->hdr.seqno)); 312728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_OPEN); 313728674a7SGreg Kroah-Hartman break; 314728674a7SGreg Kroah-Hartman default: 315728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: unexpected query: ", hp->index); 316728674a7SGreg Kroah-Hartman dump_packet(packet); 317728674a7SGreg Kroah-Hartman break; 318728674a7SGreg Kroah-Hartman } 319728674a7SGreg Kroah-Hartman } 320728674a7SGreg Kroah-Hartman 32192a19f9cSJiri Slaby static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) 322728674a7SGreg Kroah-Hartman { 323728674a7SGreg Kroah-Hartman int i; 324728674a7SGreg Kroah-Hartman 325728674a7SGreg Kroah-Hartman for (i=0; i < len; i++) { 326728674a7SGreg Kroah-Hartman char c = buf[i]; 327728674a7SGreg Kroah-Hartman #ifdef CONFIG_MAGIC_SYSRQ 328728674a7SGreg Kroah-Hartman if (c == '\0') { 329728674a7SGreg Kroah-Hartman hp->sysrq = 1; 330728674a7SGreg Kroah-Hartman continue; 331728674a7SGreg Kroah-Hartman } else if (hp->sysrq) { 332728674a7SGreg Kroah-Hartman handle_sysrq(c); 333728674a7SGreg Kroah-Hartman hp->sysrq = 0; 334728674a7SGreg Kroah-Hartman continue; 335728674a7SGreg Kroah-Hartman } 336728674a7SGreg Kroah-Hartman #endif /* CONFIG_MAGIC_SYSRQ */ 33792a19f9cSJiri Slaby tty_insert_flip_char(&hp->port, c, 0); 338728674a7SGreg Kroah-Hartman } 339728674a7SGreg Kroah-Hartman } 340728674a7SGreg Kroah-Hartman 341728674a7SGreg Kroah-Hartman /* 342728674a7SGreg Kroah-Hartman * We could get 252 bytes of data at once here. But the tty layer only 343728674a7SGreg Kroah-Hartman * throttles us at TTY_THRESHOLD_THROTTLE (128) bytes, so we could overflow 344728674a7SGreg Kroah-Hartman * it. Accordingly we won't send more than 128 bytes at a time to the flip 345728674a7SGreg Kroah-Hartman * buffer, which will give the tty buffer a chance to throttle us. Should the 346728674a7SGreg Kroah-Hartman * value of TTY_THRESHOLD_THROTTLE change in n_tty.c, this code should be 347728674a7SGreg Kroah-Hartman * revisited. 348728674a7SGreg Kroah-Hartman */ 349728674a7SGreg Kroah-Hartman #define TTY_THRESHOLD_THROTTLE 128 35092a19f9cSJiri Slaby static bool hvsi_recv_data(struct hvsi_struct *hp, const uint8_t *packet) 351728674a7SGreg Kroah-Hartman { 352728674a7SGreg Kroah-Hartman const struct hvsi_header *header = (const struct hvsi_header *)packet; 353728674a7SGreg Kroah-Hartman const uint8_t *data = packet + sizeof(struct hvsi_header); 354728674a7SGreg Kroah-Hartman int datalen = header->len - sizeof(struct hvsi_header); 355728674a7SGreg Kroah-Hartman int overflow = datalen - TTY_THRESHOLD_THROTTLE; 356728674a7SGreg Kroah-Hartman 357728674a7SGreg Kroah-Hartman pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data); 358728674a7SGreg Kroah-Hartman 359728674a7SGreg Kroah-Hartman if (datalen == 0) 36028c0447dSJiri Slaby return false; 361728674a7SGreg Kroah-Hartman 362728674a7SGreg Kroah-Hartman if (overflow > 0) { 363728674a7SGreg Kroah-Hartman pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __func__); 364728674a7SGreg Kroah-Hartman datalen = TTY_THRESHOLD_THROTTLE; 365728674a7SGreg Kroah-Hartman } 366728674a7SGreg Kroah-Hartman 36792a19f9cSJiri Slaby hvsi_insert_chars(hp, data, datalen); 368728674a7SGreg Kroah-Hartman 369728674a7SGreg Kroah-Hartman if (overflow > 0) { 370728674a7SGreg Kroah-Hartman /* 371728674a7SGreg Kroah-Hartman * we still have more data to deliver, so we need to save off the 372728674a7SGreg Kroah-Hartman * overflow and send it later 373728674a7SGreg Kroah-Hartman */ 374728674a7SGreg Kroah-Hartman pr_debug("%s: deferring overflow\n", __func__); 375728674a7SGreg Kroah-Hartman memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow); 376728674a7SGreg Kroah-Hartman hp->n_throttle = overflow; 377728674a7SGreg Kroah-Hartman } 378728674a7SGreg Kroah-Hartman 37928c0447dSJiri Slaby return true; 380728674a7SGreg Kroah-Hartman } 381728674a7SGreg Kroah-Hartman 382728674a7SGreg Kroah-Hartman /* 383728674a7SGreg Kroah-Hartman * Returns true/false indicating data successfully read from hypervisor. 384728674a7SGreg Kroah-Hartman * Used both to get packets for tty connections and to advance the state 385728674a7SGreg Kroah-Hartman * machine during console handshaking (in which case tty = NULL and we ignore 386728674a7SGreg Kroah-Hartman * incoming data). 387728674a7SGreg Kroah-Hartman */ 38828c0447dSJiri Slaby static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct *tty, 38928c0447dSJiri Slaby struct hvsi_struct **handshake) 390728674a7SGreg Kroah-Hartman { 391728674a7SGreg Kroah-Hartman uint8_t *packet = hp->inbuf; 392728674a7SGreg Kroah-Hartman int chunklen; 39328c0447dSJiri Slaby bool flip = false; 394728674a7SGreg Kroah-Hartman 395728674a7SGreg Kroah-Hartman *handshake = NULL; 396728674a7SGreg Kroah-Hartman 397728674a7SGreg Kroah-Hartman chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); 398728674a7SGreg Kroah-Hartman if (chunklen == 0) { 399728674a7SGreg Kroah-Hartman pr_debug("%s: 0-length read\n", __func__); 400728674a7SGreg Kroah-Hartman return 0; 401728674a7SGreg Kroah-Hartman } 402728674a7SGreg Kroah-Hartman 403728674a7SGreg Kroah-Hartman pr_debug("%s: got %i bytes\n", __func__, chunklen); 404728674a7SGreg Kroah-Hartman dbg_dump_hex(hp->inbuf_end, chunklen); 405728674a7SGreg Kroah-Hartman 406728674a7SGreg Kroah-Hartman hp->inbuf_end += chunklen; 407728674a7SGreg Kroah-Hartman 408728674a7SGreg Kroah-Hartman /* handle all completed packets */ 409728674a7SGreg Kroah-Hartman while ((packet < hp->inbuf_end) && got_packet(hp, packet)) { 410728674a7SGreg Kroah-Hartman struct hvsi_header *header = (struct hvsi_header *)packet; 411728674a7SGreg Kroah-Hartman 412728674a7SGreg Kroah-Hartman if (!is_header(packet)) { 413728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: got malformed packet\n", hp->index); 414728674a7SGreg Kroah-Hartman /* skip bytes until we find a header or run out of data */ 415728674a7SGreg Kroah-Hartman while ((packet < hp->inbuf_end) && (!is_header(packet))) 416728674a7SGreg Kroah-Hartman packet++; 417728674a7SGreg Kroah-Hartman continue; 418728674a7SGreg Kroah-Hartman } 419728674a7SGreg Kroah-Hartman 420728674a7SGreg Kroah-Hartman pr_debug("%s: handling %i-byte packet\n", __func__, 421728674a7SGreg Kroah-Hartman len_packet(packet)); 422728674a7SGreg Kroah-Hartman dbg_dump_packet(packet); 423728674a7SGreg Kroah-Hartman 424728674a7SGreg Kroah-Hartman switch (header->type) { 425728674a7SGreg Kroah-Hartman case VS_DATA_PACKET_HEADER: 426728674a7SGreg Kroah-Hartman if (!is_open(hp)) 427728674a7SGreg Kroah-Hartman break; 42892a19f9cSJiri Slaby flip = hvsi_recv_data(hp, packet); 429728674a7SGreg Kroah-Hartman break; 430728674a7SGreg Kroah-Hartman case VS_CONTROL_PACKET_HEADER: 43128c0447dSJiri Slaby hvsi_recv_control(hp, packet, tty, handshake); 432728674a7SGreg Kroah-Hartman break; 433728674a7SGreg Kroah-Hartman case VS_QUERY_RESPONSE_PACKET_HEADER: 434728674a7SGreg Kroah-Hartman hvsi_recv_response(hp, packet); 435728674a7SGreg Kroah-Hartman break; 436728674a7SGreg Kroah-Hartman case VS_QUERY_PACKET_HEADER: 437728674a7SGreg Kroah-Hartman hvsi_recv_query(hp, packet); 438728674a7SGreg Kroah-Hartman break; 439728674a7SGreg Kroah-Hartman default: 440728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: unknown HVSI packet type 0x%x\n", 441728674a7SGreg Kroah-Hartman hp->index, header->type); 442728674a7SGreg Kroah-Hartman dump_packet(packet); 443728674a7SGreg Kroah-Hartman break; 444728674a7SGreg Kroah-Hartman } 445728674a7SGreg Kroah-Hartman 446728674a7SGreg Kroah-Hartman packet += len_packet(packet); 447728674a7SGreg Kroah-Hartman 44828c0447dSJiri Slaby if (*handshake) { 44928c0447dSJiri Slaby pr_debug("%s: handshake\n", __func__); 450728674a7SGreg Kroah-Hartman break; 451728674a7SGreg Kroah-Hartman } 452728674a7SGreg Kroah-Hartman } 453728674a7SGreg Kroah-Hartman 454728674a7SGreg Kroah-Hartman compact_inbuf(hp, packet); 455728674a7SGreg Kroah-Hartman 45628c0447dSJiri Slaby if (flip) 4572e124b4aSJiri Slaby tty_flip_buffer_push(&hp->port); 45828c0447dSJiri Slaby 459728674a7SGreg Kroah-Hartman return 1; 460728674a7SGreg Kroah-Hartman } 461728674a7SGreg Kroah-Hartman 46292a19f9cSJiri Slaby static void hvsi_send_overflow(struct hvsi_struct *hp) 463728674a7SGreg Kroah-Hartman { 464728674a7SGreg Kroah-Hartman pr_debug("%s: delivering %i bytes overflow\n", __func__, 465728674a7SGreg Kroah-Hartman hp->n_throttle); 466728674a7SGreg Kroah-Hartman 46792a19f9cSJiri Slaby hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); 468728674a7SGreg Kroah-Hartman hp->n_throttle = 0; 469728674a7SGreg Kroah-Hartman } 470728674a7SGreg Kroah-Hartman 471728674a7SGreg Kroah-Hartman /* 472728674a7SGreg Kroah-Hartman * must get all pending data because we only get an irq on empty->non-empty 473728674a7SGreg Kroah-Hartman * transition 474728674a7SGreg Kroah-Hartman */ 475728674a7SGreg Kroah-Hartman static irqreturn_t hvsi_interrupt(int irq, void *arg) 476728674a7SGreg Kroah-Hartman { 477728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = (struct hvsi_struct *)arg; 478728674a7SGreg Kroah-Hartman struct hvsi_struct *handshake; 479daea4402SJiri Slaby struct tty_struct *tty; 480728674a7SGreg Kroah-Hartman unsigned long flags; 481728674a7SGreg Kroah-Hartman int again = 1; 482728674a7SGreg Kroah-Hartman 483728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 484728674a7SGreg Kroah-Hartman 485daea4402SJiri Slaby tty = tty_port_tty_get(&hp->port); 486daea4402SJiri Slaby 487728674a7SGreg Kroah-Hartman while (again) { 488728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 489daea4402SJiri Slaby again = hvsi_load_chunk(hp, tty, &handshake); 490728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 491728674a7SGreg Kroah-Hartman 492728674a7SGreg Kroah-Hartman if (handshake) { 493728674a7SGreg Kroah-Hartman pr_debug("hvsi%i: attempting re-handshake\n", handshake->index); 494728674a7SGreg Kroah-Hartman schedule_work(&handshake->handshaker); 495728674a7SGreg Kroah-Hartman } 496728674a7SGreg Kroah-Hartman } 497728674a7SGreg Kroah-Hartman 498728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 49997ef38b8SPeter Hurley if (tty && hp->n_throttle && !tty_throttled(tty)) { 500daea4402SJiri Slaby /* we weren't hung up and we weren't throttled, so we can 501daea4402SJiri Slaby * deliver the rest now */ 50292a19f9cSJiri Slaby hvsi_send_overflow(hp); 5032e124b4aSJiri Slaby tty_flip_buffer_push(&hp->port); 504728674a7SGreg Kroah-Hartman } 505728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 506728674a7SGreg Kroah-Hartman 507daea4402SJiri Slaby tty_kref_put(tty); 508daea4402SJiri Slaby 509728674a7SGreg Kroah-Hartman return IRQ_HANDLED; 510728674a7SGreg Kroah-Hartman } 511728674a7SGreg Kroah-Hartman 512728674a7SGreg Kroah-Hartman /* for boot console, before the irq handler is running */ 513728674a7SGreg Kroah-Hartman static int __init poll_for_state(struct hvsi_struct *hp, int state) 514728674a7SGreg Kroah-Hartman { 515728674a7SGreg Kroah-Hartman unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; 516728674a7SGreg Kroah-Hartman 517728674a7SGreg Kroah-Hartman for (;;) { 518728674a7SGreg Kroah-Hartman hvsi_interrupt(hp->virq, (void *)hp); /* get pending data */ 519728674a7SGreg Kroah-Hartman 520728674a7SGreg Kroah-Hartman if (hp->state == state) 521728674a7SGreg Kroah-Hartman return 0; 522728674a7SGreg Kroah-Hartman 523728674a7SGreg Kroah-Hartman mdelay(5); 524728674a7SGreg Kroah-Hartman if (time_after(jiffies, end_jiffies)) 525728674a7SGreg Kroah-Hartman return -EIO; 526728674a7SGreg Kroah-Hartman } 527728674a7SGreg Kroah-Hartman } 528728674a7SGreg Kroah-Hartman 529728674a7SGreg Kroah-Hartman /* wait for irq handler to change our state */ 530728674a7SGreg Kroah-Hartman static int wait_for_state(struct hvsi_struct *hp, int state) 531728674a7SGreg Kroah-Hartman { 532728674a7SGreg Kroah-Hartman int ret = 0; 533728674a7SGreg Kroah-Hartman 534728674a7SGreg Kroah-Hartman if (!wait_event_timeout(hp->stateq, (hp->state == state), HVSI_TIMEOUT)) 535728674a7SGreg Kroah-Hartman ret = -EIO; 536728674a7SGreg Kroah-Hartman 537728674a7SGreg Kroah-Hartman return ret; 538728674a7SGreg Kroah-Hartman } 539728674a7SGreg Kroah-Hartman 540728674a7SGreg Kroah-Hartman static int hvsi_query(struct hvsi_struct *hp, uint16_t verb) 541728674a7SGreg Kroah-Hartman { 542728674a7SGreg Kroah-Hartman struct hvsi_query packet __ALIGNED__; 543728674a7SGreg Kroah-Hartman int wrote; 544728674a7SGreg Kroah-Hartman 545048bee77SBenjamin Herrenschmidt packet.hdr.type = VS_QUERY_PACKET_HEADER; 546048bee77SBenjamin Herrenschmidt packet.hdr.len = sizeof(struct hvsi_query); 54748079804SLaurent Dufour packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 54848079804SLaurent Dufour packet.verb = cpu_to_be16(verb); 549728674a7SGreg Kroah-Hartman 550048bee77SBenjamin Herrenschmidt pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); 551048bee77SBenjamin Herrenschmidt dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); 552728674a7SGreg Kroah-Hartman 553048bee77SBenjamin Herrenschmidt wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 554048bee77SBenjamin Herrenschmidt if (wrote != packet.hdr.len) { 555728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index, 556728674a7SGreg Kroah-Hartman wrote); 557728674a7SGreg Kroah-Hartman return -EIO; 558728674a7SGreg Kroah-Hartman } 559728674a7SGreg Kroah-Hartman 560728674a7SGreg Kroah-Hartman return 0; 561728674a7SGreg Kroah-Hartman } 562728674a7SGreg Kroah-Hartman 563728674a7SGreg Kroah-Hartman static int hvsi_get_mctrl(struct hvsi_struct *hp) 564728674a7SGreg Kroah-Hartman { 565728674a7SGreg Kroah-Hartman int ret; 566728674a7SGreg Kroah-Hartman 567728674a7SGreg Kroah-Hartman set_state(hp, HVSI_WAIT_FOR_MCTRL_RESPONSE); 568728674a7SGreg Kroah-Hartman hvsi_query(hp, VSV_SEND_MODEM_CTL_STATUS); 569728674a7SGreg Kroah-Hartman 570728674a7SGreg Kroah-Hartman ret = hvsi_wait(hp, HVSI_OPEN); 571728674a7SGreg Kroah-Hartman if (ret < 0) { 572728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: didn't get modem flags\n", hp->index); 573728674a7SGreg Kroah-Hartman set_state(hp, HVSI_OPEN); 574728674a7SGreg Kroah-Hartman return ret; 575728674a7SGreg Kroah-Hartman } 576728674a7SGreg Kroah-Hartman 577728674a7SGreg Kroah-Hartman pr_debug("%s: mctrl 0x%x\n", __func__, hp->mctrl); 578728674a7SGreg Kroah-Hartman 579728674a7SGreg Kroah-Hartman return 0; 580728674a7SGreg Kroah-Hartman } 581728674a7SGreg Kroah-Hartman 582728674a7SGreg Kroah-Hartman /* note that we can only set DTR */ 583728674a7SGreg Kroah-Hartman static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) 584728674a7SGreg Kroah-Hartman { 585728674a7SGreg Kroah-Hartman struct hvsi_control packet __ALIGNED__; 586728674a7SGreg Kroah-Hartman int wrote; 587728674a7SGreg Kroah-Hartman 58848079804SLaurent Dufour packet.hdr.type = VS_CONTROL_PACKET_HEADER; 58948079804SLaurent Dufour packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 590048bee77SBenjamin Herrenschmidt packet.hdr.len = sizeof(struct hvsi_control); 59148079804SLaurent Dufour packet.verb = cpu_to_be16(VSV_SET_MODEM_CTL); 59248079804SLaurent Dufour packet.mask = cpu_to_be32(HVSI_TSDTR); 593728674a7SGreg Kroah-Hartman 594728674a7SGreg Kroah-Hartman if (mctrl & TIOCM_DTR) 59548079804SLaurent Dufour packet.word = cpu_to_be32(HVSI_TSDTR); 596728674a7SGreg Kroah-Hartman 597048bee77SBenjamin Herrenschmidt pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); 598048bee77SBenjamin Herrenschmidt dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); 599728674a7SGreg Kroah-Hartman 600048bee77SBenjamin Herrenschmidt wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 601048bee77SBenjamin Herrenschmidt if (wrote != packet.hdr.len) { 602728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index); 603728674a7SGreg Kroah-Hartman return -EIO; 604728674a7SGreg Kroah-Hartman } 605728674a7SGreg Kroah-Hartman 606728674a7SGreg Kroah-Hartman return 0; 607728674a7SGreg Kroah-Hartman } 608728674a7SGreg Kroah-Hartman 609728674a7SGreg Kroah-Hartman static void hvsi_drain_input(struct hvsi_struct *hp) 610728674a7SGreg Kroah-Hartman { 611728674a7SGreg Kroah-Hartman uint8_t buf[HVSI_MAX_READ] __ALIGNED__; 612728674a7SGreg Kroah-Hartman unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; 613728674a7SGreg Kroah-Hartman 614728674a7SGreg Kroah-Hartman while (time_before(end_jiffies, jiffies)) 615728674a7SGreg Kroah-Hartman if (0 == hvsi_read(hp, buf, HVSI_MAX_READ)) 616728674a7SGreg Kroah-Hartman break; 617728674a7SGreg Kroah-Hartman } 618728674a7SGreg Kroah-Hartman 619728674a7SGreg Kroah-Hartman static int hvsi_handshake(struct hvsi_struct *hp) 620728674a7SGreg Kroah-Hartman { 621728674a7SGreg Kroah-Hartman int ret; 622728674a7SGreg Kroah-Hartman 623728674a7SGreg Kroah-Hartman /* 624728674a7SGreg Kroah-Hartman * We could have a CLOSE or other data waiting for us before we even try 625728674a7SGreg Kroah-Hartman * to open; try to throw it all away so we don't get confused. (CLOSE 626728674a7SGreg Kroah-Hartman * is the first message sent up the pipe when the FSP comes online. We 627728674a7SGreg Kroah-Hartman * need to distinguish between "it came up a while ago and we're the first 628728674a7SGreg Kroah-Hartman * user" and "it was just reset before it saw our handshake packet".) 629728674a7SGreg Kroah-Hartman */ 630728674a7SGreg Kroah-Hartman hvsi_drain_input(hp); 631728674a7SGreg Kroah-Hartman 632728674a7SGreg Kroah-Hartman set_state(hp, HVSI_WAIT_FOR_VER_RESPONSE); 633728674a7SGreg Kroah-Hartman ret = hvsi_query(hp, VSV_SEND_VERSION_NUMBER); 634728674a7SGreg Kroah-Hartman if (ret < 0) { 635728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: couldn't send version query\n", hp->index); 636728674a7SGreg Kroah-Hartman return ret; 637728674a7SGreg Kroah-Hartman } 638728674a7SGreg Kroah-Hartman 639728674a7SGreg Kroah-Hartman ret = hvsi_wait(hp, HVSI_OPEN); 640728674a7SGreg Kroah-Hartman if (ret < 0) 641728674a7SGreg Kroah-Hartman return ret; 642728674a7SGreg Kroah-Hartman 643728674a7SGreg Kroah-Hartman return 0; 644728674a7SGreg Kroah-Hartman } 645728674a7SGreg Kroah-Hartman 646728674a7SGreg Kroah-Hartman static void hvsi_handshaker(struct work_struct *work) 647728674a7SGreg Kroah-Hartman { 648728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = 649728674a7SGreg Kroah-Hartman container_of(work, struct hvsi_struct, handshaker); 650728674a7SGreg Kroah-Hartman 651728674a7SGreg Kroah-Hartman if (hvsi_handshake(hp) >= 0) 652728674a7SGreg Kroah-Hartman return; 653728674a7SGreg Kroah-Hartman 654728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: re-handshaking failed\n", hp->index); 655728674a7SGreg Kroah-Hartman if (is_console(hp)) { 656728674a7SGreg Kroah-Hartman /* 657728674a7SGreg Kroah-Hartman * ttys will re-attempt the handshake via hvsi_open, but 658728674a7SGreg Kroah-Hartman * the console will not. 659728674a7SGreg Kroah-Hartman */ 660728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: lost console!\n", hp->index); 661728674a7SGreg Kroah-Hartman } 662728674a7SGreg Kroah-Hartman } 663728674a7SGreg Kroah-Hartman 664728674a7SGreg Kroah-Hartman static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count) 665728674a7SGreg Kroah-Hartman { 666728674a7SGreg Kroah-Hartman struct hvsi_data packet __ALIGNED__; 667728674a7SGreg Kroah-Hartman int ret; 668728674a7SGreg Kroah-Hartman 669728674a7SGreg Kroah-Hartman BUG_ON(count > HVSI_MAX_OUTGOING_DATA); 670728674a7SGreg Kroah-Hartman 671048bee77SBenjamin Herrenschmidt packet.hdr.type = VS_DATA_PACKET_HEADER; 67248079804SLaurent Dufour packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 673048bee77SBenjamin Herrenschmidt packet.hdr.len = count + sizeof(struct hvsi_header); 674728674a7SGreg Kroah-Hartman memcpy(&packet.data, buf, count); 675728674a7SGreg Kroah-Hartman 676048bee77SBenjamin Herrenschmidt ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 677048bee77SBenjamin Herrenschmidt if (ret == packet.hdr.len) { 678728674a7SGreg Kroah-Hartman /* return the number of chars written, not the packet length */ 679728674a7SGreg Kroah-Hartman return count; 680728674a7SGreg Kroah-Hartman } 681728674a7SGreg Kroah-Hartman return ret; /* return any errors */ 682728674a7SGreg Kroah-Hartman } 683728674a7SGreg Kroah-Hartman 684728674a7SGreg Kroah-Hartman static void hvsi_close_protocol(struct hvsi_struct *hp) 685728674a7SGreg Kroah-Hartman { 686728674a7SGreg Kroah-Hartman struct hvsi_control packet __ALIGNED__; 687728674a7SGreg Kroah-Hartman 688048bee77SBenjamin Herrenschmidt packet.hdr.type = VS_CONTROL_PACKET_HEADER; 68948079804SLaurent Dufour packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 690048bee77SBenjamin Herrenschmidt packet.hdr.len = 6; 69148079804SLaurent Dufour packet.verb = cpu_to_be16(VSV_CLOSE_PROTOCOL); 692728674a7SGreg Kroah-Hartman 693048bee77SBenjamin Herrenschmidt pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); 694048bee77SBenjamin Herrenschmidt dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); 695728674a7SGreg Kroah-Hartman 696048bee77SBenjamin Herrenschmidt hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 697728674a7SGreg Kroah-Hartman } 698728674a7SGreg Kroah-Hartman 699728674a7SGreg Kroah-Hartman static int hvsi_open(struct tty_struct *tty, struct file *filp) 700728674a7SGreg Kroah-Hartman { 701728674a7SGreg Kroah-Hartman struct hvsi_struct *hp; 702728674a7SGreg Kroah-Hartman unsigned long flags; 703728674a7SGreg Kroah-Hartman int ret; 704728674a7SGreg Kroah-Hartman 705728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 706728674a7SGreg Kroah-Hartman 707410235fdSJiri Slaby hp = &hvsi_ports[tty->index]; 708728674a7SGreg Kroah-Hartman 709728674a7SGreg Kroah-Hartman tty->driver_data = hp; 710728674a7SGreg Kroah-Hartman 711728674a7SGreg Kroah-Hartman mb(); 712728674a7SGreg Kroah-Hartman if (hp->state == HVSI_FSP_DIED) 713728674a7SGreg Kroah-Hartman return -EIO; 714728674a7SGreg Kroah-Hartman 715daea4402SJiri Slaby tty_port_tty_set(&hp->port, tty); 716728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 717d73a4e79SJiri Slaby hp->port.count++; 718728674a7SGreg Kroah-Hartman atomic_set(&hp->seqno, 0); 719728674a7SGreg Kroah-Hartman h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); 720728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 721728674a7SGreg Kroah-Hartman 722728674a7SGreg Kroah-Hartman if (is_console(hp)) 723728674a7SGreg Kroah-Hartman return 0; /* this has already been handshaked as the console */ 724728674a7SGreg Kroah-Hartman 725728674a7SGreg Kroah-Hartman ret = hvsi_handshake(hp); 726728674a7SGreg Kroah-Hartman if (ret < 0) { 727728674a7SGreg Kroah-Hartman printk(KERN_ERR "%s: HVSI handshaking failed\n", tty->name); 728728674a7SGreg Kroah-Hartman return ret; 729728674a7SGreg Kroah-Hartman } 730728674a7SGreg Kroah-Hartman 731728674a7SGreg Kroah-Hartman ret = hvsi_get_mctrl(hp); 732728674a7SGreg Kroah-Hartman if (ret < 0) { 733728674a7SGreg Kroah-Hartman printk(KERN_ERR "%s: couldn't get initial modem flags\n", tty->name); 734728674a7SGreg Kroah-Hartman return ret; 735728674a7SGreg Kroah-Hartman } 736728674a7SGreg Kroah-Hartman 737728674a7SGreg Kroah-Hartman ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); 738728674a7SGreg Kroah-Hartman if (ret < 0) { 739728674a7SGreg Kroah-Hartman printk(KERN_ERR "%s: couldn't set DTR\n", tty->name); 740728674a7SGreg Kroah-Hartman return ret; 741728674a7SGreg Kroah-Hartman } 742728674a7SGreg Kroah-Hartman 743728674a7SGreg Kroah-Hartman return 0; 744728674a7SGreg Kroah-Hartman } 745728674a7SGreg Kroah-Hartman 746728674a7SGreg Kroah-Hartman /* wait for hvsi_write_worker to empty hp->outbuf */ 747728674a7SGreg Kroah-Hartman static void hvsi_flush_output(struct hvsi_struct *hp) 748728674a7SGreg Kroah-Hartman { 749728674a7SGreg Kroah-Hartman wait_event_timeout(hp->emptyq, (hp->n_outbuf <= 0), HVSI_TIMEOUT); 750728674a7SGreg Kroah-Hartman 751728674a7SGreg Kroah-Hartman /* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */ 752728674a7SGreg Kroah-Hartman cancel_delayed_work_sync(&hp->writer); 75343829731STejun Heo flush_work(&hp->handshaker); 754728674a7SGreg Kroah-Hartman 755728674a7SGreg Kroah-Hartman /* 756728674a7SGreg Kroah-Hartman * it's also possible that our timeout expired and hvsi_write_worker 757728674a7SGreg Kroah-Hartman * didn't manage to push outbuf. poof. 758728674a7SGreg Kroah-Hartman */ 759728674a7SGreg Kroah-Hartman hp->n_outbuf = 0; 760728674a7SGreg Kroah-Hartman } 761728674a7SGreg Kroah-Hartman 762728674a7SGreg Kroah-Hartman static void hvsi_close(struct tty_struct *tty, struct file *filp) 763728674a7SGreg Kroah-Hartman { 764728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 765728674a7SGreg Kroah-Hartman unsigned long flags; 766728674a7SGreg Kroah-Hartman 767728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 768728674a7SGreg Kroah-Hartman 769728674a7SGreg Kroah-Hartman if (tty_hung_up_p(filp)) 770728674a7SGreg Kroah-Hartman return; 771728674a7SGreg Kroah-Hartman 772728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 773728674a7SGreg Kroah-Hartman 774d73a4e79SJiri Slaby if (--hp->port.count == 0) { 775daea4402SJiri Slaby tty_port_tty_set(&hp->port, NULL); 776728674a7SGreg Kroah-Hartman hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ 777728674a7SGreg Kroah-Hartman 778728674a7SGreg Kroah-Hartman /* only close down connection if it is not the console */ 779728674a7SGreg Kroah-Hartman if (!is_console(hp)) { 780728674a7SGreg Kroah-Hartman h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); /* no more irqs */ 781728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_CLOSED); 782728674a7SGreg Kroah-Hartman /* 783728674a7SGreg Kroah-Hartman * any data delivered to the tty layer after this will be 784728674a7SGreg Kroah-Hartman * discarded (except for XON/XOFF) 785728674a7SGreg Kroah-Hartman */ 786728674a7SGreg Kroah-Hartman tty->closing = 1; 787728674a7SGreg Kroah-Hartman 788728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 789728674a7SGreg Kroah-Hartman 790728674a7SGreg Kroah-Hartman /* let any existing irq handlers finish. no more will start. */ 791728674a7SGreg Kroah-Hartman synchronize_irq(hp->virq); 792728674a7SGreg Kroah-Hartman 793728674a7SGreg Kroah-Hartman /* hvsi_write_worker will re-schedule until outbuf is empty. */ 794728674a7SGreg Kroah-Hartman hvsi_flush_output(hp); 795728674a7SGreg Kroah-Hartman 796728674a7SGreg Kroah-Hartman /* tell FSP to stop sending data */ 797728674a7SGreg Kroah-Hartman hvsi_close_protocol(hp); 798728674a7SGreg Kroah-Hartman 799728674a7SGreg Kroah-Hartman /* 800728674a7SGreg Kroah-Hartman * drain anything FSP is still in the middle of sending, and let 801728674a7SGreg Kroah-Hartman * hvsi_handshake drain the rest on the next open. 802728674a7SGreg Kroah-Hartman */ 803728674a7SGreg Kroah-Hartman hvsi_drain_input(hp); 804728674a7SGreg Kroah-Hartman 805728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 806728674a7SGreg Kroah-Hartman } 807d73a4e79SJiri Slaby } else if (hp->port.count < 0) 808728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", 809d73a4e79SJiri Slaby hp - hvsi_ports, hp->port.count); 810728674a7SGreg Kroah-Hartman 811728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 812728674a7SGreg Kroah-Hartman } 813728674a7SGreg Kroah-Hartman 814728674a7SGreg Kroah-Hartman static void hvsi_hangup(struct tty_struct *tty) 815728674a7SGreg Kroah-Hartman { 816728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 817728674a7SGreg Kroah-Hartman unsigned long flags; 818728674a7SGreg Kroah-Hartman 819728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 820728674a7SGreg Kroah-Hartman 821daea4402SJiri Slaby tty_port_tty_set(&hp->port, NULL); 822728674a7SGreg Kroah-Hartman 823daea4402SJiri Slaby spin_lock_irqsave(&hp->lock, flags); 824d73a4e79SJiri Slaby hp->port.count = 0; 825728674a7SGreg Kroah-Hartman hp->n_outbuf = 0; 826728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 827728674a7SGreg Kroah-Hartman } 828728674a7SGreg Kroah-Hartman 829728674a7SGreg Kroah-Hartman /* called with hp->lock held */ 830728674a7SGreg Kroah-Hartman static void hvsi_push(struct hvsi_struct *hp) 831728674a7SGreg Kroah-Hartman { 832728674a7SGreg Kroah-Hartman int n; 833728674a7SGreg Kroah-Hartman 834728674a7SGreg Kroah-Hartman if (hp->n_outbuf <= 0) 835728674a7SGreg Kroah-Hartman return; 836728674a7SGreg Kroah-Hartman 837728674a7SGreg Kroah-Hartman n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf); 838728674a7SGreg Kroah-Hartman if (n > 0) { 839728674a7SGreg Kroah-Hartman /* success */ 840728674a7SGreg Kroah-Hartman pr_debug("%s: wrote %i chars\n", __func__, n); 841728674a7SGreg Kroah-Hartman hp->n_outbuf = 0; 842728674a7SGreg Kroah-Hartman } else if (n == -EIO) { 843728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_FSP_DIED); 844728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: service processor died\n", hp->index); 845728674a7SGreg Kroah-Hartman } 846728674a7SGreg Kroah-Hartman } 847728674a7SGreg Kroah-Hartman 848728674a7SGreg Kroah-Hartman /* hvsi_write_worker will keep rescheduling itself until outbuf is empty */ 849728674a7SGreg Kroah-Hartman static void hvsi_write_worker(struct work_struct *work) 850728674a7SGreg Kroah-Hartman { 851728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = 852728674a7SGreg Kroah-Hartman container_of(work, struct hvsi_struct, writer.work); 853728674a7SGreg Kroah-Hartman unsigned long flags; 854728674a7SGreg Kroah-Hartman #ifdef DEBUG 855728674a7SGreg Kroah-Hartman static long start_j = 0; 856728674a7SGreg Kroah-Hartman 857728674a7SGreg Kroah-Hartman if (start_j == 0) 858728674a7SGreg Kroah-Hartman start_j = jiffies; 859728674a7SGreg Kroah-Hartman #endif /* DEBUG */ 860728674a7SGreg Kroah-Hartman 861728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 862728674a7SGreg Kroah-Hartman 863728674a7SGreg Kroah-Hartman pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); 864728674a7SGreg Kroah-Hartman 865728674a7SGreg Kroah-Hartman if (!is_open(hp)) { 866728674a7SGreg Kroah-Hartman /* 867728674a7SGreg Kroah-Hartman * We could have a non-open connection if the service processor died 868728674a7SGreg Kroah-Hartman * while we were busily scheduling ourselves. In that case, it could 869728674a7SGreg Kroah-Hartman * be minutes before the service processor comes back, so only try 870728674a7SGreg Kroah-Hartman * again once a second. 871728674a7SGreg Kroah-Hartman */ 872728674a7SGreg Kroah-Hartman schedule_delayed_work(&hp->writer, HZ); 873728674a7SGreg Kroah-Hartman goto out; 874728674a7SGreg Kroah-Hartman } 875728674a7SGreg Kroah-Hartman 876728674a7SGreg Kroah-Hartman hvsi_push(hp); 877728674a7SGreg Kroah-Hartman if (hp->n_outbuf > 0) 878728674a7SGreg Kroah-Hartman schedule_delayed_work(&hp->writer, 10); 879728674a7SGreg Kroah-Hartman else { 880728674a7SGreg Kroah-Hartman #ifdef DEBUG 881728674a7SGreg Kroah-Hartman pr_debug("%s: outbuf emptied after %li jiffies\n", __func__, 882728674a7SGreg Kroah-Hartman jiffies - start_j); 883728674a7SGreg Kroah-Hartman start_j = 0; 884728674a7SGreg Kroah-Hartman #endif /* DEBUG */ 885728674a7SGreg Kroah-Hartman wake_up_all(&hp->emptyq); 8866aad04f2SJiri Slaby tty_port_tty_wakeup(&hp->port); 887728674a7SGreg Kroah-Hartman } 888728674a7SGreg Kroah-Hartman 889728674a7SGreg Kroah-Hartman out: 890728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 891728674a7SGreg Kroah-Hartman } 892728674a7SGreg Kroah-Hartman 89303b3b1a2SJiri Slaby static unsigned int hvsi_write_room(struct tty_struct *tty) 894728674a7SGreg Kroah-Hartman { 895728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 896728674a7SGreg Kroah-Hartman 897728674a7SGreg Kroah-Hartman return N_OUTBUF - hp->n_outbuf; 898728674a7SGreg Kroah-Hartman } 899728674a7SGreg Kroah-Hartman 900fff4ef17SJiri Slaby static unsigned int hvsi_chars_in_buffer(struct tty_struct *tty) 901728674a7SGreg Kroah-Hartman { 902728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 903728674a7SGreg Kroah-Hartman 904728674a7SGreg Kroah-Hartman return hp->n_outbuf; 905728674a7SGreg Kroah-Hartman } 906728674a7SGreg Kroah-Hartman 907*95713967SJiri Slaby (SUSE) static ssize_t hvsi_write(struct tty_struct *tty, const u8 *source, 908*95713967SJiri Slaby (SUSE) size_t count) 909728674a7SGreg Kroah-Hartman { 910728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 911728674a7SGreg Kroah-Hartman unsigned long flags; 912728674a7SGreg Kroah-Hartman int total = 0; 913728674a7SGreg Kroah-Hartman int origcount = count; 914728674a7SGreg Kroah-Hartman 915728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 916728674a7SGreg Kroah-Hartman 917728674a7SGreg Kroah-Hartman pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); 918728674a7SGreg Kroah-Hartman 919728674a7SGreg Kroah-Hartman if (!is_open(hp)) { 920728674a7SGreg Kroah-Hartman /* we're either closing or not yet open; don't accept data */ 921728674a7SGreg Kroah-Hartman pr_debug("%s: not open\n", __func__); 922728674a7SGreg Kroah-Hartman goto out; 923728674a7SGreg Kroah-Hartman } 924728674a7SGreg Kroah-Hartman 925728674a7SGreg Kroah-Hartman /* 926728674a7SGreg Kroah-Hartman * when the hypervisor buffer (16K) fills, data will stay in hp->outbuf 927728674a7SGreg Kroah-Hartman * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls 928728674a7SGreg Kroah-Hartman * will see there is no room in outbuf and return. 929728674a7SGreg Kroah-Hartman */ 93028c0447dSJiri Slaby while ((count > 0) && (hvsi_write_room(tty) > 0)) { 93103b3b1a2SJiri Slaby int chunksize = min_t(int, count, hvsi_write_room(tty)); 932728674a7SGreg Kroah-Hartman 933728674a7SGreg Kroah-Hartman BUG_ON(hp->n_outbuf < 0); 934728674a7SGreg Kroah-Hartman memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); 935728674a7SGreg Kroah-Hartman hp->n_outbuf += chunksize; 936728674a7SGreg Kroah-Hartman 937728674a7SGreg Kroah-Hartman total += chunksize; 938728674a7SGreg Kroah-Hartman source += chunksize; 939728674a7SGreg Kroah-Hartman count -= chunksize; 940728674a7SGreg Kroah-Hartman hvsi_push(hp); 941728674a7SGreg Kroah-Hartman } 942728674a7SGreg Kroah-Hartman 943728674a7SGreg Kroah-Hartman if (hp->n_outbuf > 0) { 944728674a7SGreg Kroah-Hartman /* 945728674a7SGreg Kroah-Hartman * we weren't able to write it all to the hypervisor. 946728674a7SGreg Kroah-Hartman * schedule another push attempt. 947728674a7SGreg Kroah-Hartman */ 948728674a7SGreg Kroah-Hartman schedule_delayed_work(&hp->writer, 10); 949728674a7SGreg Kroah-Hartman } 950728674a7SGreg Kroah-Hartman 951728674a7SGreg Kroah-Hartman out: 952728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 953728674a7SGreg Kroah-Hartman 954728674a7SGreg Kroah-Hartman if (total != origcount) 955728674a7SGreg Kroah-Hartman pr_debug("%s: wanted %i, only wrote %i\n", __func__, origcount, 956728674a7SGreg Kroah-Hartman total); 957728674a7SGreg Kroah-Hartman 958728674a7SGreg Kroah-Hartman return total; 959728674a7SGreg Kroah-Hartman } 960728674a7SGreg Kroah-Hartman 961728674a7SGreg Kroah-Hartman /* 962728674a7SGreg Kroah-Hartman * I have never seen throttle or unthrottle called, so this little throttle 963728674a7SGreg Kroah-Hartman * buffering scheme may or may not work. 964728674a7SGreg Kroah-Hartman */ 965728674a7SGreg Kroah-Hartman static void hvsi_throttle(struct tty_struct *tty) 966728674a7SGreg Kroah-Hartman { 967728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 968728674a7SGreg Kroah-Hartman 969728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 970728674a7SGreg Kroah-Hartman 971728674a7SGreg Kroah-Hartman h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); 972728674a7SGreg Kroah-Hartman } 973728674a7SGreg Kroah-Hartman 974728674a7SGreg Kroah-Hartman static void hvsi_unthrottle(struct tty_struct *tty) 975728674a7SGreg Kroah-Hartman { 976728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 977728674a7SGreg Kroah-Hartman unsigned long flags; 978728674a7SGreg Kroah-Hartman 979728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 980728674a7SGreg Kroah-Hartman 981728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 982728674a7SGreg Kroah-Hartman if (hp->n_throttle) { 98392a19f9cSJiri Slaby hvsi_send_overflow(hp); 9842e124b4aSJiri Slaby tty_flip_buffer_push(&hp->port); 985728674a7SGreg Kroah-Hartman } 986728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 987728674a7SGreg Kroah-Hartman 988728674a7SGreg Kroah-Hartman 989728674a7SGreg Kroah-Hartman h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); 990728674a7SGreg Kroah-Hartman } 991728674a7SGreg Kroah-Hartman 99260b33c13SAlan Cox static int hvsi_tiocmget(struct tty_struct *tty) 993728674a7SGreg Kroah-Hartman { 994728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 995728674a7SGreg Kroah-Hartman 996728674a7SGreg Kroah-Hartman hvsi_get_mctrl(hp); 997728674a7SGreg Kroah-Hartman return hp->mctrl; 998728674a7SGreg Kroah-Hartman } 999728674a7SGreg Kroah-Hartman 100020b9d177SAlan Cox static int hvsi_tiocmset(struct tty_struct *tty, 1001728674a7SGreg Kroah-Hartman unsigned int set, unsigned int clear) 1002728674a7SGreg Kroah-Hartman { 1003728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 1004728674a7SGreg Kroah-Hartman unsigned long flags; 1005728674a7SGreg Kroah-Hartman uint16_t new_mctrl; 1006728674a7SGreg Kroah-Hartman 1007728674a7SGreg Kroah-Hartman /* we can only alter DTR */ 1008728674a7SGreg Kroah-Hartman clear &= TIOCM_DTR; 1009728674a7SGreg Kroah-Hartman set &= TIOCM_DTR; 1010728674a7SGreg Kroah-Hartman 1011728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 1012728674a7SGreg Kroah-Hartman 1013728674a7SGreg Kroah-Hartman new_mctrl = (hp->mctrl & ~clear) | set; 1014728674a7SGreg Kroah-Hartman 1015728674a7SGreg Kroah-Hartman if (hp->mctrl != new_mctrl) { 1016728674a7SGreg Kroah-Hartman hvsi_set_mctrl(hp, new_mctrl); 1017728674a7SGreg Kroah-Hartman hp->mctrl = new_mctrl; 1018728674a7SGreg Kroah-Hartman } 1019728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 1020728674a7SGreg Kroah-Hartman 1021728674a7SGreg Kroah-Hartman return 0; 1022728674a7SGreg Kroah-Hartman } 1023728674a7SGreg Kroah-Hartman 1024728674a7SGreg Kroah-Hartman 1025728674a7SGreg Kroah-Hartman static const struct tty_operations hvsi_ops = { 1026728674a7SGreg Kroah-Hartman .open = hvsi_open, 1027728674a7SGreg Kroah-Hartman .close = hvsi_close, 1028728674a7SGreg Kroah-Hartman .write = hvsi_write, 1029728674a7SGreg Kroah-Hartman .hangup = hvsi_hangup, 1030728674a7SGreg Kroah-Hartman .write_room = hvsi_write_room, 1031728674a7SGreg Kroah-Hartman .chars_in_buffer = hvsi_chars_in_buffer, 1032728674a7SGreg Kroah-Hartman .throttle = hvsi_throttle, 1033728674a7SGreg Kroah-Hartman .unthrottle = hvsi_unthrottle, 1034728674a7SGreg Kroah-Hartman .tiocmget = hvsi_tiocmget, 1035728674a7SGreg Kroah-Hartman .tiocmset = hvsi_tiocmset, 1036728674a7SGreg Kroah-Hartman }; 1037728674a7SGreg Kroah-Hartman 1038728674a7SGreg Kroah-Hartman static int __init hvsi_init(void) 1039728674a7SGreg Kroah-Hartman { 10400524513aSJiri Slaby struct tty_driver *driver; 10417ccbdcc4SJiri Slaby int i, ret; 1042728674a7SGreg Kroah-Hartman 104339b7b42bSJiri Slaby driver = tty_alloc_driver(hvsi_count, TTY_DRIVER_REAL_RAW); 104439b7b42bSJiri Slaby if (IS_ERR(driver)) 104539b7b42bSJiri Slaby return PTR_ERR(driver); 1046728674a7SGreg Kroah-Hartman 10470524513aSJiri Slaby driver->driver_name = "hvsi"; 10480524513aSJiri Slaby driver->name = "hvsi"; 10490524513aSJiri Slaby driver->major = HVSI_MAJOR; 10500524513aSJiri Slaby driver->minor_start = HVSI_MINOR; 10510524513aSJiri Slaby driver->type = TTY_DRIVER_TYPE_SYSTEM; 10520524513aSJiri Slaby driver->init_termios = tty_std_termios; 10530524513aSJiri Slaby driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; 10540524513aSJiri Slaby driver->init_termios.c_ispeed = 9600; 10550524513aSJiri Slaby driver->init_termios.c_ospeed = 9600; 10560524513aSJiri Slaby tty_set_operations(driver, &hvsi_ops); 1057728674a7SGreg Kroah-Hartman 1058728674a7SGreg Kroah-Hartman for (i=0; i < hvsi_count; i++) { 1059728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = &hvsi_ports[i]; 1060728674a7SGreg Kroah-Hartman int ret = 1; 1061728674a7SGreg Kroah-Hartman 10620524513aSJiri Slaby tty_port_link_device(&hp->port, driver, i); 1063b19e2ca7SJiri Slaby 10649cfb5c05SYong Zhang ret = request_irq(hp->virq, hvsi_interrupt, 0, "hvsi", hp); 1065728674a7SGreg Kroah-Hartman if (ret) 1066728674a7SGreg Kroah-Hartman printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n", 1067728674a7SGreg Kroah-Hartman hp->virq, ret); 1068728674a7SGreg Kroah-Hartman } 1069728674a7SGreg Kroah-Hartman hvsi_wait = wait_for_state; /* irqs active now */ 1070728674a7SGreg Kroah-Hartman 10710524513aSJiri Slaby ret = tty_register_driver(driver); 10727ccbdcc4SJiri Slaby if (ret) { 10737ccbdcc4SJiri Slaby pr_err("Couldn't register hvsi console driver\n"); 10747ccbdcc4SJiri Slaby goto err_free_irq; 10757ccbdcc4SJiri Slaby } 1076728674a7SGreg Kroah-Hartman 10770524513aSJiri Slaby hvsi_driver = driver; 10780524513aSJiri Slaby 1079728674a7SGreg Kroah-Hartman printk(KERN_DEBUG "HVSI: registered %i devices\n", hvsi_count); 1080728674a7SGreg Kroah-Hartman 1081728674a7SGreg Kroah-Hartman return 0; 10827ccbdcc4SJiri Slaby err_free_irq: 10837ccbdcc4SJiri Slaby hvsi_wait = poll_for_state; 10847ccbdcc4SJiri Slaby for (i = 0; i < hvsi_count; i++) { 10857ccbdcc4SJiri Slaby struct hvsi_struct *hp = &hvsi_ports[i]; 10867ccbdcc4SJiri Slaby 10877ccbdcc4SJiri Slaby free_irq(hp->virq, hp); 10887ccbdcc4SJiri Slaby } 10890524513aSJiri Slaby tty_driver_kref_put(driver); 10907ccbdcc4SJiri Slaby 10917ccbdcc4SJiri Slaby return ret; 1092728674a7SGreg Kroah-Hartman } 1093728674a7SGreg Kroah-Hartman device_initcall(hvsi_init); 1094728674a7SGreg Kroah-Hartman 1095728674a7SGreg Kroah-Hartman /***** console (not tty) code: *****/ 1096728674a7SGreg Kroah-Hartman 1097728674a7SGreg Kroah-Hartman static void hvsi_console_print(struct console *console, const char *buf, 1098728674a7SGreg Kroah-Hartman unsigned int count) 1099728674a7SGreg Kroah-Hartman { 1100728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = &hvsi_ports[console->index]; 1101728674a7SGreg Kroah-Hartman char c[HVSI_MAX_OUTGOING_DATA] __ALIGNED__; 1102728674a7SGreg Kroah-Hartman unsigned int i = 0, n = 0; 1103728674a7SGreg Kroah-Hartman int ret, donecr = 0; 1104728674a7SGreg Kroah-Hartman 1105728674a7SGreg Kroah-Hartman mb(); 1106728674a7SGreg Kroah-Hartman if (!is_open(hp)) 1107728674a7SGreg Kroah-Hartman return; 1108728674a7SGreg Kroah-Hartman 1109728674a7SGreg Kroah-Hartman /* 1110728674a7SGreg Kroah-Hartman * ugh, we have to translate LF -> CRLF ourselves, in place. 1111728674a7SGreg Kroah-Hartman * copied from hvc_console.c: 1112728674a7SGreg Kroah-Hartman */ 1113728674a7SGreg Kroah-Hartman while (count > 0 || i > 0) { 1114728674a7SGreg Kroah-Hartman if (count > 0 && i < sizeof(c)) { 1115728674a7SGreg Kroah-Hartman if (buf[n] == '\n' && !donecr) { 1116728674a7SGreg Kroah-Hartman c[i++] = '\r'; 1117728674a7SGreg Kroah-Hartman donecr = 1; 1118728674a7SGreg Kroah-Hartman } else { 1119728674a7SGreg Kroah-Hartman c[i++] = buf[n++]; 1120728674a7SGreg Kroah-Hartman donecr = 0; 1121728674a7SGreg Kroah-Hartman --count; 1122728674a7SGreg Kroah-Hartman } 1123728674a7SGreg Kroah-Hartman } else { 1124728674a7SGreg Kroah-Hartman ret = hvsi_put_chars(hp, c, i); 1125728674a7SGreg Kroah-Hartman if (ret < 0) 1126728674a7SGreg Kroah-Hartman i = 0; 1127728674a7SGreg Kroah-Hartman i -= ret; 1128728674a7SGreg Kroah-Hartman } 1129728674a7SGreg Kroah-Hartman } 1130728674a7SGreg Kroah-Hartman } 1131728674a7SGreg Kroah-Hartman 1132728674a7SGreg Kroah-Hartman static struct tty_driver *hvsi_console_device(struct console *console, 1133728674a7SGreg Kroah-Hartman int *index) 1134728674a7SGreg Kroah-Hartman { 1135728674a7SGreg Kroah-Hartman *index = console->index; 1136728674a7SGreg Kroah-Hartman return hvsi_driver; 1137728674a7SGreg Kroah-Hartman } 1138728674a7SGreg Kroah-Hartman 1139728674a7SGreg Kroah-Hartman static int __init hvsi_console_setup(struct console *console, char *options) 1140728674a7SGreg Kroah-Hartman { 1141728674a7SGreg Kroah-Hartman struct hvsi_struct *hp; 1142728674a7SGreg Kroah-Hartman int ret; 1143728674a7SGreg Kroah-Hartman 1144728674a7SGreg Kroah-Hartman if (console->index < 0 || console->index >= hvsi_count) 11459f028427SAndy Shevchenko return -EINVAL; 1146728674a7SGreg Kroah-Hartman hp = &hvsi_ports[console->index]; 1147728674a7SGreg Kroah-Hartman 1148728674a7SGreg Kroah-Hartman /* give the FSP a chance to change the baud rate when we re-open */ 1149728674a7SGreg Kroah-Hartman hvsi_close_protocol(hp); 1150728674a7SGreg Kroah-Hartman 1151728674a7SGreg Kroah-Hartman ret = hvsi_handshake(hp); 1152728674a7SGreg Kroah-Hartman if (ret < 0) 1153728674a7SGreg Kroah-Hartman return ret; 1154728674a7SGreg Kroah-Hartman 1155728674a7SGreg Kroah-Hartman ret = hvsi_get_mctrl(hp); 1156728674a7SGreg Kroah-Hartman if (ret < 0) 1157728674a7SGreg Kroah-Hartman return ret; 1158728674a7SGreg Kroah-Hartman 1159728674a7SGreg Kroah-Hartman ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); 1160728674a7SGreg Kroah-Hartman if (ret < 0) 1161728674a7SGreg Kroah-Hartman return ret; 1162728674a7SGreg Kroah-Hartman 1163728674a7SGreg Kroah-Hartman hp->flags |= HVSI_CONSOLE; 1164728674a7SGreg Kroah-Hartman 1165728674a7SGreg Kroah-Hartman return 0; 1166728674a7SGreg Kroah-Hartman } 1167728674a7SGreg Kroah-Hartman 1168728674a7SGreg Kroah-Hartman static struct console hvsi_console = { 1169728674a7SGreg Kroah-Hartman .name = "hvsi", 1170728674a7SGreg Kroah-Hartman .write = hvsi_console_print, 1171728674a7SGreg Kroah-Hartman .device = hvsi_console_device, 1172728674a7SGreg Kroah-Hartman .setup = hvsi_console_setup, 1173728674a7SGreg Kroah-Hartman .flags = CON_PRINTBUFFER, 1174728674a7SGreg Kroah-Hartman .index = -1, 1175728674a7SGreg Kroah-Hartman }; 1176728674a7SGreg Kroah-Hartman 1177728674a7SGreg Kroah-Hartman static int __init hvsi_console_init(void) 1178728674a7SGreg Kroah-Hartman { 1179728674a7SGreg Kroah-Hartman struct device_node *vty; 1180728674a7SGreg Kroah-Hartman 1181728674a7SGreg Kroah-Hartman hvsi_wait = poll_for_state; /* no irqs yet; must poll */ 1182728674a7SGreg Kroah-Hartman 1183728674a7SGreg Kroah-Hartman /* search device tree for vty nodes */ 1184074d35ceSWei Yongjun for_each_compatible_node(vty, "serial", "hvterm-protocol") { 1185728674a7SGreg Kroah-Hartman struct hvsi_struct *hp; 118648079804SLaurent Dufour const __be32 *vtermno, *irq; 1187728674a7SGreg Kroah-Hartman 1188728674a7SGreg Kroah-Hartman vtermno = of_get_property(vty, "reg", NULL); 1189728674a7SGreg Kroah-Hartman irq = of_get_property(vty, "interrupts", NULL); 1190728674a7SGreg Kroah-Hartman if (!vtermno || !irq) 1191728674a7SGreg Kroah-Hartman continue; 1192728674a7SGreg Kroah-Hartman 1193728674a7SGreg Kroah-Hartman if (hvsi_count >= MAX_NR_HVSI_CONSOLES) { 1194728674a7SGreg Kroah-Hartman of_node_put(vty); 1195728674a7SGreg Kroah-Hartman break; 1196728674a7SGreg Kroah-Hartman } 1197728674a7SGreg Kroah-Hartman 1198728674a7SGreg Kroah-Hartman hp = &hvsi_ports[hvsi_count]; 1199728674a7SGreg Kroah-Hartman INIT_DELAYED_WORK(&hp->writer, hvsi_write_worker); 1200728674a7SGreg Kroah-Hartman INIT_WORK(&hp->handshaker, hvsi_handshaker); 1201728674a7SGreg Kroah-Hartman init_waitqueue_head(&hp->emptyq); 1202728674a7SGreg Kroah-Hartman init_waitqueue_head(&hp->stateq); 1203728674a7SGreg Kroah-Hartman spin_lock_init(&hp->lock); 1204d73a4e79SJiri Slaby tty_port_init(&hp->port); 1205728674a7SGreg Kroah-Hartman hp->index = hvsi_count; 1206728674a7SGreg Kroah-Hartman hp->inbuf_end = hp->inbuf; 1207728674a7SGreg Kroah-Hartman hp->state = HVSI_CLOSED; 120848079804SLaurent Dufour hp->vtermno = be32_to_cpup(vtermno); 120948079804SLaurent Dufour hp->virq = irq_create_mapping(NULL, be32_to_cpup(irq)); 1210d4e33facSAlan Cox if (hp->virq == 0) { 1211728674a7SGreg Kroah-Hartman printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", 121248079804SLaurent Dufour __func__, be32_to_cpup(irq)); 1213191c5f10SJiri Slaby tty_port_destroy(&hp->port); 1214728674a7SGreg Kroah-Hartman continue; 1215728674a7SGreg Kroah-Hartman } 1216728674a7SGreg Kroah-Hartman 1217728674a7SGreg Kroah-Hartman hvsi_count++; 1218728674a7SGreg Kroah-Hartman } 1219728674a7SGreg Kroah-Hartman 1220728674a7SGreg Kroah-Hartman if (hvsi_count) 1221728674a7SGreg Kroah-Hartman register_console(&hvsi_console); 1222728674a7SGreg Kroah-Hartman return 0; 1223728674a7SGreg Kroah-Hartman } 1224728674a7SGreg Kroah-Hartman console_initcall(hvsi_console_init); 1225