Commit 134d8368 authored by Jakub Kicinski's avatar Jakub Kicinski

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.orgSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 56aecc0a 5b89aeae
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
......
# 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,
......
This diff is collapsed.
......@@ -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(&eth->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(&eth->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)
......
......@@ -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
// 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/mfd/syscon.h>
#include <linux/mdio.h>
#include <linux/of.h>
#include <linux/pcs/pcs-mtk-lynxi.h>
#include <linux/phylink.h>
#include <linux/regmap.h>
#include "mtk_eth_soc.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 *pcs_to_mtk_pcs(struct phylink_pcs *pcs)
static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs)
{
return container_of(pcs, struct mtk_pcs, pcs);
return container_of(pcs, struct mtk_pcs_lynxi, pcs);
}
static void mtk_pcs_get_state(struct phylink_pcs *pcs,
struct phylink_link_state *state)
static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs,
struct phylink_link_state *state)
{
struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
unsigned int bm, adv;
/* Read the BMSR and LPA */
......@@ -33,13 +102,13 @@ static void mtk_pcs_get_state(struct phylink_pcs *pcs,
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)
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;
struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
unsigned int rgc3, sgm_mode, bmcr;
int advertise, link_timer;
......@@ -72,11 +141,10 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
use_an = false;
}
if (use_an) {
bmcr = SGMII_AN_ENABLE;
} else {
if (use_an)
bmcr = BMCR_ANENABLE;
else
bmcr = 0;
}
if (mpcs->interface != interface) {
link_timer = phylink_get_link_timer_ns(interface);
......@@ -84,24 +152,30 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
return link_timer;
/* PHYA power down */
regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
SGMII_PHYA_PWD, SGMII_PHYA_PWD);
regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
SGMII_PHYA_PWD);
/* Reset SGMII PCS state */
regmap_update_bits(mpcs->regmap, SGMII_RESERVED_0,
SGMII_SW_RESET, SGMII_SW_RESET);
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 = RG_PHY_SPEED_3_125G;
rgc3 = SGMII_PHY_SPEED_3_125G;
else
rgc3 = 0;
rgc3 = SGMII_PHY_SPEED_1_25G;
/* Configure the underlying interface speed */
regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
RG_PHY_SPEED_3_125G, rgc3);
SGMII_PHY_SPEED_MASK, rgc3);
/* Setup the link timer */
regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8);
regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
SGMII_LINK_TIMER_VAL(link_timer));
mpcs->interface = interface;
mode_changed = true;
......@@ -118,7 +192,7 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
/* Update the BMCR */
regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
SGMII_AN_ENABLE, bmcr);
BMCR_ANENABLE, bmcr);
/* Release PHYA power down state
* Only removing bit SGMII_PHYA_PWD isn't enough.
......@@ -135,18 +209,18 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
return changed || mode_changed;
}
static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs)
{
struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
SGMII_AN_RESTART, SGMII_AN_RESTART);
regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART);
}
static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
phy_interface_t interface, int speed, int duplex)
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 *mpcs = pcs_to_mtk_pcs(pcs);
struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
unsigned int sgm_mode;
if (!phylink_autoneg_inband(mode)) {
......@@ -167,41 +241,65 @@ static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int 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,
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,
};
int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
struct regmap *regmap, u32 ana_rgc3,
u32 flags)
{
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;
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;
}
return 0;
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);
struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id)
void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs)
{
if (!ss->pcs[id].regmap)
return NULL;
if (!pcs)
return;
return &ss->pcs[id].pcs;
kfree(pcs_to_mtk_pcs_lynxi(pcs));
}
EXPORT_SYMBOL(mtk_pcs_lynxi_destroy);
MODULE_LICENSE("GPL");
/* 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment