Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Copyright (C) 2015 Broadcom Corporation
  3 *
  4 * This program is free software; you can redistribute it and/or
  5 * modify it under the terms of the GNU General Public License as
  6 * published by the Free Software Foundation version 2.
  7 *
  8 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  9 * kind, whether express or implied; without even the implied warranty
 10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 11 * GNU General Public License for more details.
 12 */
 13
 14#include "bcm-phy-lib.h"
 15#include <linux/brcmphy.h>
 16#include <linux/export.h>
 17#include <linux/mdio.h>
 18#include <linux/module.h>
 19#include <linux/phy.h>
 20
 21#define MII_BCM_CHANNEL_WIDTH     0x2000
 22#define BCM_CL45VEN_EEE_ADV       0x3c
 23
 24int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)
 25{
 26	int rc;
 27
 28	rc = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
 29	if (rc < 0)
 30		return rc;
 31
 32	return phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
 33}
 34EXPORT_SYMBOL_GPL(bcm_phy_write_exp);
 35
 36int bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
 37{
 38	int val;
 39
 40	val = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
 41	if (val < 0)
 42		return val;
 43
 44	val = phy_read(phydev, MII_BCM54XX_EXP_DATA);
 45
 46	/* Restore default value.  It's O.K. if this write fails. */
 47	phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
 48
 49	return val;
 50}
 51EXPORT_SYMBOL_GPL(bcm_phy_read_exp);
 52
 53int bcm_phy_write_misc(struct phy_device *phydev,
 54		       u16 reg, u16 chl, u16 val)
 55{
 56	int rc;
 57	int tmp;
 58
 59	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
 60		       MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
 61	if (rc < 0)
 62		return rc;
 63
 64	tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
 65	tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
 66	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
 67	if (rc < 0)
 68		return rc;
 69
 70	tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
 71	rc = bcm_phy_write_exp(phydev, tmp, val);
 72
 73	return rc;
 74}
 75EXPORT_SYMBOL_GPL(bcm_phy_write_misc);
 76
 77int bcm_phy_read_misc(struct phy_device *phydev,
 78		      u16 reg, u16 chl)
 79{
 80	int rc;
 81	int tmp;
 82
 83	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
 84		       MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
 85	if (rc < 0)
 86		return rc;
 87
 88	tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
 89	tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
 90	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
 91	if (rc < 0)
 92		return rc;
 93
 94	tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
 95	rc = bcm_phy_read_exp(phydev, tmp);
 96
 97	return rc;
 98}
 99EXPORT_SYMBOL_GPL(bcm_phy_read_misc);
100
101int bcm_phy_ack_intr(struct phy_device *phydev)
102{
103	int reg;
104
105	/* Clear pending interrupts.  */
106	reg = phy_read(phydev, MII_BCM54XX_ISR);
107	if (reg < 0)
108		return reg;
109
110	return 0;
111}
112EXPORT_SYMBOL_GPL(bcm_phy_ack_intr);
113
114int bcm_phy_config_intr(struct phy_device *phydev)
115{
116	int reg;
117
118	reg = phy_read(phydev, MII_BCM54XX_ECR);
119	if (reg < 0)
120		return reg;
121
122	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
123		reg &= ~MII_BCM54XX_ECR_IM;
124	else
125		reg |= MII_BCM54XX_ECR_IM;
126
127	return phy_write(phydev, MII_BCM54XX_ECR, reg);
128}
129EXPORT_SYMBOL_GPL(bcm_phy_config_intr);
130
131int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow)
132{
133	phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow));
134	return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD));
135}
136EXPORT_SYMBOL_GPL(bcm_phy_read_shadow);
137
138int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow,
139			 u16 val)
140{
141	return phy_write(phydev, MII_BCM54XX_SHD,
142			 MII_BCM54XX_SHD_WRITE |
143			 MII_BCM54XX_SHD_VAL(shadow) |
144			 MII_BCM54XX_SHD_DATA(val));
145}
146EXPORT_SYMBOL_GPL(bcm_phy_write_shadow);
147
148int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down)
149{
150	int val;
151
152	if (dll_pwr_down) {
153		val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
154		if (val < 0)
155			return val;
156
157		val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
158		bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
159	}
160
161	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
162	if (val < 0)
163		return val;
164
165	/* Clear APD bits */
166	val &= BCM_APD_CLR_MASK;
167
168	if (phydev->autoneg == AUTONEG_ENABLE)
169		val |= BCM54XX_SHD_APD_EN;
170	else
171		val |= BCM_NO_ANEG_APD_EN;
172
173	/* Enable energy detect single link pulse for easy wakeup */
174	val |= BCM_APD_SINGLELP_EN;
175
176	/* Enable Auto Power-Down (APD) for the PHY */
177	return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
178}
179EXPORT_SYMBOL_GPL(bcm_phy_enable_apd);
180
181int bcm_phy_enable_eee(struct phy_device *phydev)
182{
183	int val;
184
185	/* Enable EEE at PHY level */
186	val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
187				    MDIO_MMD_AN);
188	if (val < 0)
189		return val;
190
191	val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
192
193	phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
194			       MDIO_MMD_AN, (u32)val);
195
196	/* Advertise EEE */
197	val = phy_read_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
198				    MDIO_MMD_AN);
199	if (val < 0)
200		return val;
201
202	val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
203
204	phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
205			       MDIO_MMD_AN, (u32)val);
206
207	return 0;
208}
209EXPORT_SYMBOL_GPL(bcm_phy_enable_eee);
210
211MODULE_DESCRIPTION("Broadcom PHY Library");
212MODULE_LICENSE("GPL v2");
213MODULE_AUTHOR("Broadcom Corporation");