diff options
| author | Kees Cook <kees+treewide@kernel.org> | 2026-09-02 15:31:14 -0700 |
|---|---|---|
| committer | Kees Cook <kees@kernel.org> | 2026-09-04 21:37:00 -0700 |
| commit | 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d (patch) | |
| tree | c65086f9bdcd48c6360fb7cb4598bca084da1f32 /drivers/net/phy/meson-gxl.c | |
| download | linux-stable-3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d.tar.gz linux-stable-3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d.zip | |
treewide: refresh kmalloc_obj() conversionsgrafted
This is another run of the Coccinelle script for converting kmalloc()
family of allocations to kmalloc_obj() via the existing rules in
scripts/coccinelle/api/kmalloc_objs.cocci
This catches both the set of kmalloc() uses added since the first
kmalloc_obj() conversions in v7.0 and adds a large group missed in the
first pass due to Coccinelle not interacting well with the cleanup.h
scoped_...() family of macros[1]. I worked around this with spatch's
"--macro-file" argument to a file with all the scoped_...() macros mapped
to Coccinelle's YACFE_ITERATOR[2] as that was the closest viable control
flow indicator I could find.
Build tested allmodconfig on x86, arm64, arm, loongarch, mips, powerpc,
riscv, and s390 with no new warnings.
Link: https://lore.kernel.org/lkml/202609021314.8A9C0B8@keescook/ [1]
Link: https://github.com/coccinelle/coccinelle/blob/master/standard.h [2]
Signed-off-by: Kees Cook <kees+treewide@kernel.org>
Diffstat (limited to 'drivers/net/phy/meson-gxl.c')
| -rw-r--r-- | drivers/net/phy/meson-gxl.c | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c new file mode 100644 index 000000000..962ebbbc1 --- /dev/null +++ b/drivers/net/phy/meson-gxl.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * 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> + */ +#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> +#include <linux/smscphy.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 0; +} + +/* 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_MATCH_EXACT(0x01814400), + .name = "Meson GXL Internal PHY", + /* PHY_BASIC_FEATURES */ + .flags = PHY_IS_INTERNAL, + .soft_reset = genphy_soft_reset, + .config_init = meson_gxl_config_init, + .read_status = meson_gxl_read_status, + .config_intr = smsc_phy_config_intr, + .handle_interrupt = smsc_phy_handle_interrupt, + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_mmd = genphy_read_mmd_unsupported, + .write_mmd = genphy_write_mmd_unsupported, + }, { + PHY_ID_MATCH_EXACT(0x01803301), + .name = "Meson G12A Internal PHY", + /* PHY_BASIC_FEATURES */ + .flags = PHY_IS_INTERNAL, + .probe = smsc_phy_probe, + .config_init = smsc_phy_config_init, + .soft_reset = genphy_soft_reset, + .read_status = lan87xx_read_status, + .config_intr = smsc_phy_config_intr, + .handle_interrupt = smsc_phy_handle_interrupt, + + .get_tunable = smsc_phy_get_tunable, + .set_tunable = smsc_phy_set_tunable, + + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_mmd = genphy_read_mmd_unsupported, + .write_mmd = genphy_write_mmd_unsupported, + }, +}; + +static const struct mdio_device_id __maybe_unused meson_gxl_tbl[] = { + { PHY_ID_MATCH_VENDOR(0x01814400) }, + { PHY_ID_MATCH_VENDOR(0x01803301) }, + { } +}; + +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_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL"); |
