Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.4-rc5 160 lines 4.3 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2022 Bootlin 4 * 5 * Maxime Chevallier <maxime.chevallier@bootlin.com> 6 */ 7 8#include <linux/netdevice.h> 9#include <linux/phy.h> 10#include <linux/phylink.h> 11#include <linux/pcs-altera-tse.h> 12 13/* SGMII PCS register addresses 14 */ 15#define SGMII_PCS_LINK_TIMER_0 0x12 16#define SGMII_PCS_LINK_TIMER_1 0x13 17#define SGMII_PCS_IF_MODE 0x14 18#define PCS_IF_MODE_SGMII_ENA BIT(0) 19#define PCS_IF_MODE_USE_SGMII_AN BIT(1) 20#define PCS_IF_MODE_SGMI_HALF_DUPLEX BIT(4) 21#define PCS_IF_MODE_SGMI_PHY_AN BIT(5) 22#define SGMII_PCS_SW_RESET_TIMEOUT 100 /* usecs */ 23 24struct altera_tse_pcs { 25 struct phylink_pcs pcs; 26 void __iomem *base; 27 int reg_width; 28}; 29 30static struct altera_tse_pcs *phylink_pcs_to_tse_pcs(struct phylink_pcs *pcs) 31{ 32 return container_of(pcs, struct altera_tse_pcs, pcs); 33} 34 35static u16 tse_pcs_read(struct altera_tse_pcs *tse_pcs, int regnum) 36{ 37 if (tse_pcs->reg_width == 4) 38 return readl(tse_pcs->base + regnum * 4); 39 else 40 return readw(tse_pcs->base + regnum * 2); 41} 42 43static void tse_pcs_write(struct altera_tse_pcs *tse_pcs, int regnum, 44 u16 value) 45{ 46 if (tse_pcs->reg_width == 4) 47 writel(value, tse_pcs->base + regnum * 4); 48 else 49 writew(value, tse_pcs->base + regnum * 2); 50} 51 52static int tse_pcs_reset(struct altera_tse_pcs *tse_pcs) 53{ 54 u16 bmcr; 55 56 /* Reset PCS block */ 57 bmcr = tse_pcs_read(tse_pcs, MII_BMCR); 58 bmcr |= BMCR_RESET; 59 tse_pcs_write(tse_pcs, MII_BMCR, bmcr); 60 61 return read_poll_timeout(tse_pcs_read, bmcr, (bmcr & BMCR_RESET), 62 10, SGMII_PCS_SW_RESET_TIMEOUT, 1, 63 tse_pcs, MII_BMCR); 64} 65 66static int alt_tse_pcs_validate(struct phylink_pcs *pcs, 67 unsigned long *supported, 68 const struct phylink_link_state *state) 69{ 70 if (state->interface == PHY_INTERFACE_MODE_SGMII || 71 state->interface == PHY_INTERFACE_MODE_1000BASEX) 72 return 1; 73 74 return -EINVAL; 75} 76 77static int alt_tse_pcs_config(struct phylink_pcs *pcs, unsigned int mode, 78 phy_interface_t interface, 79 const unsigned long *advertising, 80 bool permit_pause_to_mac) 81{ 82 struct altera_tse_pcs *tse_pcs = phylink_pcs_to_tse_pcs(pcs); 83 u32 ctrl, if_mode; 84 85 ctrl = tse_pcs_read(tse_pcs, MII_BMCR); 86 if_mode = tse_pcs_read(tse_pcs, SGMII_PCS_IF_MODE); 87 88 /* Set link timer to 1.6ms, as per the MegaCore Function User Guide */ 89 tse_pcs_write(tse_pcs, SGMII_PCS_LINK_TIMER_0, 0x0D40); 90 tse_pcs_write(tse_pcs, SGMII_PCS_LINK_TIMER_1, 0x03); 91 92 if (interface == PHY_INTERFACE_MODE_SGMII) { 93 if_mode |= PCS_IF_MODE_USE_SGMII_AN | PCS_IF_MODE_SGMII_ENA; 94 } else if (interface == PHY_INTERFACE_MODE_1000BASEX) { 95 if_mode &= ~(PCS_IF_MODE_USE_SGMII_AN | PCS_IF_MODE_SGMII_ENA); 96 } 97 98 ctrl |= (BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_ANENABLE); 99 100 tse_pcs_write(tse_pcs, MII_BMCR, ctrl); 101 tse_pcs_write(tse_pcs, SGMII_PCS_IF_MODE, if_mode); 102 103 return tse_pcs_reset(tse_pcs); 104} 105 106static void alt_tse_pcs_get_state(struct phylink_pcs *pcs, 107 struct phylink_link_state *state) 108{ 109 struct altera_tse_pcs *tse_pcs = phylink_pcs_to_tse_pcs(pcs); 110 u16 bmsr, lpa; 111 112 bmsr = tse_pcs_read(tse_pcs, MII_BMSR); 113 lpa = tse_pcs_read(tse_pcs, MII_LPA); 114 115 phylink_mii_c22_pcs_decode_state(state, bmsr, lpa); 116} 117 118static void alt_tse_pcs_an_restart(struct phylink_pcs *pcs) 119{ 120 struct altera_tse_pcs *tse_pcs = phylink_pcs_to_tse_pcs(pcs); 121 u16 bmcr; 122 123 bmcr = tse_pcs_read(tse_pcs, MII_BMCR); 124 bmcr |= BMCR_ANRESTART; 125 tse_pcs_write(tse_pcs, MII_BMCR, bmcr); 126 127 /* This PCS seems to require a soft reset to re-sync the AN logic */ 128 tse_pcs_reset(tse_pcs); 129} 130 131static const struct phylink_pcs_ops alt_tse_pcs_ops = { 132 .pcs_validate = alt_tse_pcs_validate, 133 .pcs_get_state = alt_tse_pcs_get_state, 134 .pcs_config = alt_tse_pcs_config, 135 .pcs_an_restart = alt_tse_pcs_an_restart, 136}; 137 138struct phylink_pcs *alt_tse_pcs_create(struct net_device *ndev, 139 void __iomem *pcs_base, int reg_width) 140{ 141 struct altera_tse_pcs *tse_pcs; 142 143 if (reg_width != 4 && reg_width != 2) 144 return ERR_PTR(-EINVAL); 145 146 tse_pcs = devm_kzalloc(&ndev->dev, sizeof(*tse_pcs), GFP_KERNEL); 147 if (!tse_pcs) 148 return ERR_PTR(-ENOMEM); 149 150 tse_pcs->pcs.ops = &alt_tse_pcs_ops; 151 tse_pcs->base = pcs_base; 152 tse_pcs->reg_width = reg_width; 153 154 return &tse_pcs->pcs; 155} 156EXPORT_SYMBOL_GPL(alt_tse_pcs_create); 157 158MODULE_LICENSE("GPL"); 159MODULE_DESCRIPTION("Altera TSE PCS driver"); 160MODULE_AUTHOR("Maxime Chevallier <maxime.chevallier@bootlin.com>");