Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Driver for the Renesas PHY uPD60620.
  4 *
  5 * Copyright (C) 2015 Softing Industrial Automation GmbH
  6 */
  7
  8#include <linux/kernel.h>
  9#include <linux/module.h>
 10#include <linux/phy.h>
 11
 12#define UPD60620_PHY_ID    0xb8242824
 13
 14/* Extended Registers and values */
 15/* PHY Special Control/Status    */
 16#define PHY_PHYSCR         0x1F      /* PHY.31 */
 17#define PHY_PHYSCR_10MB    0x0004    /* PHY speed = 10mb */
 18#define PHY_PHYSCR_100MB   0x0008    /* PHY speed = 100mb */
 19#define PHY_PHYSCR_DUPLEX  0x0010    /* PHY Duplex */
 20
 21/* PHY Special Modes */
 22#define PHY_SPM            0x12      /* PHY.18 */
 23
 24/* Init PHY */
 25
 26static int upd60620_config_init(struct phy_device *phydev)
 27{
 28	/* Enable support for passive HUBs (could be a strap option) */
 29	/* PHYMODE: All speeds, HD in parallel detect */
 30	return phy_write(phydev, PHY_SPM, 0x0180 | phydev->mdio.addr);
 31}
 32
 33/* Get PHY status from common registers */
 34
 35static int upd60620_read_status(struct phy_device *phydev)
 36{
 37	int phy_state;
 38
 39	/* Read negotiated state */
 40	phy_state = phy_read(phydev, MII_BMSR);
 41	if (phy_state < 0)
 42		return phy_state;
 43
 44	phydev->link = 0;
 45	linkmode_zero(phydev->lp_advertising);
 46	phydev->pause = 0;
 47	phydev->asym_pause = 0;
 48
 49	if (phy_state & (BMSR_ANEGCOMPLETE | BMSR_LSTATUS)) {
 50		phy_state = phy_read(phydev, PHY_PHYSCR);
 51		if (phy_state < 0)
 52			return phy_state;
 53
 54		if (phy_state & (PHY_PHYSCR_10MB | PHY_PHYSCR_100MB)) {
 55			phydev->link = 1;
 56			phydev->speed = SPEED_10;
 57			phydev->duplex = DUPLEX_HALF;
 58
 59			if (phy_state & PHY_PHYSCR_100MB)
 60				phydev->speed = SPEED_100;
 61			if (phy_state & PHY_PHYSCR_DUPLEX)
 62				phydev->duplex = DUPLEX_FULL;
 63
 64			phy_state = phy_read(phydev, MII_LPA);
 65			if (phy_state < 0)
 66				return phy_state;
 67
 68			mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising,
 69						  phy_state);
 70
 71			if (phydev->duplex == DUPLEX_FULL) {
 72				if (phy_state & LPA_PAUSE_CAP)
 73					phydev->pause = 1;
 74				if (phy_state & LPA_PAUSE_ASYM)
 75					phydev->asym_pause = 1;
 76			}
 77		}
 78	}
 79	return 0;
 80}
 81
 82MODULE_DESCRIPTION("Renesas uPD60620 PHY driver");
 83MODULE_AUTHOR("Bernd Edlinger <bernd.edlinger@hotmail.de>");
 84MODULE_LICENSE("GPL");
 85
 86static struct phy_driver upd60620_driver[1] = { {
 87	.phy_id         = UPD60620_PHY_ID,
 88	.phy_id_mask    = 0xfffffffe,
 89	.name           = "Renesas uPD60620",
 90	/* PHY_BASIC_FEATURES */
 91	.flags          = 0,
 92	.config_init    = upd60620_config_init,
 93	.read_status    = upd60620_read_status,
 94} };
 95
 96module_phy_driver(upd60620_driver);
 97
 98static struct mdio_device_id __maybe_unused upd60620_tbl[] = {
 99	{ UPD60620_PHY_ID, 0xfffffffe },
100	{ }
101};
102
103MODULE_DEVICE_TABLE(mdio, upd60620_tbl);