From nobody Mon Dec 1 22:37:45 2025 Received: from TYPPR03CU001.outbound.protection.outlook.com (mail-japaneastazon11022136.outbound.protection.outlook.com [52.101.126.136]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5E10F2798F8; Sun, 30 Nov 2025 10:43:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.126.136 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764499407; cv=fail; b=JUprYtVIRrwWZMYr56veQEg3SMWEO7Pg4UDiZnOCIvDXbvbpA6b8bqDTV8F1zNZ//JxQ5zSeOqIRF7E5EdjaTavj57AlDGbVYT7hvR8kLkPq0iHX6Ttylt2Aywp2djH2F8LhSFXyjyYNXd4V/SFqcAqgCRG7MjFaely3BrbwxM8= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764499407; c=relaxed/simple; bh=XBfl+wRjcnRlwDFIzFapvAyCIUeHkdr5XE0Mfi4r2tM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: Content-Type:MIME-Version; b=bi+MBAb2KEEemAc4Sh22wgaIC2r9OFiOoFSffN7adHNFNj0mSw5UiDHKDCBVb6gGVrsNj9sriEI+DEWJWZ9f8OZ1Pj7NhISLFB8kKrfWGr7tM93Ymru71NuW0rdh2ACFPBPU3R5ayrDipguyl6taPbpztNpFZHY5P3mAscz/4M8= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=moxa.com; spf=pass smtp.mailfrom=moxa.com; dkim=pass (1024-bit key) header.d=moxa.com header.i=@moxa.com header.b=F558SBrp; arc=fail smtp.client-ip=52.101.126.136 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=moxa.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=moxa.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=moxa.com header.i=@moxa.com header.b="F558SBrp" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=J7DKe6mmb7Cus1kmgFsN2LKMtdbd+1S2JHei4MUgKh3Tx8dh9VsJHrDARvAMynTdvbjC9mUQa6DfsQYbHEZzWguMnHnZDpDOIuu69F2sEDnqggueNet//aLv0G4hvn0dYZFfZmSCXJ8pl9bNZxxgodoFE0LkF7OchrV+hgBo8ln8B01HeQdWfrfyQGdKAThh7MS3b32CM9snrlJQhf2ca58uVhomxGHLyMmEtJgHIVHJKYTtd0DTUnXlDRDPRm1Ybv1XMHRVWIAkfbQjma1ayPNXHVR+pJ+AM4oLh2JXruJTSy2B315DebTdF41sag/assVBmX18UiKhN3FXhOoUOA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=m8nTVMo0EuIdKPVvyKmjiWVY4HZwpuePIulTqGEwabo=; b=rDUOyURcBobJaXZHI21SmV59cVFSjw7Qnjitl49HhiqZMvVja7abNIBXZFhXS1q9hfPlfG3D579NM38j1jttqSbeyTKdT9KfjWJk5CwwUOP0oTXUiOCD5BJ6iOkBxpCzrI/8r4lutEMWT+LUoYjr5ZwcB4nPOsTeiNyvyLBzpOGl/jA3gHiJVNNZD7NiXiLltIkdTRDwz8E2GnDqCgJjG8Sb2HlYpOSizgerVTJUeaNBEgOn+hQJjvGJMC/Yce6aB3/VBCdhgNCDMGZEFlo34TrXMAvkJtDFB74LHcVjl363B6Zbp3fic0D4Q1Wn01yEBLWEFsHC4T1kBUbEefkdbw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=moxa.com; dmarc=pass action=none header.from=moxa.com; dkim=pass header.d=moxa.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=moxa.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=m8nTVMo0EuIdKPVvyKmjiWVY4HZwpuePIulTqGEwabo=; b=F558SBrpOyl3i/brfHEInV2G2V/huHDx+XHZDn1+kYhfcMVP7xtf8/ycpP4ZDZd0YfE7fcs3XlSeBcLqHvu0Pgpec8HNh7MRh9fv9fmMrJ/SNc1xfzBRfOkoWVhcoF4ylDfOKHcOrmJxJiEfpNAeU9zqK7AyBLAEV9sNUAuLv5U= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=moxa.com; Received: from PUZPR01MB5405.apcprd01.prod.exchangelabs.com (2603:1096:301:115::14) by TYZPR01MB4235.apcprd01.prod.exchangelabs.com (2603:1096:400:1c4::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9366.17; Sun, 30 Nov 2025 10:43:18 +0000 Received: from PUZPR01MB5405.apcprd01.prod.exchangelabs.com ([fe80::60ab:8615:ab67:8817]) by PUZPR01MB5405.apcprd01.prod.exchangelabs.com ([fe80::60ab:8615:ab67:8817%6]) with mapi id 15.20.9320.013; Sun, 30 Nov 2025 10:43:18 +0000 From: Crescent Hsieh To: gregkh@linuxfoundation.org, jirislaby@kernel.org, ilpo.jarvinen@linux.intel.com, andy.shevchenko@gmail.com Cc: linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org, crescentcy.hsieh@moxa.com Subject: [PATCH v1 01/31] tty: mxser: serial: 8250: replace mxser with 8250-based driver Date: Sun, 30 Nov 2025 18:41:52 +0800 Message-ID: <20251130104222.63077-2-crescentcy.hsieh@moxa.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251130104222.63077-1-crescentcy.hsieh@moxa.com> References: <20251130104222.63077-1-crescentcy.hsieh@moxa.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: TPYP295CA0001.TWNP295.PROD.OUTLOOK.COM (2603:1096:7d0:9::18) To PUZPR01MB5405.apcprd01.prod.exchangelabs.com (2603:1096:301:115::14) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PUZPR01MB5405:EE_|TYZPR01MB4235:EE_ X-MS-Office365-Filtering-Correlation-Id: 0d19d256-d7b7-4d61-020c-08de2ffd4633 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|52116014|366016|1800799024|376014|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?ab+h3063b/8qakTI3jeUX9+qCI07tUj0bqWcOrsmqfvex5mQNrdDnWoqzjOw?= =?us-ascii?Q?Sg7Zgo1qn0NSvcSRLR5ywQOcguzgUNHcs7JTCL5KTP4jPWfJE6ibJd5sDt7C?= =?us-ascii?Q?/NfZnNOZPAkYF9oTnnnzG+eSWNFROGG265nH6kKJEXYze+D6xQpsJiLrOPo7?= =?us-ascii?Q?yiRRt3Y9fZR/Q0dLr47C/UgQxRAQFFrOcgTCPtYQx56hRdrmB73TkUCZhi7r?= =?us-ascii?Q?R+KNq1047X5NMFk64XCz3x9Lv2Uywm2dTkFGNC5IpYHf7WPgqUdp7VUeeC7A?= =?us-ascii?Q?GNBaxCBYC4SqvvKVBmroEnnbFPIjOfbZs4oVsCvlIwhosb/h6uj4wlzfUidG?= =?us-ascii?Q?LT6wi1JcfDn0Bo/S4YkBUurfCX0QaRJJc4lID8lJeSCWnnb3zBVG5V/gF/l/?= =?us-ascii?Q?6oo5oIEA7g0CULiUu2HvRF59F53bAAYRvWFct6KnfT85bdjLfnryGrwUMrfd?= =?us-ascii?Q?fgQvS5TcCjN8fpEEbB6muBbeU3NViUN/7KQplowe7E+SF+Exl8WBHgPuSjca?= =?us-ascii?Q?5Vlz3XvFQoqJkrNlXA9ZpgYWTPStOwuH1oDrx+WW+H5rL2FmvDS4NuFVuxt4?= =?us-ascii?Q?x113Co8A6VLGtfSPZeV7ZoWufQvbE1zgKCpdv3z8exL4l+FDZ4WFgDVzK4cI?= =?us-ascii?Q?kzOK27rINXBGmclJmVLMX2LEERIBoX5Mz/84+dcF3r4d4htowSpAfZgK4lxu?= =?us-ascii?Q?78MWPLZaOiEIzXxSg75G2OPz9y1o1Qo+al0GFv3283nVQTP0+x5hlaexqgnj?= =?us-ascii?Q?Uuk/0EPBYP7jyrcDEYe18tXp41svt5bWUMmFXp7ZypENxvanZzE8qWqi6qBF?= =?us-ascii?Q?XC8ahDvrDfL1fXkbQW5EsqHGgNK7sMDqfkvi3HPaEdae5pPXD3IlTd5tBcqG?= =?us-ascii?Q?AFzd9Y9daW6On7D5xQuwILU9pjFcXhiLj+540GD5TqNwsYP7BYh5cuJ1pmZa?= =?us-ascii?Q?Xj0jfaqtHFQdKgXwzrsiDTD1m+O27iA8DlcNH1nphnsfCY3IFkBNmIgkol8v?= =?us-ascii?Q?Fw2I67ZYirvYyFbrnMdO8/xRZqSH7nYICRYg/bl8qxmmLF6vJivcGqn1ew+g?= =?us-ascii?Q?UJt+m9+IVIP/2uUQ3/+ReoSHTfAq1tyYp0VtiPWGkiS9lSJxtCDwYyd/CQTw?= =?us-ascii?Q?zuwGUCYH6TWqxWeNJ7BO3eUjKqJmnYe6IpfcCDYqLbd9fqQq6FygF6A8rX/Y?= =?us-ascii?Q?akA7XkbM6iRWklY43jUV9SeLAEReXtsRmIYqU9p3De8knMZBpOqzXBrP09wi?= =?us-ascii?Q?sYpTnoH2t0kN8HAz7gmqPtHO20vMNc3B78pUfL+tFac2VLYTAThwScX8JL7T?= =?us-ascii?Q?liecMGMtZCBOyWVAdMexMZWUEX/EiZJUyWxJd9n6sL7xj1BnN2LVZxJFmwIp?= =?us-ascii?Q?pgypts259P7qIhSEs06Qfehnp3pfXIIDvj31wMPhAf/AYuSdCLndRvpoV1Bu?= =?us-ascii?Q?DZMb61h/SFpVCJgKwVF4AtRHvveBZ9n3WO6t2xSnI0kdxzb9IrjvHRJHSlJ4?= =?us-ascii?Q?8+MKfPVHrs/zEExrMVl2DS8ClAA+98O39uUJ?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PUZPR01MB5405.apcprd01.prod.exchangelabs.com;PTR:;CAT:NONE;SFS:(13230040)(52116014)(366016)(1800799024)(376014)(38350700014);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?swPZBROKxMgyyfrUZAkLPADJXWBjBP9q8jHdLjv9ycnnueqpnAsHKIhYZR/H?= =?us-ascii?Q?SuwYzrwGISvrFMQ1kV+V5qVeBWrQVfQVwZbWbzBXzbmTROPJat2PLl3uwU1R?= =?us-ascii?Q?epfhH+1wO9DjLoNMbktRo63Vq7bAdSH31uKGGxrq/8ZoHpWzNj/3U4cXwymI?= =?us-ascii?Q?ArGPkJFkojrQDMRBDiVqR9bC538tfWmjAu7YUif8ZLZbUziKzq+L0/FSAlTk?= =?us-ascii?Q?QQArXni1USGBgdzFoZu6L/zeyT4c/i91jAgySw6fN1Fh/BZC+0VetxdgTqLj?= =?us-ascii?Q?A6Ruu8s7aBG/GR6ZzJTqKbZgzMzxE7vtyXRsiSBtRfADdgrr84lCy/6xGx5n?= =?us-ascii?Q?7PPICA9ffi3WDLXiv5madz861W3kH+VlowL3Y+NfdLYadK0aATC7FToy6mB4?= =?us-ascii?Q?8qk0bRo+uvjyisODq4LH5PJsx5Il/NFN5jGpeeI4HwVkYMcslWGh24289mCj?= =?us-ascii?Q?kz0rpn6U68e5GbJP51mu3Dj7scIJ2ztWO0mYZJf188BxdjsEpFoXHTSJDEDX?= =?us-ascii?Q?PYIvZ741GL8s5+G4zR4dTMtagWFm2grvSExPPk8aXGl4vLiSy47xAT/L2oXi?= =?us-ascii?Q?6rtAuqNMZskxzVcGOZ+TqREzC+GdVHxrSZpT9xjKu3am90qQOCuUzs3n+YIm?= =?us-ascii?Q?kvJpRvbh+tybcd0kOJBYVou012LoxxuvN9olzAsfgNRWgKeZxpZ/uAjknrM/?= =?us-ascii?Q?34eYfdMaTK4rpw56zCOVDSU9n1rBceCfojNdC31wJ7jwTCsFbjuYnwlymMTR?= =?us-ascii?Q?2aeQwC9S0VHyOIMbLWnko1srWxpLgnrMoKD7Bf5OYm3y4eIFq/wQKmsh/XwQ?= =?us-ascii?Q?g3kzXnYA7dpYgaMTEgxxyyRPoGtHN3r9Sq7n8O/vtWVMoCdgMLmCMeYOgGZ5?= =?us-ascii?Q?4UAkxhYsHOo7oFFHt/xLWLaS/+6zHZ3iR6E8LFmxlOUEPMf+Q/upIY3Ujs6D?= =?us-ascii?Q?Xgoys38jsPb484uytvDkXmXr3x0hcSX+Rd8kTToUFKe4kTkGhTWDkTxGn5jb?= =?us-ascii?Q?p+EMv+/BM+v7xQSGtPWhVlbxTVadePcYS4Ihqz/1Frq225FEnX8m4M2kXYVp?= =?us-ascii?Q?1cnI4nd8SfRm0/AqnRPtmA+pXOs8wl0m9iJ8CTRCO4hzd9/XtBN71QadMmyR?= =?us-ascii?Q?+aIuMvltCAAZsXgPLKhu4fa2jYE6JQHNR1jt04KNXWz+N6HV1Pd/Og+dj5Jr?= =?us-ascii?Q?8M7U1/f35LS9TXkKJbVZ5/HtSd6a4Y9WCkjtqEyrl/4FfjV4Xo81AYCgER2Q?= =?us-ascii?Q?/w5ZOU3VLEVw+DEPXvsYHselLU04SlsL3MqCCrpZkeA0YMWAXABC51HlRDoA?= =?us-ascii?Q?yGrdwdZ4MWFnujR+7orAXqBr5WTEkKwqk5AtRzrwUUyd23qQwXaurE7LLZWC?= =?us-ascii?Q?FCHH/k6gdHwefVkpuM1lUtksBjYzsVyAgsQDm4tNttNdQ7nvKjzJ7pSensh9?= =?us-ascii?Q?ome/69hom4FPwSCC6yNsQE7hfntBpxBvYgxXgtSBtX9b+QMSDzh1QBrzDI90?= =?us-ascii?Q?pFJGJnrBRM0oHmp3De9/Pky6HOV4X6xYGMLGsa2NVJUK6wlFaTv/o6WPXKE1?= =?us-ascii?Q?dOWyfF3DF8wzM8zC0YffZsbf0sROn9SwXvEdkT1OcE3kg32c+QpA2FdZaRWJ?= =?us-ascii?Q?AA=3D=3D?= X-OriginatorOrg: moxa.com X-MS-Exchange-CrossTenant-Network-Message-Id: 0d19d256-d7b7-4d61-020c-08de2ffd4633 X-MS-Exchange-CrossTenant-AuthSource: PUZPR01MB5405.apcprd01.prod.exchangelabs.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Nov 2025 10:43:18.6090 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 5571c7d4-286b-47f6-9dd5-0aa688773c8e X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: XupNBqOuV+ufpNdJm3ozxo9/pFbV2MxMQDhF2hJO/R9/NwucbRJYQok4GaTFVb+CrMaj7zQYArxSryNeEjZv2GSyjQaFK3St7/G3Hrt7aJ0= X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYZPR01MB4235 Content-Type: text/plain; charset="utf-8" Remove the legacy mxser driver and replace it with 8250-based implementation (8250_mxupci) for Moxa UPCI serial devices. This simplifies maintenance and improves integration with the existing 8250 serial infrastructure. Signed-off-by: Crescent Hsieh --- drivers/tty/Kconfig | 13 - drivers/tty/Makefile | 1 - drivers/tty/mxser.c | 1934 ------------------------- drivers/tty/serial/8250/8250_mxupci.c | 165 +++ drivers/tty/serial/8250/Kconfig | 11 + drivers/tty/serial/8250/Makefile | 1 + 6 files changed, 177 insertions(+), 1948 deletions(-) delete mode 100644 drivers/tty/mxser.c create mode 100644 drivers/tty/serial/8250/8250_mxupci.c diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 63a494d36a1f..3973b52bcfce 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -218,19 +218,6 @@ config MOXA_INTELLIO To compile this driver as a module, choose M here: the module will be called moxa. =20 -config MOXA_SMARTIO - tristate "Moxa SmartIO support v. 2.0" - depends on SERIAL_NONSTANDARD && PCI && HAS_IOPORT - help - Say Y here if you have a Moxa SmartIO multiport serial card and/or - want to help develop a new version of this driver. - - This is upgraded (1.9.1) driver from original Moxa drivers with - changes finally resulting in PCI probing. - - This driver can also be built as a module. The module will be called - mxser. If you want to do that, say M here. - config SYNCLINK_GT tristate "SyncLink GT/AC support" depends on SERIAL_NONSTANDARD && PCI diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 07aca5184a55..7db70532e8da 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -18,7 +18,6 @@ obj-$(CONFIG_SERIAL_DEV_BUS) +=3D serdev/ # tty drivers obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) +=3D amiserial.o obj-$(CONFIG_MOXA_INTELLIO) +=3D moxa.o -obj-$(CONFIG_MOXA_SMARTIO) +=3D mxser.o obj-$(CONFIG_NOZOMI) +=3D nozomi.o obj-$(CONFIG_NULL_TTY) +=3D ttynull.o obj-$(CONFIG_SYNCLINK_GT) +=3D synclink_gt.o diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c deleted file mode 100644 index 4d45eca4929a..000000000000 --- a/drivers/tty/mxser.c +++ /dev/null @@ -1,1934 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * mxser.c -- MOXA Smartio/Industio family multiport serial driv= er. - * - * Copyright (C) 1999-2006 Moxa Technologies (support@moxa.com). - * Copyright (C) 2006-2008 Jiri Slaby - * - * This code is loosely based on the 1.8 moxa driver which is based on - * Linux serial driver, written by Linus Torvalds, Theodore T'so and - * others. - * - * Fed through a cleanup, indent and remove of non 2.6 code by Alan Cox - * . The original 1.8 code is available on - * www.moxa.com. - * - Fixed x86_64 cleanness - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* - * Semi-public control interfaces - */ - -/* - * MOXA ioctls - */ - -#define MOXA 0x400 -#define MOXA_SET_OP_MODE (MOXA + 66) -#define MOXA_GET_OP_MODE (MOXA + 67) - -#define RS232_MODE 0 -#define RS485_2WIRE_MODE 1 -#define RS422_MODE 2 -#define RS485_4WIRE_MODE 3 -#define OP_MODE_MASK 3 - -/* --------------------------------------------------- */ - -/* - * Follow just what Moxa Must chip defines. - * - * When LCR register (offset 0x03) is written the following value, the Mus= t chip - * will enter enhanced mode. And a write to EFR (offset 0x02) bit 6,7 will - * change bank. - */ -#define MOXA_MUST_ENTER_ENHANCED 0xBF - -/* when enhanced mode is enabled, access to general bank register */ -#define MOXA_MUST_GDL_REGISTER 0x07 -#define MOXA_MUST_GDL_MASK 0x7F -#define MOXA_MUST_GDL_HAS_BAD_DATA 0x80 - -#define MOXA_MUST_LSR_RERR 0x80 /* error in receive FIFO */ -/* enhanced register bank select and enhanced mode setting register */ -/* This works only when LCR register equals to 0xBF */ -#define MOXA_MUST_EFR_REGISTER 0x02 -#define MOXA_MUST_EFR_EFRB_ENABLE 0x10 /* enhanced mode enable */ -/* enhanced register bank set 0, 1, 2 */ -#define MOXA_MUST_EFR_BANK0 0x00 -#define MOXA_MUST_EFR_BANK1 0x40 -#define MOXA_MUST_EFR_BANK2 0x80 -#define MOXA_MUST_EFR_BANK3 0xC0 -#define MOXA_MUST_EFR_BANK_MASK 0xC0 - -/* set XON1 value register, when LCR=3D0xBF and change to bank0 */ -#define MOXA_MUST_XON1_REGISTER 0x04 - -/* set XON2 value register, when LCR=3D0xBF and change to bank0 */ -#define MOXA_MUST_XON2_REGISTER 0x05 - -/* set XOFF1 value register, when LCR=3D0xBF and change to bank0 */ -#define MOXA_MUST_XOFF1_REGISTER 0x06 - -/* set XOFF2 value register, when LCR=3D0xBF and change to bank0 */ -#define MOXA_MUST_XOFF2_REGISTER 0x07 - -#define MOXA_MUST_RBRTL_REGISTER 0x04 -#define MOXA_MUST_RBRTH_REGISTER 0x05 -#define MOXA_MUST_RBRTI_REGISTER 0x06 -#define MOXA_MUST_THRTL_REGISTER 0x07 -#define MOXA_MUST_ENUM_REGISTER 0x04 -#define MOXA_MUST_HWID_REGISTER 0x05 -#define MOXA_MUST_ECR_REGISTER 0x06 -#define MOXA_MUST_CSR_REGISTER 0x07 - -#define MOXA_MUST_FCR_GDA_MODE_ENABLE 0x20 /* good data mode enable */ -#define MOXA_MUST_FCR_GDA_ONLY_ENABLE 0x10 /* only good data put into RxFI= FO */ - -#define MOXA_MUST_IER_ECTSI 0x80 /* enable CTS interrupt */ -#define MOXA_MUST_IER_ERTSI 0x40 /* enable RTS interrupt */ -#define MOXA_MUST_IER_XINT 0x20 /* enable Xon/Xoff interrupt */ -#define MOXA_MUST_IER_EGDAI 0x10 /* enable GDA interrupt */ - -#define MOXA_MUST_RECV_ISR (UART_IER_RDI | MOXA_MUST_IER_EGDAI) - -/* GDA interrupt pending */ -#define MOXA_MUST_IIR_GDA 0x1C -#define MOXA_MUST_IIR_RDA 0x04 -#define MOXA_MUST_IIR_RTO 0x0C -#define MOXA_MUST_IIR_LSR 0x06 - -/* received Xon/Xoff or specical interrupt pending */ -#define MOXA_MUST_IIR_XSC 0x10 - -/* RTS/CTS change state interrupt pending */ -#define MOXA_MUST_IIR_RTSCTS 0x20 -#define MOXA_MUST_IIR_MASK 0x3E - -#define MOXA_MUST_MCR_XON_FLAG 0x40 -#define MOXA_MUST_MCR_XON_ANY 0x80 -#define MOXA_MUST_MCR_TX_XON 0x08 - -#define MOXA_MUST_EFR_SF_MASK 0x0F /* software flow control on chip mask = value */ -#define MOXA_MUST_EFR_SF_TX1 0x08 /* send Xon1/Xoff1 */ -#define MOXA_MUST_EFR_SF_TX2 0x04 /* send Xon2/Xoff2 */ -#define MOXA_MUST_EFR_SF_TX12 0x0C /* send Xon1,Xon2/Xoff1,Xoff2 */ -#define MOXA_MUST_EFR_SF_TX_NO 0x00 /* don't send Xon/Xoff */ -#define MOXA_MUST_EFR_SF_TX_MASK 0x0C /* Tx software flow control mask */ -#define MOXA_MUST_EFR_SF_RX_NO 0x00 /* don't receive Xon/Xoff */ -#define MOXA_MUST_EFR_SF_RX1 0x02 /* receive Xon1/Xoff1 */ -#define MOXA_MUST_EFR_SF_RX2 0x01 /* receive Xon2/Xoff2 */ -#define MOXA_MUST_EFR_SF_RX12 0x03 /* receive Xon1,Xon2/Xoff1,Xoff2 */ -#define MOXA_MUST_EFR_SF_RX_MASK 0x03 /* Rx software flow control mask */ - -#define MXSERMAJOR 174 - -#define MXSER_BOARDS 4 /* Max. boards */ -#define MXSER_PORTS_PER_BOARD 8 /* Max. ports per board */ -#define MXSER_PORTS (MXSER_BOARDS * MXSER_PORTS_PER_BOARD) -#define MXSER_ISR_PASS_LIMIT 100 - -#define WAKEUP_CHARS 256 - -#define MXSER_BAUD_BASE 921600 -#define MXSER_CUSTOM_DIVISOR (MXSER_BAUD_BASE * 16) - -#define PCI_DEVICE_ID_MOXA_RC7000 0x0001 -#define PCI_DEVICE_ID_MOXA_CP102 0x1020 -#define PCI_DEVICE_ID_MOXA_CP102UL 0x1021 -#define PCI_DEVICE_ID_MOXA_CP102U 0x1022 -#define PCI_DEVICE_ID_MOXA_CP102UF 0x1023 -#define PCI_DEVICE_ID_MOXA_C104 0x1040 -#define PCI_DEVICE_ID_MOXA_CP104U 0x1041 -#define PCI_DEVICE_ID_MOXA_CP104JU 0x1042 -#define PCI_DEVICE_ID_MOXA_CP104EL 0x1043 -#define PCI_DEVICE_ID_MOXA_POS104UL 0x1044 -#define PCI_DEVICE_ID_MOXA_CB108 0x1080 -#define PCI_DEVICE_ID_MOXA_CP112UL 0x1120 -#define PCI_DEVICE_ID_MOXA_CT114 0x1140 -#define PCI_DEVICE_ID_MOXA_CP114 0x1141 -#define PCI_DEVICE_ID_MOXA_CB114 0x1142 -#define PCI_DEVICE_ID_MOXA_CP114UL 0x1143 -#define PCI_DEVICE_ID_MOXA_CP118U 0x1180 -#define PCI_DEVICE_ID_MOXA_CP118EL 0x1181 -#define PCI_DEVICE_ID_MOXA_CP132 0x1320 -#define PCI_DEVICE_ID_MOXA_CP132U 0x1321 -#define PCI_DEVICE_ID_MOXA_CP134U 0x1340 -#define PCI_DEVICE_ID_MOXA_CB134I 0x1341 -#define PCI_DEVICE_ID_MOXA_CP138U 0x1380 -#define PCI_DEVICE_ID_MOXA_C168 0x1680 -#define PCI_DEVICE_ID_MOXA_CP168U 0x1681 -#define PCI_DEVICE_ID_MOXA_CP168EL 0x1682 - -#define MXSER_NPORTS(ddata) ((ddata) & 0xffU) -#define MXSER_HIGHBAUD 0x0100 - -enum mxser_must_hwid { - MOXA_OTHER_UART =3D 0x00, - MOXA_MUST_MU150_HWID =3D 0x01, - MOXA_MUST_MU860_HWID =3D 0x02, -}; - -static const struct { - u8 type; - u8 fifo_size; - u8 rx_high_water; - u8 rx_low_water; - speed_t max_baud; -} Gpci_uart_info[] =3D { - { MOXA_OTHER_UART, 16, 14, 1, 921600 }, - { MOXA_MUST_MU150_HWID, 64, 48, 16, 230400 }, - { MOXA_MUST_MU860_HWID, 128, 96, 32, 921600 } -}; -#define UART_INFO_NUM ARRAY_SIZE(Gpci_uart_info) - -static const struct pci_device_id mxser_pcibrds[] =3D { - { PCI_DEVICE_DATA(MOXA, C168, 8) }, - { PCI_DEVICE_DATA(MOXA, C104, 4) }, - { PCI_DEVICE_DATA(MOXA, CP132, 2) }, - { PCI_DEVICE_DATA(MOXA, CP114, 4) }, - { PCI_DEVICE_DATA(MOXA, CT114, 4) }, - { PCI_DEVICE_DATA(MOXA, CP102, 2 | MXSER_HIGHBAUD) }, - { PCI_DEVICE_DATA(MOXA, CP104U, 4) }, - { PCI_DEVICE_DATA(MOXA, CP168U, 8) }, - { PCI_DEVICE_DATA(MOXA, CP132U, 2) }, - { PCI_DEVICE_DATA(MOXA, CP134U, 4) }, - { PCI_DEVICE_DATA(MOXA, CP104JU, 4) }, - { PCI_DEVICE_DATA(MOXA, RC7000, 8) }, /* RC7000 */ - { PCI_DEVICE_DATA(MOXA, CP118U, 8) }, - { PCI_DEVICE_DATA(MOXA, CP102UL, 2) }, - { PCI_DEVICE_DATA(MOXA, CP102U, 2) }, - { PCI_DEVICE_DATA(MOXA, CP118EL, 8) }, - { PCI_DEVICE_DATA(MOXA, CP168EL, 8) }, - { PCI_DEVICE_DATA(MOXA, CP104EL, 4) }, - { PCI_DEVICE_DATA(MOXA, CB108, 8) }, - { PCI_DEVICE_DATA(MOXA, CB114, 4) }, - { PCI_DEVICE_DATA(MOXA, CB134I, 4) }, - { PCI_DEVICE_DATA(MOXA, CP138U, 8) }, - { PCI_DEVICE_DATA(MOXA, POS104UL, 4) }, - { PCI_DEVICE_DATA(MOXA, CP114UL, 4) }, - { PCI_DEVICE_DATA(MOXA, CP102UF, 2) }, - { PCI_DEVICE_DATA(MOXA, CP112UL, 2) }, - { } -}; -MODULE_DEVICE_TABLE(pci, mxser_pcibrds); - -static int ttymajor =3D MXSERMAJOR; - -/* Variables for insmod */ - -MODULE_AUTHOR("Casper Yang"); -MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Dr= iver"); -module_param(ttymajor, int, 0); -MODULE_LICENSE("GPL"); - -struct mxser_board; - -struct mxser_port { - struct tty_port port; - struct mxser_board *board; - - unsigned long ioaddr; - unsigned long opmode_ioaddr; - - u8 rx_high_water; - u8 rx_low_water; - int type; /* UART type */ - - u8 x_char; /* xon/xoff character */ - u8 IER; /* Interrupt Enable Register */ - u8 MCR; /* Modem control register */ - u8 FCR; /* FIFO control register */ - - struct async_icount icount; /* kernel counters for 4 input interrupts */ - unsigned int timeout; - - u8 read_status_mask; - u8 ignore_status_mask; - u8 xmit_fifo_size; - - spinlock_t slock; -}; - -struct mxser_board { - unsigned int idx; - unsigned short nports; - int irq; - unsigned long vector; - - enum mxser_must_hwid must_hwid; - speed_t max_baud; - - struct mxser_port ports[] /* __counted_by(nports) */; -}; - -static DECLARE_BITMAP(mxser_boards, MXSER_BOARDS); -static struct tty_driver *mxvar_sdriver; - -static u8 __mxser_must_set_EFR(unsigned long baseio, u8 clear, u8 set, - bool restore_LCR) -{ - u8 oldlcr, efr; - - oldlcr =3D inb(baseio + UART_LCR); - outb(MOXA_MUST_ENTER_ENHANCED, baseio + UART_LCR); - - efr =3D inb(baseio + MOXA_MUST_EFR_REGISTER); - efr &=3D ~clear; - efr |=3D set; - - outb(efr, baseio + MOXA_MUST_EFR_REGISTER); - - if (restore_LCR) - outb(oldlcr, baseio + UART_LCR); - - return oldlcr; -} - -static u8 mxser_must_select_bank(unsigned long baseio, u8 bank) -{ - return __mxser_must_set_EFR(baseio, MOXA_MUST_EFR_BANK_MASK, bank, - false); -} - -static void mxser_set_must_xon1_value(unsigned long baseio, u8 value) -{ - u8 oldlcr =3D mxser_must_select_bank(baseio, MOXA_MUST_EFR_BANK0); - outb(value, baseio + MOXA_MUST_XON1_REGISTER); - outb(oldlcr, baseio + UART_LCR); -} - -static void mxser_set_must_xoff1_value(unsigned long baseio, u8 value) -{ - u8 oldlcr =3D mxser_must_select_bank(baseio, MOXA_MUST_EFR_BANK0); - outb(value, baseio + MOXA_MUST_XOFF1_REGISTER); - outb(oldlcr, baseio + UART_LCR); -} - -static void mxser_set_must_fifo_value(struct mxser_port *info) -{ - u8 oldlcr =3D mxser_must_select_bank(info->ioaddr, MOXA_MUST_EFR_BANK1); - outb(info->rx_high_water, info->ioaddr + MOXA_MUST_RBRTH_REGISTER); - outb(info->rx_high_water, info->ioaddr + MOXA_MUST_RBRTI_REGISTER); - outb(info->rx_low_water, info->ioaddr + MOXA_MUST_RBRTL_REGISTER); - outb(oldlcr, info->ioaddr + UART_LCR); -} - -static void mxser_set_must_enum_value(unsigned long baseio, u8 value) -{ - u8 oldlcr =3D mxser_must_select_bank(baseio, MOXA_MUST_EFR_BANK2); - outb(value, baseio + MOXA_MUST_ENUM_REGISTER); - outb(oldlcr, baseio + UART_LCR); -} - -static u8 mxser_get_must_hardware_id(unsigned long baseio) -{ - u8 oldlcr =3D mxser_must_select_bank(baseio, MOXA_MUST_EFR_BANK2); - u8 id =3D inb(baseio + MOXA_MUST_HWID_REGISTER); - outb(oldlcr, baseio + UART_LCR); - - return id; -} - -static void mxser_must_set_EFR(unsigned long baseio, u8 clear, u8 set) -{ - __mxser_must_set_EFR(baseio, clear, set, true); -} - -static void mxser_must_set_enhance_mode(unsigned long baseio, bool enable) -{ - mxser_must_set_EFR(baseio, - enable ? 0 : MOXA_MUST_EFR_EFRB_ENABLE, - enable ? MOXA_MUST_EFR_EFRB_ENABLE : 0); -} - -static void mxser_must_no_sw_flow_control(unsigned long baseio) -{ - mxser_must_set_EFR(baseio, MOXA_MUST_EFR_SF_MASK, 0); -} - -static void mxser_must_set_tx_sw_flow_control(unsigned long baseio, bool e= nable) -{ - mxser_must_set_EFR(baseio, MOXA_MUST_EFR_SF_TX_MASK, - enable ? MOXA_MUST_EFR_SF_TX1 : 0); -} - -static void mxser_must_set_rx_sw_flow_control(unsigned long baseio, bool e= nable) -{ - mxser_must_set_EFR(baseio, MOXA_MUST_EFR_SF_RX_MASK, - enable ? MOXA_MUST_EFR_SF_RX1 : 0); -} - -static enum mxser_must_hwid mxser_must_get_hwid(unsigned long io) -{ - u8 oldmcr, hwid; - int i; - - outb(0, io + UART_LCR); - mxser_must_set_enhance_mode(io, false); - oldmcr =3D inb(io + UART_MCR); - outb(0, io + UART_MCR); - mxser_set_must_xon1_value(io, 0x11); - if (inb(io + UART_MCR) !=3D 0) { - outb(oldmcr, io + UART_MCR); - return MOXA_OTHER_UART; - } - - hwid =3D mxser_get_must_hardware_id(io); - for (i =3D 1; i < UART_INFO_NUM; i++) /* 0 =3D OTHER_UART */ - if (hwid =3D=3D Gpci_uart_info[i].type) - return hwid; - - return MOXA_OTHER_UART; -} - -static bool mxser_16550A_or_MUST(struct mxser_port *info) -{ - return info->type =3D=3D PORT_16550A || info->board->must_hwid; -} - -static void mxser_process_txrx_fifo(struct mxser_port *info) -{ - unsigned int i; - - if (info->type =3D=3D PORT_16450 || info->type =3D=3D PORT_8250) { - info->rx_high_water =3D 1; - info->rx_low_water =3D 1; - info->xmit_fifo_size =3D 1; - return; - } - - for (i =3D 0; i < UART_INFO_NUM; i++) - if (info->board->must_hwid =3D=3D Gpci_uart_info[i].type) { - info->rx_low_water =3D Gpci_uart_info[i].rx_low_water; - info->rx_high_water =3D Gpci_uart_info[i].rx_high_water; - info->xmit_fifo_size =3D Gpci_uart_info[i].fifo_size; - break; - } -} - -static void __mxser_start_tx(struct mxser_port *info) -{ - outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER); - info->IER |=3D UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); -} - -static void mxser_start_tx(struct mxser_port *info) -{ - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - __mxser_start_tx(info); - spin_unlock_irqrestore(&info->slock, flags); -} - -static void __mxser_stop_tx(struct mxser_port *info) -{ - info->IER &=3D ~UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); -} - -static bool mxser_carrier_raised(struct tty_port *port) -{ - struct mxser_port *mp =3D container_of(port, struct mxser_port, port); - - return inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD; -} - -static void mxser_dtr_rts(struct tty_port *port, bool active) -{ - struct mxser_port *mp =3D container_of(port, struct mxser_port, port); - unsigned long flags; - u8 mcr; - - spin_lock_irqsave(&mp->slock, flags); - mcr =3D inb(mp->ioaddr + UART_MCR); - if (active) - mcr |=3D UART_MCR_DTR | UART_MCR_RTS; - else - mcr &=3D ~(UART_MCR_DTR | UART_MCR_RTS); - outb(mcr, mp->ioaddr + UART_MCR); - spin_unlock_irqrestore(&mp->slock, flags); -} - -static int mxser_set_baud(struct tty_struct *tty, speed_t newspd) -{ - struct mxser_port *info =3D tty->driver_data; - unsigned int quot =3D 0, baud; - unsigned char cval; - u64 timeout; - - if (newspd > info->board->max_baud) - return -1; - - if (newspd =3D=3D 134) { - quot =3D 2 * MXSER_BAUD_BASE / 269; - tty_encode_baud_rate(tty, 134, 134); - } else if (newspd) { - quot =3D MXSER_BAUD_BASE / newspd; - if (quot =3D=3D 0) - quot =3D 1; - baud =3D MXSER_BAUD_BASE / quot; - tty_encode_baud_rate(tty, baud, baud); - } else { - quot =3D 0; - } - - /* - * worst case (128 * 1000 * 10 * 18432) needs 35 bits, so divide in the - * u64 domain - */ - timeout =3D (u64)info->xmit_fifo_size * HZ * 10 * quot; - do_div(timeout, MXSER_BAUD_BASE); - info->timeout =3D timeout + HZ / 50; /* Add .02 seconds of slop */ - - if (quot) { - info->MCR |=3D UART_MCR_DTR; - outb(info->MCR, info->ioaddr + UART_MCR); - } else { - info->MCR &=3D ~UART_MCR_DTR; - outb(info->MCR, info->ioaddr + UART_MCR); - return 0; - } - - cval =3D inb(info->ioaddr + UART_LCR); - - outb(cval | UART_LCR_DLAB, info->ioaddr + UART_LCR); /* set DLAB */ - - outb(quot & 0xff, info->ioaddr + UART_DLL); /* LS of divisor */ - outb(quot >> 8, info->ioaddr + UART_DLM); /* MS of divisor */ - outb(cval, info->ioaddr + UART_LCR); /* reset DLAB */ - - if (C_BAUD(tty) =3D=3D BOTHER) { - quot =3D MXSER_BAUD_BASE % newspd; - quot *=3D 8; - if (quot % newspd > newspd / 2) { - quot /=3D newspd; - quot++; - } else - quot /=3D newspd; - - mxser_set_must_enum_value(info->ioaddr, quot); - } else { - mxser_set_must_enum_value(info->ioaddr, 0); - } - - return 0; -} - -static void mxser_handle_cts(struct tty_struct *tty, struct mxser_port *in= fo, - u8 msr) -{ - bool cts =3D msr & UART_MSR_CTS; - - if (tty->hw_stopped) { - if (cts) { - tty->hw_stopped =3D false; - - if (!mxser_16550A_or_MUST(info)) - __mxser_start_tx(info); - tty_wakeup(tty); - } - return; - } else if (cts) - return; - - tty->hw_stopped =3D true; - if (!mxser_16550A_or_MUST(info)) - __mxser_stop_tx(info); -} - -/* - * This routine is called to set the UART divisor registers to match - * the specified baud rate for a serial port. - */ -static void mxser_change_speed(struct tty_struct *tty, - const struct ktermios *old_termios) -{ - struct mxser_port *info =3D tty->driver_data; - unsigned cflag, cval; - - cflag =3D tty->termios.c_cflag; - - if (mxser_set_baud(tty, tty_get_baud_rate(tty))) { - /* Use previous rate on a failure */ - if (old_termios) { - speed_t baud =3D tty_termios_baud_rate(old_termios); - tty_encode_baud_rate(tty, baud, baud); - } - } - - /* byte size and parity */ - cval =3D UART_LCR_WLEN(tty_get_char_size(tty->termios.c_cflag)); - - if (cflag & CSTOPB) - cval |=3D UART_LCR_STOP; - if (cflag & PARENB) - cval |=3D UART_LCR_PARITY; - if (!(cflag & PARODD)) - cval |=3D UART_LCR_EPAR; - if (cflag & CMSPAR) - cval |=3D UART_LCR_SPAR; - - info->FCR =3D 0; - if (info->board->must_hwid) { - info->FCR |=3D UART_FCR_ENABLE_FIFO | - MOXA_MUST_FCR_GDA_MODE_ENABLE; - mxser_set_must_fifo_value(info); - } else if (info->type !=3D PORT_8250 && info->type !=3D PORT_16450) { - info->FCR |=3D UART_FCR_ENABLE_FIFO; - switch (info->rx_high_water) { - case 1: - info->FCR |=3D UART_FCR_TRIGGER_1; - break; - case 4: - info->FCR |=3D UART_FCR_TRIGGER_4; - break; - case 8: - info->FCR |=3D UART_FCR_TRIGGER_8; - break; - default: - info->FCR |=3D UART_FCR_TRIGGER_14; - break; - } - } - - /* CTS flow control flag and modem status interrupts */ - info->IER &=3D ~UART_IER_MSI; - info->MCR &=3D ~UART_MCR_AFE; - tty_port_set_cts_flow(&info->port, cflag & CRTSCTS); - if (cflag & CRTSCTS) { - info->IER |=3D UART_IER_MSI; - if (mxser_16550A_or_MUST(info)) { - info->MCR |=3D UART_MCR_AFE; - } else { - mxser_handle_cts(tty, info, - inb(info->ioaddr + UART_MSR)); - } - } - outb(info->MCR, info->ioaddr + UART_MCR); - tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); - if (~cflag & CLOCAL) - info->IER |=3D UART_IER_MSI; - outb(info->IER, info->ioaddr + UART_IER); - - /* - * Set up parity check flag - */ - info->read_status_mask =3D UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (I_INPCK(tty)) - info->read_status_mask |=3D UART_LSR_FE | UART_LSR_PE; - if (I_BRKINT(tty) || I_PARMRK(tty)) - info->read_status_mask |=3D UART_LSR_BI; - - info->ignore_status_mask =3D 0; - - if (I_IGNBRK(tty)) { - info->ignore_status_mask |=3D UART_LSR_BI; - info->read_status_mask |=3D UART_LSR_BI; - /* - * If we're ignore parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (I_IGNPAR(tty)) { - info->ignore_status_mask |=3D - UART_LSR_OE | - UART_LSR_PE | - UART_LSR_FE; - info->read_status_mask |=3D - UART_LSR_OE | - UART_LSR_PE | - UART_LSR_FE; - } - } - if (info->board->must_hwid) { - mxser_set_must_xon1_value(info->ioaddr, START_CHAR(tty)); - mxser_set_must_xoff1_value(info->ioaddr, STOP_CHAR(tty)); - mxser_must_set_rx_sw_flow_control(info->ioaddr, I_IXON(tty)); - mxser_must_set_tx_sw_flow_control(info->ioaddr, I_IXOFF(tty)); - } - - - outb(info->FCR, info->ioaddr + UART_FCR); - outb(cval, info->ioaddr + UART_LCR); -} - -static u8 mxser_check_modem_status(struct tty_struct *tty, - struct mxser_port *port) -{ - u8 msr =3D inb(port->ioaddr + UART_MSR); - - if (!(msr & UART_MSR_ANY_DELTA)) - return msr; - - /* update input line counters */ - if (msr & UART_MSR_TERI) - port->icount.rng++; - if (msr & UART_MSR_DDSR) - port->icount.dsr++; - if (msr & UART_MSR_DDCD) - port->icount.dcd++; - if (msr & UART_MSR_DCTS) - port->icount.cts++; - wake_up_interruptible(&port->port.delta_msr_wait); - - if (tty_port_check_carrier(&port->port) && (msr & UART_MSR_DDCD)) { - if (msr & UART_MSR_DCD) - wake_up_interruptible(&port->port.open_wait); - } - - if (tty_port_cts_enabled(&port->port)) - mxser_handle_cts(tty, port, msr); - - return msr; -} - -static void mxser_disable_and_clear_FIFO(struct mxser_port *info) -{ - u8 fcr =3D UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT; - - if (info->board->must_hwid) - fcr |=3D MOXA_MUST_FCR_GDA_MODE_ENABLE; - - outb(fcr, info->ioaddr + UART_FCR); -} - -static int mxser_activate(struct tty_port *port, struct tty_struct *tty) -{ - struct mxser_port *info =3D container_of(port, struct mxser_port, port); - unsigned long flags; - int ret; - - ret =3D tty_port_alloc_xmit_buf(port); - if (ret < 0) - return ret; - - spin_lock_irqsave(&info->slock, flags); - - if (!info->type) { - set_bit(TTY_IO_ERROR, &tty->flags); - spin_unlock_irqrestore(&info->slock, flags); - ret =3D 0; - goto err_free_xmit; - } - - /* - * Clear the FIFO buffers and disable them - * (they will be reenabled in mxser_change_speed()) - */ - mxser_disable_and_clear_FIFO(info); - - /* - * At this point there's no way the LSR could still be 0xFF; - * if it is, then bail out, because there's likely no UART - * here. - */ - if (inb(info->ioaddr + UART_LSR) =3D=3D 0xff) { - spin_unlock_irqrestore(&info->slock, flags); - if (capable(CAP_SYS_ADMIN)) { - set_bit(TTY_IO_ERROR, &tty->flags); - return 0; - } - - ret =3D -ENODEV; - goto err_free_xmit; - } - - /* - * Clear the interrupt registers. - */ - (void) inb(info->ioaddr + UART_LSR); - (void) inb(info->ioaddr + UART_RX); - (void) inb(info->ioaddr + UART_IIR); - (void) inb(info->ioaddr + UART_MSR); - - /* - * Now, initialize the UART - */ - outb(UART_LCR_WLEN8, info->ioaddr + UART_LCR); /* reset DLAB */ - info->MCR =3D UART_MCR_DTR | UART_MCR_RTS; - outb(info->MCR, info->ioaddr + UART_MCR); - - /* - * Finally, enable interrupts - */ - info->IER =3D UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; - - if (info->board->must_hwid) - info->IER |=3D MOXA_MUST_IER_EGDAI; - outb(info->IER, info->ioaddr + UART_IER); /* enable interrupts */ - - /* - * And clear the interrupt registers again for luck. - */ - (void) inb(info->ioaddr + UART_LSR); - (void) inb(info->ioaddr + UART_RX); - (void) inb(info->ioaddr + UART_IIR); - (void) inb(info->ioaddr + UART_MSR); - - clear_bit(TTY_IO_ERROR, &tty->flags); - kfifo_reset(&port->xmit_fifo); - - /* - * and set the speed of the serial port - */ - mxser_change_speed(tty, NULL); - spin_unlock_irqrestore(&info->slock, flags); - - return 0; -err_free_xmit: - tty_port_free_xmit_buf(port); - return ret; -} - -/* - * To stop accepting input, we disable the receive line status interrupts,= and - * tell the interrupt driver to stop checking the data ready bit in the li= ne - * status register. - */ -static void mxser_stop_rx(struct mxser_port *info) -{ - info->IER &=3D ~UART_IER_RLSI; - if (info->board->must_hwid) - info->IER &=3D ~MOXA_MUST_RECV_ISR; - - outb(info->IER, info->ioaddr + UART_IER); -} - -/* - * This routine will shutdown a serial port - */ -static void mxser_shutdown_port(struct tty_port *port) -{ - struct mxser_port *info =3D container_of(port, struct mxser_port, port); - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - - mxser_stop_rx(info); - - /* - * clear delta_msr_wait queue to avoid mem leaks: we may free the irq - * here so the queue might never be waken up - */ - wake_up_interruptible(&info->port.delta_msr_wait); - - info->IER =3D 0; - outb(0x00, info->ioaddr + UART_IER); - - /* clear Rx/Tx FIFO's */ - mxser_disable_and_clear_FIFO(info); - - /* read data port to reset things */ - (void) inb(info->ioaddr + UART_RX); - - - if (info->board->must_hwid) - mxser_must_no_sw_flow_control(info->ioaddr); - - spin_unlock_irqrestore(&info->slock, flags); - - /* make sure ISR is not running while we free the buffer */ - synchronize_irq(info->board->irq); - - tty_port_free_xmit_buf(port); -} - -/* - * This routine is called whenever a serial port is opened. It - * enables interrupts for a serial port, linking in its async structure in= to - * the IRQ chain. It also performs the serial-specific - * initialization for the tty structure. - */ -static int mxser_open(struct tty_struct *tty, struct file *filp) -{ - struct tty_port *tport =3D tty->port; - struct mxser_port *port =3D container_of(tport, struct mxser_port, port); - - tty->driver_data =3D port; - - return tty_port_open(tport, tty, filp); -} - -static void mxser_flush_buffer(struct tty_struct *tty) -{ - struct mxser_port *info =3D tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - kfifo_reset(&info->port.xmit_fifo); - - outb(info->FCR | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, - info->ioaddr + UART_FCR); - - spin_unlock_irqrestore(&info->slock, flags); - - tty_wakeup(tty); -} - -static void mxser_close(struct tty_struct *tty, struct file *filp) -{ - tty_port_close(tty->port, tty, filp); -} - -static ssize_t mxser_write(struct tty_struct *tty, const u8 *buf, size_t c= ount) -{ - struct mxser_port *info =3D tty->driver_data; - unsigned long flags; - size_t written; - bool is_empty; - - spin_lock_irqsave(&info->slock, flags); - written =3D kfifo_in(&info->port.xmit_fifo, buf, count); - is_empty =3D kfifo_is_empty(&info->port.xmit_fifo); - spin_unlock_irqrestore(&info->slock, flags); - - if (!is_empty && !tty->flow.stopped) - if (!tty->hw_stopped || mxser_16550A_or_MUST(info)) - mxser_start_tx(info); - - return written; -} - -static int mxser_put_char(struct tty_struct *tty, u8 ch) -{ - struct mxser_port *info =3D tty->driver_data; - unsigned long flags; - int ret; - - spin_lock_irqsave(&info->slock, flags); - ret =3D kfifo_put(&info->port.xmit_fifo, ch); - spin_unlock_irqrestore(&info->slock, flags); - - return ret; -} - - -static void mxser_flush_chars(struct tty_struct *tty) -{ - struct mxser_port *info =3D tty->driver_data; - - if (kfifo_is_empty(&info->port.xmit_fifo) || tty->flow.stopped || - (tty->hw_stopped && !mxser_16550A_or_MUST(info))) - return; - - mxser_start_tx(info); -} - -static unsigned int mxser_write_room(struct tty_struct *tty) -{ - struct mxser_port *info =3D tty->driver_data; - - return kfifo_avail(&info->port.xmit_fifo); -} - -static unsigned int mxser_chars_in_buffer(struct tty_struct *tty) -{ - struct mxser_port *info =3D tty->driver_data; - - return kfifo_len(&info->port.xmit_fifo); -} - -/* - * ------------------------------------------------------------ - * friends of mxser_ioctl() - * ------------------------------------------------------------ - */ -static int mxser_get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct mxser_port *info =3D tty->driver_data; - struct tty_port *port =3D &info->port; - unsigned int closing_wait, close_delay; - - mutex_lock(&port->mutex); - - close_delay =3D jiffies_to_msecs(info->port.close_delay) / 10; - closing_wait =3D info->port.closing_wait; - if (closing_wait !=3D ASYNC_CLOSING_WAIT_NONE) - closing_wait =3D jiffies_to_msecs(closing_wait) / 10; - - ss->type =3D info->type; - ss->line =3D tty->index; - ss->port =3D info->ioaddr; - ss->irq =3D info->board->irq; - ss->flags =3D info->port.flags; - ss->baud_base =3D MXSER_BAUD_BASE; - ss->close_delay =3D close_delay; - ss->closing_wait =3D closing_wait; - ss->custom_divisor =3D MXSER_CUSTOM_DIVISOR; - mutex_unlock(&port->mutex); - return 0; -} - -static int mxser_set_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct mxser_port *info =3D tty->driver_data; - struct tty_port *port =3D &info->port; - speed_t baud; - unsigned long sl_flags; - unsigned int old_speed, close_delay, closing_wait; - int retval =3D 0; - - if (tty_io_error(tty)) - return -EIO; - - mutex_lock(&port->mutex); - - if (ss->irq !=3D info->board->irq || - ss->port !=3D info->ioaddr) { - mutex_unlock(&port->mutex); - return -EINVAL; - } - - old_speed =3D port->flags & ASYNC_SPD_MASK; - - close_delay =3D msecs_to_jiffies(ss->close_delay * 10); - closing_wait =3D ss->closing_wait; - if (closing_wait !=3D ASYNC_CLOSING_WAIT_NONE) - closing_wait =3D msecs_to_jiffies(closing_wait * 10); - - if (!capable(CAP_SYS_ADMIN)) { - if ((ss->baud_base !=3D MXSER_BAUD_BASE) || - (close_delay !=3D port->close_delay) || - (closing_wait !=3D port->closing_wait) || - ((ss->flags & ~ASYNC_USR_MASK) !=3D (port->flags & ~ASYNC_USR_MASK))) { - mutex_unlock(&port->mutex); - return -EPERM; - } - port->flags =3D (port->flags & ~ASYNC_USR_MASK) | - (ss->flags & ASYNC_USR_MASK); - } else { - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - port->flags =3D ((port->flags & ~ASYNC_FLAGS) | - (ss->flags & ASYNC_FLAGS)); - port->close_delay =3D close_delay; - port->closing_wait =3D closing_wait; - if ((port->flags & ASYNC_SPD_MASK) =3D=3D ASYNC_SPD_CUST && - (ss->baud_base !=3D MXSER_BAUD_BASE || - ss->custom_divisor !=3D - MXSER_CUSTOM_DIVISOR)) { - if (ss->custom_divisor =3D=3D 0) { - mutex_unlock(&port->mutex); - return -EINVAL; - } - baud =3D ss->baud_base / ss->custom_divisor; - tty_encode_baud_rate(tty, baud, baud); - } - - info->type =3D ss->type; - - mxser_process_txrx_fifo(info); - } - - if (tty_port_initialized(port)) { - if (old_speed !=3D (port->flags & ASYNC_SPD_MASK)) { - spin_lock_irqsave(&info->slock, sl_flags); - mxser_change_speed(tty, NULL); - spin_unlock_irqrestore(&info->slock, sl_flags); - } - } else { - retval =3D mxser_activate(port, tty); - if (retval =3D=3D 0) - tty_port_set_initialized(port, true); - } - mutex_unlock(&port->mutex); - return retval; -} - -/* - * mxser_get_lsr_info - get line status register info - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static int mxser_get_lsr_info(struct mxser_port *info, - unsigned int __user *value) -{ - unsigned char status; - unsigned int result; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - status =3D inb(info->ioaddr + UART_LSR); - spin_unlock_irqrestore(&info->slock, flags); - result =3D ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); - return put_user(result, value); -} - -static int mxser_tiocmget(struct tty_struct *tty) -{ - struct mxser_port *info =3D tty->driver_data; - unsigned char control; - unsigned long flags; - u8 msr; - - if (tty_io_error(tty)) - return -EIO; - - spin_lock_irqsave(&info->slock, flags); - control =3D info->MCR; - msr =3D mxser_check_modem_status(tty, info); - spin_unlock_irqrestore(&info->slock, flags); - - return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | - ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | - ((msr & UART_MSR_DCD) ? TIOCM_CAR : 0) | - ((msr & UART_MSR_RI) ? TIOCM_RNG : 0) | - ((msr & UART_MSR_DSR) ? TIOCM_DSR : 0) | - ((msr & UART_MSR_CTS) ? TIOCM_CTS : 0); -} - -static int mxser_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct mxser_port *info =3D tty->driver_data; - unsigned long flags; - - if (tty_io_error(tty)) - return -EIO; - - spin_lock_irqsave(&info->slock, flags); - - if (set & TIOCM_RTS) - info->MCR |=3D UART_MCR_RTS; - if (set & TIOCM_DTR) - info->MCR |=3D UART_MCR_DTR; - - if (clear & TIOCM_RTS) - info->MCR &=3D ~UART_MCR_RTS; - if (clear & TIOCM_DTR) - info->MCR &=3D ~UART_MCR_DTR; - - outb(info->MCR, info->ioaddr + UART_MCR); - spin_unlock_irqrestore(&info->slock, flags); - return 0; -} - -static int mxser_cflags_changed(struct mxser_port *info, unsigned long arg, - struct async_icount *cprev) -{ - struct async_icount cnow; - unsigned long flags; - int ret; - - spin_lock_irqsave(&info->slock, flags); - cnow =3D info->icount; /* atomic copy */ - spin_unlock_irqrestore(&info->slock, flags); - - ret =3D ((arg & TIOCM_RNG) && (cnow.rng !=3D cprev->rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr !=3D cprev->dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd !=3D cprev->dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts !=3D cprev->cts)); - - *cprev =3D cnow; - - return ret; -} - -/* We should likely switch to TIOCGRS485/TIOCSRS485. */ -static int mxser_ioctl_op_mode(struct mxser_port *port, int index, bool se= t, - int __user *u_opmode) -{ - int opmode, p =3D index % 4; - int shiftbit =3D p * 2; - u8 val; - - if (port->board->must_hwid !=3D MOXA_MUST_MU860_HWID) - return -EFAULT; - - if (set) { - if (get_user(opmode, u_opmode)) - return -EFAULT; - - if (opmode & ~OP_MODE_MASK) - return -EINVAL; - - spin_lock_irq(&port->slock); - val =3D inb(port->opmode_ioaddr); - val &=3D ~(OP_MODE_MASK << shiftbit); - val |=3D (opmode << shiftbit); - outb(val, port->opmode_ioaddr); - spin_unlock_irq(&port->slock); - - return 0; - } - - spin_lock_irq(&port->slock); - opmode =3D inb(port->opmode_ioaddr) >> shiftbit; - spin_unlock_irq(&port->slock); - - return put_user(opmode & OP_MODE_MASK, u_opmode); -} - -static int mxser_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct mxser_port *info =3D tty->driver_data; - struct async_icount cnow; - unsigned long flags; - void __user *argp =3D (void __user *)arg; - - if (cmd =3D=3D MOXA_SET_OP_MODE || cmd =3D=3D MOXA_GET_OP_MODE) - return mxser_ioctl_op_mode(info, tty->index, - cmd =3D=3D MOXA_SET_OP_MODE, argp); - - if (cmd !=3D TIOCMIWAIT && tty_io_error(tty)) - return -EIO; - - switch (cmd) { - case TIOCSERGETLSR: /* Get line status register */ - return mxser_get_lsr_info(info, argp); - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ - case TIOCMIWAIT: - spin_lock_irqsave(&info->slock, flags); - cnow =3D info->icount; /* note the counters on entry */ - spin_unlock_irqrestore(&info->slock, flags); - - return wait_event_interruptible(info->port.delta_msr_wait, - mxser_cflags_changed(info, arg, &cnow)); - default: - return -ENOIOCTLCMD; - } - return 0; -} - - /* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ - -static int mxser_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) - -{ - struct mxser_port *info =3D tty->driver_data; - struct async_icount cnow; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - cnow =3D info->icount; - spin_unlock_irqrestore(&info->slock, flags); - - icount->frame =3D cnow.frame; - icount->brk =3D cnow.brk; - icount->overrun =3D cnow.overrun; - icount->buf_overrun =3D cnow.buf_overrun; - icount->parity =3D cnow.parity; - icount->rx =3D cnow.rx; - icount->tx =3D cnow.tx; - icount->cts =3D cnow.cts; - icount->dsr =3D cnow.dsr; - icount->rng =3D cnow.rng; - icount->dcd =3D cnow.dcd; - return 0; -} - -/* - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled. - */ -static void mxser_throttle(struct tty_struct *tty) -{ - struct mxser_port *info =3D tty->driver_data; - - if (I_IXOFF(tty)) { - if (info->board->must_hwid) { - info->IER &=3D ~MOXA_MUST_RECV_ISR; - outb(info->IER, info->ioaddr + UART_IER); - } else { - info->x_char =3D STOP_CHAR(tty); - outb(0, info->ioaddr + UART_IER); - info->IER |=3D UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - } - } - - if (C_CRTSCTS(tty)) { - info->MCR &=3D ~UART_MCR_RTS; - outb(info->MCR, info->ioaddr + UART_MCR); - } -} - -static void mxser_unthrottle(struct tty_struct *tty) -{ - struct mxser_port *info =3D tty->driver_data; - - /* startrx */ - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char =3D 0; - else { - if (info->board->must_hwid) { - info->IER |=3D MOXA_MUST_RECV_ISR; - outb(info->IER, info->ioaddr + UART_IER); - } else { - info->x_char =3D START_CHAR(tty); - outb(0, info->ioaddr + UART_IER); - info->IER |=3D UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - } - } - } - - if (C_CRTSCTS(tty)) { - info->MCR |=3D UART_MCR_RTS; - outb(info->MCR, info->ioaddr + UART_MCR); - } -} - -/* - * mxser_stop() and mxser_start() - * - * This routines are called before setting or resetting tty->flow.stopped. - * They enable or disable transmitter interrupts, as necessary. - */ -static void mxser_stop(struct tty_struct *tty) -{ - struct mxser_port *info =3D tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - if (info->IER & UART_IER_THRI) - __mxser_stop_tx(info); - spin_unlock_irqrestore(&info->slock, flags); -} - -static void mxser_start(struct tty_struct *tty) -{ - struct mxser_port *info =3D tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - if (!kfifo_is_empty(&info->port.xmit_fifo)) - __mxser_start_tx(info); - spin_unlock_irqrestore(&info->slock, flags); -} - -static void mxser_set_termios(struct tty_struct *tty, - const struct ktermios *old_termios) -{ - struct mxser_port *info =3D tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - mxser_change_speed(tty, old_termios); - spin_unlock_irqrestore(&info->slock, flags); - - if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { - tty->hw_stopped =3D false; - mxser_start(tty); - } - - /* Handle sw stopped */ - if ((old_termios->c_iflag & IXON) && !I_IXON(tty)) { - tty->flow.stopped =3D 0; - - if (info->board->must_hwid) { - spin_lock_irqsave(&info->slock, flags); - mxser_must_set_rx_sw_flow_control(info->ioaddr, false); - spin_unlock_irqrestore(&info->slock, flags); - } - - mxser_start(tty); - } -} - -static bool mxser_tx_empty(struct mxser_port *info) -{ - unsigned long flags; - u8 lsr; - - spin_lock_irqsave(&info->slock, flags); - lsr =3D inb(info->ioaddr + UART_LSR); - spin_unlock_irqrestore(&info->slock, flags); - - return !(lsr & UART_LSR_TEMT); -} - -/* - * mxser_wait_until_sent() --- wait until the transmitter is empty - */ -static void mxser_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct mxser_port *info =3D tty->driver_data; - unsigned long expire, char_time; - - if (info->type =3D=3D PORT_UNKNOWN) - return; - - if (info->xmit_fifo_size =3D=3D 0) - return; /* Just in case.... */ - - /* - * Set the check interval to be 1/5 of the estimated time to - * send a single character, and make it at least 1. The check - * interval should also be less than the timeout. - * - * Note: we have to use pretty tight timings here to satisfy - * the NIST-PCTS. - */ - char_time =3D (info->timeout - HZ / 50) / info->xmit_fifo_size; - char_time =3D char_time / 5; - if (char_time =3D=3D 0) - char_time =3D 1; - if (timeout && timeout < char_time) - char_time =3D timeout; - - char_time =3D jiffies_to_msecs(char_time); - - /* - * If the transmitter hasn't cleared in twice the approximate - * amount of time to send the entire FIFO, it probably won't - * ever clear. This assumes the UART isn't doing flow - * control, which is currently the case. Hence, if it ever - * takes longer than info->timeout, this is probably due to a - * UART bug of some kind. So, we clamp the timeout parameter at - * 2*info->timeout. - */ - if (!timeout || timeout > 2 * info->timeout) - timeout =3D 2 * info->timeout; - - expire =3D jiffies + timeout; - - while (mxser_tx_empty(info)) { - msleep_interruptible(char_time); - if (signal_pending(current)) - break; - if (time_after(jiffies, expire)) - break; - } -} - -/* - * This routine is called by tty_hangup() when a hangup is signaled. - */ -static void mxser_hangup(struct tty_struct *tty) -{ - struct mxser_port *info =3D tty->driver_data; - - mxser_flush_buffer(tty); - tty_port_hangup(&info->port); -} - -/* - * mxser_rs_break() --- routine which turns the break handling on or off - */ -static int mxser_rs_break(struct tty_struct *tty, int break_state) -{ - struct mxser_port *info =3D tty->driver_data; - unsigned long flags; - u8 lcr; - - spin_lock_irqsave(&info->slock, flags); - lcr =3D inb(info->ioaddr + UART_LCR); - if (break_state =3D=3D -1) - lcr |=3D UART_LCR_SBC; - else - lcr &=3D ~UART_LCR_SBC; - outb(lcr, info->ioaddr + UART_LCR); - spin_unlock_irqrestore(&info->slock, flags); - - return 0; -} - -static bool mxser_receive_chars_new(struct mxser_port *port, u8 status) -{ - enum mxser_must_hwid hwid =3D port->board->must_hwid; - u8 gdl; - - if (hwid =3D=3D MOXA_OTHER_UART) - return false; - if (status & (UART_LSR_BRK_ERROR_BITS | MOXA_MUST_LSR_RERR)) - return false; - - gdl =3D inb(port->ioaddr + MOXA_MUST_GDL_REGISTER); - if (hwid =3D=3D MOXA_MUST_MU150_HWID) - gdl &=3D MOXA_MUST_GDL_MASK; - - while (gdl--) { - u8 ch =3D inb(port->ioaddr + UART_RX); - if (!tty_insert_flip_char(&port->port, ch, 0)) - port->icount.buf_overrun++; - } - - return true; -} - -static u8 mxser_receive_chars_old(struct tty_struct *tty, - struct mxser_port *port, u8 status) -{ - enum mxser_must_hwid hwid =3D port->board->must_hwid; - int ignored =3D 0; - int max =3D 256; - u8 ch; - - do { - if (max-- < 0) - break; - - ch =3D inb(port->ioaddr + UART_RX); - if (hwid && (status & UART_LSR_OE)) - outb(port->FCR | UART_FCR_CLEAR_RCVR, - port->ioaddr + UART_FCR); - status &=3D port->read_status_mask; - if (status & port->ignore_status_mask) { - if (++ignored > 100) - break; - } else { - u8 flag =3D 0; - if (status & UART_LSR_BRK_ERROR_BITS) { - if (status & UART_LSR_BI) { - flag =3D TTY_BREAK; - port->icount.brk++; - - if (port->port.flags & ASYNC_SAK) - do_SAK(tty); - } else if (status & UART_LSR_PE) { - flag =3D TTY_PARITY; - port->icount.parity++; - } else if (status & UART_LSR_FE) { - flag =3D TTY_FRAME; - port->icount.frame++; - } else if (status & UART_LSR_OE) { - flag =3D TTY_OVERRUN; - port->icount.overrun++; - } - } - if (!tty_insert_flip_char(&port->port, ch, flag)) { - port->icount.buf_overrun++; - break; - } - } - - if (hwid) - break; - - status =3D inb(port->ioaddr + UART_LSR); - } while (status & UART_LSR_DR); - - return status; -} - -static u8 mxser_receive_chars(struct tty_struct *tty, - struct mxser_port *port, u8 status) -{ - if (!mxser_receive_chars_new(port, status)) - status =3D mxser_receive_chars_old(tty, port, status); - - tty_flip_buffer_push(&port->port); - - return status; -} - -static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port= *port) -{ - int count; - - if (port->x_char) { - outb(port->x_char, port->ioaddr + UART_TX); - port->x_char =3D 0; - port->icount.tx++; - return; - } - - if (kfifo_is_empty(&port->port.xmit_fifo) || tty->flow.stopped || - (tty->hw_stopped && !mxser_16550A_or_MUST(port))) { - __mxser_stop_tx(port); - return; - } - - count =3D port->xmit_fifo_size; - do { - u8 c; - - if (!kfifo_get(&port->port.xmit_fifo, &c)) - break; - - outb(c, port->ioaddr + UART_TX); - port->icount.tx++; - } while (--count > 0); - - if (kfifo_len(&port->port.xmit_fifo) < WAKEUP_CHARS) - tty_wakeup(tty); - - if (kfifo_is_empty(&port->port.xmit_fifo)) - __mxser_stop_tx(port); -} - -static bool mxser_port_isr(struct mxser_port *port) -{ - struct tty_struct *tty; - u8 iir, status; - bool error =3D false; - - iir =3D inb(port->ioaddr + UART_IIR); - if (iir & UART_IIR_NO_INT) - return true; - - iir &=3D MOXA_MUST_IIR_MASK; - tty =3D tty_port_tty_get(&port->port); - if (!tty) { - status =3D inb(port->ioaddr + UART_LSR); - outb(port->FCR | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, - port->ioaddr + UART_FCR); - inb(port->ioaddr + UART_MSR); - - error =3D true; - goto put_tty; - } - - status =3D inb(port->ioaddr + UART_LSR); - - if (port->board->must_hwid) { - if (iir =3D=3D MOXA_MUST_IIR_GDA || - iir =3D=3D MOXA_MUST_IIR_RDA || - iir =3D=3D MOXA_MUST_IIR_RTO || - iir =3D=3D MOXA_MUST_IIR_LSR) - status =3D mxser_receive_chars(tty, port, status); - } else { - status &=3D port->read_status_mask; - if (status & UART_LSR_DR) - status =3D mxser_receive_chars(tty, port, status); - } - - mxser_check_modem_status(tty, port); - - if (port->board->must_hwid) { - if (iir =3D=3D 0x02 && (status & UART_LSR_THRE)) - mxser_transmit_chars(tty, port); - } else { - if (status & UART_LSR_THRE) - mxser_transmit_chars(tty, port); - } - -put_tty: - tty_kref_put(tty); - - return error; -} - -/* - * This is the serial driver's generic interrupt routine - */ -static irqreturn_t mxser_interrupt(int irq, void *dev_id) -{ - struct mxser_board *brd =3D dev_id; - struct mxser_port *port; - unsigned int int_cnt, pass_counter =3D 0; - unsigned int i, max =3D brd->nports; - int handled =3D IRQ_NONE; - u8 irqbits, bits, mask =3D BIT(max) - 1; - - while (pass_counter++ < MXSER_ISR_PASS_LIMIT) { - irqbits =3D inb(brd->vector) & mask; - if (irqbits =3D=3D mask) - break; - - handled =3D IRQ_HANDLED; - for (i =3D 0, bits =3D 1; i < max; i++, irqbits |=3D bits, bits <<=3D 1)= { - if (irqbits =3D=3D mask) - break; - if (bits & irqbits) - continue; - port =3D &brd->ports[i]; - - int_cnt =3D 0; - spin_lock(&port->slock); - do { - if (mxser_port_isr(port)) - break; - } while (int_cnt++ < MXSER_ISR_PASS_LIMIT); - spin_unlock(&port->slock); - } - } - - return handled; -} - -static const struct tty_operations mxser_ops =3D { - .open =3D mxser_open, - .close =3D mxser_close, - .write =3D mxser_write, - .put_char =3D mxser_put_char, - .flush_chars =3D mxser_flush_chars, - .write_room =3D mxser_write_room, - .chars_in_buffer =3D mxser_chars_in_buffer, - .flush_buffer =3D mxser_flush_buffer, - .ioctl =3D mxser_ioctl, - .throttle =3D mxser_throttle, - .unthrottle =3D mxser_unthrottle, - .set_termios =3D mxser_set_termios, - .stop =3D mxser_stop, - .start =3D mxser_start, - .hangup =3D mxser_hangup, - .break_ctl =3D mxser_rs_break, - .wait_until_sent =3D mxser_wait_until_sent, - .tiocmget =3D mxser_tiocmget, - .tiocmset =3D mxser_tiocmset, - .set_serial =3D mxser_set_serial_info, - .get_serial =3D mxser_get_serial_info, - .get_icount =3D mxser_get_icount, -}; - -static const struct tty_port_operations mxser_port_ops =3D { - .carrier_raised =3D mxser_carrier_raised, - .dtr_rts =3D mxser_dtr_rts, - .activate =3D mxser_activate, - .shutdown =3D mxser_shutdown_port, -}; - -/* - * The MOXA Smartio/Industio serial driver boot-time initialization code! - */ - -static void mxser_initbrd(struct mxser_board *brd, bool high_baud) -{ - struct mxser_port *info; - unsigned int i; - bool is_mu860; - - brd->must_hwid =3D mxser_must_get_hwid(brd->ports[0].ioaddr); - is_mu860 =3D brd->must_hwid =3D=3D MOXA_MUST_MU860_HWID; - - for (i =3D 0; i < UART_INFO_NUM; i++) { - if (Gpci_uart_info[i].type =3D=3D brd->must_hwid) { - brd->max_baud =3D Gpci_uart_info[i].max_baud; - - /* exception....CP-102 */ - if (high_baud) - brd->max_baud =3D 921600; - break; - } - } - - if (is_mu860) { - /* set to RS232 mode by default */ - outb(0, brd->vector + 4); - outb(0, brd->vector + 0x0c); - } - - for (i =3D 0; i < brd->nports; i++) { - info =3D &brd->ports[i]; - if (is_mu860) { - if (i < 4) - info->opmode_ioaddr =3D brd->vector + 4; - else - info->opmode_ioaddr =3D brd->vector + 0x0c; - } - tty_port_init(&info->port); - info->port.ops =3D &mxser_port_ops; - info->board =3D brd; - - /* Enhance mode enabled here */ - if (brd->must_hwid !=3D MOXA_OTHER_UART) - mxser_must_set_enhance_mode(info->ioaddr, true); - - info->type =3D PORT_16550A; - - mxser_process_txrx_fifo(info); - - spin_lock_init(&info->slock); - - /* before set INT ISR, disable all int */ - outb(inb(info->ioaddr + UART_IER) & 0xf0, - info->ioaddr + UART_IER); - } -} - -static int mxser_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct mxser_board *brd; - unsigned int i, base; - unsigned long ioaddress; - unsigned short nports =3D MXSER_NPORTS(ent->driver_data); - struct device *tty_dev; - int retval =3D -EINVAL; - - i =3D find_first_zero_bit(mxser_boards, MXSER_BOARDS); - if (i >=3D MXSER_BOARDS) { - dev_err(&pdev->dev, "too many boards found (maximum %d), board " - "not configured\n", MXSER_BOARDS); - goto err; - } - - brd =3D devm_kzalloc(&pdev->dev, struct_size(brd, ports, nports), - GFP_KERNEL); - if (!brd) - goto err; - - brd->idx =3D i; - __set_bit(brd->idx, mxser_boards); - base =3D i * MXSER_PORTS_PER_BOARD; - - retval =3D pcim_enable_device(pdev); - if (retval) { - dev_err(&pdev->dev, "PCI enable failed\n"); - goto err_zero; - } - - /* io address */ - ioaddress =3D pci_resource_start(pdev, 2); - retval =3D pci_request_region(pdev, 2, "mxser(IO)"); - if (retval) - goto err_zero; - - brd->nports =3D nports; - for (i =3D 0; i < nports; i++) - brd->ports[i].ioaddr =3D ioaddress + 8 * i; - - /* vector */ - ioaddress =3D pci_resource_start(pdev, 3); - retval =3D pci_request_region(pdev, 3, "mxser(vector)"); - if (retval) - goto err_zero; - brd->vector =3D ioaddress; - - /* irq */ - brd->irq =3D pdev->irq; - - mxser_initbrd(brd, ent->driver_data & MXSER_HIGHBAUD); - - retval =3D devm_request_irq(&pdev->dev, brd->irq, mxser_interrupt, - IRQF_SHARED, "mxser", brd); - if (retval) { - dev_err(&pdev->dev, "request irq failed"); - goto err_relbrd; - } - - for (i =3D 0; i < nports; i++) { - tty_dev =3D tty_port_register_device(&brd->ports[i].port, - mxvar_sdriver, base + i, &pdev->dev); - if (IS_ERR(tty_dev)) { - retval =3D PTR_ERR(tty_dev); - for (; i > 0; i--) - tty_unregister_device(mxvar_sdriver, - base + i - 1); - goto err_relbrd; - } - } - - pci_set_drvdata(pdev, brd); - - return 0; -err_relbrd: - for (i =3D 0; i < nports; i++) - tty_port_destroy(&brd->ports[i].port); -err_zero: - __clear_bit(brd->idx, mxser_boards); -err: - return retval; -} - -static void mxser_remove(struct pci_dev *pdev) -{ - struct mxser_board *brd =3D pci_get_drvdata(pdev); - unsigned int i, base =3D brd->idx * MXSER_PORTS_PER_BOARD; - - for (i =3D 0; i < brd->nports; i++) { - tty_unregister_device(mxvar_sdriver, base + i); - tty_port_destroy(&brd->ports[i].port); - } - - __clear_bit(brd->idx, mxser_boards); -} - -static struct pci_driver mxser_driver =3D { - .name =3D "mxser", - .id_table =3D mxser_pcibrds, - .probe =3D mxser_probe, - .remove =3D mxser_remove -}; - -static int __init mxser_module_init(void) -{ - int retval; - - mxvar_sdriver =3D tty_alloc_driver(MXSER_PORTS, TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV); - if (IS_ERR(mxvar_sdriver)) - return PTR_ERR(mxvar_sdriver); - - /* Initialize the tty_driver structure */ - mxvar_sdriver->name =3D "ttyMI"; - mxvar_sdriver->major =3D ttymajor; - mxvar_sdriver->minor_start =3D 0; - mxvar_sdriver->type =3D TTY_DRIVER_TYPE_SERIAL; - mxvar_sdriver->subtype =3D SERIAL_TYPE_NORMAL; - mxvar_sdriver->init_termios =3D tty_std_termios; - mxvar_sdriver->init_termios.c_cflag =3D B9600|CS8|CREAD|HUPCL|CLOCAL; - tty_set_operations(mxvar_sdriver, &mxser_ops); - - retval =3D tty_register_driver(mxvar_sdriver); - if (retval) { - printk(KERN_ERR "Couldn't install MOXA Smartio/Industio family " - "tty driver !\n"); - goto err_put; - } - - retval =3D pci_register_driver(&mxser_driver); - if (retval) { - printk(KERN_ERR "mxser: can't register pci driver\n"); - goto err_unr; - } - - return 0; -err_unr: - tty_unregister_driver(mxvar_sdriver); -err_put: - tty_driver_kref_put(mxvar_sdriver); - return retval; -} - -static void __exit mxser_module_exit(void) -{ - pci_unregister_driver(&mxser_driver); - tty_unregister_driver(mxvar_sdriver); - tty_driver_kref_put(mxvar_sdriver); -} - -module_init(mxser_module_init); -module_exit(mxser_module_exit); diff --git a/drivers/tty/serial/8250/8250_mxupci.c b/drivers/tty/serial/825= 0/8250_mxupci.c new file mode 100644 index 000000000000..fe899ff090c4 --- /dev/null +++ b/drivers/tty/serial/8250/8250_mxupci.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 8250_mxupci.c - Moxa UPCI multiport serial device driver + * + * Copyright (C) 2025 Moxa Inc. (support@moxa.com) + * Author: Crescent Hsieh + */ + +#include +#include +#include + +#include "8250.h" +#include "8250_pcilib.h" + +#define PCI_DEVICE_ID_MOXA_RC7000 0x0001 +#define PCI_DEVICE_ID_MOXA_CP102 0x1020 +#define PCI_DEVICE_ID_MOXA_CP102UL 0x1021 +#define PCI_DEVICE_ID_MOXA_CP102U 0x1022 +#define PCI_DEVICE_ID_MOXA_CP102UF 0x1023 +#define PCI_DEVICE_ID_MOXA_C104 0x1040 +#define PCI_DEVICE_ID_MOXA_CP104U 0x1041 +#define PCI_DEVICE_ID_MOXA_CP104JU 0x1042 +#define PCI_DEVICE_ID_MOXA_CP104EL 0x1043 +#define PCI_DEVICE_ID_MOXA_POS104UL 0x1044 +#define PCI_DEVICE_ID_MOXA_CB108 0x1080 +#define PCI_DEVICE_ID_MOXA_CP112UL 0x1120 +#define PCI_DEVICE_ID_MOXA_CT114 0x1140 +#define PCI_DEVICE_ID_MOXA_CP114 0x1141 +#define PCI_DEVICE_ID_MOXA_CB114 0x1142 +#define PCI_DEVICE_ID_MOXA_CP114UL 0x1143 +#define PCI_DEVICE_ID_MOXA_CP118U 0x1180 +#define PCI_DEVICE_ID_MOXA_CP118EL 0x1181 +#define PCI_DEVICE_ID_MOXA_CP132 0x1320 +#define PCI_DEVICE_ID_MOXA_CP132U 0x1321 +#define PCI_DEVICE_ID_MOXA_CP134U 0x1340 +#define PCI_DEVICE_ID_MOXA_CB134I 0x1341 +#define PCI_DEVICE_ID_MOXA_CP138U 0x1380 +#define PCI_DEVICE_ID_MOXA_C168 0x1680 +#define PCI_DEVICE_ID_MOXA_CP168U 0x1681 +#define PCI_DEVICE_ID_MOXA_CP168EL 0x1682 + +#define MOXA_UART_BASE_BAUD 921600 +#define MOXA_UART_OFFSET 8 + +struct mxupci8250 { + struct pci_dev *pdev; + unsigned int num_ports; + int line[]; +}; + +static unsigned short mxupci8250_get_nports(unsigned short device) +{ + switch (device) { + case PCI_DEVICE_ID_MOXA_RC7000: + return 8; + } + + return FIELD_GET(0x00F0, device); +} + +static int mxupci8250_probe(struct pci_dev *pdev, const struct pci_device_= id *id) +{ + struct uart_8250_port up; + struct mxupci8250 *priv; + unsigned int i, num_ports; + int ret; + + ret =3D pcim_enable_device(pdev); + pci_save_state(pdev); + + if (ret) + return ret; + + num_ports =3D mxupci8250_get_nports(pdev->device); + + priv =3D devm_kzalloc(&pdev->dev, struct_size(priv, line, num_ports), GFP= _KERNEL); + + if (!priv) + return -ENOMEM; + + priv->num_ports =3D num_ports; + priv->pdev =3D pdev; + + memset(&up, 0, sizeof(up)); + + up.port.dev =3D &pdev->dev; + up.port.irq =3D pdev->irq; + up.port.uartclk =3D MOXA_UART_BASE_BAUD * 16; + up.port.flags =3D UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + + for (i =3D 0; i < num_ports; i++) { + if (serial8250_pci_setup_port(pdev, &up, FL_GET_BASE(FL_BASE2), i * MOXA= _UART_OFFSET, 0)) + break; + + dev_dbg(&pdev->dev, "Setup PCI port: port %lx, irq %d, type %d\n", + up.port.iobase, up.port.irq, up.port.iotype); + + priv->line[i] =3D serial8250_register_8250_port(&up); + + if (priv->line[i] < 0) { + dev_err(&pdev->dev, + "Couldn't register serial port %lx, irq %d, type %d, error %d\n", + up.port.iobase, up.port.irq, + up.port.iotype, priv->line[i]); + break; + } + } + pci_set_drvdata(pdev, priv); + + return 0; +} + +static void mxupci8250_remove(struct pci_dev *pdev) +{ + struct mxupci8250 *priv =3D pci_get_drvdata(pdev); + unsigned int i; + + for (i =3D 0; i < priv->num_ports; i++) + serial8250_unregister_port(priv->line[i]); +} + +static const struct pci_device_id mxupci8250_pci_ids[] =3D { + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_RC7000), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102UL), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102U), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102UF), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C104), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104U), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104JU), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_POS104UL), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CB108), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP112UL), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CT114), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CB114), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114UL), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118U), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132U), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134U), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CB134I), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP138U), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C168), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168U), }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL), }, + { } +}; + +static struct pci_driver mxupci8250_pci_driver =3D { + .name =3D "8250_mxupci", + .id_table =3D mxupci8250_pci_ids, + .probe =3D mxupci8250_probe, + .remove =3D mxupci8250_remove, +}; + +module_pci_driver(mxupci8250_pci_driver); +MODULE_AUTHOR("Moxa Inc."); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Moxa UPCI Multiport Serial Device Driver"); +MODULE_DEVICE_TABLE(pci, mxupci8250_pci_ids); +MODULE_IMPORT_NS("SERIAL_8250_PCI"); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kcon= fig index 55d26d16df9b..e8b2878f8ab0 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -157,6 +157,17 @@ config SERIAL_8250_EXAR 422x PCIe serial cards that are not covered by the more generic SERIAL_8250_PCI option. =20 +config SERIAL_8250_MOXA_UPCI + tristate "8250/16550 Moxa UPCI device support" + depends on SERIAL_8250 && PCI + select SERIAL_8250_PCILIB + default SERIAL_8250 + help + Say Y here if you have a Moxa UPCI serial card. + + To compile this driver as a module, choose M here: the + module will be called 8250_mxupci. + config SERIAL_8250_HP300 tristate depends on SERIAL_8250 && HP300 diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Mak= efile index 1516de629b61..da9d35911bd5 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_SERIAL_8250_LPC18XX) +=3D 8250_lpc18xx.o obj-$(CONFIG_SERIAL_8250_LPSS) +=3D 8250_lpss.o obj-$(CONFIG_SERIAL_8250_MEN_MCB) +=3D 8250_men_mcb.o obj-$(CONFIG_SERIAL_8250_MID) +=3D 8250_mid.o +obj-$(CONFIG_SERIAL_8250_MOXA_UPCI) +=3D 8250_mxupci.o obj-$(CONFIG_SERIAL_8250_MT6577) +=3D 8250_mtk.o obj-$(CONFIG_SERIAL_OF_PLATFORM) +=3D 8250_of.o obj-$(CONFIG_SERIAL_8250_OMAP) +=3D 8250_omap.o --=20 2.45.2