cregit-Linux how code gets into the kernel

Release 4.7 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_DEBUG_ADDR			0x1D

#define AT803X_DEBUG_DATA			0x1E


#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 AT803X_REG_CHIP_CONFIG			0x1f

#define AT803X_BT_BX_REG_SEL			0x8000


#define ATH8030_PHY_ID 0x004dd076

#define ATH8031_PHY_ID 0x004dd074

#define ATH8035_PHY_ID 0x004dd072

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 blumenstinglmartin 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 blumenstinglmartin 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 blumenstinglmartin 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 blumenstinglmartin 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 mackdaniel 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 mackdaniel 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 ujhelyimatus ujhelyi13753.10%133.33%
mugunthan v nmugunthan v n11946.12%133.33%
martin blumenstinglmartin 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 nmugunthan v n5096.15%133.33%
martin blumenstinglmartin blumenstingl11.92%133.33%
matus ujhelyimatus ujhelyi11.92%133.33%
Total52100.00%3100.00%


static int at803x_suspend(struct phy_device *phydev) { int value; int wol_enabled; int ccr; 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); if (phydev->interface != PHY_INTERFACE_MODE_SGMII) goto done; /* also power-down SGMII interface */ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL); phy_write(phydev, MII_BMCR, phy_read(phydev, MII_BMCR) | BMCR_PDOWN); phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL); done: mutex_unlock(&phydev->lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
daniel mackdaniel mack8155.10%133.33%
zefir kurtisizefir kurtisi6544.22%133.33%
martin blumenstinglmartin blumenstingl10.68%133.33%
Total147100.00%3100.00%


static int at803x_resume(struct phy_device *phydev) { int value; int ccr; mutex_lock(&phydev->lock); value = phy_read(phydev, MII_BMCR); value &= ~(BMCR_PDOWN | BMCR_ISOLATE); phy_write(phydev, MII_BMCR, value); if (phydev->interface != PHY_INTERFACE_MODE_SGMII) goto done; /* also power-up SGMII interface */ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL); value = phy_read(phydev, MII_BMCR) & ~(BMCR_PDOWN | BMCR_ISOLATE); phy_write(phydev, MII_BMCR, value); phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL); done: mutex_unlock(&phydev->lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
zefir kurtisizefir kurtisi7455.22%150.00%
daniel mackdaniel mack6044.78%150.00%
Total134100.00%2100.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 mackdaniel mack7971.17%116.67%
sebastian friassebastian frias1513.51%116.67%
uwe kleine-koeniguwe kleine-koenig1311.71%116.67%
andrew lunnandrew lunn21.80%116.67%
fengguang wufengguang wu10.90%116.67%
sergei shtylyovsergei shtylyov10.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 nmugunthan v n3536.08%125.00%
matus ujhelyimatus ujhelyi2929.90%125.00%
martin blumenstinglmartin blumenstingl2929.90%125.00%
daniel mackdaniel 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 qiangzhao qiang3397.06%150.00%
martin blumenstinglmartin 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 qiangzhao qiang5769.51%133.33%
martin blumenstinglmartin 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 mackdaniel mack11397.41%133.33%
sergei shtylyovsergei shtylyov21.72%133.33%
andrew lunnandrew lunn10.86%133.33%
Total116100.00%3100.00%

static struct phy_driver at803x_driver[] = { { /* ATHEROS 8035 */ .phy_id = ATH8035_PHY_ID, .name = "Atheros 8035 ethernet", .phy_id_mask = 0xffffffef, .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 = 0xffffffef, .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 = 0xffffffef, .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, } }; module_phy_driver(at803x_driver); static struct mdio_device_id __maybe_unused atheros_tbl[] = { { ATH8030_PHY_ID, 0xffffffef }, { ATH8031_PHY_ID, 0xffffffef }, { ATH8035_PHY_ID, 0xffffffef }, { } }; MODULE_DEVICE_TABLE(mdio, atheros_tbl);

Overall Contributors

PersonTokensPropCommitsCommitProp
daniel mackdaniel mack62433.75%416.67%
matus ujhelyimatus ujhelyi35419.15%14.17%
mugunthan v nmugunthan v n29816.12%416.67%
martin blumenstinglmartin blumenstingl26814.49%416.67%
zefir kurtisizefir kurtisi1477.95%14.17%
zhao qiangzhao qiang1005.41%14.17%
mans rullgardmans rullgard160.87%14.17%
sebastian friassebastian frias150.81%14.17%
uwe kleine-koeniguwe kleine-koenig130.70%14.17%
helmut schaahelmut schaa50.27%14.17%
andrew lunnandrew lunn30.16%28.33%
sergei shtylyovsergei shtylyov30.16%14.17%
johan hovoldjohan hovold20.11%14.17%
fengguang wufengguang wu10.05%14.17%
Total1849100.00%24100.00%
Directory: drivers/net/phy
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}