You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
327 lines
8.0 KiB
327 lines
8.0 KiB
/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/of_gpio.h>
|
|
|
|
#include "stmmac.h"
|
|
#include "dwmac-qcom-ethqos.h"
|
|
|
|
#define EMAC_GDSC_EMAC_NAME "gdsc_emac"
|
|
#define EMAC_VREG_RGMII_NAME "vreg_rgmii"
|
|
#define EMAC_VREG_EMAC_PHY_NAME "vreg_emac_phy"
|
|
#define EMAC_VREG_RGMII_IO_PADS_NAME "vreg_rgmii_io_pads"
|
|
#define EMAC_PIN_PPS0 "dev-emac_pin_pps_0"
|
|
|
|
|
|
static int setup_gpio_input_common
|
|
(struct device *dev, const char *name, int *gpio)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (of_find_property(dev->of_node, name, NULL)) {
|
|
*gpio = ret = of_get_named_gpio(dev->of_node, name, 0);
|
|
if (ret >= 0) {
|
|
ret = gpio_request(*gpio, name);
|
|
if (ret) {
|
|
ETHQOSERR("%s: Can't get GPIO %s, ret = %d\n",
|
|
name, *gpio);
|
|
*gpio = -1;
|
|
return ret;
|
|
}
|
|
|
|
ret = gpio_direction_input(*gpio);
|
|
if (ret) {
|
|
ETHQOSERR(
|
|
"%s: Can't set GPIO %s direction, ret = %d\n",
|
|
name, ret);
|
|
return ret;
|
|
}
|
|
} else {
|
|
if (ret == -EPROBE_DEFER)
|
|
ETHQOSERR("get EMAC_GPIO probe defer\n");
|
|
else
|
|
ETHQOSERR("can't get gpio %s ret %d\n", name,
|
|
ret);
|
|
return ret;
|
|
}
|
|
} else {
|
|
ETHQOSERR("can't find gpio %s\n", name);
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ethqos_init_reqgulators(struct qcom_ethqos *ethqos)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (of_property_read_bool(ethqos->pdev->dev.of_node,
|
|
"gdsc_emac-supply")) {
|
|
ethqos->gdsc_emac =
|
|
devm_regulator_get(ðqos->pdev->dev, EMAC_GDSC_EMAC_NAME);
|
|
if (IS_ERR(ethqos->gdsc_emac)) {
|
|
ETHQOSERR("Can not get <%s>\n", EMAC_GDSC_EMAC_NAME);
|
|
return PTR_ERR(ethqos->gdsc_emac);
|
|
}
|
|
|
|
ret = regulator_enable(ethqos->gdsc_emac);
|
|
if (ret) {
|
|
ETHQOSERR("Can not enable <%s>\n", EMAC_GDSC_EMAC_NAME);
|
|
goto reg_error;
|
|
}
|
|
|
|
ETHQOSDBG("Enabled <%s>\n", EMAC_GDSC_EMAC_NAME);
|
|
}
|
|
|
|
if (of_property_read_bool(ethqos->pdev->dev.of_node,
|
|
"vreg_emac_phy-supply")) {
|
|
ethqos->reg_emac_phy =
|
|
devm_regulator_get(ðqos->pdev->dev, EMAC_VREG_EMAC_PHY_NAME);
|
|
if (IS_ERR(ethqos->reg_emac_phy)) {
|
|
ETHQOSERR("Can not get <%s>\n",
|
|
EMAC_VREG_EMAC_PHY_NAME);
|
|
return PTR_ERR(ethqos->reg_emac_phy);
|
|
}
|
|
|
|
ret = regulator_enable(ethqos->reg_emac_phy);
|
|
if (ret) {
|
|
ETHQOSERR("Can not enable <%s>\n",
|
|
EMAC_VREG_EMAC_PHY_NAME);
|
|
goto reg_error;
|
|
}
|
|
|
|
ETHQOSDBG("Enabled <%s>\n", EMAC_VREG_EMAC_PHY_NAME);
|
|
}
|
|
|
|
if (of_property_read_bool(ethqos->pdev->dev.of_node,
|
|
"vreg_rgmii_io_pads-supply")) {
|
|
ethqos->reg_rgmii_io_pads = devm_regulator_get
|
|
(ðqos->pdev->dev, EMAC_VREG_RGMII_IO_PADS_NAME);
|
|
if (IS_ERR(ethqos->reg_rgmii_io_pads)) {
|
|
ETHQOSERR("Can not get <%s>\n",
|
|
EMAC_VREG_RGMII_IO_PADS_NAME);
|
|
return PTR_ERR(ethqos->reg_rgmii_io_pads);
|
|
}
|
|
|
|
ret = regulator_enable(ethqos->reg_rgmii_io_pads);
|
|
if (ret) {
|
|
ETHQOSERR("Can not enable <%s>\n",
|
|
EMAC_VREG_RGMII_IO_PADS_NAME);
|
|
goto reg_error;
|
|
}
|
|
|
|
ETHQOSDBG("Enabled <%s>\n", EMAC_VREG_RGMII_IO_PADS_NAME);
|
|
}
|
|
|
|
if (of_property_read_bool(ethqos->pdev->dev.of_node,
|
|
"vreg_rgmii-supply") && (2500000 ==
|
|
regulator_get_voltage(ethqos->reg_rgmii_io_pads))) {
|
|
ethqos->reg_rgmii =
|
|
devm_regulator_get(ðqos->pdev->dev, EMAC_VREG_RGMII_NAME);
|
|
if (IS_ERR(ethqos->reg_rgmii)) {
|
|
ETHQOSERR("Can not get <%s>\n", EMAC_VREG_RGMII_NAME);
|
|
return PTR_ERR(ethqos->reg_rgmii);
|
|
}
|
|
|
|
ret = regulator_enable(ethqos->reg_rgmii);
|
|
if (ret) {
|
|
ETHQOSERR("Can not enable <%s>\n",
|
|
EMAC_VREG_RGMII_NAME);
|
|
goto reg_error;
|
|
}
|
|
|
|
ETHQOSDBG("Enabled <%s>\n", EMAC_VREG_RGMII_NAME);
|
|
}
|
|
|
|
return ret;
|
|
|
|
reg_error:
|
|
ETHQOSERR("%s failed\n", __func__);
|
|
ethqos_disable_regulators(ethqos);
|
|
return ret;
|
|
}
|
|
|
|
void ethqos_disable_regulators(struct qcom_ethqos *ethqos)
|
|
{
|
|
if (ethqos->reg_rgmii) {
|
|
regulator_disable(ethqos->reg_rgmii);
|
|
devm_regulator_put(ethqos->reg_rgmii);
|
|
ethqos->reg_rgmii = NULL;
|
|
}
|
|
|
|
if (ethqos->reg_emac_phy) {
|
|
regulator_disable(ethqos->reg_emac_phy);
|
|
devm_regulator_put(ethqos->reg_emac_phy);
|
|
ethqos->reg_emac_phy = NULL;
|
|
}
|
|
|
|
if (ethqos->reg_rgmii_io_pads) {
|
|
regulator_disable(ethqos->reg_rgmii_io_pads);
|
|
devm_regulator_put(ethqos->reg_rgmii_io_pads);
|
|
ethqos->reg_rgmii_io_pads = NULL;
|
|
}
|
|
|
|
if (ethqos->gdsc_emac) {
|
|
regulator_disable(ethqos->gdsc_emac);
|
|
devm_regulator_put(ethqos->gdsc_emac);
|
|
ethqos->gdsc_emac = NULL;
|
|
}
|
|
}
|
|
|
|
void ethqos_reset_phy_enable_interrupt(struct qcom_ethqos *ethqos)
|
|
{
|
|
struct stmmac_priv *priv = qcom_ethqos_get_priv(ethqos);
|
|
struct phy_device *phydev = priv->dev->phydev;
|
|
|
|
/* reset the phy so that it's ready */
|
|
if (priv->mii) {
|
|
ETHQOSERR("do mdio reset\n");
|
|
stmmac_mdio_reset(priv->mii);
|
|
}
|
|
/*Enable phy interrupt*/
|
|
if (phy_intr_en && phydev) {
|
|
ETHQOSDBG("PHY interrupt Mode enabled\n");
|
|
phydev->irq = PHY_IGNORE_INTERRUPT;
|
|
phydev->interrupts = PHY_INTERRUPT_ENABLED;
|
|
|
|
if (phydev->drv->config_intr &&
|
|
!phydev->drv->config_intr(phydev)) {
|
|
ETHQOSERR("config_phy_intr successful after phy on\n");
|
|
}
|
|
qcom_ethqos_request_phy_wol(priv->plat);
|
|
} else if (!phy_intr_en) {
|
|
phydev->irq = PHY_POLL;
|
|
ETHQOSDBG("PHY Polling Mode enabled\n");
|
|
} else {
|
|
ETHQOSERR("phydev is null , intr value=%d\n", phy_intr_en);
|
|
}
|
|
}
|
|
|
|
int ethqos_phy_power_on(struct qcom_ethqos *ethqos)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (ethqos->reg_emac_phy) {
|
|
ret = regulator_enable(ethqos->reg_emac_phy);
|
|
if (ret) {
|
|
ETHQOSERR("Can not enable <%s>\n",
|
|
EMAC_VREG_EMAC_PHY_NAME);
|
|
return ret;
|
|
}
|
|
ethqos->phy_state = PHY_IS_ON;
|
|
} else {
|
|
ETHQOSERR("reg_emac_phy is NULL\n");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void ethqos_phy_power_off(struct qcom_ethqos *ethqos)
|
|
{
|
|
if (ethqos->reg_emac_phy) {
|
|
regulator_disable(ethqos->reg_emac_phy);
|
|
ethqos->phy_state = PHY_IS_OFF;
|
|
} else {
|
|
ETHQOSERR("reg_emac_phy is NULL\n");
|
|
}
|
|
}
|
|
|
|
void ethqos_free_gpios(struct qcom_ethqos *ethqos)
|
|
{
|
|
if (gpio_is_valid(ethqos->gpio_phy_intr_redirect))
|
|
gpio_free(ethqos->gpio_phy_intr_redirect);
|
|
ethqos->gpio_phy_intr_redirect = -1;
|
|
}
|
|
|
|
int ethqos_init_pinctrl(struct device *dev)
|
|
{
|
|
struct pinctrl *pinctrl;
|
|
struct pinctrl_state *pinctrl_state;
|
|
int i = 0;
|
|
int num_names;
|
|
const char *name;
|
|
int ret = 0;
|
|
|
|
pinctrl = devm_pinctrl_get(dev);
|
|
if (IS_ERR_OR_NULL(pinctrl)) {
|
|
ret = PTR_ERR(pinctrl);
|
|
ETHQOSERR("Failed to get pinctrl, err = %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
num_names = of_property_count_strings(dev->of_node, "pinctrl-names");
|
|
if (num_names < 0) {
|
|
dev_err(dev, "Cannot parse pinctrl-names: %d\n", num_names);
|
|
return num_names;
|
|
}
|
|
|
|
for (i = 0; i < num_names; i++) {
|
|
ret = of_property_read_string_index(
|
|
dev->of_node, "pinctrl-names", i, &name);
|
|
|
|
if (!strcmp(name, PINCTRL_STATE_DEFAULT))
|
|
continue;
|
|
|
|
pinctrl_state = pinctrl_lookup_state(pinctrl, name);
|
|
if (IS_ERR_OR_NULL(pinctrl_state)) {
|
|
ret = PTR_ERR(pinctrl_state);
|
|
ETHQOSERR("lookup_state %s failed %d\n", name, ret);
|
|
return ret;
|
|
}
|
|
|
|
ETHQOSDBG("pinctrl_lookup_state %s succeded\n", name);
|
|
|
|
ret = pinctrl_select_state(pinctrl, pinctrl_state);
|
|
if (ret) {
|
|
ETHQOSERR("select_state %s failed %d\n", name, ret);
|
|
return ret;
|
|
}
|
|
|
|
ETHQOSDBG("pinctrl_select_state %s succeded\n", name);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ethqos_init_gpio(struct qcom_ethqos *ethqos)
|
|
{
|
|
int ret = 0;
|
|
|
|
ethqos->gpio_phy_intr_redirect = -1;
|
|
|
|
ret = ethqos_init_pinctrl(ðqos->pdev->dev);
|
|
if (ret) {
|
|
ETHQOSERR("ethqos_init_pinctrl failed");
|
|
return ret;
|
|
}
|
|
|
|
ret = setup_gpio_input_common(
|
|
ðqos->pdev->dev, "qcom,phy-intr-redirect",
|
|
ðqos->gpio_phy_intr_redirect);
|
|
|
|
if (ret) {
|
|
ETHQOSERR("Failed to setup <%s> gpio\n",
|
|
"qcom,phy-intr-redirect");
|
|
goto gpio_error;
|
|
}
|
|
|
|
return ret;
|
|
|
|
gpio_error:
|
|
ethqos_free_gpios(ethqos);
|
|
return ret;
|
|
}
|
|
|