cregit-Linux how code gets into the kernel

Release 4.11 drivers/net/phy/at803x.c

Directory: drivers/net/phy
/*
 * drivers/net/phy/at803x.c
 *
 * Driver for Atheros 803x PHY
 *
 * Author: Matus Ujhelyi <ujhelyi.m@gmail.com>
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 */

#include <linux/phy.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>


#define AT803X_INTR_ENABLE			0x12

#define AT803X_INTR_ENABLE_AUTONEG_ERR		BIT(15)

#define AT803X_INTR_ENABLE_SPEED_CHANGED	BIT(14)

#define AT803X_INTR_ENABLE_DUPLEX_CHANGED	BIT(13)

#define AT803X_INTR_ENABLE_PAGE_RECEIVED	BIT(12)

#define AT803X_INTR_ENABLE_LINK_FAIL		BIT(11)

#define AT803X_INTR_ENABLE_LINK_SUCCESS		BIT(10)

#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE	BIT(5)

#define AT803X_INTR_ENABLE_POLARITY_CHANGED	BIT(1)

#define AT803X_INTR_ENABLE_WOL			BIT(0)


#define AT803X_INTR_STATUS			0x13


#define AT803X_SMART_SPEED			0x14

#define AT803X_LED_CONTROL			0x18


#define AT803X_DEVICE_ADDR			0x03

#define AT803X_LOC_MAC_ADDR_0_15_OFFSET		0x804C

#define AT803X_LOC_MAC_ADDR_16_31_OFFSET	0x804B

#define AT803X_LOC_MAC_ADDR_32_47_OFFSET	0x804A

#define AT803X_MMD_ACCESS_CONTROL		0x0D

#define AT803X_MMD_ACCESS_CONTROL_DATA		0x0E

#define AT803X_FUNC_DATA			0x4003

#define AT803X_REG_CHIP_CONFIG			0x1f

#define AT803X_BT_BX_REG_SEL			0x8000


#define AT803X_DEBUG_ADDR			0x1D

#define AT803X_DEBUG_DATA			0x1E


#define AT803X_MODE_CFG_MASK			0x0F

#define AT803X_MODE_CFG_SGMII			0x01


#define AT803X_PSSR			0x11	
/*PHY-Specific Status Register*/

#define AT803X_PSSR_MR_AN_COMPLETE	0x0200


#define AT803X_DEBUG_REG_0			0x00

#define AT803X_DEBUG_RX_CLK_DLY_EN		BIT(15)


#define AT803X_DEBUG_REG_5			0x05

#define AT803X_DEBUG_TX_CLK_DLY_EN		BIT(8)


#define ATH8030_PHY_ID 0x004dd076

#define ATH8031_PHY_ID 0x004dd074

#define ATH8035_PHY_ID 0x004dd072

#define AT803X_PHY_ID_MASK			0xffffffef

MODULE_DESCRIPTION("Atheros 803x PHY driver");
MODULE_AUTHOR("Matus Ujhelyi");
MODULE_LICENSE("GPL");


struct at803x_priv {
	
bool phy_reset:1;
	
struct gpio_desc *gpiod_reset;
};


struct at803x_context {
	
u16 bmcr;
	
u16 advertise;
	
u16 control1000;
	
u16 int_enable;
	
u16 smart_speed;
	
u16 led_control;
};


static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) { int ret; ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); if (ret < 0) return ret; return phy_read(phydev, AT803X_DEBUG_DATA); }

Contributors

PersonTokensPropCommitsCommitProp
Martin Blumenstingl45100.00%1100.00%
Total45100.00%1100.00%


static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, u16 clear, u16 set) { u16 val; int ret; ret = at803x_debug_reg_read(phydev, reg); if (ret < 0) return ret; val = ret & 0xffff; val &= ~clear; val |= set; return phy_write(phydev, AT803X_DEBUG_DATA, val); }

Contributors

