blob: 5499312e917262cd1a2ee7aec6e1cf895b1df0d1 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Common part for Airoha AN8855 gigabit switch
*
* Copyright (C) 2023 Airoha Inc. All Rights Reserved.
*
* Author: Min Yao <min.yao@airoha.com>
*/
#include <linux/delay.h>
#include <linux/hrtimer.h>
#include <linux/kernel.h>
#include <net/dsa.h>
#include "an8855.h"
#include "an8855_phy.h"
#define AN8855_NUM_PHYS 5
static u32
an8855_phy_read_dev_reg(struct dsa_switch *ds, u32 port_num,
u32 dev_addr, u32 reg_addr)
{
struct an8855_priv *priv = ds->priv;
u32 phy_val;
u32 addr;
addr = MII_ADDR_C45 | (dev_addr << 16) | (reg_addr & 0xffff);
phy_val = priv->info->phy_read(ds, port_num, addr);
return phy_val;
}
static void
an8855_phy_write_dev_reg(struct dsa_switch *ds, u32 port_num,
u32 dev_addr, u32 reg_addr, u32 write_data)
{
struct an8855_priv *priv = ds->priv;
u32 addr;
addr = MII_ADDR_C45 | (dev_addr << 16) | (reg_addr & 0xffff);
priv->info->phy_write(ds, port_num, addr, write_data);
}
static void
an8855_switch_phy_write(struct dsa_switch *ds, u32 port_num,
u32 reg_addr, u32 write_data)
{
struct an8855_priv *priv = ds->priv;
priv->info->phy_write(ds, port_num, reg_addr, write_data);
}
static u32
an8855_switch_phy_read(struct dsa_switch *ds, u32 port_num,
u32 reg_addr)
{
struct an8855_priv *priv = ds->priv;
return priv->info->phy_read(ds, port_num, reg_addr);
}
static void
an8855_phy_setting(struct dsa_switch *ds)
{
struct an8855_priv *priv = ds->priv;
int i;
u32 val;
/* Release power down */
an8855_write(priv, RG_GPHY_AFE_PWD, 0x0);
for (i = 0; i < AN8855_NUM_PHYS; i++) {
/* Enable HW auto downshift */
an8855_switch_phy_write(ds, i, 0x1f, 0x1);
val = an8855_switch_phy_read(ds, i, PHY_EXT_REG_14);
val |= PHY_EN_DOWN_SHFIT;
an8855_switch_phy_write(ds, i, PHY_EXT_REG_14, val);
an8855_switch_phy_write(ds, i, 0x1f, 0x0);
/* Enable Asymmetric Pause Capability */
val = an8855_switch_phy_read(ds, i, MII_ADVERTISE);
val |= ADVERTISE_PAUSE_ASYM;
an8855_switch_phy_write(ds, i, MII_ADVERTISE, val);
}
}
static void
an8855_low_power_setting(struct dsa_switch *ds)
{
int port, addr;
for (port = 0; port < AN8855_NUM_PHYS; port++) {
an8855_phy_write_dev_reg(ds, port, 0x1e, 0x11, 0x0f00);
an8855_phy_write_dev_reg(ds, port, 0x1e, 0x3c, 0x0000);
an8855_phy_write_dev_reg(ds, port, 0x1e, 0x3d, 0x0000);
an8855_phy_write_dev_reg(ds, port, 0x1e, 0x3e, 0x0000);
an8855_phy_write_dev_reg(ds, port, 0x1e, 0xc6, 0x53aa);
}
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x268, 0x07f1);
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x269, 0x2111);
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x26a, 0x0000);
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x26b, 0x0074);
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x26e, 0x00f6);
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x26f, 0x6666);
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x271, 0x2c02);
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x272, 0x0c22);
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x700, 0x0001);
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x701, 0x0803);
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x702, 0x01b6);
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x703, 0x2111);
an8855_phy_write_dev_reg(ds, 1, 0x1f, 0x700, 0x0001);
for (addr = 0x200; addr <= 0x230; addr += 2)
an8855_phy_write_dev_reg(ds, 0, 0x1f, addr, 0x2020);
for (addr = 0x201; addr <= 0x231; addr += 2)
an8855_phy_write_dev_reg(ds, 0, 0x1f, addr, 0x0020);
}
static void
an8855_eee_setting(struct dsa_switch *ds, u32 port)
{
/* Disable EEE */
an8855_phy_write_dev_reg(ds, port, PHY_DEV07, PHY_DEV07_REG_03C, 0);
}
int
an8855_phy_setup(struct dsa_switch *ds)
{
int ret = 0;
int i;
an8855_phy_setting(ds);
for (i = 0; i < AN8855_NUM_PHYS; i++)
an8855_eee_setting(ds, i);
return ret;
}