18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * 3215 line mode terminal driver.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 1999, 2009
68c2ecf20Sopenharmony_ci * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Updated:
98c2ecf20Sopenharmony_ci *  Aug-2000: Added tab support
108c2ecf20Sopenharmony_ci *	      Dan Morrison, IBM Corporation <dmorriso@cse.buffalo.edu>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/types.h>
148c2ecf20Sopenharmony_ci#include <linux/kdev_t.h>
158c2ecf20Sopenharmony_ci#include <linux/tty.h>
168c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
178c2ecf20Sopenharmony_ci#include <linux/vt_kern.h>
188c2ecf20Sopenharmony_ci#include <linux/init.h>
198c2ecf20Sopenharmony_ci#include <linux/console.h>
208c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
218c2ecf20Sopenharmony_ci#include <linux/err.h>
228c2ecf20Sopenharmony_ci#include <linux/reboot.h>
238c2ecf20Sopenharmony_ci#include <linux/serial.h> /* ASYNC_* flags */
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <asm/ccwdev.h>
268c2ecf20Sopenharmony_ci#include <asm/cio.h>
278c2ecf20Sopenharmony_ci#include <asm/io.h>
288c2ecf20Sopenharmony_ci#include <asm/ebcdic.h>
298c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
308c2ecf20Sopenharmony_ci#include <asm/delay.h>
318c2ecf20Sopenharmony_ci#include <asm/cpcmd.h>
328c2ecf20Sopenharmony_ci#include <asm/setup.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include "ctrlchar.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define NR_3215		    1
378c2ecf20Sopenharmony_ci#define NR_3215_REQ	    (4*NR_3215)
388c2ecf20Sopenharmony_ci#define RAW3215_BUFFER_SIZE 65536     /* output buffer size */
398c2ecf20Sopenharmony_ci#define RAW3215_INBUF_SIZE  256	      /* input buffer size */
408c2ecf20Sopenharmony_ci#define RAW3215_MIN_SPACE   128	      /* minimum free space for wakeup */
418c2ecf20Sopenharmony_ci#define RAW3215_MIN_WRITE   1024      /* min. length for immediate output */
428c2ecf20Sopenharmony_ci#define RAW3215_MAX_BYTES   3968      /* max. bytes to write with one ssch */
438c2ecf20Sopenharmony_ci#define RAW3215_MAX_NEWLINE 50	      /* max. lines to write with one ssch */
448c2ecf20Sopenharmony_ci#define RAW3215_NR_CCWS	    3
458c2ecf20Sopenharmony_ci#define RAW3215_TIMEOUT	    HZ/10     /* time for delayed output */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define RAW3215_FIXED	    1	      /* 3215 console device is not be freed */
488c2ecf20Sopenharmony_ci#define RAW3215_WORKING	    4	      /* set if a request is being worked on */
498c2ecf20Sopenharmony_ci#define RAW3215_THROTTLED   8	      /* set if reading is disabled */
508c2ecf20Sopenharmony_ci#define RAW3215_STOPPED	    16	      /* set if writing is disabled */
518c2ecf20Sopenharmony_ci#define RAW3215_TIMER_RUNS  64	      /* set if the output delay timer is on */
528c2ecf20Sopenharmony_ci#define RAW3215_FLUSHING    128	      /* set to flush buffer (no delay) */
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define TAB_STOP_SIZE	    8	      /* tab stop size */
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * Request types for a 3215 device
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_cienum raw3215_type {
608c2ecf20Sopenharmony_ci	RAW3215_FREE, RAW3215_READ, RAW3215_WRITE
618c2ecf20Sopenharmony_ci};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/*
648c2ecf20Sopenharmony_ci * Request structure for a 3215 device
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_cistruct raw3215_req {
678c2ecf20Sopenharmony_ci	enum raw3215_type type;	      /* type of the request */
688c2ecf20Sopenharmony_ci	int start, len;		      /* start index & len in output buffer */
698c2ecf20Sopenharmony_ci	int delayable;		      /* indication to wait for more data */
708c2ecf20Sopenharmony_ci	int residual;		      /* residual count for read request */
718c2ecf20Sopenharmony_ci	struct ccw1 ccws[RAW3215_NR_CCWS]; /* space for the channel program */
728c2ecf20Sopenharmony_ci	struct raw3215_info *info;    /* pointer to main structure */
738c2ecf20Sopenharmony_ci	struct raw3215_req *next;     /* pointer to next request */
748c2ecf20Sopenharmony_ci} __attribute__ ((aligned(8)));
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistruct raw3215_info {
778c2ecf20Sopenharmony_ci	struct tty_port port;
788c2ecf20Sopenharmony_ci	struct ccw_device *cdev;      /* device for tty driver */
798c2ecf20Sopenharmony_ci	spinlock_t *lock;	      /* pointer to irq lock */
808c2ecf20Sopenharmony_ci	int flags;		      /* state flags */
818c2ecf20Sopenharmony_ci	char *buffer;		      /* pointer to output buffer */
828c2ecf20Sopenharmony_ci	char *inbuf;		      /* pointer to input buffer */
838c2ecf20Sopenharmony_ci	int head;		      /* first free byte in output buffer */
848c2ecf20Sopenharmony_ci	int count;		      /* number of bytes in output buffer */
858c2ecf20Sopenharmony_ci	int written;		      /* number of bytes in write requests */
868c2ecf20Sopenharmony_ci	struct raw3215_req *queued_read; /* pointer to queued read requests */
878c2ecf20Sopenharmony_ci	struct raw3215_req *queued_write;/* pointer to queued write requests */
888c2ecf20Sopenharmony_ci	struct tasklet_struct tlet;   /* tasklet to invoke tty_wakeup */
898c2ecf20Sopenharmony_ci	wait_queue_head_t empty_wait; /* wait queue for flushing */
908c2ecf20Sopenharmony_ci	struct timer_list timer;      /* timer for delayed output */
918c2ecf20Sopenharmony_ci	int line_pos;		      /* position on the line (for tabs) */
928c2ecf20Sopenharmony_ci	char ubuffer[80];	      /* copy_from_user buffer */
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/* array of 3215 devices structures */
968c2ecf20Sopenharmony_cistatic struct raw3215_info *raw3215[NR_3215];
978c2ecf20Sopenharmony_ci/* spinlock to protect the raw3215 array */
988c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(raw3215_device_lock);
998c2ecf20Sopenharmony_ci/* list of free request structures */
1008c2ecf20Sopenharmony_cistatic struct raw3215_req *raw3215_freelist;
1018c2ecf20Sopenharmony_ci/* spinlock to protect free list */
1028c2ecf20Sopenharmony_cistatic spinlock_t raw3215_freelist_lock;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic struct tty_driver *tty3215_driver;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/*
1078c2ecf20Sopenharmony_ci * Get a request structure from the free list
1088c2ecf20Sopenharmony_ci */
1098c2ecf20Sopenharmony_cistatic inline struct raw3215_req *raw3215_alloc_req(void)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct raw3215_req *req;
1128c2ecf20Sopenharmony_ci	unsigned long flags;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&raw3215_freelist_lock, flags);
1158c2ecf20Sopenharmony_ci	req = raw3215_freelist;
1168c2ecf20Sopenharmony_ci	raw3215_freelist = req->next;
1178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&raw3215_freelist_lock, flags);
1188c2ecf20Sopenharmony_ci	return req;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/*
1228c2ecf20Sopenharmony_ci * Put a request structure back to the free list
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_cistatic inline void raw3215_free_req(struct raw3215_req *req)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	unsigned long flags;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (req->type == RAW3215_FREE)
1298c2ecf20Sopenharmony_ci		return;		/* don't free a free request */
1308c2ecf20Sopenharmony_ci	req->type = RAW3215_FREE;
1318c2ecf20Sopenharmony_ci	spin_lock_irqsave(&raw3215_freelist_lock, flags);
1328c2ecf20Sopenharmony_ci	req->next = raw3215_freelist;
1338c2ecf20Sopenharmony_ci	raw3215_freelist = req;
1348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&raw3215_freelist_lock, flags);
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci/*
1388c2ecf20Sopenharmony_ci * Set up a read request that reads up to 160 byte from the 3215 device.
1398c2ecf20Sopenharmony_ci * If there is a queued read request it is used, but that shouldn't happen
1408c2ecf20Sopenharmony_ci * because a 3215 terminal won't accept a new read before the old one is
1418c2ecf20Sopenharmony_ci * completed.
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_cistatic void raw3215_mk_read_req(struct raw3215_info *raw)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct raw3215_req *req;
1468c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* there can only be ONE read request at a time */
1498c2ecf20Sopenharmony_ci	req = raw->queued_read;
1508c2ecf20Sopenharmony_ci	if (req == NULL) {
1518c2ecf20Sopenharmony_ci		/* no queued read request, use new req structure */
1528c2ecf20Sopenharmony_ci		req = raw3215_alloc_req();
1538c2ecf20Sopenharmony_ci		req->type = RAW3215_READ;
1548c2ecf20Sopenharmony_ci		req->info = raw;
1558c2ecf20Sopenharmony_ci		raw->queued_read = req;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	ccw = req->ccws;
1598c2ecf20Sopenharmony_ci	ccw->cmd_code = 0x0A; /* read inquiry */
1608c2ecf20Sopenharmony_ci	ccw->flags = 0x20;    /* ignore incorrect length */
1618c2ecf20Sopenharmony_ci	ccw->count = 160;
1628c2ecf20Sopenharmony_ci	ccw->cda = (__u32) __pa(raw->inbuf);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/*
1668c2ecf20Sopenharmony_ci * Set up a write request with the information from the main structure.
1678c2ecf20Sopenharmony_ci * A ccw chain is created that writes as much as possible from the output
1688c2ecf20Sopenharmony_ci * buffer to the 3215 device. If a queued write exists it is replaced by
1698c2ecf20Sopenharmony_ci * the new, probably lengthened request.
1708c2ecf20Sopenharmony_ci */
1718c2ecf20Sopenharmony_cistatic void raw3215_mk_write_req(struct raw3215_info *raw)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct raw3215_req *req;
1748c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
1758c2ecf20Sopenharmony_ci	int len, count, ix, lines;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (raw->count <= raw->written)
1788c2ecf20Sopenharmony_ci		return;
1798c2ecf20Sopenharmony_ci	/* check if there is a queued write request */
1808c2ecf20Sopenharmony_ci	req = raw->queued_write;
1818c2ecf20Sopenharmony_ci	if (req == NULL) {
1828c2ecf20Sopenharmony_ci		/* no queued write request, use new req structure */
1838c2ecf20Sopenharmony_ci		req = raw3215_alloc_req();
1848c2ecf20Sopenharmony_ci		req->type = RAW3215_WRITE;
1858c2ecf20Sopenharmony_ci		req->info = raw;
1868c2ecf20Sopenharmony_ci		raw->queued_write = req;
1878c2ecf20Sopenharmony_ci	} else {
1888c2ecf20Sopenharmony_ci		raw->written -= req->len;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	ccw = req->ccws;
1928c2ecf20Sopenharmony_ci	req->start = (raw->head - raw->count + raw->written) &
1938c2ecf20Sopenharmony_ci		     (RAW3215_BUFFER_SIZE - 1);
1948c2ecf20Sopenharmony_ci	/*
1958c2ecf20Sopenharmony_ci	 * now we have to count newlines. We can at max accept
1968c2ecf20Sopenharmony_ci	 * RAW3215_MAX_NEWLINE newlines in a single ssch due to
1978c2ecf20Sopenharmony_ci	 * a restriction in VM
1988c2ecf20Sopenharmony_ci	 */
1998c2ecf20Sopenharmony_ci	lines = 0;
2008c2ecf20Sopenharmony_ci	ix = req->start;
2018c2ecf20Sopenharmony_ci	while (lines < RAW3215_MAX_NEWLINE && ix != raw->head) {
2028c2ecf20Sopenharmony_ci		if (raw->buffer[ix] == 0x15)
2038c2ecf20Sopenharmony_ci			lines++;
2048c2ecf20Sopenharmony_ci		ix = (ix + 1) & (RAW3215_BUFFER_SIZE - 1);
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci	len = ((ix - 1 - req->start) & (RAW3215_BUFFER_SIZE - 1)) + 1;
2078c2ecf20Sopenharmony_ci	if (len > RAW3215_MAX_BYTES)
2088c2ecf20Sopenharmony_ci		len = RAW3215_MAX_BYTES;
2098c2ecf20Sopenharmony_ci	req->len = len;
2108c2ecf20Sopenharmony_ci	raw->written += len;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* set the indication if we should try to enlarge this request */
2138c2ecf20Sopenharmony_ci	req->delayable = (ix == raw->head) && (len < RAW3215_MIN_WRITE);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	ix = req->start;
2168c2ecf20Sopenharmony_ci	while (len > 0) {
2178c2ecf20Sopenharmony_ci		if (ccw > req->ccws)
2188c2ecf20Sopenharmony_ci			ccw[-1].flags |= 0x40; /* use command chaining */
2198c2ecf20Sopenharmony_ci		ccw->cmd_code = 0x01; /* write, auto carrier return */
2208c2ecf20Sopenharmony_ci		ccw->flags = 0x20;    /* ignore incorrect length ind.  */
2218c2ecf20Sopenharmony_ci		ccw->cda =
2228c2ecf20Sopenharmony_ci			(__u32) __pa(raw->buffer + ix);
2238c2ecf20Sopenharmony_ci		count = len;
2248c2ecf20Sopenharmony_ci		if (ix + count > RAW3215_BUFFER_SIZE)
2258c2ecf20Sopenharmony_ci			count = RAW3215_BUFFER_SIZE - ix;
2268c2ecf20Sopenharmony_ci		ccw->count = count;
2278c2ecf20Sopenharmony_ci		len -= count;
2288c2ecf20Sopenharmony_ci		ix = (ix + count) & (RAW3215_BUFFER_SIZE - 1);
2298c2ecf20Sopenharmony_ci		ccw++;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci	/*
2328c2ecf20Sopenharmony_ci	 * Add a NOP to the channel program. 3215 devices are purely
2338c2ecf20Sopenharmony_ci	 * emulated and its much better to avoid the channel end
2348c2ecf20Sopenharmony_ci	 * interrupt in this case.
2358c2ecf20Sopenharmony_ci	 */
2368c2ecf20Sopenharmony_ci	if (ccw > req->ccws)
2378c2ecf20Sopenharmony_ci		ccw[-1].flags |= 0x40; /* use command chaining */
2388c2ecf20Sopenharmony_ci	ccw->cmd_code = 0x03; /* NOP */
2398c2ecf20Sopenharmony_ci	ccw->flags = 0;
2408c2ecf20Sopenharmony_ci	ccw->cda = 0;
2418c2ecf20Sopenharmony_ci	ccw->count = 1;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci/*
2458c2ecf20Sopenharmony_ci * Start a read or a write request
2468c2ecf20Sopenharmony_ci */
2478c2ecf20Sopenharmony_cistatic void raw3215_start_io(struct raw3215_info *raw)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct raw3215_req *req;
2508c2ecf20Sopenharmony_ci	int res;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	req = raw->queued_read;
2538c2ecf20Sopenharmony_ci	if (req != NULL &&
2548c2ecf20Sopenharmony_ci	    !(raw->flags & (RAW3215_WORKING | RAW3215_THROTTLED))) {
2558c2ecf20Sopenharmony_ci		/* dequeue request */
2568c2ecf20Sopenharmony_ci		raw->queued_read = NULL;
2578c2ecf20Sopenharmony_ci		res = ccw_device_start(raw->cdev, req->ccws,
2588c2ecf20Sopenharmony_ci				       (unsigned long) req, 0, 0);
2598c2ecf20Sopenharmony_ci		if (res != 0) {
2608c2ecf20Sopenharmony_ci			/* do_IO failed, put request back to queue */
2618c2ecf20Sopenharmony_ci			raw->queued_read = req;
2628c2ecf20Sopenharmony_ci		} else {
2638c2ecf20Sopenharmony_ci			raw->flags |= RAW3215_WORKING;
2648c2ecf20Sopenharmony_ci		}
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci	req = raw->queued_write;
2678c2ecf20Sopenharmony_ci	if (req != NULL &&
2688c2ecf20Sopenharmony_ci	    !(raw->flags & (RAW3215_WORKING | RAW3215_STOPPED))) {
2698c2ecf20Sopenharmony_ci		/* dequeue request */
2708c2ecf20Sopenharmony_ci		raw->queued_write = NULL;
2718c2ecf20Sopenharmony_ci		res = ccw_device_start(raw->cdev, req->ccws,
2728c2ecf20Sopenharmony_ci				       (unsigned long) req, 0, 0);
2738c2ecf20Sopenharmony_ci		if (res != 0) {
2748c2ecf20Sopenharmony_ci			/* do_IO failed, put request back to queue */
2758c2ecf20Sopenharmony_ci			raw->queued_write = req;
2768c2ecf20Sopenharmony_ci		} else {
2778c2ecf20Sopenharmony_ci			raw->flags |= RAW3215_WORKING;
2788c2ecf20Sopenharmony_ci		}
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci/*
2838c2ecf20Sopenharmony_ci * Function to start a delayed output after RAW3215_TIMEOUT seconds
2848c2ecf20Sopenharmony_ci */
2858c2ecf20Sopenharmony_cistatic void raw3215_timeout(struct timer_list *t)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct raw3215_info *raw = from_timer(raw, t, timer);
2888c2ecf20Sopenharmony_ci	unsigned long flags;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
2918c2ecf20Sopenharmony_ci	raw->flags &= ~RAW3215_TIMER_RUNS;
2928c2ecf20Sopenharmony_ci	if (!tty_port_suspended(&raw->port)) {
2938c2ecf20Sopenharmony_ci		raw3215_mk_write_req(raw);
2948c2ecf20Sopenharmony_ci		raw3215_start_io(raw);
2958c2ecf20Sopenharmony_ci		if ((raw->queued_read || raw->queued_write) &&
2968c2ecf20Sopenharmony_ci		    !(raw->flags & RAW3215_WORKING) &&
2978c2ecf20Sopenharmony_ci		    !(raw->flags & RAW3215_TIMER_RUNS)) {
2988c2ecf20Sopenharmony_ci			raw->timer.expires = RAW3215_TIMEOUT + jiffies;
2998c2ecf20Sopenharmony_ci			add_timer(&raw->timer);
3008c2ecf20Sopenharmony_ci			raw->flags |= RAW3215_TIMER_RUNS;
3018c2ecf20Sopenharmony_ci		}
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci/*
3078c2ecf20Sopenharmony_ci * Function to conditionally start an IO. A read is started immediately,
3088c2ecf20Sopenharmony_ci * a write is only started immediately if the flush flag is on or the
3098c2ecf20Sopenharmony_ci * amount of data is bigger than RAW3215_MIN_WRITE. If a write is not
3108c2ecf20Sopenharmony_ci * done immediately a timer is started with a delay of RAW3215_TIMEOUT.
3118c2ecf20Sopenharmony_ci */
3128c2ecf20Sopenharmony_cistatic inline void raw3215_try_io(struct raw3215_info *raw)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	if (!tty_port_initialized(&raw->port) || tty_port_suspended(&raw->port))
3158c2ecf20Sopenharmony_ci		return;
3168c2ecf20Sopenharmony_ci	if (raw->queued_read != NULL)
3178c2ecf20Sopenharmony_ci		raw3215_start_io(raw);
3188c2ecf20Sopenharmony_ci	else if (raw->queued_write != NULL) {
3198c2ecf20Sopenharmony_ci		if ((raw->queued_write->delayable == 0) ||
3208c2ecf20Sopenharmony_ci		    (raw->flags & RAW3215_FLUSHING)) {
3218c2ecf20Sopenharmony_ci			/* execute write requests bigger than minimum size */
3228c2ecf20Sopenharmony_ci			raw3215_start_io(raw);
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci	if ((raw->queued_read || raw->queued_write) &&
3268c2ecf20Sopenharmony_ci	    !(raw->flags & RAW3215_WORKING) &&
3278c2ecf20Sopenharmony_ci	    !(raw->flags & RAW3215_TIMER_RUNS)) {
3288c2ecf20Sopenharmony_ci		raw->timer.expires = RAW3215_TIMEOUT + jiffies;
3298c2ecf20Sopenharmony_ci		add_timer(&raw->timer);
3308c2ecf20Sopenharmony_ci		raw->flags |= RAW3215_TIMER_RUNS;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci/*
3358c2ecf20Sopenharmony_ci * Call tty_wakeup from tasklet context
3368c2ecf20Sopenharmony_ci */
3378c2ecf20Sopenharmony_cistatic void raw3215_wakeup(unsigned long data)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	struct raw3215_info *raw = (struct raw3215_info *) data;
3408c2ecf20Sopenharmony_ci	struct tty_struct *tty;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	tty = tty_port_tty_get(&raw->port);
3438c2ecf20Sopenharmony_ci	if (tty) {
3448c2ecf20Sopenharmony_ci		tty_wakeup(tty);
3458c2ecf20Sopenharmony_ci		tty_kref_put(tty);
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci/*
3508c2ecf20Sopenharmony_ci * Try to start the next IO and wake up processes waiting on the tty.
3518c2ecf20Sopenharmony_ci */
3528c2ecf20Sopenharmony_cistatic void raw3215_next_io(struct raw3215_info *raw, struct tty_struct *tty)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	raw3215_mk_write_req(raw);
3558c2ecf20Sopenharmony_ci	raw3215_try_io(raw);
3568c2ecf20Sopenharmony_ci	if (tty && RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE)
3578c2ecf20Sopenharmony_ci		tasklet_schedule(&raw->tlet);
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci/*
3618c2ecf20Sopenharmony_ci * Interrupt routine, called from common io layer
3628c2ecf20Sopenharmony_ci */
3638c2ecf20Sopenharmony_cistatic void raw3215_irq(struct ccw_device *cdev, unsigned long intparm,
3648c2ecf20Sopenharmony_ci			struct irb *irb)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
3678c2ecf20Sopenharmony_ci	struct raw3215_req *req;
3688c2ecf20Sopenharmony_ci	struct tty_struct *tty;
3698c2ecf20Sopenharmony_ci	int cstat, dstat;
3708c2ecf20Sopenharmony_ci	int count;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	raw = dev_get_drvdata(&cdev->dev);
3738c2ecf20Sopenharmony_ci	req = (struct raw3215_req *) intparm;
3748c2ecf20Sopenharmony_ci	tty = tty_port_tty_get(&raw->port);
3758c2ecf20Sopenharmony_ci	cstat = irb->scsw.cmd.cstat;
3768c2ecf20Sopenharmony_ci	dstat = irb->scsw.cmd.dstat;
3778c2ecf20Sopenharmony_ci	if (cstat != 0)
3788c2ecf20Sopenharmony_ci		raw3215_next_io(raw, tty);
3798c2ecf20Sopenharmony_ci	if (dstat & 0x01) { /* we got a unit exception */
3808c2ecf20Sopenharmony_ci		dstat &= ~0x01;	 /* we can ignore it */
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci	switch (dstat) {
3838c2ecf20Sopenharmony_ci	case 0x80:
3848c2ecf20Sopenharmony_ci		if (cstat != 0)
3858c2ecf20Sopenharmony_ci			break;
3868c2ecf20Sopenharmony_ci		/* Attention interrupt, someone hit the enter key */
3878c2ecf20Sopenharmony_ci		raw3215_mk_read_req(raw);
3888c2ecf20Sopenharmony_ci		raw3215_next_io(raw, tty);
3898c2ecf20Sopenharmony_ci		break;
3908c2ecf20Sopenharmony_ci	case 0x08:
3918c2ecf20Sopenharmony_ci	case 0x0C:
3928c2ecf20Sopenharmony_ci		/* Channel end interrupt. */
3938c2ecf20Sopenharmony_ci		if ((raw = req->info) == NULL)
3948c2ecf20Sopenharmony_ci			goto put_tty;	     /* That shouldn't happen ... */
3958c2ecf20Sopenharmony_ci		if (req->type == RAW3215_READ) {
3968c2ecf20Sopenharmony_ci			/* store residual count, then wait for device end */
3978c2ecf20Sopenharmony_ci			req->residual = irb->scsw.cmd.count;
3988c2ecf20Sopenharmony_ci		}
3998c2ecf20Sopenharmony_ci		if (dstat == 0x08)
4008c2ecf20Sopenharmony_ci			break;
4018c2ecf20Sopenharmony_ci		fallthrough;
4028c2ecf20Sopenharmony_ci	case 0x04:
4038c2ecf20Sopenharmony_ci		/* Device end interrupt. */
4048c2ecf20Sopenharmony_ci		if ((raw = req->info) == NULL)
4058c2ecf20Sopenharmony_ci			goto put_tty;	     /* That shouldn't happen ... */
4068c2ecf20Sopenharmony_ci		if (req->type == RAW3215_READ && tty != NULL) {
4078c2ecf20Sopenharmony_ci			unsigned int cchar;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci			count = 160 - req->residual;
4108c2ecf20Sopenharmony_ci			EBCASC(raw->inbuf, count);
4118c2ecf20Sopenharmony_ci			cchar = ctrlchar_handle(raw->inbuf, count, tty);
4128c2ecf20Sopenharmony_ci			switch (cchar & CTRLCHAR_MASK) {
4138c2ecf20Sopenharmony_ci			case CTRLCHAR_SYSRQ:
4148c2ecf20Sopenharmony_ci				break;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci			case CTRLCHAR_CTRL:
4178c2ecf20Sopenharmony_ci				tty_insert_flip_char(&raw->port, cchar,
4188c2ecf20Sopenharmony_ci						TTY_NORMAL);
4198c2ecf20Sopenharmony_ci				tty_flip_buffer_push(&raw->port);
4208c2ecf20Sopenharmony_ci				break;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci			case CTRLCHAR_NONE:
4238c2ecf20Sopenharmony_ci				if (count < 2 ||
4248c2ecf20Sopenharmony_ci				    (strncmp(raw->inbuf+count-2, "\252n", 2) &&
4258c2ecf20Sopenharmony_ci				     strncmp(raw->inbuf+count-2, "^n", 2)) ) {
4268c2ecf20Sopenharmony_ci					/* add the auto \n */
4278c2ecf20Sopenharmony_ci					raw->inbuf[count] = '\n';
4288c2ecf20Sopenharmony_ci					count++;
4298c2ecf20Sopenharmony_ci				} else
4308c2ecf20Sopenharmony_ci					count -= 2;
4318c2ecf20Sopenharmony_ci				tty_insert_flip_string(&raw->port, raw->inbuf,
4328c2ecf20Sopenharmony_ci						count);
4338c2ecf20Sopenharmony_ci				tty_flip_buffer_push(&raw->port);
4348c2ecf20Sopenharmony_ci				break;
4358c2ecf20Sopenharmony_ci			}
4368c2ecf20Sopenharmony_ci		} else if (req->type == RAW3215_WRITE) {
4378c2ecf20Sopenharmony_ci			raw->count -= req->len;
4388c2ecf20Sopenharmony_ci			raw->written -= req->len;
4398c2ecf20Sopenharmony_ci		}
4408c2ecf20Sopenharmony_ci		raw->flags &= ~RAW3215_WORKING;
4418c2ecf20Sopenharmony_ci		raw3215_free_req(req);
4428c2ecf20Sopenharmony_ci		/* check for empty wait */
4438c2ecf20Sopenharmony_ci		if (waitqueue_active(&raw->empty_wait) &&
4448c2ecf20Sopenharmony_ci		    raw->queued_write == NULL &&
4458c2ecf20Sopenharmony_ci		    raw->queued_read == NULL) {
4468c2ecf20Sopenharmony_ci			wake_up_interruptible(&raw->empty_wait);
4478c2ecf20Sopenharmony_ci		}
4488c2ecf20Sopenharmony_ci		raw3215_next_io(raw, tty);
4498c2ecf20Sopenharmony_ci		break;
4508c2ecf20Sopenharmony_ci	default:
4518c2ecf20Sopenharmony_ci		/* Strange interrupt, I'll do my best to clean up */
4528c2ecf20Sopenharmony_ci		if (req != NULL && req->type != RAW3215_FREE) {
4538c2ecf20Sopenharmony_ci			if (req->type == RAW3215_WRITE) {
4548c2ecf20Sopenharmony_ci				raw->count -= req->len;
4558c2ecf20Sopenharmony_ci				raw->written -= req->len;
4568c2ecf20Sopenharmony_ci			}
4578c2ecf20Sopenharmony_ci			raw->flags &= ~RAW3215_WORKING;
4588c2ecf20Sopenharmony_ci			raw3215_free_req(req);
4598c2ecf20Sopenharmony_ci		}
4608c2ecf20Sopenharmony_ci		raw3215_next_io(raw, tty);
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ciput_tty:
4638c2ecf20Sopenharmony_ci	tty_kref_put(tty);
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci/*
4678c2ecf20Sopenharmony_ci * Drop the oldest line from the output buffer.
4688c2ecf20Sopenharmony_ci */
4698c2ecf20Sopenharmony_cistatic void raw3215_drop_line(struct raw3215_info *raw)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	int ix;
4728c2ecf20Sopenharmony_ci	char ch;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	BUG_ON(raw->written != 0);
4758c2ecf20Sopenharmony_ci	ix = (raw->head - raw->count) & (RAW3215_BUFFER_SIZE - 1);
4768c2ecf20Sopenharmony_ci	while (raw->count > 0) {
4778c2ecf20Sopenharmony_ci		ch = raw->buffer[ix];
4788c2ecf20Sopenharmony_ci		ix = (ix + 1) & (RAW3215_BUFFER_SIZE - 1);
4798c2ecf20Sopenharmony_ci		raw->count--;
4808c2ecf20Sopenharmony_ci		if (ch == 0x15)
4818c2ecf20Sopenharmony_ci			break;
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci	raw->head = ix;
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci/*
4878c2ecf20Sopenharmony_ci * Wait until length bytes are available int the output buffer.
4888c2ecf20Sopenharmony_ci * Has to be called with the s390irq lock held. Can be called
4898c2ecf20Sopenharmony_ci * disabled.
4908c2ecf20Sopenharmony_ci */
4918c2ecf20Sopenharmony_cistatic void raw3215_make_room(struct raw3215_info *raw, unsigned int length)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	while (RAW3215_BUFFER_SIZE - raw->count < length) {
4948c2ecf20Sopenharmony_ci		/* While console is frozen for suspend we have no other
4958c2ecf20Sopenharmony_ci		 * choice but to drop message from the buffer to make
4968c2ecf20Sopenharmony_ci		 * room for even more messages. */
4978c2ecf20Sopenharmony_ci		if (tty_port_suspended(&raw->port)) {
4988c2ecf20Sopenharmony_ci			raw3215_drop_line(raw);
4998c2ecf20Sopenharmony_ci			continue;
5008c2ecf20Sopenharmony_ci		}
5018c2ecf20Sopenharmony_ci		/* there might be a request pending */
5028c2ecf20Sopenharmony_ci		raw->flags |= RAW3215_FLUSHING;
5038c2ecf20Sopenharmony_ci		raw3215_mk_write_req(raw);
5048c2ecf20Sopenharmony_ci		raw3215_try_io(raw);
5058c2ecf20Sopenharmony_ci		raw->flags &= ~RAW3215_FLUSHING;
5068c2ecf20Sopenharmony_ci#ifdef CONFIG_TN3215_CONSOLE
5078c2ecf20Sopenharmony_ci		ccw_device_wait_idle(raw->cdev);
5088c2ecf20Sopenharmony_ci#endif
5098c2ecf20Sopenharmony_ci		/* Enough room freed up ? */
5108c2ecf20Sopenharmony_ci		if (RAW3215_BUFFER_SIZE - raw->count >= length)
5118c2ecf20Sopenharmony_ci			break;
5128c2ecf20Sopenharmony_ci		/* there might be another cpu waiting for the lock */
5138c2ecf20Sopenharmony_ci		spin_unlock(get_ccwdev_lock(raw->cdev));
5148c2ecf20Sopenharmony_ci		udelay(100);
5158c2ecf20Sopenharmony_ci		spin_lock(get_ccwdev_lock(raw->cdev));
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci/*
5208c2ecf20Sopenharmony_ci * String write routine for 3215 devices
5218c2ecf20Sopenharmony_ci */
5228c2ecf20Sopenharmony_cistatic void raw3215_write(struct raw3215_info *raw, const char *str,
5238c2ecf20Sopenharmony_ci			  unsigned int length)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	unsigned long flags;
5268c2ecf20Sopenharmony_ci	int c, count;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	while (length > 0) {
5298c2ecf20Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
5308c2ecf20Sopenharmony_ci		count = (length > RAW3215_BUFFER_SIZE) ?
5318c2ecf20Sopenharmony_ci					     RAW3215_BUFFER_SIZE : length;
5328c2ecf20Sopenharmony_ci		length -= count;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci		raw3215_make_room(raw, count);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci		/* copy string to output buffer and convert it to EBCDIC */
5378c2ecf20Sopenharmony_ci		while (1) {
5388c2ecf20Sopenharmony_ci			c = min_t(int, count,
5398c2ecf20Sopenharmony_ci				  min(RAW3215_BUFFER_SIZE - raw->count,
5408c2ecf20Sopenharmony_ci				      RAW3215_BUFFER_SIZE - raw->head));
5418c2ecf20Sopenharmony_ci			if (c <= 0)
5428c2ecf20Sopenharmony_ci				break;
5438c2ecf20Sopenharmony_ci			memcpy(raw->buffer + raw->head, str, c);
5448c2ecf20Sopenharmony_ci			ASCEBC(raw->buffer + raw->head, c);
5458c2ecf20Sopenharmony_ci			raw->head = (raw->head + c) & (RAW3215_BUFFER_SIZE - 1);
5468c2ecf20Sopenharmony_ci			raw->count += c;
5478c2ecf20Sopenharmony_ci			raw->line_pos += c;
5488c2ecf20Sopenharmony_ci			str += c;
5498c2ecf20Sopenharmony_ci			count -= c;
5508c2ecf20Sopenharmony_ci		}
5518c2ecf20Sopenharmony_ci		if (!(raw->flags & RAW3215_WORKING)) {
5528c2ecf20Sopenharmony_ci			raw3215_mk_write_req(raw);
5538c2ecf20Sopenharmony_ci			/* start or queue request */
5548c2ecf20Sopenharmony_ci			raw3215_try_io(raw);
5558c2ecf20Sopenharmony_ci		}
5568c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci/*
5618c2ecf20Sopenharmony_ci * Put character routine for 3215 devices
5628c2ecf20Sopenharmony_ci */
5638c2ecf20Sopenharmony_cistatic void raw3215_putchar(struct raw3215_info *raw, unsigned char ch)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	unsigned long flags;
5668c2ecf20Sopenharmony_ci	unsigned int length, i;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
5698c2ecf20Sopenharmony_ci	if (ch == '\t') {
5708c2ecf20Sopenharmony_ci		length = TAB_STOP_SIZE - (raw->line_pos%TAB_STOP_SIZE);
5718c2ecf20Sopenharmony_ci		raw->line_pos += length;
5728c2ecf20Sopenharmony_ci		ch = ' ';
5738c2ecf20Sopenharmony_ci	} else if (ch == '\n') {
5748c2ecf20Sopenharmony_ci		length = 1;
5758c2ecf20Sopenharmony_ci		raw->line_pos = 0;
5768c2ecf20Sopenharmony_ci	} else {
5778c2ecf20Sopenharmony_ci		length = 1;
5788c2ecf20Sopenharmony_ci		raw->line_pos++;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci	raw3215_make_room(raw, length);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	for (i = 0; i < length; i++) {
5838c2ecf20Sopenharmony_ci		raw->buffer[raw->head] = (char) _ascebc[(int) ch];
5848c2ecf20Sopenharmony_ci		raw->head = (raw->head + 1) & (RAW3215_BUFFER_SIZE - 1);
5858c2ecf20Sopenharmony_ci		raw->count++;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci	if (!(raw->flags & RAW3215_WORKING)) {
5888c2ecf20Sopenharmony_ci		raw3215_mk_write_req(raw);
5898c2ecf20Sopenharmony_ci		/* start or queue request */
5908c2ecf20Sopenharmony_ci		raw3215_try_io(raw);
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci/*
5968c2ecf20Sopenharmony_ci * Flush routine, it simply sets the flush flag and tries to start
5978c2ecf20Sopenharmony_ci * pending IO.
5988c2ecf20Sopenharmony_ci */
5998c2ecf20Sopenharmony_cistatic void raw3215_flush_buffer(struct raw3215_info *raw)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	unsigned long flags;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
6048c2ecf20Sopenharmony_ci	if (raw->count > 0) {
6058c2ecf20Sopenharmony_ci		raw->flags |= RAW3215_FLUSHING;
6068c2ecf20Sopenharmony_ci		raw3215_try_io(raw);
6078c2ecf20Sopenharmony_ci		raw->flags &= ~RAW3215_FLUSHING;
6088c2ecf20Sopenharmony_ci	}
6098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci/*
6138c2ecf20Sopenharmony_ci * Fire up a 3215 device.
6148c2ecf20Sopenharmony_ci */
6158c2ecf20Sopenharmony_cistatic int raw3215_startup(struct raw3215_info *raw)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	unsigned long flags;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	if (tty_port_initialized(&raw->port))
6208c2ecf20Sopenharmony_ci		return 0;
6218c2ecf20Sopenharmony_ci	raw->line_pos = 0;
6228c2ecf20Sopenharmony_ci	tty_port_set_initialized(&raw->port, 1);
6238c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
6248c2ecf20Sopenharmony_ci	raw3215_try_io(raw);
6258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	return 0;
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci/*
6318c2ecf20Sopenharmony_ci * Shutdown a 3215 device.
6328c2ecf20Sopenharmony_ci */
6338c2ecf20Sopenharmony_cistatic void raw3215_shutdown(struct raw3215_info *raw)
6348c2ecf20Sopenharmony_ci{
6358c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
6368c2ecf20Sopenharmony_ci	unsigned long flags;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	if (!tty_port_initialized(&raw->port) || (raw->flags & RAW3215_FIXED))
6398c2ecf20Sopenharmony_ci		return;
6408c2ecf20Sopenharmony_ci	/* Wait for outstanding requests, then free irq */
6418c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
6428c2ecf20Sopenharmony_ci	if ((raw->flags & RAW3215_WORKING) ||
6438c2ecf20Sopenharmony_ci	    raw->queued_write != NULL ||
6448c2ecf20Sopenharmony_ci	    raw->queued_read != NULL) {
6458c2ecf20Sopenharmony_ci		add_wait_queue(&raw->empty_wait, &wait);
6468c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
6478c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
6488c2ecf20Sopenharmony_ci		schedule();
6498c2ecf20Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
6508c2ecf20Sopenharmony_ci		remove_wait_queue(&raw->empty_wait, &wait);
6518c2ecf20Sopenharmony_ci		set_current_state(TASK_RUNNING);
6528c2ecf20Sopenharmony_ci		tty_port_set_initialized(&raw->port, 1);
6538c2ecf20Sopenharmony_ci	}
6548c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cistatic struct raw3215_info *raw3215_alloc_info(void)
6588c2ecf20Sopenharmony_ci{
6598c2ecf20Sopenharmony_ci	struct raw3215_info *info;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	info = kzalloc(sizeof(struct raw3215_info), GFP_KERNEL | GFP_DMA);
6628c2ecf20Sopenharmony_ci	if (!info)
6638c2ecf20Sopenharmony_ci		return NULL;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	info->buffer = kzalloc(RAW3215_BUFFER_SIZE, GFP_KERNEL | GFP_DMA);
6668c2ecf20Sopenharmony_ci	info->inbuf = kzalloc(RAW3215_INBUF_SIZE, GFP_KERNEL | GFP_DMA);
6678c2ecf20Sopenharmony_ci	if (!info->buffer || !info->inbuf) {
6688c2ecf20Sopenharmony_ci		kfree(info->inbuf);
6698c2ecf20Sopenharmony_ci		kfree(info->buffer);
6708c2ecf20Sopenharmony_ci		kfree(info);
6718c2ecf20Sopenharmony_ci		return NULL;
6728c2ecf20Sopenharmony_ci	}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	timer_setup(&info->timer, raw3215_timeout, 0);
6758c2ecf20Sopenharmony_ci	init_waitqueue_head(&info->empty_wait);
6768c2ecf20Sopenharmony_ci	tasklet_init(&info->tlet, raw3215_wakeup, (unsigned long)info);
6778c2ecf20Sopenharmony_ci	tty_port_init(&info->port);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	return info;
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_cistatic void raw3215_free_info(struct raw3215_info *raw)
6838c2ecf20Sopenharmony_ci{
6848c2ecf20Sopenharmony_ci	kfree(raw->inbuf);
6858c2ecf20Sopenharmony_ci	kfree(raw->buffer);
6868c2ecf20Sopenharmony_ci	tty_port_destroy(&raw->port);
6878c2ecf20Sopenharmony_ci	kfree(raw);
6888c2ecf20Sopenharmony_ci}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_cistatic int raw3215_probe (struct ccw_device *cdev)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
6938c2ecf20Sopenharmony_ci	int line;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	/* Console is special. */
6968c2ecf20Sopenharmony_ci	if (raw3215[0] && (raw3215[0] == dev_get_drvdata(&cdev->dev)))
6978c2ecf20Sopenharmony_ci		return 0;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	raw = raw3215_alloc_info();
7008c2ecf20Sopenharmony_ci	if (raw == NULL)
7018c2ecf20Sopenharmony_ci		return -ENOMEM;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	raw->cdev = cdev;
7048c2ecf20Sopenharmony_ci	dev_set_drvdata(&cdev->dev, raw);
7058c2ecf20Sopenharmony_ci	cdev->handler = raw3215_irq;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	spin_lock(&raw3215_device_lock);
7088c2ecf20Sopenharmony_ci	for (line = 0; line < NR_3215; line++) {
7098c2ecf20Sopenharmony_ci		if (!raw3215[line]) {
7108c2ecf20Sopenharmony_ci			raw3215[line] = raw;
7118c2ecf20Sopenharmony_ci			break;
7128c2ecf20Sopenharmony_ci		}
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci	spin_unlock(&raw3215_device_lock);
7158c2ecf20Sopenharmony_ci	if (line == NR_3215) {
7168c2ecf20Sopenharmony_ci		raw3215_free_info(raw);
7178c2ecf20Sopenharmony_ci		return -ENODEV;
7188c2ecf20Sopenharmony_ci	}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	return 0;
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_cistatic void raw3215_remove (struct ccw_device *cdev)
7248c2ecf20Sopenharmony_ci{
7258c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
7268c2ecf20Sopenharmony_ci	unsigned int line;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	ccw_device_set_offline(cdev);
7298c2ecf20Sopenharmony_ci	raw = dev_get_drvdata(&cdev->dev);
7308c2ecf20Sopenharmony_ci	if (raw) {
7318c2ecf20Sopenharmony_ci		spin_lock(&raw3215_device_lock);
7328c2ecf20Sopenharmony_ci		for (line = 0; line < NR_3215; line++)
7338c2ecf20Sopenharmony_ci			if (raw3215[line] == raw)
7348c2ecf20Sopenharmony_ci				break;
7358c2ecf20Sopenharmony_ci		raw3215[line] = NULL;
7368c2ecf20Sopenharmony_ci		spin_unlock(&raw3215_device_lock);
7378c2ecf20Sopenharmony_ci		dev_set_drvdata(&cdev->dev, NULL);
7388c2ecf20Sopenharmony_ci		raw3215_free_info(raw);
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_cistatic int raw3215_set_online (struct ccw_device *cdev)
7438c2ecf20Sopenharmony_ci{
7448c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	raw = dev_get_drvdata(&cdev->dev);
7478c2ecf20Sopenharmony_ci	if (!raw)
7488c2ecf20Sopenharmony_ci		return -ENODEV;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	return raw3215_startup(raw);
7518c2ecf20Sopenharmony_ci}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_cistatic int raw3215_set_offline (struct ccw_device *cdev)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	raw = dev_get_drvdata(&cdev->dev);
7588c2ecf20Sopenharmony_ci	if (!raw)
7598c2ecf20Sopenharmony_ci		return -ENODEV;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	raw3215_shutdown(raw);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	return 0;
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_cistatic int raw3215_pm_stop(struct ccw_device *cdev)
7678c2ecf20Sopenharmony_ci{
7688c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
7698c2ecf20Sopenharmony_ci	unsigned long flags;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	/* Empty the output buffer, then prevent new I/O. */
7728c2ecf20Sopenharmony_ci	raw = dev_get_drvdata(&cdev->dev);
7738c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
7748c2ecf20Sopenharmony_ci	raw3215_make_room(raw, RAW3215_BUFFER_SIZE);
7758c2ecf20Sopenharmony_ci	tty_port_set_suspended(&raw->port, 1);
7768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
7778c2ecf20Sopenharmony_ci	return 0;
7788c2ecf20Sopenharmony_ci}
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_cistatic int raw3215_pm_start(struct ccw_device *cdev)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
7838c2ecf20Sopenharmony_ci	unsigned long flags;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	/* Allow I/O again and flush output buffer. */
7868c2ecf20Sopenharmony_ci	raw = dev_get_drvdata(&cdev->dev);
7878c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
7888c2ecf20Sopenharmony_ci	tty_port_set_suspended(&raw->port, 0);
7898c2ecf20Sopenharmony_ci	raw->flags |= RAW3215_FLUSHING;
7908c2ecf20Sopenharmony_ci	raw3215_try_io(raw);
7918c2ecf20Sopenharmony_ci	raw->flags &= ~RAW3215_FLUSHING;
7928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
7938c2ecf20Sopenharmony_ci	return 0;
7948c2ecf20Sopenharmony_ci}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_cistatic struct ccw_device_id raw3215_id[] = {
7978c2ecf20Sopenharmony_ci	{ CCW_DEVICE(0x3215, 0) },
7988c2ecf20Sopenharmony_ci	{ /* end of list */ },
7998c2ecf20Sopenharmony_ci};
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_cistatic struct ccw_driver raw3215_ccw_driver = {
8028c2ecf20Sopenharmony_ci	.driver = {
8038c2ecf20Sopenharmony_ci		.name	= "3215",
8048c2ecf20Sopenharmony_ci		.owner	= THIS_MODULE,
8058c2ecf20Sopenharmony_ci	},
8068c2ecf20Sopenharmony_ci	.ids		= raw3215_id,
8078c2ecf20Sopenharmony_ci	.probe		= &raw3215_probe,
8088c2ecf20Sopenharmony_ci	.remove		= &raw3215_remove,
8098c2ecf20Sopenharmony_ci	.set_online	= &raw3215_set_online,
8108c2ecf20Sopenharmony_ci	.set_offline	= &raw3215_set_offline,
8118c2ecf20Sopenharmony_ci	.freeze		= &raw3215_pm_stop,
8128c2ecf20Sopenharmony_ci	.thaw		= &raw3215_pm_start,
8138c2ecf20Sopenharmony_ci	.restore	= &raw3215_pm_start,
8148c2ecf20Sopenharmony_ci	.int_class	= IRQIO_C15,
8158c2ecf20Sopenharmony_ci};
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci#ifdef CONFIG_TN3215_CONSOLE
8188c2ecf20Sopenharmony_ci/*
8198c2ecf20Sopenharmony_ci * Write a string to the 3215 console
8208c2ecf20Sopenharmony_ci */
8218c2ecf20Sopenharmony_cistatic void con3215_write(struct console *co, const char *str,
8228c2ecf20Sopenharmony_ci			  unsigned int count)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
8258c2ecf20Sopenharmony_ci	int i;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	if (count <= 0)
8288c2ecf20Sopenharmony_ci		return;
8298c2ecf20Sopenharmony_ci	raw = raw3215[0];	/* console 3215 is the first one */
8308c2ecf20Sopenharmony_ci	while (count > 0) {
8318c2ecf20Sopenharmony_ci		for (i = 0; i < count; i++)
8328c2ecf20Sopenharmony_ci			if (str[i] == '\t' || str[i] == '\n')
8338c2ecf20Sopenharmony_ci				break;
8348c2ecf20Sopenharmony_ci		raw3215_write(raw, str, i);
8358c2ecf20Sopenharmony_ci		count -= i;
8368c2ecf20Sopenharmony_ci		str += i;
8378c2ecf20Sopenharmony_ci		if (count > 0) {
8388c2ecf20Sopenharmony_ci			raw3215_putchar(raw, *str);
8398c2ecf20Sopenharmony_ci			count--;
8408c2ecf20Sopenharmony_ci			str++;
8418c2ecf20Sopenharmony_ci		}
8428c2ecf20Sopenharmony_ci	}
8438c2ecf20Sopenharmony_ci}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_cistatic struct tty_driver *con3215_device(struct console *c, int *index)
8468c2ecf20Sopenharmony_ci{
8478c2ecf20Sopenharmony_ci	*index = c->index;
8488c2ecf20Sopenharmony_ci	return tty3215_driver;
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci/*
8528c2ecf20Sopenharmony_ci * panic() calls con3215_flush through a panic_notifier
8538c2ecf20Sopenharmony_ci * before the system enters a disabled, endless loop.
8548c2ecf20Sopenharmony_ci */
8558c2ecf20Sopenharmony_cistatic void con3215_flush(void)
8568c2ecf20Sopenharmony_ci{
8578c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
8588c2ecf20Sopenharmony_ci	unsigned long flags;
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	raw = raw3215[0];  /* console 3215 is the first one */
8618c2ecf20Sopenharmony_ci	if (tty_port_suspended(&raw->port))
8628c2ecf20Sopenharmony_ci		/* The console is still frozen for suspend. */
8638c2ecf20Sopenharmony_ci		if (ccw_device_force_console(raw->cdev))
8648c2ecf20Sopenharmony_ci			/* Forcing didn't work, no panic message .. */
8658c2ecf20Sopenharmony_ci			return;
8668c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
8678c2ecf20Sopenharmony_ci	raw3215_make_room(raw, RAW3215_BUFFER_SIZE);
8688c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
8698c2ecf20Sopenharmony_ci}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_cistatic int con3215_notify(struct notifier_block *self,
8728c2ecf20Sopenharmony_ci			  unsigned long event, void *data)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci	con3215_flush();
8758c2ecf20Sopenharmony_ci	return NOTIFY_OK;
8768c2ecf20Sopenharmony_ci}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_cistatic struct notifier_block on_panic_nb = {
8798c2ecf20Sopenharmony_ci	.notifier_call = con3215_notify,
8808c2ecf20Sopenharmony_ci	.priority = 0,
8818c2ecf20Sopenharmony_ci};
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_cistatic struct notifier_block on_reboot_nb = {
8848c2ecf20Sopenharmony_ci	.notifier_call = con3215_notify,
8858c2ecf20Sopenharmony_ci	.priority = 0,
8868c2ecf20Sopenharmony_ci};
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci/*
8898c2ecf20Sopenharmony_ci *  The console structure for the 3215 console
8908c2ecf20Sopenharmony_ci */
8918c2ecf20Sopenharmony_cistatic struct console con3215 = {
8928c2ecf20Sopenharmony_ci	.name	 = "ttyS",
8938c2ecf20Sopenharmony_ci	.write	 = con3215_write,
8948c2ecf20Sopenharmony_ci	.device	 = con3215_device,
8958c2ecf20Sopenharmony_ci	.flags	 = CON_PRINTBUFFER,
8968c2ecf20Sopenharmony_ci};
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci/*
8998c2ecf20Sopenharmony_ci * 3215 console initialization code called from console_init().
9008c2ecf20Sopenharmony_ci */
9018c2ecf20Sopenharmony_cistatic int __init con3215_init(void)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
9048c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
9058c2ecf20Sopenharmony_ci	struct raw3215_req *req;
9068c2ecf20Sopenharmony_ci	int i;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	/* Check if 3215 is to be the console */
9098c2ecf20Sopenharmony_ci	if (!CONSOLE_IS_3215)
9108c2ecf20Sopenharmony_ci		return -ENODEV;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	/* Set the console mode for VM */
9138c2ecf20Sopenharmony_ci	if (MACHINE_IS_VM) {
9148c2ecf20Sopenharmony_ci		cpcmd("TERM CONMODE 3215", NULL, 0, NULL);
9158c2ecf20Sopenharmony_ci		cpcmd("TERM AUTOCR OFF", NULL, 0, NULL);
9168c2ecf20Sopenharmony_ci	}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	/* allocate 3215 request structures */
9198c2ecf20Sopenharmony_ci	raw3215_freelist = NULL;
9208c2ecf20Sopenharmony_ci	spin_lock_init(&raw3215_freelist_lock);
9218c2ecf20Sopenharmony_ci	for (i = 0; i < NR_3215_REQ; i++) {
9228c2ecf20Sopenharmony_ci		req = kzalloc(sizeof(struct raw3215_req), GFP_KERNEL | GFP_DMA);
9238c2ecf20Sopenharmony_ci		if (!req)
9248c2ecf20Sopenharmony_ci			return -ENOMEM;
9258c2ecf20Sopenharmony_ci		req->next = raw3215_freelist;
9268c2ecf20Sopenharmony_ci		raw3215_freelist = req;
9278c2ecf20Sopenharmony_ci	}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	cdev = ccw_device_create_console(&raw3215_ccw_driver);
9308c2ecf20Sopenharmony_ci	if (IS_ERR(cdev))
9318c2ecf20Sopenharmony_ci		return -ENODEV;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	raw3215[0] = raw = raw3215_alloc_info();
9348c2ecf20Sopenharmony_ci	raw->cdev = cdev;
9358c2ecf20Sopenharmony_ci	dev_set_drvdata(&cdev->dev, raw);
9368c2ecf20Sopenharmony_ci	cdev->handler = raw3215_irq;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	raw->flags |= RAW3215_FIXED;
9398c2ecf20Sopenharmony_ci	if (ccw_device_enable_console(cdev)) {
9408c2ecf20Sopenharmony_ci		ccw_device_destroy_console(cdev);
9418c2ecf20Sopenharmony_ci		raw3215_free_info(raw);
9428c2ecf20Sopenharmony_ci		raw3215[0] = NULL;
9438c2ecf20Sopenharmony_ci		return -ENODEV;
9448c2ecf20Sopenharmony_ci	}
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	/* Request the console irq */
9478c2ecf20Sopenharmony_ci	if (raw3215_startup(raw) != 0) {
9488c2ecf20Sopenharmony_ci		raw3215_free_info(raw);
9498c2ecf20Sopenharmony_ci		raw3215[0] = NULL;
9508c2ecf20Sopenharmony_ci		return -ENODEV;
9518c2ecf20Sopenharmony_ci	}
9528c2ecf20Sopenharmony_ci	atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
9538c2ecf20Sopenharmony_ci	register_reboot_notifier(&on_reboot_nb);
9548c2ecf20Sopenharmony_ci	register_console(&con3215);
9558c2ecf20Sopenharmony_ci	return 0;
9568c2ecf20Sopenharmony_ci}
9578c2ecf20Sopenharmony_ciconsole_initcall(con3215_init);
9588c2ecf20Sopenharmony_ci#endif
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_cistatic int tty3215_install(struct tty_driver *driver, struct tty_struct *tty)
9618c2ecf20Sopenharmony_ci{
9628c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	raw = raw3215[tty->index];
9658c2ecf20Sopenharmony_ci	if (raw == NULL)
9668c2ecf20Sopenharmony_ci		return -ENODEV;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	tty->driver_data = raw;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	return tty_port_install(&raw->port, driver, tty);
9718c2ecf20Sopenharmony_ci}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci/*
9748c2ecf20Sopenharmony_ci * tty3215_open
9758c2ecf20Sopenharmony_ci *
9768c2ecf20Sopenharmony_ci * This routine is called whenever a 3215 tty is opened.
9778c2ecf20Sopenharmony_ci */
9788c2ecf20Sopenharmony_cistatic int tty3215_open(struct tty_struct *tty, struct file * filp)
9798c2ecf20Sopenharmony_ci{
9808c2ecf20Sopenharmony_ci	struct raw3215_info *raw = tty->driver_data;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	tty_port_tty_set(&raw->port, tty);
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	raw->port.low_latency = 0; /* don't use bottom half for pushing chars */
9858c2ecf20Sopenharmony_ci	/*
9868c2ecf20Sopenharmony_ci	 * Start up 3215 device
9878c2ecf20Sopenharmony_ci	 */
9888c2ecf20Sopenharmony_ci	return raw3215_startup(raw);
9898c2ecf20Sopenharmony_ci}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci/*
9928c2ecf20Sopenharmony_ci * tty3215_close()
9938c2ecf20Sopenharmony_ci *
9948c2ecf20Sopenharmony_ci * This routine is called when the 3215 tty is closed. We wait
9958c2ecf20Sopenharmony_ci * for the remaining request to be completed. Then we clean up.
9968c2ecf20Sopenharmony_ci */
9978c2ecf20Sopenharmony_cistatic void tty3215_close(struct tty_struct *tty, struct file * filp)
9988c2ecf20Sopenharmony_ci{
9998c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	raw = (struct raw3215_info *) tty->driver_data;
10028c2ecf20Sopenharmony_ci	if (raw == NULL || tty->count > 1)
10038c2ecf20Sopenharmony_ci		return;
10048c2ecf20Sopenharmony_ci	tty->closing = 1;
10058c2ecf20Sopenharmony_ci	/* Shutdown the terminal */
10068c2ecf20Sopenharmony_ci	raw3215_shutdown(raw);
10078c2ecf20Sopenharmony_ci	tasklet_kill(&raw->tlet);
10088c2ecf20Sopenharmony_ci	tty->closing = 0;
10098c2ecf20Sopenharmony_ci	tty_port_tty_set(&raw->port, NULL);
10108c2ecf20Sopenharmony_ci}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci/*
10138c2ecf20Sopenharmony_ci * Returns the amount of free space in the output buffer.
10148c2ecf20Sopenharmony_ci */
10158c2ecf20Sopenharmony_cistatic int tty3215_write_room(struct tty_struct *tty)
10168c2ecf20Sopenharmony_ci{
10178c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	raw = (struct raw3215_info *) tty->driver_data;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	/* Subtract TAB_STOP_SIZE to allow for a tab, 8 <<< 64K */
10228c2ecf20Sopenharmony_ci	if ((RAW3215_BUFFER_SIZE - raw->count - TAB_STOP_SIZE) >= 0)
10238c2ecf20Sopenharmony_ci		return RAW3215_BUFFER_SIZE - raw->count - TAB_STOP_SIZE;
10248c2ecf20Sopenharmony_ci	else
10258c2ecf20Sopenharmony_ci		return 0;
10268c2ecf20Sopenharmony_ci}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci/*
10298c2ecf20Sopenharmony_ci * String write routine for 3215 ttys
10308c2ecf20Sopenharmony_ci */
10318c2ecf20Sopenharmony_cistatic int tty3215_write(struct tty_struct * tty,
10328c2ecf20Sopenharmony_ci			 const unsigned char *buf, int count)
10338c2ecf20Sopenharmony_ci{
10348c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
10358c2ecf20Sopenharmony_ci	int i, written;
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	if (!tty)
10388c2ecf20Sopenharmony_ci		return 0;
10398c2ecf20Sopenharmony_ci	raw = (struct raw3215_info *) tty->driver_data;
10408c2ecf20Sopenharmony_ci	written = count;
10418c2ecf20Sopenharmony_ci	while (count > 0) {
10428c2ecf20Sopenharmony_ci		for (i = 0; i < count; i++)
10438c2ecf20Sopenharmony_ci			if (buf[i] == '\t' || buf[i] == '\n')
10448c2ecf20Sopenharmony_ci				break;
10458c2ecf20Sopenharmony_ci		raw3215_write(raw, buf, i);
10468c2ecf20Sopenharmony_ci		count -= i;
10478c2ecf20Sopenharmony_ci		buf += i;
10488c2ecf20Sopenharmony_ci		if (count > 0) {
10498c2ecf20Sopenharmony_ci			raw3215_putchar(raw, *buf);
10508c2ecf20Sopenharmony_ci			count--;
10518c2ecf20Sopenharmony_ci			buf++;
10528c2ecf20Sopenharmony_ci		}
10538c2ecf20Sopenharmony_ci	}
10548c2ecf20Sopenharmony_ci	return written;
10558c2ecf20Sopenharmony_ci}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci/*
10588c2ecf20Sopenharmony_ci * Put character routine for 3215 ttys
10598c2ecf20Sopenharmony_ci */
10608c2ecf20Sopenharmony_cistatic int tty3215_put_char(struct tty_struct *tty, unsigned char ch)
10618c2ecf20Sopenharmony_ci{
10628c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	if (!tty)
10658c2ecf20Sopenharmony_ci		return 0;
10668c2ecf20Sopenharmony_ci	raw = (struct raw3215_info *) tty->driver_data;
10678c2ecf20Sopenharmony_ci	raw3215_putchar(raw, ch);
10688c2ecf20Sopenharmony_ci	return 1;
10698c2ecf20Sopenharmony_ci}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_cistatic void tty3215_flush_chars(struct tty_struct *tty)
10728c2ecf20Sopenharmony_ci{
10738c2ecf20Sopenharmony_ci}
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci/*
10768c2ecf20Sopenharmony_ci * Returns the number of characters in the output buffer
10778c2ecf20Sopenharmony_ci */
10788c2ecf20Sopenharmony_cistatic int tty3215_chars_in_buffer(struct tty_struct *tty)
10798c2ecf20Sopenharmony_ci{
10808c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	raw = (struct raw3215_info *) tty->driver_data;
10838c2ecf20Sopenharmony_ci	return raw->count;
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_cistatic void tty3215_flush_buffer(struct tty_struct *tty)
10878c2ecf20Sopenharmony_ci{
10888c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	raw = (struct raw3215_info *) tty->driver_data;
10918c2ecf20Sopenharmony_ci	raw3215_flush_buffer(raw);
10928c2ecf20Sopenharmony_ci	tty_wakeup(tty);
10938c2ecf20Sopenharmony_ci}
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci/*
10968c2ecf20Sopenharmony_ci * Disable reading from a 3215 tty
10978c2ecf20Sopenharmony_ci */
10988c2ecf20Sopenharmony_cistatic void tty3215_throttle(struct tty_struct * tty)
10998c2ecf20Sopenharmony_ci{
11008c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	raw = (struct raw3215_info *) tty->driver_data;
11038c2ecf20Sopenharmony_ci	raw->flags |= RAW3215_THROTTLED;
11048c2ecf20Sopenharmony_ci}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci/*
11078c2ecf20Sopenharmony_ci * Enable reading from a 3215 tty
11088c2ecf20Sopenharmony_ci */
11098c2ecf20Sopenharmony_cistatic void tty3215_unthrottle(struct tty_struct * tty)
11108c2ecf20Sopenharmony_ci{
11118c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
11128c2ecf20Sopenharmony_ci	unsigned long flags;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	raw = (struct raw3215_info *) tty->driver_data;
11158c2ecf20Sopenharmony_ci	if (raw->flags & RAW3215_THROTTLED) {
11168c2ecf20Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
11178c2ecf20Sopenharmony_ci		raw->flags &= ~RAW3215_THROTTLED;
11188c2ecf20Sopenharmony_ci		raw3215_try_io(raw);
11198c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
11208c2ecf20Sopenharmony_ci	}
11218c2ecf20Sopenharmony_ci}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci/*
11248c2ecf20Sopenharmony_ci * Disable writing to a 3215 tty
11258c2ecf20Sopenharmony_ci */
11268c2ecf20Sopenharmony_cistatic void tty3215_stop(struct tty_struct *tty)
11278c2ecf20Sopenharmony_ci{
11288c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	raw = (struct raw3215_info *) tty->driver_data;
11318c2ecf20Sopenharmony_ci	raw->flags |= RAW3215_STOPPED;
11328c2ecf20Sopenharmony_ci}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci/*
11358c2ecf20Sopenharmony_ci * Enable writing to a 3215 tty
11368c2ecf20Sopenharmony_ci */
11378c2ecf20Sopenharmony_cistatic void tty3215_start(struct tty_struct *tty)
11388c2ecf20Sopenharmony_ci{
11398c2ecf20Sopenharmony_ci	struct raw3215_info *raw;
11408c2ecf20Sopenharmony_ci	unsigned long flags;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	raw = (struct raw3215_info *) tty->driver_data;
11438c2ecf20Sopenharmony_ci	if (raw->flags & RAW3215_STOPPED) {
11448c2ecf20Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
11458c2ecf20Sopenharmony_ci		raw->flags &= ~RAW3215_STOPPED;
11468c2ecf20Sopenharmony_ci		raw3215_try_io(raw);
11478c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
11488c2ecf20Sopenharmony_ci	}
11498c2ecf20Sopenharmony_ci}
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_cistatic const struct tty_operations tty3215_ops = {
11528c2ecf20Sopenharmony_ci	.install = tty3215_install,
11538c2ecf20Sopenharmony_ci	.open = tty3215_open,
11548c2ecf20Sopenharmony_ci	.close = tty3215_close,
11558c2ecf20Sopenharmony_ci	.write = tty3215_write,
11568c2ecf20Sopenharmony_ci	.put_char = tty3215_put_char,
11578c2ecf20Sopenharmony_ci	.flush_chars = tty3215_flush_chars,
11588c2ecf20Sopenharmony_ci	.write_room = tty3215_write_room,
11598c2ecf20Sopenharmony_ci	.chars_in_buffer = tty3215_chars_in_buffer,
11608c2ecf20Sopenharmony_ci	.flush_buffer = tty3215_flush_buffer,
11618c2ecf20Sopenharmony_ci	.throttle = tty3215_throttle,
11628c2ecf20Sopenharmony_ci	.unthrottle = tty3215_unthrottle,
11638c2ecf20Sopenharmony_ci	.stop = tty3215_stop,
11648c2ecf20Sopenharmony_ci	.start = tty3215_start,
11658c2ecf20Sopenharmony_ci};
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci/*
11688c2ecf20Sopenharmony_ci * 3215 tty registration code called from tty_init().
11698c2ecf20Sopenharmony_ci * Most kernel services (incl. kmalloc) are available at this poimt.
11708c2ecf20Sopenharmony_ci */
11718c2ecf20Sopenharmony_cistatic int __init tty3215_init(void)
11728c2ecf20Sopenharmony_ci{
11738c2ecf20Sopenharmony_ci	struct tty_driver *driver;
11748c2ecf20Sopenharmony_ci	int ret;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	if (!CONSOLE_IS_3215)
11778c2ecf20Sopenharmony_ci		return 0;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	driver = alloc_tty_driver(NR_3215);
11808c2ecf20Sopenharmony_ci	if (!driver)
11818c2ecf20Sopenharmony_ci		return -ENOMEM;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	ret = ccw_driver_register(&raw3215_ccw_driver);
11848c2ecf20Sopenharmony_ci	if (ret) {
11858c2ecf20Sopenharmony_ci		put_tty_driver(driver);
11868c2ecf20Sopenharmony_ci		return ret;
11878c2ecf20Sopenharmony_ci	}
11888c2ecf20Sopenharmony_ci	/*
11898c2ecf20Sopenharmony_ci	 * Initialize the tty_driver structure
11908c2ecf20Sopenharmony_ci	 * Entries in tty3215_driver that are NOT initialized:
11918c2ecf20Sopenharmony_ci	 * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc
11928c2ecf20Sopenharmony_ci	 */
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	driver->driver_name = "tty3215";
11958c2ecf20Sopenharmony_ci	driver->name = "ttyS";
11968c2ecf20Sopenharmony_ci	driver->major = TTY_MAJOR;
11978c2ecf20Sopenharmony_ci	driver->minor_start = 64;
11988c2ecf20Sopenharmony_ci	driver->type = TTY_DRIVER_TYPE_SYSTEM;
11998c2ecf20Sopenharmony_ci	driver->subtype = SYSTEM_TYPE_TTY;
12008c2ecf20Sopenharmony_ci	driver->init_termios = tty_std_termios;
12018c2ecf20Sopenharmony_ci	driver->init_termios.c_iflag = IGNBRK | IGNPAR;
12028c2ecf20Sopenharmony_ci	driver->init_termios.c_oflag = ONLCR;
12038c2ecf20Sopenharmony_ci	driver->init_termios.c_lflag = ISIG;
12048c2ecf20Sopenharmony_ci	driver->flags = TTY_DRIVER_REAL_RAW;
12058c2ecf20Sopenharmony_ci	tty_set_operations(driver, &tty3215_ops);
12068c2ecf20Sopenharmony_ci	ret = tty_register_driver(driver);
12078c2ecf20Sopenharmony_ci	if (ret) {
12088c2ecf20Sopenharmony_ci		put_tty_driver(driver);
12098c2ecf20Sopenharmony_ci		return ret;
12108c2ecf20Sopenharmony_ci	}
12118c2ecf20Sopenharmony_ci	tty3215_driver = driver;
12128c2ecf20Sopenharmony_ci	return 0;
12138c2ecf20Sopenharmony_ci}
12148c2ecf20Sopenharmony_cidevice_initcall(tty3215_init);
1215