Received: by 2002:a05:6a10:6744:0:0:0:0 with SMTP id w4csp398536pxu; Fri, 23 Oct 2020 03:59:47 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyvxSi1Q4nGvf046Q5ZY+0H9nzq26/StLQ+XJLSI5EIm46e/EhaRyT8NMDeWFk2201uFrY3 X-Received: by 2002:a17:906:1a57:: with SMTP id j23mr1400004ejf.291.1603450786989; Fri, 23 Oct 2020 03:59:46 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1603450786; cv=none; d=google.com; s=arc-20160816; b=XEybBBOftgeblrVubmu8pkIIGpG3e5LmR0usadbztwTxMvtMrO+weOD+kdRwF8B04a 1qnsQE05zeEzxnqeRe3aUBY5SrvEm6arhkypRxDeY9+1nlpNEn1y7Czm5BEk0D892Hgm kAfVSrLjqikQ6NPCQDytWj3viXjnAzCOCQiqnd5xURqPwd6gX2nr2qIFnPHmW8VIafN7 kTYQwAbsfbu6NxvgJUYl7QRaamBaw0EI+4IBirTqyUWcCSaHHE0/9+m7L/jXFcJLO+Oj TQt8cW6/bSJbq7u1UIOiZfMS2aG47oUo/E0hHMUiaezAMMqopU4b6RcrSEc8GFzEKsLZ H88A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=MMSEFS+i2YWOgoTiI1vXxAt9t/sbgoCSBCGkV+3jqt0=; b=AT6T8NLcKtVGBzl1cSG2UVgTf5s7X16a5HfHbgnUt1bbA+eSoi2aWUh5SRpg0IEQaD q3lasuUCDQkUQ388cQwPARq0LE9FRAxfAuFZ18DykR9Nnr/B3JtWOhDnZSL6q5GapzoN 26xbm9KmU0ZBScD+nkFN8RBxUUt4QjM2zWgQdM82aG7SVIxBG5q7OKdGpS9wsnZ6a77Q 4Eub7xhCIrS5OmKN2AzUBbAMkEGbhWIWuwZKAT3De9rnXhTMubm9HtRYvsEdGsjR9236 jtZACwHxYqj58sic4ljwoKktVSKj7polVIoMxScMNg/cmn/7UEubnFKEDrZYDlryEyAh DHTg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id e9si560293ejk.493.2020.10.23.03.59.25; Fri, 23 Oct 2020 03:59:46 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S462656AbgJWK5R (ORCPT + 99 others); Fri, 23 Oct 2020 06:57:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52844 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S462554AbgJWK4i (ORCPT ); Fri, 23 Oct 2020 06:56:38 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 43606C0613CE for ; Fri, 23 Oct 2020 03:56:38 -0700 (PDT) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kVukG-00087o-Kr; Fri, 23 Oct 2020 12:56:28 +0200 Received: from ore by dude.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1kVukF-0001kK-E8; Fri, 23 Oct 2020 12:56:27 +0200 From: Oleksij Rempel To: Andrew Lunn , "David S. Miller" , Florian Fainelli , Heiner Kallweit , Jakub Kicinski , Oliver Hartkopp Cc: Oleksij Rempel , David Jander , kernel@pengutronix.de, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Russell King , mkl@pengutronix.de, Marek Vasut , linux-can@vger.kernel.org Subject: [RFC PATCH v1 2/6] net: phy: add a driver for generic CAN PHYs Date: Fri, 23 Oct 2020 12:56:22 +0200 Message-Id: <20201023105626.6534-3-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20201023105626.6534-1-o.rempel@pengutronix.de> References: <20201023105626.6534-1-o.rempel@pengutronix.de> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-kernel@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Generic CAN PHY driver should provide support for simple CAN PHYs (transceiver). Signed-off-by: Oleksij Rempel --- drivers/net/phy/Kconfig | 6 + drivers/net/phy/Makefile | 1 + drivers/net/phy/can_phy_drv.c | 236 ++++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 drivers/net/phy/can_phy_drv.c diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 39e3f57ea60a..c4ae29c8e9ff 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -153,6 +153,12 @@ config BCM_CYGNUS_PHY config BCM_NET_PHYLIB tristate +config CAN_GENERIC_PHY + tristate "Generic CAN PHY" + depends on CAN_PHY_BUS + help + Enable this driver to support the majority of simple CAN PHYs. + config CAN_PHY_BUS tristate "Virtual CAN PHY Bus" depends on PHYLIB diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 0d76d802c07f..80053cb13dc0 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_BCM87XX_PHY) += bcm87xx.o obj-$(CONFIG_BCM_CYGNUS_PHY) += bcm-cygnus.o obj-$(CONFIG_BCM_NET_PHYLIB) += bcm-phy-lib.o obj-$(CONFIG_BROADCOM_PHY) += broadcom.o +obj-$(CONFIG_CAN_GENERIC_PHY) += can_phy_drv.o obj-$(CONFIG_CAN_PHY_BUS) += can_phy_bus.o obj-$(CONFIG_CICADA_PHY) += cicada.o obj-$(CONFIG_CORTINA_PHY) += cortina.o diff --git a/drivers/net/phy/can_phy_drv.c b/drivers/net/phy/can_phy_drv.c new file mode 100644 index 000000000000..c52bf11fdc03 --- /dev/null +++ b/drivers/net/phy/can_phy_drv.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2020 Oleksij Rempel , Pengutronix + +#include +#include +#include +#include + +/* flags */ +#define CAN_GEN_SIC_XL BIT(5) +#define CAN_GEN_SIC BIT(4) +#define CAN_GEN_FD BIT(3) +#define CAN_GEN_HS BIT(2) +#define CAN_GEN_LS BIT(1) +#define CAN_GEN_SW BIT(0) + +struct can_gen_dcfg { + u32 flags; + u32 max_bitrate; +}; + +struct can_gen_priv { + struct regulator *reg_xceiver; + struct can_gen_dcfg dcfg; + bool off; +}; + +static const struct can_gen_dcfg can_gen_nxp_tja1051 = { + .flags = CAN_GEN_HS | CAN_GEN_FD, + .max_bitrate = 5000000, +}; + +static const struct of_device_id can_gen_ids[] = { + { + .compatible = "can,generic-transceiver", + }, { + .compatible = "nxp,tja1051", + .data = &can_gen_nxp_tja1051, + }, { /* sentinel */ } +}; + +static void can_gen_get_bitrate(struct can_gen_priv *priv, + struct phy_device *phydev) +{ + struct device_node *np = phydev->mdio.dev.of_node; + struct can_gen_dcfg *dcfg = &priv->dcfg; + u32 max_bitrate; + int ret; + + ret = of_property_read_u32(np, "max-bitrate", &max_bitrate); + if (ret) + phydev_warn(phydev, "Can't read max-bitrate\n"); + + if (!dcfg->max_bitrate && !max_bitrate) { + phydev_warn(phydev, "Huh... Limitless PHY!!\n"); + + if (dcfg->flags & CAN_GEN_FD) { + max_bitrate = 12000000; /* 12Mbit */ + } else if (dcfg->flags & CAN_GEN_HS) { + max_bitrate = 1000000; /* 1Mbit */ + } else if (dcfg->flags & CAN_GEN_LS) { + max_bitrate = 125000; /* 125Kbit */ + } else if (dcfg->flags & CAN_GEN_SW) { + max_bitrate = 83300; /* 83.3kbit */ + } else { + max_bitrate = 12000000; /* 12Mbit */ + phydev_warn(phydev, "Can't determine the max bitrate! Set: %d\n", + max_bitrate); + } + + dcfg->max_bitrate = max_bitrate; + } else if (dcfg->max_bitrate && max_bitrate > dcfg->max_bitrate) { + phydev_info(phydev, "Ignoring max-bitrate property: %d, hw limit: %d\n", + max_bitrate, dcfg->max_bitrate); + } else { + dcfg->max_bitrate = max_bitrate; + } +} + +static void can_gen_parse_dt_flags(struct can_gen_priv *priv, + struct phy_device *phydev) +{ + struct can_gen_dcfg *dcfg = &priv->dcfg; + struct device *dev = &phydev->mdio.dev; + + if (device_property_read_bool(dev, "can-sic-xl")) + dcfg->flags |= CAN_GEN_SIC_XL; + if (device_property_read_bool(dev, "can-sic")) + dcfg->flags |= CAN_GEN_SIC; + if (device_property_read_bool(dev, "can-fd")) + dcfg->flags |= CAN_GEN_FD; + if (device_property_read_bool(dev, "can-hs")) + dcfg->flags |= CAN_GEN_HS; + if (device_property_read_bool(dev, "can-ls")) + dcfg->flags |= CAN_GEN_LS; + if (device_property_read_bool(dev, "can-sw")) + dcfg->flags |= CAN_GEN_SW; +} + +static int can_gen_probe(struct phy_device *phydev) +{ + struct device_node *np = phydev->mdio.dev.of_node; + const struct of_device_id *match; + struct regulator *reg_xceiver; + struct can_gen_priv *priv; + + match = of_match_node(can_gen_ids, np); + if (!match) + return 0; + + reg_xceiver = devm_regulator_get_optional(&phydev->mdio.dev, "xceiver"); + if (PTR_ERR(reg_xceiver) == -ENODEV) + reg_xceiver = NULL; + else if (IS_ERR(reg_xceiver)) + return dev_err_probe(&phydev->mdio.dev, PTR_ERR(reg_xceiver), + "Failed to get Transceiver regulator!\n"); + + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (!match->data) + can_gen_parse_dt_flags(priv, phydev); + else + memcpy(&priv->dcfg, match->data, sizeof(priv->dcfg)); + + can_gen_get_bitrate(priv, phydev); + + phydev->priv = priv; + priv->reg_xceiver = reg_xceiver; + priv->off = true; + + return 0; +} + +static int can_gen_read_status(struct phy_device *phydev) +{ + phydev->link = true; + phydev->duplex = DUPLEX_HALF; + + return 0; +} + +static int can_gen_match_phy_device(struct phy_device *phydev) +{ + struct device_node *np = phydev->mdio.dev.of_node; + const struct of_device_id *match; + + if (phydev->phy_id) + return 0; + + match = of_match_node(can_gen_ids, np); + if (!match) + return 0; + + return 1; +} + +static int can_get_features(struct phy_device *phydev) +{ + struct can_gen_priv *priv = phydev->priv; + struct can_gen_dcfg *dcfg = &priv->dcfg; + + phydev->interface = PHY_INTERFACE_MODE_CAN; + phydev->max_bitrate = dcfg->max_bitrate; + + if (dcfg->flags & CAN_GEN_SIC_XL) + linkmode_set_bit(ETHTOOL_LINK_MODE_CAN_SIC_XL_BIT, + phydev->supported); + if (dcfg->flags & CAN_GEN_SIC) + linkmode_set_bit(ETHTOOL_LINK_MODE_CAN_SIC_BIT, + phydev->supported); + if (dcfg->flags & CAN_GEN_FD) + linkmode_set_bit(ETHTOOL_LINK_MODE_CAN_FD_BIT, + phydev->supported); + if (dcfg->flags & CAN_GEN_HS) + linkmode_set_bit(ETHTOOL_LINK_MODE_CAN_HS_BIT, + phydev->supported); + if (dcfg->flags & CAN_GEN_LS) + linkmode_set_bit(ETHTOOL_LINK_MODE_CAN_LS_BIT, + phydev->supported); + if (dcfg->flags & CAN_GEN_SW) + linkmode_set_bit(ETHTOOL_LINK_MODE_CAN_SW_BIT, + phydev->supported); + + return 0; +} + +static int can_gen_config_aneg(struct phy_device *phydev) +{ + return 0; +} + +static int can_gen_resume(struct phy_device *phydev) +{ + struct can_gen_priv *priv = phydev->priv; + + /* Resume can be called multiple times, so it will be asymmetric to + * the suspend call.*/ + if (!priv->off) + return 0; + + priv->off = false; + + return regulator_enable(priv->reg_xceiver); +} + +static int can_gen_suspend(struct phy_device *phydev) +{ + struct can_gen_priv *priv = phydev->priv; + + if (priv->off) + return 0; + + priv->off = true; + + return regulator_disable(priv->reg_xceiver); +} + +static struct phy_driver can_gen_drv[] = { + { + .name = "Generic CAN PHY", + .match_phy_device = can_gen_match_phy_device, + .probe = can_gen_probe, + .read_status = can_gen_read_status, + .suspend = can_gen_suspend, + .resume = can_gen_resume, + .get_features = can_get_features, + .config_aneg = can_gen_config_aneg, + }, +}; +module_phy_driver(can_gen_drv); + +MODULE_DESCRIPTION("Generic CAN PHY driver"); +MODULE_AUTHOR("Oleksij Rempel"); +MODULE_LICENSE("GPLv2"); -- 2.28.0