mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 16:44:58 +02:00
Merge branch 'net-ethernet-mtk_eth_soc-various-enhancements'
Daniel Golle says: ==================== net: ethernet: mtk_eth_soc: various enhancements This series brings a variety of fixes and enhancements for mtk_eth_soc, adds support for the MT7981 SoC and facilitates sharing the SGMII PCS code between mtk_eth_soc and mt7530. The whole series has been tested on MT7622+MT7531 (BPi-R64), MT7623+MT7530 (BPi-R2), MT7981+GPY211 (GL.iNet GL-MT3000) and MT7986+MT7531 (BPi-R3). On the BananaPi R3 a variete of SFP modules have been tested, all of them (some SGMII with PHY, others 2500Base-X or 1000Base-X without PHY) are working well now, however, some of them need manually disabling of autonegotiation for the link to come up. ==================== Link: https://lore.kernel.org/r/cover.1679230025.git.daniel@makrotopia.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
134d836861
|
|
@ -1,27 +0,0 @@
|
|||
MediaTek SGMIISYS controller
|
||||
============================
|
||||
|
||||
The MediaTek SGMIISYS controller provides various clocks to the system.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Should be:
|
||||
- "mediatek,mt7622-sgmiisys", "syscon"
|
||||
- "mediatek,mt7629-sgmiisys", "syscon"
|
||||
- "mediatek,mt7981-sgmiisys_0", "syscon"
|
||||
- "mediatek,mt7981-sgmiisys_1", "syscon"
|
||||
- "mediatek,mt7986-sgmiisys_0", "syscon"
|
||||
- "mediatek,mt7986-sgmiisys_1", "syscon"
|
||||
- #clock-cells: Must be 1
|
||||
|
||||
The SGMIISYS controller uses the common clk binding from
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
The available clocks are defined in dt-bindings/clock/mt*-clk.h.
|
||||
|
||||
Example:
|
||||
|
||||
sgmiisys: sgmiisys@1b128000 {
|
||||
compatible = "mediatek,mt7622-sgmiisys", "syscon";
|
||||
reg = <0 0x1b128000 0 0x1000>;
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
|
|
@ -21,6 +21,7 @@ properties:
|
|||
- mediatek,mt7623-eth
|
||||
- mediatek,mt7622-eth
|
||||
- mediatek,mt7629-eth
|
||||
- mediatek,mt7981-eth
|
||||
- mediatek,mt7986-eth
|
||||
- ralink,rt5350-eth
|
||||
|
||||
|
|
@ -78,6 +79,11 @@ properties:
|
|||
description:
|
||||
List of phandles to wireless ethernet dispatch nodes.
|
||||
|
||||
mediatek,wed-pcie:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
Phandle to the mediatek wed-pcie controller.
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
mdio-bus:
|
||||
|
|
@ -123,6 +129,8 @@ allOf:
|
|||
|
||||
mediatek,wed: false
|
||||
|
||||
mediatek,wed-pcie: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
|
@ -160,6 +168,8 @@ allOf:
|
|||
description:
|
||||
Phandle to the mediatek pcie-mirror controller.
|
||||
|
||||
mediatek,wed-pcie: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
|
@ -206,6 +216,44 @@ allOf:
|
|||
|
||||
mediatek,wed: false
|
||||
|
||||
mediatek,wed-pcie: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: mediatek,mt7981-eth
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 4
|
||||
|
||||
clocks:
|
||||
minItems: 15
|
||||
maxItems: 15
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: fe
|
||||
- const: gp2
|
||||
- const: gp1
|
||||
- const: wocpu0
|
||||
- const: sgmii_ck
|
||||
- const: sgmii_tx250m
|
||||
- const: sgmii_rx250m
|
||||
- const: sgmii_cdr_ref
|
||||
- const: sgmii_cdr_fb
|
||||
- const: sgmii2_tx250m
|
||||
- const: sgmii2_rx250m
|
||||
- const: sgmii2_cdr_ref
|
||||
- const: sgmii2_cdr_fb
|
||||
- const: netsys0
|
||||
- const: netsys1
|
||||
|
||||
mediatek,sgmiisys:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
|
@ -242,11 +290,6 @@ allOf:
|
|||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
mediatek,wed-pcie:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
Phandle to the mediatek wed-pcie controller.
|
||||
|
||||
patternProperties:
|
||||
"^mac@[0-1]$":
|
||||
type: object
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/pcs/mediatek,sgmiisys.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MediaTek SGMIISYS Controller
|
||||
|
||||
maintainers:
|
||||
- Matthias Brugger <matthias.bgg@gmail.com>
|
||||
|
||||
description:
|
||||
The MediaTek SGMIISYS controller provides a SGMII PCS and some clocks
|
||||
to the ethernet subsystem to which it is attached.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- mediatek,mt7622-sgmiisys
|
||||
- mediatek,mt7629-sgmiisys
|
||||
- mediatek,mt7981-sgmiisys_0
|
||||
- mediatek,mt7981-sgmiisys_1
|
||||
- mediatek,mt7986-sgmiisys_0
|
||||
- mediatek,mt7986-sgmiisys_1
|
||||
- const: syscon
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
'#clock-cells':
|
||||
const: 1
|
||||
|
||||
mediatek,pnswap:
|
||||
description: Invert polarity of the SGMII data lanes
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- '#clock-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
sgmiisys: syscon@1b128000 {
|
||||
compatible = "mediatek,mt7622-sgmiisys", "syscon";
|
||||
reg = <0 0x1b128000 0 0x1000>;
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
|
@ -13042,6 +13042,14 @@ L: netdev@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/net/ethernet/mediatek/
|
||||
|
||||
MEDIATEK ETHERNET PCS DRIVER
|
||||
M: Alexander Couzens <lynxis@fe80.eu>
|
||||
M: Daniel Golle <daniel@makrotopia.org>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/pcs/pcs-mtk-lynxi.c
|
||||
F: include/linux/pcs/pcs-mtk-lynxi.h
|
||||
|
||||
MEDIATEK I2C CONTROLLER DRIVER
|
||||
M: Qii Wang <qii.wang@mediatek.com>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ config NET_DSA_MT7530
|
|||
tristate "MediaTek MT7530 and MT7531 Ethernet switch support"
|
||||
select NET_DSA_TAG_MTK
|
||||
select MEDIATEK_GE_PHY
|
||||
select PCS_MTK_LYNXI
|
||||
help
|
||||
This enables support for the MediaTek MT7530 and MT7531 Ethernet
|
||||
switch chips. Multi-chip module MT7530 in MT7621AT, MT7621DAT,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/of_mdio.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pcs/pcs-mtk-lynxi.h>
|
||||
#include <linux/phylink.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
|
@ -2572,128 +2573,11 @@ static int mt7531_rgmii_setup(struct mt7530_priv *priv, u32 port,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mt7531_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
phy_interface_t interface, int speed, int duplex)
|
||||
{
|
||||
struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
|
||||
int port = pcs_to_mt753x_pcs(pcs)->port;
|
||||
unsigned int val;
|
||||
|
||||
/* For adjusting speed and duplex of SGMII force mode. */
|
||||
if (interface != PHY_INTERFACE_MODE_SGMII ||
|
||||
phylink_autoneg_inband(mode))
|
||||
return;
|
||||
|
||||
/* SGMII force mode setting */
|
||||
val = mt7530_read(priv, MT7531_SGMII_MODE(port));
|
||||
val &= ~MT7531_SGMII_IF_MODE_MASK;
|
||||
|
||||
switch (speed) {
|
||||
case SPEED_10:
|
||||
val |= MT7531_SGMII_FORCE_SPEED_10;
|
||||
break;
|
||||
case SPEED_100:
|
||||
val |= MT7531_SGMII_FORCE_SPEED_100;
|
||||
break;
|
||||
case SPEED_1000:
|
||||
val |= MT7531_SGMII_FORCE_SPEED_1000;
|
||||
break;
|
||||
}
|
||||
|
||||
/* MT7531 SGMII 1G force mode can only work in full duplex mode,
|
||||
* no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not.
|
||||
*
|
||||
* The speed check is unnecessary as the MAC capabilities apply
|
||||
* this restriction. --rmk
|
||||
*/
|
||||
if ((speed == SPEED_10 || speed == SPEED_100) &&
|
||||
duplex != DUPLEX_FULL)
|
||||
val |= MT7531_SGMII_FORCE_HALF_DUPLEX;
|
||||
|
||||
mt7530_write(priv, MT7531_SGMII_MODE(port), val);
|
||||
}
|
||||
|
||||
static bool mt753x_is_mac_port(u32 port)
|
||||
{
|
||||
return (port == 5 || port == 6);
|
||||
}
|
||||
|
||||
static int mt7531_sgmii_setup_mode_force(struct mt7530_priv *priv, u32 port,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (!mt753x_is_mac_port(port))
|
||||
return -EINVAL;
|
||||
|
||||
mt7530_set(priv, MT7531_QPHY_PWR_STATE_CTRL(port),
|
||||
MT7531_SGMII_PHYA_PWD);
|
||||
|
||||
val = mt7530_read(priv, MT7531_PHYA_CTRL_SIGNAL3(port));
|
||||
val &= ~MT7531_RG_TPHY_SPEED_MASK;
|
||||
/* Setup 2.5 times faster clock for 2.5Gbps data speeds with 10B/8B
|
||||
* encoding.
|
||||
*/
|
||||
val |= (interface == PHY_INTERFACE_MODE_2500BASEX) ?
|
||||
MT7531_RG_TPHY_SPEED_3_125G : MT7531_RG_TPHY_SPEED_1_25G;
|
||||
mt7530_write(priv, MT7531_PHYA_CTRL_SIGNAL3(port), val);
|
||||
|
||||
mt7530_clear(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_ENABLE);
|
||||
|
||||
/* MT7531 SGMII 1G and 2.5G force mode can only work in full duplex
|
||||
* mode, no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not.
|
||||
*/
|
||||
mt7530_rmw(priv, MT7531_SGMII_MODE(port),
|
||||
MT7531_SGMII_IF_MODE_MASK | MT7531_SGMII_REMOTE_FAULT_DIS,
|
||||
MT7531_SGMII_FORCE_SPEED_1000);
|
||||
|
||||
mt7530_write(priv, MT7531_QPHY_PWR_STATE_CTRL(port), 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt7531_sgmii_setup_mode_an(struct mt7530_priv *priv, int port,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
if (!mt753x_is_mac_port(port))
|
||||
return -EINVAL;
|
||||
|
||||
mt7530_set(priv, MT7531_QPHY_PWR_STATE_CTRL(port),
|
||||
MT7531_SGMII_PHYA_PWD);
|
||||
|
||||
mt7530_rmw(priv, MT7531_PHYA_CTRL_SIGNAL3(port),
|
||||
MT7531_RG_TPHY_SPEED_MASK, MT7531_RG_TPHY_SPEED_1_25G);
|
||||
|
||||
mt7530_set(priv, MT7531_SGMII_MODE(port),
|
||||
MT7531_SGMII_REMOTE_FAULT_DIS |
|
||||
MT7531_SGMII_SPEED_DUPLEX_AN);
|
||||
|
||||
mt7530_rmw(priv, MT7531_PCS_SPEED_ABILITY(port),
|
||||
MT7531_SGMII_TX_CONFIG_MASK, 1);
|
||||
|
||||
mt7530_set(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_ENABLE);
|
||||
|
||||
mt7530_set(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_RESTART);
|
||||
|
||||
mt7530_write(priv, MT7531_QPHY_PWR_STATE_CTRL(port), 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mt7531_pcs_an_restart(struct phylink_pcs *pcs)
|
||||
{
|
||||
struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
|
||||
int port = pcs_to_mt753x_pcs(pcs)->port;
|
||||
u32 val;
|
||||
|
||||
/* Only restart AN when AN is enabled */
|
||||
val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
|
||||
if (val & MT7531_SGMII_AN_ENABLE) {
|
||||
val |= MT7531_SGMII_AN_RESTART;
|
||||
mt7530_write(priv, MT7531_PCS_CONTROL_1(port), val);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
|
||||
phy_interface_t interface)
|
||||
|
|
@ -2716,11 +2600,11 @@ mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
|
|||
phydev = dp->slave->phydev;
|
||||
return mt7531_rgmii_setup(priv, port, interface, phydev);
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
return mt7531_sgmii_setup_mode_an(priv, port, interface);
|
||||
case PHY_INTERFACE_MODE_NA:
|
||||
case PHY_INTERFACE_MODE_1000BASEX:
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
return mt7531_sgmii_setup_mode_force(priv, port, interface);
|
||||
/* handled in SGMII PCS driver */
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -2745,11 +2629,11 @@ mt753x_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
|
|||
|
||||
switch (interface) {
|
||||
case PHY_INTERFACE_MODE_TRGMII:
|
||||
return &priv->pcs[port].pcs;
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_1000BASEX:
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
return &priv->pcs[port].pcs;
|
||||
|
||||
return priv->ports[port].sgmii_pcs;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -2987,86 +2871,6 @@ static void mt7530_pcs_get_state(struct phylink_pcs *pcs,
|
|||
state->pause |= MLO_PAUSE_TX;
|
||||
}
|
||||
|
||||
static int
|
||||
mt7531_sgmii_pcs_get_state_an(struct mt7530_priv *priv, int port,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
u32 status, val;
|
||||
u16 config_reg;
|
||||
|
||||
status = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
|
||||
state->link = !!(status & MT7531_SGMII_LINK_STATUS);
|
||||
state->an_complete = !!(status & MT7531_SGMII_AN_COMPLETE);
|
||||
if (state->interface == PHY_INTERFACE_MODE_SGMII &&
|
||||
(status & MT7531_SGMII_AN_ENABLE)) {
|
||||
val = mt7530_read(priv, MT7531_PCS_SPEED_ABILITY(port));
|
||||
config_reg = val >> 16;
|
||||
|
||||
switch (config_reg & LPA_SGMII_SPD_MASK) {
|
||||
case LPA_SGMII_1000:
|
||||
state->speed = SPEED_1000;
|
||||
break;
|
||||
case LPA_SGMII_100:
|
||||
state->speed = SPEED_100;
|
||||
break;
|
||||
case LPA_SGMII_10:
|
||||
state->speed = SPEED_10;
|
||||
break;
|
||||
default:
|
||||
dev_err(priv->dev, "invalid sgmii PHY speed\n");
|
||||
state->link = false;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (config_reg & LPA_SGMII_FULL_DUPLEX)
|
||||
state->duplex = DUPLEX_FULL;
|
||||
else
|
||||
state->duplex = DUPLEX_HALF;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mt7531_sgmii_pcs_get_state_inband(struct mt7530_priv *priv, int port,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
|
||||
state->link = !!(val & MT7531_SGMII_LINK_STATUS);
|
||||
if (!state->link)
|
||||
return;
|
||||
|
||||
state->an_complete = state->link;
|
||||
|
||||
if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
|
||||
state->speed = SPEED_2500;
|
||||
else
|
||||
state->speed = SPEED_1000;
|
||||
|
||||
state->duplex = DUPLEX_FULL;
|
||||
state->pause = MLO_PAUSE_NONE;
|
||||
}
|
||||
|
||||
static void mt7531_pcs_get_state(struct phylink_pcs *pcs,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
|
||||
int port = pcs_to_mt753x_pcs(pcs)->port;
|
||||
|
||||
if (state->interface == PHY_INTERFACE_MODE_SGMII) {
|
||||
mt7531_sgmii_pcs_get_state_an(priv, port, state);
|
||||
return;
|
||||
} else if ((state->interface == PHY_INTERFACE_MODE_1000BASEX) ||
|
||||
(state->interface == PHY_INTERFACE_MODE_2500BASEX)) {
|
||||
mt7531_sgmii_pcs_get_state_inband(priv, port, state);
|
||||
return;
|
||||
}
|
||||
|
||||
state->link = false;
|
||||
}
|
||||
|
||||
static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
|
|
@ -3086,18 +2890,57 @@ static const struct phylink_pcs_ops mt7530_pcs_ops = {
|
|||
.pcs_an_restart = mt7530_pcs_an_restart,
|
||||
};
|
||||
|
||||
static const struct phylink_pcs_ops mt7531_pcs_ops = {
|
||||
.pcs_validate = mt753x_pcs_validate,
|
||||
.pcs_get_state = mt7531_pcs_get_state,
|
||||
.pcs_config = mt753x_pcs_config,
|
||||
.pcs_an_restart = mt7531_pcs_an_restart,
|
||||
.pcs_link_up = mt7531_pcs_link_up,
|
||||
static int mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
|
||||
{
|
||||
struct mt7530_priv *priv = context;
|
||||
|
||||
*val = mt7530_read(priv, reg);
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int mt7530_regmap_write(void *context, unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct mt7530_priv *priv = context;
|
||||
|
||||
mt7530_write(priv, reg, val);
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int mt7530_regmap_update_bits(void *context, unsigned int reg,
|
||||
unsigned int mask, unsigned int val)
|
||||
{
|
||||
struct mt7530_priv *priv = context;
|
||||
|
||||
mt7530_rmw(priv, reg, mask, val);
|
||||
return 0;
|
||||
};
|
||||
|
||||
static const struct regmap_bus mt7531_regmap_bus = {
|
||||
.reg_write = mt7530_regmap_write,
|
||||
.reg_read = mt7530_regmap_read,
|
||||
.reg_update_bits = mt7530_regmap_update_bits,
|
||||
};
|
||||
|
||||
#define MT7531_PCS_REGMAP_CONFIG(_name, _reg_base) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
.reg_bits = 16, \
|
||||
.val_bits = 32, \
|
||||
.reg_stride = 4, \
|
||||
.reg_base = _reg_base, \
|
||||
.max_register = 0x17c, \
|
||||
}
|
||||
|
||||
static const struct regmap_config mt7531_pcs_config[] = {
|
||||
MT7531_PCS_REGMAP_CONFIG("port5", MT7531_SGMII_REG_BASE(5)),
|
||||
MT7531_PCS_REGMAP_CONFIG("port6", MT7531_SGMII_REG_BASE(6)),
|
||||
};
|
||||
|
||||
static int
|
||||
mt753x_setup(struct dsa_switch *ds)
|
||||
{
|
||||
struct mt7530_priv *priv = ds->priv;
|
||||
struct regmap *regmap;
|
||||
int i, ret;
|
||||
|
||||
/* Initialise the PCS devices */
|
||||
|
|
@ -3105,8 +2948,6 @@ mt753x_setup(struct dsa_switch *ds)
|
|||
priv->pcs[i].pcs.ops = priv->info->pcs_ops;
|
||||
priv->pcs[i].priv = priv;
|
||||
priv->pcs[i].port = i;
|
||||
if (mt753x_is_mac_port(i))
|
||||
priv->pcs[i].pcs.poll = 1;
|
||||
}
|
||||
|
||||
ret = priv->info->sw_setup(ds);
|
||||
|
|
@ -3121,6 +2962,16 @@ mt753x_setup(struct dsa_switch *ds)
|
|||
if (ret && priv->irq)
|
||||
mt7530_free_irq_common(priv);
|
||||
|
||||
if (priv->id == ID_MT7531)
|
||||
for (i = 0; i < 2; i++) {
|
||||
regmap = devm_regmap_init(ds->dev,
|
||||
&mt7531_regmap_bus, priv,
|
||||
&mt7531_pcs_config[i]);
|
||||
priv->ports[5 + i].sgmii_pcs =
|
||||
mtk_pcs_lynxi_create(ds->dev, regmap,
|
||||
MT7531_PHYA_CTRL_SIGNAL3, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -3216,7 +3067,7 @@ static const struct mt753x_info mt753x_table[] = {
|
|||
},
|
||||
[ID_MT7531] = {
|
||||
.id = ID_MT7531,
|
||||
.pcs_ops = &mt7531_pcs_ops,
|
||||
.pcs_ops = &mt7530_pcs_ops,
|
||||
.sw_setup = mt7531_setup,
|
||||
.phy_read_c22 = mt7531_ind_c22_phy_read,
|
||||
.phy_write_c22 = mt7531_ind_c22_phy_write,
|
||||
|
|
@ -3326,7 +3177,7 @@ static void
|
|||
mt7530_remove(struct mdio_device *mdiodev)
|
||||
{
|
||||
struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
|
||||
int ret = 0;
|
||||
int ret = 0, i;
|
||||
|
||||
if (!priv)
|
||||
return;
|
||||
|
|
@ -3345,6 +3196,10 @@ mt7530_remove(struct mdio_device *mdiodev)
|
|||
mt7530_free_irq(priv);
|
||||
|
||||
dsa_unregister_switch(priv->ds);
|
||||
|
||||
for (i = 0; i < 2; ++i)
|
||||
mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs);
|
||||
|
||||
mutex_destroy(&priv->reg_mutex);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -364,47 +364,8 @@ enum mt7530_vlan_port_acc_frm {
|
|||
CCR_TX_OCT_CNT_BAD)
|
||||
|
||||
/* MT7531 SGMII register group */
|
||||
#define MT7531_SGMII_REG_BASE 0x5000
|
||||
#define MT7531_SGMII_REG(p, r) (MT7531_SGMII_REG_BASE + \
|
||||
((p) - 5) * 0x1000 + (r))
|
||||
|
||||
/* Register forSGMII PCS_CONTROL_1 */
|
||||
#define MT7531_PCS_CONTROL_1(p) MT7531_SGMII_REG(p, 0x00)
|
||||
#define MT7531_SGMII_LINK_STATUS BIT(18)
|
||||
#define MT7531_SGMII_AN_ENABLE BIT(12)
|
||||
#define MT7531_SGMII_AN_RESTART BIT(9)
|
||||
#define MT7531_SGMII_AN_COMPLETE BIT(21)
|
||||
|
||||
/* Register for SGMII PCS_SPPED_ABILITY */
|
||||
#define MT7531_PCS_SPEED_ABILITY(p) MT7531_SGMII_REG(p, 0x08)
|
||||
#define MT7531_SGMII_TX_CONFIG_MASK GENMASK(15, 0)
|
||||
#define MT7531_SGMII_TX_CONFIG BIT(0)
|
||||
|
||||
/* Register for SGMII_MODE */
|
||||
#define MT7531_SGMII_MODE(p) MT7531_SGMII_REG(p, 0x20)
|
||||
#define MT7531_SGMII_REMOTE_FAULT_DIS BIT(8)
|
||||
#define MT7531_SGMII_IF_MODE_MASK GENMASK(5, 1)
|
||||
#define MT7531_SGMII_FORCE_DUPLEX BIT(4)
|
||||
#define MT7531_SGMII_FORCE_SPEED_MASK GENMASK(3, 2)
|
||||
#define MT7531_SGMII_FORCE_SPEED_1000 BIT(3)
|
||||
#define MT7531_SGMII_FORCE_SPEED_100 BIT(2)
|
||||
#define MT7531_SGMII_FORCE_SPEED_10 0
|
||||
#define MT7531_SGMII_SPEED_DUPLEX_AN BIT(1)
|
||||
|
||||
enum mt7531_sgmii_force_duplex {
|
||||
MT7531_SGMII_FORCE_FULL_DUPLEX = 0,
|
||||
MT7531_SGMII_FORCE_HALF_DUPLEX = 0x10,
|
||||
};
|
||||
|
||||
/* Fields of QPHY_PWR_STATE_CTRL */
|
||||
#define MT7531_QPHY_PWR_STATE_CTRL(p) MT7531_SGMII_REG(p, 0xe8)
|
||||
#define MT7531_SGMII_PHYA_PWD BIT(4)
|
||||
|
||||
/* Values of SGMII SPEED */
|
||||
#define MT7531_PHYA_CTRL_SIGNAL3(p) MT7531_SGMII_REG(p, 0x128)
|
||||
#define MT7531_RG_TPHY_SPEED_MASK (BIT(2) | BIT(3))
|
||||
#define MT7531_RG_TPHY_SPEED_1_25G 0x0
|
||||
#define MT7531_RG_TPHY_SPEED_3_125G BIT(2)
|
||||
#define MT7531_SGMII_REG_BASE(p) (0x5000 + ((p) - 5) * 0x1000)
|
||||
#define MT7531_PHYA_CTRL_SIGNAL3 0x128
|
||||
|
||||
/* Register for system reset */
|
||||
#define MT7530_SYS_CTRL 0x7000
|
||||
|
|
@ -703,13 +664,13 @@ struct mt7530_fdb {
|
|||
* @pm: The matrix used to show all connections with the port.
|
||||
* @pvid: The VLAN specified is to be considered a PVID at ingress. Any
|
||||
* untagged frames will be assigned to the related VLAN.
|
||||
* @vlan_filtering: The flags indicating whether the port that can recognize
|
||||
* VLAN-tagged frames.
|
||||
* @sgmii_pcs: Pointer to PCS instance for SerDes ports
|
||||
*/
|
||||
struct mt7530_port {
|
||||
bool enable;
|
||||
u32 pm;
|
||||
u16 pvid;
|
||||
struct phylink_pcs *sgmii_pcs;
|
||||
};
|
||||
|
||||
/* Port 5 interface select definitions */
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ config NET_MEDIATEK_SOC
|
|||
select DIMLIB
|
||||
select PAGE_POOL
|
||||
select PAGE_POOL_STATS
|
||||
select PCS_MTK_LYNXI
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
This driver supports the gigabit ethernet MACs in the
|
||||
MediaTek SoC family.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#
|
||||
|
||||
obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
|
||||
mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
|
||||
mtk_eth-y := mtk_eth_soc.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
|
||||
mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o mtk_wed_wo.o
|
||||
ifdef CONFIG_DEBUG_FS
|
||||
mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o
|
||||
|
|
|
|||
|
|
@ -96,12 +96,20 @@ static int set_mux_gmac2_gmac0_to_gephy(struct mtk_eth *eth, int path)
|
|||
|
||||
static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, int path)
|
||||
{
|
||||
unsigned int val = 0;
|
||||
unsigned int val = 0, mask = 0, reg = 0;
|
||||
bool updated = true;
|
||||
|
||||
switch (path) {
|
||||
case MTK_ETH_PATH_GMAC2_SGMII:
|
||||
val = CO_QPHY_SEL;
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_U3_COPHY_V2)) {
|
||||
reg = USB_PHY_SWITCH_REG;
|
||||
val = SGMII_QPHY_SEL;
|
||||
mask = QPHY_SEL_MASK;
|
||||
} else {
|
||||
reg = INFRA_MISC2;
|
||||
val = CO_QPHY_SEL;
|
||||
mask = val;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
updated = false;
|
||||
|
|
@ -109,7 +117,7 @@ static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, int path)
|
|||
}
|
||||
|
||||
if (updated)
|
||||
regmap_update_bits(eth->infra, INFRA_MISC2, CO_QPHY_SEL, val);
|
||||
regmap_update_bits(eth->infra, reg, mask, val);
|
||||
|
||||
dev_dbg(eth->dev, "path %s in %s updated = %d\n",
|
||||
mtk_eth_path_name(path), __func__, updated);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/pinctrl/devinfo.h>
|
||||
#include <linux/phylink.h>
|
||||
#include <linux/pcs/pcs-mtk-lynxi.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <net/dsa.h>
|
||||
|
|
@ -437,7 +438,7 @@ static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config,
|
|||
sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
|
||||
0 : mac->id;
|
||||
|
||||
return mtk_sgmii_select_pcs(eth->sgmii, sid);
|
||||
return eth->sgmii_pcs[sid];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
|
@ -765,8 +766,10 @@ static const struct phylink_mac_ops mtk_phylink_ops = {
|
|||
|
||||
static int mtk_mdio_init(struct mtk_eth *eth)
|
||||
{
|
||||
unsigned int max_clk = 2500000, divider;
|
||||
struct device_node *mii_np;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
mii_np = of_get_child_by_name(eth->dev->of_node, "mdio-bus");
|
||||
if (!mii_np) {
|
||||
|
|
@ -794,6 +797,25 @@ static int mtk_mdio_init(struct mtk_eth *eth)
|
|||
eth->mii_bus->parent = eth->dev;
|
||||
|
||||
snprintf(eth->mii_bus->id, MII_BUS_ID_SIZE, "%pOFn", mii_np);
|
||||
|
||||
if (!of_property_read_u32(mii_np, "clock-frequency", &val)) {
|
||||
if (val > MDC_MAX_FREQ || val < MDC_MAX_FREQ / MDC_MAX_DIVIDER) {
|
||||
dev_err(eth->dev, "MDIO clock frequency out of range");
|
||||
ret = -EINVAL;
|
||||
goto err_put_node;
|
||||
}
|
||||
max_clk = val;
|
||||
}
|
||||
divider = min_t(unsigned int, DIV_ROUND_UP(MDC_MAX_FREQ, max_clk), 63);
|
||||
|
||||
/* Configure MDC Divider */
|
||||
val = mtk_r32(eth, MTK_PPSC);
|
||||
val &= ~PPSC_MDC_CFG;
|
||||
val |= FIELD_PREP(PPSC_MDC_CFG, divider) | PPSC_MDC_TURBO;
|
||||
mtk_w32(eth, val, MTK_PPSC);
|
||||
|
||||
dev_dbg(eth->dev, "MDC is running on %d Hz\n", MDC_MAX_FREQ / divider);
|
||||
|
||||
ret = of_mdiobus_register(eth->mii_bus, mii_np);
|
||||
|
||||
err_put_node:
|
||||
|
|
@ -4032,8 +4054,17 @@ static int mtk_unreg_dev(struct mtk_eth *eth)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_sgmii_destroy(struct mtk_eth *eth)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MTK_MAX_DEVS; i++)
|
||||
mtk_pcs_lynxi_destroy(eth->sgmii_pcs[i]);
|
||||
}
|
||||
|
||||
static int mtk_cleanup(struct mtk_eth *eth)
|
||||
{
|
||||
mtk_sgmii_destroy(eth);
|
||||
mtk_unreg_dev(eth);
|
||||
mtk_free_dev(eth);
|
||||
cancel_work_sync(ð->pending_work);
|
||||
|
|
@ -4479,6 +4510,36 @@ void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev)
|
|||
rtnl_unlock();
|
||||
}
|
||||
|
||||
static int mtk_sgmii_init(struct mtk_eth *eth)
|
||||
{
|
||||
struct device_node *np;
|
||||
struct regmap *regmap;
|
||||
u32 flags;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MTK_MAX_DEVS; i++) {
|
||||
np = of_parse_phandle(eth->dev->of_node, "mediatek,sgmiisys", i);
|
||||
if (!np)
|
||||
break;
|
||||
|
||||
regmap = syscon_node_to_regmap(np);
|
||||
flags = 0;
|
||||
if (of_property_read_bool(np, "mediatek,pnswap"))
|
||||
flags |= MTK_SGMII_FLAG_PN_SWAP;
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
eth->sgmii_pcs[i] = mtk_pcs_lynxi_create(eth->dev, regmap,
|
||||
eth->soc->ana_rgc3,
|
||||
flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res = NULL;
|
||||
|
|
@ -4542,13 +4603,7 @@ static int mtk_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
|
||||
eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii),
|
||||
GFP_KERNEL);
|
||||
if (!eth->sgmii)
|
||||
return -ENOMEM;
|
||||
|
||||
err = mtk_sgmii_init(eth->sgmii, pdev->dev.of_node,
|
||||
eth->soc->ana_rgc3);
|
||||
err = mtk_sgmii_init(eth);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
|
@ -4559,14 +4614,17 @@ static int mtk_probe(struct platform_device *pdev)
|
|||
"mediatek,pctl");
|
||||
if (IS_ERR(eth->pctl)) {
|
||||
dev_err(&pdev->dev, "no pctl regmap found\n");
|
||||
return PTR_ERR(eth->pctl);
|
||||
err = PTR_ERR(eth->pctl);
|
||||
goto err_destroy_sgmii;
|
||||
}
|
||||
}
|
||||
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
if (!res) {
|
||||
err = -EINVAL;
|
||||
goto err_destroy_sgmii;
|
||||
}
|
||||
}
|
||||
|
||||
if (eth->soc->offload_version) {
|
||||
|
|
@ -4676,8 +4734,8 @@ static int mtk_probe(struct platform_device *pdev)
|
|||
for (i = 0; i < num_ppe; i++) {
|
||||
u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400;
|
||||
|
||||
eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr,
|
||||
eth->soc->offload_version, i);
|
||||
eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr, i);
|
||||
|
||||
if (!eth->ppe[i]) {
|
||||
err = -ENOMEM;
|
||||
goto err_deinit_ppe;
|
||||
|
|
@ -4725,6 +4783,8 @@ static int mtk_probe(struct platform_device *pdev)
|
|||
mtk_hw_deinit(eth);
|
||||
err_wed_exit:
|
||||
mtk_wed_exit();
|
||||
err_destroy_sgmii:
|
||||
mtk_sgmii_destroy(eth);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -4799,6 +4859,7 @@ static const struct mtk_soc_data mt7622_data = {
|
|||
.required_pctl = false,
|
||||
.offload_version = 2,
|
||||
.hash_offset = 2,
|
||||
.has_accounting = true,
|
||||
.foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
|
||||
.txrx = {
|
||||
.txd_size = sizeof(struct mtk_tx_dma),
|
||||
|
|
@ -4836,6 +4897,7 @@ static const struct mtk_soc_data mt7629_data = {
|
|||
.hw_features = MTK_HW_FEATURES,
|
||||
.required_clks = MT7629_CLKS_BITMAP,
|
||||
.required_pctl = false,
|
||||
.has_accounting = true,
|
||||
.txrx = {
|
||||
.txd_size = sizeof(struct mtk_tx_dma),
|
||||
.rxd_size = sizeof(struct mtk_rx_dma),
|
||||
|
|
@ -4846,6 +4908,27 @@ static const struct mtk_soc_data mt7629_data = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct mtk_soc_data mt7981_data = {
|
||||
.reg_map = &mt7986_reg_map,
|
||||
.ana_rgc3 = 0x128,
|
||||
.caps = MT7981_CAPS,
|
||||
.hw_features = MTK_HW_FEATURES,
|
||||
.required_clks = MT7981_CLKS_BITMAP,
|
||||
.required_pctl = false,
|
||||
.offload_version = 2,
|
||||
.hash_offset = 4,
|
||||
.foe_entry_size = sizeof(struct mtk_foe_entry),
|
||||
.has_accounting = true,
|
||||
.txrx = {
|
||||
.txd_size = sizeof(struct mtk_tx_dma_v2),
|
||||
.rxd_size = sizeof(struct mtk_rx_dma_v2),
|
||||
.rx_irq_done_mask = MTK_RX_DONE_INT_V2,
|
||||
.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
|
||||
.dma_len_offset = 8,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct mtk_soc_data mt7986_data = {
|
||||
.reg_map = &mt7986_reg_map,
|
||||
.ana_rgc3 = 0x128,
|
||||
|
|
@ -4856,6 +4939,7 @@ static const struct mtk_soc_data mt7986_data = {
|
|||
.offload_version = 2,
|
||||
.hash_offset = 4,
|
||||
.foe_entry_size = sizeof(struct mtk_foe_entry),
|
||||
.has_accounting = true,
|
||||
.txrx = {
|
||||
.txd_size = sizeof(struct mtk_tx_dma_v2),
|
||||
.rxd_size = sizeof(struct mtk_rx_dma_v2),
|
||||
|
|
@ -4888,6 +4972,7 @@ const struct of_device_id of_mtk_match[] = {
|
|||
{ .compatible = "mediatek,mt7622-eth", .data = &mt7622_data},
|
||||
{ .compatible = "mediatek,mt7623-eth", .data = &mt7623_data},
|
||||
{ .compatible = "mediatek,mt7629-eth", .data = &mt7629_data},
|
||||
{ .compatible = "mediatek,mt7981-eth", .data = &mt7981_data},
|
||||
{ .compatible = "mediatek,mt7986-eth", .data = &mt7986_data},
|
||||
{ .compatible = "ralink,rt5350-eth", .data = &rt5350_data},
|
||||
{},
|
||||
|
|
|
|||
|
|
@ -363,6 +363,13 @@
|
|||
#define RX_DMA_VTAG_V2 BIT(0)
|
||||
#define RX_DMA_L4_VALID_V2 BIT(2)
|
||||
|
||||
/* PHY Polling and SMI Master Control registers */
|
||||
#define MTK_PPSC 0x10000
|
||||
#define PPSC_MDC_CFG GENMASK(29, 24)
|
||||
#define PPSC_MDC_TURBO BIT(20)
|
||||
#define MDC_MAX_FREQ 25000000
|
||||
#define MDC_MAX_DIVIDER 63
|
||||
|
||||
/* PHY Indirect Access Control registers */
|
||||
#define MTK_PHY_IAC 0x10004
|
||||
#define PHY_IAC_ACCESS BIT(31)
|
||||
|
|
@ -503,64 +510,16 @@
|
|||
#define ETHSYS_DMA_AG_MAP_QDMA BIT(1)
|
||||
#define ETHSYS_DMA_AG_MAP_PPE BIT(2)
|
||||
|
||||
/* SGMII subsystem config registers */
|
||||
/* BMCR (low 16) BMSR (high 16) */
|
||||
#define SGMSYS_PCS_CONTROL_1 0x0
|
||||
#define SGMII_BMCR GENMASK(15, 0)
|
||||
#define SGMII_BMSR GENMASK(31, 16)
|
||||
#define SGMII_AN_RESTART BIT(9)
|
||||
#define SGMII_ISOLATE BIT(10)
|
||||
#define SGMII_AN_ENABLE BIT(12)
|
||||
#define SGMII_LINK_STATYS BIT(18)
|
||||
#define SGMII_AN_ABILITY BIT(19)
|
||||
#define SGMII_AN_COMPLETE BIT(21)
|
||||
#define SGMII_PCS_FAULT BIT(23)
|
||||
#define SGMII_AN_EXPANSION_CLR BIT(30)
|
||||
|
||||
#define SGMSYS_PCS_ADVERTISE 0x8
|
||||
#define SGMII_ADVERTISE GENMASK(15, 0)
|
||||
#define SGMII_LPA GENMASK(31, 16)
|
||||
|
||||
/* Register to programmable link timer, the unit in 2 * 8ns */
|
||||
#define SGMSYS_PCS_LINK_TIMER 0x18
|
||||
#define SGMII_LINK_TIMER_MASK GENMASK(19, 0)
|
||||
#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & SGMII_LINK_TIMER_MASK)
|
||||
|
||||
/* Register to control remote fault */
|
||||
#define SGMSYS_SGMII_MODE 0x20
|
||||
#define SGMII_IF_MODE_SGMII BIT(0)
|
||||
#define SGMII_SPEED_DUPLEX_AN BIT(1)
|
||||
#define SGMII_SPEED_MASK GENMASK(3, 2)
|
||||
#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
|
||||
#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1)
|
||||
#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
|
||||
#define SGMII_DUPLEX_HALF BIT(4)
|
||||
#define SGMII_IF_MODE_BIT5 BIT(5)
|
||||
#define SGMII_REMOTE_FAULT_DIS BIT(8)
|
||||
#define SGMII_CODE_SYNC_SET_VAL BIT(9)
|
||||
#define SGMII_CODE_SYNC_SET_EN BIT(10)
|
||||
#define SGMII_SEND_AN_ERROR_EN BIT(11)
|
||||
#define SGMII_IF_MODE_MASK GENMASK(5, 1)
|
||||
|
||||
/* Register to reset SGMII design */
|
||||
#define SGMII_RESERVED_0 0x34
|
||||
#define SGMII_SW_RESET BIT(0)
|
||||
|
||||
/* Register to set SGMII speed, ANA RG_ Control Signals III*/
|
||||
#define SGMSYS_ANA_RG_CS3 0x2028
|
||||
#define RG_PHY_SPEED_MASK (BIT(2) | BIT(3))
|
||||
#define RG_PHY_SPEED_1_25G 0x0
|
||||
#define RG_PHY_SPEED_3_125G BIT(2)
|
||||
|
||||
/* Register to power up QPHY */
|
||||
#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8
|
||||
#define SGMII_PHYA_PWD BIT(4)
|
||||
|
||||
/* Infrasys subsystem config registers */
|
||||
#define INFRA_MISC2 0x70c
|
||||
#define CO_QPHY_SEL BIT(0)
|
||||
#define GEPHY_MAC_SEL BIT(1)
|
||||
|
||||
/* Top misc registers */
|
||||
#define USB_PHY_SWITCH_REG 0x218
|
||||
#define QPHY_SEL_MASK GENMASK(1, 0)
|
||||
#define SGMII_QPHY_SEL 0x2
|
||||
|
||||
/* MT7628/88 specific stuff */
|
||||
#define MT7628_PDMA_OFFSET 0x0800
|
||||
#define MT7628_SDM_OFFSET 0x0c00
|
||||
|
|
@ -741,6 +700,17 @@ enum mtk_clks_map {
|
|||
BIT(MTK_CLK_SGMII2_CDR_FB) | \
|
||||
BIT(MTK_CLK_SGMII_CK) | \
|
||||
BIT(MTK_CLK_ETH2PLL) | BIT(MTK_CLK_SGMIITOP))
|
||||
#define MT7981_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \
|
||||
BIT(MTK_CLK_WOCPU0) | \
|
||||
BIT(MTK_CLK_SGMII_TX_250M) | \
|
||||
BIT(MTK_CLK_SGMII_RX_250M) | \
|
||||
BIT(MTK_CLK_SGMII_CDR_REF) | \
|
||||
BIT(MTK_CLK_SGMII_CDR_FB) | \
|
||||
BIT(MTK_CLK_SGMII2_TX_250M) | \
|
||||
BIT(MTK_CLK_SGMII2_RX_250M) | \
|
||||
BIT(MTK_CLK_SGMII2_CDR_REF) | \
|
||||
BIT(MTK_CLK_SGMII2_CDR_FB) | \
|
||||
BIT(MTK_CLK_SGMII_CK))
|
||||
#define MT7986_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \
|
||||
BIT(MTK_CLK_WOCPU1) | BIT(MTK_CLK_WOCPU0) | \
|
||||
BIT(MTK_CLK_SGMII_TX_250M) | \
|
||||
|
|
@ -854,6 +824,7 @@ enum mkt_eth_capabilities {
|
|||
MTK_NETSYS_V2_BIT,
|
||||
MTK_SOC_MT7628_BIT,
|
||||
MTK_RSTCTRL_PPE1_BIT,
|
||||
MTK_U3_COPHY_V2_BIT,
|
||||
|
||||
/* MUX BITS*/
|
||||
MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT,
|
||||
|
|
@ -888,6 +859,7 @@ enum mkt_eth_capabilities {
|
|||
#define MTK_NETSYS_V2 BIT(MTK_NETSYS_V2_BIT)
|
||||
#define MTK_SOC_MT7628 BIT(MTK_SOC_MT7628_BIT)
|
||||
#define MTK_RSTCTRL_PPE1 BIT(MTK_RSTCTRL_PPE1_BIT)
|
||||
#define MTK_U3_COPHY_V2 BIT(MTK_U3_COPHY_V2_BIT)
|
||||
|
||||
#define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \
|
||||
BIT(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT)
|
||||
|
|
@ -960,6 +932,11 @@ enum mkt_eth_capabilities {
|
|||
MTK_MUX_U3_GMAC2_TO_QPHY | \
|
||||
MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA)
|
||||
|
||||
#define MT7981_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \
|
||||
MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
|
||||
MTK_MUX_U3_GMAC2_TO_QPHY | MTK_U3_COPHY_V2 | \
|
||||
MTK_NETSYS_V2 | MTK_RSTCTRL_PPE1)
|
||||
|
||||
#define MT7986_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \
|
||||
MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
|
||||
MTK_NETSYS_V2 | MTK_RSTCTRL_PPE1)
|
||||
|
|
@ -1034,6 +1011,8 @@ struct mtk_reg_map {
|
|||
* the extra setup for those pins used by GMAC.
|
||||
* @hash_offset Flow table hash offset.
|
||||
* @foe_entry_size Foe table entry size.
|
||||
* @has_accounting Bool indicating support for accounting of
|
||||
* offloaded flows.
|
||||
* @txd_size Tx DMA descriptor size.
|
||||
* @rxd_size Rx DMA descriptor size.
|
||||
* @rx_irq_done_mask Rx irq done register mask.
|
||||
|
|
@ -1051,6 +1030,7 @@ struct mtk_soc_data {
|
|||
u8 hash_offset;
|
||||
u16 foe_entry_size;
|
||||
netdev_features_t hw_features;
|
||||
bool has_accounting;
|
||||
struct {
|
||||
u32 txd_size;
|
||||
u32 rxd_size;
|
||||
|
|
@ -1066,29 +1046,6 @@ struct mtk_soc_data {
|
|||
/* currently no SoC has more than 2 macs */
|
||||
#define MTK_MAX_DEVS 2
|
||||
|
||||
/* struct mtk_pcs - This structure holds each sgmii regmap and associated
|
||||
* data
|
||||
* @regmap: The register map pointing at the range used to setup
|
||||
* SGMII modes
|
||||
* @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
|
||||
* @interface: Currently configured interface mode
|
||||
* @pcs: Phylink PCS structure
|
||||
*/
|
||||
struct mtk_pcs {
|
||||
struct regmap *regmap;
|
||||
u32 ana_rgc3;
|
||||
phy_interface_t interface;
|
||||
struct phylink_pcs pcs;
|
||||
};
|
||||
|
||||
/* struct mtk_sgmii - This is the structure holding sgmii regmap and its
|
||||
* characteristics
|
||||
* @pcs Array of individual PCS structures
|
||||
*/
|
||||
struct mtk_sgmii {
|
||||
struct mtk_pcs pcs[MTK_MAX_DEVS];
|
||||
};
|
||||
|
||||
/* struct mtk_eth - This is the main datasructure for holding the state
|
||||
* of the driver
|
||||
* @dev: The device pointer
|
||||
|
|
@ -1108,6 +1065,7 @@ struct mtk_sgmii {
|
|||
* MII modes
|
||||
* @infra: The register map pointing at the range used to setup
|
||||
* SGMII and GePHY path
|
||||
* @sgmii_pcs: Pointers to mtk-pcs-lynxi phylink_pcs instances
|
||||
* @pctl: The register map pointing at the range used to setup
|
||||
* GMAC port drive/slew values
|
||||
* @dma_refcnt: track how many netdevs are using the DMA engine
|
||||
|
|
@ -1148,8 +1106,8 @@ struct mtk_eth {
|
|||
u32 msg_enable;
|
||||
unsigned long sysclk;
|
||||
struct regmap *ethsys;
|
||||
struct regmap *infra;
|
||||
struct mtk_sgmii *sgmii;
|
||||
struct regmap *infra;
|
||||
struct phylink_pcs *sgmii_pcs[MTK_MAX_DEVS];
|
||||
struct regmap *pctl;
|
||||
bool hwlro;
|
||||
refcount_t dma_refcnt;
|
||||
|
|
@ -1311,10 +1269,6 @@ void mtk_stats_update_mac(struct mtk_mac *mac);
|
|||
void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
|
||||
u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
|
||||
|
||||
struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id);
|
||||
int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np,
|
||||
u32 ana_rgc3);
|
||||
|
||||
int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id);
|
||||
int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
|
||||
int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id);
|
||||
|
|
|
|||
|
|
@ -74,6 +74,48 @@ static int mtk_ppe_wait_busy(struct mtk_ppe *ppe)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_ppe_mib_wait_busy(struct mtk_ppe *ppe)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = readl_poll_timeout(ppe->base + MTK_PPE_MIB_SER_CR, val,
|
||||
!(val & MTK_PPE_MIB_SER_CR_ST),
|
||||
20, MTK_PPE_WAIT_TIMEOUT_US);
|
||||
|
||||
if (ret)
|
||||
dev_err(ppe->dev, "MIB table busy");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets)
|
||||
{
|
||||
u32 byte_cnt_low, byte_cnt_high, pkt_cnt_low, pkt_cnt_high;
|
||||
u32 val, cnt_r0, cnt_r1, cnt_r2;
|
||||
int ret;
|
||||
|
||||
val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST;
|
||||
ppe_w32(ppe, MTK_PPE_MIB_SER_CR, val);
|
||||
|
||||
ret = mtk_ppe_mib_wait_busy(ppe);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0);
|
||||
cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1);
|
||||
cnt_r2 = readl(ppe->base + MTK_PPE_MIB_SER_R2);
|
||||
|
||||
byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0);
|
||||
byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1);
|
||||
pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1);
|
||||
pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2);
|
||||
*bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low;
|
||||
*packets = (pkt_cnt_high << 16) | pkt_cnt_low;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
|
||||
{
|
||||
ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
|
||||
|
|
@ -458,6 +500,13 @@ __mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
|
|||
hwe->ib1 &= ~MTK_FOE_IB1_STATE;
|
||||
hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID);
|
||||
dma_wmb();
|
||||
if (ppe->accounting) {
|
||||
struct mtk_foe_accounting *acct;
|
||||
|
||||
acct = ppe->acct_table + entry->hash * sizeof(*acct);
|
||||
acct->packets = 0;
|
||||
acct->bytes = 0;
|
||||
}
|
||||
}
|
||||
entry->hash = 0xffff;
|
||||
|
||||
|
|
@ -565,6 +614,9 @@ __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
|
|||
wmb();
|
||||
hwe->ib1 = entry->ib1;
|
||||
|
||||
if (ppe->accounting)
|
||||
*mtk_foe_entry_ib2(eth, hwe) |= MTK_FOE_IB2_MIB_CNT;
|
||||
|
||||
dma_wmb();
|
||||
|
||||
mtk_ppe_cache_clear(ppe);
|
||||
|
|
@ -756,11 +808,39 @@ int mtk_ppe_prepare_reset(struct mtk_ppe *ppe)
|
|||
return mtk_ppe_wait_busy(ppe);
|
||||
}
|
||||
|
||||
struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
|
||||
int version, int index)
|
||||
struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
|
||||
struct mtk_foe_accounting *diff)
|
||||
{
|
||||
struct mtk_foe_accounting *acct;
|
||||
int size = sizeof(struct mtk_foe_accounting);
|
||||
u64 bytes, packets;
|
||||
|
||||
if (!ppe->accounting)
|
||||
return NULL;
|
||||
|
||||
if (mtk_mib_entry_read(ppe, index, &bytes, &packets))
|
||||
return NULL;
|
||||
|
||||
acct = ppe->acct_table + index * size;
|
||||
|
||||
acct->bytes += bytes;
|
||||
acct->packets += packets;
|
||||
|
||||
if (diff) {
|
||||
diff->bytes = bytes;
|
||||
diff->packets = packets;
|
||||
}
|
||||
|
||||
return acct;
|
||||
}
|
||||
|
||||
struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index)
|
||||
{
|
||||
bool accounting = eth->soc->has_accounting;
|
||||
const struct mtk_soc_data *soc = eth->soc;
|
||||
struct mtk_foe_accounting *acct;
|
||||
struct device *dev = eth->dev;
|
||||
struct mtk_mib_entry *mib;
|
||||
struct mtk_ppe *ppe;
|
||||
u32 foe_flow_size;
|
||||
void *foe;
|
||||
|
|
@ -777,7 +857,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
|
|||
ppe->base = base;
|
||||
ppe->eth = eth;
|
||||
ppe->dev = dev;
|
||||
ppe->version = version;
|
||||
ppe->version = eth->soc->offload_version;
|
||||
ppe->accounting = accounting;
|
||||
|
||||
foe = dmam_alloc_coherent(ppe->dev,
|
||||
MTK_PPE_ENTRIES * soc->foe_entry_size,
|
||||
|
|
@ -793,6 +874,23 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
|
|||
if (!ppe->foe_flow)
|
||||
goto err_free_l2_flows;
|
||||
|
||||
if (accounting) {
|
||||
mib = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*mib),
|
||||
&ppe->mib_phys, GFP_KERNEL);
|
||||
if (!mib)
|
||||
return NULL;
|
||||
|
||||
ppe->mib_table = mib;
|
||||
|
||||
acct = devm_kzalloc(dev, MTK_PPE_ENTRIES * sizeof(*acct),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!acct)
|
||||
return NULL;
|
||||
|
||||
ppe->acct_table = acct;
|
||||
}
|
||||
|
||||
mtk_ppe_debugfs_init(ppe, index);
|
||||
|
||||
return ppe;
|
||||
|
|
@ -922,6 +1020,16 @@ void mtk_ppe_start(struct mtk_ppe *ppe)
|
|||
ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT1, 0xcb777);
|
||||
ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f);
|
||||
}
|
||||
|
||||
if (ppe->accounting && ppe->mib_phys) {
|
||||
ppe_w32(ppe, MTK_PPE_MIB_TB_BASE, ppe->mib_phys);
|
||||
ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_EN,
|
||||
MTK_PPE_MIB_CFG_EN);
|
||||
ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_RD_CLR,
|
||||
MTK_PPE_MIB_CFG_RD_CLR);
|
||||
ppe_m32(ppe, MTK_PPE_MIB_CACHE_CTL, MTK_PPE_MIB_CACHE_CTL_EN,
|
||||
MTK_PPE_MIB_CFG_RD_CLR);
|
||||
}
|
||||
}
|
||||
|
||||
int mtk_ppe_stop(struct mtk_ppe *ppe)
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ enum {
|
|||
#define MTK_FOE_IB2_MULTICAST BIT(8)
|
||||
|
||||
#define MTK_FOE_IB2_WDMA_QID2 GENMASK(13, 12)
|
||||
#define MTK_FOE_IB2_MIB_CNT BIT(15)
|
||||
#define MTK_FOE_IB2_WDMA_DEVIDX BIT(16)
|
||||
#define MTK_FOE_IB2_WDMA_WINFO BIT(17)
|
||||
|
||||
|
|
@ -285,16 +286,34 @@ struct mtk_flow_entry {
|
|||
unsigned long cookie;
|
||||
};
|
||||
|
||||
struct mtk_mib_entry {
|
||||
u32 byt_cnt_l;
|
||||
u16 byt_cnt_h;
|
||||
u32 pkt_cnt_l;
|
||||
u8 pkt_cnt_h;
|
||||
u8 _rsv0;
|
||||
u32 _rsv1;
|
||||
} __packed;
|
||||
|
||||
struct mtk_foe_accounting {
|
||||
u64 bytes;
|
||||
u64 packets;
|
||||
};
|
||||
|
||||
struct mtk_ppe {
|
||||
struct mtk_eth *eth;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
int version;
|
||||
char dirname[5];
|
||||
bool accounting;
|
||||
|
||||
void *foe_table;
|
||||
dma_addr_t foe_phys;
|
||||
|
||||
struct mtk_mib_entry *mib_table;
|
||||
dma_addr_t mib_phys;
|
||||
|
||||
u16 foe_check_time[MTK_PPE_ENTRIES];
|
||||
struct hlist_head *foe_flow;
|
||||
|
||||
|
|
@ -303,8 +322,8 @@ struct mtk_ppe {
|
|||
void *acct_table;
|
||||
};
|
||||
|
||||
struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
|
||||
int version, int index);
|
||||
struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index);
|
||||
|
||||
void mtk_ppe_deinit(struct mtk_eth *eth);
|
||||
void mtk_ppe_start(struct mtk_ppe *ppe);
|
||||
int mtk_ppe_stop(struct mtk_ppe *ppe);
|
||||
|
|
@ -359,5 +378,7 @@ int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
|
|||
void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
|
||||
int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
|
||||
int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index);
|
||||
struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
|
||||
struct mtk_foe_accounting *diff);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind)
|
|||
struct mtk_foe_entry *entry = mtk_foe_get_entry(ppe, i);
|
||||
struct mtk_foe_mac_info *l2;
|
||||
struct mtk_flow_addr_info ai = {};
|
||||
struct mtk_foe_accounting *acct;
|
||||
unsigned char h_source[ETH_ALEN];
|
||||
unsigned char h_dest[ETH_ALEN];
|
||||
int type, state;
|
||||
|
|
@ -95,6 +96,8 @@ mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind)
|
|||
if (bind && state != MTK_FOE_STATE_BIND)
|
||||
continue;
|
||||
|
||||
acct = mtk_foe_entry_get_mib(ppe, i, NULL);
|
||||
|
||||
type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
|
||||
seq_printf(m, "%05x %s %7s", i,
|
||||
mtk_foe_entry_state_str(state),
|
||||
|
|
@ -153,9 +156,11 @@ mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind)
|
|||
*((__be16 *)&h_dest[4]) = htons(l2->dest_mac_lo);
|
||||
|
||||
seq_printf(m, " eth=%pM->%pM etype=%04x"
|
||||
" vlan=%d,%d ib1=%08x ib2=%08x\n",
|
||||
" vlan=%d,%d ib1=%08x ib2=%08x"
|
||||
" packets=%llu bytes=%llu\n",
|
||||
h_source, h_dest, ntohs(l2->etype),
|
||||
l2->vlan1, l2->vlan2, entry->ib1, ib2);
|
||||
l2->vlan1, l2->vlan2, entry->ib1, ib2,
|
||||
acct ? acct->packets : 0, acct ? acct->bytes : 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -497,6 +497,7 @@ static int
|
|||
mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
|
||||
{
|
||||
struct mtk_flow_entry *entry;
|
||||
struct mtk_foe_accounting diff;
|
||||
u32 idle;
|
||||
|
||||
entry = rhashtable_lookup(ð->flow_table, &f->cookie,
|
||||
|
|
@ -507,6 +508,13 @@ mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
|
|||
idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
|
||||
f->stats.lastused = jiffies - idle * HZ;
|
||||
|
||||
if (entry->hash != 0xFFFF &&
|
||||
mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash,
|
||||
&diff)) {
|
||||
f->stats.pkts += diff.packets;
|
||||
f->stats.bytes += diff.bytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -149,6 +149,20 @@ enum {
|
|||
|
||||
#define MTK_PPE_MIB_TB_BASE 0x338
|
||||
|
||||
#define MTK_PPE_MIB_SER_CR 0x33C
|
||||
#define MTK_PPE_MIB_SER_CR_ST BIT(16)
|
||||
#define MTK_PPE_MIB_SER_CR_ADDR GENMASK(13, 0)
|
||||
|
||||
#define MTK_PPE_MIB_SER_R0 0x340
|
||||
#define MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW GENMASK(31, 0)
|
||||
|
||||
#define MTK_PPE_MIB_SER_R1 0x344
|
||||
#define MTK_PPE_MIB_SER_R1_PKT_CNT_LOW GENMASK(31, 16)
|
||||
#define MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH GENMASK(15, 0)
|
||||
|
||||
#define MTK_PPE_MIB_SER_R2 0x348
|
||||
#define MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH GENMASK(23, 0)
|
||||
|
||||
#define MTK_PPE_MIB_CACHE_CTL 0x350
|
||||
#define MTK_PPE_MIB_CACHE_CTL_EN BIT(0)
|
||||
#define MTK_PPE_MIB_CACHE_CTL_FLUSH BIT(2)
|
||||
|
|
|
|||
|
|
@ -1,207 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2018-2019 MediaTek Inc.
|
||||
|
||||
/* A library for MediaTek SGMII circuit
|
||||
*
|
||||
* Author: Sean Wang <sean.wang@mediatek.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phylink.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "mtk_eth_soc.h"
|
||||
|
||||
static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs)
|
||||
{
|
||||
return container_of(pcs, struct mtk_pcs, pcs);
|
||||
}
|
||||
|
||||
static void mtk_pcs_get_state(struct phylink_pcs *pcs,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
|
||||
unsigned int bm, adv;
|
||||
|
||||
/* Read the BMSR and LPA */
|
||||
regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
|
||||
regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
|
||||
|
||||
phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm),
|
||||
FIELD_GET(SGMII_LPA, adv));
|
||||
}
|
||||
|
||||
static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
{
|
||||
bool mode_changed = false, changed, use_an;
|
||||
struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
|
||||
unsigned int rgc3, sgm_mode, bmcr;
|
||||
int advertise, link_timer;
|
||||
|
||||
advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
|
||||
advertising);
|
||||
if (advertise < 0)
|
||||
return advertise;
|
||||
|
||||
/* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
|
||||
* we assume that fixes it's speed at bitrate = line rate (in
|
||||
* other words, 1000Mbps or 2500Mbps).
|
||||
*/
|
||||
if (interface == PHY_INTERFACE_MODE_SGMII) {
|
||||
sgm_mode = SGMII_IF_MODE_SGMII;
|
||||
if (phylink_autoneg_inband(mode)) {
|
||||
sgm_mode |= SGMII_REMOTE_FAULT_DIS |
|
||||
SGMII_SPEED_DUPLEX_AN;
|
||||
use_an = true;
|
||||
} else {
|
||||
use_an = false;
|
||||
}
|
||||
} else if (phylink_autoneg_inband(mode)) {
|
||||
/* 1000base-X or 2500base-X autoneg */
|
||||
sgm_mode = SGMII_REMOTE_FAULT_DIS;
|
||||
use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
advertising);
|
||||
} else {
|
||||
/* 1000base-X or 2500base-X without autoneg */
|
||||
sgm_mode = 0;
|
||||
use_an = false;
|
||||
}
|
||||
|
||||
if (use_an) {
|
||||
bmcr = SGMII_AN_ENABLE;
|
||||
} else {
|
||||
bmcr = 0;
|
||||
}
|
||||
|
||||
if (mpcs->interface != interface) {
|
||||
link_timer = phylink_get_link_timer_ns(interface);
|
||||
if (link_timer < 0)
|
||||
return link_timer;
|
||||
|
||||
/* PHYA power down */
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
|
||||
SGMII_PHYA_PWD, SGMII_PHYA_PWD);
|
||||
|
||||
/* Reset SGMII PCS state */
|
||||
regmap_update_bits(mpcs->regmap, SGMII_RESERVED_0,
|
||||
SGMII_SW_RESET, SGMII_SW_RESET);
|
||||
|
||||
if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
||||
rgc3 = RG_PHY_SPEED_3_125G;
|
||||
else
|
||||
rgc3 = 0;
|
||||
|
||||
/* Configure the underlying interface speed */
|
||||
regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
|
||||
RG_PHY_SPEED_3_125G, rgc3);
|
||||
|
||||
/* Setup the link timer */
|
||||
regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8);
|
||||
|
||||
mpcs->interface = interface;
|
||||
mode_changed = true;
|
||||
}
|
||||
|
||||
/* Update the advertisement, noting whether it has changed */
|
||||
regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
|
||||
SGMII_ADVERTISE, advertise, &changed);
|
||||
|
||||
/* Update the sgmsys mode register */
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
|
||||
SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
|
||||
SGMII_IF_MODE_SGMII, sgm_mode);
|
||||
|
||||
/* Update the BMCR */
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
|
||||
SGMII_AN_ENABLE, bmcr);
|
||||
|
||||
/* Release PHYA power down state
|
||||
* Only removing bit SGMII_PHYA_PWD isn't enough.
|
||||
* There are cases when the SGMII_PHYA_PWD register contains 0x9 which
|
||||
* prevents SGMII from working. The SGMII still shows link but no traffic
|
||||
* can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
|
||||
* taken from a good working state of the SGMII interface.
|
||||
* Unknown how much the QPHY needs but it is racy without a sleep.
|
||||
* Tested on mt7622 & mt7986.
|
||||
*/
|
||||
usleep_range(50, 100);
|
||||
regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
|
||||
|
||||
return changed || mode_changed;
|
||||
}
|
||||
|
||||
static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
|
||||
{
|
||||
struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
|
||||
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
|
||||
SGMII_AN_RESTART, SGMII_AN_RESTART);
|
||||
}
|
||||
|
||||
static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
phy_interface_t interface, int speed, int duplex)
|
||||
{
|
||||
struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
|
||||
unsigned int sgm_mode;
|
||||
|
||||
if (!phylink_autoneg_inband(mode)) {
|
||||
/* Force the speed and duplex setting */
|
||||
if (speed == SPEED_10)
|
||||
sgm_mode = SGMII_SPEED_10;
|
||||
else if (speed == SPEED_100)
|
||||
sgm_mode = SGMII_SPEED_100;
|
||||
else
|
||||
sgm_mode = SGMII_SPEED_1000;
|
||||
|
||||
if (duplex != DUPLEX_FULL)
|
||||
sgm_mode |= SGMII_DUPLEX_HALF;
|
||||
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
|
||||
SGMII_DUPLEX_HALF | SGMII_SPEED_MASK,
|
||||
sgm_mode);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct phylink_pcs_ops mtk_pcs_ops = {
|
||||
.pcs_get_state = mtk_pcs_get_state,
|
||||
.pcs_config = mtk_pcs_config,
|
||||
.pcs_an_restart = mtk_pcs_restart_an,
|
||||
.pcs_link_up = mtk_pcs_link_up,
|
||||
};
|
||||
|
||||
int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
|
||||
{
|
||||
struct device_node *np;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MTK_MAX_DEVS; i++) {
|
||||
np = of_parse_phandle(r, "mediatek,sgmiisys", i);
|
||||
if (!np)
|
||||
break;
|
||||
|
||||
ss->pcs[i].ana_rgc3 = ana_rgc3;
|
||||
ss->pcs[i].regmap = syscon_node_to_regmap(np);
|
||||
of_node_put(np);
|
||||
if (IS_ERR(ss->pcs[i].regmap))
|
||||
return PTR_ERR(ss->pcs[i].regmap);
|
||||
|
||||
ss->pcs[i].pcs.ops = &mtk_pcs_ops;
|
||||
ss->pcs[i].pcs.poll = true;
|
||||
ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id)
|
||||
{
|
||||
if (!ss->pcs[id].regmap)
|
||||
return NULL;
|
||||
|
||||
return &ss->pcs[id].pcs;
|
||||
}
|
||||
|
|
@ -18,6 +18,13 @@ config PCS_LYNX
|
|||
This module provides helpers to phylink for managing the Lynx PCS
|
||||
which is part of the Layerscape and QorIQ Ethernet SERDES.
|
||||
|
||||
config PCS_MTK_LYNXI
|
||||
tristate
|
||||
select REGMAP
|
||||
help
|
||||
This module provides helpers to phylink for managing the LynxI PCS
|
||||
which is part of MediaTek's SoC and Ethernet switch ICs.
|
||||
|
||||
config PCS_RZN1_MIIC
|
||||
tristate "Renesas RZ/N1 MII converter"
|
||||
depends on OF && (ARCH_RZN1 || COMPILE_TEST)
|
||||
|
|
|
|||
|
|
@ -5,5 +5,6 @@ pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-nxp.o
|
|||
|
||||
obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o
|
||||
obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o
|
||||
obj-$(CONFIG_PCS_MTK_LYNXI) += pcs-mtk-lynxi.o
|
||||
obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o
|
||||
obj-$(CONFIG_PCS_ALTERA_TSE) += pcs-altera-tse.o
|
||||
|
|
|
|||
305
drivers/net/pcs/pcs-mtk-lynxi.c
Normal file
305
drivers/net/pcs/pcs-mtk-lynxi.c
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2018-2019 MediaTek Inc.
|
||||
/* A library for MediaTek SGMII circuit
|
||||
*
|
||||
* Author: Sean Wang <sean.wang@mediatek.com>
|
||||
* Author: Alexander Couzens <lynxis@fe80.eu>
|
||||
* Author: Daniel Golle <daniel@makrotopia.org>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/mdio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pcs/pcs-mtk-lynxi.h>
|
||||
#include <linux/phylink.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* SGMII subsystem config registers */
|
||||
/* BMCR (low 16) BMSR (high 16) */
|
||||
#define SGMSYS_PCS_CONTROL_1 0x0
|
||||
#define SGMII_BMCR GENMASK(15, 0)
|
||||
#define SGMII_BMSR GENMASK(31, 16)
|
||||
|
||||
#define SGMSYS_PCS_DEVICE_ID 0x4
|
||||
#define SGMII_LYNXI_DEV_ID 0x4d544950
|
||||
|
||||
#define SGMSYS_PCS_ADVERTISE 0x8
|
||||
#define SGMII_ADVERTISE GENMASK(15, 0)
|
||||
#define SGMII_LPA GENMASK(31, 16)
|
||||
|
||||
#define SGMSYS_PCS_SCRATCH 0x14
|
||||
#define SGMII_DEV_VERSION GENMASK(31, 16)
|
||||
|
||||
/* Register to programmable link timer, the unit in 2 * 8ns */
|
||||
#define SGMSYS_PCS_LINK_TIMER 0x18
|
||||
#define SGMII_LINK_TIMER_MASK GENMASK(19, 0)
|
||||
#define SGMII_LINK_TIMER_VAL(ns) FIELD_PREP(SGMII_LINK_TIMER_MASK, \
|
||||
((ns) / 2 / 8))
|
||||
|
||||
/* Register to control remote fault */
|
||||
#define SGMSYS_SGMII_MODE 0x20
|
||||
#define SGMII_IF_MODE_SGMII BIT(0)
|
||||
#define SGMII_SPEED_DUPLEX_AN BIT(1)
|
||||
#define SGMII_SPEED_MASK GENMASK(3, 2)
|
||||
#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
|
||||
#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1)
|
||||
#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
|
||||
#define SGMII_DUPLEX_HALF BIT(4)
|
||||
#define SGMII_REMOTE_FAULT_DIS BIT(8)
|
||||
|
||||
/* Register to reset SGMII design */
|
||||
#define SGMSYS_RESERVED_0 0x34
|
||||
#define SGMII_SW_RESET BIT(0)
|
||||
|
||||
/* Register to set SGMII speed, ANA RG_ Control Signals III */
|
||||
#define SGMII_PHY_SPEED_MASK GENMASK(3, 2)
|
||||
#define SGMII_PHY_SPEED_1_25G FIELD_PREP(SGMII_PHY_SPEED_MASK, 0)
|
||||
#define SGMII_PHY_SPEED_3_125G FIELD_PREP(SGMII_PHY_SPEED_MASK, 1)
|
||||
|
||||
/* Register to power up QPHY */
|
||||
#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8
|
||||
#define SGMII_PHYA_PWD BIT(4)
|
||||
|
||||
/* Register to QPHY wrapper control */
|
||||
#define SGMSYS_QPHY_WRAP_CTRL 0xec
|
||||
#define SGMII_PN_SWAP_MASK GENMASK(1, 0)
|
||||
#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1))
|
||||
|
||||
/* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated
|
||||
* data
|
||||
* @regmap: The register map pointing at the range used to setup
|
||||
* SGMII modes
|
||||
* @dev: Pointer to device owning the PCS
|
||||
* @ana_rgc3: The offset of register ANA_RGC3 relative to regmap
|
||||
* @interface: Currently configured interface mode
|
||||
* @pcs: Phylink PCS structure
|
||||
* @flags: Flags indicating hardware properties
|
||||
*/
|
||||
struct mtk_pcs_lynxi {
|
||||
struct regmap *regmap;
|
||||
u32 ana_rgc3;
|
||||
phy_interface_t interface;
|
||||
struct phylink_pcs pcs;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs)
|
||||
{
|
||||
return container_of(pcs, struct mtk_pcs_lynxi, pcs);
|
||||
}
|
||||
|
||||
static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
|
||||
unsigned int bm, adv;
|
||||
|
||||
/* Read the BMSR and LPA */
|
||||
regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
|
||||
regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
|
||||
|
||||
phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm),
|
||||
FIELD_GET(SGMII_LPA, adv));
|
||||
}
|
||||
|
||||
static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
{
|
||||
struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
|
||||
bool mode_changed = false, changed, use_an;
|
||||
unsigned int rgc3, sgm_mode, bmcr;
|
||||
int advertise, link_timer;
|
||||
|
||||
advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
|
||||
advertising);
|
||||
if (advertise < 0)
|
||||
return advertise;
|
||||
|
||||
/* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
|
||||
* we assume that fixes it's speed at bitrate = line rate (in
|
||||
* other words, 1000Mbps or 2500Mbps).
|
||||
*/
|
||||
if (interface == PHY_INTERFACE_MODE_SGMII) {
|
||||
sgm_mode = SGMII_IF_MODE_SGMII;
|
||||
if (phylink_autoneg_inband(mode)) {
|
||||
sgm_mode |= SGMII_REMOTE_FAULT_DIS |
|
||||
SGMII_SPEED_DUPLEX_AN;
|
||||
use_an = true;
|
||||
} else {
|
||||
use_an = false;
|
||||
}
|
||||
} else if (phylink_autoneg_inband(mode)) {
|
||||
/* 1000base-X or 2500base-X autoneg */
|
||||
sgm_mode = SGMII_REMOTE_FAULT_DIS;
|
||||
use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
advertising);
|
||||
} else {
|
||||
/* 1000base-X or 2500base-X without autoneg */
|
||||
sgm_mode = 0;
|
||||
use_an = false;
|
||||
}
|
||||
|
||||
if (use_an)
|
||||
bmcr = BMCR_ANENABLE;
|
||||
else
|
||||
bmcr = 0;
|
||||
|
||||
if (mpcs->interface != interface) {
|
||||
link_timer = phylink_get_link_timer_ns(interface);
|
||||
if (link_timer < 0)
|
||||
return link_timer;
|
||||
|
||||
/* PHYA power down */
|
||||
regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
|
||||
SGMII_PHYA_PWD);
|
||||
|
||||
/* Reset SGMII PCS state */
|
||||
regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0,
|
||||
SGMII_SW_RESET);
|
||||
|
||||
if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP)
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
|
||||
SGMII_PN_SWAP_MASK,
|
||||
SGMII_PN_SWAP_TX_RX);
|
||||
|
||||
if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
||||
rgc3 = SGMII_PHY_SPEED_3_125G;
|
||||
else
|
||||
rgc3 = SGMII_PHY_SPEED_1_25G;
|
||||
|
||||
/* Configure the underlying interface speed */
|
||||
regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
|
||||
SGMII_PHY_SPEED_MASK, rgc3);
|
||||
|
||||
/* Setup the link timer */
|
||||
regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
|
||||
SGMII_LINK_TIMER_VAL(link_timer));
|
||||
|
||||
mpcs->interface = interface;
|
||||
mode_changed = true;
|
||||
}
|
||||
|
||||
/* Update the advertisement, noting whether it has changed */
|
||||
regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
|
||||
SGMII_ADVERTISE, advertise, &changed);
|
||||
|
||||
/* Update the sgmsys mode register */
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
|
||||
SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
|
||||
SGMII_IF_MODE_SGMII, sgm_mode);
|
||||
|
||||
/* Update the BMCR */
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
|
||||
BMCR_ANENABLE, bmcr);
|
||||
|
||||
/* Release PHYA power down state
|
||||
* Only removing bit SGMII_PHYA_PWD isn't enough.
|
||||
* There are cases when the SGMII_PHYA_PWD register contains 0x9 which
|
||||
* prevents SGMII from working. The SGMII still shows link but no traffic
|
||||
* can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
|
||||
* taken from a good working state of the SGMII interface.
|
||||
* Unknown how much the QPHY needs but it is racy without a sleep.
|
||||
* Tested on mt7622 & mt7986.
|
||||
*/
|
||||
usleep_range(50, 100);
|
||||
regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
|
||||
|
||||
return changed || mode_changed;
|
||||
}
|
||||
|
||||
static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs)
|
||||
{
|
||||
struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
|
||||
|
||||
regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART);
|
||||
}
|
||||
|
||||
static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
phy_interface_t interface, int speed,
|
||||
int duplex)
|
||||
{
|
||||
struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
|
||||
unsigned int sgm_mode;
|
||||
|
||||
if (!phylink_autoneg_inband(mode)) {
|
||||
/* Force the speed and duplex setting */
|
||||
if (speed == SPEED_10)
|
||||
sgm_mode = SGMII_SPEED_10;
|
||||
else if (speed == SPEED_100)
|
||||
sgm_mode = SGMII_SPEED_100;
|
||||
else
|
||||
sgm_mode = SGMII_SPEED_1000;
|
||||
|
||||
if (duplex != DUPLEX_FULL)
|
||||
sgm_mode |= SGMII_DUPLEX_HALF;
|
||||
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
|
||||
SGMII_DUPLEX_HALF | SGMII_SPEED_MASK,
|
||||
sgm_mode);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = {
|
||||
.pcs_get_state = mtk_pcs_lynxi_get_state,
|
||||
.pcs_config = mtk_pcs_lynxi_config,
|
||||
.pcs_an_restart = mtk_pcs_lynxi_restart_an,
|
||||
.pcs_link_up = mtk_pcs_lynxi_link_up,
|
||||
};
|
||||
|
||||
struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
|
||||
struct regmap *regmap, u32 ana_rgc3,
|
||||
u32 flags)
|
||||
{
|
||||
struct mtk_pcs_lynxi *mpcs;
|
||||
u32 id, ver;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
if (id != SGMII_LYNXI_DEV_ID) {
|
||||
dev_err(dev, "unknown PCS device id %08x\n", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
ver = FIELD_GET(SGMII_DEV_VERSION, ver);
|
||||
if (ver != 0x1) {
|
||||
dev_err(dev, "unknown PCS device version %04x\n", ver);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id,
|
||||
ver);
|
||||
|
||||
mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
|
||||
if (!mpcs)
|
||||
return NULL;
|
||||
|
||||
mpcs->ana_rgc3 = ana_rgc3;
|
||||
mpcs->regmap = regmap;
|
||||
mpcs->flags = flags;
|
||||
mpcs->pcs.ops = &mtk_pcs_lynxi_ops;
|
||||
mpcs->pcs.poll = true;
|
||||
mpcs->interface = PHY_INTERFACE_MODE_NA;
|
||||
|
||||
return &mpcs->pcs;
|
||||
}
|
||||
EXPORT_SYMBOL(mtk_pcs_lynxi_create);
|
||||
|
||||
void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs)
|
||||
{
|
||||
if (!pcs)
|
||||
return;
|
||||
|
||||
kfree(pcs_to_mtk_pcs_lynxi(pcs));
|
||||
}
|
||||
EXPORT_SYMBOL(mtk_pcs_lynxi_destroy);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
13
include/linux/pcs/pcs-mtk-lynxi.h
Normal file
13
include/linux/pcs/pcs-mtk-lynxi.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __LINUX_PCS_MTK_LYNXI_H
|
||||
#define __LINUX_PCS_MTK_LYNXI_H
|
||||
|
||||
#include <linux/phylink.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define MTK_SGMII_FLAG_PN_SWAP BIT(0)
|
||||
struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
|
||||
struct regmap *regmap,
|
||||
u32 ana_rgc3, u32 flags);
|
||||
void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs);
|
||||
#endif
|
||||
Loading…
Reference in New Issue
Block a user