xref: /openbmc/linux/drivers/tty/nozomi.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2a6afd9f3SGreg Kroah-Hartman /*
3a6afd9f3SGreg Kroah-Hartman  * nozomi.c  -- HSDPA driver Broadband Wireless Data Card - Globe Trotter
4a6afd9f3SGreg Kroah-Hartman  *
5a6afd9f3SGreg Kroah-Hartman  * Written by: Ulf Jakobsson,
6a6afd9f3SGreg Kroah-Hartman  *             Jan Åkerfeldt,
7a6afd9f3SGreg Kroah-Hartman  *             Stefan Thomasson,
8a6afd9f3SGreg Kroah-Hartman  *
9a6afd9f3SGreg Kroah-Hartman  * Maintained by: Paul Hardwick (p.hardwick@option.com)
10a6afd9f3SGreg Kroah-Hartman  *
11a6afd9f3SGreg Kroah-Hartman  * Patches:
12a6afd9f3SGreg Kroah-Hartman  *          Locking code changes for Vodafone by Sphere Systems Ltd,
13a6afd9f3SGreg Kroah-Hartman  *                              Andrew Bird (ajb@spheresystems.co.uk )
14a6afd9f3SGreg Kroah-Hartman  *                              & Phil Sanderson
15a6afd9f3SGreg Kroah-Hartman  *
16a6afd9f3SGreg Kroah-Hartman  * Source has been ported from an implementation made by Filip Aben @ Option
17a6afd9f3SGreg Kroah-Hartman  *
18a6afd9f3SGreg Kroah-Hartman  * --------------------------------------------------------------------------
19a6afd9f3SGreg Kroah-Hartman  *
20a6afd9f3SGreg Kroah-Hartman  * Copyright (c) 2005,2006 Option Wireless Sweden AB
21a6afd9f3SGreg Kroah-Hartman  * Copyright (c) 2006 Sphere Systems Ltd
22a6afd9f3SGreg Kroah-Hartman  * Copyright (c) 2006 Option Wireless n/v
23a6afd9f3SGreg Kroah-Hartman  * All rights Reserved.
24a6afd9f3SGreg Kroah-Hartman  *
25a6afd9f3SGreg Kroah-Hartman  * --------------------------------------------------------------------------
26a6afd9f3SGreg Kroah-Hartman  */
27a6afd9f3SGreg Kroah-Hartman 
28a6afd9f3SGreg Kroah-Hartman /* Enable this to have a lot of debug printouts */
29a6afd9f3SGreg Kroah-Hartman #define DEBUG
30a6afd9f3SGreg Kroah-Hartman 
31a6afd9f3SGreg Kroah-Hartman #include <linux/kernel.h>
32a6afd9f3SGreg Kroah-Hartman #include <linux/module.h>
33a6afd9f3SGreg Kroah-Hartman #include <linux/pci.h>
34a6afd9f3SGreg Kroah-Hartman #include <linux/ioport.h>
35a6afd9f3SGreg Kroah-Hartman #include <linux/tty.h>
36a6afd9f3SGreg Kroah-Hartman #include <linux/tty_driver.h>
37a6afd9f3SGreg Kroah-Hartman #include <linux/tty_flip.h>
38a6afd9f3SGreg Kroah-Hartman #include <linux/sched.h>
39a6afd9f3SGreg Kroah-Hartman #include <linux/serial.h>
40a6afd9f3SGreg Kroah-Hartman #include <linux/interrupt.h>
41a6afd9f3SGreg Kroah-Hartman #include <linux/kmod.h>
42a6afd9f3SGreg Kroah-Hartman #include <linux/init.h>
43a6afd9f3SGreg Kroah-Hartman #include <linux/kfifo.h>
44a6afd9f3SGreg Kroah-Hartman #include <linux/uaccess.h>
45a6afd9f3SGreg Kroah-Hartman #include <linux/slab.h>
46a6afd9f3SGreg Kroah-Hartman #include <asm/byteorder.h>
47a6afd9f3SGreg Kroah-Hartman 
48a6afd9f3SGreg Kroah-Hartman #include <linux/delay.h>
49a6afd9f3SGreg Kroah-Hartman 
50a6afd9f3SGreg Kroah-Hartman /* Default debug printout level */
51a6afd9f3SGreg Kroah-Hartman #define NOZOMI_DEBUG_LEVEL 0x00
525020ded7SArnd Bergmann static int debug = NOZOMI_DEBUG_LEVEL;
535020ded7SArnd Bergmann module_param(debug, int, S_IRUGO | S_IWUSR);
54a6afd9f3SGreg Kroah-Hartman 
555020ded7SArnd Bergmann /*    Macros definitions */
565020ded7SArnd Bergmann #define DBG_(lvl, fmt, args...)				\
57a6afd9f3SGreg Kroah-Hartman do {							\
585020ded7SArnd Bergmann 	if (lvl & debug)				\
595020ded7SArnd Bergmann 		pr_debug("[%d] %s(): " fmt "\n",	\
605020ded7SArnd Bergmann 			 __LINE__, __func__,  ##args);	\
61a6afd9f3SGreg Kroah-Hartman } while (0)
62a6afd9f3SGreg Kroah-Hartman 
635020ded7SArnd Bergmann #define DBG1(args...) DBG_(0x01, ##args)
645020ded7SArnd Bergmann #define DBG2(args...) DBG_(0x02, ##args)
655020ded7SArnd Bergmann #define DBG3(args...) DBG_(0x04, ##args)
665020ded7SArnd Bergmann #define DBG4(args...) DBG_(0x08, ##args)
67a6afd9f3SGreg Kroah-Hartman 
68a6afd9f3SGreg Kroah-Hartman /* TODO: rewrite to optimize macros... */
69a6afd9f3SGreg Kroah-Hartman 
70a6afd9f3SGreg Kroah-Hartman #define TMP_BUF_MAX 256
71a6afd9f3SGreg Kroah-Hartman 
72a6afd9f3SGreg Kroah-Hartman #define DUMP(buf__, len__)						\
73a6afd9f3SGreg Kroah-Hartman 	do {								\
74a6afd9f3SGreg Kroah-Hartman 		char tbuf[TMP_BUF_MAX] = {0};				\
75a6afd9f3SGreg Kroah-Hartman 		if (len__ > 1) {					\
762ea0452cSJoey Pabalinas 			u32 data_len = min_t(u32, len__, TMP_BUF_MAX);	\
772ea0452cSJoey Pabalinas 			strscpy(tbuf, buf__, data_len);			\
782ea0452cSJoey Pabalinas 			if (tbuf[data_len - 2] == '\r')			\
792ea0452cSJoey Pabalinas 				tbuf[data_len - 2] = 'r';		\
80a6afd9f3SGreg Kroah-Hartman 			DBG1("SENDING: '%s' (%d+n)", tbuf, len__);	\
81a6afd9f3SGreg Kroah-Hartman 		} else {						\
82a6afd9f3SGreg Kroah-Hartman 			DBG1("SENDING: '%s' (%d)", tbuf, len__);	\
83a6afd9f3SGreg Kroah-Hartman 		}							\
84a6afd9f3SGreg Kroah-Hartman 	} while (0)
85a6afd9f3SGreg Kroah-Hartman 
86a6afd9f3SGreg Kroah-Hartman /*    Defines */
87a6afd9f3SGreg Kroah-Hartman #define NOZOMI_NAME		"nozomi"
88a6afd9f3SGreg Kroah-Hartman #define NOZOMI_NAME_TTY		"nozomi_tty"
89a6afd9f3SGreg Kroah-Hartman 
90a6afd9f3SGreg Kroah-Hartman #define NTTY_TTY_MAXMINORS	256
91a6afd9f3SGreg Kroah-Hartman #define NTTY_FIFO_BUFFER_SIZE	8192
92a6afd9f3SGreg Kroah-Hartman 
93a6afd9f3SGreg Kroah-Hartman /* Must be power of 2 */
94a6afd9f3SGreg Kroah-Hartman #define FIFO_BUFFER_SIZE_UL	8192
95a6afd9f3SGreg Kroah-Hartman 
96a6afd9f3SGreg Kroah-Hartman /* Size of tmp send buffer to card */
97a6afd9f3SGreg Kroah-Hartman #define SEND_BUF_MAX		1024
98a6afd9f3SGreg Kroah-Hartman #define RECEIVE_BUF_MAX		4
99a6afd9f3SGreg Kroah-Hartman 
100a6afd9f3SGreg Kroah-Hartman 
101a6afd9f3SGreg Kroah-Hartman #define R_IIR			0x0000	/* Interrupt Identity Register */
102a6afd9f3SGreg Kroah-Hartman #define R_FCR			0x0000	/* Flow Control Register */
103a6afd9f3SGreg Kroah-Hartman #define R_IER			0x0004	/* Interrupt Enable Register */
104a6afd9f3SGreg Kroah-Hartman 
10503ac6b34SValentin Rothberg #define NOZOMI_CONFIG_MAGIC	0xEFEFFEFE
106a6afd9f3SGreg Kroah-Hartman #define TOGGLE_VALID		0x0000
107a6afd9f3SGreg Kroah-Hartman 
108a6afd9f3SGreg Kroah-Hartman /* Definition of interrupt tokens */
109a6afd9f3SGreg Kroah-Hartman #define MDM_DL1			0x0001
110a6afd9f3SGreg Kroah-Hartman #define MDM_UL1			0x0002
111a6afd9f3SGreg Kroah-Hartman #define MDM_DL2			0x0004
112a6afd9f3SGreg Kroah-Hartman #define MDM_UL2			0x0008
113a6afd9f3SGreg Kroah-Hartman #define DIAG_DL1		0x0010
114a6afd9f3SGreg Kroah-Hartman #define DIAG_DL2		0x0020
115a6afd9f3SGreg Kroah-Hartman #define DIAG_UL			0x0040
116a6afd9f3SGreg Kroah-Hartman #define APP1_DL			0x0080
117a6afd9f3SGreg Kroah-Hartman #define APP1_UL			0x0100
118a6afd9f3SGreg Kroah-Hartman #define APP2_DL			0x0200
119a6afd9f3SGreg Kroah-Hartman #define APP2_UL			0x0400
120a6afd9f3SGreg Kroah-Hartman #define CTRL_DL			0x0800
121a6afd9f3SGreg Kroah-Hartman #define CTRL_UL			0x1000
122a6afd9f3SGreg Kroah-Hartman #define RESET			0x8000
123a6afd9f3SGreg Kroah-Hartman 
124a6afd9f3SGreg Kroah-Hartman #define MDM_DL			(MDM_DL1  | MDM_DL2)
125a6afd9f3SGreg Kroah-Hartman #define MDM_UL			(MDM_UL1  | MDM_UL2)
126a6afd9f3SGreg Kroah-Hartman #define DIAG_DL			(DIAG_DL1 | DIAG_DL2)
127a6afd9f3SGreg Kroah-Hartman 
128a6afd9f3SGreg Kroah-Hartman /* modem signal definition */
129a6afd9f3SGreg Kroah-Hartman #define CTRL_DSR		0x0001
130a6afd9f3SGreg Kroah-Hartman #define CTRL_DCD		0x0002
131a6afd9f3SGreg Kroah-Hartman #define CTRL_RI			0x0004
132a6afd9f3SGreg Kroah-Hartman #define CTRL_CTS		0x0008
133a6afd9f3SGreg Kroah-Hartman 
134a6afd9f3SGreg Kroah-Hartman #define CTRL_DTR		0x0001
135a6afd9f3SGreg Kroah-Hartman #define CTRL_RTS		0x0002
136a6afd9f3SGreg Kroah-Hartman 
137a6afd9f3SGreg Kroah-Hartman #define MAX_PORT		4
138a6afd9f3SGreg Kroah-Hartman #define NOZOMI_MAX_PORTS	5
139a6afd9f3SGreg Kroah-Hartman #define NOZOMI_MAX_CARDS	(NTTY_TTY_MAXMINORS / MAX_PORT)
140a6afd9f3SGreg Kroah-Hartman 
141a6afd9f3SGreg Kroah-Hartman /*    Type definitions */
142a6afd9f3SGreg Kroah-Hartman 
143a6afd9f3SGreg Kroah-Hartman /*
144a6afd9f3SGreg Kroah-Hartman  * There are two types of nozomi cards,
145a6afd9f3SGreg Kroah-Hartman  * one with 2048 memory and with 8192 memory
146a6afd9f3SGreg Kroah-Hartman  */
147a6afd9f3SGreg Kroah-Hartman enum card_type {
148a6afd9f3SGreg Kroah-Hartman 	F32_2 = 2048,	/* 512 bytes downlink + uplink * 2 -> 2048 */
149a6afd9f3SGreg Kroah-Hartman 	F32_8 = 8192,	/* 3072 bytes downl. + 1024 bytes uplink * 2 -> 8192 */
150a6afd9f3SGreg Kroah-Hartman };
151a6afd9f3SGreg Kroah-Hartman 
152a6afd9f3SGreg Kroah-Hartman /* Initialization states a card can be in */
153a6afd9f3SGreg Kroah-Hartman enum card_state {
1544405898dSColin Ian King 	NOZOMI_STATE_UNKNOWN	= 0,
155a6afd9f3SGreg Kroah-Hartman 	NOZOMI_STATE_ENABLED	= 1,	/* pci device enabled */
156a6afd9f3SGreg Kroah-Hartman 	NOZOMI_STATE_ALLOCATED	= 2,	/* config setup done */
157a6afd9f3SGreg Kroah-Hartman 	NOZOMI_STATE_READY	= 3,	/* flowcontrols received */
158a6afd9f3SGreg Kroah-Hartman };
159a6afd9f3SGreg Kroah-Hartman 
160a6afd9f3SGreg Kroah-Hartman /* Two different toggle channels exist */
161a6afd9f3SGreg Kroah-Hartman enum channel_type {
162a6afd9f3SGreg Kroah-Hartman 	CH_A = 0,
163a6afd9f3SGreg Kroah-Hartman 	CH_B = 1,
164a6afd9f3SGreg Kroah-Hartman };
165a6afd9f3SGreg Kroah-Hartman 
166a6afd9f3SGreg Kroah-Hartman /* Port definition for the card regarding flow control */
167a6afd9f3SGreg Kroah-Hartman enum ctrl_port_type {
168a6afd9f3SGreg Kroah-Hartman 	CTRL_CMD	= 0,
169a6afd9f3SGreg Kroah-Hartman 	CTRL_MDM	= 1,
170a6afd9f3SGreg Kroah-Hartman 	CTRL_DIAG	= 2,
171a6afd9f3SGreg Kroah-Hartman 	CTRL_APP1	= 3,
172a6afd9f3SGreg Kroah-Hartman 	CTRL_APP2	= 4,
173a6afd9f3SGreg Kroah-Hartman 	CTRL_ERROR	= -1,
174a6afd9f3SGreg Kroah-Hartman };
175a6afd9f3SGreg Kroah-Hartman 
176a6afd9f3SGreg Kroah-Hartman /* Ports that the nozomi has */
177a6afd9f3SGreg Kroah-Hartman enum port_type {
178a6afd9f3SGreg Kroah-Hartman 	PORT_MDM	= 0,
179a6afd9f3SGreg Kroah-Hartman 	PORT_DIAG	= 1,
180a6afd9f3SGreg Kroah-Hartman 	PORT_APP1	= 2,
181a6afd9f3SGreg Kroah-Hartman 	PORT_APP2	= 3,
182a6afd9f3SGreg Kroah-Hartman 	PORT_CTRL	= 4,
183a6afd9f3SGreg Kroah-Hartman 	PORT_ERROR	= -1,
184a6afd9f3SGreg Kroah-Hartman };
185a6afd9f3SGreg Kroah-Hartman 
186a6afd9f3SGreg Kroah-Hartman #ifdef __BIG_ENDIAN
187a6afd9f3SGreg Kroah-Hartman /* Big endian */
188a6afd9f3SGreg Kroah-Hartman 
189a6afd9f3SGreg Kroah-Hartman struct toggles {
190a6afd9f3SGreg Kroah-Hartman 	unsigned int enabled:5;	/*
191a6afd9f3SGreg Kroah-Hartman 				 * Toggle fields are valid if enabled is 0,
192a6afd9f3SGreg Kroah-Hartman 				 * else A-channels must always be used.
193a6afd9f3SGreg Kroah-Hartman 				 */
194a6afd9f3SGreg Kroah-Hartman 	unsigned int diag_dl:1;
195a6afd9f3SGreg Kroah-Hartman 	unsigned int mdm_dl:1;
196a6afd9f3SGreg Kroah-Hartman 	unsigned int mdm_ul:1;
197a6afd9f3SGreg Kroah-Hartman } __attribute__ ((packed));
198a6afd9f3SGreg Kroah-Hartman 
199a6afd9f3SGreg Kroah-Hartman /* Configuration table to read at startup of card */
200a6afd9f3SGreg Kroah-Hartman /* Is for now only needed during initialization phase */
201a6afd9f3SGreg Kroah-Hartman struct config_table {
202a6afd9f3SGreg Kroah-Hartman 	u32 signature;
203a6afd9f3SGreg Kroah-Hartman 	u16 product_information;
204a6afd9f3SGreg Kroah-Hartman 	u16 version;
205a6afd9f3SGreg Kroah-Hartman 	u8 pad3[3];
206a6afd9f3SGreg Kroah-Hartman 	struct toggles toggle;
207a6afd9f3SGreg Kroah-Hartman 	u8 pad1[4];
208a6afd9f3SGreg Kroah-Hartman 	u16 dl_mdm_len1;	/*
209a6afd9f3SGreg Kroah-Hartman 				 * If this is 64, it can hold
210a6afd9f3SGreg Kroah-Hartman 				 * 60 bytes + 4 that is length field
211a6afd9f3SGreg Kroah-Hartman 				 */
212a6afd9f3SGreg Kroah-Hartman 	u16 dl_start;
213a6afd9f3SGreg Kroah-Hartman 
214a6afd9f3SGreg Kroah-Hartman 	u16 dl_diag_len1;
215a6afd9f3SGreg Kroah-Hartman 	u16 dl_mdm_len2;	/*
216a6afd9f3SGreg Kroah-Hartman 				 * If this is 64, it can hold
217a6afd9f3SGreg Kroah-Hartman 				 * 60 bytes + 4 that is length field
218a6afd9f3SGreg Kroah-Hartman 				 */
219a6afd9f3SGreg Kroah-Hartman 	u16 dl_app1_len;
220a6afd9f3SGreg Kroah-Hartman 
221a6afd9f3SGreg Kroah-Hartman 	u16 dl_diag_len2;
222a6afd9f3SGreg Kroah-Hartman 	u16 dl_ctrl_len;
223a6afd9f3SGreg Kroah-Hartman 	u16 dl_app2_len;
224a6afd9f3SGreg Kroah-Hartman 	u8 pad2[16];
225a6afd9f3SGreg Kroah-Hartman 	u16 ul_mdm_len1;
226a6afd9f3SGreg Kroah-Hartman 	u16 ul_start;
227a6afd9f3SGreg Kroah-Hartman 	u16 ul_diag_len;
228a6afd9f3SGreg Kroah-Hartman 	u16 ul_mdm_len2;
229a6afd9f3SGreg Kroah-Hartman 	u16 ul_app1_len;
230a6afd9f3SGreg Kroah-Hartman 	u16 ul_app2_len;
231a6afd9f3SGreg Kroah-Hartman 	u16 ul_ctrl_len;
232a6afd9f3SGreg Kroah-Hartman } __attribute__ ((packed));
233a6afd9f3SGreg Kroah-Hartman 
234a6afd9f3SGreg Kroah-Hartman /* This stores all control downlink flags */
235a6afd9f3SGreg Kroah-Hartman struct ctrl_dl {
236a6afd9f3SGreg Kroah-Hartman 	u8 port;
237a6afd9f3SGreg Kroah-Hartman 	unsigned int reserved:4;
238a6afd9f3SGreg Kroah-Hartman 	unsigned int CTS:1;
239a6afd9f3SGreg Kroah-Hartman 	unsigned int RI:1;
240a6afd9f3SGreg Kroah-Hartman 	unsigned int DCD:1;
241a6afd9f3SGreg Kroah-Hartman 	unsigned int DSR:1;
242a6afd9f3SGreg Kroah-Hartman } __attribute__ ((packed));
243a6afd9f3SGreg Kroah-Hartman 
244a6afd9f3SGreg Kroah-Hartman /* This stores all control uplink flags */
245a6afd9f3SGreg Kroah-Hartman struct ctrl_ul {
246a6afd9f3SGreg Kroah-Hartman 	u8 port;
247a6afd9f3SGreg Kroah-Hartman 	unsigned int reserved:6;
248a6afd9f3SGreg Kroah-Hartman 	unsigned int RTS:1;
249a6afd9f3SGreg Kroah-Hartman 	unsigned int DTR:1;
250a6afd9f3SGreg Kroah-Hartman } __attribute__ ((packed));
251a6afd9f3SGreg Kroah-Hartman 
252a6afd9f3SGreg Kroah-Hartman #else
253a6afd9f3SGreg Kroah-Hartman /* Little endian */
254a6afd9f3SGreg Kroah-Hartman 
255a6afd9f3SGreg Kroah-Hartman /* This represents the toggle information */
256a6afd9f3SGreg Kroah-Hartman struct toggles {
257a6afd9f3SGreg Kroah-Hartman 	unsigned int mdm_ul:1;
258a6afd9f3SGreg Kroah-Hartman 	unsigned int mdm_dl:1;
259a6afd9f3SGreg Kroah-Hartman 	unsigned int diag_dl:1;
260a6afd9f3SGreg Kroah-Hartman 	unsigned int enabled:5;	/*
261a6afd9f3SGreg Kroah-Hartman 				 * Toggle fields are valid if enabled is 0,
262a6afd9f3SGreg Kroah-Hartman 				 * else A-channels must always be used.
263a6afd9f3SGreg Kroah-Hartman 				 */
264a6afd9f3SGreg Kroah-Hartman } __attribute__ ((packed));
265a6afd9f3SGreg Kroah-Hartman 
266a6afd9f3SGreg Kroah-Hartman /* Configuration table to read at startup of card */
267a6afd9f3SGreg Kroah-Hartman struct config_table {
268a6afd9f3SGreg Kroah-Hartman 	u32 signature;
269a6afd9f3SGreg Kroah-Hartman 	u16 version;
270a6afd9f3SGreg Kroah-Hartman 	u16 product_information;
271a6afd9f3SGreg Kroah-Hartman 	struct toggles toggle;
272a6afd9f3SGreg Kroah-Hartman 	u8 pad1[7];
273a6afd9f3SGreg Kroah-Hartman 	u16 dl_start;
274a6afd9f3SGreg Kroah-Hartman 	u16 dl_mdm_len1;	/*
275a6afd9f3SGreg Kroah-Hartman 				 * If this is 64, it can hold
276a6afd9f3SGreg Kroah-Hartman 				 * 60 bytes + 4 that is length field
277a6afd9f3SGreg Kroah-Hartman 				 */
278a6afd9f3SGreg Kroah-Hartman 	u16 dl_mdm_len2;
279a6afd9f3SGreg Kroah-Hartman 	u16 dl_diag_len1;
280a6afd9f3SGreg Kroah-Hartman 	u16 dl_diag_len2;
281a6afd9f3SGreg Kroah-Hartman 	u16 dl_app1_len;
282a6afd9f3SGreg Kroah-Hartman 	u16 dl_app2_len;
283a6afd9f3SGreg Kroah-Hartman 	u16 dl_ctrl_len;
284a6afd9f3SGreg Kroah-Hartman 	u8 pad2[16];
285a6afd9f3SGreg Kroah-Hartman 	u16 ul_start;
286a6afd9f3SGreg Kroah-Hartman 	u16 ul_mdm_len2;
287a6afd9f3SGreg Kroah-Hartman 	u16 ul_mdm_len1;
288a6afd9f3SGreg Kroah-Hartman 	u16 ul_diag_len;
289a6afd9f3SGreg Kroah-Hartman 	u16 ul_app1_len;
290a6afd9f3SGreg Kroah-Hartman 	u16 ul_app2_len;
291a6afd9f3SGreg Kroah-Hartman 	u16 ul_ctrl_len;
292a6afd9f3SGreg Kroah-Hartman } __attribute__ ((packed));
293a6afd9f3SGreg Kroah-Hartman 
294a6afd9f3SGreg Kroah-Hartman /* This stores all control downlink flags */
295a6afd9f3SGreg Kroah-Hartman struct ctrl_dl {
296a6afd9f3SGreg Kroah-Hartman 	unsigned int DSR:1;
297a6afd9f3SGreg Kroah-Hartman 	unsigned int DCD:1;
298a6afd9f3SGreg Kroah-Hartman 	unsigned int RI:1;
299a6afd9f3SGreg Kroah-Hartman 	unsigned int CTS:1;
300e2c2e798SAlexandre Belloni 	unsigned int reserved:4;
301a6afd9f3SGreg Kroah-Hartman 	u8 port;
302a6afd9f3SGreg Kroah-Hartman } __attribute__ ((packed));
303a6afd9f3SGreg Kroah-Hartman 
304a6afd9f3SGreg Kroah-Hartman /* This stores all control uplink flags */
305a6afd9f3SGreg Kroah-Hartman struct ctrl_ul {
306a6afd9f3SGreg Kroah-Hartman 	unsigned int DTR:1;
307a6afd9f3SGreg Kroah-Hartman 	unsigned int RTS:1;
308a6afd9f3SGreg Kroah-Hartman 	unsigned int reserved:6;
309a6afd9f3SGreg Kroah-Hartman 	u8 port;
310a6afd9f3SGreg Kroah-Hartman } __attribute__ ((packed));
311a6afd9f3SGreg Kroah-Hartman #endif
312a6afd9f3SGreg Kroah-Hartman 
313a6afd9f3SGreg Kroah-Hartman /* This holds all information that is needed regarding a port */
314a6afd9f3SGreg Kroah-Hartman struct port {
315a6afd9f3SGreg Kroah-Hartman 	struct tty_port port;
316a6afd9f3SGreg Kroah-Hartman 	u8 update_flow_control;
317a6afd9f3SGreg Kroah-Hartman 	struct ctrl_ul ctrl_ul;
318a6afd9f3SGreg Kroah-Hartman 	struct ctrl_dl ctrl_dl;
319a6afd9f3SGreg Kroah-Hartman 	struct kfifo fifo_ul;
320a6afd9f3SGreg Kroah-Hartman 	void __iomem *dl_addr[2];
321a6afd9f3SGreg Kroah-Hartman 	u32 dl_size[2];
322a6afd9f3SGreg Kroah-Hartman 	u8 toggle_dl;
323a6afd9f3SGreg Kroah-Hartman 	void __iomem *ul_addr[2];
324a6afd9f3SGreg Kroah-Hartman 	u32 ul_size[2];
325a6afd9f3SGreg Kroah-Hartman 	u8 toggle_ul;
326a6afd9f3SGreg Kroah-Hartman 	u16 token_dl;
327a6afd9f3SGreg Kroah-Hartman 
328a6afd9f3SGreg Kroah-Hartman 	wait_queue_head_t tty_wait;
329a6afd9f3SGreg Kroah-Hartman 	struct async_icount tty_icount;
330a6afd9f3SGreg Kroah-Hartman 
331a6afd9f3SGreg Kroah-Hartman 	struct nozomi *dc;
332a6afd9f3SGreg Kroah-Hartman };
333a6afd9f3SGreg Kroah-Hartman 
334a6afd9f3SGreg Kroah-Hartman /* Private data one for each card in the system */
335a6afd9f3SGreg Kroah-Hartman struct nozomi {
336a6afd9f3SGreg Kroah-Hartman 	void __iomem *base_addr;
337a6afd9f3SGreg Kroah-Hartman 	unsigned long flip;
338a6afd9f3SGreg Kroah-Hartman 
339a6afd9f3SGreg Kroah-Hartman 	/* Pointers to registers */
340a6afd9f3SGreg Kroah-Hartman 	void __iomem *reg_iir;
341a6afd9f3SGreg Kroah-Hartman 	void __iomem *reg_fcr;
342a6afd9f3SGreg Kroah-Hartman 	void __iomem *reg_ier;
343a6afd9f3SGreg Kroah-Hartman 
344a6afd9f3SGreg Kroah-Hartman 	u16 last_ier;
345a6afd9f3SGreg Kroah-Hartman 	enum card_type card_type;
346a6afd9f3SGreg Kroah-Hartman 	struct config_table config_table;	/* Configuration table */
347a6afd9f3SGreg Kroah-Hartman 	struct pci_dev *pdev;
348a6afd9f3SGreg Kroah-Hartman 	struct port port[NOZOMI_MAX_PORTS];
349a6afd9f3SGreg Kroah-Hartman 	u8 *send_buf;
350a6afd9f3SGreg Kroah-Hartman 
351a6afd9f3SGreg Kroah-Hartman 	spinlock_t spin_mutex;	/* secures access to registers and tty */
352a6afd9f3SGreg Kroah-Hartman 
353a6afd9f3SGreg Kroah-Hartman 	unsigned int index_start;
354a6afd9f3SGreg Kroah-Hartman 	enum card_state state;
355a6afd9f3SGreg Kroah-Hartman 	u32 open_ttys;
356a6afd9f3SGreg Kroah-Hartman };
357a6afd9f3SGreg Kroah-Hartman 
358a6afd9f3SGreg Kroah-Hartman /* Global variables */
359512f82a0SBill Pemberton static const struct pci_device_id nozomi_pci_tbl[] = {
360a6afd9f3SGreg Kroah-Hartman 	{PCI_DEVICE(0x1931, 0x000c)},	/* Nozomi HSDPA */
361a6afd9f3SGreg Kroah-Hartman 	{},
362a6afd9f3SGreg Kroah-Hartman };
363a6afd9f3SGreg Kroah-Hartman 
364a6afd9f3SGreg Kroah-Hartman MODULE_DEVICE_TABLE(pci, nozomi_pci_tbl);
365a6afd9f3SGreg Kroah-Hartman 
366a6afd9f3SGreg Kroah-Hartman static struct nozomi *ndevs[NOZOMI_MAX_CARDS];
367a6afd9f3SGreg Kroah-Hartman static struct tty_driver *ntty_driver;
368a6afd9f3SGreg Kroah-Hartman 
369a6afd9f3SGreg Kroah-Hartman static const struct tty_port_operations noz_tty_port_ops;
370a6afd9f3SGreg Kroah-Hartman 
371a6afd9f3SGreg Kroah-Hartman /*
372a6afd9f3SGreg Kroah-Hartman  * find card by tty_index
373a6afd9f3SGreg Kroah-Hartman  */
get_dc_by_tty(const struct tty_struct * tty)374a6afd9f3SGreg Kroah-Hartman static inline struct nozomi *get_dc_by_tty(const struct tty_struct *tty)
375a6afd9f3SGreg Kroah-Hartman {
376a6afd9f3SGreg Kroah-Hartman 	return tty ? ndevs[tty->index / MAX_PORT] : NULL;
377a6afd9f3SGreg Kroah-Hartman }
378a6afd9f3SGreg Kroah-Hartman 
get_port_by_tty(const struct tty_struct * tty)379a6afd9f3SGreg Kroah-Hartman static inline struct port *get_port_by_tty(const struct tty_struct *tty)
380a6afd9f3SGreg Kroah-Hartman {
381a6afd9f3SGreg Kroah-Hartman 	struct nozomi *ndev = get_dc_by_tty(tty);
382a6afd9f3SGreg Kroah-Hartman 	return ndev ? &ndev->port[tty->index % MAX_PORT] : NULL;
383a6afd9f3SGreg Kroah-Hartman }
384a6afd9f3SGreg Kroah-Hartman 
385a6afd9f3SGreg Kroah-Hartman /*
386a6afd9f3SGreg Kroah-Hartman  * TODO:
387a6afd9f3SGreg Kroah-Hartman  * -Optimize
388a6afd9f3SGreg Kroah-Hartman  * -Rewrite cleaner
389a6afd9f3SGreg Kroah-Hartman  */
390a6afd9f3SGreg Kroah-Hartman 
read_mem32(u32 * buf,const void __iomem * mem_addr_start,u32 size_bytes)391a6afd9f3SGreg Kroah-Hartman static void read_mem32(u32 *buf, const void __iomem *mem_addr_start,
392a6afd9f3SGreg Kroah-Hartman 			u32 size_bytes)
393a6afd9f3SGreg Kroah-Hartman {
394a6afd9f3SGreg Kroah-Hartman 	u32 i = 0;
395a6afd9f3SGreg Kroah-Hartman 	const u32 __iomem *ptr = mem_addr_start;
396a6afd9f3SGreg Kroah-Hartman 	u16 *buf16;
397a6afd9f3SGreg Kroah-Hartman 
398a6afd9f3SGreg Kroah-Hartman 	if (unlikely(!ptr || !buf))
399a6afd9f3SGreg Kroah-Hartman 		goto out;
400a6afd9f3SGreg Kroah-Hartman 
401a6afd9f3SGreg Kroah-Hartman 	/* shortcut for extremely often used cases */
402a6afd9f3SGreg Kroah-Hartman 	switch (size_bytes) {
403a6afd9f3SGreg Kroah-Hartman 	case 2:	/* 2 bytes */
404a6afd9f3SGreg Kroah-Hartman 		buf16 = (u16 *) buf;
405a6afd9f3SGreg Kroah-Hartman 		*buf16 = __le16_to_cpu(readw(ptr));
406a6afd9f3SGreg Kroah-Hartman 		goto out;
407a6afd9f3SGreg Kroah-Hartman 	case 4:	/* 4 bytes */
408a6afd9f3SGreg Kroah-Hartman 		*(buf) = __le32_to_cpu(readl(ptr));
409a6afd9f3SGreg Kroah-Hartman 		goto out;
410a6afd9f3SGreg Kroah-Hartman 	}
411a6afd9f3SGreg Kroah-Hartman 
412a6afd9f3SGreg Kroah-Hartman 	while (i < size_bytes) {
413a6afd9f3SGreg Kroah-Hartman 		if (size_bytes - i == 2) {
414a6afd9f3SGreg Kroah-Hartman 			/* Handle 2 bytes in the end */
415a6afd9f3SGreg Kroah-Hartman 			buf16 = (u16 *) buf;
416a6afd9f3SGreg Kroah-Hartman 			*(buf16) = __le16_to_cpu(readw(ptr));
417a6afd9f3SGreg Kroah-Hartman 			i += 2;
418a6afd9f3SGreg Kroah-Hartman 		} else {
419a6afd9f3SGreg Kroah-Hartman 			/* Read 4 bytes */
420a6afd9f3SGreg Kroah-Hartman 			*(buf) = __le32_to_cpu(readl(ptr));
421a6afd9f3SGreg Kroah-Hartman 			i += 4;
422a6afd9f3SGreg Kroah-Hartman 		}
423a6afd9f3SGreg Kroah-Hartman 		buf++;
424a6afd9f3SGreg Kroah-Hartman 		ptr++;
425a6afd9f3SGreg Kroah-Hartman 	}
426a6afd9f3SGreg Kroah-Hartman out:
427a6afd9f3SGreg Kroah-Hartman 	return;
428a6afd9f3SGreg Kroah-Hartman }
429a6afd9f3SGreg Kroah-Hartman 
430a6afd9f3SGreg Kroah-Hartman /*
431a6afd9f3SGreg Kroah-Hartman  * TODO:
432a6afd9f3SGreg Kroah-Hartman  * -Optimize
433a6afd9f3SGreg Kroah-Hartman  * -Rewrite cleaner
434a6afd9f3SGreg Kroah-Hartman  */
write_mem32(void __iomem * mem_addr_start,const u32 * buf,u32 size_bytes)435a6afd9f3SGreg Kroah-Hartman static u32 write_mem32(void __iomem *mem_addr_start, const u32 *buf,
436a6afd9f3SGreg Kroah-Hartman 			u32 size_bytes)
437a6afd9f3SGreg Kroah-Hartman {
438a6afd9f3SGreg Kroah-Hartman 	u32 i = 0;
439a6afd9f3SGreg Kroah-Hartman 	u32 __iomem *ptr = mem_addr_start;
440a6afd9f3SGreg Kroah-Hartman 	const u16 *buf16;
441a6afd9f3SGreg Kroah-Hartman 
442a6afd9f3SGreg Kroah-Hartman 	if (unlikely(!ptr || !buf))
443a6afd9f3SGreg Kroah-Hartman 		return 0;
444a6afd9f3SGreg Kroah-Hartman 
445a6afd9f3SGreg Kroah-Hartman 	/* shortcut for extremely often used cases */
446a6afd9f3SGreg Kroah-Hartman 	switch (size_bytes) {
447a6afd9f3SGreg Kroah-Hartman 	case 2:	/* 2 bytes */
448a6afd9f3SGreg Kroah-Hartman 		buf16 = (const u16 *)buf;
449a6afd9f3SGreg Kroah-Hartman 		writew(__cpu_to_le16(*buf16), ptr);
450a6afd9f3SGreg Kroah-Hartman 		return 2;
451a6afd9f3SGreg Kroah-Hartman 	case 1: /*
452a6afd9f3SGreg Kroah-Hartman 		 * also needs to write 4 bytes in this case
453a6afd9f3SGreg Kroah-Hartman 		 * so falling through..
454a6afd9f3SGreg Kroah-Hartman 		 */
4551a460c36STom Rix 		fallthrough;
456a6afd9f3SGreg Kroah-Hartman 	case 4: /* 4 bytes */
457a6afd9f3SGreg Kroah-Hartman 		writel(__cpu_to_le32(*buf), ptr);
458a6afd9f3SGreg Kroah-Hartman 		return 4;
459a6afd9f3SGreg Kroah-Hartman 	}
460a6afd9f3SGreg Kroah-Hartman 
461a6afd9f3SGreg Kroah-Hartman 	while (i < size_bytes) {
462a6afd9f3SGreg Kroah-Hartman 		if (size_bytes - i == 2) {
463a6afd9f3SGreg Kroah-Hartman 			/* 2 bytes */
464a6afd9f3SGreg Kroah-Hartman 			buf16 = (const u16 *)buf;
465a6afd9f3SGreg Kroah-Hartman 			writew(__cpu_to_le16(*buf16), ptr);
466a6afd9f3SGreg Kroah-Hartman 			i += 2;
467a6afd9f3SGreg Kroah-Hartman 		} else {
468a6afd9f3SGreg Kroah-Hartman 			/* 4 bytes */
469a6afd9f3SGreg Kroah-Hartman 			writel(__cpu_to_le32(*buf), ptr);
470a6afd9f3SGreg Kroah-Hartman 			i += 4;
471a6afd9f3SGreg Kroah-Hartman 		}
472a6afd9f3SGreg Kroah-Hartman 		buf++;
473a6afd9f3SGreg Kroah-Hartman 		ptr++;
474a6afd9f3SGreg Kroah-Hartman 	}
475a6afd9f3SGreg Kroah-Hartman 	return i;
476a6afd9f3SGreg Kroah-Hartman }
477a6afd9f3SGreg Kroah-Hartman 
478a6afd9f3SGreg Kroah-Hartman /* Setup pointers to different channels and also setup buffer sizes. */
nozomi_setup_memory(struct nozomi * dc)4797acb88afSChen Gang static void nozomi_setup_memory(struct nozomi *dc)
480a6afd9f3SGreg Kroah-Hartman {
481a6afd9f3SGreg Kroah-Hartman 	void __iomem *offset = dc->base_addr + dc->config_table.dl_start;
482a6afd9f3SGreg Kroah-Hartman 	/* The length reported is including the length field of 4 bytes,
483a6afd9f3SGreg Kroah-Hartman 	 * hence subtract with 4.
484a6afd9f3SGreg Kroah-Hartman 	 */
485a6afd9f3SGreg Kroah-Hartman 	const u16 buff_offset = 4;
486a6afd9f3SGreg Kroah-Hartman 
487a6afd9f3SGreg Kroah-Hartman 	/* Modem port dl configuration */
488a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_MDM].dl_addr[CH_A] = offset;
489a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_MDM].dl_addr[CH_B] =
490a6afd9f3SGreg Kroah-Hartman 				(offset += dc->config_table.dl_mdm_len1);
491a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_MDM].dl_size[CH_A] =
492a6afd9f3SGreg Kroah-Hartman 				dc->config_table.dl_mdm_len1 - buff_offset;
493a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_MDM].dl_size[CH_B] =
494a6afd9f3SGreg Kroah-Hartman 				dc->config_table.dl_mdm_len2 - buff_offset;
495a6afd9f3SGreg Kroah-Hartman 
496a6afd9f3SGreg Kroah-Hartman 	/* Diag port dl configuration */
497a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_DIAG].dl_addr[CH_A] =
498a6afd9f3SGreg Kroah-Hartman 				(offset += dc->config_table.dl_mdm_len2);
499a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_DIAG].dl_size[CH_A] =
500a6afd9f3SGreg Kroah-Hartman 				dc->config_table.dl_diag_len1 - buff_offset;
501a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_DIAG].dl_addr[CH_B] =
502a6afd9f3SGreg Kroah-Hartman 				(offset += dc->config_table.dl_diag_len1);
503a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_DIAG].dl_size[CH_B] =
504a6afd9f3SGreg Kroah-Hartman 				dc->config_table.dl_diag_len2 - buff_offset;
505a6afd9f3SGreg Kroah-Hartman 
506a6afd9f3SGreg Kroah-Hartman 	/* App1 port dl configuration */
507a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_APP1].dl_addr[CH_A] =
508a6afd9f3SGreg Kroah-Hartman 				(offset += dc->config_table.dl_diag_len2);
509a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_APP1].dl_size[CH_A] =
510a6afd9f3SGreg Kroah-Hartman 				dc->config_table.dl_app1_len - buff_offset;
511a6afd9f3SGreg Kroah-Hartman 
512a6afd9f3SGreg Kroah-Hartman 	/* App2 port dl configuration */
513a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_APP2].dl_addr[CH_A] =
514a6afd9f3SGreg Kroah-Hartman 				(offset += dc->config_table.dl_app1_len);
515a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_APP2].dl_size[CH_A] =
516a6afd9f3SGreg Kroah-Hartman 				dc->config_table.dl_app2_len - buff_offset;
517a6afd9f3SGreg Kroah-Hartman 
518a6afd9f3SGreg Kroah-Hartman 	/* Ctrl dl configuration */
519a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_CTRL].dl_addr[CH_A] =
520a6afd9f3SGreg Kroah-Hartman 				(offset += dc->config_table.dl_app2_len);
521a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_CTRL].dl_size[CH_A] =
522a6afd9f3SGreg Kroah-Hartman 				dc->config_table.dl_ctrl_len - buff_offset;
523a6afd9f3SGreg Kroah-Hartman 
524a6afd9f3SGreg Kroah-Hartman 	offset = dc->base_addr + dc->config_table.ul_start;
525a6afd9f3SGreg Kroah-Hartman 
526a6afd9f3SGreg Kroah-Hartman 	/* Modem Port ul configuration */
527a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_MDM].ul_addr[CH_A] = offset;
528a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_MDM].ul_size[CH_A] =
529a6afd9f3SGreg Kroah-Hartman 				dc->config_table.ul_mdm_len1 - buff_offset;
530a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_MDM].ul_addr[CH_B] =
531a6afd9f3SGreg Kroah-Hartman 				(offset += dc->config_table.ul_mdm_len1);
532a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_MDM].ul_size[CH_B] =
533a6afd9f3SGreg Kroah-Hartman 				dc->config_table.ul_mdm_len2 - buff_offset;
534a6afd9f3SGreg Kroah-Hartman 
535a6afd9f3SGreg Kroah-Hartman 	/* Diag port ul configuration */
536a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_DIAG].ul_addr[CH_A] =
537a6afd9f3SGreg Kroah-Hartman 				(offset += dc->config_table.ul_mdm_len2);
538a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_DIAG].ul_size[CH_A] =
539a6afd9f3SGreg Kroah-Hartman 				dc->config_table.ul_diag_len - buff_offset;
540a6afd9f3SGreg Kroah-Hartman 
541a6afd9f3SGreg Kroah-Hartman 	/* App1 port ul configuration */
542a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_APP1].ul_addr[CH_A] =
543a6afd9f3SGreg Kroah-Hartman 				(offset += dc->config_table.ul_diag_len);
544a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_APP1].ul_size[CH_A] =
545a6afd9f3SGreg Kroah-Hartman 				dc->config_table.ul_app1_len - buff_offset;
546a6afd9f3SGreg Kroah-Hartman 
547a6afd9f3SGreg Kroah-Hartman 	/* App2 port ul configuration */
548a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_APP2].ul_addr[CH_A] =
549a6afd9f3SGreg Kroah-Hartman 				(offset += dc->config_table.ul_app1_len);
550a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_APP2].ul_size[CH_A] =
551a6afd9f3SGreg Kroah-Hartman 				dc->config_table.ul_app2_len - buff_offset;
552a6afd9f3SGreg Kroah-Hartman 
553a6afd9f3SGreg Kroah-Hartman 	/* Ctrl ul configuration */
554a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_CTRL].ul_addr[CH_A] =
555a6afd9f3SGreg Kroah-Hartman 				(offset += dc->config_table.ul_app2_len);
556a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_CTRL].ul_size[CH_A] =
557a6afd9f3SGreg Kroah-Hartman 				dc->config_table.ul_ctrl_len - buff_offset;
558a6afd9f3SGreg Kroah-Hartman }
559a6afd9f3SGreg Kroah-Hartman 
560a6afd9f3SGreg Kroah-Hartman /* Dump config table under initalization phase */
561a6afd9f3SGreg Kroah-Hartman #ifdef DEBUG
dump_table(const struct nozomi * dc)562a6afd9f3SGreg Kroah-Hartman static void dump_table(const struct nozomi *dc)
563a6afd9f3SGreg Kroah-Hartman {
564a6afd9f3SGreg Kroah-Hartman 	DBG3("signature: 0x%08X", dc->config_table.signature);
565a6afd9f3SGreg Kroah-Hartman 	DBG3("version: 0x%04X", dc->config_table.version);
566a6afd9f3SGreg Kroah-Hartman 	DBG3("product_information: 0x%04X", \
567a6afd9f3SGreg Kroah-Hartman 				dc->config_table.product_information);
568a6afd9f3SGreg Kroah-Hartman 	DBG3("toggle enabled: %d", dc->config_table.toggle.enabled);
569a6afd9f3SGreg Kroah-Hartman 	DBG3("toggle up_mdm: %d", dc->config_table.toggle.mdm_ul);
570a6afd9f3SGreg Kroah-Hartman 	DBG3("toggle dl_mdm: %d", dc->config_table.toggle.mdm_dl);
571a6afd9f3SGreg Kroah-Hartman 	DBG3("toggle dl_dbg: %d", dc->config_table.toggle.diag_dl);
572a6afd9f3SGreg Kroah-Hartman 
573a6afd9f3SGreg Kroah-Hartman 	DBG3("dl_start: 0x%04X", dc->config_table.dl_start);
574a6afd9f3SGreg Kroah-Hartman 	DBG3("dl_mdm_len0: 0x%04X, %d", dc->config_table.dl_mdm_len1,
575a6afd9f3SGreg Kroah-Hartman 	   dc->config_table.dl_mdm_len1);
576a6afd9f3SGreg Kroah-Hartman 	DBG3("dl_mdm_len1: 0x%04X, %d", dc->config_table.dl_mdm_len2,
577a6afd9f3SGreg Kroah-Hartman 	   dc->config_table.dl_mdm_len2);
578a6afd9f3SGreg Kroah-Hartman 	DBG3("dl_diag_len0: 0x%04X, %d", dc->config_table.dl_diag_len1,
579a6afd9f3SGreg Kroah-Hartman 	   dc->config_table.dl_diag_len1);
580a6afd9f3SGreg Kroah-Hartman 	DBG3("dl_diag_len1: 0x%04X, %d", dc->config_table.dl_diag_len2,
581a6afd9f3SGreg Kroah-Hartman 	   dc->config_table.dl_diag_len2);
582a6afd9f3SGreg Kroah-Hartman 	DBG3("dl_app1_len: 0x%04X, %d", dc->config_table.dl_app1_len,
583a6afd9f3SGreg Kroah-Hartman 	   dc->config_table.dl_app1_len);
584a6afd9f3SGreg Kroah-Hartman 	DBG3("dl_app2_len: 0x%04X, %d", dc->config_table.dl_app2_len,
585a6afd9f3SGreg Kroah-Hartman 	   dc->config_table.dl_app2_len);
586a6afd9f3SGreg Kroah-Hartman 	DBG3("dl_ctrl_len: 0x%04X, %d", dc->config_table.dl_ctrl_len,
587a6afd9f3SGreg Kroah-Hartman 	   dc->config_table.dl_ctrl_len);
588a6afd9f3SGreg Kroah-Hartman 	DBG3("ul_start: 0x%04X, %d", dc->config_table.ul_start,
589a6afd9f3SGreg Kroah-Hartman 	   dc->config_table.ul_start);
590a6afd9f3SGreg Kroah-Hartman 	DBG3("ul_mdm_len[0]: 0x%04X, %d", dc->config_table.ul_mdm_len1,
591a6afd9f3SGreg Kroah-Hartman 	   dc->config_table.ul_mdm_len1);
592a6afd9f3SGreg Kroah-Hartman 	DBG3("ul_mdm_len[1]: 0x%04X, %d", dc->config_table.ul_mdm_len2,
593a6afd9f3SGreg Kroah-Hartman 	   dc->config_table.ul_mdm_len2);
594a6afd9f3SGreg Kroah-Hartman 	DBG3("ul_diag_len: 0x%04X, %d", dc->config_table.ul_diag_len,
595a6afd9f3SGreg Kroah-Hartman 	   dc->config_table.ul_diag_len);
596a6afd9f3SGreg Kroah-Hartman 	DBG3("ul_app1_len: 0x%04X, %d", dc->config_table.ul_app1_len,
597a6afd9f3SGreg Kroah-Hartman 	   dc->config_table.ul_app1_len);
598a6afd9f3SGreg Kroah-Hartman 	DBG3("ul_app2_len: 0x%04X, %d", dc->config_table.ul_app2_len,
599a6afd9f3SGreg Kroah-Hartman 	   dc->config_table.ul_app2_len);
600a6afd9f3SGreg Kroah-Hartman 	DBG3("ul_ctrl_len: 0x%04X, %d", dc->config_table.ul_ctrl_len,
601a6afd9f3SGreg Kroah-Hartman 	   dc->config_table.ul_ctrl_len);
602a6afd9f3SGreg Kroah-Hartman }
603a6afd9f3SGreg Kroah-Hartman #else
dump_table(const struct nozomi * dc)604a6afd9f3SGreg Kroah-Hartman static inline void dump_table(const struct nozomi *dc) { }
605a6afd9f3SGreg Kroah-Hartman #endif
606a6afd9f3SGreg Kroah-Hartman 
607a6afd9f3SGreg Kroah-Hartman /*
608a6afd9f3SGreg Kroah-Hartman  * Read configuration table from card under intalization phase
609a6afd9f3SGreg Kroah-Hartman  * Returns 1 if ok, else 0
610a6afd9f3SGreg Kroah-Hartman  */
nozomi_read_config_table(struct nozomi * dc)611a6afd9f3SGreg Kroah-Hartman static int nozomi_read_config_table(struct nozomi *dc)
612a6afd9f3SGreg Kroah-Hartman {
613a6afd9f3SGreg Kroah-Hartman 	read_mem32((u32 *) &dc->config_table, dc->base_addr + 0,
614a6afd9f3SGreg Kroah-Hartman 						sizeof(struct config_table));
615a6afd9f3SGreg Kroah-Hartman 
61603ac6b34SValentin Rothberg 	if (dc->config_table.signature != NOZOMI_CONFIG_MAGIC) {
617a6afd9f3SGreg Kroah-Hartman 		dev_err(&dc->pdev->dev, "ConfigTable Bad! 0x%08X != 0x%08X\n",
61803ac6b34SValentin Rothberg 			dc->config_table.signature, NOZOMI_CONFIG_MAGIC);
619a6afd9f3SGreg Kroah-Hartman 		return 0;
620a6afd9f3SGreg Kroah-Hartman 	}
621a6afd9f3SGreg Kroah-Hartman 
622a6afd9f3SGreg Kroah-Hartman 	if ((dc->config_table.version == 0)
623a6afd9f3SGreg Kroah-Hartman 	    || (dc->config_table.toggle.enabled == TOGGLE_VALID)) {
624a6afd9f3SGreg Kroah-Hartman 		int i;
625a6afd9f3SGreg Kroah-Hartman 		DBG1("Second phase, configuring card");
626a6afd9f3SGreg Kroah-Hartman 
6277acb88afSChen Gang 		nozomi_setup_memory(dc);
628a6afd9f3SGreg Kroah-Hartman 
629a6afd9f3SGreg Kroah-Hartman 		dc->port[PORT_MDM].toggle_ul = dc->config_table.toggle.mdm_ul;
630a6afd9f3SGreg Kroah-Hartman 		dc->port[PORT_MDM].toggle_dl = dc->config_table.toggle.mdm_dl;
631a6afd9f3SGreg Kroah-Hartman 		dc->port[PORT_DIAG].toggle_dl = dc->config_table.toggle.diag_dl;
632a6afd9f3SGreg Kroah-Hartman 		DBG1("toggle ports: MDM UL:%d MDM DL:%d, DIAG DL:%d",
633a6afd9f3SGreg Kroah-Hartman 		   dc->port[PORT_MDM].toggle_ul,
634a6afd9f3SGreg Kroah-Hartman 		   dc->port[PORT_MDM].toggle_dl, dc->port[PORT_DIAG].toggle_dl);
635a6afd9f3SGreg Kroah-Hartman 
636a6afd9f3SGreg Kroah-Hartman 		dump_table(dc);
637a6afd9f3SGreg Kroah-Hartman 
638a6afd9f3SGreg Kroah-Hartman 		for (i = PORT_MDM; i < MAX_PORT; i++) {
639a6afd9f3SGreg Kroah-Hartman 			memset(&dc->port[i].ctrl_dl, 0, sizeof(struct ctrl_dl));
640a6afd9f3SGreg Kroah-Hartman 			memset(&dc->port[i].ctrl_ul, 0, sizeof(struct ctrl_ul));
641a6afd9f3SGreg Kroah-Hartman 		}
642a6afd9f3SGreg Kroah-Hartman 
643a6afd9f3SGreg Kroah-Hartman 		/* Enable control channel */
644a6afd9f3SGreg Kroah-Hartman 		dc->last_ier = dc->last_ier | CTRL_DL;
645a6afd9f3SGreg Kroah-Hartman 		writew(dc->last_ier, dc->reg_ier);
646a6afd9f3SGreg Kroah-Hartman 
647a6afd9f3SGreg Kroah-Hartman 		dc->state = NOZOMI_STATE_ALLOCATED;
648a6afd9f3SGreg Kroah-Hartman 		dev_info(&dc->pdev->dev, "Initialization OK!\n");
649a6afd9f3SGreg Kroah-Hartman 		return 1;
650a6afd9f3SGreg Kroah-Hartman 	}
651a6afd9f3SGreg Kroah-Hartman 
652a6afd9f3SGreg Kroah-Hartman 	if ((dc->config_table.version > 0)
653a6afd9f3SGreg Kroah-Hartman 	    && (dc->config_table.toggle.enabled != TOGGLE_VALID)) {
654a6afd9f3SGreg Kroah-Hartman 		u32 offset = 0;
655a6afd9f3SGreg Kroah-Hartman 		DBG1("First phase: pushing upload buffers, clearing download");
656a6afd9f3SGreg Kroah-Hartman 
657a6afd9f3SGreg Kroah-Hartman 		dev_info(&dc->pdev->dev, "Version of card: %d\n",
658a6afd9f3SGreg Kroah-Hartman 			 dc->config_table.version);
659a6afd9f3SGreg Kroah-Hartman 
660a6afd9f3SGreg Kroah-Hartman 		/* Here we should disable all I/O over F32. */
6617acb88afSChen Gang 		nozomi_setup_memory(dc);
662a6afd9f3SGreg Kroah-Hartman 
663a6afd9f3SGreg Kroah-Hartman 		/*
664a6afd9f3SGreg Kroah-Hartman 		 * We should send ALL channel pair tokens back along
665a6afd9f3SGreg Kroah-Hartman 		 * with reset token
666a6afd9f3SGreg Kroah-Hartman 		 */
667a6afd9f3SGreg Kroah-Hartman 
668a6afd9f3SGreg Kroah-Hartman 		/* push upload modem buffers */
669a6afd9f3SGreg Kroah-Hartman 		write_mem32(dc->port[PORT_MDM].ul_addr[CH_A],
670a6afd9f3SGreg Kroah-Hartman 			(u32 *) &offset, 4);
671a6afd9f3SGreg Kroah-Hartman 		write_mem32(dc->port[PORT_MDM].ul_addr[CH_B],
672a6afd9f3SGreg Kroah-Hartman 			(u32 *) &offset, 4);
673a6afd9f3SGreg Kroah-Hartman 
674a6afd9f3SGreg Kroah-Hartman 		writew(MDM_UL | DIAG_DL | MDM_DL, dc->reg_fcr);
675a6afd9f3SGreg Kroah-Hartman 
676a6afd9f3SGreg Kroah-Hartman 		DBG1("First phase done");
677a6afd9f3SGreg Kroah-Hartman 	}
678a6afd9f3SGreg Kroah-Hartman 
679a6afd9f3SGreg Kroah-Hartman 	return 1;
680a6afd9f3SGreg Kroah-Hartman }
681a6afd9f3SGreg Kroah-Hartman 
682a6afd9f3SGreg Kroah-Hartman /* Enable uplink interrupts  */
enable_transmit_ul(enum port_type port,struct nozomi * dc)683a6afd9f3SGreg Kroah-Hartman static void enable_transmit_ul(enum port_type port, struct nozomi *dc)
684a6afd9f3SGreg Kroah-Hartman {
685a6afd9f3SGreg Kroah-Hartman 	static const u16 mask[] = {MDM_UL, DIAG_UL, APP1_UL, APP2_UL, CTRL_UL};
686a6afd9f3SGreg Kroah-Hartman 
687a6afd9f3SGreg Kroah-Hartman 	if (port < NOZOMI_MAX_PORTS) {
688a6afd9f3SGreg Kroah-Hartman 		dc->last_ier |= mask[port];
689a6afd9f3SGreg Kroah-Hartman 		writew(dc->last_ier, dc->reg_ier);
690a6afd9f3SGreg Kroah-Hartman 	} else {
691a6afd9f3SGreg Kroah-Hartman 		dev_err(&dc->pdev->dev, "Called with wrong port?\n");
692a6afd9f3SGreg Kroah-Hartman 	}
693a6afd9f3SGreg Kroah-Hartman }
694a6afd9f3SGreg Kroah-Hartman 
695a6afd9f3SGreg Kroah-Hartman /* Disable uplink interrupts  */
disable_transmit_ul(enum port_type port,struct nozomi * dc)696a6afd9f3SGreg Kroah-Hartman static void disable_transmit_ul(enum port_type port, struct nozomi *dc)
697a6afd9f3SGreg Kroah-Hartman {
698a6afd9f3SGreg Kroah-Hartman 	static const u16 mask[] =
699a6afd9f3SGreg Kroah-Hartman 		{~MDM_UL, ~DIAG_UL, ~APP1_UL, ~APP2_UL, ~CTRL_UL};
700a6afd9f3SGreg Kroah-Hartman 
701a6afd9f3SGreg Kroah-Hartman 	if (port < NOZOMI_MAX_PORTS) {
702a6afd9f3SGreg Kroah-Hartman 		dc->last_ier &= mask[port];
703a6afd9f3SGreg Kroah-Hartman 		writew(dc->last_ier, dc->reg_ier);
704a6afd9f3SGreg Kroah-Hartman 	} else {
705a6afd9f3SGreg Kroah-Hartman 		dev_err(&dc->pdev->dev, "Called with wrong port?\n");
706a6afd9f3SGreg Kroah-Hartman 	}
707a6afd9f3SGreg Kroah-Hartman }
708a6afd9f3SGreg Kroah-Hartman 
709a6afd9f3SGreg Kroah-Hartman /* Enable downlink interrupts */
enable_transmit_dl(enum port_type port,struct nozomi * dc)710a6afd9f3SGreg Kroah-Hartman static void enable_transmit_dl(enum port_type port, struct nozomi *dc)
711a6afd9f3SGreg Kroah-Hartman {
712a6afd9f3SGreg Kroah-Hartman 	static const u16 mask[] = {MDM_DL, DIAG_DL, APP1_DL, APP2_DL, CTRL_DL};
713a6afd9f3SGreg Kroah-Hartman 
714a6afd9f3SGreg Kroah-Hartman 	if (port < NOZOMI_MAX_PORTS) {
715a6afd9f3SGreg Kroah-Hartman 		dc->last_ier |= mask[port];
716a6afd9f3SGreg Kroah-Hartman 		writew(dc->last_ier, dc->reg_ier);
717a6afd9f3SGreg Kroah-Hartman 	} else {
718a6afd9f3SGreg Kroah-Hartman 		dev_err(&dc->pdev->dev, "Called with wrong port?\n");
719a6afd9f3SGreg Kroah-Hartman 	}
720a6afd9f3SGreg Kroah-Hartman }
721a6afd9f3SGreg Kroah-Hartman 
722a6afd9f3SGreg Kroah-Hartman /* Disable downlink interrupts */
disable_transmit_dl(enum port_type port,struct nozomi * dc)723a6afd9f3SGreg Kroah-Hartman static void disable_transmit_dl(enum port_type port, struct nozomi *dc)
724a6afd9f3SGreg Kroah-Hartman {
725a6afd9f3SGreg Kroah-Hartman 	static const u16 mask[] =
726a6afd9f3SGreg Kroah-Hartman 		{~MDM_DL, ~DIAG_DL, ~APP1_DL, ~APP2_DL, ~CTRL_DL};
727a6afd9f3SGreg Kroah-Hartman 
728a6afd9f3SGreg Kroah-Hartman 	if (port < NOZOMI_MAX_PORTS) {
729a6afd9f3SGreg Kroah-Hartman 		dc->last_ier &= mask[port];
730a6afd9f3SGreg Kroah-Hartman 		writew(dc->last_ier, dc->reg_ier);
731a6afd9f3SGreg Kroah-Hartman 	} else {
732a6afd9f3SGreg Kroah-Hartman 		dev_err(&dc->pdev->dev, "Called with wrong port?\n");
733a6afd9f3SGreg Kroah-Hartman 	}
734a6afd9f3SGreg Kroah-Hartman }
735a6afd9f3SGreg Kroah-Hartman 
736a6afd9f3SGreg Kroah-Hartman /*
737a6afd9f3SGreg Kroah-Hartman  * Return 1 - send buffer to card and ack.
738a6afd9f3SGreg Kroah-Hartman  * Return 0 - don't ack, don't send buffer to card.
739a6afd9f3SGreg Kroah-Hartman  */
send_data(enum port_type index,struct nozomi * dc)740a6afd9f3SGreg Kroah-Hartman static int send_data(enum port_type index, struct nozomi *dc)
741a6afd9f3SGreg Kroah-Hartman {
742a6afd9f3SGreg Kroah-Hartman 	u32 size = 0;
743a6afd9f3SGreg Kroah-Hartman 	struct port *port = &dc->port[index];
744a6afd9f3SGreg Kroah-Hartman 	const u8 toggle = port->toggle_ul;
745a6afd9f3SGreg Kroah-Hartman 	void __iomem *addr = port->ul_addr[toggle];
746a6afd9f3SGreg Kroah-Hartman 	const u32 ul_size = port->ul_size[toggle];
747a6afd9f3SGreg Kroah-Hartman 
748a6afd9f3SGreg Kroah-Hartman 	/* Get data from tty and place in buf for now */
749a6afd9f3SGreg Kroah-Hartman 	size = kfifo_out(&port->fifo_ul, dc->send_buf,
750a6afd9f3SGreg Kroah-Hartman 			   ul_size < SEND_BUF_MAX ? ul_size : SEND_BUF_MAX);
751a6afd9f3SGreg Kroah-Hartman 
752a6afd9f3SGreg Kroah-Hartman 	if (size == 0) {
753a6afd9f3SGreg Kroah-Hartman 		DBG4("No more data to send, disable link:");
754a6afd9f3SGreg Kroah-Hartman 		return 0;
755a6afd9f3SGreg Kroah-Hartman 	}
756a6afd9f3SGreg Kroah-Hartman 
757a6afd9f3SGreg Kroah-Hartman 	/* DUMP(buf, size); */
758a6afd9f3SGreg Kroah-Hartman 
759a6afd9f3SGreg Kroah-Hartman 	/* Write length + data */
760a6afd9f3SGreg Kroah-Hartman 	write_mem32(addr, (u32 *) &size, 4);
761a6afd9f3SGreg Kroah-Hartman 	write_mem32(addr + 4, (u32 *) dc->send_buf, size);
762a6afd9f3SGreg Kroah-Hartman 
7636aad04f2SJiri Slaby 	tty_port_tty_wakeup(&port->port);
764a6afd9f3SGreg Kroah-Hartman 
765a6afd9f3SGreg Kroah-Hartman 	return 1;
766a6afd9f3SGreg Kroah-Hartman }
767a6afd9f3SGreg Kroah-Hartman 
768a6afd9f3SGreg Kroah-Hartman /* If all data has been read, return 1, else 0 */
receive_data(enum port_type index,struct nozomi * dc)769a6afd9f3SGreg Kroah-Hartman static int receive_data(enum port_type index, struct nozomi *dc)
770a6afd9f3SGreg Kroah-Hartman {
771a6afd9f3SGreg Kroah-Hartman 	u8 buf[RECEIVE_BUF_MAX] = { 0 };
772a6afd9f3SGreg Kroah-Hartman 	int size;
773a6afd9f3SGreg Kroah-Hartman 	u32 offset = 4;
774a6afd9f3SGreg Kroah-Hartman 	struct port *port = &dc->port[index];
775a6afd9f3SGreg Kroah-Hartman 	void __iomem *addr = port->dl_addr[port->toggle_dl];
776a6afd9f3SGreg Kroah-Hartman 	struct tty_struct *tty = tty_port_tty_get(&port->port);
777a6afd9f3SGreg Kroah-Hartman 	int i, ret;
778a6afd9f3SGreg Kroah-Hartman 
779a4f642a8SArnd Bergmann 	size = __le32_to_cpu(readl(addr));
780a6afd9f3SGreg Kroah-Hartman 
78197ef38b8SPeter Hurley 	if (tty && tty_throttled(tty)) {
782a6afd9f3SGreg Kroah-Hartman 		DBG1("No room in tty, don't read data, don't ack interrupt, "
783a6afd9f3SGreg Kroah-Hartman 			"disable interrupt");
784a6afd9f3SGreg Kroah-Hartman 
785a6afd9f3SGreg Kroah-Hartman 		/* disable interrupt in downlink... */
786a6afd9f3SGreg Kroah-Hartman 		disable_transmit_dl(index, dc);
787a6afd9f3SGreg Kroah-Hartman 		ret = 0;
788a6afd9f3SGreg Kroah-Hartman 		goto put;
789a6afd9f3SGreg Kroah-Hartman 	}
790a6afd9f3SGreg Kroah-Hartman 
791a6afd9f3SGreg Kroah-Hartman 	if (unlikely(size == 0)) {
792a6afd9f3SGreg Kroah-Hartman 		dev_err(&dc->pdev->dev, "size == 0?\n");
793a6afd9f3SGreg Kroah-Hartman 		ret = 1;
794a6afd9f3SGreg Kroah-Hartman 		goto put;
795a6afd9f3SGreg Kroah-Hartman 	}
796a6afd9f3SGreg Kroah-Hartman 
797a6afd9f3SGreg Kroah-Hartman 	while (size > 0) {
798a6afd9f3SGreg Kroah-Hartman 		read_mem32((u32 *) buf, addr + offset, RECEIVE_BUF_MAX);
799a6afd9f3SGreg Kroah-Hartman 
800a6afd9f3SGreg Kroah-Hartman 		if (size == 1) {
80192a19f9cSJiri Slaby 			tty_insert_flip_char(&port->port, buf[0], TTY_NORMAL);
802a6afd9f3SGreg Kroah-Hartman 			size = 0;
803a6afd9f3SGreg Kroah-Hartman 		} else if (size < RECEIVE_BUF_MAX) {
80405c7cd39SJiri Slaby 			size -= tty_insert_flip_string(&port->port,
80505c7cd39SJiri Slaby 					(char *)buf, size);
806a6afd9f3SGreg Kroah-Hartman 		} else {
80705c7cd39SJiri Slaby 			i = tty_insert_flip_string(&port->port,
808a6afd9f3SGreg Kroah-Hartman 					(char *)buf, RECEIVE_BUF_MAX);
809a6afd9f3SGreg Kroah-Hartman 			size -= i;
810a6afd9f3SGreg Kroah-Hartman 			offset += i;
811a6afd9f3SGreg Kroah-Hartman 		}
812a6afd9f3SGreg Kroah-Hartman 	}
813a6afd9f3SGreg Kroah-Hartman 
814a6afd9f3SGreg Kroah-Hartman 	set_bit(index, &dc->flip);
815a6afd9f3SGreg Kroah-Hartman 	ret = 1;
816a6afd9f3SGreg Kroah-Hartman put:
817a6afd9f3SGreg Kroah-Hartman 	tty_kref_put(tty);
818a6afd9f3SGreg Kroah-Hartman 	return ret;
819a6afd9f3SGreg Kroah-Hartman }
820a6afd9f3SGreg Kroah-Hartman 
821a6afd9f3SGreg Kroah-Hartman /* Debug for interrupts */
822a6afd9f3SGreg Kroah-Hartman #ifdef DEBUG
interrupt2str(u16 interrupt)823a6afd9f3SGreg Kroah-Hartman static char *interrupt2str(u16 interrupt)
824a6afd9f3SGreg Kroah-Hartman {
825a6afd9f3SGreg Kroah-Hartman 	static char buf[TMP_BUF_MAX];
826a6afd9f3SGreg Kroah-Hartman 	char *p = buf;
827a6afd9f3SGreg Kroah-Hartman 
828caa47cc6STakashi Iwai 	if (interrupt & MDM_DL1)
829caa47cc6STakashi Iwai 		p += scnprintf(p, TMP_BUF_MAX, "MDM_DL1 ");
830caa47cc6STakashi Iwai 	if (interrupt & MDM_DL2)
831caa47cc6STakashi Iwai 		p += scnprintf(p, TMP_BUF_MAX - (p - buf), "MDM_DL2 ");
832caa47cc6STakashi Iwai 	if (interrupt & MDM_UL1)
833caa47cc6STakashi Iwai 		p += scnprintf(p, TMP_BUF_MAX - (p - buf), "MDM_UL1 ");
834caa47cc6STakashi Iwai 	if (interrupt & MDM_UL2)
835caa47cc6STakashi Iwai 		p += scnprintf(p, TMP_BUF_MAX - (p - buf), "MDM_UL2 ");
836caa47cc6STakashi Iwai 	if (interrupt & DIAG_DL1)
837caa47cc6STakashi Iwai 		p += scnprintf(p, TMP_BUF_MAX - (p - buf), "DIAG_DL1 ");
838caa47cc6STakashi Iwai 	if (interrupt & DIAG_DL2)
839caa47cc6STakashi Iwai 		p += scnprintf(p, TMP_BUF_MAX - (p - buf), "DIAG_DL2 ");
840a6afd9f3SGreg Kroah-Hartman 
841caa47cc6STakashi Iwai 	if (interrupt & DIAG_UL)
842caa47cc6STakashi Iwai 		p += scnprintf(p, TMP_BUF_MAX - (p - buf), "DIAG_UL ");
843a6afd9f3SGreg Kroah-Hartman 
844caa47cc6STakashi Iwai 	if (interrupt & APP1_DL)
845caa47cc6STakashi Iwai 		p += scnprintf(p, TMP_BUF_MAX - (p - buf), "APP1_DL ");
846caa47cc6STakashi Iwai 	if (interrupt & APP2_DL)
847caa47cc6STakashi Iwai 		p += scnprintf(p, TMP_BUF_MAX - (p - buf), "APP2_DL ");
848a6afd9f3SGreg Kroah-Hartman 
849caa47cc6STakashi Iwai 	if (interrupt & APP1_UL)
850caa47cc6STakashi Iwai 		p += scnprintf(p, TMP_BUF_MAX - (p - buf), "APP1_UL ");
851caa47cc6STakashi Iwai 	if (interrupt & APP2_UL)
852caa47cc6STakashi Iwai 		p += scnprintf(p, TMP_BUF_MAX - (p - buf), "APP2_UL ");
853a6afd9f3SGreg Kroah-Hartman 
854caa47cc6STakashi Iwai 	if (interrupt & CTRL_DL)
855caa47cc6STakashi Iwai 		p += scnprintf(p, TMP_BUF_MAX - (p - buf), "CTRL_DL ");
856caa47cc6STakashi Iwai 	if (interrupt & CTRL_UL)
857caa47cc6STakashi Iwai 		p += scnprintf(p, TMP_BUF_MAX - (p - buf), "CTRL_UL ");
858a6afd9f3SGreg Kroah-Hartman 
859caa47cc6STakashi Iwai 	if (interrupt & RESET)
860caa47cc6STakashi Iwai 		p += scnprintf(p, TMP_BUF_MAX - (p - buf), "RESET ");
861a6afd9f3SGreg Kroah-Hartman 
862a6afd9f3SGreg Kroah-Hartman 	return buf;
863a6afd9f3SGreg Kroah-Hartman }
864a6afd9f3SGreg Kroah-Hartman #endif
865a6afd9f3SGreg Kroah-Hartman 
866a6afd9f3SGreg Kroah-Hartman /*
867a6afd9f3SGreg Kroah-Hartman  * Receive flow control
868a6afd9f3SGreg Kroah-Hartman  * Return 1 - If ok, else 0
869a6afd9f3SGreg Kroah-Hartman  */
receive_flow_control(struct nozomi * dc)870a6afd9f3SGreg Kroah-Hartman static int receive_flow_control(struct nozomi *dc)
871a6afd9f3SGreg Kroah-Hartman {
872a6afd9f3SGreg Kroah-Hartman 	enum port_type port = PORT_MDM;
873a6afd9f3SGreg Kroah-Hartman 	struct ctrl_dl ctrl_dl;
874a6afd9f3SGreg Kroah-Hartman 	struct ctrl_dl old_ctrl;
875a6afd9f3SGreg Kroah-Hartman 	u16 enable_ier = 0;
876a6afd9f3SGreg Kroah-Hartman 
877a6afd9f3SGreg Kroah-Hartman 	read_mem32((u32 *) &ctrl_dl, dc->port[PORT_CTRL].dl_addr[CH_A], 2);
878a6afd9f3SGreg Kroah-Hartman 
879a6afd9f3SGreg Kroah-Hartman 	switch (ctrl_dl.port) {
880a6afd9f3SGreg Kroah-Hartman 	case CTRL_CMD:
881a6afd9f3SGreg Kroah-Hartman 		DBG1("The Base Band sends this value as a response to a "
882a6afd9f3SGreg Kroah-Hartman 			"request for IMSI detach sent over the control "
883a6afd9f3SGreg Kroah-Hartman 			"channel uplink (see section 7.6.1).");
884a6afd9f3SGreg Kroah-Hartman 		break;
885a6afd9f3SGreg Kroah-Hartman 	case CTRL_MDM:
886a6afd9f3SGreg Kroah-Hartman 		port = PORT_MDM;
887a6afd9f3SGreg Kroah-Hartman 		enable_ier = MDM_DL;
888a6afd9f3SGreg Kroah-Hartman 		break;
889a6afd9f3SGreg Kroah-Hartman 	case CTRL_DIAG:
890a6afd9f3SGreg Kroah-Hartman 		port = PORT_DIAG;
891a6afd9f3SGreg Kroah-Hartman 		enable_ier = DIAG_DL;
892a6afd9f3SGreg Kroah-Hartman 		break;
893a6afd9f3SGreg Kroah-Hartman 	case CTRL_APP1:
894a6afd9f3SGreg Kroah-Hartman 		port = PORT_APP1;
895a6afd9f3SGreg Kroah-Hartman 		enable_ier = APP1_DL;
896a6afd9f3SGreg Kroah-Hartman 		break;
897a6afd9f3SGreg Kroah-Hartman 	case CTRL_APP2:
898a6afd9f3SGreg Kroah-Hartman 		port = PORT_APP2;
899a6afd9f3SGreg Kroah-Hartman 		enable_ier = APP2_DL;
900a6afd9f3SGreg Kroah-Hartman 		if (dc->state == NOZOMI_STATE_ALLOCATED) {
901a6afd9f3SGreg Kroah-Hartman 			/*
902a6afd9f3SGreg Kroah-Hartman 			 * After card initialization the flow control
903a6afd9f3SGreg Kroah-Hartman 			 * received for APP2 is always the last
904a6afd9f3SGreg Kroah-Hartman 			 */
905a6afd9f3SGreg Kroah-Hartman 			dc->state = NOZOMI_STATE_READY;
906a6afd9f3SGreg Kroah-Hartman 			dev_info(&dc->pdev->dev, "Device READY!\n");
907a6afd9f3SGreg Kroah-Hartman 		}
908a6afd9f3SGreg Kroah-Hartman 		break;
909a6afd9f3SGreg Kroah-Hartman 	default:
910a6afd9f3SGreg Kroah-Hartman 		dev_err(&dc->pdev->dev,
911a6afd9f3SGreg Kroah-Hartman 			"ERROR: flow control received for non-existing port\n");
912a6afd9f3SGreg Kroah-Hartman 		return 0;
913fc811472SJoe Perches 	}
914a6afd9f3SGreg Kroah-Hartman 
915a6afd9f3SGreg Kroah-Hartman 	DBG1("0x%04X->0x%04X", *((u16 *)&dc->port[port].ctrl_dl),
916a6afd9f3SGreg Kroah-Hartman 	   *((u16 *)&ctrl_dl));
917a6afd9f3SGreg Kroah-Hartman 
918a6afd9f3SGreg Kroah-Hartman 	old_ctrl = dc->port[port].ctrl_dl;
919a6afd9f3SGreg Kroah-Hartman 	dc->port[port].ctrl_dl = ctrl_dl;
920a6afd9f3SGreg Kroah-Hartman 
921a6afd9f3SGreg Kroah-Hartman 	if (old_ctrl.CTS == 1 && ctrl_dl.CTS == 0) {
922a6afd9f3SGreg Kroah-Hartman 		DBG1("Disable interrupt (0x%04X) on port: %d",
923a6afd9f3SGreg Kroah-Hartman 			enable_ier, port);
924a6afd9f3SGreg Kroah-Hartman 		disable_transmit_ul(port, dc);
925a6afd9f3SGreg Kroah-Hartman 
926a6afd9f3SGreg Kroah-Hartman 	} else if (old_ctrl.CTS == 0 && ctrl_dl.CTS == 1) {
927a6afd9f3SGreg Kroah-Hartman 
928a6afd9f3SGreg Kroah-Hartman 		if (kfifo_len(&dc->port[port].fifo_ul)) {
929a6afd9f3SGreg Kroah-Hartman 			DBG1("Enable interrupt (0x%04X) on port: %d",
930a6afd9f3SGreg Kroah-Hartman 				enable_ier, port);
931a6afd9f3SGreg Kroah-Hartman 			DBG1("Data in buffer [%d], enable transmit! ",
932a6afd9f3SGreg Kroah-Hartman 				kfifo_len(&dc->port[port].fifo_ul));
933a6afd9f3SGreg Kroah-Hartman 			enable_transmit_ul(port, dc);
934a6afd9f3SGreg Kroah-Hartman 		} else {
935a6afd9f3SGreg Kroah-Hartman 			DBG1("No data in buffer...");
936a6afd9f3SGreg Kroah-Hartman 		}
937a6afd9f3SGreg Kroah-Hartman 	}
938a6afd9f3SGreg Kroah-Hartman 
939a6afd9f3SGreg Kroah-Hartman 	if (*(u16 *)&old_ctrl == *(u16 *)&ctrl_dl) {
940a6afd9f3SGreg Kroah-Hartman 		DBG1(" No change in mctrl");
941a6afd9f3SGreg Kroah-Hartman 		return 1;
942a6afd9f3SGreg Kroah-Hartman 	}
943a6afd9f3SGreg Kroah-Hartman 	/* Update statistics */
944a6afd9f3SGreg Kroah-Hartman 	if (old_ctrl.CTS != ctrl_dl.CTS)
945a6afd9f3SGreg Kroah-Hartman 		dc->port[port].tty_icount.cts++;
946a6afd9f3SGreg Kroah-Hartman 	if (old_ctrl.DSR != ctrl_dl.DSR)
947a6afd9f3SGreg Kroah-Hartman 		dc->port[port].tty_icount.dsr++;
948a6afd9f3SGreg Kroah-Hartman 	if (old_ctrl.RI != ctrl_dl.RI)
949a6afd9f3SGreg Kroah-Hartman 		dc->port[port].tty_icount.rng++;
950a6afd9f3SGreg Kroah-Hartman 	if (old_ctrl.DCD != ctrl_dl.DCD)
951a6afd9f3SGreg Kroah-Hartman 		dc->port[port].tty_icount.dcd++;
952a6afd9f3SGreg Kroah-Hartman 
953a6afd9f3SGreg Kroah-Hartman 	wake_up_interruptible(&dc->port[port].tty_wait);
954a6afd9f3SGreg Kroah-Hartman 
955a6afd9f3SGreg Kroah-Hartman 	DBG1("port: %d DCD(%d), CTS(%d), RI(%d), DSR(%d)",
956a6afd9f3SGreg Kroah-Hartman 	   port,
957a6afd9f3SGreg Kroah-Hartman 	   dc->port[port].tty_icount.dcd, dc->port[port].tty_icount.cts,
958a6afd9f3SGreg Kroah-Hartman 	   dc->port[port].tty_icount.rng, dc->port[port].tty_icount.dsr);
959a6afd9f3SGreg Kroah-Hartman 
960a6afd9f3SGreg Kroah-Hartman 	return 1;
961a6afd9f3SGreg Kroah-Hartman }
962a6afd9f3SGreg Kroah-Hartman 
port2ctrl(enum port_type port,const struct nozomi * dc)963a6afd9f3SGreg Kroah-Hartman static enum ctrl_port_type port2ctrl(enum port_type port,
964a6afd9f3SGreg Kroah-Hartman 					const struct nozomi *dc)
965a6afd9f3SGreg Kroah-Hartman {
966a6afd9f3SGreg Kroah-Hartman 	switch (port) {
967a6afd9f3SGreg Kroah-Hartman 	case PORT_MDM:
968a6afd9f3SGreg Kroah-Hartman 		return CTRL_MDM;
969a6afd9f3SGreg Kroah-Hartman 	case PORT_DIAG:
970a6afd9f3SGreg Kroah-Hartman 		return CTRL_DIAG;
971a6afd9f3SGreg Kroah-Hartman 	case PORT_APP1:
972a6afd9f3SGreg Kroah-Hartman 		return CTRL_APP1;
973a6afd9f3SGreg Kroah-Hartman 	case PORT_APP2:
974a6afd9f3SGreg Kroah-Hartman 		return CTRL_APP2;
975a6afd9f3SGreg Kroah-Hartman 	default:
976a6afd9f3SGreg Kroah-Hartman 		dev_err(&dc->pdev->dev,
977a6afd9f3SGreg Kroah-Hartman 			"ERROR: send flow control " \
978a6afd9f3SGreg Kroah-Hartman 			"received for non-existing port\n");
979fc811472SJoe Perches 	}
980a6afd9f3SGreg Kroah-Hartman 	return CTRL_ERROR;
981a6afd9f3SGreg Kroah-Hartman }
982a6afd9f3SGreg Kroah-Hartman 
983a6afd9f3SGreg Kroah-Hartman /*
984a6afd9f3SGreg Kroah-Hartman  * Send flow control, can only update one channel at a time
985a6afd9f3SGreg Kroah-Hartman  * Return 0 - If we have updated all flow control
986a6afd9f3SGreg Kroah-Hartman  * Return 1 - If we need to update more flow control, ack current enable more
987a6afd9f3SGreg Kroah-Hartman  */
send_flow_control(struct nozomi * dc)988a6afd9f3SGreg Kroah-Hartman static int send_flow_control(struct nozomi *dc)
989a6afd9f3SGreg Kroah-Hartman {
990a6afd9f3SGreg Kroah-Hartman 	u32 i, more_flow_control_to_be_updated = 0;
991a6afd9f3SGreg Kroah-Hartman 	u16 *ctrl;
992a6afd9f3SGreg Kroah-Hartman 
993a6afd9f3SGreg Kroah-Hartman 	for (i = PORT_MDM; i < MAX_PORT; i++) {
994a6afd9f3SGreg Kroah-Hartman 		if (dc->port[i].update_flow_control) {
995a6afd9f3SGreg Kroah-Hartman 			if (more_flow_control_to_be_updated) {
996a6afd9f3SGreg Kroah-Hartman 				/* We have more flow control to be updated */
997a6afd9f3SGreg Kroah-Hartman 				return 1;
998a6afd9f3SGreg Kroah-Hartman 			}
999a6afd9f3SGreg Kroah-Hartman 			dc->port[i].ctrl_ul.port = port2ctrl(i, dc);
1000a6afd9f3SGreg Kroah-Hartman 			ctrl = (u16 *)&dc->port[i].ctrl_ul;
1001a6afd9f3SGreg Kroah-Hartman 			write_mem32(dc->port[PORT_CTRL].ul_addr[0], \
1002a6afd9f3SGreg Kroah-Hartman 				(u32 *) ctrl, 2);
1003a6afd9f3SGreg Kroah-Hartman 			dc->port[i].update_flow_control = 0;
1004a6afd9f3SGreg Kroah-Hartman 			more_flow_control_to_be_updated = 1;
1005a6afd9f3SGreg Kroah-Hartman 		}
1006a6afd9f3SGreg Kroah-Hartman 	}
1007a6afd9f3SGreg Kroah-Hartman 	return 0;
1008a6afd9f3SGreg Kroah-Hartman }
1009a6afd9f3SGreg Kroah-Hartman 
1010a6afd9f3SGreg Kroah-Hartman /*
1011a6afd9f3SGreg Kroah-Hartman  * Handle downlink data, ports that are handled are modem and diagnostics
1012a6afd9f3SGreg Kroah-Hartman  * Return 1 - ok
1013a6afd9f3SGreg Kroah-Hartman  * Return 0 - toggle fields are out of sync
1014a6afd9f3SGreg Kroah-Hartman  */
handle_data_dl(struct nozomi * dc,enum port_type port,u8 * toggle,u16 read_iir,u16 mask1,u16 mask2)1015a6afd9f3SGreg Kroah-Hartman static int handle_data_dl(struct nozomi *dc, enum port_type port, u8 *toggle,
1016a6afd9f3SGreg Kroah-Hartman 			u16 read_iir, u16 mask1, u16 mask2)
1017a6afd9f3SGreg Kroah-Hartman {
1018a6afd9f3SGreg Kroah-Hartman 	if (*toggle == 0 && read_iir & mask1) {
1019a6afd9f3SGreg Kroah-Hartman 		if (receive_data(port, dc)) {
1020a6afd9f3SGreg Kroah-Hartman 			writew(mask1, dc->reg_fcr);
1021a6afd9f3SGreg Kroah-Hartman 			*toggle = !(*toggle);
1022a6afd9f3SGreg Kroah-Hartman 		}
1023a6afd9f3SGreg Kroah-Hartman 
1024a6afd9f3SGreg Kroah-Hartman 		if (read_iir & mask2) {
1025a6afd9f3SGreg Kroah-Hartman 			if (receive_data(port, dc)) {
1026a6afd9f3SGreg Kroah-Hartman 				writew(mask2, dc->reg_fcr);
1027a6afd9f3SGreg Kroah-Hartman 				*toggle = !(*toggle);
1028a6afd9f3SGreg Kroah-Hartman 			}
1029a6afd9f3SGreg Kroah-Hartman 		}
1030a6afd9f3SGreg Kroah-Hartman 	} else if (*toggle == 1 && read_iir & mask2) {
1031a6afd9f3SGreg Kroah-Hartman 		if (receive_data(port, dc)) {
1032a6afd9f3SGreg Kroah-Hartman 			writew(mask2, dc->reg_fcr);
1033a6afd9f3SGreg Kroah-Hartman 			*toggle = !(*toggle);
1034a6afd9f3SGreg Kroah-Hartman 		}
1035a6afd9f3SGreg Kroah-Hartman 
1036a6afd9f3SGreg Kroah-Hartman 		if (read_iir & mask1) {
1037a6afd9f3SGreg Kroah-Hartman 			if (receive_data(port, dc)) {
1038a6afd9f3SGreg Kroah-Hartman 				writew(mask1, dc->reg_fcr);
1039a6afd9f3SGreg Kroah-Hartman 				*toggle = !(*toggle);
1040a6afd9f3SGreg Kroah-Hartman 			}
1041a6afd9f3SGreg Kroah-Hartman 		}
1042a6afd9f3SGreg Kroah-Hartman 	} else {
1043a6afd9f3SGreg Kroah-Hartman 		dev_err(&dc->pdev->dev, "port out of sync!, toggle:%d\n",
1044a6afd9f3SGreg Kroah-Hartman 			*toggle);
1045a6afd9f3SGreg Kroah-Hartman 		return 0;
1046a6afd9f3SGreg Kroah-Hartman 	}
1047a6afd9f3SGreg Kroah-Hartman 	return 1;
1048a6afd9f3SGreg Kroah-Hartman }
1049a6afd9f3SGreg Kroah-Hartman 
1050a6afd9f3SGreg Kroah-Hartman /*
1051a6afd9f3SGreg Kroah-Hartman  * Handle uplink data, this is currently for the modem port
1052a6afd9f3SGreg Kroah-Hartman  * Return 1 - ok
1053a6afd9f3SGreg Kroah-Hartman  * Return 0 - toggle field are out of sync
1054a6afd9f3SGreg Kroah-Hartman  */
handle_data_ul(struct nozomi * dc,enum port_type port,u16 read_iir)1055a6afd9f3SGreg Kroah-Hartman static int handle_data_ul(struct nozomi *dc, enum port_type port, u16 read_iir)
1056a6afd9f3SGreg Kroah-Hartman {
1057a6afd9f3SGreg Kroah-Hartman 	u8 *toggle = &(dc->port[port].toggle_ul);
1058a6afd9f3SGreg Kroah-Hartman 
1059a6afd9f3SGreg Kroah-Hartman 	if (*toggle == 0 && read_iir & MDM_UL1) {
1060a6afd9f3SGreg Kroah-Hartman 		dc->last_ier &= ~MDM_UL;
1061a6afd9f3SGreg Kroah-Hartman 		writew(dc->last_ier, dc->reg_ier);
1062a6afd9f3SGreg Kroah-Hartman 		if (send_data(port, dc)) {
1063a6afd9f3SGreg Kroah-Hartman 			writew(MDM_UL1, dc->reg_fcr);
1064a6afd9f3SGreg Kroah-Hartman 			dc->last_ier = dc->last_ier | MDM_UL;
1065a6afd9f3SGreg Kroah-Hartman 			writew(dc->last_ier, dc->reg_ier);
1066a6afd9f3SGreg Kroah-Hartman 			*toggle = !*toggle;
1067a6afd9f3SGreg Kroah-Hartman 		}
1068a6afd9f3SGreg Kroah-Hartman 
1069a6afd9f3SGreg Kroah-Hartman 		if (read_iir & MDM_UL2) {
1070a6afd9f3SGreg Kroah-Hartman 			dc->last_ier &= ~MDM_UL;
1071a6afd9f3SGreg Kroah-Hartman 			writew(dc->last_ier, dc->reg_ier);
1072a6afd9f3SGreg Kroah-Hartman 			if (send_data(port, dc)) {
1073a6afd9f3SGreg Kroah-Hartman 				writew(MDM_UL2, dc->reg_fcr);
1074a6afd9f3SGreg Kroah-Hartman 				dc->last_ier = dc->last_ier | MDM_UL;
1075a6afd9f3SGreg Kroah-Hartman 				writew(dc->last_ier, dc->reg_ier);
1076a6afd9f3SGreg Kroah-Hartman 				*toggle = !*toggle;
1077a6afd9f3SGreg Kroah-Hartman 			}
1078a6afd9f3SGreg Kroah-Hartman 		}
1079a6afd9f3SGreg Kroah-Hartman 
1080a6afd9f3SGreg Kroah-Hartman 	} else if (*toggle == 1 && read_iir & MDM_UL2) {
1081a6afd9f3SGreg Kroah-Hartman 		dc->last_ier &= ~MDM_UL;
1082a6afd9f3SGreg Kroah-Hartman 		writew(dc->last_ier, dc->reg_ier);
1083a6afd9f3SGreg Kroah-Hartman 		if (send_data(port, dc)) {
1084a6afd9f3SGreg Kroah-Hartman 			writew(MDM_UL2, dc->reg_fcr);
1085a6afd9f3SGreg Kroah-Hartman 			dc->last_ier = dc->last_ier | MDM_UL;
1086a6afd9f3SGreg Kroah-Hartman 			writew(dc->last_ier, dc->reg_ier);
1087a6afd9f3SGreg Kroah-Hartman 			*toggle = !*toggle;
1088a6afd9f3SGreg Kroah-Hartman 		}
1089a6afd9f3SGreg Kroah-Hartman 
1090a6afd9f3SGreg Kroah-Hartman 		if (read_iir & MDM_UL1) {
1091a6afd9f3SGreg Kroah-Hartman 			dc->last_ier &= ~MDM_UL;
1092a6afd9f3SGreg Kroah-Hartman 			writew(dc->last_ier, dc->reg_ier);
1093a6afd9f3SGreg Kroah-Hartman 			if (send_data(port, dc)) {
1094a6afd9f3SGreg Kroah-Hartman 				writew(MDM_UL1, dc->reg_fcr);
1095a6afd9f3SGreg Kroah-Hartman 				dc->last_ier = dc->last_ier | MDM_UL;
1096a6afd9f3SGreg Kroah-Hartman 				writew(dc->last_ier, dc->reg_ier);
1097a6afd9f3SGreg Kroah-Hartman 				*toggle = !*toggle;
1098a6afd9f3SGreg Kroah-Hartman 			}
1099a6afd9f3SGreg Kroah-Hartman 		}
1100a6afd9f3SGreg Kroah-Hartman 	} else {
1101a6afd9f3SGreg Kroah-Hartman 		writew(read_iir & MDM_UL, dc->reg_fcr);
1102a6afd9f3SGreg Kroah-Hartman 		dev_err(&dc->pdev->dev, "port out of sync!\n");
1103a6afd9f3SGreg Kroah-Hartman 		return 0;
1104a6afd9f3SGreg Kroah-Hartman 	}
1105a6afd9f3SGreg Kroah-Hartman 	return 1;
1106a6afd9f3SGreg Kroah-Hartman }
1107a6afd9f3SGreg Kroah-Hartman 
interrupt_handler(int irq,void * dev_id)1108a6afd9f3SGreg Kroah-Hartman static irqreturn_t interrupt_handler(int irq, void *dev_id)
1109a6afd9f3SGreg Kroah-Hartman {
1110a6afd9f3SGreg Kroah-Hartman 	struct nozomi *dc = dev_id;
1111a6afd9f3SGreg Kroah-Hartman 	unsigned int a;
1112a6afd9f3SGreg Kroah-Hartman 	u16 read_iir;
1113a6afd9f3SGreg Kroah-Hartman 
1114a6afd9f3SGreg Kroah-Hartman 	if (!dc)
1115a6afd9f3SGreg Kroah-Hartman 		return IRQ_NONE;
1116a6afd9f3SGreg Kroah-Hartman 
1117a6afd9f3SGreg Kroah-Hartman 	spin_lock(&dc->spin_mutex);
1118a6afd9f3SGreg Kroah-Hartman 	read_iir = readw(dc->reg_iir);
1119a6afd9f3SGreg Kroah-Hartman 
1120a6afd9f3SGreg Kroah-Hartman 	/* Card removed */
1121a6afd9f3SGreg Kroah-Hartman 	if (read_iir == (u16)-1)
1122a6afd9f3SGreg Kroah-Hartman 		goto none;
1123a6afd9f3SGreg Kroah-Hartman 	/*
1124a6afd9f3SGreg Kroah-Hartman 	 * Just handle interrupt enabled in IER
1125a6afd9f3SGreg Kroah-Hartman 	 * (by masking with dc->last_ier)
1126a6afd9f3SGreg Kroah-Hartman 	 */
1127a6afd9f3SGreg Kroah-Hartman 	read_iir &= dc->last_ier;
1128a6afd9f3SGreg Kroah-Hartman 
1129a6afd9f3SGreg Kroah-Hartman 	if (read_iir == 0)
1130a6afd9f3SGreg Kroah-Hartman 		goto none;
1131a6afd9f3SGreg Kroah-Hartman 
1132a6afd9f3SGreg Kroah-Hartman 
1133a6afd9f3SGreg Kroah-Hartman 	DBG4("%s irq:0x%04X, prev:0x%04X", interrupt2str(read_iir), read_iir,
1134a6afd9f3SGreg Kroah-Hartman 		dc->last_ier);
1135a6afd9f3SGreg Kroah-Hartman 
1136a6afd9f3SGreg Kroah-Hartman 	if (read_iir & RESET) {
1137a6afd9f3SGreg Kroah-Hartman 		if (unlikely(!nozomi_read_config_table(dc))) {
1138a6afd9f3SGreg Kroah-Hartman 			dc->last_ier = 0x0;
1139a6afd9f3SGreg Kroah-Hartman 			writew(dc->last_ier, dc->reg_ier);
1140a6afd9f3SGreg Kroah-Hartman 			dev_err(&dc->pdev->dev, "Could not read status from "
1141a6afd9f3SGreg Kroah-Hartman 				"card, we should disable interface\n");
1142a6afd9f3SGreg Kroah-Hartman 		} else {
1143a6afd9f3SGreg Kroah-Hartman 			writew(RESET, dc->reg_fcr);
1144a6afd9f3SGreg Kroah-Hartman 		}
1145a6afd9f3SGreg Kroah-Hartman 		/* No more useful info if this was the reset interrupt. */
1146a6afd9f3SGreg Kroah-Hartman 		goto exit_handler;
1147a6afd9f3SGreg Kroah-Hartman 	}
1148a6afd9f3SGreg Kroah-Hartman 	if (read_iir & CTRL_UL) {
1149a6afd9f3SGreg Kroah-Hartman 		DBG1("CTRL_UL");
1150a6afd9f3SGreg Kroah-Hartman 		dc->last_ier &= ~CTRL_UL;
1151a6afd9f3SGreg Kroah-Hartman 		writew(dc->last_ier, dc->reg_ier);
1152a6afd9f3SGreg Kroah-Hartman 		if (send_flow_control(dc)) {
1153a6afd9f3SGreg Kroah-Hartman 			writew(CTRL_UL, dc->reg_fcr);
1154a6afd9f3SGreg Kroah-Hartman 			dc->last_ier = dc->last_ier | CTRL_UL;
1155a6afd9f3SGreg Kroah-Hartman 			writew(dc->last_ier, dc->reg_ier);
1156a6afd9f3SGreg Kroah-Hartman 		}
1157a6afd9f3SGreg Kroah-Hartman 	}
1158a6afd9f3SGreg Kroah-Hartman 	if (read_iir & CTRL_DL) {
1159a6afd9f3SGreg Kroah-Hartman 		receive_flow_control(dc);
1160a6afd9f3SGreg Kroah-Hartman 		writew(CTRL_DL, dc->reg_fcr);
1161a6afd9f3SGreg Kroah-Hartman 	}
1162a6afd9f3SGreg Kroah-Hartman 	if (read_iir & MDM_DL) {
1163a6afd9f3SGreg Kroah-Hartman 		if (!handle_data_dl(dc, PORT_MDM,
1164a6afd9f3SGreg Kroah-Hartman 				&(dc->port[PORT_MDM].toggle_dl), read_iir,
1165a6afd9f3SGreg Kroah-Hartman 				MDM_DL1, MDM_DL2)) {
1166a6afd9f3SGreg Kroah-Hartman 			dev_err(&dc->pdev->dev, "MDM_DL out of sync!\n");
1167a6afd9f3SGreg Kroah-Hartman 			goto exit_handler;
1168a6afd9f3SGreg Kroah-Hartman 		}
1169a6afd9f3SGreg Kroah-Hartman 	}
1170a6afd9f3SGreg Kroah-Hartman 	if (read_iir & MDM_UL) {
1171a6afd9f3SGreg Kroah-Hartman 		if (!handle_data_ul(dc, PORT_MDM, read_iir)) {
1172a6afd9f3SGreg Kroah-Hartman 			dev_err(&dc->pdev->dev, "MDM_UL out of sync!\n");
1173a6afd9f3SGreg Kroah-Hartman 			goto exit_handler;
1174a6afd9f3SGreg Kroah-Hartman 		}
1175a6afd9f3SGreg Kroah-Hartman 	}
1176a6afd9f3SGreg Kroah-Hartman 	if (read_iir & DIAG_DL) {
1177a6afd9f3SGreg Kroah-Hartman 		if (!handle_data_dl(dc, PORT_DIAG,
1178a6afd9f3SGreg Kroah-Hartman 				&(dc->port[PORT_DIAG].toggle_dl), read_iir,
1179a6afd9f3SGreg Kroah-Hartman 				DIAG_DL1, DIAG_DL2)) {
1180a6afd9f3SGreg Kroah-Hartman 			dev_err(&dc->pdev->dev, "DIAG_DL out of sync!\n");
1181a6afd9f3SGreg Kroah-Hartman 			goto exit_handler;
1182a6afd9f3SGreg Kroah-Hartman 		}
1183a6afd9f3SGreg Kroah-Hartman 	}
1184a6afd9f3SGreg Kroah-Hartman 	if (read_iir & DIAG_UL) {
1185a6afd9f3SGreg Kroah-Hartman 		dc->last_ier &= ~DIAG_UL;
1186a6afd9f3SGreg Kroah-Hartman 		writew(dc->last_ier, dc->reg_ier);
1187a6afd9f3SGreg Kroah-Hartman 		if (send_data(PORT_DIAG, dc)) {
1188a6afd9f3SGreg Kroah-Hartman 			writew(DIAG_UL, dc->reg_fcr);
1189a6afd9f3SGreg Kroah-Hartman 			dc->last_ier = dc->last_ier | DIAG_UL;
1190a6afd9f3SGreg Kroah-Hartman 			writew(dc->last_ier, dc->reg_ier);
1191a6afd9f3SGreg Kroah-Hartman 		}
1192a6afd9f3SGreg Kroah-Hartman 	}
1193a6afd9f3SGreg Kroah-Hartman 	if (read_iir & APP1_DL) {
1194a6afd9f3SGreg Kroah-Hartman 		if (receive_data(PORT_APP1, dc))
1195a6afd9f3SGreg Kroah-Hartman 			writew(APP1_DL, dc->reg_fcr);
1196a6afd9f3SGreg Kroah-Hartman 	}
1197a6afd9f3SGreg Kroah-Hartman 	if (read_iir & APP1_UL) {
1198a6afd9f3SGreg Kroah-Hartman 		dc->last_ier &= ~APP1_UL;
1199a6afd9f3SGreg Kroah-Hartman 		writew(dc->last_ier, dc->reg_ier);
1200a6afd9f3SGreg Kroah-Hartman 		if (send_data(PORT_APP1, dc)) {
1201a6afd9f3SGreg Kroah-Hartman 			writew(APP1_UL, dc->reg_fcr);
1202a6afd9f3SGreg Kroah-Hartman 			dc->last_ier = dc->last_ier | APP1_UL;
1203a6afd9f3SGreg Kroah-Hartman 			writew(dc->last_ier, dc->reg_ier);
1204a6afd9f3SGreg Kroah-Hartman 		}
1205a6afd9f3SGreg Kroah-Hartman 	}
1206a6afd9f3SGreg Kroah-Hartman 	if (read_iir & APP2_DL) {
1207a6afd9f3SGreg Kroah-Hartman 		if (receive_data(PORT_APP2, dc))
1208a6afd9f3SGreg Kroah-Hartman 			writew(APP2_DL, dc->reg_fcr);
1209a6afd9f3SGreg Kroah-Hartman 	}
1210a6afd9f3SGreg Kroah-Hartman 	if (read_iir & APP2_UL) {
1211a6afd9f3SGreg Kroah-Hartman 		dc->last_ier &= ~APP2_UL;
1212a6afd9f3SGreg Kroah-Hartman 		writew(dc->last_ier, dc->reg_ier);
1213a6afd9f3SGreg Kroah-Hartman 		if (send_data(PORT_APP2, dc)) {
1214a6afd9f3SGreg Kroah-Hartman 			writew(APP2_UL, dc->reg_fcr);
1215a6afd9f3SGreg Kroah-Hartman 			dc->last_ier = dc->last_ier | APP2_UL;
1216a6afd9f3SGreg Kroah-Hartman 			writew(dc->last_ier, dc->reg_ier);
1217a6afd9f3SGreg Kroah-Hartman 		}
1218a6afd9f3SGreg Kroah-Hartman 	}
1219a6afd9f3SGreg Kroah-Hartman 
1220a6afd9f3SGreg Kroah-Hartman exit_handler:
1221a6afd9f3SGreg Kroah-Hartman 	spin_unlock(&dc->spin_mutex);
12222e124b4aSJiri Slaby 
12232e124b4aSJiri Slaby 	for (a = 0; a < NOZOMI_MAX_PORTS; a++)
12242e124b4aSJiri Slaby 		if (test_and_clear_bit(a, &dc->flip))
12252e124b4aSJiri Slaby 			tty_flip_buffer_push(&dc->port[a].port);
12262e124b4aSJiri Slaby 
1227a6afd9f3SGreg Kroah-Hartman 	return IRQ_HANDLED;
1228a6afd9f3SGreg Kroah-Hartman none:
1229a6afd9f3SGreg Kroah-Hartman 	spin_unlock(&dc->spin_mutex);
1230a6afd9f3SGreg Kroah-Hartman 	return IRQ_NONE;
1231a6afd9f3SGreg Kroah-Hartman }
1232a6afd9f3SGreg Kroah-Hartman 
nozomi_get_card_type(struct nozomi * dc)1233a6afd9f3SGreg Kroah-Hartman static void nozomi_get_card_type(struct nozomi *dc)
1234a6afd9f3SGreg Kroah-Hartman {
1235a6afd9f3SGreg Kroah-Hartman 	int i;
1236a6afd9f3SGreg Kroah-Hartman 	u32 size = 0;
1237a6afd9f3SGreg Kroah-Hartman 
1238a6afd9f3SGreg Kroah-Hartman 	for (i = 0; i < 6; i++)
1239a6afd9f3SGreg Kroah-Hartman 		size += pci_resource_len(dc->pdev, i);
1240a6afd9f3SGreg Kroah-Hartman 
1241a6afd9f3SGreg Kroah-Hartman 	/* Assume card type F32_8 if no match */
1242a6afd9f3SGreg Kroah-Hartman 	dc->card_type = size == 2048 ? F32_2 : F32_8;
1243a6afd9f3SGreg Kroah-Hartman 
1244a6afd9f3SGreg Kroah-Hartman 	dev_info(&dc->pdev->dev, "Card type is: %d\n", dc->card_type);
1245a6afd9f3SGreg Kroah-Hartman }
1246a6afd9f3SGreg Kroah-Hartman 
nozomi_setup_private_data(struct nozomi * dc)1247a6afd9f3SGreg Kroah-Hartman static void nozomi_setup_private_data(struct nozomi *dc)
1248a6afd9f3SGreg Kroah-Hartman {
1249a6afd9f3SGreg Kroah-Hartman 	void __iomem *offset = dc->base_addr + dc->card_type / 2;
1250a6afd9f3SGreg Kroah-Hartman 	unsigned int i;
1251a6afd9f3SGreg Kroah-Hartman 
1252a6afd9f3SGreg Kroah-Hartman 	dc->reg_fcr = (void __iomem *)(offset + R_FCR);
1253a6afd9f3SGreg Kroah-Hartman 	dc->reg_iir = (void __iomem *)(offset + R_IIR);
1254a6afd9f3SGreg Kroah-Hartman 	dc->reg_ier = (void __iomem *)(offset + R_IER);
1255a6afd9f3SGreg Kroah-Hartman 	dc->last_ier = 0;
1256a6afd9f3SGreg Kroah-Hartman 	dc->flip = 0;
1257a6afd9f3SGreg Kroah-Hartman 
1258a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_MDM].token_dl = MDM_DL;
1259a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_DIAG].token_dl = DIAG_DL;
1260a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_APP1].token_dl = APP1_DL;
1261a6afd9f3SGreg Kroah-Hartman 	dc->port[PORT_APP2].token_dl = APP2_DL;
1262a6afd9f3SGreg Kroah-Hartman 
1263a6afd9f3SGreg Kroah-Hartman 	for (i = 0; i < MAX_PORT; i++)
1264a6afd9f3SGreg Kroah-Hartman 		init_waitqueue_head(&dc->port[i].tty_wait);
1265a6afd9f3SGreg Kroah-Hartman }
1266a6afd9f3SGreg Kroah-Hartman 
card_type_show(struct device * dev,struct device_attribute * attr,char * buf)1267a6afd9f3SGreg Kroah-Hartman static ssize_t card_type_show(struct device *dev, struct device_attribute *attr,
1268a6afd9f3SGreg Kroah-Hartman 			  char *buf)
1269a6afd9f3SGreg Kroah-Hartman {
127018b1345eSChuhong Yuan 	const struct nozomi *dc = dev_get_drvdata(dev);
1271a6afd9f3SGreg Kroah-Hartman 
1272a6afd9f3SGreg Kroah-Hartman 	return sprintf(buf, "%d\n", dc->card_type);
1273a6afd9f3SGreg Kroah-Hartman }
127419467fa1SJulia Lawall static DEVICE_ATTR_RO(card_type);
1275a6afd9f3SGreg Kroah-Hartman 
open_ttys_show(struct device * dev,struct device_attribute * attr,char * buf)1276a6afd9f3SGreg Kroah-Hartman static ssize_t open_ttys_show(struct device *dev, struct device_attribute *attr,
1277a6afd9f3SGreg Kroah-Hartman 			  char *buf)
1278a6afd9f3SGreg Kroah-Hartman {
127918b1345eSChuhong Yuan 	const struct nozomi *dc = dev_get_drvdata(dev);
1280a6afd9f3SGreg Kroah-Hartman 
1281a6afd9f3SGreg Kroah-Hartman 	return sprintf(buf, "%u\n", dc->open_ttys);
1282a6afd9f3SGreg Kroah-Hartman }
128319467fa1SJulia Lawall static DEVICE_ATTR_RO(open_ttys);
1284a6afd9f3SGreg Kroah-Hartman 
make_sysfs_files(struct nozomi * dc)1285a6afd9f3SGreg Kroah-Hartman static void make_sysfs_files(struct nozomi *dc)
1286a6afd9f3SGreg Kroah-Hartman {
1287a6afd9f3SGreg Kroah-Hartman 	if (device_create_file(&dc->pdev->dev, &dev_attr_card_type))
1288a6afd9f3SGreg Kroah-Hartman 		dev_err(&dc->pdev->dev,
1289a6afd9f3SGreg Kroah-Hartman 			"Could not create sysfs file for card_type\n");
1290a6afd9f3SGreg Kroah-Hartman 	if (device_create_file(&dc->pdev->dev, &dev_attr_open_ttys))
1291a6afd9f3SGreg Kroah-Hartman 		dev_err(&dc->pdev->dev,
1292a6afd9f3SGreg Kroah-Hartman 			"Could not create sysfs file for open_ttys\n");
1293a6afd9f3SGreg Kroah-Hartman }
1294a6afd9f3SGreg Kroah-Hartman 
remove_sysfs_files(struct nozomi * dc)1295a6afd9f3SGreg Kroah-Hartman static void remove_sysfs_files(struct nozomi *dc)
1296a6afd9f3SGreg Kroah-Hartman {
1297a6afd9f3SGreg Kroah-Hartman 	device_remove_file(&dc->pdev->dev, &dev_attr_card_type);
1298a6afd9f3SGreg Kroah-Hartman 	device_remove_file(&dc->pdev->dev, &dev_attr_open_ttys);
1299a6afd9f3SGreg Kroah-Hartman }
1300a6afd9f3SGreg Kroah-Hartman 
1301a6afd9f3SGreg Kroah-Hartman /* Allocate memory for one device */
nozomi_card_init(struct pci_dev * pdev,const struct pci_device_id * ent)13029671f099SBill Pemberton static int nozomi_card_init(struct pci_dev *pdev,
1303a6afd9f3SGreg Kroah-Hartman 				      const struct pci_device_id *ent)
1304a6afd9f3SGreg Kroah-Hartman {
1305a6afd9f3SGreg Kroah-Hartman 	int ret;
1306a6afd9f3SGreg Kroah-Hartman 	struct nozomi *dc = NULL;
1307a6afd9f3SGreg Kroah-Hartman 	int ndev_idx;
1308a6afd9f3SGreg Kroah-Hartman 	int i;
1309a6afd9f3SGreg Kroah-Hartman 
1310a6afd9f3SGreg Kroah-Hartman 	for (ndev_idx = 0; ndev_idx < ARRAY_SIZE(ndevs); ndev_idx++)
1311a6afd9f3SGreg Kroah-Hartman 		if (!ndevs[ndev_idx])
1312a6afd9f3SGreg Kroah-Hartman 			break;
1313a6afd9f3SGreg Kroah-Hartman 
1314a6afd9f3SGreg Kroah-Hartman 	if (ndev_idx >= ARRAY_SIZE(ndevs)) {
1315a6afd9f3SGreg Kroah-Hartman 		dev_err(&pdev->dev, "no free tty range for this card left\n");
1316a6afd9f3SGreg Kroah-Hartman 		ret = -EIO;
1317a6afd9f3SGreg Kroah-Hartman 		goto err;
1318a6afd9f3SGreg Kroah-Hartman 	}
1319a6afd9f3SGreg Kroah-Hartman 
1320a6afd9f3SGreg Kroah-Hartman 	dc = kzalloc(sizeof(struct nozomi), GFP_KERNEL);
1321a6afd9f3SGreg Kroah-Hartman 	if (unlikely(!dc)) {
1322a6afd9f3SGreg Kroah-Hartman 		dev_err(&pdev->dev, "Could not allocate memory\n");
1323a6afd9f3SGreg Kroah-Hartman 		ret = -ENOMEM;
1324a6afd9f3SGreg Kroah-Hartman 		goto err_free;
1325a6afd9f3SGreg Kroah-Hartman 	}
1326a6afd9f3SGreg Kroah-Hartman 
1327a6afd9f3SGreg Kroah-Hartman 	dc->pdev = pdev;
1328a6afd9f3SGreg Kroah-Hartman 
1329a6afd9f3SGreg Kroah-Hartman 	ret = pci_enable_device(dc->pdev);
1330a6afd9f3SGreg Kroah-Hartman 	if (ret) {
1331a6afd9f3SGreg Kroah-Hartman 		dev_err(&pdev->dev, "Failed to enable PCI Device\n");
1332a6afd9f3SGreg Kroah-Hartman 		goto err_free;
1333a6afd9f3SGreg Kroah-Hartman 	}
1334a6afd9f3SGreg Kroah-Hartman 
1335a6afd9f3SGreg Kroah-Hartman 	ret = pci_request_regions(dc->pdev, NOZOMI_NAME);
1336a6afd9f3SGreg Kroah-Hartman 	if (ret) {
1337a6afd9f3SGreg Kroah-Hartman 		dev_err(&pdev->dev, "I/O address 0x%04x already in use\n",
1338a6afd9f3SGreg Kroah-Hartman 			(int) /* nozomi_private.io_addr */ 0);
1339a6afd9f3SGreg Kroah-Hartman 		goto err_disable_device;
1340a6afd9f3SGreg Kroah-Hartman 	}
1341a6afd9f3SGreg Kroah-Hartman 
1342a6afd9f3SGreg Kroah-Hartman 	/* Find out what card type it is */
1343a6afd9f3SGreg Kroah-Hartman 	nozomi_get_card_type(dc);
1344a6afd9f3SGreg Kroah-Hartman 
1345c392ed46SHugo Lefeuvre 	dc->base_addr = pci_iomap(dc->pdev, 0, dc->card_type);
1346a6afd9f3SGreg Kroah-Hartman 	if (!dc->base_addr) {
1347a6afd9f3SGreg Kroah-Hartman 		dev_err(&pdev->dev, "Unable to map card MMIO\n");
1348a6afd9f3SGreg Kroah-Hartman 		ret = -ENODEV;
1349a6afd9f3SGreg Kroah-Hartman 		goto err_rel_regs;
1350a6afd9f3SGreg Kroah-Hartman 	}
1351a6afd9f3SGreg Kroah-Hartman 
1352a6afd9f3SGreg Kroah-Hartman 	dc->send_buf = kmalloc(SEND_BUF_MAX, GFP_KERNEL);
1353a6afd9f3SGreg Kroah-Hartman 	if (!dc->send_buf) {
1354a6afd9f3SGreg Kroah-Hartman 		dev_err(&pdev->dev, "Could not allocate send buffer?\n");
1355a6afd9f3SGreg Kroah-Hartman 		ret = -ENOMEM;
1356a6afd9f3SGreg Kroah-Hartman 		goto err_free_sbuf;
1357a6afd9f3SGreg Kroah-Hartman 	}
1358a6afd9f3SGreg Kroah-Hartman 
1359a6afd9f3SGreg Kroah-Hartman 	for (i = PORT_MDM; i < MAX_PORT; i++) {
1360c29bd8d8SJiri Slaby 		if (kfifo_alloc(&dc->port[i].fifo_ul, FIFO_BUFFER_SIZE_UL,
1361c29bd8d8SJiri Slaby 					GFP_KERNEL)) {
1362a6afd9f3SGreg Kroah-Hartman 			dev_err(&pdev->dev,
1363a6afd9f3SGreg Kroah-Hartman 					"Could not allocate kfifo buffer\n");
1364a6afd9f3SGreg Kroah-Hartman 			ret = -ENOMEM;
1365a6afd9f3SGreg Kroah-Hartman 			goto err_free_kfifo;
1366a6afd9f3SGreg Kroah-Hartman 		}
1367a6afd9f3SGreg Kroah-Hartman 	}
1368a6afd9f3SGreg Kroah-Hartman 
1369a6afd9f3SGreg Kroah-Hartman 	spin_lock_init(&dc->spin_mutex);
1370a6afd9f3SGreg Kroah-Hartman 
1371a6afd9f3SGreg Kroah-Hartman 	nozomi_setup_private_data(dc);
1372a6afd9f3SGreg Kroah-Hartman 
1373a6afd9f3SGreg Kroah-Hartman 	/* Disable all interrupts */
1374a6afd9f3SGreg Kroah-Hartman 	dc->last_ier = 0;
1375a6afd9f3SGreg Kroah-Hartman 	writew(dc->last_ier, dc->reg_ier);
1376a6afd9f3SGreg Kroah-Hartman 
1377a6afd9f3SGreg Kroah-Hartman 	ret = request_irq(pdev->irq, &interrupt_handler, IRQF_SHARED,
1378a6afd9f3SGreg Kroah-Hartman 			NOZOMI_NAME, dc);
1379a6afd9f3SGreg Kroah-Hartman 	if (unlikely(ret)) {
1380a6afd9f3SGreg Kroah-Hartman 		dev_err(&pdev->dev, "can't request irq %d\n", pdev->irq);
13816ae7d0f5SChristophe JAILLET 		goto err_free_all_kfifo;
1382a6afd9f3SGreg Kroah-Hartman 	}
1383a6afd9f3SGreg Kroah-Hartman 
1384a6afd9f3SGreg Kroah-Hartman 	DBG1("base_addr: %p", dc->base_addr);
1385a6afd9f3SGreg Kroah-Hartman 
1386a6afd9f3SGreg Kroah-Hartman 	make_sysfs_files(dc);
1387a6afd9f3SGreg Kroah-Hartman 
1388a6afd9f3SGreg Kroah-Hartman 	dc->index_start = ndev_idx * MAX_PORT;
1389a6afd9f3SGreg Kroah-Hartman 	ndevs[ndev_idx] = dc;
1390a6afd9f3SGreg Kroah-Hartman 
1391a6afd9f3SGreg Kroah-Hartman 	pci_set_drvdata(pdev, dc);
1392a6afd9f3SGreg Kroah-Hartman 
1393a6afd9f3SGreg Kroah-Hartman 	/* Enable RESET interrupt */
1394a6afd9f3SGreg Kroah-Hartman 	dc->last_ier = RESET;
1395a6afd9f3SGreg Kroah-Hartman 	iowrite16(dc->last_ier, dc->reg_ier);
1396a6afd9f3SGreg Kroah-Hartman 
1397a6afd9f3SGreg Kroah-Hartman 	dc->state = NOZOMI_STATE_ENABLED;
1398a6afd9f3SGreg Kroah-Hartman 
1399a6afd9f3SGreg Kroah-Hartman 	for (i = 0; i < MAX_PORT; i++) {
1400a6afd9f3SGreg Kroah-Hartman 		struct device *tty_dev;
1401a6afd9f3SGreg Kroah-Hartman 		struct port *port = &dc->port[i];
1402a6afd9f3SGreg Kroah-Hartman 		port->dc = dc;
1403a6afd9f3SGreg Kroah-Hartman 		tty_port_init(&port->port);
1404a6afd9f3SGreg Kroah-Hartman 		port->port.ops = &noz_tty_port_ops;
1405734cc178SJiri Slaby 		tty_dev = tty_port_register_device(&port->port, ntty_driver,
1406734cc178SJiri Slaby 				dc->index_start + i, &pdev->dev);
1407a6afd9f3SGreg Kroah-Hartman 
1408a6afd9f3SGreg Kroah-Hartman 		if (IS_ERR(tty_dev)) {
1409a6afd9f3SGreg Kroah-Hartman 			ret = PTR_ERR(tty_dev);
1410a6afd9f3SGreg Kroah-Hartman 			dev_err(&pdev->dev, "Could not allocate tty?\n");
1411191c5f10SJiri Slaby 			tty_port_destroy(&port->port);
1412a6afd9f3SGreg Kroah-Hartman 			goto err_free_tty;
1413a6afd9f3SGreg Kroah-Hartman 		}
1414a6afd9f3SGreg Kroah-Hartman 	}
1415a6afd9f3SGreg Kroah-Hartman 
1416a6afd9f3SGreg Kroah-Hartman 	return 0;
1417a6afd9f3SGreg Kroah-Hartman 
1418a6afd9f3SGreg Kroah-Hartman err_free_tty:
14196ae7d0f5SChristophe JAILLET 	for (i--; i >= 0; i--) {
1420191c5f10SJiri Slaby 		tty_unregister_device(ntty_driver, dc->index_start + i);
1421191c5f10SJiri Slaby 		tty_port_destroy(&dc->port[i].port);
1422191c5f10SJiri Slaby 	}
142331a9a318SChristophe JAILLET 	free_irq(pdev->irq, dc);
14246ae7d0f5SChristophe JAILLET err_free_all_kfifo:
14256ae7d0f5SChristophe JAILLET 	i = MAX_PORT;
1426a6afd9f3SGreg Kroah-Hartman err_free_kfifo:
14276ae7d0f5SChristophe JAILLET 	for (i--; i >= PORT_MDM; i--)
1428a6afd9f3SGreg Kroah-Hartman 		kfifo_free(&dc->port[i].fifo_ul);
1429a6afd9f3SGreg Kroah-Hartman err_free_sbuf:
1430a6afd9f3SGreg Kroah-Hartman 	kfree(dc->send_buf);
1431a6afd9f3SGreg Kroah-Hartman 	iounmap(dc->base_addr);
1432a6afd9f3SGreg Kroah-Hartman err_rel_regs:
1433a6afd9f3SGreg Kroah-Hartman 	pci_release_regions(pdev);
1434a6afd9f3SGreg Kroah-Hartman err_disable_device:
1435a6afd9f3SGreg Kroah-Hartman 	pci_disable_device(pdev);
1436a6afd9f3SGreg Kroah-Hartman err_free:
1437a6afd9f3SGreg Kroah-Hartman 	kfree(dc);
1438a6afd9f3SGreg Kroah-Hartman err:
1439a6afd9f3SGreg Kroah-Hartman 	return ret;
1440a6afd9f3SGreg Kroah-Hartman }
1441a6afd9f3SGreg Kroah-Hartman 
tty_exit(struct nozomi * dc)1442ae8d8a14SBill Pemberton static void tty_exit(struct nozomi *dc)
1443a6afd9f3SGreg Kroah-Hartman {
1444a6afd9f3SGreg Kroah-Hartman 	unsigned int i;
1445a6afd9f3SGreg Kroah-Hartman 
1446aa27a094SJiri Slaby 	for (i = 0; i < MAX_PORT; ++i)
1447aa27a094SJiri Slaby 		tty_port_tty_hangup(&dc->port[i].port, false);
1448aa27a094SJiri Slaby 
1449a6afd9f3SGreg Kroah-Hartman 	/* Racy below - surely should wait for scheduled work to be done or
1450a6afd9f3SGreg Kroah-Hartman 	   complete off a hangup method ? */
1451a6afd9f3SGreg Kroah-Hartman 	while (dc->open_ttys)
1452a6afd9f3SGreg Kroah-Hartman 		msleep(1);
1453191c5f10SJiri Slaby 	for (i = 0; i < MAX_PORT; ++i) {
1454191c5f10SJiri Slaby 		tty_unregister_device(ntty_driver, dc->index_start + i);
1455191c5f10SJiri Slaby 		tty_port_destroy(&dc->port[i].port);
1456191c5f10SJiri Slaby 	}
1457a6afd9f3SGreg Kroah-Hartman }
1458a6afd9f3SGreg Kroah-Hartman 
1459a6afd9f3SGreg Kroah-Hartman /* Deallocate memory for one device */
nozomi_card_exit(struct pci_dev * pdev)1460ae8d8a14SBill Pemberton static void nozomi_card_exit(struct pci_dev *pdev)
1461a6afd9f3SGreg Kroah-Hartman {
1462a6afd9f3SGreg Kroah-Hartman 	int i;
1463a6afd9f3SGreg Kroah-Hartman 	struct ctrl_ul ctrl;
1464a6afd9f3SGreg Kroah-Hartman 	struct nozomi *dc = pci_get_drvdata(pdev);
1465a6afd9f3SGreg Kroah-Hartman 
1466a6afd9f3SGreg Kroah-Hartman 	/* Disable all interrupts */
1467a6afd9f3SGreg Kroah-Hartman 	dc->last_ier = 0;
1468a6afd9f3SGreg Kroah-Hartman 	writew(dc->last_ier, dc->reg_ier);
1469a6afd9f3SGreg Kroah-Hartman 
1470a6afd9f3SGreg Kroah-Hartman 	tty_exit(dc);
1471a6afd9f3SGreg Kroah-Hartman 
1472a6afd9f3SGreg Kroah-Hartman 	/* Send 0x0001, command card to resend the reset token.  */
1473a6afd9f3SGreg Kroah-Hartman 	/* This is to get the reset when the module is reloaded. */
1474a6afd9f3SGreg Kroah-Hartman 	ctrl.port = 0x00;
1475a6afd9f3SGreg Kroah-Hartman 	ctrl.reserved = 0;
1476a6afd9f3SGreg Kroah-Hartman 	ctrl.RTS = 0;
1477a6afd9f3SGreg Kroah-Hartman 	ctrl.DTR = 1;
1478a6afd9f3SGreg Kroah-Hartman 	DBG1("sending flow control 0x%04X", *((u16 *)&ctrl));
1479a6afd9f3SGreg Kroah-Hartman 
1480a6afd9f3SGreg Kroah-Hartman 	/* Setup dc->reg addresses to we can use defines here */
1481a6afd9f3SGreg Kroah-Hartman 	write_mem32(dc->port[PORT_CTRL].ul_addr[0], (u32 *)&ctrl, 2);
1482a6afd9f3SGreg Kroah-Hartman 	writew(CTRL_UL, dc->reg_fcr);	/* push the token to the card. */
1483a6afd9f3SGreg Kroah-Hartman 
1484a6afd9f3SGreg Kroah-Hartman 	remove_sysfs_files(dc);
1485a6afd9f3SGreg Kroah-Hartman 
1486a6afd9f3SGreg Kroah-Hartman 	free_irq(pdev->irq, dc);
1487a6afd9f3SGreg Kroah-Hartman 
1488a6afd9f3SGreg Kroah-Hartman 	for (i = 0; i < MAX_PORT; i++)
1489a6afd9f3SGreg Kroah-Hartman 		kfifo_free(&dc->port[i].fifo_ul);
1490a6afd9f3SGreg Kroah-Hartman 
1491a6afd9f3SGreg Kroah-Hartman 	kfree(dc->send_buf);
1492a6afd9f3SGreg Kroah-Hartman 
1493a6afd9f3SGreg Kroah-Hartman 	iounmap(dc->base_addr);
1494a6afd9f3SGreg Kroah-Hartman 
1495a6afd9f3SGreg Kroah-Hartman 	pci_release_regions(pdev);
1496a6afd9f3SGreg Kroah-Hartman 
1497a6afd9f3SGreg Kroah-Hartman 	pci_disable_device(pdev);
1498a6afd9f3SGreg Kroah-Hartman 
1499a6afd9f3SGreg Kroah-Hartman 	ndevs[dc->index_start / MAX_PORT] = NULL;
1500a6afd9f3SGreg Kroah-Hartman 
1501a6afd9f3SGreg Kroah-Hartman 	kfree(dc);
1502a6afd9f3SGreg Kroah-Hartman }
1503a6afd9f3SGreg Kroah-Hartman 
set_rts(const struct tty_struct * tty,int rts)1504a6afd9f3SGreg Kroah-Hartman static void set_rts(const struct tty_struct *tty, int rts)
1505a6afd9f3SGreg Kroah-Hartman {
1506a6afd9f3SGreg Kroah-Hartman 	struct port *port = get_port_by_tty(tty);
1507a6afd9f3SGreg Kroah-Hartman 
1508a6afd9f3SGreg Kroah-Hartman 	port->ctrl_ul.RTS = rts;
1509a6afd9f3SGreg Kroah-Hartman 	port->update_flow_control = 1;
1510a6afd9f3SGreg Kroah-Hartman 	enable_transmit_ul(PORT_CTRL, get_dc_by_tty(tty));
1511a6afd9f3SGreg Kroah-Hartman }
1512a6afd9f3SGreg Kroah-Hartman 
set_dtr(const struct tty_struct * tty,int dtr)1513a6afd9f3SGreg Kroah-Hartman static void set_dtr(const struct tty_struct *tty, int dtr)
1514a6afd9f3SGreg Kroah-Hartman {
1515a6afd9f3SGreg Kroah-Hartman 	struct port *port = get_port_by_tty(tty);
1516a6afd9f3SGreg Kroah-Hartman 
1517a6afd9f3SGreg Kroah-Hartman 	DBG1("SETTING DTR index: %d, dtr: %d", tty->index, dtr);
1518a6afd9f3SGreg Kroah-Hartman 
1519a6afd9f3SGreg Kroah-Hartman 	port->ctrl_ul.DTR = dtr;
1520a6afd9f3SGreg Kroah-Hartman 	port->update_flow_control = 1;
1521a6afd9f3SGreg Kroah-Hartman 	enable_transmit_ul(PORT_CTRL, get_dc_by_tty(tty));
1522a6afd9f3SGreg Kroah-Hartman }
1523a6afd9f3SGreg Kroah-Hartman 
1524a6afd9f3SGreg Kroah-Hartman /*
1525a6afd9f3SGreg Kroah-Hartman  * ----------------------------------------------------------------------------
1526a6afd9f3SGreg Kroah-Hartman  * TTY code
1527a6afd9f3SGreg Kroah-Hartman  * ----------------------------------------------------------------------------
1528a6afd9f3SGreg Kroah-Hartman  */
1529a6afd9f3SGreg Kroah-Hartman 
ntty_install(struct tty_driver * driver,struct tty_struct * tty)1530a6afd9f3SGreg Kroah-Hartman static int ntty_install(struct tty_driver *driver, struct tty_struct *tty)
1531a6afd9f3SGreg Kroah-Hartman {
1532a6afd9f3SGreg Kroah-Hartman 	struct port *port = get_port_by_tty(tty);
1533a6afd9f3SGreg Kroah-Hartman 	struct nozomi *dc = get_dc_by_tty(tty);
1534a6afd9f3SGreg Kroah-Hartman 	int ret;
1535a6afd9f3SGreg Kroah-Hartman 	if (!port || !dc || dc->state != NOZOMI_STATE_READY)
1536a6afd9f3SGreg Kroah-Hartman 		return -ENODEV;
153781f5835eSJiri Slaby 	ret = tty_standard_install(driver, tty);
153881f5835eSJiri Slaby 	if (ret == 0)
1539a6afd9f3SGreg Kroah-Hartman 		tty->driver_data = port;
1540a6afd9f3SGreg Kroah-Hartman 	return ret;
1541a6afd9f3SGreg Kroah-Hartman }
1542a6afd9f3SGreg Kroah-Hartman 
ntty_cleanup(struct tty_struct * tty)1543a6afd9f3SGreg Kroah-Hartman static void ntty_cleanup(struct tty_struct *tty)
1544a6afd9f3SGreg Kroah-Hartman {
1545a6afd9f3SGreg Kroah-Hartman 	tty->driver_data = NULL;
1546a6afd9f3SGreg Kroah-Hartman }
1547a6afd9f3SGreg Kroah-Hartman 
ntty_activate(struct tty_port * tport,struct tty_struct * tty)1548a6afd9f3SGreg Kroah-Hartman static int ntty_activate(struct tty_port *tport, struct tty_struct *tty)
1549a6afd9f3SGreg Kroah-Hartman {
1550a6afd9f3SGreg Kroah-Hartman 	struct port *port = container_of(tport, struct port, port);
1551a6afd9f3SGreg Kroah-Hartman 	struct nozomi *dc = port->dc;
1552a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1553a6afd9f3SGreg Kroah-Hartman 
1554a6afd9f3SGreg Kroah-Hartman 	DBG1("open: %d", port->token_dl);
1555a6afd9f3SGreg Kroah-Hartman 	spin_lock_irqsave(&dc->spin_mutex, flags);
1556a6afd9f3SGreg Kroah-Hartman 	dc->last_ier = dc->last_ier | port->token_dl;
1557a6afd9f3SGreg Kroah-Hartman 	writew(dc->last_ier, dc->reg_ier);
1558a6afd9f3SGreg Kroah-Hartman 	dc->open_ttys++;
1559a6afd9f3SGreg Kroah-Hartman 	spin_unlock_irqrestore(&dc->spin_mutex, flags);
1560a6afd9f3SGreg Kroah-Hartman 	printk("noz: activated %d: %p\n", tty->index, tport);
1561a6afd9f3SGreg Kroah-Hartman 	return 0;
1562a6afd9f3SGreg Kroah-Hartman }
1563a6afd9f3SGreg Kroah-Hartman 
ntty_open(struct tty_struct * tty,struct file * filp)1564a6afd9f3SGreg Kroah-Hartman static int ntty_open(struct tty_struct *tty, struct file *filp)
1565a6afd9f3SGreg Kroah-Hartman {
1566a6afd9f3SGreg Kroah-Hartman 	struct port *port = tty->driver_data;
1567a6afd9f3SGreg Kroah-Hartman 	return tty_port_open(&port->port, tty, filp);
1568a6afd9f3SGreg Kroah-Hartman }
1569a6afd9f3SGreg Kroah-Hartman 
ntty_shutdown(struct tty_port * tport)1570a6afd9f3SGreg Kroah-Hartman static void ntty_shutdown(struct tty_port *tport)
1571a6afd9f3SGreg Kroah-Hartman {
1572a6afd9f3SGreg Kroah-Hartman 	struct port *port = container_of(tport, struct port, port);
1573a6afd9f3SGreg Kroah-Hartman 	struct nozomi *dc = port->dc;
1574a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1575a6afd9f3SGreg Kroah-Hartman 
1576a6afd9f3SGreg Kroah-Hartman 	DBG1("close: %d", port->token_dl);
1577a6afd9f3SGreg Kroah-Hartman 	spin_lock_irqsave(&dc->spin_mutex, flags);
1578a6afd9f3SGreg Kroah-Hartman 	dc->last_ier &= ~(port->token_dl);
1579a6afd9f3SGreg Kroah-Hartman 	writew(dc->last_ier, dc->reg_ier);
1580a6afd9f3SGreg Kroah-Hartman 	dc->open_ttys--;
1581a6afd9f3SGreg Kroah-Hartman 	spin_unlock_irqrestore(&dc->spin_mutex, flags);
1582a6afd9f3SGreg Kroah-Hartman 	printk("noz: shutdown %p\n", tport);
1583a6afd9f3SGreg Kroah-Hartman }
1584a6afd9f3SGreg Kroah-Hartman 
ntty_close(struct tty_struct * tty,struct file * filp)1585a6afd9f3SGreg Kroah-Hartman static void ntty_close(struct tty_struct *tty, struct file *filp)
1586a6afd9f3SGreg Kroah-Hartman {
1587a6afd9f3SGreg Kroah-Hartman 	struct port *port = tty->driver_data;
1588a6afd9f3SGreg Kroah-Hartman 	if (port)
1589a6afd9f3SGreg Kroah-Hartman 		tty_port_close(&port->port, tty, filp);
1590a6afd9f3SGreg Kroah-Hartman }
1591a6afd9f3SGreg Kroah-Hartman 
ntty_hangup(struct tty_struct * tty)1592a6afd9f3SGreg Kroah-Hartman static void ntty_hangup(struct tty_struct *tty)
1593a6afd9f3SGreg Kroah-Hartman {
1594a6afd9f3SGreg Kroah-Hartman 	struct port *port = tty->driver_data;
1595a6afd9f3SGreg Kroah-Hartman 	tty_port_hangup(&port->port);
1596a6afd9f3SGreg Kroah-Hartman }
1597a6afd9f3SGreg Kroah-Hartman 
1598a6afd9f3SGreg Kroah-Hartman /*
1599a6afd9f3SGreg Kroah-Hartman  * called when the userspace process writes to the tty (/dev/noz*).
160025985edcSLucas De Marchi  * Data is inserted into a fifo, which is then read and transferred to the modem.
1601a6afd9f3SGreg Kroah-Hartman  */
ntty_write(struct tty_struct * tty,const u8 * buffer,size_t count)1602*95713967SJiri Slaby (SUSE) static ssize_t ntty_write(struct tty_struct *tty, const u8 *buffer,
1603*95713967SJiri Slaby (SUSE) 			  size_t count)
1604a6afd9f3SGreg Kroah-Hartman {
1605a6afd9f3SGreg Kroah-Hartman 	int rval = -EINVAL;
1606a6afd9f3SGreg Kroah-Hartman 	struct nozomi *dc = get_dc_by_tty(tty);
1607a6afd9f3SGreg Kroah-Hartman 	struct port *port = tty->driver_data;
1608a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1609a6afd9f3SGreg Kroah-Hartman 
1610a6afd9f3SGreg Kroah-Hartman 	if (!dc || !port)
1611a6afd9f3SGreg Kroah-Hartman 		return -ENODEV;
1612a6afd9f3SGreg Kroah-Hartman 
161369851e4aSJiri Slaby (SUSE) 	rval = kfifo_in(&port->fifo_ul, buffer, count);
1614a6afd9f3SGreg Kroah-Hartman 
1615a6afd9f3SGreg Kroah-Hartman 	spin_lock_irqsave(&dc->spin_mutex, flags);
1616a6afd9f3SGreg Kroah-Hartman 	/* CTS is only valid on the modem channel */
1617a6afd9f3SGreg Kroah-Hartman 	if (port == &(dc->port[PORT_MDM])) {
1618a6afd9f3SGreg Kroah-Hartman 		if (port->ctrl_dl.CTS) {
1619a6afd9f3SGreg Kroah-Hartman 			DBG4("Enable interrupt");
1620a6afd9f3SGreg Kroah-Hartman 			enable_transmit_ul(tty->index % MAX_PORT, dc);
1621a6afd9f3SGreg Kroah-Hartman 		} else {
1622a6afd9f3SGreg Kroah-Hartman 			dev_err(&dc->pdev->dev,
1623a6afd9f3SGreg Kroah-Hartman 				"CTS not active on modem port?\n");
1624a6afd9f3SGreg Kroah-Hartman 		}
1625a6afd9f3SGreg Kroah-Hartman 	} else {
1626a6afd9f3SGreg Kroah-Hartman 		enable_transmit_ul(tty->index % MAX_PORT, dc);
1627a6afd9f3SGreg Kroah-Hartman 	}
1628a6afd9f3SGreg Kroah-Hartman 	spin_unlock_irqrestore(&dc->spin_mutex, flags);
1629a6afd9f3SGreg Kroah-Hartman 
1630a6afd9f3SGreg Kroah-Hartman 	return rval;
1631a6afd9f3SGreg Kroah-Hartman }
1632a6afd9f3SGreg Kroah-Hartman 
1633a6afd9f3SGreg Kroah-Hartman /*
1634a6afd9f3SGreg Kroah-Hartman  * Calculate how much is left in device
1635a6afd9f3SGreg Kroah-Hartman  * This method is called by the upper tty layer.
1636a6afd9f3SGreg Kroah-Hartman  *   #according to sources N_TTY.c it expects a value >= 0 and
1637a6afd9f3SGreg Kroah-Hartman  *    does not check for negative values.
1638a6afd9f3SGreg Kroah-Hartman  *
1639a6afd9f3SGreg Kroah-Hartman  * If the port is unplugged report lots of room and let the bits
1640a6afd9f3SGreg Kroah-Hartman  * dribble away so we don't block anything.
1641a6afd9f3SGreg Kroah-Hartman  */
ntty_write_room(struct tty_struct * tty)164203b3b1a2SJiri Slaby static unsigned int ntty_write_room(struct tty_struct *tty)
1643a6afd9f3SGreg Kroah-Hartman {
1644a6afd9f3SGreg Kroah-Hartman 	struct port *port = tty->driver_data;
164503b3b1a2SJiri Slaby 	unsigned int room = 4096;
1646a6afd9f3SGreg Kroah-Hartman 	const struct nozomi *dc = get_dc_by_tty(tty);
1647a6afd9f3SGreg Kroah-Hartman 
16487fdc2893SJiri Slaby 	if (dc)
1649a6afd9f3SGreg Kroah-Hartman 		room = kfifo_avail(&port->fifo_ul);
16507fdc2893SJiri Slaby 
1651a6afd9f3SGreg Kroah-Hartman 	return room;
1652a6afd9f3SGreg Kroah-Hartman }
1653a6afd9f3SGreg Kroah-Hartman 
1654a6afd9f3SGreg Kroah-Hartman /* Gets io control parameters */
ntty_tiocmget(struct tty_struct * tty)1655a6afd9f3SGreg Kroah-Hartman static int ntty_tiocmget(struct tty_struct *tty)
1656a6afd9f3SGreg Kroah-Hartman {
1657a6afd9f3SGreg Kroah-Hartman 	const struct port *port = tty->driver_data;
1658a6afd9f3SGreg Kroah-Hartman 	const struct ctrl_dl *ctrl_dl = &port->ctrl_dl;
1659a6afd9f3SGreg Kroah-Hartman 	const struct ctrl_ul *ctrl_ul = &port->ctrl_ul;
1660a6afd9f3SGreg Kroah-Hartman 
1661a6afd9f3SGreg Kroah-Hartman 	/* Note: these could change under us but it is not clear this
1662a6afd9f3SGreg Kroah-Hartman 	   matters if so */
1663c6964e93SJoey Pabalinas 	return (ctrl_ul->RTS ? TIOCM_RTS : 0)
1664c6964e93SJoey Pabalinas 		| (ctrl_ul->DTR ? TIOCM_DTR : 0)
1665c6964e93SJoey Pabalinas 		| (ctrl_dl->DCD ? TIOCM_CAR : 0)
1666c6964e93SJoey Pabalinas 		| (ctrl_dl->RI  ? TIOCM_RNG : 0)
1667c6964e93SJoey Pabalinas 		| (ctrl_dl->DSR ? TIOCM_DSR : 0)
1668c6964e93SJoey Pabalinas 		| (ctrl_dl->CTS ? TIOCM_CTS : 0);
1669a6afd9f3SGreg Kroah-Hartman }
1670a6afd9f3SGreg Kroah-Hartman 
1671a6afd9f3SGreg Kroah-Hartman /* Sets io controls parameters */
ntty_tiocmset(struct tty_struct * tty,unsigned int set,unsigned int clear)1672a6afd9f3SGreg Kroah-Hartman static int ntty_tiocmset(struct tty_struct *tty,
1673a6afd9f3SGreg Kroah-Hartman 					unsigned int set, unsigned int clear)
1674a6afd9f3SGreg Kroah-Hartman {
1675a6afd9f3SGreg Kroah-Hartman 	struct nozomi *dc = get_dc_by_tty(tty);
1676a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1677a6afd9f3SGreg Kroah-Hartman 
1678a6afd9f3SGreg Kroah-Hartman 	spin_lock_irqsave(&dc->spin_mutex, flags);
1679a6afd9f3SGreg Kroah-Hartman 	if (set & TIOCM_RTS)
1680a6afd9f3SGreg Kroah-Hartman 		set_rts(tty, 1);
1681a6afd9f3SGreg Kroah-Hartman 	else if (clear & TIOCM_RTS)
1682a6afd9f3SGreg Kroah-Hartman 		set_rts(tty, 0);
1683a6afd9f3SGreg Kroah-Hartman 
1684a6afd9f3SGreg Kroah-Hartman 	if (set & TIOCM_DTR)
1685a6afd9f3SGreg Kroah-Hartman 		set_dtr(tty, 1);
1686a6afd9f3SGreg Kroah-Hartman 	else if (clear & TIOCM_DTR)
1687a6afd9f3SGreg Kroah-Hartman 		set_dtr(tty, 0);
1688a6afd9f3SGreg Kroah-Hartman 	spin_unlock_irqrestore(&dc->spin_mutex, flags);
1689a6afd9f3SGreg Kroah-Hartman 
1690a6afd9f3SGreg Kroah-Hartman 	return 0;
1691a6afd9f3SGreg Kroah-Hartman }
1692a6afd9f3SGreg Kroah-Hartman 
ntty_cflags_changed(struct port * port,unsigned long flags,struct async_icount * cprev)1693a6afd9f3SGreg Kroah-Hartman static int ntty_cflags_changed(struct port *port, unsigned long flags,
1694a6afd9f3SGreg Kroah-Hartman 		struct async_icount *cprev)
1695a6afd9f3SGreg Kroah-Hartman {
1696a6afd9f3SGreg Kroah-Hartman 	const struct async_icount cnow = port->tty_icount;
1697a6afd9f3SGreg Kroah-Hartman 	int ret;
1698a6afd9f3SGreg Kroah-Hartman 
1699c6964e93SJoey Pabalinas 	ret = ((flags & TIOCM_RNG) && (cnow.rng != cprev->rng))
1700c6964e93SJoey Pabalinas 		|| ((flags & TIOCM_DSR) && (cnow.dsr != cprev->dsr))
1701c6964e93SJoey Pabalinas 		|| ((flags & TIOCM_CD)  && (cnow.dcd != cprev->dcd))
1702c6964e93SJoey Pabalinas 		|| ((flags & TIOCM_CTS) && (cnow.cts != cprev->cts));
1703a6afd9f3SGreg Kroah-Hartman 
1704a6afd9f3SGreg Kroah-Hartman 	*cprev = cnow;
1705a6afd9f3SGreg Kroah-Hartman 
1706a6afd9f3SGreg Kroah-Hartman 	return ret;
1707a6afd9f3SGreg Kroah-Hartman }
1708a6afd9f3SGreg Kroah-Hartman 
ntty_tiocgicount(struct tty_struct * tty,struct serial_icounter_struct * icount)1709a6afd9f3SGreg Kroah-Hartman static int ntty_tiocgicount(struct tty_struct *tty,
1710a6afd9f3SGreg Kroah-Hartman 				struct serial_icounter_struct *icount)
1711a6afd9f3SGreg Kroah-Hartman {
1712a6afd9f3SGreg Kroah-Hartman 	struct port *port = tty->driver_data;
1713a6afd9f3SGreg Kroah-Hartman 	const struct async_icount cnow = port->tty_icount;
1714a6afd9f3SGreg Kroah-Hartman 
1715a6afd9f3SGreg Kroah-Hartman 	icount->cts = cnow.cts;
1716a6afd9f3SGreg Kroah-Hartman 	icount->dsr = cnow.dsr;
1717a6afd9f3SGreg Kroah-Hartman 	icount->rng = cnow.rng;
1718a6afd9f3SGreg Kroah-Hartman 	icount->dcd = cnow.dcd;
1719a6afd9f3SGreg Kroah-Hartman 	icount->rx = cnow.rx;
1720a6afd9f3SGreg Kroah-Hartman 	icount->tx = cnow.tx;
1721a6afd9f3SGreg Kroah-Hartman 	icount->frame = cnow.frame;
1722a6afd9f3SGreg Kroah-Hartman 	icount->overrun = cnow.overrun;
1723a6afd9f3SGreg Kroah-Hartman 	icount->parity = cnow.parity;
1724a6afd9f3SGreg Kroah-Hartman 	icount->brk = cnow.brk;
1725a6afd9f3SGreg Kroah-Hartman 	icount->buf_overrun = cnow.buf_overrun;
1726a6afd9f3SGreg Kroah-Hartman 	return 0;
1727a6afd9f3SGreg Kroah-Hartman }
1728a6afd9f3SGreg Kroah-Hartman 
ntty_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)1729a6afd9f3SGreg Kroah-Hartman static int ntty_ioctl(struct tty_struct *tty,
1730a6afd9f3SGreg Kroah-Hartman 		      unsigned int cmd, unsigned long arg)
1731a6afd9f3SGreg Kroah-Hartman {
1732a6afd9f3SGreg Kroah-Hartman 	struct port *port = tty->driver_data;
1733a6afd9f3SGreg Kroah-Hartman 	int rval = -ENOIOCTLCMD;
1734a6afd9f3SGreg Kroah-Hartman 
1735a6afd9f3SGreg Kroah-Hartman 	switch (cmd) {
1736a6afd9f3SGreg Kroah-Hartman 	case TIOCMIWAIT: {
1737a6afd9f3SGreg Kroah-Hartman 		struct async_icount cprev = port->tty_icount;
1738a6afd9f3SGreg Kroah-Hartman 
1739a6afd9f3SGreg Kroah-Hartman 		rval = wait_event_interruptible(port->tty_wait,
1740a6afd9f3SGreg Kroah-Hartman 				ntty_cflags_changed(port, arg, &cprev));
1741a6afd9f3SGreg Kroah-Hartman 		break;
1742a6afd9f3SGreg Kroah-Hartman 	}
1743a6afd9f3SGreg Kroah-Hartman 	default:
1744a6afd9f3SGreg Kroah-Hartman 		DBG1("ERR: 0x%08X, %d", cmd, cmd);
1745a6afd9f3SGreg Kroah-Hartman 		break;
1746fc811472SJoe Perches 	}
1747a6afd9f3SGreg Kroah-Hartman 
1748a6afd9f3SGreg Kroah-Hartman 	return rval;
1749a6afd9f3SGreg Kroah-Hartman }
1750a6afd9f3SGreg Kroah-Hartman 
1751a6afd9f3SGreg Kroah-Hartman /*
1752a6afd9f3SGreg Kroah-Hartman  * Called by the upper tty layer when tty buffers are ready
1753a6afd9f3SGreg Kroah-Hartman  * to receive data again after a call to throttle.
1754a6afd9f3SGreg Kroah-Hartman  */
ntty_unthrottle(struct tty_struct * tty)1755a6afd9f3SGreg Kroah-Hartman static void ntty_unthrottle(struct tty_struct *tty)
1756a6afd9f3SGreg Kroah-Hartman {
1757a6afd9f3SGreg Kroah-Hartman 	struct nozomi *dc = get_dc_by_tty(tty);
1758a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1759a6afd9f3SGreg Kroah-Hartman 
1760a6afd9f3SGreg Kroah-Hartman 	spin_lock_irqsave(&dc->spin_mutex, flags);
1761a6afd9f3SGreg Kroah-Hartman 	enable_transmit_dl(tty->index % MAX_PORT, dc);
1762a6afd9f3SGreg Kroah-Hartman 	set_rts(tty, 1);
1763a6afd9f3SGreg Kroah-Hartman 
1764a6afd9f3SGreg Kroah-Hartman 	spin_unlock_irqrestore(&dc->spin_mutex, flags);
1765a6afd9f3SGreg Kroah-Hartman }
1766a6afd9f3SGreg Kroah-Hartman 
1767a6afd9f3SGreg Kroah-Hartman /*
1768a6afd9f3SGreg Kroah-Hartman  * Called by the upper tty layer when the tty buffers are almost full.
1769a6afd9f3SGreg Kroah-Hartman  * The driver should stop send more data.
1770a6afd9f3SGreg Kroah-Hartman  */
ntty_throttle(struct tty_struct * tty)1771a6afd9f3SGreg Kroah-Hartman static void ntty_throttle(struct tty_struct *tty)
1772a6afd9f3SGreg Kroah-Hartman {
1773a6afd9f3SGreg Kroah-Hartman 	struct nozomi *dc = get_dc_by_tty(tty);
1774a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1775a6afd9f3SGreg Kroah-Hartman 
1776a6afd9f3SGreg Kroah-Hartman 	spin_lock_irqsave(&dc->spin_mutex, flags);
1777a6afd9f3SGreg Kroah-Hartman 	set_rts(tty, 0);
1778a6afd9f3SGreg Kroah-Hartman 	spin_unlock_irqrestore(&dc->spin_mutex, flags);
1779a6afd9f3SGreg Kroah-Hartman }
1780a6afd9f3SGreg Kroah-Hartman 
1781a6afd9f3SGreg Kroah-Hartman /* Returns number of chars in buffer, called by tty layer */
ntty_chars_in_buffer(struct tty_struct * tty)1782fff4ef17SJiri Slaby static unsigned int ntty_chars_in_buffer(struct tty_struct *tty)
1783a6afd9f3SGreg Kroah-Hartman {
1784a6afd9f3SGreg Kroah-Hartman 	struct port *port = tty->driver_data;
1785a6afd9f3SGreg Kroah-Hartman 	struct nozomi *dc = get_dc_by_tty(tty);
1786a6afd9f3SGreg Kroah-Hartman 
17873d183542SJiri Slaby 	if (unlikely(!dc || !port))
17883d183542SJiri Slaby 		return 0;
1789a6afd9f3SGreg Kroah-Hartman 
17903d183542SJiri Slaby 	return kfifo_len(&port->fifo_ul);
1791a6afd9f3SGreg Kroah-Hartman }
1792a6afd9f3SGreg Kroah-Hartman 
1793a6afd9f3SGreg Kroah-Hartman static const struct tty_port_operations noz_tty_port_ops = {
1794a6afd9f3SGreg Kroah-Hartman 	.activate = ntty_activate,
1795a6afd9f3SGreg Kroah-Hartman 	.shutdown = ntty_shutdown,
1796a6afd9f3SGreg Kroah-Hartman };
1797a6afd9f3SGreg Kroah-Hartman 
1798a6afd9f3SGreg Kroah-Hartman static const struct tty_operations tty_ops = {
1799a6afd9f3SGreg Kroah-Hartman 	.ioctl = ntty_ioctl,
1800a6afd9f3SGreg Kroah-Hartman 	.open = ntty_open,
1801a6afd9f3SGreg Kroah-Hartman 	.close = ntty_close,
1802a6afd9f3SGreg Kroah-Hartman 	.hangup = ntty_hangup,
1803a6afd9f3SGreg Kroah-Hartman 	.write = ntty_write,
1804a6afd9f3SGreg Kroah-Hartman 	.write_room = ntty_write_room,
1805a6afd9f3SGreg Kroah-Hartman 	.unthrottle = ntty_unthrottle,
1806a6afd9f3SGreg Kroah-Hartman 	.throttle = ntty_throttle,
1807a6afd9f3SGreg Kroah-Hartman 	.chars_in_buffer = ntty_chars_in_buffer,
1808a6afd9f3SGreg Kroah-Hartman 	.tiocmget = ntty_tiocmget,
1809a6afd9f3SGreg Kroah-Hartman 	.tiocmset = ntty_tiocmset,
1810a6afd9f3SGreg Kroah-Hartman 	.get_icount = ntty_tiocgicount,
1811a6afd9f3SGreg Kroah-Hartman 	.install = ntty_install,
1812a6afd9f3SGreg Kroah-Hartman 	.cleanup = ntty_cleanup,
1813a6afd9f3SGreg Kroah-Hartman };
1814a6afd9f3SGreg Kroah-Hartman 
1815a6afd9f3SGreg Kroah-Hartman /* Module initialization */
1816a6afd9f3SGreg Kroah-Hartman static struct pci_driver nozomi_driver = {
1817a6afd9f3SGreg Kroah-Hartman 	.name = NOZOMI_NAME,
1818a6afd9f3SGreg Kroah-Hartman 	.id_table = nozomi_pci_tbl,
1819a6afd9f3SGreg Kroah-Hartman 	.probe = nozomi_card_init,
182091116cbaSBill Pemberton 	.remove = nozomi_card_exit,
1821a6afd9f3SGreg Kroah-Hartman };
1822a6afd9f3SGreg Kroah-Hartman 
nozomi_init(void)1823a6afd9f3SGreg Kroah-Hartman static __init int nozomi_init(void)
1824a6afd9f3SGreg Kroah-Hartman {
1825a6afd9f3SGreg Kroah-Hartman 	int ret;
1826a6afd9f3SGreg Kroah-Hartman 
182739b7b42bSJiri Slaby 	ntty_driver = tty_alloc_driver(NTTY_TTY_MAXMINORS, TTY_DRIVER_REAL_RAW |
182839b7b42bSJiri Slaby 			TTY_DRIVER_DYNAMIC_DEV);
182939b7b42bSJiri Slaby 	if (IS_ERR(ntty_driver))
183039b7b42bSJiri Slaby 		return PTR_ERR(ntty_driver);
1831a6afd9f3SGreg Kroah-Hartman 
1832a6afd9f3SGreg Kroah-Hartman 	ntty_driver->driver_name = NOZOMI_NAME_TTY;
1833a6afd9f3SGreg Kroah-Hartman 	ntty_driver->name = "noz";
1834a6afd9f3SGreg Kroah-Hartman 	ntty_driver->major = 0;
1835a6afd9f3SGreg Kroah-Hartman 	ntty_driver->type = TTY_DRIVER_TYPE_SERIAL;
1836a6afd9f3SGreg Kroah-Hartman 	ntty_driver->subtype = SERIAL_TYPE_NORMAL;
1837a6afd9f3SGreg Kroah-Hartman 	ntty_driver->init_termios = tty_std_termios;
1838a6afd9f3SGreg Kroah-Hartman 	ntty_driver->init_termios.c_cflag = B115200 | CS8 | CREAD | \
1839a6afd9f3SGreg Kroah-Hartman 						HUPCL | CLOCAL;
1840a6afd9f3SGreg Kroah-Hartman 	ntty_driver->init_termios.c_ispeed = 115200;
1841a6afd9f3SGreg Kroah-Hartman 	ntty_driver->init_termios.c_ospeed = 115200;
1842a6afd9f3SGreg Kroah-Hartman 	tty_set_operations(ntty_driver, &tty_ops);
1843a6afd9f3SGreg Kroah-Hartman 
1844a6afd9f3SGreg Kroah-Hartman 	ret = tty_register_driver(ntty_driver);
1845a6afd9f3SGreg Kroah-Hartman 	if (ret) {
1846a6afd9f3SGreg Kroah-Hartman 		printk(KERN_ERR "Nozomi: failed to register ntty driver\n");
1847a6afd9f3SGreg Kroah-Hartman 		goto free_tty;
1848a6afd9f3SGreg Kroah-Hartman 	}
1849a6afd9f3SGreg Kroah-Hartman 
1850a6afd9f3SGreg Kroah-Hartman 	ret = pci_register_driver(&nozomi_driver);
1851a6afd9f3SGreg Kroah-Hartman 	if (ret) {
1852a6afd9f3SGreg Kroah-Hartman 		printk(KERN_ERR "Nozomi: can't register pci driver\n");
1853a6afd9f3SGreg Kroah-Hartman 		goto unr_tty;
1854a6afd9f3SGreg Kroah-Hartman 	}
1855a6afd9f3SGreg Kroah-Hartman 
1856a6afd9f3SGreg Kroah-Hartman 	return 0;
1857a6afd9f3SGreg Kroah-Hartman unr_tty:
1858a6afd9f3SGreg Kroah-Hartman 	tty_unregister_driver(ntty_driver);
1859a6afd9f3SGreg Kroah-Hartman free_tty:
18609f90a4ddSJiri Slaby 	tty_driver_kref_put(ntty_driver);
1861a6afd9f3SGreg Kroah-Hartman 	return ret;
1862a6afd9f3SGreg Kroah-Hartman }
1863a6afd9f3SGreg Kroah-Hartman 
nozomi_exit(void)1864a6afd9f3SGreg Kroah-Hartman static __exit void nozomi_exit(void)
1865a6afd9f3SGreg Kroah-Hartman {
1866a6afd9f3SGreg Kroah-Hartman 	pci_unregister_driver(&nozomi_driver);
1867a6afd9f3SGreg Kroah-Hartman 	tty_unregister_driver(ntty_driver);
18689f90a4ddSJiri Slaby 	tty_driver_kref_put(ntty_driver);
1869a6afd9f3SGreg Kroah-Hartman }
1870a6afd9f3SGreg Kroah-Hartman 
1871a6afd9f3SGreg Kroah-Hartman module_init(nozomi_init);
1872a6afd9f3SGreg Kroah-Hartman module_exit(nozomi_exit);
1873a6afd9f3SGreg Kroah-Hartman 
1874a6afd9f3SGreg Kroah-Hartman MODULE_LICENSE("Dual BSD/GPL");
187583e826bfSJiri Slaby MODULE_DESCRIPTION("Nozomi driver");
1876