From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 06674C433F5 for ; Wed, 29 Sep 2021 15:07:26 +0000 (UTC) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 52C60613A6 for ; Wed, 29 Sep 2021 15:07:25 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org 52C60613A6 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=nxp.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=lists.denx.de Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id AC753820ED; Wed, 29 Sep 2021 17:06:56 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=nxp.com header.i=@nxp.com header.b="rZ8uCacG"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id A9B3080F3B; Wed, 29 Sep 2021 17:05:36 +0200 (CEST) Received: from EUR03-DB5-obe.outbound.protection.outlook.com (mail-db5eur03on0614.outbound.protection.outlook.com [IPv6:2a01:111:f400:fe0a::614]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 5E8DB80F84 for ; Wed, 29 Sep 2021 17:05:09 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=vladimir.oltean@nxp.com ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=J9ROx5tcAUKqc6kNfAdBjwSlcIiJ1pAnNDKLKwCxIRYq2PYTBGxMfEk1bu62vkU3panv/4cq+iCvMSkZUZb81/Ea79ZeVod6642fmCAf8lFuoFvTDGkrqTSSymJojym+p5i3Ub6HDLjbTY5gDEwLmTh0ddRJ7u4ofp0mWNQ4Ty5wFSmQNNWcn+60cz82ZbpI3GXOscJQQTSu5LsA6qy64PsGJvVdl7F2b3UVX8Ofl9dDbwSRsHBr+AyKoAf27riXKMSaGfIdE1LTQ22TqZW7tTT0jtXVb9gSBR5sxLAn1FVEfGDsf7Q4Ev9yfV9xLBE6VdYj7UBNwx/7YGyJYfJv1w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=YWXHzMVNfy3Os+4DV0gjVHACO7w+IBaMj9ike6Ta8UA=; b=H6pV+jqbzMb/2D7BCu5TNv5jpu19rcealNUUasPJTUBBsVSHpa/SDTSEbo1oPGaWj4vWluiTIzI+CJUW/kCh6lQcDrQfWbBGtZQl+UFIym/OOzhx2bcD55lNf75spukZuCIauhIGpKxIqetVm+Wh9wVklbZ2qyqpVkWTx040JBWSrLUeu5Fem2RZff+Ee1BrR0eg7b/C0S5YZJCxpSomcQEO0p6U5uWQyTLI1BsrHkYyoSZVDhfdAaDi//BoU5S4qRWKkNbWnS5VXh85wD23BEVDBc1KjqjzP3vi+ahJqgXKRyeZSk9Ja7Ei8mjNdkOuDeZTJ69ss6wb3xwFnujfpw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=YWXHzMVNfy3Os+4DV0gjVHACO7w+IBaMj9ike6Ta8UA=; b=rZ8uCacGmCXPU53oKN5axwNYZ5oLBTmbISqO/D0P0nvQtcHiEZIkivkWb20E6SQKF5g7b6i5AjTO61d6J6fP+gsVvo+Rnxk2XQrl5CztTnKtWWDmcFBlldkHqdLUbYawguV1rAPhE8Ho0tDhFssoDCcO3dTYdIoOjEv+5rHrh8k= Authentication-Results: lists.denx.de; dkim=none (message not signed) header.d=none;lists.denx.de; dmarc=none action=none header.from=nxp.com; Received: from VI1PR04MB5136.eurprd04.prod.outlook.com (2603:10a6:803:55::19) by VI1PR04MB6944.eurprd04.prod.outlook.com (2603:10a6:803:133::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4544.13; Wed, 29 Sep 2021 15:05:07 +0000 Received: from VI1PR04MB5136.eurprd04.prod.outlook.com ([fe80::e157:3280:7bc3:18c4]) by VI1PR04MB5136.eurprd04.prod.outlook.com ([fe80::e157:3280:7bc3:18c4%5]) with mapi id 15.20.4544.022; Wed, 29 Sep 2021 15:05:07 +0000 From: Vladimir Oltean To: u-boot@lists.denx.de, Joe Hershberger , Ramon Fried Cc: Priyanka Jain , Bin Meng , Michael Walle Subject: [PATCH v2 07/10] net: dsa: sja1105: add support for SGMII Date: Wed, 29 Sep 2021 18:04:42 +0300 Message-Id: <20210929150445.1593364-8-vladimir.oltean@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210929150445.1593364-1-vladimir.oltean@nxp.com> References: <20210929150445.1593364-1-vladimir.oltean@nxp.com> Content-Transfer-Encoding: 8bit Content-Type: text/plain X-ClientProxiedBy: AM0PR01CA0171.eurprd01.prod.exchangelabs.com (2603:10a6:208:aa::40) To VI1PR04MB5136.eurprd04.prod.outlook.com (2603:10a6:803:55::19) MIME-Version: 1.0 Received: from localhost.localdomain (188.26.53.217) by AM0PR01CA0171.eurprd01.prod.exchangelabs.com (2603:10a6:208:aa::40) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4566.14 via Frontend Transport; Wed, 29 Sep 2021 15:05:06 +0000 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 463b1d80-009f-41db-72fe-08d9835a8634 X-MS-TrafficTypeDiagnostic: VI1PR04MB6944: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:10000; X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: C89MpPmMyLeb62UqFw6/bBQ6Zg3A8ZUe11vkZt+IqsZbuxgY4avozljEII7xv+EuH7U3pmVZJwhUxdIFNlU9W9LMYIj35DhEdsVb3lkrp0H0E5tMpOdLSH3zIgD0XP/fdj/vF9PgwXlif56uxTh9o3Sjo23R1AEG5noOj8POwL/9CClmcvIH++rLpiTjnWEdFBHSOryc7FCfDYdqo2rj6z1uAM5ncp+N8Ca+GA7Onom9Y5hHgKFkZXjKbvVhxUaEljrzHQ7+c/lhRBqtAWdIDTDz2IOsZHSlKGlEMnFEw+396oynSXuhn57yaGF2JvD3W1yBtj1TgEF3GA5tBwuOMlvhYkntzMYHqSQhid1qMPkb42Yacj/Gtj9fotTkrr1nNV5WlI1MzCjCvuSnAbEjR+BrUNGxTWOwBMdnWETFKg94XaplGljF/JYk6p+sVFJiDX8dtsk8gCdHJjulv2467dXArAAa3yVDDZk6W0g/Sl4bD7LWu9xj9eLDIQht+8F8INju3ex8URSopIWUWt/CN2LAxmyu0s/0Co6eFM9iSxUmgu69qnd4ifKCIjnK+7xXYAeHV/NrISddw/oHy6iGhzM+L3TdN5FdWEuQSpqfhRJ04yUVvCWIrczv+MuD/evRzbbaN6B+YWoRWdqDCnhF35TnkfG5P1vBjnurLPo7OmulEmQnhYD1Xr6RnFsuww4d7lNRtxoGB8/y4Ausax47Vv0yA6BqMBDgjJBYLjQtAoxsvjNmVtDIzC9dI+9b9lyuNgZtnJjggS02gcNpq0uyU16emPBmHezEHK0Mqlf8JOZUtEseKvedDdMlA8Cc/GxnJoJ6KmzXA5jswXBhq+BLZg== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:VI1PR04MB5136.eurprd04.prod.outlook.com; PTR:; CAT:NONE; SFS:(4636009)(366004)(38350700002)(66946007)(66556008)(26005)(66476007)(86362001)(6512007)(6486002)(110136005)(966005)(508600001)(8676002)(83380400001)(6506007)(186003)(52116002)(316002)(8936002)(6666004)(956004)(38100700002)(44832011)(5660300002)(30864003)(2906002)(1076003)(2616005)(54906003)(4326008)(36756003); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?wRUD9bT3n2cN//flHBxbDomPr1mU23I05Ju8V8zwZcAy6D3y9uCLmJbewRDt?= =?us-ascii?Q?Jyn9MLQDe+m6pXU9jwJuLn3ZeSUQv/Vmvvvk/V7RRqhNIUqUQ48u6GQcPWYM?= =?us-ascii?Q?GVMzQL/GecqPFa43d/05w/WB37ew33wQ1Hj9Po/vQrjeX0DJQ2N484+CMXCr?= =?us-ascii?Q?J5ISr48hKPfAFBXxlfzdJ1QQgLbuo/aRSt60rFocex7Hepqh7tOQ23OdThAg?= =?us-ascii?Q?bG9Q5LgY3hvN7FtwIMNE+kj6FAXM272IAREMkXCslcpxFy/YqvVSbnu8cWd1?= =?us-ascii?Q?FAUV4CDrwQYNKDxTA1pTcpSHsyU4Z3LEcNfSd6bVtu+p1jdnhWvbrQ2w5hKt?= =?us-ascii?Q?iMIt8sZ0qfN3vszRScTYt6QGXJVJceuQHr6g+FyLAACTLEZyqwSf7ctfy33G?= =?us-ascii?Q?Mnjk8HfFz4oLgyGQavnbjO/Xv1Ro9yJSWFx7l/CBxHM0F2hQea/m6Lhjpt5c?= =?us-ascii?Q?hhzqGluvK/UKfc4zHhyBpfwmO7hAlt6kd7KdybUR4qGvVz8jG2LgP3ABhkBZ?= =?us-ascii?Q?TJSz56HtyyMzMjRva1yjrSfTgcgo9HwlOb+9EnB9xv89AwkCCDVuvGNS+m20?= =?us-ascii?Q?RgtxxNfuZxCEXoyfmXTK9V2j0kbYxjLx88ydD+WDNLZlBWuavrzpKztB0twR?= =?us-ascii?Q?M/SEzByoavuxCoIa1q4UXGGAPMmcZJyHod+NfUbOys+sZPwRW7DrvIy27syI?= =?us-ascii?Q?Xly8f9Gl8JxfjhWMU//w4gDha3ABCfmrrWdjeUhIAVsUqwE95VAbJSHJKZT+?= =?us-ascii?Q?tcw4tn3iBqhD5ih2E6gYBPv12ZE8UBWotn9cyQf1vqNhh9KZaWtUaIJlt7Ti?= =?us-ascii?Q?nf4S2eQQbNWWPAQB7p1arhx+6Jy19bBM/jdx3xp8JmvztyStMmwNuVg8i/Iw?= =?us-ascii?Q?fK4u3ftGErFQe4E3ZkTUy8BakICfcQTv/wFKCtWGWCQ3EDWjSdkz+rSrx1Fz?= =?us-ascii?Q?1KIBZlpUiDjtfhmEX/vptz7dVq2gwULogTGkIBfcuEQ2FIWc9TNcOp0is1x7?= =?us-ascii?Q?nSOKzqDyrlqtlDlnU8bcq9P97Ubkxuic6S4hJreirq4Z/tbKwX6wljWTLlso?= =?us-ascii?Q?iL/Y/ibiD25CMQuYQf8UFOvMWucHVFV9UL/0GmngwRC4skUfKO/GOCB8aC9A?= =?us-ascii?Q?yNPMuEV8z7EME45fdyhUVb1hsirM3akF9iJ5DhTFItb8Bt19t2/oqkB7o+wD?= =?us-ascii?Q?+qTVulGh9zHnY9B0YoU1nSlNrPY7K9wh42Qn7CyRsQDMtu/I9YLV/kcqE18t?= =?us-ascii?Q?YKl53oSun16stCl4UEVNHvfjiZw7QLUYSSNWuqMFyGVwL2cZew/zE+JauT0s?= =?us-ascii?Q?9U4gUlSxGm/jV1K63QSz34B8?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 463b1d80-009f-41db-72fe-08d9835a8634 X-MS-Exchange-CrossTenant-AuthSource: VI1PR04MB5136.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 29 Sep 2021 15:05:07.3254 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: /z2qH9+mpmcgaoo4dKSlHCwOvqDRo4cH8w0SJv+ugEikZd0ndznIY2z9FvuqY0ikAchwJ0ZdIEbM153eqtmUyw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR04MB6944 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean The list of ports which support SGMII depending on switch generation is available here: https://www.kernel.org/doc/html/latest/networking/dsa/sja1105.html#port-compatibility-matrix SGMII can either be used to connect to an external PHY or to the host port. In the first case, the use of in-band autoneg is expected, in the last, in-band autoneg is expected to be turned off (fixed-link). So the driver supports both cases. SGMII support means configuring the PCS and PMA. The PCS is a Synopsys Designware XPCS, in Linux this has a separate driver but here it is embedded within the sja1105 driver. If needed it can be taken out later, although we would need a UCLASS_PCS for it, which we don't have atm. Nonetheless, I did go all the way to export an internal MDIO bus for PCS access, because it is nice to be able to debug the PCS through commands such as: => mdio read ethernet-switch@1-pcs 4 1f.0 Reading from bus ethernet-switch@1-pcs PHY at address 4: 31.0 - 0x1140 The internal MDIO bus is not registered with DM because there is no udevice on it, as mentioned. But the XPCS code can still be ripped out, as needed. I did not add support for 2500base-x because I do not expect this interface type to be used as a boot source for anybody, it would just add unnecessary bloat. Signed-off-by: Vladimir Oltean --- v1->v2: - split from the previous patch. - fixed the XPCS initialization sequence to be the same as what it is going to be in Linux when this patch gets applied: https://patchwork.kernel.org/project/netdevbpf/patch/20210929120534.411157-1-vee.khee.wong@linux.intel.com/ drivers/net/sja1105.c | 571 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 570 insertions(+), 1 deletion(-) diff --git a/drivers/net/sja1105.c b/drivers/net/sja1105.c index 077240311619..17bab33eddb7 100644 --- a/drivers/net/sja1105.c +++ b/drivers/net/sja1105.c @@ -37,6 +37,7 @@ enum packing_op { #define SJA1105ET_FDB_BIN_SIZE 4 #define SJA1105_SIZE_CGU_CMD 4 #define SJA1105_SIZE_RESET_CMD 4 +#define SJA1105_SIZE_MDIO_CMD 4 #define SJA1105_SIZE_SPI_MSG_HEADER 4 #define SJA1105_SIZE_SPI_MSG_MAXLEN (64 * 4) #define SJA1105_SIZE_DEVICE_ID 4 @@ -95,6 +96,8 @@ enum packing_op { #define SJA1105_RSV_ADDR 0xffffffffffffffffull +#define SJA1110_PCS_BANK_REG SJA1110_SPI_ADDR(0x3fc) + #define DSA_8021Q_DIR_TX BIT(11) #define DSA_8021Q_PORT_SHIFT 0 #define DSA_8021Q_PORT_MASK GENMASK(3, 0) @@ -103,6 +106,89 @@ enum packing_op { #define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000) +/* XPCS registers */ + +/* VR MII MMD registers offsets */ +#define DW_VR_MII_DIG_CTRL1 0x8000 +#define DW_VR_MII_AN_CTRL 0x8001 +#define DW_VR_MII_DIG_CTRL2 0x80e1 + +/* VR_MII_DIG_CTRL1 */ +#define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW BIT(9) + +/* VR_MII_DIG_CTRL2 */ +#define DW_VR_MII_DIG_CTRL2_TX_POL_INV BIT(4) + +/* VR_MII_AN_CTRL */ +#define DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT 3 +#define DW_VR_MII_TX_CONFIG_MASK BIT(3) +#define DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII 0x0 +#define DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT 1 +#define DW_VR_MII_PCS_MODE_MASK GENMASK(2, 1) +#define DW_VR_MII_PCS_MODE_C37_SGMII 0x2 + +/* PMA registers */ + +/* LANE_DRIVER1_0 register */ +#define SJA1110_LANE_DRIVER1_0 0x8038 +#define SJA1110_TXDRV(x) (((x) << 12) & GENMASK(14, 12)) + +/* LANE_DRIVER2_0 register */ +#define SJA1110_LANE_DRIVER2_0 0x803a +#define SJA1110_TXDRVTRIM_LSB(x) ((x) & GENMASK_ULL(15, 0)) + +/* LANE_DRIVER2_1 register */ +#define SJA1110_LANE_DRIVER2_1 0x803b +#define SJA1110_LANE_DRIVER2_1_RSV BIT(9) +#define SJA1110_TXDRVTRIM_MSB(x) (((x) & GENMASK_ULL(23, 16)) >> 16) + +/* LANE_TRIM register */ +#define SJA1110_LANE_TRIM 0x8040 +#define SJA1110_TXTEN BIT(11) +#define SJA1110_TXRTRIM(x) (((x) << 8) & GENMASK(10, 8)) +#define SJA1110_TXPLL_BWSEL BIT(7) +#define SJA1110_RXTEN BIT(6) +#define SJA1110_RXRTRIM(x) (((x) << 3) & GENMASK(5, 3)) +#define SJA1110_CDR_GAIN BIT(2) +#define SJA1110_ACCOUPLE_RXVCM_EN BIT(0) + +/* LANE_DATAPATH_1 register */ +#define SJA1110_LANE_DATAPATH_1 0x8037 + +/* POWERDOWN_ENABLE register */ +#define SJA1110_POWERDOWN_ENABLE 0x8041 +#define SJA1110_TXPLL_PD BIT(12) +#define SJA1110_TXPD BIT(11) +#define SJA1110_RXPKDETEN BIT(10) +#define SJA1110_RXCH_PD BIT(9) +#define SJA1110_RXBIAS_PD BIT(8) +#define SJA1110_RESET_SER_EN BIT(7) +#define SJA1110_RESET_SER BIT(6) +#define SJA1110_RESET_DES BIT(5) +#define SJA1110_RCVEN BIT(4) + +/* RXPLL_CTRL0 register */ +#define SJA1110_RXPLL_CTRL0 0x8065 +#define SJA1110_RXPLL_FBDIV(x) (((x) << 2) & GENMASK(9, 2)) + +/* RXPLL_CTRL1 register */ +#define SJA1110_RXPLL_CTRL1 0x8066 +#define SJA1110_RXPLL_REFDIV(x) ((x) & GENMASK(4, 0)) + +/* TXPLL_CTRL0 register */ +#define SJA1110_TXPLL_CTRL0 0x806d +#define SJA1110_TXPLL_FBDIV(x) ((x) & GENMASK(11, 0)) + +/* TXPLL_CTRL1 register */ +#define SJA1110_TXPLL_CTRL1 0x806e +#define SJA1110_TXPLL_REFDIV(x) ((x) & GENMASK(5, 0)) + +/* RX_DATA_DETECT register */ +#define SJA1110_RX_DATA_DETECT 0x8045 + +/* RX_CDR_CTLE register */ +#define SJA1110_RX_CDR_CTLE 0x8042 + /* UM10944.pdf Page 11, Table 2. Configuration Blocks */ enum { BLKID_L2_POLICING = 0x06, @@ -203,11 +289,18 @@ struct sja1105_static_config { struct sja1105_table tables[BLK_IDX_MAX]; }; +struct sja1105_xpcs_cfg { + bool inband_an; + int speed; +}; + struct sja1105_private { struct sja1105_static_config static_config; bool rgmii_rx_delay[SJA1105_MAX_NUM_PORTS]; bool rgmii_tx_delay[SJA1105_MAX_NUM_PORTS]; u16 pvid[SJA1105_MAX_NUM_PORTS]; + struct sja1105_xpcs_cfg xpcs_cfg[SJA1105_MAX_NUM_PORTS]; + struct mii_dev *mdio_pcs; const struct sja1105_info *info; struct udevice *dev; }; @@ -226,6 +319,7 @@ typedef enum { XMII_MODE_MII = 0, XMII_MODE_RMII = 1, XMII_MODE_RGMII = 2, + XMII_MODE_SGMII = 3, } sja1105_phy_interface_t; enum { @@ -263,6 +357,7 @@ struct sja1105_regs { u64 rgmii_tx_clk[SJA1105_MAX_NUM_PORTS]; u64 rmii_ref_clk[SJA1105_MAX_NUM_PORTS]; u64 rmii_ext_tx_clk[SJA1105_MAX_NUM_PORTS]; + u64 pcs_base[SJA1105_MAX_NUM_PORTS]; }; struct sja1105_info { @@ -272,10 +367,15 @@ struct sja1105_info { const struct sja1105_regs *regs; int (*reset_cmd)(struct sja1105_private *priv); int (*setup_rgmii_delay)(struct sja1105_private *priv, int port); + int (*pcs_mdio_read)(struct mii_dev *bus, int phy, int mmd, int reg); + int (*pcs_mdio_write)(struct mii_dev *bus, int phy, int mmd, int reg, + u16 val); + int (*pma_config)(struct sja1105_private *priv, int port); const char *name; bool supports_mii[SJA1105_MAX_NUM_PORTS]; bool supports_rmii[SJA1105_MAX_NUM_PORTS]; bool supports_rgmii[SJA1105_MAX_NUM_PORTS]; + bool supports_sgmii[SJA1105_MAX_NUM_PORTS]; const u64 port_speed[SJA1105_SPEED_MAX]; }; @@ -2030,6 +2130,233 @@ static int sja1105_rmii_clocking_setup(struct sja1105_private *priv, int port, return 0; } +static int sja1105_pcs_read(struct sja1105_private *priv, int addr, + int devad, int regnum) +{ + return priv->mdio_pcs->read(priv->mdio_pcs, addr, devad, regnum); +} + +static int sja1105_pcs_write(struct sja1105_private *priv, int addr, + int devad, int regnum, u16 val) +{ + return priv->mdio_pcs->write(priv->mdio_pcs, addr, devad, regnum, val); +} + +/* In NXP SJA1105, the PCS is integrated with a PMA that has the TX lane + * polarity inverted by default (PLUS is MINUS, MINUS is PLUS). To obtain + * normal non-inverted behavior, the TX lane polarity must be inverted in the + * PCS, via the DIGITAL_CONTROL_2 register. + */ +static int sja1105_pma_config(struct sja1105_private *priv, int port) +{ + return sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, + DW_VR_MII_DIG_CTRL2, + DW_VR_MII_DIG_CTRL2_TX_POL_INV); +} + +static int sja1110_pma_config(struct sja1105_private *priv, int port) +{ + u16 txpll_fbdiv = 0x19, txpll_refdiv = 0x1; + u16 rxpll_fbdiv = 0x19, rxpll_refdiv = 0x1; + u16 rx_cdr_ctle = 0x212a; + u16 val; + int rc; + + /* Program TX PLL feedback divider and reference divider settings for + * correct oscillation frequency. + */ + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, SJA1110_TXPLL_CTRL0, + SJA1110_TXPLL_FBDIV(txpll_fbdiv)); + if (rc < 0) + return rc; + + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, SJA1110_TXPLL_CTRL1, + SJA1110_TXPLL_REFDIV(txpll_refdiv)); + if (rc < 0) + return rc; + + /* Program transmitter amplitude and disable amplitude trimming */ + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, + SJA1110_LANE_DRIVER1_0, SJA1110_TXDRV(0x5)); + if (rc < 0) + return rc; + + val = SJA1110_TXDRVTRIM_LSB(0xffffffull); + + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, + SJA1110_LANE_DRIVER2_0, val); + if (rc < 0) + return rc; + + val = SJA1110_TXDRVTRIM_MSB(0xffffffull) | SJA1110_LANE_DRIVER2_1_RSV; + + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, + SJA1110_LANE_DRIVER2_1, val); + if (rc < 0) + return rc; + + /* Enable input and output resistor terminations for low BER. */ + val = SJA1110_ACCOUPLE_RXVCM_EN | SJA1110_CDR_GAIN | + SJA1110_RXRTRIM(4) | SJA1110_RXTEN | SJA1110_TXPLL_BWSEL | + SJA1110_TXRTRIM(3) | SJA1110_TXTEN; + + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, SJA1110_LANE_TRIM, + val); + if (rc < 0) + return rc; + + /* Select PCS as transmitter data source. */ + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, + SJA1110_LANE_DATAPATH_1, 0); + if (rc < 0) + return rc; + + /* Program RX PLL feedback divider and reference divider for correct + * oscillation frequency. + */ + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, SJA1110_RXPLL_CTRL0, + SJA1110_RXPLL_FBDIV(rxpll_fbdiv)); + if (rc < 0) + return rc; + + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, SJA1110_RXPLL_CTRL1, + SJA1110_RXPLL_REFDIV(rxpll_refdiv)); + if (rc < 0) + return rc; + + /* Program threshold for receiver signal detector. + * Enable control of RXPLL by receiver signal detector to disable RXPLL + * when an input signal is not present. + */ + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, + SJA1110_RX_DATA_DETECT, 0x0005); + if (rc < 0) + return rc; + + /* Enable TX and RX PLLs and circuits. + * Release reset of PMA to enable data flow to/from PCS. + */ + rc = sja1105_pcs_read(priv, port, MDIO_MMD_VEND2, + SJA1110_POWERDOWN_ENABLE); + if (rc < 0) + return rc; + + val = rc & ~(SJA1110_TXPLL_PD | SJA1110_TXPD | SJA1110_RXCH_PD | + SJA1110_RXBIAS_PD | SJA1110_RESET_SER_EN | + SJA1110_RESET_SER | SJA1110_RESET_DES); + val |= SJA1110_RXPKDETEN | SJA1110_RCVEN; + + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, + SJA1110_POWERDOWN_ENABLE, val); + if (rc < 0) + return rc; + + /* Program continuous-time linear equalizer (CTLE) settings. */ + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, SJA1110_RX_CDR_CTLE, + rx_cdr_ctle); + if (rc < 0) + return rc; + + return 0; +} + +static int sja1105_xpcs_config_aneg_c37_sgmii(struct sja1105_private *priv, + int port) +{ + int rc; + + rc = sja1105_pcs_read(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1); + if (rc < 0) + return rc; + rc &= ~MDIO_AN_CTRL1_ENABLE; + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1, + rc); + if (rc < 0) + return rc; + + rc = sja1105_pcs_read(priv, port, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL); + if (rc < 0) + return rc; + + rc &= ~(DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK); + rc |= (DW_VR_MII_PCS_MODE_C37_SGMII << + DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT & + DW_VR_MII_PCS_MODE_MASK); + rc |= (DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII << + DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT & + DW_VR_MII_TX_CONFIG_MASK); + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, + rc); + if (rc < 0) + return rc; + + rc = sja1105_pcs_read(priv, port, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1); + if (rc < 0) + return rc; + + if (priv->xpcs_cfg[port].inband_an) + rc |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; + else + rc &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; + + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, + rc); + if (rc < 0) + return rc; + + rc = sja1105_pcs_read(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1); + if (rc < 0) + return rc; + + if (priv->xpcs_cfg[port].inband_an) + rc |= MDIO_AN_CTRL1_ENABLE; + else + rc &= ~MDIO_AN_CTRL1_ENABLE; + + return sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1, rc); +} + +static int sja1105_xpcs_link_up_sgmii(struct sja1105_private *priv, int port) +{ + int val = BMCR_FULLDPLX; + + if (priv->xpcs_cfg[port].inband_an) + return 0; + + switch (priv->xpcs_cfg[port].speed) { + case SPEED_1000: + val = BMCR_SPEED1000; + break; + case SPEED_100: + val = BMCR_SPEED100; + break; + case SPEED_10: + val = BMCR_SPEED10; + break; + default: + dev_err(priv->dev, "Invalid PCS speed %d\n", + priv->xpcs_cfg[port].speed); + return -EINVAL; + } + + return sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1, val); +} + +static int sja1105_sgmii_setup(struct sja1105_private *priv, int port) +{ + int rc; + + rc = sja1105_xpcs_config_aneg_c37_sgmii(priv, port); + if (rc) + return rc; + + rc = sja1105_xpcs_link_up_sgmii(priv, port); + if (rc) + return rc; + + return priv->info->pma_config(priv, port); +} + static int sja1105_clocking_setup_port(struct sja1105_private *priv, int port) { struct sja1105_xmii_params_entry *mii; @@ -2054,6 +2381,9 @@ static int sja1105_clocking_setup_port(struct sja1105_private *priv, int port) case XMII_MODE_RGMII: rc = sja1105_rgmii_clocking_setup(priv, port, role); break; + case XMII_MODE_SGMII: + rc = sja1105_sgmii_setup(priv, port); + break; default: return -EINVAL; } @@ -2077,6 +2407,188 @@ static int sja1105_clocking_setup(struct sja1105_private *priv) return 0; } +static int sja1105_pcs_mdio_read(struct mii_dev *bus, int phy, int mmd, int reg) +{ + u8 packed_buf[SJA1105_SIZE_MDIO_CMD] = {0}; + struct sja1105_private *priv = bus->priv; + const int size = SJA1105_SIZE_MDIO_CMD; + u64 addr, tmp; + int rc; + + if (mmd == MDIO_DEVAD_NONE) + return -ENODEV; + + if (!priv->info->supports_sgmii[phy]) + return -ENODEV; + + addr = (mmd << 16) | (reg & GENMASK(15, 0)); + + if (mmd != MDIO_MMD_VEND1 && mmd != MDIO_MMD_VEND2) + return 0xffff; + + rc = sja1105_xfer_buf(priv, SPI_READ, addr, packed_buf, size); + if (rc < 0) + return rc; + + sja1105_packing(packed_buf, &tmp, 31, 0, size, UNPACK); + + return tmp & 0xffff; +} + +static int sja1105_pcs_mdio_write(struct mii_dev *bus, int phy, int mmd, + int reg, u16 val) +{ + u8 packed_buf[SJA1105_SIZE_MDIO_CMD] = {0}; + struct sja1105_private *priv = bus->priv; + const int size = SJA1105_SIZE_MDIO_CMD; + u64 addr, tmp; + + if (mmd == MDIO_DEVAD_NONE) + return -ENODEV; + + if (!priv->info->supports_sgmii[phy]) + return -ENODEV; + + addr = (mmd << 16) | (reg & GENMASK(15, 0)); + tmp = val; + + if (mmd != MDIO_MMD_VEND1 && mmd != MDIO_MMD_VEND2) + return -ENODEV; + + sja1105_packing(packed_buf, &tmp, 31, 0, size, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, addr, packed_buf, size); +} + +static int sja1110_pcs_mdio_read(struct mii_dev *bus, int phy, int mmd, int reg) +{ + struct sja1105_private *priv = bus->priv; + const struct sja1105_regs *regs = priv->info->regs; + u8 packed_buf[SJA1105_SIZE_MDIO_CMD] = {0}; + const int size = SJA1105_SIZE_MDIO_CMD; + int offset, bank; + u64 addr, tmp; + int rc; + + if (mmd == MDIO_DEVAD_NONE) + return -ENODEV; + + if (regs->pcs_base[phy] == SJA1105_RSV_ADDR) + return -ENODEV; + + addr = (mmd << 16) | (reg & GENMASK(15, 0)); + + bank = addr >> 8; + offset = addr & GENMASK(7, 0); + + /* This addressing scheme reserves register 0xff for the bank address + * register, so that can never be addressed. + */ + if (offset == 0xff) + return -ENODEV; + + tmp = bank; + + sja1105_packing(packed_buf, &tmp, 31, 0, size, PACK); + + rc = sja1105_xfer_buf(priv, SPI_WRITE, + regs->pcs_base[phy] + SJA1110_PCS_BANK_REG, + packed_buf, size); + if (rc < 0) + return rc; + + rc = sja1105_xfer_buf(priv, SPI_READ, regs->pcs_base[phy] + offset, + packed_buf, size); + if (rc < 0) + return rc; + + sja1105_packing(packed_buf, &tmp, 31, 0, size, UNPACK); + + return tmp & 0xffff; +} + +static int sja1110_pcs_mdio_write(struct mii_dev *bus, int phy, int mmd, + int reg, u16 val) +{ + struct sja1105_private *priv = bus->priv; + const struct sja1105_regs *regs = priv->info->regs; + u8 packed_buf[SJA1105_SIZE_MDIO_CMD] = {0}; + const int size = SJA1105_SIZE_MDIO_CMD; + int offset, bank; + u64 addr, tmp; + int rc; + + if (mmd == MDIO_DEVAD_NONE) + return -ENODEV; + + if (regs->pcs_base[phy] == SJA1105_RSV_ADDR) + return -ENODEV; + + addr = (mmd << 16) | (reg & GENMASK(15, 0)); + + bank = addr >> 8; + offset = addr & GENMASK(7, 0); + + /* This addressing scheme reserves register 0xff for the bank address + * register, so that can never be addressed. + */ + if (offset == 0xff) + return -ENODEV; + + tmp = bank; + sja1105_packing(packed_buf, &tmp, 31, 0, size, PACK); + + rc = sja1105_xfer_buf(priv, SPI_WRITE, + regs->pcs_base[phy] + SJA1110_PCS_BANK_REG, + packed_buf, size); + if (rc < 0) + return rc; + + tmp = val; + sja1105_packing(packed_buf, &tmp, 31, 0, size, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pcs_base[phy] + offset, + packed_buf, size); +} + +static int sja1105_mdiobus_register(struct sja1105_private *priv) +{ + struct udevice *dev = priv->dev; + struct mii_dev *bus; + int rc; + + if (!priv->info->pcs_mdio_read || !priv->info->pcs_mdio_write) + return 0; + + bus = mdio_alloc(); + if (!bus) + return -ENOMEM; + + snprintf(bus->name, MDIO_NAME_LEN, "%s-pcs", dev->name); + bus->read = priv->info->pcs_mdio_read; + bus->write = priv->info->pcs_mdio_write; + bus->priv = priv; + + rc = mdio_register(bus); + if (rc) { + mdio_free(bus); + return rc; + } + + priv->mdio_pcs = bus; + + return 0; +} + +static void sja1105_mdiobus_unregister(struct sja1105_private *priv) +{ + if (!priv->mdio_pcs) + return; + + mdio_unregister(priv->mdio_pcs); + mdio_free(priv->mdio_pcs); +} + static const struct sja1105_regs sja1105et_regs = { .device_id = 0x0, .prod_id = 0x100BC3, @@ -2185,6 +2697,9 @@ static const struct sja1105_regs sja1110_regs = { SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .pcs_base = {SJA1105_RSV_ADDR, 0x1c1400, 0x1c1800, 0x1c1c00, 0x1c2000, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, }; enum sja1105_switch_id { @@ -2279,6 +2794,9 @@ static const struct sja1105_info sja1105_info[] = { .setup_rgmii_delay = sja1105pqrs_setup_rgmii_delay, .reset_cmd = sja1105pqrs_reset_cmd, .regs = &sja1105pqrs_regs, + .pcs_mdio_read = sja1105_pcs_mdio_read, + .pcs_mdio_write = sja1105_pcs_mdio_write, + .pma_config = sja1105_pma_config, .port_speed = { [SJA1105_SPEED_AUTO] = 0, [SJA1105_SPEED_10MBPS] = 3, @@ -2288,6 +2806,7 @@ static const struct sja1105_info sja1105_info[] = { .supports_mii = {true, true, true, true, true}, .supports_rmii = {true, true, true, true, true}, .supports_rgmii = {true, true, true, true, true}, + .supports_sgmii = {false, false, false, false, true}, .name = "SJA1105R", }, [SJA1105S] = { @@ -2297,6 +2816,9 @@ static const struct sja1105_info sja1105_info[] = { .setup_rgmii_delay = sja1105pqrs_setup_rgmii_delay, .reset_cmd = sja1105pqrs_reset_cmd, .regs = &sja1105pqrs_regs, + .pcs_mdio_read = sja1105_pcs_mdio_read, + .pcs_mdio_write = sja1105_pcs_mdio_write, + .pma_config = sja1105_pma_config, .port_speed = { [SJA1105_SPEED_AUTO] = 0, [SJA1105_SPEED_10MBPS] = 3, @@ -2306,6 +2828,7 @@ static const struct sja1105_info sja1105_info[] = { .supports_mii = {true, true, true, true, true}, .supports_rmii = {true, true, true, true, true}, .supports_rgmii = {true, true, true, true, true}, + .supports_sgmii = {false, false, false, false, true}, .name = "SJA1105S", }, [SJA1110A] = { @@ -2315,6 +2838,9 @@ static const struct sja1105_info sja1105_info[] = { .setup_rgmii_delay = sja1110_setup_rgmii_delay, .reset_cmd = sja1110_reset_cmd, .regs = &sja1110_regs, + .pcs_mdio_read = sja1110_pcs_mdio_read, + .pcs_mdio_write = sja1110_pcs_mdio_write, + .pma_config = sja1110_pma_config, .port_speed = { [SJA1105_SPEED_AUTO] = 0, [SJA1105_SPEED_10MBPS] = 4, @@ -2327,6 +2853,8 @@ static const struct sja1105_info sja1105_info[] = { false, false, false, false, false, false}, .supports_rgmii = {false, false, true, true, false, false, false, false, false, false, false}, + .supports_sgmii = {false, true, true, true, true, + false, false, false, false, false, false}, .name = "SJA1110A", }, [SJA1110B] = { @@ -2336,6 +2864,9 @@ static const struct sja1105_info sja1105_info[] = { .setup_rgmii_delay = sja1110_setup_rgmii_delay, .reset_cmd = sja1110_reset_cmd, .regs = &sja1110_regs, + .pcs_mdio_read = sja1110_pcs_mdio_read, + .pcs_mdio_write = sja1110_pcs_mdio_write, + .pma_config = sja1110_pma_config, .port_speed = { [SJA1105_SPEED_AUTO] = 0, [SJA1105_SPEED_10MBPS] = 4, @@ -2348,6 +2879,8 @@ static const struct sja1105_info sja1105_info[] = { false, false, false, false, false, false}, .supports_rgmii = {false, false, true, true, false, false, false, false, false, false, false}, + .supports_sgmii = {false, false, false, true, true, + false, false, false, false, false, false}, .name = "SJA1110B", }, [SJA1110C] = { @@ -2357,6 +2890,9 @@ static const struct sja1105_info sja1105_info[] = { .setup_rgmii_delay = sja1110_setup_rgmii_delay, .reset_cmd = sja1110_reset_cmd, .regs = &sja1110_regs, + .pcs_mdio_read = sja1110_pcs_mdio_read, + .pcs_mdio_write = sja1110_pcs_mdio_write, + .pma_config = sja1110_pma_config, .port_speed = { [SJA1105_SPEED_AUTO] = 0, [SJA1105_SPEED_10MBPS] = 4, @@ -2369,6 +2905,8 @@ static const struct sja1105_info sja1105_info[] = { false, false, false, false, false, false}, .supports_rgmii = {false, false, true, true, false, false, false, false, false, false, false}, + .supports_sgmii = {false, false, false, false, true, + false, false, false, false, false, false}, .name = "SJA1110C", }, [SJA1110D] = { @@ -2378,6 +2916,9 @@ static const struct sja1105_info sja1105_info[] = { .setup_rgmii_delay = sja1110_setup_rgmii_delay, .reset_cmd = sja1110_reset_cmd, .regs = &sja1110_regs, + .pcs_mdio_read = sja1110_pcs_mdio_read, + .pcs_mdio_write = sja1110_pcs_mdio_write, + .pma_config = sja1110_pma_config, .port_speed = { [SJA1105_SPEED_AUTO] = 0, [SJA1105_SPEED_10MBPS] = 4, @@ -2390,6 +2931,8 @@ static const struct sja1105_info sja1105_info[] = { false, false, false, false, false, false}, .supports_rgmii = {false, false, true, false, false, false, false, false, false, false, false}, + .supports_sgmii = {false, true, true, true, true, + false, false, false, false, false, false}, .name = "SJA1110D", }, }; @@ -2541,8 +3084,12 @@ static int sja1105_static_config_reload(struct sja1105_private *priv) static int sja1105_port_probe(struct udevice *dev, int port, struct phy_device *phy) { + struct sja1105_private *priv = dev_get_priv(dev); + ofnode node = dsa_port_get_ofnode(dev, port); phy_interface_t phy_mode = phy->interface; + priv->xpcs_cfg[port].inband_an = ofnode_eth_uses_inband_aneg(node); + if (phy_mode == PHY_INTERFACE_MODE_MII || phy_mode == PHY_INTERFACE_MODE_RMII) { phy->supported &= PHY_BASIC_FEATURES; @@ -2593,6 +3140,13 @@ static int sja1105_port_enable(struct udevice *dev, int port, mii->xmii_mode[port] = XMII_MODE_RGMII; break; + case PHY_INTERFACE_MODE_SGMII: + if (!priv->info->supports_sgmii[port]) + goto unsupported; + + mii->xmii_mode[port] = XMII_MODE_SGMII; + mii->special[port] = true; + break; unsupported: default: dev_err(dev, "Unsupported PHY mode %d on port %d!\n", @@ -2621,7 +3175,10 @@ unsupported: } } - if (phy->speed == SPEED_1000) { + if (mii->xmii_mode[port] == XMII_MODE_SGMII) { + mac[port].speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS]; + priv->xpcs_cfg[port].speed = phy->speed; + } else if (phy->speed == SPEED_1000) { mac[port].speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS]; } else if (phy->speed == SPEED_100) { mac[port].speed = priv->info->port_speed[SJA1105_SPEED_100MBPS]; @@ -2688,7 +3245,18 @@ static int sja1105_init(struct sja1105_private *priv) return rc; } + rc = sja1105_mdiobus_register(priv); + if (rc) { + printf("Failed to register MDIO bus: %d\n", rc); + goto err_mdiobus_register; + } + return 0; + +err_mdiobus_register: + sja1105_static_config_free(&priv->static_config); + + return rc; } static int sja1105_check_device_id(struct sja1105_private *priv) @@ -2777,6 +3345,7 @@ static int sja1105_remove(struct udevice *dev) { struct sja1105_private *priv = dev_get_priv(dev); + sja1105_mdiobus_unregister(priv); sja1105_static_config_free(&priv->static_config); return 0; -- 2.25.1