/* * Qualcomm Atheros IPQ806x GMAC glue layer * * Copyright (C) 2015 The Linux Foundation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
staticint get_clk_div_sgmii(struct ipq806x_gmac *gmac, int speed)
{ struct device *dev = &gmac->pdev->dev; int div;
switch (speed) { case SPEED_1000:
div = NSS_COMMON_CLK_DIV_SGMII_1000; break;
case SPEED_100:
div = NSS_COMMON_CLK_DIV_SGMII_100; break;
case SPEED_10:
div = NSS_COMMON_CLK_DIV_SGMII_10; break;
default:
dev_err(dev, "Speed %dMbps not supported in SGMII\n", speed); return -EINVAL;
}
return div;
}
staticint get_clk_div_rgmii(struct ipq806x_gmac *gmac, int speed)
{ struct device *dev = &gmac->pdev->dev; int div;
switch (speed) { case SPEED_1000:
div = NSS_COMMON_CLK_DIV_RGMII_1000; break;
case SPEED_100:
div = NSS_COMMON_CLK_DIV_RGMII_100; break;
case SPEED_10:
div = NSS_COMMON_CLK_DIV_RGMII_10; break;
default:
dev_err(dev, "Speed %dMbps not supported in RGMII\n", speed); return -EINVAL;
}
return div;
}
staticint ipq806x_gmac_set_speed(struct ipq806x_gmac *gmac, int speed)
{
uint32_t clk_bits, val; int div;
switch (gmac->phy_mode) { case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID:
div = get_clk_div_rgmii(gmac, speed);
clk_bits = NSS_COMMON_CLK_GATE_RGMII_RX_EN(gmac->id) |
NSS_COMMON_CLK_GATE_RGMII_TX_EN(gmac->id); break;
case PHY_INTERFACE_MODE_SGMII:
div = get_clk_div_sgmii(gmac, speed);
clk_bits = NSS_COMMON_CLK_GATE_GMII_RX_EN(gmac->id) |
NSS_COMMON_CLK_GATE_GMII_TX_EN(gmac->id); break;
/* Disable the clocks */
regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val);
val &= ~clk_bits;
regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val);
/* Set the divider */
regmap_read(gmac->nss_common, NSS_COMMON_CLK_DIV0, &val);
val &= ~(NSS_COMMON_CLK_DIV_MASK
<< NSS_COMMON_CLK_DIV_OFFSET(gmac->id));
val |= div << NSS_COMMON_CLK_DIV_OFFSET(gmac->id);
regmap_write(gmac->nss_common, NSS_COMMON_CLK_DIV0, val);
/* Enable the clock back */
regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val);
val |= clk_bits;
regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val);
if (of_property_read_u32(dev->of_node, "qcom,id", &gmac->id) < 0) {
dev_err(dev, "missing qcom id property\n"); return -EINVAL;
}
/* The GMACs are called 1 to 4 in the documentation, but to simplify the * code and keep it consistent with the Linux convention, we'll number * them from 0 to 3 here.
*/ if (gmac->id > 3) {
dev_err(dev, "invalid gmac id\n"); return -EINVAL;
}
/* Setup the register map for the nss common registers */
gmac->nss_common = syscon_regmap_lookup_by_phandle(dev->of_node, "qcom,nss-common"); if (IS_ERR(gmac->nss_common)) {
dev_err(dev, "missing nss-common node\n"); return PTR_ERR(gmac->nss_common);
}
/* Setup the register map for the qsgmii csr registers */
gmac->qsgmii_csr = syscon_regmap_lookup_by_phandle(dev->of_node, "qcom,qsgmii-csr"); if (IS_ERR(gmac->qsgmii_csr))
dev_err(dev, "missing qsgmii-csr node\n");
staticint
ipq806x_gmac_configure_qsgmii_pcs_speed(struct ipq806x_gmac *gmac)
{ struct platform_device *pdev = gmac->pdev; struct device *dev = &pdev->dev; struct device_node *dn; int link_speed; int val = 0; int ret;
/* Some bootloader may apply wrong configuration and cause * not functioning port. If fixed link is not set, * reset the force speed bit.
*/ if (!of_phy_is_fixed_link(pdev->dev.of_node)) goto write;
dn = of_get_child_by_name(pdev->dev.of_node, "fixed-link");
ret = of_property_read_u32(dn, "speed", &link_speed);
of_node_put(dn); if (ret) {
dev_err(dev, "found fixed-link node with no speed"); return ret;
}
val = QSGMII_PCS_CH_SPEED_FORCE;
switch (link_speed) { case SPEED_1000:
val |= QSGMII_PCS_CH_SPEED_1000; break; case SPEED_100:
val |= QSGMII_PCS_CH_SPEED_100; break; case SPEED_10:
val |= QSGMII_PCS_CH_SPEED_10; break;
}
write:
regmap_update_bits(gmac->qsgmii_csr, QSGMII_PCS_ALL_CH_CTL,
QSGMII_PCS_CH_SPEED_MASK <<
QSGMII_PCS_CH_SPEED_SHIFT(gmac->id),
val <<
QSGMII_PCS_CH_SPEED_SHIFT(gmac->id));
qsgmii_param |= QSGMII_PHY_RX_DC_BIAS(0x2); break; case 2: case 3:
qsgmii_param = QSGMII_PHY_RX_DC_BIAS(0x3) |
QSGMII_PHY_TX_DRV_AMP(0xc); break; default: /* gmac 0 can't be set in SGMII mode */
dev_err(dev, "gmac id %d can't be in SGMII mode", gmac->id); return -EINVAL;
}
/* Common params across all gmac id */
qsgmii_param |= QSGMII_PHY_CDR_EN |
QSGMII_PHY_RX_FRONT_EN |
QSGMII_PHY_RX_SIGNAL_DETECT_EN |
QSGMII_PHY_TX_DRIVER_EN |
QSGMII_PHY_QSGMII_EN |
QSGMII_PHY_PHASE_LOOP_GAIN(0x4) |
QSGMII_PHY_RX_INPUT_EQU(0x1) |
QSGMII_PHY_CDR_PI_SLEW(0x2);
/* Inter frame gap is set to 12 */
val = 12 << NSS_COMMON_GMAC_CTL_IFG_OFFSET |
12 << NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET; /* We also initiate an AXI low power exit request */
val |= NSS_COMMON_GMAC_CTL_CSYS_REQ; switch (gmac->phy_mode) { case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID:
val |= NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL; break; case PHY_INTERFACE_MODE_SGMII:
val &= ~NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL; break; default: goto err_unsupported_phy;
}
regmap_write(gmac->nss_common, NSS_COMMON_GMAC_CTL(gmac->id), val);
/* Configure the clock src according to the mode */
regmap_read(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, &val);
val &= ~(1 << NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id)); switch (gmac->phy_mode) { case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID:
val |= NSS_COMMON_CLK_SRC_CTRL_RGMII(gmac->id) <<
NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id); break; case PHY_INTERFACE_MODE_SGMII:
val |= NSS_COMMON_CLK_SRC_CTRL_SGMII(gmac->id) <<
NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id); break; default: goto err_unsupported_phy;
}
regmap_write(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, val);
/* Enable PTP clock */
regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val);
val |= NSS_COMMON_CLK_GATE_PTP_EN(gmac->id); switch (gmac->phy_mode) { case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID:
val |= NSS_COMMON_CLK_GATE_RGMII_RX_EN(gmac->id) |
NSS_COMMON_CLK_GATE_RGMII_TX_EN(gmac->id); break; case PHY_INTERFACE_MODE_SGMII:
val |= NSS_COMMON_CLK_GATE_GMII_RX_EN(gmac->id) |
NSS_COMMON_CLK_GATE_GMII_TX_EN(gmac->id); break; default: goto err_unsupported_phy;
}
regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val);
if (gmac->phy_mode == PHY_INTERFACE_MODE_SGMII) {
err = ipq806x_gmac_configure_qsgmii_params(gmac); if (err) return err;
err = ipq806x_gmac_configure_qsgmii_pcs_speed(gmac); if (err) return err;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.