PersonTokensPropCommitsCommitProp
Martin Blumenstingl69100.00%1100.00%
Total69100.00%1100.00%


static inline int at803x_enable_rx_delay(struct phy_device *phydev) { return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0, AT803X_DEBUG_RX_CLK_DLY_EN); }

Contributors

PersonTokensPropCommitsCommitProp
Martin Blumenstingl24100.00%1100.00%
Total24100.00%1100.00%


static inline int at803x_enable_tx_delay(struct phy_device *phydev) { return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0, AT803X_DEBUG_TX_CLK_DLY_EN); }

Contributors

PersonTokensPropCommitsCommitProp
Martin Blumenstingl24100.00%1100.00%
Total24100.00%1100.00%

/* save relevant PHY registers to private copy */
static void at803x_context_save(struct phy_device *phydev, struct at803x_context *context) { context->bmcr = phy_read(phydev, MII_BMCR); context->advertise = phy_read(phydev, MII_ADVERTISE); context->control1000 = phy_read(phydev, MII_CTRL1000); context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE); context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED); context->led_control = phy_read(phydev, AT803X_LED_CONTROL); }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Mack82100.00%1100.00%
Total82100.00%1100.00%

/* restore relevant PHY registers from private copy */
static void at803x_context_restore(struct phy_device *phydev, const struct at803x_context *context) { phy_write(phydev, MII_BMCR, context->bmcr); phy_write(phydev, MII_ADVERTISE, context->advertise); phy_write(phydev, MII_CTRL1000, context->control1000); phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable); phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed); phy_write(phydev, AT803X_LED_CONTROL, context->led_control); }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Mack83100.00%1100.00%
Total83100.00%1100.00%


static int at803x_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) { struct net_device *ndev = phydev->attached_dev; const u8 *mac; int ret; u32 value; unsigned int i, offsets[] = { AT803X_LOC_MAC_ADDR_32_47_OFFSET, AT803X_LOC_MAC_ADDR_16_31_OFFSET, AT803X_LOC_MAC_ADDR_0_15_OFFSET, }; if (!ndev) return -ENODEV; if (wol->wolopts & WAKE_MAGIC) { mac = (const u8 *) ndev->dev_addr; if (!is_valid_ether_addr(mac)) return -EFAULT; for (i = 0; i < 3; i++) { phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, AT803X_DEVICE_ADDR); phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, offsets[i]); phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, AT803X_FUNC_DATA); phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); } value = phy_read(phydev, AT803X_INTR_ENABLE); value |= AT803X_INTR_ENABLE_WOL; ret = phy_write(phydev, AT803X_INTR_ENABLE, value); if (ret) return ret; value = phy_read(phydev, AT803X_INTR_STATUS); } else { value = phy_read(phydev, AT803X_INTR_ENABLE); value &= (~AT803X_INTR_ENABLE_WOL); ret = phy_write(phydev, AT803X_INTR_ENABLE, value); if (ret) return ret; value = phy_read(phydev, AT803X_INTR_STATUS); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Matus Ujhelyi13753.10%133.33%
Mugunthan V N11946.12%133.33%
Martin Blumenstingl20.78%133.33%
Total258100.00%3100.00%


static void at803x_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) { u32 value; wol->supported = WAKE_MAGIC; wol->wolopts = 0; value = phy_read(phydev, AT803X_INTR_ENABLE); if (value & AT803X_INTR_ENABLE_WOL) wol->wolopts |= WAKE_MAGIC; }

Contributors

PersonTokensPropCommitsCommitProp
Mugunthan V N5096.15%133.33%
Martin Blumenstingl11.92%133.33%
Matus Ujhelyi11.92%133.33%
Total52100.00%3100.00%


