433 lines
11 KiB
C
433 lines
11 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <clk.h>
|
|
#include <cpu_func.h>
|
|
#include <dm.h>
|
|
#include <errno.h>
|
|
#include <log.h>
|
|
#include <miiphy.h>
|
|
#include <malloc.h>
|
|
#include <net.h>
|
|
#include <pci.h>
|
|
#include <reset.h>
|
|
#include <asm/cache.h>
|
|
#include <dm/device_compat.h>
|
|
#include <dm/device-internal.h>
|
|
#include <dm/devres.h>
|
|
#include <dm/lists.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/err.h>
|
|
#include <linux/kernel.h>
|
|
#include <asm/io.h>
|
|
#include <power/regulator.h>
|
|
|
|
#if defined(CONFIG_AMLOGIC_ETH)
|
|
#ifdef CONFIG_DM_ETH
|
|
#include <amlogic/pm.h>
|
|
#include <linux/io.h>
|
|
#include <command.h>
|
|
#include <asm/amlogic/arch/secure_apb.h>
|
|
#include <linux/ioport.h>
|
|
#include <asm/amlogic/arch/pwr_ctrl.h>
|
|
#include <asm/amlogic/arch/register.h>
|
|
#include <asm/amlogic/arch/eth.h>
|
|
#include <dm/pinctrl.h>
|
|
#ifdef CONFIG_DM_GPIO
|
|
#include <asm/gpio.h>
|
|
#endif
|
|
|
|
#ifndef ANACTRL_PLL_GATE_DIS
|
|
#define ANACTRL_PLL_GATE_DIS 0xffffffff
|
|
#endif
|
|
|
|
#define AML_ETH_PLL_STS 0x40
|
|
#define AML_ETH_PLL_CTL0 0x44
|
|
#define AML_ETH_PLL_CTL1 0x48
|
|
#define AML_ETH_PLL_CTL2 0x4C
|
|
#define AML_ETH_PLL_CTL3 0x50
|
|
#define AML_ETH_PLL_CTL4 0x54
|
|
#define AML_ETH_PLL_CTL5 0x58
|
|
#define AML_ETH_PLL_CTL6 0x5C
|
|
#define AML_ETH_PLL_CTL7 0x60
|
|
|
|
#define AML_ETH_PHY_CNTL0 0x80
|
|
#define AML_ETH_PHY_CNTL1 0x84
|
|
#define AML_ETH_PHY_CNTL2 0x88
|
|
|
|
|
|
enum {
|
|
/* chip num */
|
|
ETH_PHY = 0x0,
|
|
ETH_PHY_C1 = 0x1,
|
|
ETH_PHY_C2 = 0x2,
|
|
ETH_PHY_SC2 = 0x3,
|
|
};
|
|
|
|
struct aml_phy_dev {
|
|
void __iomem *phy_top;
|
|
void __iomem *phy_cfg;
|
|
struct dev_pm_ops *pm_ops;
|
|
};
|
|
struct aml_phy_dev aml_phy_dev;
|
|
struct aml_phy_dev *p_aml_phy_dev = &aml_phy_dev;
|
|
|
|
int aml_phy_suspend(void *pm_ops);
|
|
int aml_phy_resume(void *pm_ops);
|
|
int aml_phy_poweroff(void *pm_ops);
|
|
|
|
int internal_phy;
|
|
unsigned int setup_amp;
|
|
extern int soc_num;
|
|
extern struct phy_device *p_phydev;
|
|
|
|
void setup_tx_amp(struct udevice *dev)
|
|
{
|
|
unsigned int tx_amp_src = 0;
|
|
tx_amp_src = dev_read_u32_default(dev, "tx_amp_src", 0);
|
|
if (0 == tx_amp_src) {
|
|
printf("not set tx_amp_src\n");
|
|
} else {
|
|
setup_amp = readl((uintptr_t)tx_amp_src);
|
|
printf("addr 0x%x = 0x%x\n", tx_amp_src, readl((uintptr_t)tx_amp_src));
|
|
}
|
|
}
|
|
static void setup_internal_phy(struct udevice *dev)
|
|
{
|
|
u32 mc_val = 0xffffffff;
|
|
int phy_mii_clk_sel;
|
|
int ephy_clk_gate;
|
|
int chip_num = 0;
|
|
int ret = 0;
|
|
struct resource eth_top, eth_cfg;
|
|
|
|
if (dev_read_u32(dev, "mc_val", &mc_val) < 0)
|
|
printf("missing mc_val\n");
|
|
else
|
|
printf("mc_val=0x%x\n", mc_val);
|
|
|
|
if (dev_read_u32(dev, "phy-mii-clk-sel", &phy_mii_clk_sel) < 0)
|
|
phy_mii_clk_sel = -1;
|
|
|
|
if (dev_read_u32(dev, "ephy_clk_gate", &ephy_clk_gate) < 0)
|
|
ephy_clk_gate = -1;
|
|
|
|
if (dev_read_u32(dev, "chip_num", &chip_num) < 0)
|
|
printf("missing chip_num, use 0 as default\n");
|
|
else
|
|
printf("chip_num=%d\n", chip_num);
|
|
|
|
ret = dev_read_resource_byname(dev, "eth_top", ð_top);
|
|
if (ret) {
|
|
printf("can't get eth_top resource(ret = %d)\n", ret);
|
|
return;
|
|
}
|
|
|
|
ret = dev_read_resource_byname(dev, "eth_cfg", ð_cfg);
|
|
if (ret) {
|
|
printf("can't get eth_cfg resource(ret = %d)\n", ret);
|
|
return;
|
|
}
|
|
|
|
p_aml_phy_dev->phy_top = devm_ioremap(dev, eth_top.start, resource_size(ð_top));
|
|
p_aml_phy_dev->phy_cfg = devm_ioremap(dev, eth_cfg.start, resource_size(ð_cfg));
|
|
p_aml_phy_dev->pm_ops = dev_register_pm("eth ops",
|
|
&aml_phy_suspend,
|
|
&aml_phy_resume,
|
|
&aml_phy_poweroff,
|
|
0);
|
|
|
|
setup_tx_amp(dev);
|
|
|
|
/* configure eth_top */
|
|
if (mc_val != 0xffffffff)
|
|
setbits_le32(eth_top.start, mc_val);
|
|
|
|
/* mii_clk_sel */
|
|
if (phy_mii_clk_sel != -1)
|
|
setbits_le32(eth_top.start + 0x28, phy_mii_clk_sel);
|
|
/* configure pll */
|
|
writel(0x07d21003, eth_cfg.start + AML_ETH_PLL_CTL0);
|
|
writel(0x000008c0, eth_cfg.start + AML_ETH_PLL_CTL1);
|
|
udelay(200);
|
|
writel(0x07d21007, eth_cfg.start + AML_ETH_PLL_CTL0);
|
|
udelay(200);
|
|
writel(0x07d21006, eth_cfg.start + AML_ETH_PLL_CTL0);
|
|
udelay(200);
|
|
writel(0x07d21004, eth_cfg.start + AML_ETH_PLL_CTL0);
|
|
udelay(800);
|
|
writel(0xa9860000, eth_cfg.start + AML_ETH_PLL_CTL3);
|
|
writel(0x00004001, eth_cfg.start + AML_ETH_PLL_CTL6);
|
|
writel(0x10800000, eth_cfg.start + AML_ETH_PLL_CTL5);
|
|
writel(0x00003623, eth_cfg.start + AML_ETH_PLL_CTL7);
|
|
|
|
/* configure phy control */
|
|
/* config phyid should between a 0~0xffffffff */
|
|
/* please don't use 44000181, this has been used by internal phy */
|
|
writel(0x33000180, eth_cfg.start + AML_ETH_PHY_CNTL0);
|
|
writel(0x34147, eth_cfg.start + AML_ETH_PHY_CNTL1);
|
|
writel(0x260, eth_cfg.start + AML_ETH_PHY_CNTL2);
|
|
writel(0x42074147, eth_cfg.start + AML_ETH_PHY_CNTL1);
|
|
|
|
/* ephy_clk_gate */
|
|
if (ephy_clk_gate != -1)
|
|
setbits_le32(eth_cfg.start + 0x144, ephy_clk_gate);
|
|
}
|
|
|
|
static void setup_external_phy(struct udevice *dev)
|
|
{
|
|
u32 mc_val = 0xffffffff;
|
|
u32 cali_val = 0xffffffff;
|
|
int ret = 0;
|
|
struct resource eth_top, eth_cfg;
|
|
//struct gpio_desc desc;
|
|
//int ret;
|
|
|
|
#if 0
|
|
if (0) {
|
|
ret = gpio_request_by_name(dev, "reset-gpios", 0, &desc, GPIOD_IS_OUT);
|
|
if (ret) {
|
|
printf("request gpio failed!\n");
|
|
// return ret;
|
|
}
|
|
if (dm_gpio_is_valid(&desc)) {
|
|
dm_gpio_set_value(&desc, 1);
|
|
mdelay(100);
|
|
}
|
|
dm_gpio_free(dev, &desc);
|
|
}
|
|
#endif
|
|
|
|
if (dev_read_u32(dev, "mc_val", &mc_val) < 0)
|
|
printf("missing mc_val\n");
|
|
else
|
|
printf("mc_val=0x%x\n", mc_val);
|
|
|
|
if (dev_read_u32(dev, "cali_val", &cali_val) < 0)
|
|
printf("missing cali_val\n");
|
|
else
|
|
printf("cali_val=0x%x\n", cali_val);
|
|
|
|
/*set rmii pinmux*/
|
|
if (mc_val & 0x4) {
|
|
pinctrl_select_state(dev, "external_eth_rmii_pins");
|
|
printf("set rmii\n");
|
|
}
|
|
/*set rgmii pinmux*/
|
|
if (mc_val & 0x1) {
|
|
pinctrl_select_state(dev, "external_eth_rgmii_pins");
|
|
printf("set rgmii\n");
|
|
}
|
|
ret = dev_read_resource_byname(dev, "eth_top", ð_top);
|
|
if (ret) {
|
|
printf("can't get eth_top resource(ret = %d)\n", ret);
|
|
return;
|
|
}
|
|
|
|
ret = dev_read_resource_byname(dev, "eth_cfg", ð_cfg);
|
|
if (ret) {
|
|
printf("can't get eth_cfg resource(ret = %d)\n", ret);
|
|
return;
|
|
}
|
|
// printf("eth_top 0x%x eth_cfg 0x%x \n", eth_top.start, eth_cfg.start);
|
|
|
|
/* configure eth_top */
|
|
if (mc_val != -1)
|
|
setbits_le32(eth_top.start, mc_val);
|
|
if (cali_val != -1)
|
|
setbits_le32(eth_top.start + 4, cali_val);
|
|
|
|
writel(0x0, eth_cfg.start + AML_ETH_PHY_CNTL2);
|
|
}
|
|
|
|
int aml_phy_suspend(void *pm_ops)
|
|
{
|
|
struct dev_pm_ops *pm = (struct dev_pm_ops *)pm_ops;
|
|
unsigned int phy_setting;
|
|
|
|
printf("disable analog %s\n", pm->name);
|
|
writel(0x00000000, p_aml_phy_dev->phy_cfg + 0x0);
|
|
writel(0x003e0000, p_aml_phy_dev->phy_cfg + 0x4);
|
|
writel(0x12844008, p_aml_phy_dev->phy_cfg + 0x8);
|
|
writel(0x0800a40c, p_aml_phy_dev->phy_cfg + 0xc);
|
|
writel(0x00000000, p_aml_phy_dev->phy_cfg + 0x10);
|
|
writel(0x031d161c, p_aml_phy_dev->phy_cfg + 0x14);
|
|
writel(0x00001683, p_aml_phy_dev->phy_cfg + 0x18);
|
|
writel(0x09c0040a, p_aml_phy_dev->phy_cfg + 0x44);
|
|
|
|
phy_setting = phy_read(p_phydev, MDIO_DEVAD_NONE, 0);
|
|
phy_write(p_phydev, MDIO_DEVAD_NONE, 0, phy_setting | 0x800);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int aml_phy_resume(void *pm_ops)
|
|
{
|
|
struct dev_pm_ops *pm = (struct dev_pm_ops *)pm_ops;
|
|
unsigned int phy_setting;
|
|
|
|
printf("recover analog %s\n", pm->name);
|
|
#if 0
|
|
writel(0x00510630, p_aml_phy_dev->phy_cfg + 0x44);
|
|
writel(0x222210a0, p_aml_phy_dev->phy_cfg + 0x48);
|
|
writel(0x00518630, p_aml_phy_dev->phy_cfg + 0x44);
|
|
udelay(200);
|
|
writel(0x222200a0, p_aml_phy_dev->phy_cfg + 0x48);
|
|
udelay(200);
|
|
writel(0x00118630, p_aml_phy_dev->phy_cfg + 0x44);
|
|
#endif
|
|
writel(0x07d21003, p_aml_phy_dev->phy_cfg + AML_ETH_PLL_CTL0);
|
|
writel(0x000008c0, p_aml_phy_dev->phy_cfg + AML_ETH_PLL_CTL1);
|
|
udelay(200);
|
|
writel(0x07d21007, p_aml_phy_dev->phy_cfg + AML_ETH_PLL_CTL0);
|
|
udelay(200);
|
|
writel(0x07d21006, p_aml_phy_dev->phy_cfg + AML_ETH_PLL_CTL0);
|
|
udelay(200);
|
|
writel(0x07d21004, p_aml_phy_dev->phy_cfg + AML_ETH_PLL_CTL0);
|
|
|
|
udelay(1000);
|
|
writel(0x12804008, p_aml_phy_dev->phy_cfg + 0x8);
|
|
/* phy swr reset release */
|
|
writel(0x0, p_aml_phy_dev->phy_cfg + 0x4);
|
|
|
|
phy_setting = phy_read(p_phydev, MDIO_DEVAD_NONE, 0);
|
|
phy_write(p_phydev, MDIO_DEVAD_NONE, 0, phy_setting & ~(0x800));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int aml_phy_poweroff(void *pm_ops)
|
|
{
|
|
struct dev_pm_ops *pm = (struct dev_pm_ops *)pm_ops;
|
|
|
|
printf("power off %s\n", pm->name);
|
|
aml_phy_suspend(pm_ops);
|
|
return 0;
|
|
}
|
|
|
|
void __iomem *DM_network_interface_setup(struct udevice *dev)
|
|
{
|
|
internal_phy = dev_read_u32_default(dev, "internal_phy", 1);
|
|
if (internal_phy < 0) {
|
|
debug("miss internal_phy item\n");
|
|
}
|
|
debug("internal_phy = 0x%x\n", internal_phy);
|
|
if (internal_phy) {
|
|
printf("in-phy\n");
|
|
setup_internal_phy(dev);
|
|
} else {
|
|
printf("ex-phy\n");
|
|
setup_external_phy(dev);
|
|
}
|
|
soc_num = dev_read_u32_default(dev, "soc_num", 0);
|
|
printf("soc num %d\n", soc_num);
|
|
udelay(1000);
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
static unsigned int return_write_val(struct phy_device *phy_dev, int rd_addr)
|
|
{
|
|
int rd_data;
|
|
int rd_data_hi;
|
|
|
|
phy_write(phy_dev, MDIO_DEVAD_NONE, 20,
|
|
((1 << 15) | (1 << 10) | ((rd_addr & 0x1f) << 5)));
|
|
rd_data = phy_read(phy_dev, MDIO_DEVAD_NONE, 21);
|
|
rd_data_hi = phy_read(phy_dev, MDIO_DEVAD_NONE, 22);
|
|
rd_data = ((rd_data_hi & 0xffff) << 16) | rd_data;
|
|
|
|
return rd_data;
|
|
}
|
|
|
|
static unsigned int phy_tst_write(struct phy_device *phy_dev, unsigned int wr_addr,
|
|
unsigned int wr_data)
|
|
{
|
|
phy_write(phy_dev, MDIO_DEVAD_NONE, 20, 0x0000);
|
|
phy_write(phy_dev, MDIO_DEVAD_NONE, 20, 0x0400);
|
|
phy_write(phy_dev, MDIO_DEVAD_NONE, 20, 0x0000);
|
|
phy_write(phy_dev, MDIO_DEVAD_NONE, 20, 0x0400);
|
|
|
|
if (wr_addr <= 31) {
|
|
phy_write(phy_dev, MDIO_DEVAD_NONE, 23, (wr_data & 0xffff));
|
|
phy_write(phy_dev, MDIO_DEVAD_NONE, 20,
|
|
((1 << 14) | (1 << 10) | ((wr_addr << 0) & 0x1f)));
|
|
pr_info("write phy tstcntl [reg_%d] 0x%x, 0x%x\n",
|
|
wr_addr, wr_data, return_write_val(phy_dev, wr_addr));
|
|
} else {
|
|
pr_info("Invalid parameter\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int phy_tst_read(struct phy_device *phy_dev, unsigned int rd_addr)
|
|
{
|
|
unsigned int rd_data_hi;
|
|
unsigned int rd_data = 0;
|
|
|
|
/*init*/
|
|
phy_write(phy_dev, MDIO_DEVAD_NONE, 20, 0x0000);
|
|
phy_write(phy_dev, MDIO_DEVAD_NONE, 20, 0x0400);
|
|
phy_write(phy_dev, MDIO_DEVAD_NONE, 20, 0x0000);
|
|
phy_write(phy_dev, MDIO_DEVAD_NONE, 20, 0x0400);
|
|
|
|
if (rd_addr <= 31) {
|
|
phy_write(phy_dev, MDIO_DEVAD_NONE, 20,
|
|
((1 << 15) | (1 << 10) | ((rd_addr & 0x1f) << 5)));
|
|
|
|
rd_data = phy_read(phy_dev, MDIO_DEVAD_NONE, 21);
|
|
rd_data_hi = phy_read(phy_dev, MDIO_DEVAD_NONE, 22);
|
|
rd_data = ((rd_data_hi & 0xffff) << 16) | rd_data;
|
|
printf("read tstcntl phy [reg_%d] 0x%x\n", rd_addr, rd_data);
|
|
} else {
|
|
printf("Invalid parameter\n");
|
|
}
|
|
|
|
return rd_data;
|
|
}
|
|
#endif
|
|
|
|
void DM_network_interface_setup_final(struct phy_device *phydev)
|
|
{
|
|
#if 0
|
|
unsigned int reg_val;
|
|
|
|
if (internal_phy) {
|
|
phy_tst_write(phydev, 0x18, 0x8);
|
|
phy_tst_write(phydev, 0x16, 0x8406);
|
|
phy_tst_write(phydev, 0x15, 0x2408);
|
|
}
|
|
|
|
reg_val = phy_tst_read(phydev, 20);
|
|
printf("A3_CONFIG=0x%X\n", reg_val);
|
|
reg_val = phy_tst_read(phydev, 21);
|
|
printf("A4_CONFIG=0x%X\n", reg_val);
|
|
reg_val = phy_tst_read(phydev, 22);
|
|
printf("A5_CONFIG=0x%X\n", reg_val);
|
|
reg_val = phy_tst_read(phydev, 23);
|
|
printf("A6_CONFIG=0x%X\n", reg_val);
|
|
#endif
|
|
if (internal_phy) {
|
|
printf("disable eee\n");
|
|
phy_write(phydev, MDIO_DEVAD_NONE, 13, 0x7);
|
|
phy_write(phydev, MDIO_DEVAD_NONE, 14, 0x3c);
|
|
phy_write(phydev, MDIO_DEVAD_NONE, 13, 0x4007);
|
|
phy_write(phydev, MDIO_DEVAD_NONE, 14, 0x0);
|
|
printf("restart auto-neg\n");
|
|
genphy_restart_aneg(phydev);
|
|
}
|
|
}
|
|
|
|
void __iomem *DM_network_interface_remove(void)
|
|
{
|
|
if (p_aml_phy_dev->pm_ops)
|
|
dev_unregister_pm(p_aml_phy_dev->pm_ops);
|
|
return 0;
|
|
}
|
|
#endif
|
|
#endif
|
|
|