Received: by 2002:a05:7412:37c9:b0:e2:908c:2ebd with SMTP id jz9csp174604rdb; Mon, 18 Sep 2023 11:23:02 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFe9t55o66xOldaKXrckfebUrLAuwe1Ix26EgFnt5C4vY5xLvrofbqajGeV+xFMJIGJQPlG X-Received: by 2002:a05:6a20:4304:b0:145:6857:457a with SMTP id h4-20020a056a20430400b001456857457amr9878272pzk.4.1695061381961; Mon, 18 Sep 2023 11:23:01 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1695061381; cv=none; d=google.com; s=arc-20160816; b=0DoonPXQQUhVVZCOBvp4KkUjN6/8dKWYPEVqqzktEHcd8lB6Ce81FHiZY7Vli261Ny znQ7Xc5pVYOGEMZDtSYkU1bQ+KhzNaAZR6G1aJ7X5UWk/GuyEPBCFjKj518NNHK450/W jIY66JzCWmVIFsPl3vYcZZfW1IHO6hlkffrjl87IRU+CTY4sYJO8D+kqNPJb6VAQiy8f 4vFTeS2imTPBjUfHa+BPAdvOQsVcNeU58XFvHh/P5OmvN3VV/r/S12VE76PZh1/DjPV9 +TBxQNEX4okaUB3ywsIBfBMGbjUeyTfItf4x6YkjF2E83d66/crWzl1+Reb5DrKh5YdH h3gQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-disposition:mime-version:message-id :subject:to:from:date:dkim-signature; bh=tBHbjn4K2Bp4nW5gae3sR8JhzyMkT/Qdddfoxt0llfs=; fh=lqcUDBguSdRNqtVRNM6IC5SKyIOZsO9QA8dvqnJsPTk=; b=Mj0Ehy7RA1qbxN+ulVGpGX0ijSfQ298RAyppHj8ZoqGqOKIKGVzl4x3nWYhE3uK1Mt 1t423E1RHwX08GpZJe1tM86t/aD9wilfSnsNjLHcKKBXTaFagVb2dKVZ9m7s8axLYF77 WYpNM0/s5Ge8a66thZ2GSdOKpX7v2B3+Q4+0UZcLqiWoQWqtrOHPudlfAgUjVnLgc+fE GcJ5iyuGcbcxhHguRad8Rrg6KtaQnGj8ZP3ogJXb5HlLxzpJ8OZ2M5UfRtChUknx5o/j Vw7IjgbhCvN61fXvjKEHq+Ll++6gq3UVaklpMAJJGvIT5qQQSq516VFtvtSAgncoSJ2t 0Frg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@lopingdog.com header.s=mail header.b=dTYzvi0d; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:7 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=lopingdog.com Return-Path: Received: from snail.vger.email (snail.vger.email. [2620:137:e000::3:7]) by mx.google.com with ESMTPS id x64-20020a638643000000b005776a454f3csi8040637pgd.379.2023.09.18.11.23.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 18 Sep 2023 11:23:01 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:7 as permitted sender) client-ip=2620:137:e000::3:7; Authentication-Results: mx.google.com; dkim=pass header.i=@lopingdog.com header.s=mail header.b=dTYzvi0d; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:7 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=lopingdog.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by snail.vger.email (Postfix) with ESMTP id 070EB812DBE6; Mon, 18 Sep 2023 00:10:19 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at snail.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240051AbjIRHJk (ORCPT + 99 others); Mon, 18 Sep 2023 03:09:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33428 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240066AbjIRHJL (ORCPT ); Mon, 18 Sep 2023 03:09:11 -0400 X-Greylist: delayed 581 seconds by postgrey-1.37 at lindbergh.monkeyblade.net; Mon, 18 Sep 2023 00:08:36 PDT Received: from core.lopingdog.com (core.lopingdog.com [162.55.228.84]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 27145195; Mon, 18 Sep 2023 00:08:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=lopingdog.com; s=mail; t=1695020346; bh=KZNZZngVEeNsDU/AaGdJ0gm65wvWLPiYV2L8ik2igxQ=; h=Date:From:To:Subject:From; b=dTYzvi0d91dWe8L1Ang8fVGsuTDyUKgTHZQW9R7jy3bDcXfbolNiznxUqeLjDid4j +I9DLf6Lgdc7dpyJdgcnUazjkRmBABO3HH68tKD9m4qKW7Crp1I0Pa4NAEFRXtTR2v D1Vy9LwGuT7GX31I3Byc6tGPV/gm9nWQ99IAAhWd+yX0AadMTFmZeWWLCBLE3iVdwi 9bKrQ5WtYDPoZFXaqa9G19OUxf97PxmdBqgYysf/X0NGCCm49M+QBW318zMSC3rBCj THfmsCj4NDaullaEQmkPC020v++CawQawdJaBTcQ08sY4aKaTtI9SjyGruNs5otNYw AVWpKtvKDH2bw== Received: from authenticated-user (core.lopingdog.com [162.55.228.84]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by core.lopingdog.com (Postfix) with ESMTPSA id 30497440E5F; Mon, 18 Sep 2023 01:59:05 -0500 (CDT) Date: Mon, 18 Sep 2023 01:59:03 -0500 From: Jay Monkman To: devicetree@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Piergiorgio Beruto , Arndt Schuebel Subject: [PATCH 3/4] net: phy: Add GPIO and DT support to NCN26000 Message-ID: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED, SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (snail.vger.email [0.0.0.0]); Mon, 18 Sep 2023 00:10:19 -0700 (PDT) This adds GPIO support and devicetree configuration for the NCN26000 PHY. Signed-off-by: Jay Monkman --- drivers/net/phy/ncn26000.c | 467 ++++++++++++++++++++++++++++++++++++- 1 file changed, 459 insertions(+), 8 deletions(-) diff --git a/drivers/net/phy/ncn26000.c b/drivers/net/phy/ncn26000.c index 5680584f659e..d89cbac6f8e3 100644 --- a/drivers/net/phy/ncn26000.c +++ b/drivers/net/phy/ncn26000.c @@ -7,17 +7,32 @@ #include #include #include +#include #include #include #include #include +#include #include "mdio-open-alliance.h" #define PHY_ID_NCN26000 0x180FF5A1 +#define TO_TMR_DEFAULT 32 + #define NCN26000_REG_IRQ_CTL 16 #define NCN26000_REG_IRQ_STATUS 17 +#define NCN26000_REG_DIO_CONFIG 18 + +// clause 45 vendor specific registers +#define NCN26000_REG_PHYCFG1_MMD MDIO_MMD_VEND2 +#define NCN26000_REG_PHYCFG1 0x8001 + +#define NCN26000_REG_PLCAEXT_MMD MDIO_MMD_VEND2 +#define NCN26000_REG_PLCAEXT 0x8002 + +#define NCN26000_REG_TWEAKS_MMD MDIO_MMD_VEND1 +#define NCN26000_REG_TWEAKS 0x1001 // the NCN26000 maps link_ctrl to BMCR_ANENABLE #define NCN26000_BCMR_LINK_CTRL_BIT BMCR_ANENABLE @@ -33,17 +48,392 @@ #define NCN26000_IRQ_PHYSCOL_BIT BIT(5) #define NCN26000_IRQ_RESET_BIT BIT(15) -#define TO_TMR_DEFAULT 32 +#define NCN26000_DIO_FN_DISABLE 0x0 +#define NCN26000_DIO_FN_GPIO 0x1 +#define NCN26000_DIO_FN_SFD_TX 0x2 +#define NCN26000_DIO_FN_SFD_RX 0x3 +#define NCN26000_DIO_FN_LED_LINK 0x4 +#define NCN26000_DIO_FN_LED_PLCA 0x5 +#define NCN26000_DIO_FN_LED_TX 0x6 +#define NCN26000_DIO_FN_LED_RX 0x7 +#define NCN26000_DIO_FN_CLK25M 0x8 +#define NCN26000_DIO_FN_SFD_RXTX 0xb +#define NCN26000_DIO_FN_LED_RXTX 0xf + +#define NCN26000_DIO_CFG_SLEW_RATE1 BIT(15) +#define NCN26000_DIO_CFG_PULL_EN1 BIT(14) +#define NCN26000_DIO_CFG_PULL_DIR1 BIT(13) +#define NCN26000_DIO_CFG_FN1(x) ((x) << 9) +#define NCN26000_DIO_CFG_VAL1 BIT(8) +#define NCN26000_DIO_CFG_SLEW_RATE0 BIT(7) +#define NCN26000_DIO_CFG_PULL_EN0 BIT(6) +#define NCN26000_DIO_CFG_PULL_DIR0 BIT(5) +#define NCN26000_DIO_CFG_FN0(x) ((x) << 1) +#define NCN26000_DIO_CFG_VAL0 BIT(0) + +#define NCN26000_PHYCFG1_PKT_LOOP BIT(15) +#define NCN26000_PHYCFG1_ENI BIT(7) +#define NCN26000_PHYCFG1_UNJAB_TMR BIT(6) +#define NCN26000_PHYCFG1_NO_COL_MASK BIT(1) +#define NCN26000_PHYCFG1_RX_DLY BIT(0) + +#define NCN26000_PLCAEXT_PRECEDENCE BIT(15) +#define NCN26000_PLCAEXT_MIIEXTDIS BIT(11) + +#define NCN26000_TWEAKS_TX_GAIN(x) ((x) << 14) +#define NCN26000_TWEAKS_CD_THRESH(x) ((x) << 10) +#define NCN26000_TWEAKS_ED_THRESH(x) ((x) << 6) +#define NCN26000_TWEAKS_DIG_SLEW BIT(5) +#define NCN26000_TWEAKS_CMC_COMP(x) ((x) << 3) +#define NCN26000_TWEAKS_TX_SLEW BIT(2) + +#define NCN26000_TX_GAIN_1V0 0 +#define NCN26000_TX_GAIN_1V1 1 +#define NCN26000_TX_GAIN_0V9 2 +#define NCN26000_TX_GAIN_0V8 3 +#define NCN26000_CD_THRESH_DEFAULT 11 +#define NCN26000_ED_THRESH_DEFAULT 2 +#define NCN26000_DIG_SLEW_FAST 1 +#define NCN26000_DIG_SLEW_SLOW 0 +#define NCN26000_CMC_COMP_0P25 0 +#define NCN26000_CMC_COMP_1P37 1 +#define NCN26000_CMC_COMP_3P00 2 +#define NCN26000_CMC_COMP_4P37 3 +#define NCN26000_TX_SLEW_FAST 1 +#define NCN26000_TX_SLEW_SLOW 0 + +struct ncn26000_priv { + struct device *dev; + struct phy_device *phydev; + struct gpio_chip gpio_chip; + unsigned int gpiomask; + u16 diocfg; + u16 phycfg0; + u16 plcaext; + u16 tweaks; + + enum { + ENI_AUTO = 0, + ENI_FORCE_ON = 1, + ENI_FORCE_OFF = 2 + } eni_mode; +}; + +static int ncn26000_gpio_request(struct gpio_chip *gc, unsigned int offset) +{ + struct ncn26000_priv *priv = gpiochip_get_data(gc); + + if (offset > 2) + return -ENODEV; + + if (priv->gpiomask & (1 << offset)) + return 0; + + return -EBUSY; +} + +static void ncn26000_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) +{ + struct ncn26000_priv *priv = gpiochip_get_data(gc); + u32 dio_reg; + + dio_reg = phy_read(priv->phydev, NCN26000_REG_DIO_CONFIG); + + switch (offset) { + case 0: + if (!val == !(priv->diocfg & NCN26000_DIO_CFG_VAL0)) + dio_reg |= NCN26000_DIO_CFG_VAL0; + else + dio_reg &= ~NCN26000_DIO_CFG_VAL0; + break; + + case 1: + if (!val == !(priv->diocfg & NCN26000_DIO_CFG_VAL1)) + dio_reg |= NCN26000_DIO_CFG_VAL1; + else + dio_reg &= ~NCN26000_DIO_CFG_VAL1; + break; + + default: + dev_err(priv->dev, "invalid GPIO offset: %d\n", offset); + return; + } + + phy_write(priv->phydev, NCN26000_REG_DIO_CONFIG, dio_reg); +} + +static int ncn26000_gpio_get_dir(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int ncn26000_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, + int val) +{ + ncn26000_gpio_set(gc, offset, val); + return 0; +} + +static int ncn26000_gpio_setup(struct ncn26000_priv *priv) +{ + struct gpio_chip *gc = &priv->gpio_chip; + + gc->request = ncn26000_gpio_request; + gc->get_direction = ncn26000_gpio_get_dir; + gc->direction_output = ncn26000_gpio_dir_out; + gc->set = ncn26000_gpio_set; + gc->label = "ncn26000-gpio"; + gc->base = -1; + gc->ngpio = 2; + gc->parent = priv->dev; + gc->owner = THIS_MODULE; + + return devm_gpiochip_add_data(priv->dev, gc, priv); +} + +static void ncn26000_tweaks_config(struct ncn26000_priv *priv) +{ + u16 val = NCN26000_TWEAKS_CD_THRESH(NCN26000_CD_THRESH_DEFAULT) | + NCN26000_TWEAKS_ED_THRESH(NCN26000_ED_THRESH_DEFAULT) | + NCN26000_DIG_SLEW_FAST; + + struct device *dev = &priv->phydev->mdio.dev; + const char *s; + int ret; + + ret = device_property_read_string(dev, "tx-gain", &s); + if (!ret) { + dev_info(dev, "tx-gain = %s\n", s); + if (strcmp(s, "1v1") == 0) + val |= NCN26000_TWEAKS_TX_GAIN(NCN26000_TX_GAIN_1V1); + else if (strcmp(s, "0v9") == 0) + val |= NCN26000_TWEAKS_TX_GAIN(NCN26000_TX_GAIN_0V9); + else if (strcmp(s, "0v8") == 0) + val |= NCN26000_TWEAKS_TX_GAIN(NCN26000_TX_GAIN_0V8); + else if (strcmp(s, "1v0") != 0) + dev_err(dev, "tx-gain is invalid: %s\n", s); + } + + ret = device_property_read_string(dev, "tx-slew", &s); + if (!ret) { + dev_info(dev, "tx-slew = %s\n", s); + if (strcmp(s, "fast") == 0) + val |= NCN26000_TX_SLEW_FAST; + else if (strcmp(s, "slow") != 0) + dev_err(dev, "tx-slew is invalid: %s\n", s); + } + + ret = device_property_read_string(dev, "dig-slew", &s); + if (!ret) { + dev_info(dev, "dig-slew = %s\n", s); + if (strcmp(s, "slow") == 0) + val &= ~NCN26000_DIG_SLEW_FAST; + else if (strcmp(s, "fast") != 0) + dev_err(dev, "dig-slew is invalid: %s\n", s); + } + + ret = device_property_read_string(dev, "cmc-comp", &s); + if (!ret) { + dev_info(dev, "cmc-comp = %s\n", s); + if (strcmp(s, "1p37") == 0) + val |= NCN26000_TWEAKS_CMC_COMP(NCN26000_CMC_COMP_1P37); + else if (strcmp(s, "3p00") == 0) + val |= NCN26000_TWEAKS_CMC_COMP(NCN26000_CMC_COMP_3P00); + else if (strcmp(s, "4p37") == 0) + val |= NCN26000_TWEAKS_CMC_COMP(NCN26000_CMC_COMP_4P37); + else if (strcmp(s, "0p25") != 0) + dev_err(dev, "cmc-comp is invalid: %s\n", s); + } + + priv->tweaks = val; +} + +static void ncn26000_plcaext_config(struct ncn26000_priv *priv) +{ + u16 val = NCN26000_PLCAEXT_MIIEXTDIS; + + if (device_property_read_bool(priv->dev, "plca-precedence")) { + dev_info(priv->dev, "PLCA precedence mode enabled\n"); + val |= NCN26000_PLCAEXT_PRECEDENCE; + } + + priv->plcaext = val; +} + +static void ncn26000_phycfg0_config(struct ncn26000_priv *priv) +{ + u16 val = NCN26000_PHYCFG1_RX_DLY | NCN26000_PHYCFG1_NO_COL_MASK | + NCN26000_PHYCFG1_UNJAB_TMR; + + struct device *dev = &priv->phydev->mdio.dev; + const char *s; + int ret; + + priv->eni_mode = ENI_AUTO; + ret = device_property_read_string(dev, "eni-mode", &s); + if (!ret) { + dev_info(dev, "eni-mode = %s\n", s); + if (strcmp(s, "force-on") == 0) { + priv->eni_mode = ENI_FORCE_ON; + val |= NCN26000_PHYCFG1_ENI; + } else if (strcmp(s, "force-off") == 0) { + priv->eni_mode = ENI_FORCE_OFF; + } else if (strcmp(s, "auto") != 0) { + dev_err(dev, "eni-mode is invalid: %s\n", s); + } + } + + if (device_property_read_bool(priv->dev, "tx-pkt-loop")) { + dev_info(priv->dev, "TX packet loop enabled\n"); + val |= NCN26000_PHYCFG1_PKT_LOOP; + } + + if (device_property_read_bool(priv->dev, "unjab-tmr-disable")) { + dev_info(priv->dev, "unjab timer disabled\n"); + val &= ~NCN26000_PHYCFG1_UNJAB_TMR; + } + + if (device_property_read_bool(priv->dev, "col-disable")) { + dev_info(priv->dev, "ENI collision detect disabled\n"); + val &= ~NCN26000_PHYCFG1_NO_COL_MASK; + } + + if (device_property_read_bool(priv->dev, "no-rx-delay")) { + dev_info(priv->dev, "RX delay disabled\n"); + val &= ~NCN26000_PHYCFG1_RX_DLY; + } + + priv->phycfg0 = val; +} + +static void ncn26000_dio_config(struct ncn26000_priv *priv) +{ + struct device *dev = &priv->phydev->mdio.dev; + const char *s; + u16 val = 0; + int ret; + + ret = device_property_read_string(dev, "dio0-fn", &s); + if (!ret) { + dev_info(dev, "dio0-fn = %s\n", s); + if (strcmp(s, "sfd-tx") == 0) + val |= NCN26000_DIO_CFG_FN0(NCN26000_DIO_FN_SFD_TX); + else if (strcmp(s, "sfd-rx") == 0) + val |= NCN26000_DIO_CFG_FN0(NCN26000_DIO_FN_SFD_RX); + else if (strcmp(s, "sfd-rxtx") == 0) + val |= NCN26000_DIO_CFG_FN0(NCN26000_DIO_FN_SFD_RXTX); + else if (strcmp(s, "led-link") == 0) + val |= NCN26000_DIO_CFG_FN0(NCN26000_DIO_FN_LED_LINK); + else if (strcmp(s, "led-plca") == 0) + val |= NCN26000_DIO_CFG_FN0(NCN26000_DIO_FN_LED_PLCA); + else if (strcmp(s, "led-tx") == 0) + val |= NCN26000_DIO_CFG_FN0(NCN26000_DIO_FN_LED_TX); + else if (strcmp(s, "led-rx") == 0) + val |= NCN26000_DIO_CFG_FN0(NCN26000_DIO_FN_LED_RX); + else if (strcmp(s, "led-rxtx") == 0) + val |= NCN26000_DIO_CFG_FN0(NCN26000_DIO_FN_LED_RXTX); + else if (strcmp(s, "clk25m") == 0) + val |= NCN26000_DIO_CFG_FN0(NCN26000_DIO_FN_CLK25M); + else + dev_err(dev, "dio0-fn is invalid: %s\n", s); + } + + if (device_property_read_bool(dev, "dio0-pullup")) + val |= NCN26000_DIO_CFG_PULL_EN0; + + if (device_property_read_bool(dev, "dio0-active-high")) + val |= NCN26000_DIO_CFG_VAL0; + + ret = device_property_read_string(dev, "dio0-slew", &s); + if (!ret) { + dev_info(dev, "dio0-slew = %s\n", s); + if (strcmp(s, "slow") == 0) + val |= NCN26000_DIO_CFG_SLEW_RATE0; + else if (strcmp(s, "fast") != 0) + dev_err(dev, "dio0-slew is invalid: %s\n", s); + } + + ret = device_property_read_string(dev, "dio1-fn", &s); + if (!ret) { + dev_info(dev, "dio1-fn = %s\n", s); + if (strcmp(s, "sfd-tx") == 0) + val |= NCN26000_DIO_CFG_FN1(NCN26000_DIO_FN_SFD_TX); + else if (strcmp(s, "sfd-rx") == 0) + val |= NCN26000_DIO_CFG_FN1(NCN26000_DIO_FN_SFD_RX); + else if (strcmp(s, "sfd-rxtx") == 0) + val |= NCN26000_DIO_CFG_FN1(NCN26000_DIO_FN_SFD_RXTX); + else if (strcmp(s, "led-link") == 0) + val |= NCN26000_DIO_CFG_FN1(NCN26000_DIO_FN_LED_LINK); + else if (strcmp(s, "led-plca") == 0) + val |= NCN26000_DIO_CFG_FN1(NCN26000_DIO_FN_LED_PLCA); + else if (strcmp(s, "led-tx") == 0) + val |= NCN26000_DIO_CFG_FN1(NCN26000_DIO_FN_LED_TX); + else if (strcmp(s, "led-rx") == 0) + val |= NCN26000_DIO_CFG_FN1(NCN26000_DIO_FN_LED_RX); + else if (strcmp(s, "led-rxtx") == 0) + val |= NCN26000_DIO_CFG_FN1(NCN26000_DIO_FN_LED_RXTX); + else if (strcmp(s, "clk25m") == 0) + val |= NCN26000_DIO_CFG_FN1(NCN26000_DIO_FN_CLK25M); + else + dev_err(dev, "dio1-fn is invalid: %s\n", s); + } + + if (device_property_read_bool(dev, "dio1-pullup")) + val |= NCN26000_DIO_CFG_PULL_EN1; + + if (device_property_read_bool(dev, "dio1-active-high")) + val |= NCN26000_DIO_CFG_VAL1; + + ret = device_property_read_string(dev, "dio1-slew", &s); + if (!ret) { + dev_info(dev, "dio1-slew = %s\n", s); + if (strcmp(s, "slow") == 0) + val |= NCN26000_DIO_CFG_SLEW_RATE1; + else if (strcmp(s, "fast") != 0) + dev_err(dev, "dio1-slew is invalid: %s\n", s); + } + + priv->diocfg = val; +} static int ncn26000_config_init(struct phy_device *phydev) { + struct ncn26000_priv *priv = phydev->priv; + int ret; + + // configure the DIOx function + ret = phy_write(phydev, NCN26000_REG_DIO_CONFIG, priv->diocfg); + if (ret) + goto out; + + // configure proprietary PHY features + ret = phy_write_mmd(phydev, NCN26000_REG_PHYCFG1_MMD, + NCN26000_REG_PHYCFG1, priv->phycfg0); + if (ret) + goto out; + + // configure the PLCA extensions + ret = phy_write_mmd(phydev, NCN26000_REG_PLCAEXT_MMD, + NCN26000_REG_PLCAEXT, priv->plcaext); + if (ret) + goto out; + + // configure additional PHY tweaks + ret = phy_write_mmd(phydev, NCN26000_REG_TWEAKS_MMD, + NCN26000_REG_TWEAKS, priv->tweaks); + if (ret) + goto out; + /* HW bug workaround: the default value of the PLCA TO_TIMER should be * 32, where the current version of NCN26000 reports 24. This will be * fixed in future PHY versions. For the time being, we force the * correct default here. */ - return phy_write_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_TOTMR, - TO_TMR_DEFAULT); + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_TOTMR, + TO_TMR_DEFAULT); + +out: + return ret; } static int ncn26000_config_aneg(struct phy_device *phydev) @@ -100,6 +490,36 @@ static int ncn26000_read_status(struct phy_device *phydev) return 0; } +static int ncn26000_plca_set_cfg(struct phy_device *phydev, + const struct phy_plca_cfg *plca_cfg) +{ + struct ncn26000_priv *priv = phydev->priv; + int ret; + + /* We capture calls to PLCA set config to intercept PLCA enable/disable + * requests and set the proprietary ENI mode accordingly + */ + ret = genphy_c45_plca_set_cfg(phydev, plca_cfg); + if (ret || plca_cfg->enabled < 0 || priv->eni_mode != ENI_AUTO) + goto out; + + ret = phy_read_mmd(phydev, NCN26000_REG_PHYCFG1_MMD, + NCN26000_REG_PHYCFG1); + + if (ret < 0) + goto out; + + if (plca_cfg->enabled) + ret |= NCN26000_PHYCFG1_ENI; + else + ret &= ~NCN26000_PHYCFG1_ENI; + + ret = phy_write_mmd(phydev, NCN26000_REG_PHYCFG1_MMD, + NCN26000_REG_PHYCFG1, ret); +out: + return ret; +} + static irqreturn_t ncn26000_handle_interrupt(struct phy_device *phydev) { int ret; @@ -140,20 +560,51 @@ static int ncn26000_config_intr(struct phy_device *phydev) return 0; } +static int ncn26000_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct ncn26000_priv *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->phydev = phydev; + phydev->priv = priv; + + ncn26000_dio_config(priv); + if (priv->gpiomask) + ncn26000_gpio_setup(priv); + + ncn26000_phycfg0_config(priv); + ncn26000_plcaext_config(priv); + ncn26000_tweaks_config(priv); + + return 0; +} + +static void ncn26000_remove(struct phy_device *phydev) +{ + kfree(phydev->priv); +} + static struct phy_driver ncn26000_driver[] = { { PHY_ID_MATCH_MODEL(PHY_ID_NCN26000), .name = "NCN26000", .features = PHY_BASIC_T1S_P2MP_FEATURES, - .config_init = ncn26000_config_init, - .config_intr = ncn26000_config_intr, + .probe = ncn26000_probe, + .remove = ncn26000_remove, + .config_init = ncn26000_config_init, + .config_intr = ncn26000_config_intr, .config_aneg = ncn26000_config_aneg, .read_status = ncn26000_read_status, - .handle_interrupt = ncn26000_handle_interrupt, + .handle_interrupt = ncn26000_handle_interrupt, + .set_plca_cfg = ncn26000_plca_set_cfg, .get_plca_cfg = genphy_c45_plca_get_cfg, - .set_plca_cfg = genphy_c45_plca_set_cfg, .get_plca_status = genphy_c45_plca_get_status, - .soft_reset = genphy_soft_reset, + .soft_reset = genphy_soft_reset, }, }; -- 2.40.1