1728674a7SGreg Kroah-Hartman /* 2728674a7SGreg Kroah-Hartman * Copyright (C) 2004 Hollis Blanchard <hollisb@us.ibm.com>, IBM 3728674a7SGreg Kroah-Hartman * 4728674a7SGreg Kroah-Hartman * This program is free software; you can redistribute it and/or modify 5728674a7SGreg Kroah-Hartman * it under the terms of the GNU General Public License as published by 6728674a7SGreg Kroah-Hartman * the Free Software Foundation; either version 2 of the License, or 7728674a7SGreg Kroah-Hartman * (at your option) any later version. 8728674a7SGreg Kroah-Hartman * 9728674a7SGreg Kroah-Hartman * This program is distributed in the hope that it will be useful, 10728674a7SGreg Kroah-Hartman * but WITHOUT ANY WARRANTY; without even the implied warranty of 11728674a7SGreg Kroah-Hartman * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12728674a7SGreg Kroah-Hartman * GNU General Public License for more details. 13728674a7SGreg Kroah-Hartman * 14728674a7SGreg Kroah-Hartman * You should have received a copy of the GNU General Public License 15728674a7SGreg Kroah-Hartman * along with this program; if not, write to the Free Software 16728674a7SGreg Kroah-Hartman * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17728674a7SGreg Kroah-Hartman */ 18728674a7SGreg Kroah-Hartman 19728674a7SGreg Kroah-Hartman /* Host Virtual Serial Interface (HVSI) is a protocol between the hosted OS 20728674a7SGreg Kroah-Hartman * and the service processor on IBM pSeries servers. On these servers, there 21728674a7SGreg Kroah-Hartman * are no serial ports under the OS's control, and sometimes there is no other 22728674a7SGreg Kroah-Hartman * console available either. However, the service processor has two standard 23728674a7SGreg Kroah-Hartman * serial ports, so this over-complicated protocol allows the OS to control 24728674a7SGreg Kroah-Hartman * those ports by proxy. 25728674a7SGreg Kroah-Hartman * 26728674a7SGreg Kroah-Hartman * Besides data, the procotol supports the reading/writing of the serial 27728674a7SGreg Kroah-Hartman * port's DTR line, and the reading of the CD line. This is to allow the OS to 28728674a7SGreg Kroah-Hartman * control a modem attached to the service processor's serial port. Note that 29728674a7SGreg Kroah-Hartman * the OS cannot change the speed of the port through this protocol. 30728674a7SGreg Kroah-Hartman */ 31728674a7SGreg Kroah-Hartman 32728674a7SGreg Kroah-Hartman #undef DEBUG 33728674a7SGreg Kroah-Hartman 34728674a7SGreg Kroah-Hartman #include <linux/console.h> 35728674a7SGreg Kroah-Hartman #include <linux/ctype.h> 36728674a7SGreg Kroah-Hartman #include <linux/delay.h> 37728674a7SGreg Kroah-Hartman #include <linux/init.h> 38728674a7SGreg Kroah-Hartman #include <linux/interrupt.h> 39728674a7SGreg Kroah-Hartman #include <linux/module.h> 40728674a7SGreg Kroah-Hartman #include <linux/major.h> 41728674a7SGreg Kroah-Hartman #include <linux/kernel.h> 42728674a7SGreg Kroah-Hartman #include <linux/spinlock.h> 43728674a7SGreg Kroah-Hartman #include <linux/sysrq.h> 44728674a7SGreg Kroah-Hartman #include <linux/tty.h> 45728674a7SGreg Kroah-Hartman #include <linux/tty_flip.h> 46728674a7SGreg Kroah-Hartman #include <asm/hvcall.h> 47728674a7SGreg Kroah-Hartman #include <asm/hvconsole.h> 48728674a7SGreg Kroah-Hartman #include <asm/prom.h> 49728674a7SGreg Kroah-Hartman #include <asm/uaccess.h> 50728674a7SGreg Kroah-Hartman #include <asm/vio.h> 51728674a7SGreg Kroah-Hartman #include <asm/param.h> 52725e789fSBenjamin Herrenschmidt #include <asm/hvsi.h> 53728674a7SGreg Kroah-Hartman 54728674a7SGreg Kroah-Hartman #define HVSI_MAJOR 229 55728674a7SGreg Kroah-Hartman #define HVSI_MINOR 128 56728674a7SGreg Kroah-Hartman #define MAX_NR_HVSI_CONSOLES 4 57728674a7SGreg Kroah-Hartman 58728674a7SGreg Kroah-Hartman #define HVSI_TIMEOUT (5*HZ) 59728674a7SGreg Kroah-Hartman #define HVSI_VERSION 1 60728674a7SGreg Kroah-Hartman #define HVSI_MAX_PACKET 256 61728674a7SGreg Kroah-Hartman #define HVSI_MAX_READ 16 62728674a7SGreg Kroah-Hartman #define HVSI_MAX_OUTGOING_DATA 12 63728674a7SGreg Kroah-Hartman #define N_OUTBUF 12 64728674a7SGreg Kroah-Hartman 65728674a7SGreg Kroah-Hartman /* 66728674a7SGreg Kroah-Hartman * we pass data via two 8-byte registers, so we would like our char arrays 67728674a7SGreg Kroah-Hartman * properly aligned for those loads. 68728674a7SGreg Kroah-Hartman */ 69728674a7SGreg Kroah-Hartman #define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) 70728674a7SGreg Kroah-Hartman 71728674a7SGreg Kroah-Hartman struct hvsi_struct { 72d73a4e79SJiri Slaby struct tty_port port; 73728674a7SGreg Kroah-Hartman struct delayed_work writer; 74728674a7SGreg Kroah-Hartman struct work_struct handshaker; 75728674a7SGreg Kroah-Hartman wait_queue_head_t emptyq; /* woken when outbuf is emptied */ 76728674a7SGreg Kroah-Hartman wait_queue_head_t stateq; /* woken when HVSI state changes */ 77728674a7SGreg Kroah-Hartman spinlock_t lock; 78728674a7SGreg Kroah-Hartman int index; 79728674a7SGreg Kroah-Hartman uint8_t throttle_buf[128]; 80728674a7SGreg Kroah-Hartman uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */ 81728674a7SGreg Kroah-Hartman /* inbuf is for packet reassembly. leave a little room for leftovers. */ 82728674a7SGreg Kroah-Hartman uint8_t inbuf[HVSI_MAX_PACKET + HVSI_MAX_READ]; 83728674a7SGreg Kroah-Hartman uint8_t *inbuf_end; 84728674a7SGreg Kroah-Hartman int n_throttle; 85728674a7SGreg Kroah-Hartman int n_outbuf; 86728674a7SGreg Kroah-Hartman uint32_t vtermno; 87728674a7SGreg Kroah-Hartman uint32_t virq; 88728674a7SGreg Kroah-Hartman atomic_t seqno; /* HVSI packet sequence number */ 89728674a7SGreg Kroah-Hartman uint16_t mctrl; 90728674a7SGreg Kroah-Hartman uint8_t state; /* HVSI protocol state */ 91728674a7SGreg Kroah-Hartman uint8_t flags; 92728674a7SGreg Kroah-Hartman #ifdef CONFIG_MAGIC_SYSRQ 93728674a7SGreg Kroah-Hartman uint8_t sysrq; 94728674a7SGreg Kroah-Hartman #endif /* CONFIG_MAGIC_SYSRQ */ 95728674a7SGreg Kroah-Hartman }; 96728674a7SGreg Kroah-Hartman static struct hvsi_struct hvsi_ports[MAX_NR_HVSI_CONSOLES]; 97728674a7SGreg Kroah-Hartman 98728674a7SGreg Kroah-Hartman static struct tty_driver *hvsi_driver; 99728674a7SGreg Kroah-Hartman static int hvsi_count; 100728674a7SGreg Kroah-Hartman static int (*hvsi_wait)(struct hvsi_struct *hp, int state); 101728674a7SGreg Kroah-Hartman 102728674a7SGreg Kroah-Hartman enum HVSI_PROTOCOL_STATE { 103728674a7SGreg Kroah-Hartman HVSI_CLOSED, 104728674a7SGreg Kroah-Hartman HVSI_WAIT_FOR_VER_RESPONSE, 105728674a7SGreg Kroah-Hartman HVSI_WAIT_FOR_VER_QUERY, 106728674a7SGreg Kroah-Hartman HVSI_OPEN, 107728674a7SGreg Kroah-Hartman HVSI_WAIT_FOR_MCTRL_RESPONSE, 108728674a7SGreg Kroah-Hartman HVSI_FSP_DIED, 109728674a7SGreg Kroah-Hartman }; 110728674a7SGreg Kroah-Hartman #define HVSI_CONSOLE 0x1 111728674a7SGreg Kroah-Hartman 112728674a7SGreg Kroah-Hartman static inline int is_console(struct hvsi_struct *hp) 113728674a7SGreg Kroah-Hartman { 114728674a7SGreg Kroah-Hartman return hp->flags & HVSI_CONSOLE; 115728674a7SGreg Kroah-Hartman } 116728674a7SGreg Kroah-Hartman 117728674a7SGreg Kroah-Hartman static inline int is_open(struct hvsi_struct *hp) 118728674a7SGreg Kroah-Hartman { 119728674a7SGreg Kroah-Hartman /* if we're waiting for an mctrl then we're already open */ 120728674a7SGreg Kroah-Hartman return (hp->state == HVSI_OPEN) 121728674a7SGreg Kroah-Hartman || (hp->state == HVSI_WAIT_FOR_MCTRL_RESPONSE); 122728674a7SGreg Kroah-Hartman } 123728674a7SGreg Kroah-Hartman 124728674a7SGreg Kroah-Hartman static inline void print_state(struct hvsi_struct *hp) 125728674a7SGreg Kroah-Hartman { 126728674a7SGreg Kroah-Hartman #ifdef DEBUG 127728674a7SGreg Kroah-Hartman static const char *state_names[] = { 128728674a7SGreg Kroah-Hartman "HVSI_CLOSED", 129728674a7SGreg Kroah-Hartman "HVSI_WAIT_FOR_VER_RESPONSE", 130728674a7SGreg Kroah-Hartman "HVSI_WAIT_FOR_VER_QUERY", 131728674a7SGreg Kroah-Hartman "HVSI_OPEN", 132728674a7SGreg Kroah-Hartman "HVSI_WAIT_FOR_MCTRL_RESPONSE", 133728674a7SGreg Kroah-Hartman "HVSI_FSP_DIED", 134728674a7SGreg Kroah-Hartman }; 135728674a7SGreg Kroah-Hartman const char *name = (hp->state < ARRAY_SIZE(state_names)) 136728674a7SGreg Kroah-Hartman ? state_names[hp->state] : "UNKNOWN"; 137728674a7SGreg Kroah-Hartman 138728674a7SGreg Kroah-Hartman pr_debug("hvsi%i: state = %s\n", hp->index, name); 139728674a7SGreg Kroah-Hartman #endif /* DEBUG */ 140728674a7SGreg Kroah-Hartman } 141728674a7SGreg Kroah-Hartman 142728674a7SGreg Kroah-Hartman static inline void __set_state(struct hvsi_struct *hp, int state) 143728674a7SGreg Kroah-Hartman { 144728674a7SGreg Kroah-Hartman hp->state = state; 145728674a7SGreg Kroah-Hartman print_state(hp); 146728674a7SGreg Kroah-Hartman wake_up_all(&hp->stateq); 147728674a7SGreg Kroah-Hartman } 148728674a7SGreg Kroah-Hartman 149728674a7SGreg Kroah-Hartman static inline void set_state(struct hvsi_struct *hp, int state) 150728674a7SGreg Kroah-Hartman { 151728674a7SGreg Kroah-Hartman unsigned long flags; 152728674a7SGreg Kroah-Hartman 153728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 154728674a7SGreg Kroah-Hartman __set_state(hp, state); 155728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 156728674a7SGreg Kroah-Hartman } 157728674a7SGreg Kroah-Hartman 158728674a7SGreg Kroah-Hartman static inline int len_packet(const uint8_t *packet) 159728674a7SGreg Kroah-Hartman { 160728674a7SGreg Kroah-Hartman return (int)((struct hvsi_header *)packet)->len; 161728674a7SGreg Kroah-Hartman } 162728674a7SGreg Kroah-Hartman 163728674a7SGreg Kroah-Hartman static inline int is_header(const uint8_t *packet) 164728674a7SGreg Kroah-Hartman { 165728674a7SGreg Kroah-Hartman struct hvsi_header *header = (struct hvsi_header *)packet; 166728674a7SGreg Kroah-Hartman return header->type >= VS_QUERY_RESPONSE_PACKET_HEADER; 167728674a7SGreg Kroah-Hartman } 168728674a7SGreg Kroah-Hartman 169728674a7SGreg Kroah-Hartman static inline int got_packet(const struct hvsi_struct *hp, uint8_t *packet) 170728674a7SGreg Kroah-Hartman { 171728674a7SGreg Kroah-Hartman if (hp->inbuf_end < packet + sizeof(struct hvsi_header)) 172728674a7SGreg Kroah-Hartman return 0; /* don't even have the packet header */ 173728674a7SGreg Kroah-Hartman 174728674a7SGreg Kroah-Hartman if (hp->inbuf_end < (packet + len_packet(packet))) 175728674a7SGreg Kroah-Hartman return 0; /* don't have the rest of the packet */ 176728674a7SGreg Kroah-Hartman 177728674a7SGreg Kroah-Hartman return 1; 178728674a7SGreg Kroah-Hartman } 179728674a7SGreg Kroah-Hartman 180728674a7SGreg Kroah-Hartman /* shift remaining bytes in packetbuf down */ 181728674a7SGreg Kroah-Hartman static void compact_inbuf(struct hvsi_struct *hp, uint8_t *read_to) 182728674a7SGreg Kroah-Hartman { 183728674a7SGreg Kroah-Hartman int remaining = (int)(hp->inbuf_end - read_to); 184728674a7SGreg Kroah-Hartman 185728674a7SGreg Kroah-Hartman pr_debug("%s: %i chars remain\n", __func__, remaining); 186728674a7SGreg Kroah-Hartman 187728674a7SGreg Kroah-Hartman if (read_to != hp->inbuf) 188728674a7SGreg Kroah-Hartman memmove(hp->inbuf, read_to, remaining); 189728674a7SGreg Kroah-Hartman 190728674a7SGreg Kroah-Hartman hp->inbuf_end = hp->inbuf + remaining; 191728674a7SGreg Kroah-Hartman } 192728674a7SGreg Kroah-Hartman 193728674a7SGreg Kroah-Hartman #ifdef DEBUG 194728674a7SGreg Kroah-Hartman #define dbg_dump_packet(packet) dump_packet(packet) 195728674a7SGreg Kroah-Hartman #define dbg_dump_hex(data, len) dump_hex(data, len) 196728674a7SGreg Kroah-Hartman #else 197728674a7SGreg Kroah-Hartman #define dbg_dump_packet(packet) do { } while (0) 198728674a7SGreg Kroah-Hartman #define dbg_dump_hex(data, len) do { } while (0) 199728674a7SGreg Kroah-Hartman #endif 200728674a7SGreg Kroah-Hartman 201728674a7SGreg Kroah-Hartman static void dump_hex(const uint8_t *data, int len) 202728674a7SGreg Kroah-Hartman { 203728674a7SGreg Kroah-Hartman int i; 204728674a7SGreg Kroah-Hartman 205728674a7SGreg Kroah-Hartman printk(" "); 206728674a7SGreg Kroah-Hartman for (i=0; i < len; i++) 207728674a7SGreg Kroah-Hartman printk("%.2x", data[i]); 208728674a7SGreg Kroah-Hartman 209728674a7SGreg Kroah-Hartman printk("\n "); 210728674a7SGreg Kroah-Hartman for (i=0; i < len; i++) { 211728674a7SGreg Kroah-Hartman if (isprint(data[i])) 212728674a7SGreg Kroah-Hartman printk("%c", data[i]); 213728674a7SGreg Kroah-Hartman else 214728674a7SGreg Kroah-Hartman printk("."); 215728674a7SGreg Kroah-Hartman } 216728674a7SGreg Kroah-Hartman printk("\n"); 217728674a7SGreg Kroah-Hartman } 218728674a7SGreg Kroah-Hartman 219728674a7SGreg Kroah-Hartman static void dump_packet(uint8_t *packet) 220728674a7SGreg Kroah-Hartman { 221728674a7SGreg Kroah-Hartman struct hvsi_header *header = (struct hvsi_header *)packet; 222728674a7SGreg Kroah-Hartman 223728674a7SGreg Kroah-Hartman printk("type 0x%x, len %i, seqno %i:\n", header->type, header->len, 224728674a7SGreg Kroah-Hartman header->seqno); 225728674a7SGreg Kroah-Hartman 226728674a7SGreg Kroah-Hartman dump_hex(packet, header->len); 227728674a7SGreg Kroah-Hartman } 228728674a7SGreg Kroah-Hartman 229728674a7SGreg Kroah-Hartman static int hvsi_read(struct hvsi_struct *hp, char *buf, int count) 230728674a7SGreg Kroah-Hartman { 231728674a7SGreg Kroah-Hartman unsigned long got; 232728674a7SGreg Kroah-Hartman 233728674a7SGreg Kroah-Hartman got = hvc_get_chars(hp->vtermno, buf, count); 234728674a7SGreg Kroah-Hartman 235728674a7SGreg Kroah-Hartman return got; 236728674a7SGreg Kroah-Hartman } 237728674a7SGreg Kroah-Hartman 238728674a7SGreg Kroah-Hartman static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, 23928c0447dSJiri Slaby struct tty_struct *tty, struct hvsi_struct **to_handshake) 240728674a7SGreg Kroah-Hartman { 241728674a7SGreg Kroah-Hartman struct hvsi_control *header = (struct hvsi_control *)packet; 242728674a7SGreg Kroah-Hartman 24348079804SLaurent Dufour switch (be16_to_cpu(header->verb)) { 244728674a7SGreg Kroah-Hartman case VSV_MODEM_CTL_UPDATE: 24548079804SLaurent Dufour if ((be32_to_cpu(header->word) & HVSI_TSCD) == 0) { 246728674a7SGreg Kroah-Hartman /* CD went away; no more connection */ 247728674a7SGreg Kroah-Hartman pr_debug("hvsi%i: CD dropped\n", hp->index); 248728674a7SGreg Kroah-Hartman hp->mctrl &= TIOCM_CD; 24928c0447dSJiri Slaby if (tty && !C_CLOCAL(tty)) 25028c0447dSJiri Slaby tty_hangup(tty); 251728674a7SGreg Kroah-Hartman } 252728674a7SGreg Kroah-Hartman break; 253728674a7SGreg Kroah-Hartman case VSV_CLOSE_PROTOCOL: 254728674a7SGreg Kroah-Hartman pr_debug("hvsi%i: service processor came back\n", hp->index); 255728674a7SGreg Kroah-Hartman if (hp->state != HVSI_CLOSED) { 256728674a7SGreg Kroah-Hartman *to_handshake = hp; 257728674a7SGreg Kroah-Hartman } 258728674a7SGreg Kroah-Hartman break; 259728674a7SGreg Kroah-Hartman default: 260728674a7SGreg Kroah-Hartman printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ", 261728674a7SGreg Kroah-Hartman hp->index); 262728674a7SGreg Kroah-Hartman dump_packet(packet); 263728674a7SGreg Kroah-Hartman break; 264728674a7SGreg Kroah-Hartman } 265728674a7SGreg Kroah-Hartman } 266728674a7SGreg Kroah-Hartman 267728674a7SGreg Kroah-Hartman static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet) 268728674a7SGreg Kroah-Hartman { 269728674a7SGreg Kroah-Hartman struct hvsi_query_response *resp = (struct hvsi_query_response *)packet; 27048079804SLaurent Dufour uint32_t mctrl_word; 271728674a7SGreg Kroah-Hartman 272728674a7SGreg Kroah-Hartman switch (hp->state) { 273728674a7SGreg Kroah-Hartman case HVSI_WAIT_FOR_VER_RESPONSE: 274728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_WAIT_FOR_VER_QUERY); 275728674a7SGreg Kroah-Hartman break; 276728674a7SGreg Kroah-Hartman case HVSI_WAIT_FOR_MCTRL_RESPONSE: 277728674a7SGreg Kroah-Hartman hp->mctrl = 0; 27848079804SLaurent Dufour mctrl_word = be32_to_cpu(resp->u.mctrl_word); 27948079804SLaurent Dufour if (mctrl_word & HVSI_TSDTR) 280728674a7SGreg Kroah-Hartman hp->mctrl |= TIOCM_DTR; 28148079804SLaurent Dufour if (mctrl_word & HVSI_TSCD) 282728674a7SGreg Kroah-Hartman hp->mctrl |= TIOCM_CD; 283728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_OPEN); 284728674a7SGreg Kroah-Hartman break; 285728674a7SGreg Kroah-Hartman default: 286728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: unexpected query response: ", hp->index); 287728674a7SGreg Kroah-Hartman dump_packet(packet); 288728674a7SGreg Kroah-Hartman break; 289728674a7SGreg Kroah-Hartman } 290728674a7SGreg Kroah-Hartman } 291728674a7SGreg Kroah-Hartman 292728674a7SGreg Kroah-Hartman /* respond to service processor's version query */ 293728674a7SGreg Kroah-Hartman static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) 294728674a7SGreg Kroah-Hartman { 295728674a7SGreg Kroah-Hartman struct hvsi_query_response packet __ALIGNED__; 296728674a7SGreg Kroah-Hartman int wrote; 297728674a7SGreg Kroah-Hartman 298048bee77SBenjamin Herrenschmidt packet.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER; 299048bee77SBenjamin Herrenschmidt packet.hdr.len = sizeof(struct hvsi_query_response); 30048079804SLaurent Dufour packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 30148079804SLaurent Dufour packet.verb = cpu_to_be16(VSV_SEND_VERSION_NUMBER); 302728674a7SGreg Kroah-Hartman packet.u.version = HVSI_VERSION; 30348079804SLaurent Dufour packet.query_seqno = cpu_to_be16(query_seqno+1); 304728674a7SGreg Kroah-Hartman 305048bee77SBenjamin Herrenschmidt pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); 306048bee77SBenjamin Herrenschmidt dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); 307728674a7SGreg Kroah-Hartman 308048bee77SBenjamin Herrenschmidt wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 309048bee77SBenjamin Herrenschmidt if (wrote != packet.hdr.len) { 310728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: couldn't send query response!\n", 311728674a7SGreg Kroah-Hartman hp->index); 312728674a7SGreg Kroah-Hartman return -EIO; 313728674a7SGreg Kroah-Hartman } 314728674a7SGreg Kroah-Hartman 315728674a7SGreg Kroah-Hartman return 0; 316728674a7SGreg Kroah-Hartman } 317728674a7SGreg Kroah-Hartman 318728674a7SGreg Kroah-Hartman static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) 319728674a7SGreg Kroah-Hartman { 320728674a7SGreg Kroah-Hartman struct hvsi_query *query = (struct hvsi_query *)packet; 321728674a7SGreg Kroah-Hartman 322728674a7SGreg Kroah-Hartman switch (hp->state) { 323728674a7SGreg Kroah-Hartman case HVSI_WAIT_FOR_VER_QUERY: 32448079804SLaurent Dufour hvsi_version_respond(hp, be16_to_cpu(query->hdr.seqno)); 325728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_OPEN); 326728674a7SGreg Kroah-Hartman break; 327728674a7SGreg Kroah-Hartman default: 328728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: unexpected query: ", hp->index); 329728674a7SGreg Kroah-Hartman dump_packet(packet); 330728674a7SGreg Kroah-Hartman break; 331728674a7SGreg Kroah-Hartman } 332728674a7SGreg Kroah-Hartman } 333728674a7SGreg Kroah-Hartman 33492a19f9cSJiri Slaby static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) 335728674a7SGreg Kroah-Hartman { 336728674a7SGreg Kroah-Hartman int i; 337728674a7SGreg Kroah-Hartman 338728674a7SGreg Kroah-Hartman for (i=0; i < len; i++) { 339728674a7SGreg Kroah-Hartman char c = buf[i]; 340728674a7SGreg Kroah-Hartman #ifdef CONFIG_MAGIC_SYSRQ 341728674a7SGreg Kroah-Hartman if (c == '\0') { 342728674a7SGreg Kroah-Hartman hp->sysrq = 1; 343728674a7SGreg Kroah-Hartman continue; 344728674a7SGreg Kroah-Hartman } else if (hp->sysrq) { 345728674a7SGreg Kroah-Hartman handle_sysrq(c); 346728674a7SGreg Kroah-Hartman hp->sysrq = 0; 347728674a7SGreg Kroah-Hartman continue; 348728674a7SGreg Kroah-Hartman } 349728674a7SGreg Kroah-Hartman #endif /* CONFIG_MAGIC_SYSRQ */ 35092a19f9cSJiri Slaby tty_insert_flip_char(&hp->port, c, 0); 351728674a7SGreg Kroah-Hartman } 352728674a7SGreg Kroah-Hartman } 353728674a7SGreg Kroah-Hartman 354728674a7SGreg Kroah-Hartman /* 355728674a7SGreg Kroah-Hartman * We could get 252 bytes of data at once here. But the tty layer only 356728674a7SGreg Kroah-Hartman * throttles us at TTY_THRESHOLD_THROTTLE (128) bytes, so we could overflow 357728674a7SGreg Kroah-Hartman * it. Accordingly we won't send more than 128 bytes at a time to the flip 358728674a7SGreg Kroah-Hartman * buffer, which will give the tty buffer a chance to throttle us. Should the 359728674a7SGreg Kroah-Hartman * value of TTY_THRESHOLD_THROTTLE change in n_tty.c, this code should be 360728674a7SGreg Kroah-Hartman * revisited. 361728674a7SGreg Kroah-Hartman */ 362728674a7SGreg Kroah-Hartman #define TTY_THRESHOLD_THROTTLE 128 36392a19f9cSJiri Slaby static bool hvsi_recv_data(struct hvsi_struct *hp, const uint8_t *packet) 364728674a7SGreg Kroah-Hartman { 365728674a7SGreg Kroah-Hartman const struct hvsi_header *header = (const struct hvsi_header *)packet; 366728674a7SGreg Kroah-Hartman const uint8_t *data = packet + sizeof(struct hvsi_header); 367728674a7SGreg Kroah-Hartman int datalen = header->len - sizeof(struct hvsi_header); 368728674a7SGreg Kroah-Hartman int overflow = datalen - TTY_THRESHOLD_THROTTLE; 369728674a7SGreg Kroah-Hartman 370728674a7SGreg Kroah-Hartman pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data); 371728674a7SGreg Kroah-Hartman 372728674a7SGreg Kroah-Hartman if (datalen == 0) 37328c0447dSJiri Slaby return false; 374728674a7SGreg Kroah-Hartman 375728674a7SGreg Kroah-Hartman if (overflow > 0) { 376728674a7SGreg Kroah-Hartman pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __func__); 377728674a7SGreg Kroah-Hartman datalen = TTY_THRESHOLD_THROTTLE; 378728674a7SGreg Kroah-Hartman } 379728674a7SGreg Kroah-Hartman 38092a19f9cSJiri Slaby hvsi_insert_chars(hp, data, datalen); 381728674a7SGreg Kroah-Hartman 382728674a7SGreg Kroah-Hartman if (overflow > 0) { 383728674a7SGreg Kroah-Hartman /* 384728674a7SGreg Kroah-Hartman * we still have more data to deliver, so we need to save off the 385728674a7SGreg Kroah-Hartman * overflow and send it later 386728674a7SGreg Kroah-Hartman */ 387728674a7SGreg Kroah-Hartman pr_debug("%s: deferring overflow\n", __func__); 388728674a7SGreg Kroah-Hartman memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow); 389728674a7SGreg Kroah-Hartman hp->n_throttle = overflow; 390728674a7SGreg Kroah-Hartman } 391728674a7SGreg Kroah-Hartman 39228c0447dSJiri Slaby return true; 393728674a7SGreg Kroah-Hartman } 394728674a7SGreg Kroah-Hartman 395728674a7SGreg Kroah-Hartman /* 396728674a7SGreg Kroah-Hartman * Returns true/false indicating data successfully read from hypervisor. 397728674a7SGreg Kroah-Hartman * Used both to get packets for tty connections and to advance the state 398728674a7SGreg Kroah-Hartman * machine during console handshaking (in which case tty = NULL and we ignore 399728674a7SGreg Kroah-Hartman * incoming data). 400728674a7SGreg Kroah-Hartman */ 40128c0447dSJiri Slaby static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct *tty, 40228c0447dSJiri Slaby struct hvsi_struct **handshake) 403728674a7SGreg Kroah-Hartman { 404728674a7SGreg Kroah-Hartman uint8_t *packet = hp->inbuf; 405728674a7SGreg Kroah-Hartman int chunklen; 40628c0447dSJiri Slaby bool flip = false; 407728674a7SGreg Kroah-Hartman 408728674a7SGreg Kroah-Hartman *handshake = NULL; 409728674a7SGreg Kroah-Hartman 410728674a7SGreg Kroah-Hartman chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); 411728674a7SGreg Kroah-Hartman if (chunklen == 0) { 412728674a7SGreg Kroah-Hartman pr_debug("%s: 0-length read\n", __func__); 413728674a7SGreg Kroah-Hartman return 0; 414728674a7SGreg Kroah-Hartman } 415728674a7SGreg Kroah-Hartman 416728674a7SGreg Kroah-Hartman pr_debug("%s: got %i bytes\n", __func__, chunklen); 417728674a7SGreg Kroah-Hartman dbg_dump_hex(hp->inbuf_end, chunklen); 418728674a7SGreg Kroah-Hartman 419728674a7SGreg Kroah-Hartman hp->inbuf_end += chunklen; 420728674a7SGreg Kroah-Hartman 421728674a7SGreg Kroah-Hartman /* handle all completed packets */ 422728674a7SGreg Kroah-Hartman while ((packet < hp->inbuf_end) && got_packet(hp, packet)) { 423728674a7SGreg Kroah-Hartman struct hvsi_header *header = (struct hvsi_header *)packet; 424728674a7SGreg Kroah-Hartman 425728674a7SGreg Kroah-Hartman if (!is_header(packet)) { 426728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: got malformed packet\n", hp->index); 427728674a7SGreg Kroah-Hartman /* skip bytes until we find a header or run out of data */ 428728674a7SGreg Kroah-Hartman while ((packet < hp->inbuf_end) && (!is_header(packet))) 429728674a7SGreg Kroah-Hartman packet++; 430728674a7SGreg Kroah-Hartman continue; 431728674a7SGreg Kroah-Hartman } 432728674a7SGreg Kroah-Hartman 433728674a7SGreg Kroah-Hartman pr_debug("%s: handling %i-byte packet\n", __func__, 434728674a7SGreg Kroah-Hartman len_packet(packet)); 435728674a7SGreg Kroah-Hartman dbg_dump_packet(packet); 436728674a7SGreg Kroah-Hartman 437728674a7SGreg Kroah-Hartman switch (header->type) { 438728674a7SGreg Kroah-Hartman case VS_DATA_PACKET_HEADER: 439728674a7SGreg Kroah-Hartman if (!is_open(hp)) 440728674a7SGreg Kroah-Hartman break; 44192a19f9cSJiri Slaby flip = hvsi_recv_data(hp, packet); 442728674a7SGreg Kroah-Hartman break; 443728674a7SGreg Kroah-Hartman case VS_CONTROL_PACKET_HEADER: 44428c0447dSJiri Slaby hvsi_recv_control(hp, packet, tty, handshake); 445728674a7SGreg Kroah-Hartman break; 446728674a7SGreg Kroah-Hartman case VS_QUERY_RESPONSE_PACKET_HEADER: 447728674a7SGreg Kroah-Hartman hvsi_recv_response(hp, packet); 448728674a7SGreg Kroah-Hartman break; 449728674a7SGreg Kroah-Hartman case VS_QUERY_PACKET_HEADER: 450728674a7SGreg Kroah-Hartman hvsi_recv_query(hp, packet); 451728674a7SGreg Kroah-Hartman break; 452728674a7SGreg Kroah-Hartman default: 453728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: unknown HVSI packet type 0x%x\n", 454728674a7SGreg Kroah-Hartman hp->index, header->type); 455728674a7SGreg Kroah-Hartman dump_packet(packet); 456728674a7SGreg Kroah-Hartman break; 457728674a7SGreg Kroah-Hartman } 458728674a7SGreg Kroah-Hartman 459728674a7SGreg Kroah-Hartman packet += len_packet(packet); 460728674a7SGreg Kroah-Hartman 46128c0447dSJiri Slaby if (*handshake) { 46228c0447dSJiri Slaby pr_debug("%s: handshake\n", __func__); 463728674a7SGreg Kroah-Hartman break; 464728674a7SGreg Kroah-Hartman } 465728674a7SGreg Kroah-Hartman } 466728674a7SGreg Kroah-Hartman 467728674a7SGreg Kroah-Hartman compact_inbuf(hp, packet); 468728674a7SGreg Kroah-Hartman 46928c0447dSJiri Slaby if (flip) 4702e124b4aSJiri Slaby tty_flip_buffer_push(&hp->port); 47128c0447dSJiri Slaby 472728674a7SGreg Kroah-Hartman return 1; 473728674a7SGreg Kroah-Hartman } 474728674a7SGreg Kroah-Hartman 47592a19f9cSJiri Slaby static void hvsi_send_overflow(struct hvsi_struct *hp) 476728674a7SGreg Kroah-Hartman { 477728674a7SGreg Kroah-Hartman pr_debug("%s: delivering %i bytes overflow\n", __func__, 478728674a7SGreg Kroah-Hartman hp->n_throttle); 479728674a7SGreg Kroah-Hartman 48092a19f9cSJiri Slaby hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); 481728674a7SGreg Kroah-Hartman hp->n_throttle = 0; 482728674a7SGreg Kroah-Hartman } 483728674a7SGreg Kroah-Hartman 484728674a7SGreg Kroah-Hartman /* 485728674a7SGreg Kroah-Hartman * must get all pending data because we only get an irq on empty->non-empty 486728674a7SGreg Kroah-Hartman * transition 487728674a7SGreg Kroah-Hartman */ 488728674a7SGreg Kroah-Hartman static irqreturn_t hvsi_interrupt(int irq, void *arg) 489728674a7SGreg Kroah-Hartman { 490728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = (struct hvsi_struct *)arg; 491728674a7SGreg Kroah-Hartman struct hvsi_struct *handshake; 492daea4402SJiri Slaby struct tty_struct *tty; 493728674a7SGreg Kroah-Hartman unsigned long flags; 494728674a7SGreg Kroah-Hartman int again = 1; 495728674a7SGreg Kroah-Hartman 496728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 497728674a7SGreg Kroah-Hartman 498daea4402SJiri Slaby tty = tty_port_tty_get(&hp->port); 499daea4402SJiri Slaby 500728674a7SGreg Kroah-Hartman while (again) { 501728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 502daea4402SJiri Slaby again = hvsi_load_chunk(hp, tty, &handshake); 503728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 504728674a7SGreg Kroah-Hartman 505728674a7SGreg Kroah-Hartman if (handshake) { 506728674a7SGreg Kroah-Hartman pr_debug("hvsi%i: attempting re-handshake\n", handshake->index); 507728674a7SGreg Kroah-Hartman schedule_work(&handshake->handshaker); 508728674a7SGreg Kroah-Hartman } 509728674a7SGreg Kroah-Hartman } 510728674a7SGreg Kroah-Hartman 511728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 512daea4402SJiri Slaby if (tty && hp->n_throttle && !test_bit(TTY_THROTTLED, &tty->flags)) { 513daea4402SJiri Slaby /* we weren't hung up and we weren't throttled, so we can 514daea4402SJiri Slaby * deliver the rest now */ 51592a19f9cSJiri Slaby hvsi_send_overflow(hp); 5162e124b4aSJiri Slaby tty_flip_buffer_push(&hp->port); 517728674a7SGreg Kroah-Hartman } 518728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 519728674a7SGreg Kroah-Hartman 520daea4402SJiri Slaby tty_kref_put(tty); 521daea4402SJiri Slaby 522728674a7SGreg Kroah-Hartman return IRQ_HANDLED; 523728674a7SGreg Kroah-Hartman } 524728674a7SGreg Kroah-Hartman 525728674a7SGreg Kroah-Hartman /* for boot console, before the irq handler is running */ 526728674a7SGreg Kroah-Hartman static int __init poll_for_state(struct hvsi_struct *hp, int state) 527728674a7SGreg Kroah-Hartman { 528728674a7SGreg Kroah-Hartman unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; 529728674a7SGreg Kroah-Hartman 530728674a7SGreg Kroah-Hartman for (;;) { 531728674a7SGreg Kroah-Hartman hvsi_interrupt(hp->virq, (void *)hp); /* get pending data */ 532728674a7SGreg Kroah-Hartman 533728674a7SGreg Kroah-Hartman if (hp->state == state) 534728674a7SGreg Kroah-Hartman return 0; 535728674a7SGreg Kroah-Hartman 536728674a7SGreg Kroah-Hartman mdelay(5); 537728674a7SGreg Kroah-Hartman if (time_after(jiffies, end_jiffies)) 538728674a7SGreg Kroah-Hartman return -EIO; 539728674a7SGreg Kroah-Hartman } 540728674a7SGreg Kroah-Hartman } 541728674a7SGreg Kroah-Hartman 542728674a7SGreg Kroah-Hartman /* wait for irq handler to change our state */ 543728674a7SGreg Kroah-Hartman static int wait_for_state(struct hvsi_struct *hp, int state) 544728674a7SGreg Kroah-Hartman { 545728674a7SGreg Kroah-Hartman int ret = 0; 546728674a7SGreg Kroah-Hartman 547728674a7SGreg Kroah-Hartman if (!wait_event_timeout(hp->stateq, (hp->state == state), HVSI_TIMEOUT)) 548728674a7SGreg Kroah-Hartman ret = -EIO; 549728674a7SGreg Kroah-Hartman 550728674a7SGreg Kroah-Hartman return ret; 551728674a7SGreg Kroah-Hartman } 552728674a7SGreg Kroah-Hartman 553728674a7SGreg Kroah-Hartman static int hvsi_query(struct hvsi_struct *hp, uint16_t verb) 554728674a7SGreg Kroah-Hartman { 555728674a7SGreg Kroah-Hartman struct hvsi_query packet __ALIGNED__; 556728674a7SGreg Kroah-Hartman int wrote; 557728674a7SGreg Kroah-Hartman 558048bee77SBenjamin Herrenschmidt packet.hdr.type = VS_QUERY_PACKET_HEADER; 559048bee77SBenjamin Herrenschmidt packet.hdr.len = sizeof(struct hvsi_query); 56048079804SLaurent Dufour packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 56148079804SLaurent Dufour packet.verb = cpu_to_be16(verb); 562728674a7SGreg Kroah-Hartman 563048bee77SBenjamin Herrenschmidt pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); 564048bee77SBenjamin Herrenschmidt dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); 565728674a7SGreg Kroah-Hartman 566048bee77SBenjamin Herrenschmidt wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 567048bee77SBenjamin Herrenschmidt if (wrote != packet.hdr.len) { 568728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index, 569728674a7SGreg Kroah-Hartman wrote); 570728674a7SGreg Kroah-Hartman return -EIO; 571728674a7SGreg Kroah-Hartman } 572728674a7SGreg Kroah-Hartman 573728674a7SGreg Kroah-Hartman return 0; 574728674a7SGreg Kroah-Hartman } 575728674a7SGreg Kroah-Hartman 576728674a7SGreg Kroah-Hartman static int hvsi_get_mctrl(struct hvsi_struct *hp) 577728674a7SGreg Kroah-Hartman { 578728674a7SGreg Kroah-Hartman int ret; 579728674a7SGreg Kroah-Hartman 580728674a7SGreg Kroah-Hartman set_state(hp, HVSI_WAIT_FOR_MCTRL_RESPONSE); 581728674a7SGreg Kroah-Hartman hvsi_query(hp, VSV_SEND_MODEM_CTL_STATUS); 582728674a7SGreg Kroah-Hartman 583728674a7SGreg Kroah-Hartman ret = hvsi_wait(hp, HVSI_OPEN); 584728674a7SGreg Kroah-Hartman if (ret < 0) { 585728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: didn't get modem flags\n", hp->index); 586728674a7SGreg Kroah-Hartman set_state(hp, HVSI_OPEN); 587728674a7SGreg Kroah-Hartman return ret; 588728674a7SGreg Kroah-Hartman } 589728674a7SGreg Kroah-Hartman 590728674a7SGreg Kroah-Hartman pr_debug("%s: mctrl 0x%x\n", __func__, hp->mctrl); 591728674a7SGreg Kroah-Hartman 592728674a7SGreg Kroah-Hartman return 0; 593728674a7SGreg Kroah-Hartman } 594728674a7SGreg Kroah-Hartman 595728674a7SGreg Kroah-Hartman /* note that we can only set DTR */ 596728674a7SGreg Kroah-Hartman static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) 597728674a7SGreg Kroah-Hartman { 598728674a7SGreg Kroah-Hartman struct hvsi_control packet __ALIGNED__; 599728674a7SGreg Kroah-Hartman int wrote; 600728674a7SGreg Kroah-Hartman 60148079804SLaurent Dufour packet.hdr.type = VS_CONTROL_PACKET_HEADER; 60248079804SLaurent Dufour packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 603048bee77SBenjamin Herrenschmidt packet.hdr.len = sizeof(struct hvsi_control); 60448079804SLaurent Dufour packet.verb = cpu_to_be16(VSV_SET_MODEM_CTL); 60548079804SLaurent Dufour packet.mask = cpu_to_be32(HVSI_TSDTR); 606728674a7SGreg Kroah-Hartman 607728674a7SGreg Kroah-Hartman if (mctrl & TIOCM_DTR) 60848079804SLaurent Dufour packet.word = cpu_to_be32(HVSI_TSDTR); 609728674a7SGreg Kroah-Hartman 610048bee77SBenjamin Herrenschmidt pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); 611048bee77SBenjamin Herrenschmidt dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); 612728674a7SGreg Kroah-Hartman 613048bee77SBenjamin Herrenschmidt wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 614048bee77SBenjamin Herrenschmidt if (wrote != packet.hdr.len) { 615728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index); 616728674a7SGreg Kroah-Hartman return -EIO; 617728674a7SGreg Kroah-Hartman } 618728674a7SGreg Kroah-Hartman 619728674a7SGreg Kroah-Hartman return 0; 620728674a7SGreg Kroah-Hartman } 621728674a7SGreg Kroah-Hartman 622728674a7SGreg Kroah-Hartman static void hvsi_drain_input(struct hvsi_struct *hp) 623728674a7SGreg Kroah-Hartman { 624728674a7SGreg Kroah-Hartman uint8_t buf[HVSI_MAX_READ] __ALIGNED__; 625728674a7SGreg Kroah-Hartman unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; 626728674a7SGreg Kroah-Hartman 627728674a7SGreg Kroah-Hartman while (time_before(end_jiffies, jiffies)) 628728674a7SGreg Kroah-Hartman if (0 == hvsi_read(hp, buf, HVSI_MAX_READ)) 629728674a7SGreg Kroah-Hartman break; 630728674a7SGreg Kroah-Hartman } 631728674a7SGreg Kroah-Hartman 632728674a7SGreg Kroah-Hartman static int hvsi_handshake(struct hvsi_struct *hp) 633728674a7SGreg Kroah-Hartman { 634728674a7SGreg Kroah-Hartman int ret; 635728674a7SGreg Kroah-Hartman 636728674a7SGreg Kroah-Hartman /* 637728674a7SGreg Kroah-Hartman * We could have a CLOSE or other data waiting for us before we even try 638728674a7SGreg Kroah-Hartman * to open; try to throw it all away so we don't get confused. (CLOSE 639728674a7SGreg Kroah-Hartman * is the first message sent up the pipe when the FSP comes online. We 640728674a7SGreg Kroah-Hartman * need to distinguish between "it came up a while ago and we're the first 641728674a7SGreg Kroah-Hartman * user" and "it was just reset before it saw our handshake packet".) 642728674a7SGreg Kroah-Hartman */ 643728674a7SGreg Kroah-Hartman hvsi_drain_input(hp); 644728674a7SGreg Kroah-Hartman 645728674a7SGreg Kroah-Hartman set_state(hp, HVSI_WAIT_FOR_VER_RESPONSE); 646728674a7SGreg Kroah-Hartman ret = hvsi_query(hp, VSV_SEND_VERSION_NUMBER); 647728674a7SGreg Kroah-Hartman if (ret < 0) { 648728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: couldn't send version query\n", hp->index); 649728674a7SGreg Kroah-Hartman return ret; 650728674a7SGreg Kroah-Hartman } 651728674a7SGreg Kroah-Hartman 652728674a7SGreg Kroah-Hartman ret = hvsi_wait(hp, HVSI_OPEN); 653728674a7SGreg Kroah-Hartman if (ret < 0) 654728674a7SGreg Kroah-Hartman return ret; 655728674a7SGreg Kroah-Hartman 656728674a7SGreg Kroah-Hartman return 0; 657728674a7SGreg Kroah-Hartman } 658728674a7SGreg Kroah-Hartman 659728674a7SGreg Kroah-Hartman static void hvsi_handshaker(struct work_struct *work) 660728674a7SGreg Kroah-Hartman { 661728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = 662728674a7SGreg Kroah-Hartman container_of(work, struct hvsi_struct, handshaker); 663728674a7SGreg Kroah-Hartman 664728674a7SGreg Kroah-Hartman if (hvsi_handshake(hp) >= 0) 665728674a7SGreg Kroah-Hartman return; 666728674a7SGreg Kroah-Hartman 667728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: re-handshaking failed\n", hp->index); 668728674a7SGreg Kroah-Hartman if (is_console(hp)) { 669728674a7SGreg Kroah-Hartman /* 670728674a7SGreg Kroah-Hartman * ttys will re-attempt the handshake via hvsi_open, but 671728674a7SGreg Kroah-Hartman * the console will not. 672728674a7SGreg Kroah-Hartman */ 673728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: lost console!\n", hp->index); 674728674a7SGreg Kroah-Hartman } 675728674a7SGreg Kroah-Hartman } 676728674a7SGreg Kroah-Hartman 677728674a7SGreg Kroah-Hartman static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count) 678728674a7SGreg Kroah-Hartman { 679728674a7SGreg Kroah-Hartman struct hvsi_data packet __ALIGNED__; 680728674a7SGreg Kroah-Hartman int ret; 681728674a7SGreg Kroah-Hartman 682728674a7SGreg Kroah-Hartman BUG_ON(count > HVSI_MAX_OUTGOING_DATA); 683728674a7SGreg Kroah-Hartman 684048bee77SBenjamin Herrenschmidt packet.hdr.type = VS_DATA_PACKET_HEADER; 68548079804SLaurent Dufour packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 686048bee77SBenjamin Herrenschmidt packet.hdr.len = count + sizeof(struct hvsi_header); 687728674a7SGreg Kroah-Hartman memcpy(&packet.data, buf, count); 688728674a7SGreg Kroah-Hartman 689048bee77SBenjamin Herrenschmidt ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 690048bee77SBenjamin Herrenschmidt if (ret == packet.hdr.len) { 691728674a7SGreg Kroah-Hartman /* return the number of chars written, not the packet length */ 692728674a7SGreg Kroah-Hartman return count; 693728674a7SGreg Kroah-Hartman } 694728674a7SGreg Kroah-Hartman return ret; /* return any errors */ 695728674a7SGreg Kroah-Hartman } 696728674a7SGreg Kroah-Hartman 697728674a7SGreg Kroah-Hartman static void hvsi_close_protocol(struct hvsi_struct *hp) 698728674a7SGreg Kroah-Hartman { 699728674a7SGreg Kroah-Hartman struct hvsi_control packet __ALIGNED__; 700728674a7SGreg Kroah-Hartman 701048bee77SBenjamin Herrenschmidt packet.hdr.type = VS_CONTROL_PACKET_HEADER; 70248079804SLaurent Dufour packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 703048bee77SBenjamin Herrenschmidt packet.hdr.len = 6; 70448079804SLaurent Dufour packet.verb = cpu_to_be16(VSV_CLOSE_PROTOCOL); 705728674a7SGreg Kroah-Hartman 706048bee77SBenjamin Herrenschmidt pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); 707048bee77SBenjamin Herrenschmidt dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); 708728674a7SGreg Kroah-Hartman 709048bee77SBenjamin Herrenschmidt hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 710728674a7SGreg Kroah-Hartman } 711728674a7SGreg Kroah-Hartman 712728674a7SGreg Kroah-Hartman static int hvsi_open(struct tty_struct *tty, struct file *filp) 713728674a7SGreg Kroah-Hartman { 714728674a7SGreg Kroah-Hartman struct hvsi_struct *hp; 715728674a7SGreg Kroah-Hartman unsigned long flags; 716728674a7SGreg Kroah-Hartman int ret; 717728674a7SGreg Kroah-Hartman 718728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 719728674a7SGreg Kroah-Hartman 720410235fdSJiri Slaby hp = &hvsi_ports[tty->index]; 721728674a7SGreg Kroah-Hartman 722728674a7SGreg Kroah-Hartman tty->driver_data = hp; 723728674a7SGreg Kroah-Hartman 724728674a7SGreg Kroah-Hartman mb(); 725728674a7SGreg Kroah-Hartman if (hp->state == HVSI_FSP_DIED) 726728674a7SGreg Kroah-Hartman return -EIO; 727728674a7SGreg Kroah-Hartman 728daea4402SJiri Slaby tty_port_tty_set(&hp->port, tty); 729728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 730d73a4e79SJiri Slaby hp->port.count++; 731728674a7SGreg Kroah-Hartman atomic_set(&hp->seqno, 0); 732728674a7SGreg Kroah-Hartman h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); 733728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 734728674a7SGreg Kroah-Hartman 735728674a7SGreg Kroah-Hartman if (is_console(hp)) 736728674a7SGreg Kroah-Hartman return 0; /* this has already been handshaked as the console */ 737728674a7SGreg Kroah-Hartman 738728674a7SGreg Kroah-Hartman ret = hvsi_handshake(hp); 739728674a7SGreg Kroah-Hartman if (ret < 0) { 740728674a7SGreg Kroah-Hartman printk(KERN_ERR "%s: HVSI handshaking failed\n", tty->name); 741728674a7SGreg Kroah-Hartman return ret; 742728674a7SGreg Kroah-Hartman } 743728674a7SGreg Kroah-Hartman 744728674a7SGreg Kroah-Hartman ret = hvsi_get_mctrl(hp); 745728674a7SGreg Kroah-Hartman if (ret < 0) { 746728674a7SGreg Kroah-Hartman printk(KERN_ERR "%s: couldn't get initial modem flags\n", tty->name); 747728674a7SGreg Kroah-Hartman return ret; 748728674a7SGreg Kroah-Hartman } 749728674a7SGreg Kroah-Hartman 750728674a7SGreg Kroah-Hartman ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); 751728674a7SGreg Kroah-Hartman if (ret < 0) { 752728674a7SGreg Kroah-Hartman printk(KERN_ERR "%s: couldn't set DTR\n", tty->name); 753728674a7SGreg Kroah-Hartman return ret; 754728674a7SGreg Kroah-Hartman } 755728674a7SGreg Kroah-Hartman 756728674a7SGreg Kroah-Hartman return 0; 757728674a7SGreg Kroah-Hartman } 758728674a7SGreg Kroah-Hartman 759728674a7SGreg Kroah-Hartman /* wait for hvsi_write_worker to empty hp->outbuf */ 760728674a7SGreg Kroah-Hartman static void hvsi_flush_output(struct hvsi_struct *hp) 761728674a7SGreg Kroah-Hartman { 762728674a7SGreg Kroah-Hartman wait_event_timeout(hp->emptyq, (hp->n_outbuf <= 0), HVSI_TIMEOUT); 763728674a7SGreg Kroah-Hartman 764728674a7SGreg Kroah-Hartman /* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */ 765728674a7SGreg Kroah-Hartman cancel_delayed_work_sync(&hp->writer); 76643829731STejun Heo flush_work(&hp->handshaker); 767728674a7SGreg Kroah-Hartman 768728674a7SGreg Kroah-Hartman /* 769728674a7SGreg Kroah-Hartman * it's also possible that our timeout expired and hvsi_write_worker 770728674a7SGreg Kroah-Hartman * didn't manage to push outbuf. poof. 771728674a7SGreg Kroah-Hartman */ 772728674a7SGreg Kroah-Hartman hp->n_outbuf = 0; 773728674a7SGreg Kroah-Hartman } 774728674a7SGreg Kroah-Hartman 775728674a7SGreg Kroah-Hartman static void hvsi_close(struct tty_struct *tty, struct file *filp) 776728674a7SGreg Kroah-Hartman { 777728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 778728674a7SGreg Kroah-Hartman unsigned long flags; 779728674a7SGreg Kroah-Hartman 780728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 781728674a7SGreg Kroah-Hartman 782728674a7SGreg Kroah-Hartman if (tty_hung_up_p(filp)) 783728674a7SGreg Kroah-Hartman return; 784728674a7SGreg Kroah-Hartman 785728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 786728674a7SGreg Kroah-Hartman 787d73a4e79SJiri Slaby if (--hp->port.count == 0) { 788daea4402SJiri Slaby tty_port_tty_set(&hp->port, NULL); 789728674a7SGreg Kroah-Hartman hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ 790728674a7SGreg Kroah-Hartman 791728674a7SGreg Kroah-Hartman /* only close down connection if it is not the console */ 792728674a7SGreg Kroah-Hartman if (!is_console(hp)) { 793728674a7SGreg Kroah-Hartman h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); /* no more irqs */ 794728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_CLOSED); 795728674a7SGreg Kroah-Hartman /* 796728674a7SGreg Kroah-Hartman * any data delivered to the tty layer after this will be 797728674a7SGreg Kroah-Hartman * discarded (except for XON/XOFF) 798728674a7SGreg Kroah-Hartman */ 799728674a7SGreg Kroah-Hartman tty->closing = 1; 800728674a7SGreg Kroah-Hartman 801728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 802728674a7SGreg Kroah-Hartman 803728674a7SGreg Kroah-Hartman /* let any existing irq handlers finish. no more will start. */ 804728674a7SGreg Kroah-Hartman synchronize_irq(hp->virq); 805728674a7SGreg Kroah-Hartman 806728674a7SGreg Kroah-Hartman /* hvsi_write_worker will re-schedule until outbuf is empty. */ 807728674a7SGreg Kroah-Hartman hvsi_flush_output(hp); 808728674a7SGreg Kroah-Hartman 809728674a7SGreg Kroah-Hartman /* tell FSP to stop sending data */ 810728674a7SGreg Kroah-Hartman hvsi_close_protocol(hp); 811728674a7SGreg Kroah-Hartman 812728674a7SGreg Kroah-Hartman /* 813728674a7SGreg Kroah-Hartman * drain anything FSP is still in the middle of sending, and let 814728674a7SGreg Kroah-Hartman * hvsi_handshake drain the rest on the next open. 815728674a7SGreg Kroah-Hartman */ 816728674a7SGreg Kroah-Hartman hvsi_drain_input(hp); 817728674a7SGreg Kroah-Hartman 818728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 819728674a7SGreg Kroah-Hartman } 820d73a4e79SJiri Slaby } else if (hp->port.count < 0) 821728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", 822d73a4e79SJiri Slaby hp - hvsi_ports, hp->port.count); 823728674a7SGreg Kroah-Hartman 824728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 825728674a7SGreg Kroah-Hartman } 826728674a7SGreg Kroah-Hartman 827728674a7SGreg Kroah-Hartman static void hvsi_hangup(struct tty_struct *tty) 828728674a7SGreg Kroah-Hartman { 829728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 830728674a7SGreg Kroah-Hartman unsigned long flags; 831728674a7SGreg Kroah-Hartman 832728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 833728674a7SGreg Kroah-Hartman 834daea4402SJiri Slaby tty_port_tty_set(&hp->port, NULL); 835728674a7SGreg Kroah-Hartman 836daea4402SJiri Slaby spin_lock_irqsave(&hp->lock, flags); 837d73a4e79SJiri Slaby hp->port.count = 0; 838728674a7SGreg Kroah-Hartman hp->n_outbuf = 0; 839728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 840728674a7SGreg Kroah-Hartman } 841728674a7SGreg Kroah-Hartman 842728674a7SGreg Kroah-Hartman /* called with hp->lock held */ 843728674a7SGreg Kroah-Hartman static void hvsi_push(struct hvsi_struct *hp) 844728674a7SGreg Kroah-Hartman { 845728674a7SGreg Kroah-Hartman int n; 846728674a7SGreg Kroah-Hartman 847728674a7SGreg Kroah-Hartman if (hp->n_outbuf <= 0) 848728674a7SGreg Kroah-Hartman return; 849728674a7SGreg Kroah-Hartman 850728674a7SGreg Kroah-Hartman n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf); 851728674a7SGreg Kroah-Hartman if (n > 0) { 852728674a7SGreg Kroah-Hartman /* success */ 853728674a7SGreg Kroah-Hartman pr_debug("%s: wrote %i chars\n", __func__, n); 854728674a7SGreg Kroah-Hartman hp->n_outbuf = 0; 855728674a7SGreg Kroah-Hartman } else if (n == -EIO) { 856728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_FSP_DIED); 857728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: service processor died\n", hp->index); 858728674a7SGreg Kroah-Hartman } 859728674a7SGreg Kroah-Hartman } 860728674a7SGreg Kroah-Hartman 861728674a7SGreg Kroah-Hartman /* hvsi_write_worker will keep rescheduling itself until outbuf is empty */ 862728674a7SGreg Kroah-Hartman static void hvsi_write_worker(struct work_struct *work) 863728674a7SGreg Kroah-Hartman { 864728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = 865728674a7SGreg Kroah-Hartman container_of(work, struct hvsi_struct, writer.work); 866728674a7SGreg Kroah-Hartman unsigned long flags; 867728674a7SGreg Kroah-Hartman #ifdef DEBUG 868728674a7SGreg Kroah-Hartman static long start_j = 0; 869728674a7SGreg Kroah-Hartman 870728674a7SGreg Kroah-Hartman if (start_j == 0) 871728674a7SGreg Kroah-Hartman start_j = jiffies; 872728674a7SGreg Kroah-Hartman #endif /* DEBUG */ 873728674a7SGreg Kroah-Hartman 874728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 875728674a7SGreg Kroah-Hartman 876728674a7SGreg Kroah-Hartman pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); 877728674a7SGreg Kroah-Hartman 878728674a7SGreg Kroah-Hartman if (!is_open(hp)) { 879728674a7SGreg Kroah-Hartman /* 880728674a7SGreg Kroah-Hartman * We could have a non-open connection if the service processor died 881728674a7SGreg Kroah-Hartman * while we were busily scheduling ourselves. In that case, it could 882728674a7SGreg Kroah-Hartman * be minutes before the service processor comes back, so only try 883728674a7SGreg Kroah-Hartman * again once a second. 884728674a7SGreg Kroah-Hartman */ 885728674a7SGreg Kroah-Hartman schedule_delayed_work(&hp->writer, HZ); 886728674a7SGreg Kroah-Hartman goto out; 887728674a7SGreg Kroah-Hartman } 888728674a7SGreg Kroah-Hartman 889728674a7SGreg Kroah-Hartman hvsi_push(hp); 890728674a7SGreg Kroah-Hartman if (hp->n_outbuf > 0) 891728674a7SGreg Kroah-Hartman schedule_delayed_work(&hp->writer, 10); 892728674a7SGreg Kroah-Hartman else { 893728674a7SGreg Kroah-Hartman #ifdef DEBUG 894728674a7SGreg Kroah-Hartman pr_debug("%s: outbuf emptied after %li jiffies\n", __func__, 895728674a7SGreg Kroah-Hartman jiffies - start_j); 896728674a7SGreg Kroah-Hartman start_j = 0; 897728674a7SGreg Kroah-Hartman #endif /* DEBUG */ 898728674a7SGreg Kroah-Hartman wake_up_all(&hp->emptyq); 8996aad04f2SJiri Slaby tty_port_tty_wakeup(&hp->port); 900728674a7SGreg Kroah-Hartman } 901728674a7SGreg Kroah-Hartman 902728674a7SGreg Kroah-Hartman out: 903728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 904728674a7SGreg Kroah-Hartman } 905728674a7SGreg Kroah-Hartman 906728674a7SGreg Kroah-Hartman static int hvsi_write_room(struct tty_struct *tty) 907728674a7SGreg Kroah-Hartman { 908728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 909728674a7SGreg Kroah-Hartman 910728674a7SGreg Kroah-Hartman return N_OUTBUF - hp->n_outbuf; 911728674a7SGreg Kroah-Hartman } 912728674a7SGreg Kroah-Hartman 913728674a7SGreg Kroah-Hartman static int hvsi_chars_in_buffer(struct tty_struct *tty) 914728674a7SGreg Kroah-Hartman { 915728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 916728674a7SGreg Kroah-Hartman 917728674a7SGreg Kroah-Hartman return hp->n_outbuf; 918728674a7SGreg Kroah-Hartman } 919728674a7SGreg Kroah-Hartman 920728674a7SGreg Kroah-Hartman static int hvsi_write(struct tty_struct *tty, 921728674a7SGreg Kroah-Hartman const unsigned char *buf, int count) 922728674a7SGreg Kroah-Hartman { 923728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 924728674a7SGreg Kroah-Hartman const char *source = buf; 925728674a7SGreg Kroah-Hartman unsigned long flags; 926728674a7SGreg Kroah-Hartman int total = 0; 927728674a7SGreg Kroah-Hartman int origcount = count; 928728674a7SGreg Kroah-Hartman 929728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 930728674a7SGreg Kroah-Hartman 931728674a7SGreg Kroah-Hartman pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); 932728674a7SGreg Kroah-Hartman 933728674a7SGreg Kroah-Hartman if (!is_open(hp)) { 934728674a7SGreg Kroah-Hartman /* we're either closing or not yet open; don't accept data */ 935728674a7SGreg Kroah-Hartman pr_debug("%s: not open\n", __func__); 936728674a7SGreg Kroah-Hartman goto out; 937728674a7SGreg Kroah-Hartman } 938728674a7SGreg Kroah-Hartman 939728674a7SGreg Kroah-Hartman /* 940728674a7SGreg Kroah-Hartman * when the hypervisor buffer (16K) fills, data will stay in hp->outbuf 941728674a7SGreg Kroah-Hartman * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls 942728674a7SGreg Kroah-Hartman * will see there is no room in outbuf and return. 943728674a7SGreg Kroah-Hartman */ 94428c0447dSJiri Slaby while ((count > 0) && (hvsi_write_room(tty) > 0)) { 94528c0447dSJiri Slaby int chunksize = min(count, hvsi_write_room(tty)); 946728674a7SGreg Kroah-Hartman 947728674a7SGreg Kroah-Hartman BUG_ON(hp->n_outbuf < 0); 948728674a7SGreg Kroah-Hartman memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); 949728674a7SGreg Kroah-Hartman hp->n_outbuf += chunksize; 950728674a7SGreg Kroah-Hartman 951728674a7SGreg Kroah-Hartman total += chunksize; 952728674a7SGreg Kroah-Hartman source += chunksize; 953728674a7SGreg Kroah-Hartman count -= chunksize; 954728674a7SGreg Kroah-Hartman hvsi_push(hp); 955728674a7SGreg Kroah-Hartman } 956728674a7SGreg Kroah-Hartman 957728674a7SGreg Kroah-Hartman if (hp->n_outbuf > 0) { 958728674a7SGreg Kroah-Hartman /* 959728674a7SGreg Kroah-Hartman * we weren't able to write it all to the hypervisor. 960728674a7SGreg Kroah-Hartman * schedule another push attempt. 961728674a7SGreg Kroah-Hartman */ 962728674a7SGreg Kroah-Hartman schedule_delayed_work(&hp->writer, 10); 963728674a7SGreg Kroah-Hartman } 964728674a7SGreg Kroah-Hartman 965728674a7SGreg Kroah-Hartman out: 966728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 967728674a7SGreg Kroah-Hartman 968728674a7SGreg Kroah-Hartman if (total != origcount) 969728674a7SGreg Kroah-Hartman pr_debug("%s: wanted %i, only wrote %i\n", __func__, origcount, 970728674a7SGreg Kroah-Hartman total); 971728674a7SGreg Kroah-Hartman 972728674a7SGreg Kroah-Hartman return total; 973728674a7SGreg Kroah-Hartman } 974728674a7SGreg Kroah-Hartman 975728674a7SGreg Kroah-Hartman /* 976728674a7SGreg Kroah-Hartman * I have never seen throttle or unthrottle called, so this little throttle 977728674a7SGreg Kroah-Hartman * buffering scheme may or may not work. 978728674a7SGreg Kroah-Hartman */ 979728674a7SGreg Kroah-Hartman static void hvsi_throttle(struct tty_struct *tty) 980728674a7SGreg Kroah-Hartman { 981728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 982728674a7SGreg Kroah-Hartman 983728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 984728674a7SGreg Kroah-Hartman 985728674a7SGreg Kroah-Hartman h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); 986728674a7SGreg Kroah-Hartman } 987728674a7SGreg Kroah-Hartman 988728674a7SGreg Kroah-Hartman static void hvsi_unthrottle(struct tty_struct *tty) 989728674a7SGreg Kroah-Hartman { 990728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 991728674a7SGreg Kroah-Hartman unsigned long flags; 992728674a7SGreg Kroah-Hartman 993728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 994728674a7SGreg Kroah-Hartman 995728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 996728674a7SGreg Kroah-Hartman if (hp->n_throttle) { 99792a19f9cSJiri Slaby hvsi_send_overflow(hp); 9982e124b4aSJiri Slaby tty_flip_buffer_push(&hp->port); 999728674a7SGreg Kroah-Hartman } 1000728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 1001728674a7SGreg Kroah-Hartman 1002728674a7SGreg Kroah-Hartman 1003728674a7SGreg Kroah-Hartman h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); 1004728674a7SGreg Kroah-Hartman } 1005728674a7SGreg Kroah-Hartman 100660b33c13SAlan Cox static int hvsi_tiocmget(struct tty_struct *tty) 1007728674a7SGreg Kroah-Hartman { 1008728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 1009728674a7SGreg Kroah-Hartman 1010728674a7SGreg Kroah-Hartman hvsi_get_mctrl(hp); 1011728674a7SGreg Kroah-Hartman return hp->mctrl; 1012728674a7SGreg Kroah-Hartman } 1013728674a7SGreg Kroah-Hartman 101420b9d177SAlan Cox static int hvsi_tiocmset(struct tty_struct *tty, 1015728674a7SGreg Kroah-Hartman unsigned int set, unsigned int clear) 1016728674a7SGreg Kroah-Hartman { 1017728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 1018728674a7SGreg Kroah-Hartman unsigned long flags; 1019728674a7SGreg Kroah-Hartman uint16_t new_mctrl; 1020728674a7SGreg Kroah-Hartman 1021728674a7SGreg Kroah-Hartman /* we can only alter DTR */ 1022728674a7SGreg Kroah-Hartman clear &= TIOCM_DTR; 1023728674a7SGreg Kroah-Hartman set &= TIOCM_DTR; 1024728674a7SGreg Kroah-Hartman 1025728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 1026728674a7SGreg Kroah-Hartman 1027728674a7SGreg Kroah-Hartman new_mctrl = (hp->mctrl & ~clear) | set; 1028728674a7SGreg Kroah-Hartman 1029728674a7SGreg Kroah-Hartman if (hp->mctrl != new_mctrl) { 1030728674a7SGreg Kroah-Hartman hvsi_set_mctrl(hp, new_mctrl); 1031728674a7SGreg Kroah-Hartman hp->mctrl = new_mctrl; 1032728674a7SGreg Kroah-Hartman } 1033728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 1034728674a7SGreg Kroah-Hartman 1035728674a7SGreg Kroah-Hartman return 0; 1036728674a7SGreg Kroah-Hartman } 1037728674a7SGreg Kroah-Hartman 1038728674a7SGreg Kroah-Hartman 1039728674a7SGreg Kroah-Hartman static const struct tty_operations hvsi_ops = { 1040728674a7SGreg Kroah-Hartman .open = hvsi_open, 1041728674a7SGreg Kroah-Hartman .close = hvsi_close, 1042728674a7SGreg Kroah-Hartman .write = hvsi_write, 1043728674a7SGreg Kroah-Hartman .hangup = hvsi_hangup, 1044728674a7SGreg Kroah-Hartman .write_room = hvsi_write_room, 1045728674a7SGreg Kroah-Hartman .chars_in_buffer = hvsi_chars_in_buffer, 1046728674a7SGreg Kroah-Hartman .throttle = hvsi_throttle, 1047728674a7SGreg Kroah-Hartman .unthrottle = hvsi_unthrottle, 1048728674a7SGreg Kroah-Hartman .tiocmget = hvsi_tiocmget, 1049728674a7SGreg Kroah-Hartman .tiocmset = hvsi_tiocmset, 1050728674a7SGreg Kroah-Hartman }; 1051728674a7SGreg Kroah-Hartman 1052728674a7SGreg Kroah-Hartman static int __init hvsi_init(void) 1053728674a7SGreg Kroah-Hartman { 1054728674a7SGreg Kroah-Hartman int i; 1055728674a7SGreg Kroah-Hartman 1056728674a7SGreg Kroah-Hartman hvsi_driver = alloc_tty_driver(hvsi_count); 1057728674a7SGreg Kroah-Hartman if (!hvsi_driver) 1058728674a7SGreg Kroah-Hartman return -ENOMEM; 1059728674a7SGreg Kroah-Hartman 1060728674a7SGreg Kroah-Hartman hvsi_driver->driver_name = "hvsi"; 1061728674a7SGreg Kroah-Hartman hvsi_driver->name = "hvsi"; 1062728674a7SGreg Kroah-Hartman hvsi_driver->major = HVSI_MAJOR; 1063728674a7SGreg Kroah-Hartman hvsi_driver->minor_start = HVSI_MINOR; 1064728674a7SGreg Kroah-Hartman hvsi_driver->type = TTY_DRIVER_TYPE_SYSTEM; 1065728674a7SGreg Kroah-Hartman hvsi_driver->init_termios = tty_std_termios; 1066728674a7SGreg Kroah-Hartman hvsi_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; 1067728674a7SGreg Kroah-Hartman hvsi_driver->init_termios.c_ispeed = 9600; 1068728674a7SGreg Kroah-Hartman hvsi_driver->init_termios.c_ospeed = 9600; 1069728674a7SGreg Kroah-Hartman hvsi_driver->flags = TTY_DRIVER_REAL_RAW; 1070728674a7SGreg Kroah-Hartman tty_set_operations(hvsi_driver, &hvsi_ops); 1071728674a7SGreg Kroah-Hartman 1072728674a7SGreg Kroah-Hartman for (i=0; i < hvsi_count; i++) { 1073728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = &hvsi_ports[i]; 1074728674a7SGreg Kroah-Hartman int ret = 1; 1075728674a7SGreg Kroah-Hartman 1076b19e2ca7SJiri Slaby tty_port_link_device(&hp->port, hvsi_driver, i); 1077b19e2ca7SJiri Slaby 10789cfb5c05SYong Zhang ret = request_irq(hp->virq, hvsi_interrupt, 0, "hvsi", hp); 1079728674a7SGreg Kroah-Hartman if (ret) 1080728674a7SGreg Kroah-Hartman printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n", 1081728674a7SGreg Kroah-Hartman hp->virq, ret); 1082728674a7SGreg Kroah-Hartman } 1083728674a7SGreg Kroah-Hartman hvsi_wait = wait_for_state; /* irqs active now */ 1084728674a7SGreg Kroah-Hartman 1085728674a7SGreg Kroah-Hartman if (tty_register_driver(hvsi_driver)) 1086728674a7SGreg Kroah-Hartman panic("Couldn't register hvsi console driver\n"); 1087728674a7SGreg Kroah-Hartman 1088728674a7SGreg Kroah-Hartman printk(KERN_DEBUG "HVSI: registered %i devices\n", hvsi_count); 1089728674a7SGreg Kroah-Hartman 1090728674a7SGreg Kroah-Hartman return 0; 1091728674a7SGreg Kroah-Hartman } 1092728674a7SGreg Kroah-Hartman device_initcall(hvsi_init); 1093728674a7SGreg Kroah-Hartman 1094728674a7SGreg Kroah-Hartman /***** console (not tty) code: *****/ 1095728674a7SGreg Kroah-Hartman 1096728674a7SGreg Kroah-Hartman static void hvsi_console_print(struct console *console, const char *buf, 1097728674a7SGreg Kroah-Hartman unsigned int count) 1098728674a7SGreg Kroah-Hartman { 1099728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = &hvsi_ports[console->index]; 1100728674a7SGreg Kroah-Hartman char c[HVSI_MAX_OUTGOING_DATA] __ALIGNED__; 1101728674a7SGreg Kroah-Hartman unsigned int i = 0, n = 0; 1102728674a7SGreg Kroah-Hartman int ret, donecr = 0; 1103728674a7SGreg Kroah-Hartman 1104728674a7SGreg Kroah-Hartman mb(); 1105728674a7SGreg Kroah-Hartman if (!is_open(hp)) 1106728674a7SGreg Kroah-Hartman return; 1107728674a7SGreg Kroah-Hartman 1108728674a7SGreg Kroah-Hartman /* 1109728674a7SGreg Kroah-Hartman * ugh, we have to translate LF -> CRLF ourselves, in place. 1110728674a7SGreg Kroah-Hartman * copied from hvc_console.c: 1111728674a7SGreg Kroah-Hartman */ 1112728674a7SGreg Kroah-Hartman while (count > 0 || i > 0) { 1113728674a7SGreg Kroah-Hartman if (count > 0 && i < sizeof(c)) { 1114728674a7SGreg Kroah-Hartman if (buf[n] == '\n' && !donecr) { 1115728674a7SGreg Kroah-Hartman c[i++] = '\r'; 1116728674a7SGreg Kroah-Hartman donecr = 1; 1117728674a7SGreg Kroah-Hartman } else { 1118728674a7SGreg Kroah-Hartman c[i++] = buf[n++]; 1119728674a7SGreg Kroah-Hartman donecr = 0; 1120728674a7SGreg Kroah-Hartman --count; 1121728674a7SGreg Kroah-Hartman } 1122728674a7SGreg Kroah-Hartman } else { 1123728674a7SGreg Kroah-Hartman ret = hvsi_put_chars(hp, c, i); 1124728674a7SGreg Kroah-Hartman if (ret < 0) 1125728674a7SGreg Kroah-Hartman i = 0; 1126728674a7SGreg Kroah-Hartman i -= ret; 1127728674a7SGreg Kroah-Hartman } 1128728674a7SGreg Kroah-Hartman } 1129728674a7SGreg Kroah-Hartman } 1130728674a7SGreg Kroah-Hartman 1131728674a7SGreg Kroah-Hartman static struct tty_driver *hvsi_console_device(struct console *console, 1132728674a7SGreg Kroah-Hartman int *index) 1133728674a7SGreg Kroah-Hartman { 1134728674a7SGreg Kroah-Hartman *index = console->index; 1135728674a7SGreg Kroah-Hartman return hvsi_driver; 1136728674a7SGreg Kroah-Hartman } 1137728674a7SGreg Kroah-Hartman 1138728674a7SGreg Kroah-Hartman static int __init hvsi_console_setup(struct console *console, char *options) 1139728674a7SGreg Kroah-Hartman { 1140728674a7SGreg Kroah-Hartman struct hvsi_struct *hp; 1141728674a7SGreg Kroah-Hartman int ret; 1142728674a7SGreg Kroah-Hartman 1143728674a7SGreg Kroah-Hartman if (console->index < 0 || console->index >= hvsi_count) 1144728674a7SGreg Kroah-Hartman return -1; 1145728674a7SGreg Kroah-Hartman hp = &hvsi_ports[console->index]; 1146728674a7SGreg Kroah-Hartman 1147728674a7SGreg Kroah-Hartman /* give the FSP a chance to change the baud rate when we re-open */ 1148728674a7SGreg Kroah-Hartman hvsi_close_protocol(hp); 1149728674a7SGreg Kroah-Hartman 1150728674a7SGreg Kroah-Hartman ret = hvsi_handshake(hp); 1151728674a7SGreg Kroah-Hartman if (ret < 0) 1152728674a7SGreg Kroah-Hartman return ret; 1153728674a7SGreg Kroah-Hartman 1154728674a7SGreg Kroah-Hartman ret = hvsi_get_mctrl(hp); 1155728674a7SGreg Kroah-Hartman if (ret < 0) 1156728674a7SGreg Kroah-Hartman return ret; 1157728674a7SGreg Kroah-Hartman 1158728674a7SGreg Kroah-Hartman ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); 1159728674a7SGreg Kroah-Hartman if (ret < 0) 1160728674a7SGreg Kroah-Hartman return ret; 1161728674a7SGreg Kroah-Hartman 1162728674a7SGreg Kroah-Hartman hp->flags |= HVSI_CONSOLE; 1163728674a7SGreg Kroah-Hartman 1164728674a7SGreg Kroah-Hartman return 0; 1165728674a7SGreg Kroah-Hartman } 1166728674a7SGreg Kroah-Hartman 1167728674a7SGreg Kroah-Hartman static struct console hvsi_console = { 1168728674a7SGreg Kroah-Hartman .name = "hvsi", 1169728674a7SGreg Kroah-Hartman .write = hvsi_console_print, 1170728674a7SGreg Kroah-Hartman .device = hvsi_console_device, 1171728674a7SGreg Kroah-Hartman .setup = hvsi_console_setup, 1172728674a7SGreg Kroah-Hartman .flags = CON_PRINTBUFFER, 1173728674a7SGreg Kroah-Hartman .index = -1, 1174728674a7SGreg Kroah-Hartman }; 1175728674a7SGreg Kroah-Hartman 1176728674a7SGreg Kroah-Hartman static int __init hvsi_console_init(void) 1177728674a7SGreg Kroah-Hartman { 1178728674a7SGreg Kroah-Hartman struct device_node *vty; 1179728674a7SGreg Kroah-Hartman 1180728674a7SGreg Kroah-Hartman hvsi_wait = poll_for_state; /* no irqs yet; must poll */ 1181728674a7SGreg Kroah-Hartman 1182728674a7SGreg Kroah-Hartman /* search device tree for vty nodes */ 1183074d35ceSWei Yongjun for_each_compatible_node(vty, "serial", "hvterm-protocol") { 1184728674a7SGreg Kroah-Hartman struct hvsi_struct *hp; 118548079804SLaurent Dufour const __be32 *vtermno, *irq; 1186728674a7SGreg Kroah-Hartman 1187728674a7SGreg Kroah-Hartman vtermno = of_get_property(vty, "reg", NULL); 1188728674a7SGreg Kroah-Hartman irq = of_get_property(vty, "interrupts", NULL); 1189728674a7SGreg Kroah-Hartman if (!vtermno || !irq) 1190728674a7SGreg Kroah-Hartman continue; 1191728674a7SGreg Kroah-Hartman 1192728674a7SGreg Kroah-Hartman if (hvsi_count >= MAX_NR_HVSI_CONSOLES) { 1193728674a7SGreg Kroah-Hartman of_node_put(vty); 1194728674a7SGreg Kroah-Hartman break; 1195728674a7SGreg Kroah-Hartman } 1196728674a7SGreg Kroah-Hartman 1197728674a7SGreg Kroah-Hartman hp = &hvsi_ports[hvsi_count]; 1198728674a7SGreg Kroah-Hartman INIT_DELAYED_WORK(&hp->writer, hvsi_write_worker); 1199728674a7SGreg Kroah-Hartman INIT_WORK(&hp->handshaker, hvsi_handshaker); 1200728674a7SGreg Kroah-Hartman init_waitqueue_head(&hp->emptyq); 1201728674a7SGreg Kroah-Hartman init_waitqueue_head(&hp->stateq); 1202728674a7SGreg Kroah-Hartman spin_lock_init(&hp->lock); 1203d73a4e79SJiri Slaby tty_port_init(&hp->port); 1204728674a7SGreg Kroah-Hartman hp->index = hvsi_count; 1205728674a7SGreg Kroah-Hartman hp->inbuf_end = hp->inbuf; 1206728674a7SGreg Kroah-Hartman hp->state = HVSI_CLOSED; 120748079804SLaurent Dufour hp->vtermno = be32_to_cpup(vtermno); 120848079804SLaurent Dufour hp->virq = irq_create_mapping(NULL, be32_to_cpup(irq)); 1209d4e33facSAlan Cox if (hp->virq == 0) { 1210728674a7SGreg Kroah-Hartman printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", 121148079804SLaurent Dufour __func__, be32_to_cpup(irq)); 1212191c5f10SJiri Slaby tty_port_destroy(&hp->port); 1213728674a7SGreg Kroah-Hartman continue; 1214728674a7SGreg Kroah-Hartman } 1215728674a7SGreg Kroah-Hartman 1216728674a7SGreg Kroah-Hartman hvsi_count++; 1217728674a7SGreg Kroah-Hartman } 1218728674a7SGreg Kroah-Hartman 1219728674a7SGreg Kroah-Hartman if (hvsi_count) 1220728674a7SGreg Kroah-Hartman register_console(&hvsi_console); 1221728674a7SGreg Kroah-Hartman return 0; 1222728674a7SGreg Kroah-Hartman } 1223728674a7SGreg Kroah-Hartman console_initcall(hvsi_console_init); 1224