brintos

brintos / linux-shallow public Read only

0
0
Text · 6.3 KiB · bb9b33b Raw
239 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Amlogic Meson GXL Internal PHY Driver4 *5 * Copyright (C) 2015 Amlogic, Inc. All rights reserved.6 * Copyright (C) 2016 BayLibre, SAS. All rights reserved.7 * Author: Neil Armstrong <narmstrong@baylibre.com>8 */9#include <linux/kernel.h>10#include <linux/module.h>11#include <linux/mii.h>12#include <linux/ethtool.h>13#include <linux/phy.h>14#include <linux/netdevice.h>15#include <linux/bitfield.h>16#include <linux/smscphy.h>17 18#define TSTCNTL		2019#define  TSTCNTL_READ		BIT(15)20#define  TSTCNTL_WRITE		BIT(14)21#define  TSTCNTL_REG_BANK_SEL	GENMASK(12, 11)22#define  TSTCNTL_TEST_MODE	BIT(10)23#define  TSTCNTL_READ_ADDRESS	GENMASK(9, 5)24#define  TSTCNTL_WRITE_ADDRESS	GENMASK(4, 0)25#define TSTREAD1	2126#define TSTWRITE	2327 28#define BANK_ANALOG_DSP		029#define BANK_WOL		130#define BANK_BIST		331 32/* WOL Registers */33#define LPI_STATUS	0xc34#define  LPI_STATUS_RSV12	BIT(12)35 36/* BIST Registers */37#define FR_PLL_CONTROL	0x1b38#define FR_PLL_DIV0	0x1c39#define FR_PLL_DIV1	0x1d40 41static int meson_gxl_open_banks(struct phy_device *phydev)42{43	int ret;44 45	/* Enable Analog and DSP register Bank access by46	 * toggling TSTCNTL_TEST_MODE bit in the TSTCNTL register47	 */48	ret = phy_write(phydev, TSTCNTL, 0);49	if (ret)50		return ret;51	ret = phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE);52	if (ret)53		return ret;54	ret = phy_write(phydev, TSTCNTL, 0);55	if (ret)56		return ret;57	return phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE);58}59 60static void meson_gxl_close_banks(struct phy_device *phydev)61{62	phy_write(phydev, TSTCNTL, 0);63}64 65static int meson_gxl_read_reg(struct phy_device *phydev,66			      unsigned int bank, unsigned int reg)67{68	int ret;69 70	ret = meson_gxl_open_banks(phydev);71	if (ret)72		goto out;73 74	ret = phy_write(phydev, TSTCNTL, TSTCNTL_READ |75			FIELD_PREP(TSTCNTL_REG_BANK_SEL, bank) |76			TSTCNTL_TEST_MODE |77			FIELD_PREP(TSTCNTL_READ_ADDRESS, reg));78	if (ret)79		goto out;80 81	ret = phy_read(phydev, TSTREAD1);82out:83	/* Close the bank access on our way out */84	meson_gxl_close_banks(phydev);85	return ret;86}87 88static int meson_gxl_write_reg(struct phy_device *phydev,89			       unsigned int bank, unsigned int reg,90			       uint16_t value)91{92	int ret;93 94	ret = meson_gxl_open_banks(phydev);95	if (ret)96		goto out;97 98	ret = phy_write(phydev, TSTWRITE, value);99	if (ret)100		goto out;101 102	ret = phy_write(phydev, TSTCNTL, TSTCNTL_WRITE |103			FIELD_PREP(TSTCNTL_REG_BANK_SEL, bank) |104			TSTCNTL_TEST_MODE |105			FIELD_PREP(TSTCNTL_WRITE_ADDRESS, reg));106 107out:108	/* Close the bank access on our way out */109	meson_gxl_close_banks(phydev);110	return ret;111}112 113static int meson_gxl_config_init(struct phy_device *phydev)114{115	int ret;116 117	/* Enable fractional PLL */118	ret = meson_gxl_write_reg(phydev, BANK_BIST, FR_PLL_CONTROL, 0x5);119	if (ret)120		return ret;121 122	/* Program fraction FR_PLL_DIV1 */123	ret = meson_gxl_write_reg(phydev, BANK_BIST, FR_PLL_DIV1, 0x029a);124	if (ret)125		return ret;126 127	/* Program fraction FR_PLL_DIV1 */128	ret = meson_gxl_write_reg(phydev, BANK_BIST, FR_PLL_DIV0, 0xaaaa);129	if (ret)130		return ret;131 132	return 0;133}134 135/* This function is provided to cope with the possible failures of this phy136 * during aneg process. When aneg fails, the PHY reports that aneg is done137 * but the value found in MII_LPA is wrong:138 *  - Early failures: MII_LPA is just 0x0001. if MII_EXPANSION reports that139 *    the link partner (LP) supports aneg but the LP never acked our base140 *    code word, it is likely that we never sent it to begin with.141 *  - Late failures: MII_LPA is filled with a value which seems to make sense142 *    but it actually is not what the LP is advertising. It seems that we143 *    can detect this using a magic bit in the WOL bank (reg 12 - bit 12).144 *    If this particular bit is not set when aneg is reported being done,145 *    it means MII_LPA is likely to be wrong.146 *147 * In both case, forcing a restart of the aneg process solve the problem.148 * When this failure happens, the first retry is usually successful but,149 * in some cases, it may take up to 6 retries to get a decent result150 */151static int meson_gxl_read_status(struct phy_device *phydev)152{153	int ret, wol, lpa, exp;154 155	if (phydev->autoneg == AUTONEG_ENABLE) {156		ret = genphy_aneg_done(phydev);157		if (ret < 0)158			return ret;159		else if (!ret)160			goto read_status_continue;161 162		/* Aneg is done, let's check everything is fine */163		wol = meson_gxl_read_reg(phydev, BANK_WOL, LPI_STATUS);164		if (wol < 0)165			return wol;166 167		lpa = phy_read(phydev, MII_LPA);168		if (lpa < 0)169			return lpa;170 171		exp = phy_read(phydev, MII_EXPANSION);172		if (exp < 0)173			return exp;174 175		if (!(wol & LPI_STATUS_RSV12) ||176		    ((exp & EXPANSION_NWAY) && !(lpa & LPA_LPACK))) {177			/* Looks like aneg failed after all */178			phydev_dbg(phydev, "LPA corruption - aneg restart\n");179			return genphy_restart_aneg(phydev);180		}181	}182 183read_status_continue:184	return genphy_read_status(phydev);185}186 187static struct phy_driver meson_gxl_phy[] = {188	{189		PHY_ID_MATCH_EXACT(0x01814400),190		.name		= "Meson GXL Internal PHY",191		/* PHY_BASIC_FEATURES */192		.flags		= PHY_IS_INTERNAL,193		.soft_reset     = genphy_soft_reset,194		.config_init	= meson_gxl_config_init,195		.read_status	= meson_gxl_read_status,196		.config_intr	= smsc_phy_config_intr,197		.handle_interrupt = smsc_phy_handle_interrupt,198		.suspend        = genphy_suspend,199		.resume         = genphy_resume,200		.read_mmd	= genphy_read_mmd_unsupported,201		.write_mmd	= genphy_write_mmd_unsupported,202	}, {203		PHY_ID_MATCH_EXACT(0x01803301),204		.name		= "Meson G12A Internal PHY",205		/* PHY_BASIC_FEATURES */206		.flags		= PHY_IS_INTERNAL,207		.probe		= smsc_phy_probe,208		.config_init	= smsc_phy_config_init,209		.soft_reset     = genphy_soft_reset,210		.read_status	= lan87xx_read_status,211		.config_intr	= smsc_phy_config_intr,212		.handle_interrupt = smsc_phy_handle_interrupt,213 214		.get_tunable	= smsc_phy_get_tunable,215		.set_tunable	= smsc_phy_set_tunable,216 217		.suspend        = genphy_suspend,218		.resume         = genphy_resume,219		.read_mmd	= genphy_read_mmd_unsupported,220		.write_mmd	= genphy_write_mmd_unsupported,221	},222};223 224static struct mdio_device_id __maybe_unused meson_gxl_tbl[] = {225	{ PHY_ID_MATCH_VENDOR(0x01814400) },226	{ PHY_ID_MATCH_VENDOR(0x01803301) },227	{ }228};229 230module_phy_driver(meson_gxl_phy);231 232MODULE_DEVICE_TABLE(mdio, meson_gxl_tbl);233 234MODULE_DESCRIPTION("Amlogic Meson GXL Internal PHY driver");235MODULE_AUTHOR("Baoqi wang");236MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");237MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");238MODULE_LICENSE("GPL");239