static int at803x_suspend(struct phy_device *phydev) { int value; int wol_enabled; mutex_lock(&phydev->lock); value = phy_read(phydev, AT803X_INTR_ENABLE); wol_enabled = value & AT803X_INTR_ENABLE_WOL; value = phy_read(phydev, MII_BMCR); if (wol_enabled) value |= BMCR_ISOLATE; else value |= BMCR_PDOWN; phy_write(phydev, MII_BMCR, value); mutex_unlock(&phydev->lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Mack8198.78%150.00%
Martin Blumenstingl11.22%150.00%
Total82100.00%2100.00%


static int at803x_resume(struct phy_device *phydev) { int value; mutex_lock(&phydev->lock); value = phy_read(phydev, MII_BMCR); value &= ~(BMCR_PDOWN | BMCR_ISOLATE); phy_write(phydev, MII_BMCR, value); mutex_unlock(&phydev->lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Mack60100.00%1100.00%
Total60100.00%1100.00%


static int at803x_probe(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; struct at803x_priv *priv; struct gpio_desc *gpiod_reset; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; if (phydev->drv->phy_id != ATH8030_PHY_ID) goto does_not_require_reset_workaround; gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(gpiod_reset)) return PTR_ERR(gpiod_reset); priv->gpiod_reset = gpiod_reset; does_not_require_reset_workaround: phydev->priv = priv; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Mack7971.17%116.67%
Sebastian Frias1513.51%116.67%
Uwe Kleine-König1311.71%116.67%
Andrew Lunn21.80%116.67%
Sergei Shtylyov10.90%116.67%
Fengguang Wu10.90%116.67%
Total111100.00%6100.00%


static int at803x_config_init(struct phy_device *phydev) { int ret; ret = genphy_config_init(phydev); if (ret < 0) return ret; if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID || phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { ret = at803x_enable_rx_delay(phydev); if (ret < 0) return ret; } if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID || phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { ret = at803x_enable_tx_delay(phydev); if (ret < 0) return ret; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Mugunthan V N3536.08%125.00%
Matus Ujhelyi2929.90%125.00%
Martin Blumenstingl2929.90%125.00%
Daniel Mack44.12%125.00%
Total97100.00%4100.00%


static int at803x_ack_interrupt(struct phy_device *phydev) { int err; err = phy_read(phydev, AT803X_INTR_STATUS); return (err < 0) ? err : 0; }

Contributors

PersonTokensPropCommitsCommitProp
Zhao Qiang3397.06%150.00%
Martin Blumenstingl12.94%150.00%
Total34100.00%2100.00%


static int at803x_config_intr(struct phy_device *phydev) { int err; int value; value = phy_read(phydev, AT803X_INTR_ENABLE); if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { value |= AT803X_INTR_ENABLE_AUTONEG_ERR; value |= AT803X_INTR_ENABLE_SPEED_CHANGED; value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; value |= AT803X_INTR_ENABLE_LINK_FAIL; value |= AT803X_INTR_ENABLE_LINK_SUCCESS; err = phy_write(phydev, AT803X_INTR_ENABLE, value); } else err = phy_write(phydev, AT803X_INTR_ENABLE, 0); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Zhao Qiang5769.51%133.33%
Martin Blumenstingl2530.49%266.67%
Total82100.00%3100.00%


static void at803x_link_change_notify(struct phy_device *phydev) { struct at803x_priv *priv = phydev->priv; /* * Conduct a hardware reset for AT8030 every time a link loss is * signalled. This is necessary to circumvent a hardware bug that * occurs when the cable is unplugged while TX packets are pending * in the FIFO. In such cases, the FIFO enters an error mode it * cannot recover from by software. */ if (phydev->state == PHY_NOLINK) { if (priv->gpiod_reset && !priv->phy_reset) { struct at803x_context context; at803x_context_save(phydev, &context); gpiod_set_value(priv->gpiod_reset, 1); msleep(1); gpiod_set_value(priv->gpiod_reset, 0); msleep(1); at803x_context_restore(phydev, &context); phydev_dbg(phydev, "%s(): phy was reset\n", __func__); priv->phy_reset = true; } } else { priv->phy_reset = false; } }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Mack11397.41%133.33%
Sergei Shtylyov21.72%133.33%
Andrew Lunn10.86%133.33%
Total116100.00%3100.00%


static int at803x_aneg_done(struct phy_device *phydev) { int ccr; int aneg_done = genphy_aneg_done(phydev); if (aneg_done != BMSR_ANEGCOMPLETE) return aneg_done; /* * in SGMII mode, if copper side autoneg is successful, * also check SGMII side autoneg result */ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); if ((ccr & AT803X_MODE_CFG_MASK) != AT803X_MODE_CFG_SGMII) return aneg_done; /* switch to SGMII/fiber page */ phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL); /* check if the SGMII link is OK. */ if (!(phy_read(phydev, AT803X_PSSR) & AT803X_PSSR_MR_AN_COMPLETE)) { pr_warn("803x_aneg_done: SGMII link is not ok\n"); aneg_done = 0; } /* switch back to copper page */ phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL); return aneg_done; }

Contributors

PersonTokensPropCommitsCommitProp
Zefir Kurtisi108100.00%1100.00%
Total108100.00%1100.00%

static struct phy_driver at803x_driver[] = { { /* ATHEROS 8035 */ .phy_id = ATH8035_PHY_ID, .name = "Atheros 8035 ethernet", .phy_id_mask = AT803X_PHY_ID_MASK, .probe = at803x_probe, .config_init = at803x_config_init, .set_wol = at803x_set_wol, .get_wol = at803x_get_wol, .suspend = at803x_suspend, .resume = at803x_resume, .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, .ack_interrupt = at803x_ack_interrupt, .config_intr = at803x_config_intr, }, { /* ATHEROS 8030 */ .phy_id = ATH8030_PHY_ID, .name = "Atheros 8030 ethernet", .phy_id_mask = AT803X_PHY_ID_MASK, .probe = at803x_probe, .config_init = at803x_config_init, .link_change_notify = at803x_link_change_notify, .set_wol = at803x_set_wol, .get_wol = at803x_get_wol, .suspend = at803x_suspend, .resume = at803x_resume, .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, .ack_interrupt = at803x_ack_interrupt, .config_intr = at803x_config_intr, }, { /* ATHEROS 8031 */ .phy_id = ATH8031_PHY_ID, .name = "Atheros 8031 ethernet", .phy_id_mask = AT803X_PHY_ID_MASK, .probe = at803x_probe, .config_init = at803x_config_init, .set_wol = at803x_set_wol, .get_wol = at803x_get_wol, .suspend = at803x_suspend, .resume = at803x_resume, .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, .aneg_done = at803x_aneg_done, .ack_interrupt = &at803x_ack_interrupt, .config_intr = &at803x_config_intr, } }; module_phy_driver(at803x_driver); static struct mdio_device_id __maybe_unused atheros_tbl[] = { { ATH8030_PHY_ID, AT803X_PHY_ID_MASK }, { ATH8031_PHY_ID, AT803X_PHY_ID_MASK }, { ATH8035_PHY_ID, AT803X_PHY_ID_MASK }, { } }; MODULE_DEVICE_TABLE(mdio, atheros_tbl);

Overall Contributors

PersonTokensPropCommitsCommitProp
Daniel Mack62433.84%416.00%
Matus Ujhelyi35018.98%14.00%
Mugunthan V N29716.11%416.00%
Martin Blumenstingl26814.53%416.00%
Zefir Kurtisi1387.48%14.00%
Zhao Qiang1005.42%14.00%
Måns Rullgård160.87%14.00%
Sebastian Frias150.81%14.00%
Uwe Kleine-König130.70%14.00%
Fabio Estevam100.54%14.00%
Helmut Schaa40.22%14.00%
Andrew Lunn30.16%28.00%
Sergei Shtylyov30.16%14.00%
Johan Hovold20.11%14.00%
Fengguang Wu10.05%14.00%
Total1844100.00%25100.00%
Directory: drivers/net/phy
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.