From patchwork Wed Mar 30 08:50:30 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qiang Zhao X-Patchwork-Id: 603286 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3qZhnt3R34z9snl for ; Wed, 30 Mar 2016 20:15:46 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932643AbcC3JPj (ORCPT ); Wed, 30 Mar 2016 05:15:39 -0400 Received: from mail-bl2on0063.outbound.protection.outlook.com ([65.55.169.63]:42016 "EHLO na01-bl2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1758892AbcC3JPc (ORCPT ); Wed, 30 Mar 2016 05:15:32 -0400 X-Greylist: delayed 915 seconds by postgrey-1.27 at vger.kernel.org; Wed, 30 Mar 2016 05:15:31 EDT Received: from BY2PR03CA052.namprd03.prod.outlook.com (10.141.249.25) by BY2PR0301MB0710.namprd03.prod.outlook.com (10.160.63.152) with Microsoft SMTP Server (TLS) id 15.1.447.15; Wed, 30 Mar 2016 09:00:24 +0000 Received: from BY2FFO11FD029.protection.gbl (2a01:111:f400:7c0c::189) by BY2PR03CA052.outlook.office365.com (2a01:111:e400:2c5d::25) with Microsoft SMTP Server (TLS) id 15.1.447.15 via Frontend Transport; Wed, 30 Mar 2016 09:00:24 +0000 Authentication-Results: spf=fail (sender IP is 192.88.168.50) smtp.mailfrom=nxp.com; nxp.com; dkim=none (message not signed) header.d=none;nxp.com; dmarc=none action=none header.from=nxp.com; Received-SPF: Fail (protection.outlook.com: domain of nxp.com does not designate 192.88.168.50 as permitted sender) receiver=protection.outlook.com; client-ip=192.88.168.50; helo=tx30smr01.am.freescale.net; Received: from tx30smr01.am.freescale.net (192.88.168.50) by BY2FFO11FD029.mail.protection.outlook.com (10.1.14.212) with Microsoft SMTP Server (TLS) id 15.1.453.6 via Frontend Transport; Wed, 30 Mar 2016 09:00:24 +0000 Received: from titan.ap.freescale.net ([10.192.208.233]) by tx30smr01.am.freescale.net (8.14.3/8.14.0) with ESMTP id u2U8xxwM009345; Wed, 30 Mar 2016 02:00:20 -0700 From: Zhao Qiang To: CC: , , , , , , , Zhao Qiang Subject: [PATCH 5/5] drivers/net: support hdlc function for QE-UCC Date: Wed, 30 Mar 2016 16:50:30 +0800 Message-ID: <1459327830-19829-5-git-send-email-qiang.zhao@nxp.com> X-Mailer: git-send-email 2.1.0.27.g96db324 In-Reply-To: <1459327830-19829-1-git-send-email-qiang.zhao@nxp.com> References: <1459327830-19829-1-git-send-email-qiang.zhao@nxp.com> X-EOPAttributedMessage: 0 X-Matching-Connectors: 131038020243115744; (91ab9b29-cfa4-454e-5278-08d120cd25b8); () X-Forefront-Antispam-Report: CIP:192.88.168.50; IPV:NLI; CTRY:US; EFV:NLI; SFV:NSPM; SFS:(10009020)(6009001)(2980300002)(1109001)(1110001)(339900001)(199003)(189002)(92566002)(87936001)(575784001)(50226001)(36756003)(48376002)(106466001)(86362001)(2950100001)(104016004)(6806005)(76176999)(105606002)(19580395003)(2351001)(19580405001)(85426001)(2906002)(50986999)(110136002)(50466002)(4326007)(586003)(77096005)(1096002)(229853001)(5890100001)(81166005)(47776003)(189998001)(33646002)(5008740100001)(1220700001)(5003940100001)(11100500001)(7059030)(579004)(473944003); DIR:OUT; SFP:1101; SCL:1; SRVR:BY2PR0301MB0710; H:tx30smr01.am.freescale.net; FPR:; SPF:Fail; MLV:sfv; A:1; MX:1; LANG:en; X-Microsoft-Exchange-Diagnostics: 1; BY2FFO11FD029; 1:f3NL8FyD90UMytoLKrDJE20ut26TckAshDorWZIxORaeEp6t2x4DskU+J9D8ajSvWyrWkiUcYP0/Db9w4EEF4x9n++J9brZohD1hc7hkelUEkof4qPybGh9DFsDJa4x2FgGoEnpFj89PQ4ep0YW/w9tykoAJ7INZ2PX4vS5FiT2ev1FLw5s9QiBai9PgNHIvjtDpMoLznK34yzi4JqOleutMoIffNxuMzSFYu5vjzmx9UD6RQKLkJTYvvEgrmrYtMdl05B5axyf1oAYyspcjZQ5hZ9LaZb8mEpwLfgt8+TZ2BGx+XKkCCS1E/qszP2ElTcHhvCNLJ8I/CpxSheYWX8FYxsZ7MH7Gsetd/Djt/eqeDgwsuIv6WQ9EiCwnnfaxt+x+rGYLTfw8mIUe1k7hu0w6JpRbrfR2ysWKd70uHo+j4DBt8HwP2tCDCuxygxlcug2etEpr78HITjQ6pM579ZzR31llvvwiFf11PFQCx3gHUM36dzYNK9Htmn7L8EMYZ67LmWlh1VvoAwiPH2tu2wH1P6ivV0VTb9LvXYl/EEkIoCVY0X01cU9gYd8jwNleiajCBJ8k5ucCtv4UxUxCJDQROd3QbGWcpHXatWHiQ4sqAr1lv34f16OBoc1LyAk9 MIME-Version: 1.0 X-MS-Office365-Filtering-Correlation-Id: 3c2908e1-9241-4b7e-4ee0-08d35879bb20 X-Microsoft-Exchange-Diagnostics: 1; BY2PR0301MB0710; 2:TaJKYGVfmrrznw9pP58zagrOF1f+XNwAQfXOO3zJFx04DXOtmrPUYAzNuN3NfviIbQ3rVf6KVw002e1XhMgWV9ZVwnR3+tdqxOU80EmNsFnEIzur9BJM/FuTloTwmLJLh2iLlLedPOnRgO7aLfgkK5t9Vc7YwcKjz9pGFYEs++WY3InnPYhn+QmU+3HEIkxR; 3:5hFAacJZLbPnvrI79i/fabdlVAC0sebhg7FrOftTvP3WHslNISb4fYyIlP2hhf3tF0wpU0p7diSuvNmEGxV3eIazPbczC3VW0Vv4EZ5SOy29CPIkFoHWvz5enob0TyqSd8EAivIQaQWJlhN3n7ZMwpuHRJgvFz+HTFCJOE5xe2AYMlc/UcQVaJES6QSveZLRZQ8hBghp4WzntRz+aomsdAIw/XM4UwhNl93VN0j05Og=; 25:ULDUOWJuGF7LTKA4CzD8j51QiLNoqPLx6pTa51rp19CbO5kBC9XysBYgu0Pu7G/Nndn+hloAZsBq81jwPyHvBQXHjkpEVwHloNZUBqlYSDg6FrARKPyhgroLBv0+mM4N7oGBdx0uTUkj4s7zeAKCcGq0/xt8YpAecPUHazOXXQLKfKlpI6ETYKR+DhBXjyu85eV9/PS78iSspnhOpOF55/6XV+F9tfP3ul7DuQlRfzW8fGQ+SQnUN+HkN4dURUAW5p4GO2vgKe6yt67fH+Ry9O/zSk/lIYLnWyFYeeJIiBZkhZT8oFFYWnXtletaJVai13GSzPx1fFEIV6ccZBqZ5mpESKqs4IIlnUZ6Ounyr9c= X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BY2PR0301MB0710; X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004)(2401047)(13023025)(13017025)(5005006)(13024025)(8121501046)(13015025)(13018025)(3002001)(10201501046); SRVR:BY2PR0301MB0710; BCL:0; PCL:0; RULEID:(400006); SRVR:BY2PR0301MB0710; X-Microsoft-Exchange-Diagnostics: 1; BY2PR0301MB0710; 4:HJX2Xp16afOKuFyyLELJ5y2mnl9KJU4w4+oJlNmNnnYDP41fCB1/B5N5HK0Nbsg+w6/gqnfBBl2B/OaHE7El64C4U/V2U562RmW7SXuGDQgfklBm5RiQYR3x4EMO+F2eRMVFZz+aU9/TMPwPi/XybIf9aH9nRgkpPhPpeLhD6oxmspxd8K2+CbzkeRNhvvg6Q6knhjy42tHRiA6az0EQS9CCn/JDzf0/VuqL+k2+t+fJ0Bt6HE7rqiDDQMoB0VABYi7gaPIeDna+MMWuxPPhLmVKGG4GZPkXb51HzIrT74EgkRd4AdYVeyukQl6dbkCK+lCQlBqMoCPooyAjK1a5z3C41jiYEZayCjQ6GBcQNQ0HWvbDiQ+EXBvJSBgOOigtDg7XnYOvY4jXgx8JJGeZ9FOCwgPns5HkYepiYlw5//Lb4ZFt+oAe3lU5S8MPc1U7Q02q/1kGEgh9XHjGGhTfiw== X-Forefront-PRVS: 08978A8F5C X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; BY2PR0301MB0710; 23:kZQ8Yti/6RwgrHyzgrj79B/RhwdEtJfIFeLBanv?= =?us-ascii?Q?W1IGcyYfxTg3Lty1Jw5XONaQ9R/oaPFx481Sbptq5ACmfr9z+p31TP5+9Yyd?= =?us-ascii?Q?NSCVxGBitiEcJPK6s3q4cZkne1QRp/eJkExxEFmgFjY+8KLIUTHTiREQg+3Z?= =?us-ascii?Q?TPR05PYl1JXWT5I3kZpr84HAyDQW47QlFCx/s25TgXB0lLOnffOfomsGvBll?= =?us-ascii?Q?+HUKz6XqQlXXXWmLB6AdLizFZNqPn9wkMDzgo991TpP0aqgGLYhOYefdK4pW?= =?us-ascii?Q?crrm8uWAUXJbKBXzrUffSl/dBA+JSkJF77vkHh6/WKQQNqDJPkIMiPd3hR+s?= =?us-ascii?Q?d3YfQil8VhFYnHKUsGQK+gn/N7hwQJpDS7QuLDl3gi1y0iW+4RQUqsPo0bG5?= =?us-ascii?Q?L+z8NoY5xGpvwNvLnOuWSOpBqld46MsgG+4a2+uMDLrA3qiDasZ6qEGrBe32?= =?us-ascii?Q?5uzOaSmEH/wJfQngAAEe5XOVwtVggn1uDRL6M2hOvyOYJ+d8a1kpRzuZeoAh?= =?us-ascii?Q?sQ3ldn0g/QgcWu1wucZnMWHZ4i54tGNtAkzNH02TVIe/xj/2d4iC/r3bfUVl?= =?us-ascii?Q?1pS03JkFRXTmW5EcXEwnMHF0kVK2qYe7h9jIMvNwdARuT3FYBMASN7nNkYxu?= =?us-ascii?Q?Yl9L6nT+AsdtZHscte9tu9dCEnU1WM3sZc7+q9dc01fUpOwMzvKpQ9TUqueL?= =?us-ascii?Q?iCndRwstmWMSGu6XvnS3fJv5qMGqBaqKf0uoXoPSQC9NDnFFXPy2gNCdseid?= =?us-ascii?Q?zGuVuEThBnN5oFX8EExQi8b+fGYCRgtYaQbupaZXrsyZw10ldtXB3+Nf1+oH?= =?us-ascii?Q?54Vid/tU+ZS+BGFEqCPiKQ1CrkT6q9Un5T0S3HjUeoBSnVz7O08j4V7r8WtC?= =?us-ascii?Q?3VDErvb2JNqylAqriTAzAM4acMrLUlg7qOZ9nGrIsByiT20D/9epGrD74f7d?= =?us-ascii?Q?L26tvcq486ZkX8/3JJWPj1yEtvcrvYh4dO55o1GoY+RFr4XxWBpwCzM814U3?= =?us-ascii?Q?aqxmqxYGZPY9jXjRvxocd1cdlp0ZyMc/YXQkjx6AmPHg2YkLP7CB8bZKY0IS?= =?us-ascii?Q?jX3KFGNO/HY2R+1FkdiEptKxDn8ZVavmIwMCZ62kSeFnJPZ232d/doBjJTSV?= =?us-ascii?Q?YYrxcRd8P/r9MMolPG1Cjlql8dh/8LrEYkTGdZeVmHxhZ/YGM1G85tQ=3D?= =?us-ascii?Q?=3D?= X-Microsoft-Exchange-Diagnostics: 1; BY2PR0301MB0710; 5:cQggbI/C4IB/5+vNRaOZrtDjJBAK+KpzrJkgkrPDxcoeY3nYmUVLKgeq7FI7Xhd7+W9BcFkc7dtrVNR+DHe0RloXVEG9lUyk9qDlLPrmfiKD9DuKoyZ9iX/cpAV83v1g3gtUSd4wNnL0WsMjUXuyX4Pf6wRUkhugTd520InPW0M=; 24:BZRb8WSWjI3+Cw8X6dvIZXraBtQejYEDdQn703h/aiuuW+Do/4bih1o+9gn7GbqLMjBVDQgJC2SJF+fC8TVW8b/wTThYoR8BHnOZKpd7LFY= SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Mar 2016 09:00:24.1243 (UTC) X-MS-Exchange-CrossTenant-Id: 5afe0b00-7697-4969-b663-5eab37d5f47e X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=5afe0b00-7697-4969-b663-5eab37d5f47e; Ip=[192.88.168.50]; Helo=[tx30smr01.am.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BY2PR0301MB0710 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org The driver add hdlc support for Freescale QUICC Engine. It support NMSI and TSA mode. Signed-off-by: Zhao Qiang --- MAINTAINERS | 6 + drivers/net/wan/Kconfig | 12 + drivers/net/wan/Makefile | 1 + drivers/net/wan/fsl_ucc_hdlc.c | 1339 ++++++++++++++++++++++++++++++++++++++++ drivers/net/wan/fsl_ucc_hdlc.h | 140 +++++ include/soc/fsl/qe/ucc_fast.h | 4 + 6 files changed, 1502 insertions(+) create mode 100644 drivers/net/wan/fsl_ucc_hdlc.c create mode 100644 drivers/net/wan/fsl_ucc_hdlc.h diff --git a/MAINTAINERS b/MAINTAINERS index 74bbff3..428d6ed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4572,6 +4572,12 @@ F: drivers/net/ethernet/freescale/gianfar* X: drivers/net/ethernet/freescale/gianfar_ptp.c F: Documentation/devicetree/bindings/net/fsl-tsec-phy.txt +FREESCALE QUICC ENGINE UCC HDLC DRIVER +M: Zhao Qiang +L: linuxppc-dev@lists.ozlabs.org +S: Maintained +F: drivers/net/wan/fsl_ucc_hdlc* + FREESCALE QUICC ENGINE UCC UART DRIVER M: Timur Tabi L: linuxppc-dev@lists.ozlabs.org diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig index a2fdd15..cc424b2 100644 --- a/drivers/net/wan/Kconfig +++ b/drivers/net/wan/Kconfig @@ -280,6 +280,18 @@ config DSCC4 To compile this driver as a module, choose M here: the module will be called dscc4. +config FSL_UCC_HDLC + tristate "Freescale QUICC Engine HDLC support" + depends on HDLC + select QE_TDM + select QUICC_ENGINE + help + Driver for Freescale QUICC Engine HDLC controller. The driver + support HDLC run on NMSI and TDM mode. + + To compile this driver as a module, choose M here: the + module will be called fsl_ucc_hdlc. + config DSCC4_PCISYNC bool "Etinc PCISYNC features" depends on DSCC4 diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile index c135ef4..25fec40 100644 --- a/drivers/net/wan/Makefile +++ b/drivers/net/wan/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_WANXL) += wanxl.o obj-$(CONFIG_PCI200SYN) += pci200syn.o obj-$(CONFIG_PC300TOO) += pc300too.o obj-$(CONFIG_IXP4XX_HSS) += ixp4xx_hss.o +obj-$(CONFIG_FSL_UCC_HDLC) += fsl_ucc_hdlc.o clean-files := wanxlfw.inc $(obj)/wanxl.o: $(obj)/wanxlfw.inc diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c new file mode 100644 index 0000000..9958ec1 --- /dev/null +++ b/drivers/net/wan/fsl_ucc_hdlc.c @@ -0,0 +1,1339 @@ +/* Freescale QUICC Engine HDLC Device Driver + * + * Copyright 2014 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fsl_ucc_hdlc.h" + +#define DRV_DESC "Freescale QE UCC HDLC Driver" +#define DRV_NAME "ucc_hdlc" + +#define TDM_PPPOHT_SLIC_MAXIN +/* #define DEBUG */ +/* #define QE_HDLC_TEST */ +#define BROKEN_FRAME_INFO + +static struct ucc_tdm_info utdm_primary_info = { + .uf_info = { + .tsa = 0, + .cdp = 0, + .cds = 1, + .ctsp = 1, + .ctss = 1, + .revd = 0, + .urfs = 256, + .utfs = 256, + .urfet = 128, + .urfset = 192, + .utfet = 128, + .utftt = 0x40, + .ufpt = 256, + .mode = UCC_FAST_PROTOCOL_MODE_HDLC, + .ttx_trx = UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_NORMAL, + .tenc = UCC_FAST_TX_ENCODING_NRZ, + .renc = UCC_FAST_RX_ENCODING_NRZ, + .tcrc = UCC_FAST_16_BIT_CRC, + .synl = UCC_FAST_SYNC_LEN_NOT_USED, + }, + + .si_info = { +#ifdef CONFIG_FSL_PQ_MDS_T1 + .simr_rfsd = 1, /* TDM card need 1 bit delay */ + .simr_tfsd = 0, +#else +#ifdef TDM_PPPOHT_SLIC_MAXIN + .simr_rfsd = 1, + .simr_tfsd = 2, +#else + .simr_rfsd = 0, + .simr_tfsd = 0, +#endif +#endif + .simr_crt = 0, + .simr_sl = 0, + .simr_ce = 1, + .simr_fe = 1, + .simr_gm = 0, + }, +}; + +static struct ucc_tdm_info utdm_info[MAX_HDLC_NUM]; + +#ifdef DEBUG +static void mem_disp(u8 *addr, int size) +{ + void *i; + int size16_aling = (size >> 4) << 4; + int size4_aling = (size >> 2) << 2; + int not_align = 0; + + if (size % 16) + not_align = 1; + + for (i = addr; i < addr + size16_aling; i += 16) { + u32 *i32 = i; + + pr_info("0x%08p: %08x %08x %08x %08x\r\n", + i32, be32_to_cpu(i32[0]), be32_to_cpu(i32[1]), + be32_to_cpu(i32[2]), be32_to_cpu(i32[3])); + } + + if (not_align == 1) + pr_info("0x%08p: ", i); + for (; i < addr + size4_aling; i += 4) + pr_info("%08x ", be32_to_cpu(*((u32 *)(i)))); + for (; i < addr + size; i++) + pr_info("%02x", *((u8 *)(i))); + if (not_align == 1) + pr_info("\r\n"); +} + +static void dump_ucc(struct ucc_hdlc_private *priv) +{ + struct ucc_hdlc_param *ucc_pram; + + ucc_pram = priv->ucc_pram; + + dev_info(priv->dev, "DumpiniCC %d Registers\n", + priv->ut_info->uf_info.ucc_num); + ucc_fast_dump_regs(priv->uccf); + dev_info(priv->dev, "Dumping UCC %d Parameter RAM\n", + priv->ut_info->uf_info.ucc_num); + dev_info(priv->dev, "rbase = 0x%x\n", ioread32be(&ucc_pram->rbase)); + dev_info(priv->dev, "rbptr = 0x%x\n", ioread32be(&ucc_pram->rbptr)); + dev_info(priv->dev, "mrblr = 0x%x\n", ioread16be(&ucc_pram->mrblr)); + dev_info(priv->dev, "rbdlen = 0x%x\n", ioread16be(&ucc_pram->rbdlen)); + dev_info(priv->dev, "rbdstat = 0x%x\n", ioread16be(&ucc_pram->rbdstat)); + dev_info(priv->dev, "rstate = 0x%x\n", ioread32be(&ucc_pram->rstate)); + dev_info(priv->dev, "rdptr = 0x%x\n", ioread32be(&ucc_pram->rdptr)); + dev_info(priv->dev, "riptr = 0x%x\n", ioread16be(&ucc_pram->riptr)); + dev_info(priv->dev, "tbase = 0x%x\n", ioread32be(&ucc_pram->tbase)); + dev_info(priv->dev, "tbptr = 0x%x\n", ioread32be(&ucc_pram->tbptr)); + dev_info(priv->dev, "tbdlen = 0x%x\n", ioread16be(&ucc_pram->tbdlen)); + dev_info(priv->dev, "tbdstat = 0x%x\n", ioread16be(&ucc_pram->tbdstat)); + dev_info(priv->dev, "tstate = 0x%x\n", ioread32be(&ucc_pram->tstate)); + dev_info(priv->dev, "tdptr = 0x%x\n", ioread32be(&ucc_pram->tdptr)); + dev_info(priv->dev, "tiptr = 0x%x\n", ioread16be(&ucc_pram->tiptr)); + dev_info(priv->dev, "rcrc = 0x%x\n", ioread32be(&ucc_pram->rcrc)); + dev_info(priv->dev, "tcrc = 0x%x\n", ioread32be(&ucc_pram->tcrc)); + dev_info(priv->dev, "c_mask = 0x%x\n", ioread32be(&ucc_pram->c_mask)); + dev_info(priv->dev, "c_pers = 0x%x\n", ioread32be(&ucc_pram->c_pres)); + dev_info(priv->dev, "disfc = 0x%x\n", ioread16be(&ucc_pram->disfc)); + dev_info(priv->dev, "crcec = 0x%x\n", ioread16be(&ucc_pram->crcec)); +} + +static void dump_bds(struct ucc_hdlc_private *priv) +{ + int length; + + if (priv->tx_bd_base) { + length = sizeof(struct qe_bd) * TX_BD_RING_LEN; + dev_info(priv->dev, " Dump tx BDs\n"); + mem_disp((u8 *)priv->tx_bd_base, length); + } + + if (priv->rx_bd_base) { + length = sizeof(struct qe_bd) * RX_BD_RING_LEN; + dev_info(priv->dev, " Dump rx BDs\n"); + mem_disp((u8 *)priv->rx_bd_base, length); + } +} + +static void dump_priv(struct ucc_hdlc_private *priv) +{ + dev_info(priv->dev, "ut_info = 0x%x\n", (u32)priv->ut_info); + dev_info(priv->dev, "uccf = 0x%x\n", (u32)priv->uccf); + dev_info(priv->dev, "uf_regs = 0x%x\n", (u32)priv->uf_regs); + dev_info(priv->dev, "si_regs = 0x%x\n", (u32)priv->utdm->si_regs); + dev_info(priv->dev, "ucc_pram = 0x%x\n", (u32)priv->ucc_pram); + dev_info(priv->dev, "tdm_port = 0x%x\n", (u32)priv->utdm->tdm_port); + dev_info(priv->dev, "siram_entry_id = 0x%x\n", + priv->utdm->siram_entry_id); + dev_info(priv->dev, "siram = 0x%x\n", (u32)priv->utdm->siram); + dev_info(priv->dev, "tdm_mode = 0x%x\n", (u32)priv->utdm->tdm_mode); + dev_info(priv->dev, "tdm_framer_type; = 0x%x\n", + (u32)priv->utdm->tdm_framer_type); + dev_info(priv->dev, "rx_buffer; = 0x%x\n", (u32)priv->rx_buffer); + dev_info(priv->dev, "tx_buffer; = 0x%x\n", (u32)priv->tx_buffer); + dev_info(priv->dev, "dma_rx_addr; = 0x%x\n", (u32)priv->dma_rx_addr); + dev_info(priv->dev, "tx_bd; = 0x%x\n", (u32)priv->tx_bd_base); + dev_info(priv->dev, "rx_bd; = 0x%x\n", (u32)priv->rx_bd_base); + dev_info(priv->dev, "curtx_bd = 0x%x\n", (u32)priv->curtx_bd); + dev_info(priv->dev, "currx_bd = 0x%x\n", (u32)priv->currx_bd); + dev_info(priv->dev, "ucc_pram_offset = 0x%x\n", priv->ucc_pram_offset); +} + +#endif /* DEBUG */ + +static int uhdlc_init(struct ucc_hdlc_private *priv) +{ + struct ucc_tdm_info *ut_info; + struct ucc_fast_info *uf_info; + u32 cecr_subblock; + u32 bd_status; + int ret, i; + void *bd_buffer; + dma_addr_t bd_dma_addr; + u32 riptr; + u32 tiptr; + u32 gumr; + + ut_info = priv->ut_info; + uf_info = &ut_info->uf_info; + + if (priv->tsa) { + uf_info->tsa = 1; + uf_info->ctsp = 1; + } + uf_info->uccm_mask = (u32)((UCC_HDLC_UCCE_RXB | UCC_HDLC_UCCE_RXF | + UCC_HDLC_UCCE_TXB) << 16); + + if (ucc_fast_init(uf_info, &priv->uccf)) { + dev_err(priv->dev, "Failed to init uccf."); + return -ENOMEM; + } + + priv->uf_regs = priv->uccf->uf_regs; + ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + /* Loopback mode */ + if (priv->loopback) { + pr_info("TDM Mode: Loopback Mode\n"); + gumr = ioread32be(&priv->uf_regs->gumr); + gumr |= (0x40000000 | UCC_FAST_GUMR_CDS | UCC_FAST_GUMR_TCI); + gumr &= ~(UCC_FAST_GUMR_CTSP | UCC_FAST_GUMR_RSYN); + iowrite32be(gumr, &priv->uf_regs->gumr); + } + + /* Initialize SI */ + if (priv->tsa) + ucc_tdm_init(priv->utdm, priv->ut_info); + + /* Write to QE CECR, UCCx channel to Stop Transmission */ + cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); + ret = qe_issue_cmd(QE_STOP_TX, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + + /* Set UPSMR normal mode (need fixed)*/ + iowrite32be(0, &priv->uf_regs->upsmr); + + priv->rx_ring_size = RX_BD_RING_LEN; + priv->tx_ring_size = TX_BD_RING_LEN; + /* Alloc Rx BD */ + priv->rx_bd_base = dma_alloc_coherent(priv->dev, + RX_BD_RING_LEN * sizeof(struct qe_bd *), + &priv->dma_rx_bd, GFP_KERNEL); + + if (IS_ERR_VALUE((unsigned long)priv->rx_bd_base)) { + dev_err(priv->dev, "Cannot allocate MURAM memory for RxBDs\n"); + ret = -ENOMEM; + goto rxbd_alloc_error; + } + + /* Alloc Tx BD */ + priv->tx_bd_base = dma_alloc_coherent(priv->dev, + TX_BD_RING_LEN * sizeof(struct qe_bd *), + &priv->dma_tx_bd, GFP_KERNEL); + + if (IS_ERR_VALUE((unsigned long)priv->tx_bd_base)) { + dev_err(priv->dev, "Cannot allocate MURAM memory for TxBDs\n"); + ret = -ENOMEM; + goto txbd_alloc_error; + } + + /* Alloc parameter ram for ucc hdlc */ + priv->ucc_pram_offset = qe_muram_alloc(sizeof(priv->ucc_pram), + ALIGNMENT_OF_UCC_HDLC_PRAM); + + if (IS_ERR_VALUE(priv->ucc_pram_offset)) { + dev_err(priv->dev, "Can not allocate MURAM for hdlc prameter.\n"); + ret = -ENOMEM; + goto pram_alloc_error; + } + + priv->rx_skbuff = kmalloc_array(priv->rx_ring_size, + sizeof(*priv->rx_skbuff), GFP_KERNEL); + if (!priv->rx_skbuff) + goto rx_skb_alloc_error; + for (i = 0; i < priv->rx_ring_size; i++) + priv->rx_skbuff[i] = NULL; + + priv->tx_skbuff = kmalloc_array(priv->tx_ring_size, + sizeof(*priv->tx_skbuff), GFP_KERNEL); + if (!priv->tx_skbuff) + goto tx_skb_alloc_error; + for (i = 0; i < priv->tx_ring_size; i++) + priv->tx_skbuff[i] = NULL; + + priv->skb_curtx = 0; + priv->skb_dirtytx = 0; + priv->curtx_bd = priv->tx_bd_base; + priv->dirty_tx = priv->tx_bd_base; + priv->currx_bd = priv->rx_bd_base; + priv->currx_bdnum = 0; + + /* init parameter base */ + cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); + ret = qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock, + QE_CR_PROTOCOL_UNSPECIFIED, priv->ucc_pram_offset); + + priv->ucc_pram = (struct ucc_hdlc_param __iomem *) + qe_muram_addr(priv->ucc_pram_offset); + + /* Zero out parameter ram */ + memset_io(priv->ucc_pram, 0, sizeof(struct ucc_hdlc_param)); + + /* Alloc riptr, tiptr */ + riptr = qe_muram_alloc(32, 32); + if (IS_ERR_VALUE(riptr)) { + dev_err(priv->dev, "Cannot allocate MURAM mem for Receive internal temp data pointer\n"); + ret = -ENOMEM; + goto riptr_alloc_error; + } + + tiptr = qe_muram_alloc(32, 32); + if (IS_ERR_VALUE(tiptr)) { + dev_err(priv->dev, "Cannot allocate MURAM mem for Transmit internal temp data pointer\n"); + ret = -ENOMEM; + goto tiptr_alloc_error; + } + + /* Set RIPTR, TIPTR */ + iowrite16be((u16)riptr, &priv->ucc_pram->riptr); + iowrite16be((u16)tiptr, &priv->ucc_pram->tiptr); + + /* Set MRBLR */ + iowrite16be((u16)MAX_RX_BUF_LENGTH, &priv->ucc_pram->mrblr); + + /* Set RBASE, TBASE */ + iowrite32be((u32)priv->dma_rx_bd, &priv->ucc_pram->rbase); + iowrite32be((u32)priv->dma_tx_bd, &priv->ucc_pram->tbase); + + /* Set RSTATE, TSTATE */ + iowrite32be(0x30000000, &priv->ucc_pram->rstate); + iowrite32be(0x30000000, &priv->ucc_pram->tstate); + + /* Set C_MASK, C_PRES for 16bit CRC */ + iowrite32be(0x0000F0B8, &priv->ucc_pram->c_mask); + iowrite32be(0x0000FFFF, &priv->ucc_pram->c_pres); + + iowrite16be(MAX_RX_BUF_LENGTH + 8, &priv->ucc_pram->mflr); + iowrite16be(1, &priv->ucc_pram->rfthr); + iowrite16be(1, &priv->ucc_pram->rfcnt); + iowrite16be(DEFAULT_ADDR_MASK, &priv->ucc_pram->hmask); + iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr1); + iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr2); + iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr3); + iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr4); + + /* Get BD buffer */ + bd_buffer = dma_alloc_coherent(priv->dev, + (RX_BD_RING_LEN + TX_BD_RING_LEN) * + MAX_RX_BUF_LENGTH, + &bd_dma_addr, GFP_KERNEL); + + if (!bd_buffer) { + dev_err(priv->dev, "Could not allocate buffer descriptors\n"); + return -ENOMEM; + } + + memset(bd_buffer, 0, (RX_BD_RING_LEN + TX_BD_RING_LEN) + * MAX_RX_BUF_LENGTH); + + priv->rx_buffer = bd_buffer; + priv->tx_buffer = bd_buffer + RX_BD_RING_LEN * MAX_RX_BUF_LENGTH; + + priv->dma_rx_addr = bd_dma_addr; + priv->dma_tx_addr = bd_dma_addr + RX_BD_RING_LEN * MAX_RX_BUF_LENGTH; + + for (i = 0; i < RX_BD_RING_LEN; i++) { + if (i < (RX_BD_RING_LEN - 1)) + bd_status = R_E | R_I; + else + bd_status = R_E | R_I | R_W; + + iowrite32be(bd_status, (u32 *)(priv->rx_bd_base + i)); + iowrite32be(priv->dma_rx_addr + i * MAX_RX_BUF_LENGTH, + &priv->rx_bd_base[i].buf); + } + + for (i = 0; i < TX_BD_RING_LEN; i++) { + if (i < (TX_BD_RING_LEN - 1)) + bd_status = T_I | T_TC; + else + bd_status = T_I | T_TC | T_W; + + iowrite32be(bd_status, (u32 *)(priv->tx_bd_base + i)); + iowrite32be(priv->dma_tx_addr + i * MAX_RX_BUF_LENGTH, + &priv->tx_bd_base[i].buf); + } + + return 0; + +tiptr_alloc_error: + qe_muram_free(riptr); +riptr_alloc_error: + kfree(priv->tx_skbuff); +tx_skb_alloc_error: + kfree(priv->rx_skbuff); +rx_skb_alloc_error: + qe_muram_free(priv->ucc_pram_offset); +pram_alloc_error: + dma_free_coherent(priv->dev, + TX_BD_RING_LEN * sizeof(struct qe_bd), + priv->tx_bd_base, priv->dma_tx_bd); +txbd_alloc_error: + dma_free_coherent(priv->dev, + RX_BD_RING_LEN * sizeof(struct qe_bd), + priv->rx_bd_base, priv->dma_rx_bd); +rxbd_alloc_error: + ucc_fast_free(priv->uccf); + + return ret; +} + +static netdev_tx_t ucc_hdlc_tx(struct sk_buff *skb, struct net_device *dev) +{ + hdlc_device *hdlc = dev_to_hdlc(dev); + struct ucc_hdlc_private *priv = (struct ucc_hdlc_private *)hdlc->priv; + struct qe_bd __iomem *bd; + u32 bd_status; + unsigned long flags; +#ifdef QE_HDLC_TEST + u8 *send_buf; + int i; +#endif + u16 *proto_head, tmp_head; + + switch (dev->type) { + case ARPHRD_RAWHDLC: + if (skb_headroom(skb) < HDLC_HEAD_LEN) { + dev->stats.tx_dropped++; + dev_kfree_skb(skb); + netdev_err(dev, "No enough space for hdlc head\n"); + return -ENOMEM; + } + + skb_push(skb, HDLC_HEAD_LEN); + + proto_head = (u16 *)skb->data; + tmp_head = *proto_head; + tmp_head = (tmp_head & HDLC_HEAD_MASK) | + htons(DEFAULT_HDLC_HEAD); + *proto_head = tmp_head; + + dev->stats.tx_bytes += skb->len; + break; + + case ARPHRD_PPP: + proto_head = (u16 *)skb->data; + if (*proto_head != ntohs(DEFAULT_PPP_HEAD)) { + dev->stats.tx_dropped++; + dev_kfree_skb(skb); + netdev_err(dev, "Wrong ppp header\n"); + return -ENOMEM; + } + + dev->stats.tx_bytes += skb->len; + break; + + default: + dev->stats.tx_dropped++; + dev_kfree_skb(skb); + netdev_err(dev, "Protocol not supported!\n"); + return -ENOMEM; + + } /*switch right bracket*/ + +#ifdef QE_HDLC_TEST + pr_info("Tx data skb->len:%d ", skb->len); + send_buf = (u8 *)skb->data; + pr_info("\nTransmitted data:\n"); + for (i = 0; (i < 16); i++) { + if (i == skb->len) + pr_info("++++"); + else + pr_info("%02x\n", send_buf[i]); + } +#endif + spin_lock_irqsave(&priv->lock, flags); + + /* Start from the next BD that should be filled */ + bd = priv->curtx_bd; + bd_status = ioread32be((u32 __iomem *)bd); + /* Save the skb pointer so we can free it later */ + priv->tx_skbuff[priv->skb_curtx] = skb; + + /* Update the current skb pointer (wrapping if this was the last) */ + priv->skb_curtx = + (priv->skb_curtx + 1) & TX_RING_MOD_MASK(TX_BD_RING_LEN); + + /* copy skb data to tx buffer for sdma processing */ + memcpy(priv->tx_buffer + (be32_to_cpu(bd->buf) - priv->dma_tx_addr), + skb->data, skb->len); + + /* set bd status and length */ + bd_status = (bd_status & T_W) | T_R | T_I | T_L | T_TC | skb->len; + + iowrite32be(bd_status, (u32 __iomem *)bd); + + /* Move to next BD in the ring */ + if (!(bd_status & T_W)) + bd += 1; + else + bd = priv->tx_bd_base; + + if (bd == priv->dirty_tx) { + if (!netif_queue_stopped(dev)) + netif_stop_queue(dev); + } + + priv->curtx_bd = bd; + + spin_unlock_irqrestore(&priv->lock, flags); + + return NETDEV_TX_OK; +} + +static int hdlc_tx_done(struct ucc_hdlc_private *priv) +{ + /* Start from the next BD that should be filled */ + struct net_device *dev = priv->ndev; + struct qe_bd *bd; /* BD pointer */ + u32 bd_status; + + bd = priv->dirty_tx; + bd_status = ioread32be((u32 __iomem *)bd); + + /* Normal processing. */ + while ((bd_status & T_R) == 0) { + struct sk_buff *skb; + + /* BD contains already transmitted buffer. */ + /* Handle the transmitted buffer and release */ + /* the BD to be used with the current frame */ + + skb = priv->tx_skbuff[priv->skb_dirtytx]; + if (!skb) + break; +#ifdef QE_HDLC_TEST + pr_info("TxBD: %x\n", bd_status); +#endif + dev->stats.tx_packets++; + memset(priv->tx_buffer + + (be32_to_cpu(bd->buf) - priv->dma_tx_addr), + 0, skb->len); + dev_kfree_skb_irq(skb); + + priv->tx_skbuff[priv->skb_dirtytx] = NULL; + priv->skb_dirtytx = + (priv->skb_dirtytx + + 1) & TX_RING_MOD_MASK(TX_BD_RING_LEN); + + /* We freed a buffer, so now we can restart transmission */ + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + + /* Advance the confirmation BD pointer */ + if (!(bd_status & T_W)) + bd += 1; + else + bd = priv->tx_bd_base; + bd_status = ioread32be((u32 __iomem *)bd); + } + priv->dirty_tx = bd; + + return 0; +} + +static int hdlc_rx_done(struct ucc_hdlc_private *priv, int rx_work_limit) +{ + struct net_device *dev = priv->ndev; + struct sk_buff *skb; + hdlc_device *hdlc = dev_to_hdlc(dev); + struct qe_bd *bd; + u32 bd_status; + u16 length, howmany = 0; + u8 *bdbuffer; +#ifdef QE_HDLC_TEST + int i; + static int entry; +#endif + + bd = priv->currx_bd; + bd_status = ioread32be((u32 __iomem *)bd); + + /* while there are received buffers and BD is full (~R_E) */ + while (!((bd_status & (R_E)) || (--rx_work_limit < 0))) { + if (bd_status & R_CR) { +#ifdef BROKEN_FRAME_INFO + pr_info("Broken Frame with RxBD: %x\n", bd_status); +#endif + dev->stats.rx_dropped++; + goto recycle; + } + bdbuffer = priv->rx_buffer + + (priv->currx_bdnum * MAX_RX_BUF_LENGTH); + length = (u16)(bd_status & BD_LENGTH_MASK); + +#ifdef QE_HDLC_TEST + pr_info("Received data length:%d", length); + pr_info("while entry times:%d", entry++); + + pr_info("\nReceived data:\n"); + for (i = 0; (i < 16); i++) { + if (i == length) + pr_info("++++"); + else + pr_info("%02x\n", bdbuffer[i]); + } +#endif + + switch (dev->type) { + case ARPHRD_RAWHDLC: + bdbuffer += HDLC_HEAD_LEN; + length -= (HDLC_HEAD_LEN + HDLC_CRC_SIZE); + + skb = dev_alloc_skb(length); + if (!skb) { + dev->stats.rx_dropped++; + return -ENOMEM; + } + + skb_put(skb, length); + skb->len = length; + skb->dev = dev; + memcpy(skb->data, bdbuffer, length); + break; + + case ARPHRD_PPP: + length -= HDLC_CRC_SIZE; + + skb = dev_alloc_skb(length); + if (!skb) { + dev->stats.rx_dropped++; + return -ENOMEM; + } + + skb_put(skb, length); + skb->len = length; + skb->dev = dev; + memcpy(skb->data, bdbuffer, length); + break; + } + + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + howmany++; + if (hdlc->proto) + skb->protocol = hdlc_type_trans(skb, dev); +#ifdef QE_HDLC_TEST + pr_info("skb->protocol:%x\n", skb->protocol); +#endif + netif_receive_skb(skb); + +recycle: + iowrite32be((bd_status & ~BD_LENGTH_MASK) | R_E | R_I, + (u32 *)bd); + + /* update to point at the next bd */ + if (bd_status & R_W) { + priv->currx_bdnum = 0; + bd = priv->rx_bd_base; + } else { + if (priv->currx_bdnum < (RX_BD_RING_LEN - 1)) + priv->currx_bdnum += 1; + else + priv->currx_bdnum = RX_BD_RING_LEN - 1; + + bd += 1; + } + + bd_status = ioread32be((u32 __iomem *)bd); + } + + priv->currx_bd = bd; + return howmany; +} + +static int ucc_hdlc_poll(struct napi_struct *napi, int budget) +{ + struct ucc_hdlc_private *priv = container_of(napi, + struct ucc_hdlc_private, + napi); + int howmany; + + /* Tx event processing */ + spin_lock(&priv->lock); + hdlc_tx_done(priv); + spin_unlock(&priv->lock); + + howmany = 0; + howmany += hdlc_rx_done(priv, budget - howmany); + + if (howmany < budget) { + napi_complete(napi); + qe_setbits32(priv->uccf->p_uccm, + (UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS) << 16); + } + + return howmany; +} + +static irqreturn_t ucc_hdlc_irq_handler(int irq, void *dev_id) +{ + struct ucc_hdlc_private *priv = (struct ucc_hdlc_private *)dev_id; + struct net_device *dev = priv->ndev; + struct ucc_fast_private *uccf; + struct ucc_tdm_info *ut_info; + u32 ucce; + u32 uccm; + + ut_info = priv->ut_info; + uccf = priv->uccf; + + ucce = ioread32be(uccf->p_ucce); + uccm = ioread32be(uccf->p_uccm); + ucce &= uccm; + iowrite32be(ucce, uccf->p_ucce); +#ifdef QE_HDLC_TEST + pr_info("irq ucce:%x\n", ucce); +#endif + + if ((ucce >> 16) & (UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS)) { + if (napi_schedule_prep(&priv->napi)) { + uccm &= ~((UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS) + << 16); + iowrite32be(uccm, uccf->p_uccm); + __napi_schedule(&priv->napi); + } + } + + /* Errors and other events */ + if (ucce >> 16 & UCC_HDLC_UCCE_BSY) + dev->stats.rx_errors++; + if (ucce >> 16 & UCC_HDLC_UCCE_TXE) + dev->stats.tx_errors++; + + return IRQ_HANDLED; +} + +static int uhdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + const size_t size = sizeof(te1_settings); + te1_settings line; + struct ucc_hdlc_private *priv = netdev_priv(dev); + + if (cmd != SIOCWANDEV) + return hdlc_ioctl(dev, ifr, cmd); + + switch (ifr->ifr_settings.type) { + case IF_GET_IFACE: + ifr->ifr_settings.type = IF_IFACE_E1; + if (ifr->ifr_settings.size < size) { + ifr->ifr_settings.size = size; /* data size wanted */ + return -ENOBUFS; + } + line.clock_type = priv->clocking; + line.clock_rate = 0; + line.loopback = 0; + + if (copy_to_user(ifr->ifr_settings.ifs_ifsu.sync, &line, size)) + return -EFAULT; + return 0; + + default: + return hdlc_ioctl(dev, ifr, cmd); + } +} + +static int uhdlc_open(struct net_device *dev) +{ + u32 cecr_subblock; + hdlc_device *hdlc = dev_to_hdlc(dev); + struct ucc_hdlc_private *priv = hdlc->priv; + struct ucc_tdm *utdm = priv->utdm; + + if (priv->hdlc_busy != 1) { + if (request_irq(priv->ut_info->uf_info.irq, + ucc_hdlc_irq_handler, 0, + "hdlc", (void *)priv)) { + dev_err(priv->dev, "request_irq for ucc hdlc failed\n"); + return -ENODEV; + } + cecr_subblock = ucc_fast_get_qe_cr_subblock( + priv->ut_info->uf_info.ucc_num); + + qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + + ucc_fast_enable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + /* Enable the TDM port */ + if (priv->tsa) + utdm->si_regs->siglmr1_h |= (0x1 << utdm->tdm_port); + + priv->hdlc_busy = 1; + netif_device_attach(priv->ndev); + napi_enable(&priv->napi); + netif_start_queue(dev); + hdlc_open(dev); + } else { + dev_err(priv->dev, "HDLC IS RUNNING!\n"); + } + +#ifdef DEBUG + dump_priv(priv); + dump_ucc(priv); + dump_bds(priv); +#endif + return 0; +} + +static void uhdlc_memclean(struct ucc_hdlc_private *priv) +{ + qe_muram_free(priv->ucc_pram->riptr); + qe_muram_free(priv->ucc_pram->tiptr); + + if (priv->rx_bd_base) { + dma_free_coherent(priv->dev, + RX_BD_RING_LEN * sizeof(struct qe_bd), + priv->rx_bd_base, priv->dma_rx_bd); + + priv->rx_bd_base = NULL; + priv->dma_rx_bd = 0; + } + + if (priv->tx_bd_base) { + dma_free_coherent(priv->dev, + TX_BD_RING_LEN * sizeof(struct qe_bd), + priv->tx_bd_base, priv->dma_tx_bd); + + priv->tx_bd_base = NULL; + priv->dma_tx_bd = 0; + } + + if (priv->ucc_pram) { + qe_muram_free(priv->ucc_pram_offset); + priv->ucc_pram = NULL; + priv->ucc_pram_offset = 0; + } + + kfree(priv->rx_skbuff); + priv->rx_skbuff = NULL; + + kfree(priv->tx_skbuff); + priv->tx_skbuff = NULL; + + if (priv->uf_regs) { + iounmap(priv->uf_regs); + priv->uf_regs = NULL; + } + + if (priv->uccf) { + ucc_fast_free(priv->uccf); + priv->uccf = NULL; + } + + if (priv->rx_buffer) { + dma_free_coherent(priv->dev, + RX_BD_RING_LEN * MAX_RX_BUF_LENGTH, + priv->rx_buffer, priv->dma_rx_addr); + priv->rx_buffer = NULL; + priv->dma_rx_addr = 0; + } + + if (priv->tx_buffer) { + dma_free_coherent(priv->dev, + TX_BD_RING_LEN * MAX_RX_BUF_LENGTH, + priv->tx_buffer, priv->dma_tx_addr); + priv->tx_buffer = NULL; + priv->dma_tx_addr = 0; + } +} + +static int uhdlc_close(struct net_device *dev) +{ + struct ucc_hdlc_private *priv = dev_to_hdlc(dev)->priv; + struct ucc_tdm *utdm = priv->utdm; + u32 cecr_subblock; + + napi_disable(&priv->napi); + cecr_subblock = ucc_fast_get_qe_cr_subblock( + priv->ut_info->uf_info.ucc_num); + + qe_issue_cmd(QE_GRACEFUL_STOP_TX, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + qe_issue_cmd(QE_CLOSE_RX_BD, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + + if (priv->tsa) + utdm->si_regs->siglmr1_h &= ~(0x1 << utdm->tdm_port); + + ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + free_irq(priv->ut_info->uf_info.irq, priv); + netif_stop_queue(dev); + priv->hdlc_busy = 0; + + return 0; +} + +static int ucc_hdlc_attach(struct net_device *dev, unsigned short encoding, + unsigned short parity) +{ + struct ucc_hdlc_private *priv = dev_to_hdlc(dev)->priv; + + if (encoding != ENCODING_NRZ && + encoding != ENCODING_NRZI) + return -EINVAL; + + if (parity != PARITY_NONE && + parity != PARITY_CRC32_PR1_CCITT && + parity != PARITY_CRC16_PR1_CCITT) + return -EINVAL; + + priv->encoding = encoding; + priv->parity = parity; + + return 0; +} + +#ifdef CONFIG_PM +static void store_clk_config(struct ucc_hdlc_private *priv) +{ + struct qe_mux *qe_mux_reg = &qe_immr->qmx; + + /* store si clk */ + priv->cmxsi1cr_h = ioread32be(&qe_mux_reg->cmxsi1cr_h); + priv->cmxsi1cr_l = ioread32be(&qe_mux_reg->cmxsi1cr_l); + + /* store si sync */ + priv->cmxsi1syr = ioread32be(&qe_mux_reg->cmxsi1syr); + + /* store ucc clk */ + memcpy_fromio(priv->cmxucr, qe_mux_reg->cmxucr, 4 * sizeof(u32)); +} + +static void resume_clk_config(struct ucc_hdlc_private *priv) +{ + struct qe_mux *qe_mux_reg = &qe_immr->qmx; + + memcpy_toio(qe_mux_reg->cmxucr, priv->cmxucr, 4 * sizeof(u32)); + + iowrite32be(priv->cmxsi1cr_h, &qe_mux_reg->cmxsi1cr_h); + iowrite32be(priv->cmxsi1cr_l, &qe_mux_reg->cmxsi1cr_l); + + iowrite32be(priv->cmxsi1syr, &qe_mux_reg->cmxsi1syr); +} + +static int uhdlc_suspend(struct device *dev) +{ + struct ucc_hdlc_private *priv = dev_get_drvdata(dev); + struct ucc_tdm_info *ut_info; + struct ucc_fast __iomem *uf_regs; + + if (!priv) + return -EINVAL; + + if (!netif_running(priv->ndev)) + return 0; + + netif_device_detach(priv->ndev); + napi_disable(&priv->napi); + + ut_info = priv->ut_info; + uf_regs = priv->uf_regs; + + /* backup gumr guemr*/ + priv->gumr = ioread32be(&uf_regs->gumr); + priv->guemr = ioread8(&uf_regs->guemr); + + priv->ucc_pram_bak = kmalloc(sizeof(*priv->ucc_pram_bak), + GFP_KERNEL); + if (!priv->ucc_pram_bak) + return -ENOMEM; + + /* backup HDLC parameter */ + memcpy_fromio(priv->ucc_pram_bak, priv->ucc_pram, + sizeof(struct ucc_hdlc_param)); + + /* store the clk configuration */ + store_clk_config(priv); + + /* save power */ + ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + dev_dbg(dev, "ucc hdlc suspend\n"); + return 0; +} + +static int uhdlc_resume(struct device *dev) +{ + struct ucc_hdlc_private *priv = dev_get_drvdata(dev); + struct ucc_tdm *utdm = priv->utdm; + struct ucc_tdm_info *ut_info; + struct ucc_fast __iomem *uf_regs; + struct ucc_fast_private *uccf; + struct ucc_fast_info *uf_info; + int ret, i; + u32 cecr_subblock, bd_status; + + if (!priv) + return -EINVAL; + + if (!netif_running(priv->ndev)) + return 0; + + ut_info = priv->ut_info; + uf_info = &ut_info->uf_info; + uf_regs = priv->uf_regs; + uccf = priv->uccf; + + /* restore gumr guemr */ + iowrite8(priv->guemr, &uf_regs->guemr); + iowrite32be(priv->gumr, &uf_regs->gumr); + + /* Set Virtual Fifo registers */ + iowrite16be(uf_info->urfs, &uf_regs->urfs); + iowrite16be(uf_info->urfet, &uf_regs->urfet); + iowrite16be(uf_info->urfset, &uf_regs->urfset); + iowrite16be(uf_info->utfs, &uf_regs->utfs); + iowrite16be(uf_info->utfet, &uf_regs->utfet); + iowrite16be(uf_info->utftt, &uf_regs->utftt); + /* utfb, urfb are offsets from MURAM base */ + iowrite32be(uccf->ucc_fast_tx_virtual_fifo_base_offset, &uf_regs->utfb); + iowrite32be(uccf->ucc_fast_rx_virtual_fifo_base_offset, &uf_regs->urfb); + + /* Rx Tx and sync clock routing */ + resume_clk_config(priv); + + iowrite32be(uf_info->uccm_mask, &uf_regs->uccm); + iowrite32be(0xffffffff, &uf_regs->ucce); + + ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + /* rebuild SIRAM */ + if (priv->tsa) + ucc_tdm_init(priv->utdm, priv->ut_info); + + /* Write to QE CECR, UCCx channel to Stop Transmission */ + cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); + ret = qe_issue_cmd(QE_STOP_TX, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + + /* Set UPSMR normal mode */ + iowrite32be(0, &uf_regs->upsmr); + + /* init parameter base */ + cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); + ret = qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock, + QE_CR_PROTOCOL_UNSPECIFIED, priv->ucc_pram_offset); + + priv->ucc_pram = (struct ucc_hdlc_param __iomem *) + qe_muram_addr(priv->ucc_pram_offset); + + /* restore ucc parameter */ + memcpy_toio(priv->ucc_pram, priv->ucc_pram_bak, + sizeof(struct ucc_hdlc_param)); + kfree(priv->ucc_pram_bak); + + /* rebuild BD entry */ + for (i = 0; i < RX_BD_RING_LEN; i++) { + if (i < (RX_BD_RING_LEN - 1)) + bd_status = R_E | R_I; + else + bd_status = R_E | R_I | R_W; + + iowrite32be(bd_status, (u32 *)(priv->rx_bd_base + i)); + iowrite32be(priv->dma_rx_addr + i * MAX_RX_BUF_LENGTH, + &priv->rx_bd_base[i].buf); + } + + for (i = 0; i < TX_BD_RING_LEN; i++) { + if (i < (TX_BD_RING_LEN - 1)) + bd_status = T_I | T_TC; + else + bd_status = T_I | T_TC | T_W; + + iowrite32be(bd_status, (u32 *)(priv->tx_bd_base + i)); + iowrite32be(priv->dma_tx_addr + i * MAX_RX_BUF_LENGTH, + &priv->tx_bd_base[i].buf); + } + + /* if hdlc is busy enable TX and RX */ + if (priv->hdlc_busy == 1) { + cecr_subblock = ucc_fast_get_qe_cr_subblock( + priv->ut_info->uf_info.ucc_num); + + qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + + ucc_fast_enable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + /* Enable the TDM port */ + if (priv->tsa) + utdm->si_regs->siglmr1_h |= (0x1 << utdm->tdm_port); + } + + napi_enable(&priv->napi); + netif_device_attach(priv->ndev); + + return 0; +} + +static const struct dev_pm_ops uhdlc_pm_ops = { + .suspend = uhdlc_suspend, + .resume = uhdlc_resume, + .freeze = uhdlc_suspend, + .thaw = uhdlc_resume, +}; + +#define HDLC_PM_OPS (&uhdlc_pm_ops) + +#else + +#define HDLC_PM_OPS NULL + +#endif +static const struct net_device_ops uhdlc_ops = { + .ndo_open = uhdlc_open, + .ndo_stop = uhdlc_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = uhdlc_ioctl, +}; + +static int ucc_hdlc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct ucc_hdlc_private *uhdlc_priv = NULL; + struct ucc_tdm_info *ut_info; + struct ucc_tdm *utdm; + struct resource res; + struct net_device *dev; + hdlc_device *hdlc; + int ucc_num; + const char *sprop; + int ret; + u32 val; + + ret = of_property_read_u32_index(np, "cell-index", 0, &val); + if (ret) { + dev_err(&pdev->dev, "Invalid ucc property\n"); + return -ENODEV; + } + + ucc_num = val - 1; + if ((ucc_num > 3) || (ucc_num < 0)) { + dev_err(&pdev->dev, ": Invalid UCC num\n"); + return -EINVAL; + } + + memcpy(&utdm_info[ucc_num], &utdm_primary_info, + sizeof(utdm_primary_info)); + + ut_info = &utdm_info[ucc_num]; + ut_info->uf_info.ucc_num = ucc_num; + + sprop = of_get_property(np, "rx-clock-name", NULL); + if (sprop) { + ut_info->uf_info.rx_clock = qe_clock_source(sprop); + if ((ut_info->uf_info.rx_clock < QE_CLK_NONE) || + (ut_info->uf_info.rx_clock > QE_CLK24)) { + dev_err(&pdev->dev, "Invalid rx-clock-name property\n"); + return -EINVAL; + } + } else { + dev_err(&pdev->dev, "Invalid rx-clock-name property\n"); + return -EINVAL; + } + + sprop = of_get_property(np, "tx-clock-name", NULL); + if (sprop) { + ut_info->uf_info.tx_clock = qe_clock_source(sprop); + if ((ut_info->uf_info.tx_clock < QE_CLK_NONE) || + (ut_info->uf_info.tx_clock > QE_CLK24)) { + dev_err(&pdev->dev, "Invalid tx-clock-name property\n"); + return -EINVAL; + } + } else { + dev_err(&pdev->dev, "Invalid tx-clock-name property\n"); + return -EINVAL; + } + + /* use the same clock when work in loopback */ + if (ut_info->uf_info.rx_clock == ut_info->uf_info.tx_clock) + qe_setbrg(ut_info->uf_info.rx_clock, 20000000, 1); + + ret = of_address_to_resource(np, 0, &res); + if (ret) + return -EINVAL; + + ut_info->uf_info.regs = res.start; + ut_info->uf_info.irq = irq_of_parse_and_map(np, 0); + + uhdlc_priv = kzalloc(sizeof(*uhdlc_priv), GFP_KERNEL); + if (!uhdlc_priv) { + ret = -ENOMEM; + dev_err(&pdev->dev, "No mem to alloc hdlc private data\n"); + goto err_alloc_priv; + } + + dev_set_drvdata(&pdev->dev, uhdlc_priv); + uhdlc_priv->dev = &pdev->dev; + uhdlc_priv->ut_info = ut_info; + + if (of_get_property(np, "fsl,tdm-interface", NULL)) + uhdlc_priv->tsa = 1; + + if (of_get_property(np, "fsl,ucc-internal-loopback", NULL)) + uhdlc_priv->loopback = 1; + + if (uhdlc_priv->tsa == 1) { + utdm = kzalloc(sizeof(*utdm), GFP_KERNEL); + if (!utdm) { + ret = -ENOMEM; + dev_err(&pdev->dev, "No mem to alloc ucc tdm data\n"); + goto err_alloc_utdm; + } + uhdlc_priv->utdm = utdm; + ret = ucc_of_parse_tdm(np, utdm, ut_info); + if (ret) + goto err_miss_tsa_property; + } + + ret = uhdlc_init(uhdlc_priv); + if (ret) { + dev_err(&pdev->dev, "Failed to init uhdlc\n"); + goto err_hdlc_init; + } + + dev = alloc_hdlcdev(uhdlc_priv); + if (!dev) { + ret = -ENOMEM; + pr_err("ucc_hdlc: unable to allocate memory\n"); + goto err_hdlc_init; + } + + uhdlc_priv->ndev = dev; + hdlc = dev_to_hdlc(dev); + dev->tx_queue_len = 16; + dev->netdev_ops = &uhdlc_ops; + hdlc->attach = ucc_hdlc_attach; + hdlc->xmit = ucc_hdlc_tx; + netif_napi_add(dev, &uhdlc_priv->napi, ucc_hdlc_poll, 32); + if (register_hdlc_device(dev)) { + ret = -ENOBUFS; + pr_err("ucc_hdlc: unable to register hdlc device\n"); + free_netdev(dev); + goto err_hdlc_init; + } + +#ifdef DEBUG + dump_priv(uhdlc_priv); + dump_ucc(uhdlc_priv); + dump_bds(uhdlc_priv); + if (uhdlc_priv->tsa) + mem_disp((u8 *)uhdlc_priv->utdm->si_regs, 0x20); +#endif + + return 0; + +err_hdlc_init: +err_miss_tsa_property: + kfree(uhdlc_priv); + if (uhdlc_priv->tsa) + kfree(utdm); +err_alloc_utdm: + kfree(uhdlc_priv); +err_alloc_priv: + return ret; +} + +static int ucc_hdlc_remove(struct platform_device *pdev) +{ + struct ucc_hdlc_private *priv = dev_get_drvdata(&pdev->dev); + + uhdlc_memclean(priv); + + if (priv->utdm->si_regs) { + iounmap(priv->utdm->si_regs); + priv->utdm->si_regs = NULL; + } + + if (priv->utdm->siram) { + iounmap(priv->utdm->siram); + priv->utdm->siram = NULL; + } + kfree(priv); + + dev_info(&pdev->dev, "UCC based hdlc module removed\n"); + + return 0; +} + +static const struct of_device_id fsl_ucc_hdlc_of_match[] = { + { + .compatible = "fsl,ucc-hdlc", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, fsl_ucc_hdlc_of_match); + +static struct platform_driver ucc_hdlc_driver = { + .probe = ucc_hdlc_probe, + .remove = ucc_hdlc_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .pm = HDLC_PM_OPS, + .of_match_table = fsl_ucc_hdlc_of_match, + }, +}; + +static int __init ucc_hdlc_init(void) +{ + return platform_driver_register(&ucc_hdlc_driver); +} + +static void __exit ucc_hdlc_exit(void) +{ + platform_driver_unregister(&ucc_hdlc_driver); +} + +module_init(ucc_hdlc_init); +module_exit(ucc_hdlc_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale Semiconductor Inc."); +MODULE_DESCRIPTION("Driver For Freescale QE UCC HDLC controller"); +MODULE_VERSION("1.0"); diff --git a/drivers/net/wan/fsl_ucc_hdlc.h b/drivers/net/wan/fsl_ucc_hdlc.h new file mode 100644 index 0000000..ded03d6 --- /dev/null +++ b/drivers/net/wan/fsl_ucc_hdlc.h @@ -0,0 +1,140 @@ +/* Freescale QUICC Engine HDLC Device Driver + * + * Copyright 2014 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef CONFIG_UCC_HDLC_H +#define CONFIG_UCC_HDLC_H + +#include +#include + +#include +#include + +#include +#include + +/* UCC HDLC event register */ +#define UCCE_HDLC_RX_EVENTS \ +(UCC_HDLC_UCCE_RXF | UCC_HDLC_UCCE_RXB | UCC_HDLC_UCCE_BSY) +#define UCCE_HDLC_TX_EVENTS (UCC_HDLC_UCCE_TXB | UCC_HDLC_UCCE_TXE) + +struct ucc_hdlc_param { + __be16 riptr; + __be16 tiptr; + __be16 res0; + __be16 mrblr; + __be32 rstate; + __be32 rbase; + __be16 rbdstat; + __be16 rbdlen; + __be32 rdptr; + __be32 tstate; + __be32 tbase; + __be16 tbdstat; + __be16 tbdlen; + __be32 tdptr; + __be32 rbptr; + __be32 tbptr; + __be32 rcrc; + __be32 res1; + __be32 tcrc; + __be32 res2; + __be32 res3; + __be32 c_mask; + __be32 c_pres; + __be16 disfc; + __be16 crcec; + __be16 abtsc; + __be16 nmarc; + __be32 max_cnt; + __be16 mflr; + __be16 rfthr; + __be16 rfcnt; + __be16 hmask; + __be16 haddr1; + __be16 haddr2; + __be16 haddr3; + __be16 haddr4; + __be16 ts_tmp; + __be16 tmp_mb; +} __attribute__ ((__packed__)); + +struct ucc_hdlc_private { + struct ucc_tdm *utdm; + struct ucc_tdm_info *ut_info; + struct ucc_fast_private *uccf; + struct device *dev; + struct net_device *ndev; + struct napi_struct napi; + struct ucc_fast __iomem *uf_regs; /* UCC Fast registers */ + struct ucc_hdlc_param __iomem *ucc_pram; + u16 tsa; + bool hdlc_busy; + u8 loopback; + + u8 *tx_buffer; /* buffer used for Tx by the HDLC */ + u8 *rx_buffer; /* buffer used for Rx by the HDLC */ + dma_addr_t dma_tx_addr; /* dma mapped buffer for HDLC Tx */ + dma_addr_t dma_rx_addr; /* dma mapped buffer for HDLC Rx */ + + struct qe_bd *tx_bd_base; + struct qe_bd *rx_bd_base; + dma_addr_t dma_tx_bd; + dma_addr_t dma_rx_bd; + struct qe_bd *curtx_bd; + struct qe_bd *currx_bd; + struct qe_bd *dirty_tx; + u16 currx_bdnum; + + struct sk_buff **tx_skbuff; + struct sk_buff **rx_skbuff; + u16 skb_curtx; + u16 skb_currx; + unsigned short skb_dirtytx; + + unsigned short tx_ring_size; + unsigned short rx_ring_size; + u32 ucc_pram_offset; + + unsigned short encoding; + unsigned short parity; + u32 clocking; + spinlock_t lock; /* lock for Tx BD and Tx buffer */ +#ifdef CONFIG_PM + struct ucc_hdlc_param *ucc_pram_bak; + u32 gumr; + u8 guemr; + u32 cmxsi1cr_l, cmxsi1cr_h; + u32 cmxsi1syr; + u32 cmxucr[4]; +#endif +}; + +#define TX_BD_RING_LEN 0x10 +#define RX_BD_RING_LEN 0x20 +#define RX_CLEAN_MAX 0x10 +#define NUM_OF_BUF 4 +#define MAX_RX_BUF_LENGTH (48 * 0x20) +#define ALIGNMENT_OF_UCC_HDLC_PRAM 64 +#define SI_BANK_SIZE 128 +#define MAX_HDLC_NUM 4 +#define HDLC_HEAD_LEN 2 +#define HDLC_CRC_SIZE 2 +#define TX_RING_MOD_MASK(size) (size - 1) +#define RX_RING_MOD_MASK(size) (size - 1) + +#define HDLC_HEAD_MASK 0x0000 +#define DEFAULT_HDLC_HEAD 0xff44 +#define DEFAULT_ADDR_MASK 0x00ff +#define DEFAULT_HDLC_ADDR 0x00ff + +#define DEFAULT_PPP_HEAD 0xff03 + +#endif diff --git a/include/soc/fsl/qe/ucc_fast.h b/include/soc/fsl/qe/ucc_fast.h index e898895..d775550 100644 --- a/include/soc/fsl/qe/ucc_fast.h +++ b/include/soc/fsl/qe/ucc_fast.h @@ -27,12 +27,16 @@ #define R_I 0x10000000 /* interrupt on reception */ #define R_L 0x08000000 /* last */ #define R_F 0x04000000 /* first */ +#define R_CM 0x02000000 /* first */ +#define R_CR 0x00040000 /* first */ /* transmit BD's status */ #define T_R 0x80000000 /* ready bit */ #define T_W 0x20000000 /* wrap bit */ #define T_I 0x10000000 /* interrupt on completion */ #define T_L 0x08000000 /* last */ +#define T_TC 0x04000000 /* crc */ +#define T_TM 0x02000000 /* crc */ /* Rx Data buffer must be 4 bytes aligned in most cases */ #define UCC_FAST_RX_ALIGN 4