linux/drivers/net/phy/meson-gxl.c
Jerome Brunet 30e43f1334 net: phy: meson-gxl: leave CONFIG_A6 untouched
The PHY performs just as well when left in its default configuration and
it makes senses because this poke gets reset just after init.

According to the documentation, all registers in the Analog/DSP bank are
reset when there is a mode switch from 10BT to 100BT. The bank is also
reset on power down and soft reset, so we will never see the value which
may have been set by the bootloader.

In the end, we have used the default configuration so far and there is no
reason to change now. Remove CONFIG_A6 poke to make this clear.

Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-12-18 13:24:56 -05:00

224 lines
5.9 KiB
C

/*
* Amlogic Meson GXL Internal PHY Driver
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
* Copyright (C) 2016 BayLibre, SAS. All rights reserved.
* Author: Neil Armstrong <narmstrong@baylibre.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.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <linux/netdevice.h>
#include <linux/bitfield.h>
#define TSTCNTL 20
#define TSTCNTL_READ BIT(15)
#define TSTCNTL_WRITE BIT(14)
#define TSTCNTL_REG_BANK_SEL GENMASK(12, 11)
#define TSTCNTL_TEST_MODE BIT(10)
#define TSTCNTL_READ_ADDRESS GENMASK(9, 5)
#define TSTCNTL_WRITE_ADDRESS GENMASK(4, 0)
#define TSTREAD1 21
#define TSTWRITE 23
#define BANK_ANALOG_DSP 0
#define BANK_WOL 1
#define BANK_BIST 3
/* WOL Registers */
#define LPI_STATUS 0xc
#define LPI_STATUS_RSV12 BIT(12)
/* BIST Registers */
#define FR_PLL_CONTROL 0x1b
#define FR_PLL_DIV0 0x1c
#define FR_PLL_DIV1 0x1d
static int meson_gxl_open_banks(struct phy_device *phydev)
{
int ret;
/* Enable Analog and DSP register Bank access by
* toggling TSTCNTL_TEST_MODE bit in the TSTCNTL register
*/
ret = phy_write(phydev, TSTCNTL, 0);
if (ret)
return ret;
ret = phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE);
if (ret)
return ret;
ret = phy_write(phydev, TSTCNTL, 0);
if (ret)
return ret;
return phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE);
}
static void meson_gxl_close_banks(struct phy_device *phydev)
{
phy_write(phydev, TSTCNTL, 0);
}
static int meson_gxl_read_reg(struct phy_device *phydev,
unsigned int bank, unsigned int reg)
{
int ret;
ret = meson_gxl_open_banks(phydev);
if (ret)
goto out;
ret = phy_write(phydev, TSTCNTL, TSTCNTL_READ |
FIELD_PREP(TSTCNTL_REG_BANK_SEL, bank) |
TSTCNTL_TEST_MODE |
FIELD_PREP(TSTCNTL_READ_ADDRESS, reg));
if (ret)
goto out;
ret = phy_read(phydev, TSTREAD1);
out:
/* Close the bank access on our way out */
meson_gxl_close_banks(phydev);
return ret;
}
static int meson_gxl_write_reg(struct phy_device *phydev,
unsigned int bank, unsigned int reg,
uint16_t value)
{
int ret;
ret = meson_gxl_open_banks(phydev);
if (ret)
goto out;
ret = phy_write(phydev, TSTWRITE, value);
if (ret)
goto out;
ret = phy_write(phydev, TSTCNTL, TSTCNTL_WRITE |
FIELD_PREP(TSTCNTL_REG_BANK_SEL, bank) |
TSTCNTL_TEST_MODE |
FIELD_PREP(TSTCNTL_WRITE_ADDRESS, reg));
out:
/* Close the bank access on our way out */
meson_gxl_close_banks(phydev);
return ret;
}
static int meson_gxl_config_init(struct phy_device *phydev)
{
int ret;
/* Enable fractional PLL */
ret = meson_gxl_write_reg(phydev, BANK_BIST, FR_PLL_CONTROL, 0x5);
if (ret)
return ret;
/* Program fraction FR_PLL_DIV1 */
ret = meson_gxl_write_reg(phydev, BANK_BIST, FR_PLL_DIV1, 0x029a);
if (ret)
return ret;
/* Program fraction FR_PLL_DIV1 */
ret = meson_gxl_write_reg(phydev, BANK_BIST, FR_PLL_DIV0, 0xaaaa);
if (ret)
return ret;
return genphy_config_init(phydev);
}
/* This function is provided to cope with the possible failures of this phy
* during aneg process. When aneg fails, the PHY reports that aneg is done
* but the value found in MII_LPA is wrong:
* - Early failures: MII_LPA is just 0x0001. if MII_EXPANSION reports that
* the link partner (LP) supports aneg but the LP never acked our base
* code word, it is likely that we never sent it to begin with.
* - Late failures: MII_LPA is filled with a value which seems to make sense
* but it actually is not what the LP is advertising. It seems that we
* can detect this using a magic bit in the WOL bank (reg 12 - bit 12).
* If this particular bit is not set when aneg is reported being done,
* it means MII_LPA is likely to be wrong.
*
* In both case, forcing a restart of the aneg process solve the problem.
* When this failure happens, the first retry is usually successful but,
* in some cases, it may take up to 6 retries to get a decent result
*/
static int meson_gxl_read_status(struct phy_device *phydev)
{
int ret, wol, lpa, exp;
if (phydev->autoneg == AUTONEG_ENABLE) {
ret = genphy_aneg_done(phydev);
if (ret < 0)
return ret;
else if (!ret)
goto read_status_continue;
/* Aneg is done, let's check everything is fine */
wol = meson_gxl_read_reg(phydev, BANK_WOL, LPI_STATUS);
if (wol < 0)
return wol;
lpa = phy_read(phydev, MII_LPA);
if (lpa < 0)
return lpa;
exp = phy_read(phydev, MII_EXPANSION);
if (exp < 0)
return exp;
if (!(wol & LPI_STATUS_RSV12) ||
((exp & EXPANSION_NWAY) && !(lpa & LPA_LPACK))) {
/* Looks like aneg failed after all */
phydev_dbg(phydev, "LPA corruption - aneg restart\n");
return genphy_restart_aneg(phydev);
}
}
read_status_continue:
return genphy_read_status(phydev);
}
static struct phy_driver meson_gxl_phy[] = {
{
.phy_id = 0x01814400,
.phy_id_mask = 0xfffffff0,
.name = "Meson GXL Internal PHY",
.features = PHY_BASIC_FEATURES,
.flags = PHY_IS_INTERNAL,
.config_init = meson_gxl_config_init,
.aneg_done = genphy_aneg_done,
.read_status = meson_gxl_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
},
};
static struct mdio_device_id __maybe_unused meson_gxl_tbl[] = {
{ 0x01814400, 0xfffffff0 },
{ }
};
module_phy_driver(meson_gxl_phy);
MODULE_DEVICE_TABLE(mdio, meson_gxl_tbl);
MODULE_DESCRIPTION("Amlogic Meson GXL Internal PHY driver");
MODULE_AUTHOR("Baoqi wang");
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
MODULE_LICENSE("GPL");