Merge branch 'master' of git://git.denx.de/u-boot-net

This commit is contained in:
Tom Rini 2019-07-15 18:56:24 -04:00
commit 9c94e0a644
32 changed files with 1503 additions and 622 deletions

View File

@ -123,6 +123,8 @@ config SANDBOX
imply DM_SOUND
imply PCI_SANDBOX_EP
imply PCH
imply PHYLIB
imply DM_MDIO
config SH
bool "SuperH architecture"

View File

@ -64,6 +64,7 @@
/* MCUSS Range */
<0x00 0x28380000 0x00 0x28380000 0x00 0x03880000>,
<0x00 0x40200000 0x00 0x40200000 0x00 0x00900100>,
<0x00 0x40f00000 0x00 0x40f00000 0x00 0x00020000>,
<0x00 0x42040000 0x00 0x42040000 0x00 0x03ac2400>,
<0x00 0x45100000 0x00 0x45100000 0x00 0x00c24000>,
<0x00 0x46000000 0x00 0x46000000 0x00 0x00200000>,
@ -75,6 +76,7 @@
#size-cells = <2>;
ranges = <0x00 0x28380000 0x00 0x28380000 0x00 0x03880000>, /* MCU NAVSS*/
<0x00 0x40200000 0x00 0x40200000 0x00 0x00900100>, /* First peripheral window */
<0x00 0x40f00000 0x00 0x40f00000 0x00 0x00020000>, /* CTRL_MMR0 */
<0x00 0x42040000 0x00 0x42040000 0x00 0x03ac2400>, /* WKUP */
<0x00 0x45100000 0x00 0x45100000 0x00 0x00c24000>, /* MMRs, remaining NAVSS */
<0x00 0x46000000 0x00 0x46000000 0x00 0x00200000>, /* CPSW */

View File

@ -5,6 +5,7 @@
#include <dt-bindings/pinctrl/k3.h>
#include <dt-bindings/dma/k3-udma.h>
#include <dt-bindings/net/ti-dp83867.h>
/ {
chosen {
@ -13,6 +14,7 @@
aliases {
serial2 = &main_uart0;
ethernet0 = &cpsw_port1;
};
};
@ -110,6 +112,116 @@
dma-coherent;
};
};
mcu_conf: scm_conf@40f00000 {
compatible = "syscon";
reg = <0x0 0x40f00000 0x0 0x20000>;
};
mcu_cpsw: cpsw_nuss@046000000 {
compatible = "ti,am654-cpsw-nuss";
#address-cells = <2>;
#size-cells = <2>;
reg = <0x0 0x46000000 0x0 0x200000>;
reg-names = "cpsw_nuss";
ranges;
dma-coherent;
clocks = <&k3_clks 5 10>;
clock-names = "fck";
power-domains = <&k3_pds 5>;
ti,psil-base = <0x7000>;
dmas = <&mcu_udmap &mcu_cpsw 0 UDMA_DIR_TX>,
<&mcu_udmap &mcu_cpsw 1 UDMA_DIR_TX>,
<&mcu_udmap &mcu_cpsw 2 UDMA_DIR_TX>,
<&mcu_udmap &mcu_cpsw 3 UDMA_DIR_TX>,
<&mcu_udmap &mcu_cpsw 4 UDMA_DIR_TX>,
<&mcu_udmap &mcu_cpsw 5 UDMA_DIR_TX>,
<&mcu_udmap &mcu_cpsw 6 UDMA_DIR_TX>,
<&mcu_udmap &mcu_cpsw 7 UDMA_DIR_TX>,
<&mcu_udmap &mcu_cpsw 0 UDMA_DIR_RX>;
dma-names = "tx0", "tx1", "tx2", "tx3",
"tx4", "tx5", "tx6", "tx7",
"rx";
ports {
#address-cells = <1>;
#size-cells = <0>;
host: host@0 {
reg = <0>;
ti,label = "host";
};
cpsw_port1: port@1 {
reg = <1>;
ti,mac-only;
ti,label = "port1";
ti,syscon-efuse = <&mcu_conf 0x200>;
};
};
davinci_mdio: mdio {
#address-cells = <1>;
#size-cells = <0>;
bus_freq = <1000000>;
};
ti,psil-config0 {
linux,udma-mode = <UDMA_PKT_MODE>;
statictr-type = <PSIL_STATIC_TR_NONE>;
ti,needs-epib;
ti,psd-size = <16>;
};
ti,psil-config1 {
linux,udma-mode = <UDMA_PKT_MODE>;
statictr-type = <PSIL_STATIC_TR_NONE>;
ti,needs-epib;
ti,psd-size = <16>;
};
ti,psil-config2 {
linux,udma-mode = <UDMA_PKT_MODE>;
statictr-type = <PSIL_STATIC_TR_NONE>;
ti,needs-epib;
ti,psd-size = <16>;
};
ti,psil-config3 {
linux,udma-mode = <UDMA_PKT_MODE>;
statictr-type = <PSIL_STATIC_TR_NONE>;
ti,needs-epib;
ti,psd-size = <16>;
};
ti,psil-config4 {
linux,udma-mode = <UDMA_PKT_MODE>;
statictr-type = <PSIL_STATIC_TR_NONE>;
ti,needs-epib;
ti,psd-size = <16>;
};
ti,psil-config5 {
linux,udma-mode = <UDMA_PKT_MODE>;
statictr-type = <PSIL_STATIC_TR_NONE>;
ti,needs-epib;
ti,psd-size = <16>;
};
ti,psil-config6 {
linux,udma-mode = <UDMA_PKT_MODE>;
statictr-type = <PSIL_STATIC_TR_NONE>;
ti,needs-epib;
ti,psd-size = <16>;
};
ti,psil-config7 {
linux,udma-mode = <UDMA_PKT_MODE>;
statictr-type = <PSIL_STATIC_TR_NONE>;
ti,needs-epib;
ti,psd-size = <16>;
};
};
};
&cbass_wakeup {
@ -189,6 +301,32 @@
u-boot,dm-spl;
};
&wkup_pmx0 {
mcu_cpsw_pins_default: mcu_cpsw_pins_default {
pinctrl-single,pins = <
AM65X_WKUP_IOPAD(0x0058, PIN_OUTPUT, 0) /* (N4) MCU_RGMII1_TX_CTL */
AM65X_WKUP_IOPAD(0x005c, PIN_INPUT, 0) /* (N5) MCU_RGMII1_RX_CTL */
AM65X_WKUP_IOPAD(0x0060, PIN_OUTPUT, 0) /* (M2) MCU_RGMII1_TD3 */
AM65X_WKUP_IOPAD(0x0064, PIN_OUTPUT, 0) /* (M3) MCU_RGMII1_TD2 */
AM65X_WKUP_IOPAD(0x0068, PIN_OUTPUT, 0) /* (M4) MCU_RGMII1_TD1 */
AM65X_WKUP_IOPAD(0x006c, PIN_OUTPUT, 0) /* (M5) MCU_RGMII1_TD0 */
AM65X_WKUP_IOPAD(0x0078, PIN_INPUT, 0) /* (L2) MCU_RGMII1_RD3 */
AM65X_WKUP_IOPAD(0x007c, PIN_INPUT, 0) /* (L5) MCU_RGMII1_RD2 */
AM65X_WKUP_IOPAD(0x0080, PIN_INPUT, 0) /* (M6) MCU_RGMII1_RD1 */
AM65X_WKUP_IOPAD(0x0084, PIN_INPUT, 0) /* (L6) MCU_RGMII1_RD0 */
AM65X_WKUP_IOPAD(0x0070, PIN_INPUT, 0) /* (N1) MCU_RGMII1_TXC */
AM65X_WKUP_IOPAD(0x0074, PIN_INPUT, 0) /* (M1) MCU_RGMII1_RXC */
>;
};
mcu_mdio_pins_default: mcu_mdio1_pins_default {
pinctrl-single,pins = <
AM65X_WKUP_IOPAD(0x008c, PIN_OUTPUT, 0) /* (L1) MCU_MDIO0_MDC */
AM65X_WKUP_IOPAD(0x0088, PIN_INPUT, 0) /* (L4) MCU_MDIO0_MDIO */
>;
};
};
&main_uart0 {
u-boot,dm-spl;
pinctrl-names = "default";
@ -212,3 +350,35 @@
pinctrl-0 = <&main_mmc1_pins_default>;
sdhci-caps-mask = <0x7 0x0>;
};
&mcu_cpsw {
pinctrl-names = "default";
pinctrl-0 = <&mcu_cpsw_pins_default &mcu_mdio_pins_default>;
};
&davinci_mdio {
phy0: ethernet-phy@0 {
reg = <0>;
/* TODO: phy reset: TCA9555RTWR(i2c:0x21)[p04].GPIO_MCU_RGMII_RSTN */
ti,rx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
ti,tx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
ti,fifo-depth = <DP83867_PHYCR_FIFO_DEPTH_4_B_NIB>;
};
};
&cpsw_port1 {
phy-mode = "rgmii-id";
phy-handle = <&phy0>;
};
&mcu_cpsw {
reg = <0x0 0x46000000 0x0 0x200000>,
<0x0 0x40f00200 0x0 0x2>;
reg-names = "cpsw_nuss", "mac_efuse";
cpsw-phy-sel@40f04040 {
compatible = "ti,am654-cpsw-phy-sel";
reg= <0x0 0x40f04040 0x0 0x4>;
reg-names = "gmii-sel";
};
};

View File

@ -823,6 +823,10 @@
dmas = <&dma 0>, <&dma 1>, <&dma 2>;
dma-names = "m2m", "tx0", "rx0";
};
mdio-test {
compatible = "sandbox,mdio";
};
};
#include "sandbox_pmic.dtsi"

View File

@ -203,6 +203,11 @@ static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
if (argc < 2)
return CMD_RET_USAGE;
#ifdef CONFIG_DM_MDIO
/* probe DM MII device before any operation so they are all accesible */
dm_mdio_probe_devices();
#endif
/*
* We use the last specified parameters, unless new ones are
* entered.

183
cmd/mii.c
View File

@ -12,25 +12,11 @@
#include <command.h>
#include <miiphy.h>
typedef struct _MII_reg_desc_t {
ushort regno;
char * name;
} MII_reg_desc_t;
static const MII_reg_desc_t reg_0_5_desc_tbl[] = {
{ MII_BMCR, "PHY control register" },
{ MII_BMSR, "PHY status register" },
{ MII_PHYSID1, "PHY ID 1 register" },
{ MII_PHYSID2, "PHY ID 2 register" },
{ MII_ADVERTISE, "Autonegotiation advertisement register" },
{ MII_LPA, "Autonegotiation partner abilities register" },
};
typedef struct _MII_field_desc_t {
ushort hi;
ushort lo;
ushort mask;
char * name;
const char *name;
} MII_field_desc_t;
static const MII_field_desc_t reg_0_desc_tbl[] = {
@ -87,7 +73,7 @@ static const MII_field_desc_t reg_4_desc_tbl[] = {
{ 7, 7, 0x01, "100BASE-TX able" },
{ 6, 6, 0x01, "10BASE-T full duplex able" },
{ 5, 5, 0x01, "10BASE-T able" },
{ 4, 0, 0x1f, "xxx to do" },
{ 4, 0, 0x1f, "selector" },
};
static const MII_field_desc_t reg_5_desc_tbl[] = {
@ -102,50 +88,91 @@ static const MII_field_desc_t reg_5_desc_tbl[] = {
{ 7, 7, 0x01, "100BASE-TX able" },
{ 6, 6, 0x01, "10BASE-T full duplex able" },
{ 5, 5, 0x01, "10BASE-T able" },
{ 4, 0, 0x1f, "xxx to do" },
{ 4, 0, 0x1f, "partner selector" },
};
typedef struct _MII_field_desc_and_len_t {
static const MII_field_desc_t reg_9_desc_tbl[] = {
{ 15, 13, 0x07, "test mode" },
{ 12, 12, 0x01, "manual master/slave enable" },
{ 11, 11, 0x01, "manual master/slave value" },
{ 10, 10, 0x01, "multi/single port" },
{ 9, 9, 0x01, "1000BASE-T full duplex able" },
{ 8, 8, 0x01, "1000BASE-T half duplex able" },
{ 7, 7, 0x01, "automatic TDR on link down" },
{ 6, 6, 0x7f, "(reserved)" },
};
static const MII_field_desc_t reg_10_desc_tbl[] = {
{ 15, 15, 0x01, "master/slave config fault" },
{ 14, 14, 0x01, "master/slave config result" },
{ 13, 13, 0x01, "local receiver status OK" },
{ 12, 12, 0x01, "remote receiver status OK" },
{ 11, 11, 0x01, "1000BASE-T full duplex able" },
{ 10, 10, 0x01, "1000BASE-T half duplex able" },
{ 9, 8, 0x03, "(reserved)" },
{ 7, 0, 0xff, "1000BASE-T idle error counter"},
};
typedef struct _MII_reg_desc_t {
ushort regno;
const MII_field_desc_t *pdesc;
ushort len;
} MII_field_desc_and_len_t;
const char *name;
} MII_reg_desc_t;
static const MII_field_desc_and_len_t desc_and_len_tbl[] = {
{ reg_0_desc_tbl, ARRAY_SIZE(reg_0_desc_tbl) },
{ reg_1_desc_tbl, ARRAY_SIZE(reg_1_desc_tbl) },
{ reg_2_desc_tbl, ARRAY_SIZE(reg_2_desc_tbl) },
{ reg_3_desc_tbl, ARRAY_SIZE(reg_3_desc_tbl) },
{ reg_4_desc_tbl, ARRAY_SIZE(reg_4_desc_tbl) },
{ reg_5_desc_tbl, ARRAY_SIZE(reg_5_desc_tbl) },
static const MII_reg_desc_t mii_reg_desc_tbl[] = {
{ MII_BMCR, reg_0_desc_tbl, ARRAY_SIZE(reg_0_desc_tbl),
"PHY control register" },
{ MII_BMSR, reg_1_desc_tbl, ARRAY_SIZE(reg_1_desc_tbl),
"PHY status register" },
{ MII_PHYSID1, reg_2_desc_tbl, ARRAY_SIZE(reg_2_desc_tbl),
"PHY ID 1 register" },
{ MII_PHYSID2, reg_3_desc_tbl, ARRAY_SIZE(reg_3_desc_tbl),
"PHY ID 2 register" },
{ MII_ADVERTISE, reg_4_desc_tbl, ARRAY_SIZE(reg_4_desc_tbl),
"Autonegotiation advertisement register" },
{ MII_LPA, reg_5_desc_tbl, ARRAY_SIZE(reg_5_desc_tbl),
"Autonegotiation partner abilities register" },
{ MII_CTRL1000, reg_9_desc_tbl, ARRAY_SIZE(reg_9_desc_tbl),
"1000BASE-T control register" },
{ MII_STAT1000, reg_10_desc_tbl, ARRAY_SIZE(reg_10_desc_tbl),
"1000BASE-T status register" },
};
static void dump_reg(
ushort regval,
const MII_reg_desc_t *prd,
const MII_field_desc_and_len_t *pdl);
const MII_reg_desc_t *prd);
static int special_field(
ushort regno,
const MII_field_desc_t *pdesc,
ushort regval);
static bool special_field(ushort regno, const MII_field_desc_t *pdesc,
ushort regval);
static void MII_dump_0_to_5(
ushort regvals[6],
uchar reglo,
uchar reghi)
static void MII_dump(const ushort *regvals, uchar reglo, uchar reghi)
{
ulong i;
for (i = 0; i < 6; i++) {
if ((reglo <= i) && (i <= reghi))
dump_reg(regvals[i], &reg_0_5_desc_tbl[i],
&desc_and_len_tbl[i]);
for (i = 0; i < ARRAY_SIZE(mii_reg_desc_tbl); i++) {
const uchar reg = mii_reg_desc_tbl[i].regno;
if (reg >= reglo && reg <= reghi)
dump_reg(regvals[reg - reglo], &mii_reg_desc_tbl[i]);
}
}
/* Print out field position, value, name */
static void dump_field(const MII_field_desc_t *pdesc, ushort regval)
{
if (pdesc->hi == pdesc->lo)
printf("%2u ", pdesc->lo);
else
printf("%2u-%2u", pdesc->hi, pdesc->lo);
printf(" = %5u %s", (regval >> pdesc->lo) & pdesc->mask,
pdesc->name);
}
static void dump_reg(
ushort regval,
const MII_reg_desc_t *prd,
const MII_field_desc_and_len_t *pdl)
const MII_reg_desc_t *prd)
{
ulong i;
ushort mask_in_place;
@ -154,8 +181,8 @@ static void dump_reg(
printf("%u. (%04hx) -- %s --\n",
prd->regno, regval, prd->name);
for (i = 0; i < pdl->len; i++) {
pdesc = &pdl->pdesc[i];
for (i = 0; i < prd->len; i++) {
pdesc = &prd->pdesc[i];
mask_in_place = pdesc->mask << pdesc->lo;
@ -164,17 +191,8 @@ static void dump_reg(
regval & mask_in_place,
prd->regno);
if (special_field(prd->regno, pdesc, regval)) {
}
else {
if (pdesc->hi == pdesc->lo)
printf("%2u ", pdesc->lo);
else
printf("%2u-%2u", pdesc->hi, pdesc->lo);
printf(" = %5u %s",
(regval & mask_in_place) >> pdesc->lo,
pdesc->name);
}
if (!special_field(prd->regno, pdesc, regval))
dump_field(pdesc, regval);
printf("\n");
}
@ -190,11 +208,11 @@ static void dump_reg(
** 5.4-0
*/
static int special_field(
ushort regno,
const MII_field_desc_t *pdesc,
ushort regval)
static bool special_field(ushort regno, const MII_field_desc_t *pdesc,
ushort regval)
{
const ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask;
if ((regno == MII_BMCR) && (pdesc->lo == 6)) {
ushort speed_bits = regval & (BMCR_SPEED1000 | BMCR_SPEED100);
printf("%2u,%2u = b%u%u speed selection = %s Mbps",
@ -208,34 +226,26 @@ static int special_field(
}
else if ((regno == MII_BMCR) && (pdesc->lo == 8)) {
printf("%2u = %5u duplex = %s",
pdesc->lo,
(regval >> pdesc->lo) & 1,
((regval >> pdesc->lo) & 1) ? "full" : "half");
dump_field(pdesc, regval);
printf(" = %s", ((regval >> pdesc->lo) & 1) ? "full" : "half");
return 1;
}
else if ((regno == MII_ADVERTISE) && (pdesc->lo == 0)) {
ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask;
printf("%2u-%2u = %5u selector = %s",
pdesc->hi, pdesc->lo, sel_bits,
sel_bits == PHY_ANLPAR_PSB_802_3 ?
"IEEE 802.3" :
sel_bits == PHY_ANLPAR_PSB_802_9 ?
"IEEE 802.9 ISLAN-16T" :
"???");
dump_field(pdesc, regval);
printf(" = %s",
sel_bits == PHY_ANLPAR_PSB_802_3 ? "IEEE 802.3 CSMA/CD" :
sel_bits == PHY_ANLPAR_PSB_802_9 ?
"IEEE 802.9 ISLAN-16T" : "???");
return 1;
}
else if ((regno == MII_LPA) && (pdesc->lo == 0)) {
ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask;
printf("%2u-%2u = %u selector = %s",
pdesc->hi, pdesc->lo, sel_bits,
sel_bits == PHY_ANLPAR_PSB_802_3 ?
"IEEE 802.3" :
sel_bits == PHY_ANLPAR_PSB_802_9 ?
"IEEE 802.9 ISLAN-16T" :
"???");
dump_field(pdesc, regval);
printf(" = %s",
sel_bits == PHY_ANLPAR_PSB_802_3 ? "IEEE 802.3 CSMA/CD" :
sel_bits == PHY_ANLPAR_PSB_802_9 ?
"IEEE 802.9 ISLAN-16T" : "???");
return 1;
}
@ -406,17 +416,16 @@ static int do_mii(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
}
} else if (strncmp(op, "du", 2) == 0) {
ushort regs[6];
ushort regs[MII_STAT1000 + 1]; /* Last reg is 0x0a */
int ok = 1;
if ((reglo > 5) || (reghi > 5)) {
printf(
"The MII dump command only formats the "
"standard MII registers, 0-5.\n");
if (reglo > MII_STAT1000 || reghi > MII_STAT1000) {
printf("The MII dump command only formats the standard MII registers, 0-5, 9-a.\n");
return 1;
}
for (addr = addrlo; addr <= addrhi; addr++) {
for (reg = reglo; reg < reghi + 1; reg++) {
if (miiphy_read(devname, addr, reg, &regs[reg]) != 0) {
for (reg = reglo; reg <= reghi; reg++) {
if (miiphy_read(devname, addr, reg,
&regs[reg - reglo]) != 0) {
ok = 0;
printf(
"Error reading from the PHY addr=%02x reg=%02x\n",
@ -425,7 +434,7 @@ static int do_mii(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
}
if (ok)
MII_dump_0_to_5(regs, reglo, reghi);
MII_dump(regs, reglo, reghi);
printf("\n");
}
} else if (strncmp(op, "de", 2) == 0) {

View File

@ -44,6 +44,7 @@ CONFIG_SPL_MULTI_DTB_FIT_NO_COMPRESSION=y
CONFIG_ENV_IS_IN_FAT=y
CONFIG_ENV_FAT_INTERFACE="mmc"
CONFIG_ENV_FAT_DEVICE_AND_PART="1:1"
CONFIG_NET_RANDOM_ETHADDR=y
CONFIG_DM=y
CONFIG_SPL_DM=y
CONFIG_SPL_DM_SEQ_ALIAS=y
@ -58,6 +59,11 @@ CONFIG_K3_SEC_PROXY=y
CONFIG_DM_MMC=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_K3_ARASAN=y
CONFIG_PHY_TI=y
CONFIG_PHY_FIXED=y
CONFIG_DM_ETH=y
CONFIG_TI_AM65_CPSW_NUSS=y
CONFIG_PHY=y
CONFIG_PINCTRL=y
# CONFIG_PINCTRL_GENERIC is not set
CONFIG_SPL_PINCTRL=y

View File

@ -11,6 +11,29 @@ config DM_ETH
This is currently implemented in net/eth-uclass.c
Look in include/net.h for details.
config DM_MDIO
bool "Enable Driver Model for MDIO devices"
depends on DM_ETH && PHYLIB
help
Enable driver model for MDIO devices
Adds UCLASS_MDIO DM class supporting MDIO buses that are probed as
stand-alone devices. Useful in particular for systems that support
DM_ETH and have a stand-alone MDIO hardware block shared by multiple
Ethernet interfaces.
This is currently implemented in net/mdio-uclass.c
Look in include/miiphy.h for details.
config MDIO_SANDBOX
depends on DM_MDIO && SANDBOX
default y
bool "Sandbox: Mocked MDIO driver"
help
This driver implements dummy read/write/reset MDIO functions mimicking
a bus with a single PHY.
This driver is used in for testing in test/dm/mdio.c
menuconfig NETDEVICES
bool "Network device support"
depends on NET

View File

@ -77,3 +77,4 @@ obj-y += ti/
obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o
obj-y += mscc_eswitch/
obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o
obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o

View File

@ -613,10 +613,12 @@ static int macb_phy_init(struct macb_device *macb, const char *name)
/* First check for GMAC and that it is GiB capable */
if (gem_is_gigabit_capable(macb)) {
lpa = macb_mdio_read(macb, MII_STAT1000);
lpa = macb_mdio_read(macb, MII_LPA);
if (lpa & (LPA_1000FULL | LPA_1000HALF)) {
duplex = ((lpa & LPA_1000FULL) ? 1 : 0);
if (lpa & (LPA_1000FULL | LPA_1000HALF | LPA_1000XFULL |
LPA_1000XHALF)) {
duplex = ((lpa & (LPA_1000FULL | LPA_1000XFULL)) ?
1 : 0);
printf("%s: link up, 1000Mbps %s-duplex (lpa: 0x%04x)\n",
name,

View File

@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2019
* Alex Marginean, NXP
*/
#include <dm.h>
#include <errno.h>
#include <miiphy.h>
#define SANDBOX_PHY_ADDR 5
#define SANDBOX_PHY_REG 0
struct mdio_sandbox_priv {
int enabled;
u16 reg;
};
static int mdio_sandbox_read(struct udevice *dev, int addr, int devad, int reg)
{
struct mdio_sandbox_priv *priv = dev_get_priv(dev);
if (!priv->enabled)
return -ENODEV;
if (addr != SANDBOX_PHY_ADDR)
return -ENODEV;
if (devad != MDIO_DEVAD_NONE)
return -ENODEV;
if (reg != SANDBOX_PHY_REG)
return -ENODEV;
return priv->reg;
}
static int mdio_sandbox_write(struct udevice *dev, int addr, int devad, int reg,
u16 val)
{
struct mdio_sandbox_priv *priv = dev_get_priv(dev);
if (!priv->enabled)
return -ENODEV;
if (addr != SANDBOX_PHY_ADDR)
return -ENODEV;
if (devad != MDIO_DEVAD_NONE)
return -ENODEV;
if (reg != SANDBOX_PHY_REG)
return -ENODEV;
priv->reg = val;
return 0;
}
static int mdio_sandbox_reset(struct udevice *dev)
{
struct mdio_sandbox_priv *priv = dev_get_priv(dev);
priv->reg = 0;
return 0;
}
static const struct mdio_ops mdio_sandbox_ops = {
.read = mdio_sandbox_read,
.write = mdio_sandbox_write,
.reset = mdio_sandbox_reset,
};
static int mdio_sandbox_probe(struct udevice *dev)
{
struct mdio_sandbox_priv *priv = dev_get_priv(dev);
priv->enabled = 1;
return 0;
}
static const struct udevice_id mdio_sandbox_ids[] = {
{ .compatible = "sandbox,mdio" },
{ }
};
U_BOOT_DRIVER(mdio_sandbox) = {
.name = "mdio_sandbox",
.id = UCLASS_MDIO,
.of_match = mdio_sandbox_ids,
.probe = mdio_sandbox_probe,
.ops = &mdio_sandbox_ops,
.priv_auto_alloc_size = sizeof(struct mdio_sandbox_priv),
};

View File

@ -1,6 +1,6 @@
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += ocelot_switch.o mscc_xfer.o mscc_mac_table.o
obj-$(CONFIG_MSCC_LUTON_SWITCH) += luton_switch.o mscc_xfer.o mscc_mac_table.o
obj-$(CONFIG_MSCC_JR2_SWITCH) += jr2_switch.o mscc_xfer.o
obj-$(CONFIG_MSCC_SERVALT_SWITCH) += servalt_switch.o mscc_xfer.o
obj-$(CONFIG_MSCC_SERVAL_SWITCH) += serval_switch.o mscc_xfer.o mscc_mac_table.o
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += ocelot_switch.o mscc_xfer.o mscc_mac_table.o mscc_miim.o
obj-$(CONFIG_MSCC_LUTON_SWITCH) += luton_switch.o mscc_xfer.o mscc_mac_table.o mscc_miim.o
obj-$(CONFIG_MSCC_JR2_SWITCH) += jr2_switch.o mscc_xfer.o mscc_miim.o
obj-$(CONFIG_MSCC_SERVALT_SWITCH) += servalt_switch.o mscc_xfer.o mscc_miim.o
obj-$(CONFIG_MSCC_SERVAL_SWITCH) += serval_switch.o mscc_xfer.o mscc_mac_table.o mscc_miim.o

View File

@ -17,20 +17,7 @@
#include <dt-bindings/mscc/jr2_data.h>
#include "mscc_xfer.h"
#define GCB_MIIM_MII_STATUS 0x0
#define GCB_MIIM_STAT_BUSY BIT(3)
#define GCB_MIIM_MII_CMD 0x8
#define GCB_MIIM_MII_CMD_SCAN BIT(0)
#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1)
#define GCB_MIIM_MII_CMD_OPR_READ BIT(2)
#define GCB_MIIM_MII_CMD_SINGLE_SCAN BIT(3)
#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4)
#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20)
#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25)
#define GCB_MIIM_MII_CMD_VLD BIT(31)
#define GCB_MIIM_DATA 0xC
#define GCB_MIIM_DATA_ERROR (0x3 << 16)
#include "mscc_miim.h"
#define ANA_AC_RAM_CTRL_RAM_INIT 0x94358
#define ANA_AC_STAT_GLOBAL_CFG_PORT_RESET 0x94370
@ -279,13 +266,6 @@ struct jr2_private {
struct jr2_phy_port_t ports[MAX_PORT];
};
struct jr2_miim_dev {
void __iomem *regs;
phys_addr_t miim_base;
unsigned long miim_size;
struct mii_dev *bus;
};
static const unsigned long jr2_regs_qs[] = {
[MSCC_QS_XTR_RD] = 0x8,
[MSCC_QS_XTR_FLUSH] = 0x18,
@ -294,99 +274,9 @@ static const unsigned long jr2_regs_qs[] = {
[MSCC_QS_INJ_CTRL] = 0x34,
};
static struct jr2_miim_dev miim[JR2_MIIM_BUS_COUNT];
static struct mscc_miim_dev miim[JR2_MIIM_BUS_COUNT];
static int miim_count = -1;
static int mscc_miim_wait_ready(struct jr2_miim_dev *miim)
{
unsigned long deadline;
u32 val;
deadline = timer_get_us() + 250000;
do {
val = readl(miim->regs + GCB_MIIM_MII_STATUS);
} while (timer_get_us() <= deadline && (val & GCB_MIIM_STAT_BUSY));
if (val & GCB_MIIM_STAT_BUSY)
return -ETIMEDOUT;
return 0;
}
static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg)
{
struct jr2_miim_dev *miim = (struct jr2_miim_dev *)bus->priv;
u32 val;
int ret;
ret = mscc_miim_wait_ready(miim);
if (ret)
goto out;
writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) |
GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ,
miim->regs + GCB_MIIM_MII_CMD);
ret = mscc_miim_wait_ready(miim);
if (ret)
goto out;
val = readl(miim->regs + GCB_MIIM_DATA);
if (val & GCB_MIIM_DATA_ERROR) {
ret = -EIO;
goto out;
}
ret = val & 0xFFFF;
out:
return ret;
}
static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg,
u16 val)
{
struct jr2_miim_dev *miim = (struct jr2_miim_dev *)bus->priv;
int ret;
ret = mscc_miim_wait_ready(miim);
if (ret < 0)
goto out;
writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) |
GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) |
GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD);
out:
return ret;
}
static struct mii_dev *jr2_mdiobus_init(phys_addr_t miim_base,
unsigned long miim_size)
{
struct mii_dev *bus;
bus = mdio_alloc();
if (!bus)
return NULL;
++miim_count;
sprintf(bus->name, "miim-bus%d", miim_count);
miim[miim_count].regs = ioremap(miim_base, miim_size);
miim[miim_count].miim_base = miim_base;
miim[miim_count].miim_size = miim_size;
bus->priv = &miim[miim_count];
bus->read = mscc_miim_read;
bus->write = mscc_miim_write;
if (mdio_register(bus))
return NULL;
miim[miim_count].bus = bus;
return bus;
}
static void jr2_cpu_capture_setup(struct jr2_private *priv)
{
/* ASM: No preamble and IFH prefix on CPU injected frames */
@ -973,7 +863,7 @@ static int jr2_probe(struct udevice *dev)
}
/* Initialize miim buses */
memset(&miim, 0x0, sizeof(struct jr2_miim_dev) * JR2_MIIM_BUS_COUNT);
memset(&miim, 0x0, sizeof(struct mscc_miim_dev) * JR2_MIIM_BUS_COUNT);
/* iterate all the ports and find out on which bus they are */
i = 0;
@ -1008,7 +898,8 @@ static int jr2_probe(struct udevice *dev)
/* If the bus is new then create a new bus */
if (!get_mdiobus(addr_base, addr_size))
priv->bus[miim_count] =
jr2_mdiobus_init(addr_base, addr_size);
mscc_mdiobus_init(miim, &miim_count, addr_base,
addr_size);
/* Connect mdio bus with the port */
bus = get_mdiobus(addr_base, addr_size);

View File

@ -17,18 +17,7 @@
#include "mscc_xfer.h"
#include "mscc_mac_table.h"
#define GCB_MIIM_MII_STATUS 0x0
#define GCB_MIIM_STAT_BUSY BIT(3)
#define GCB_MIIM_MII_CMD 0x8
#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1)
#define GCB_MIIM_MII_CMD_OPR_READ BIT(2)
#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4)
#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20)
#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25)
#define GCB_MIIM_MII_CMD_VLD BIT(31)
#define GCB_MIIM_DATA 0xC
#define GCB_MIIM_DATA_ERROR (0x2 << 16)
#include "mscc_miim.h"
#define ANA_PORT_VLAN_CFG(x) (0x00 + 0x80 * (x))
#define ANA_PORT_VLAN_CFG_AWARE_ENA BIT(20)
@ -189,13 +178,6 @@ struct luton_private {
struct luton_phy_port_t ports[MAX_PORT];
};
struct mscc_miim_dev {
void __iomem *regs;
phys_addr_t miim_base;
unsigned long miim_size;
struct mii_dev *bus;
};
static const unsigned long luton_regs_qs[] = {
[MSCC_QS_XTR_RD] = 0x18,
[MSCC_QS_XTR_FLUSH] = 0x28,
@ -213,84 +195,6 @@ static const unsigned long luton_regs_ana_table[] = {
static struct mscc_miim_dev miim[LUTON_MIIM_BUS_COUNT];
static int miim_count = -1;
static int mscc_miim_wait_ready(struct mscc_miim_dev *miim)
{
return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS,
GCB_MIIM_STAT_BUSY, false, 250, false);
}
static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg)
{
struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv;
u32 val;
int ret;
ret = mscc_miim_wait_ready(miim);
if (ret)
goto out;
writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) |
GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ,
miim->regs + GCB_MIIM_MII_CMD);
ret = mscc_miim_wait_ready(miim);
if (ret)
goto out;
val = readl(miim->regs + GCB_MIIM_DATA);
if (val & GCB_MIIM_DATA_ERROR) {
ret = -EIO;
goto out;
}
ret = val & 0xFFFF;
out:
return ret;
}
static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg,
u16 val)
{
struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv;
int ret;
ret = mscc_miim_wait_ready(miim);
if (ret < 0)
goto out;
writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) |
GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) |
GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD);
out:
return ret;
}
static struct mii_dev *serval_mdiobus_init(phys_addr_t miim_base,
unsigned long miim_size)
{
struct mii_dev *bus;
bus = mdio_alloc();
if (!bus)
return NULL;
++miim_count;
sprintf(bus->name, "miim-bus%d", miim_count);
miim[miim_count].regs = ioremap(miim_base, miim_size);
miim[miim_count].miim_base = miim_base;
miim[miim_count].miim_size = miim_size;
bus->priv = &miim[miim_count];
bus->read = mscc_miim_read;
bus->write = mscc_miim_write;
if (mdio_register(bus))
return NULL;
miim[miim_count].bus = bus;
return bus;
}
static void luton_stop(struct udevice *dev)
{
struct luton_private *priv = dev_get_priv(dev);
@ -760,7 +664,8 @@ static int luton_probe(struct udevice *dev)
/* If the bus is new then create a new bus */
if (!get_mdiobus(addr_base, addr_size))
priv->bus[miim_count] =
serval_mdiobus_init(addr_base, addr_size);
mscc_mdiobus_init(miim, &miim_count, addr_base,
addr_size);
/* Connect mdio bus with the port */
bus = get_mdiobus(addr_base, addr_size);

View File

@ -72,3 +72,31 @@ int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg,
out:
return ret;
}
struct mii_dev *mscc_mdiobus_init(struct mscc_miim_dev *miim, int *miim_count,
phys_addr_t miim_base,
unsigned long miim_size)
{
struct mii_dev *bus;
bus = mdio_alloc();
if (!bus)
return NULL;
*miim_count += 1;
sprintf(bus->name, "miim-bus%d", *miim_count);
miim[*miim_count].regs = ioremap(miim_base, miim_size);
miim[*miim_count].miim_base = miim_base;
miim[*miim_count].miim_size = miim_size;
bus->priv = &miim[*miim_count];
bus->read = mscc_miim_read;
bus->write = mscc_miim_write;
if (mdio_register(bus))
return NULL;
miim[*miim_count].bus = bus;
return bus;
}

View File

@ -3,10 +3,22 @@
* Copyright (c) 2018 Microsemi Corporation
*/
#ifndef _MSCC_MIIM_H_
#define _MSCC_MIIM_H_
struct mscc_miim_dev {
void __iomem *regs;
void __iomem *phy_regs;
phys_addr_t miim_base;
unsigned long miim_size;
struct mii_dev *bus;
};
int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg);
int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, u16 val);
struct mii_dev *mscc_mdiobus_init(struct mscc_miim_dev *miim, int *miim_count,
phys_addr_t miim_base,
unsigned long miim_size);
#endif /* _MSCC_MIIM_H_ */

View File

@ -17,6 +17,7 @@
#include "mscc_xfer.h"
#include "mscc_mac_table.h"
#include "mscc_miim.h"
#define PHY_CFG 0x0
#define PHY_CFG_ENA 0xF
@ -25,20 +26,6 @@
#define PHY_STAT 0x4
#define PHY_STAT_SUPERVISOR_COMPLETE BIT(0)
#define GCB_MIIM_MII_STATUS 0x0
#define GCB_MIIM_STAT_BUSY BIT(3)
#define GCB_MIIM_MII_CMD 0x8
#define GCB_MIIM_MII_CMD_SCAN BIT(0)
#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1)
#define GCB_MIIM_MII_CMD_OPR_READ BIT(2)
#define GCB_MIIM_MII_CMD_SINGLE_SCAN BIT(3)
#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4)
#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20)
#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25)
#define GCB_MIIM_MII_CMD_VLD BIT(31)
#define GCB_MIIM_DATA 0xC
#define GCB_MIIM_DATA_ERROR (0x3 << 16)
#define ANA_PORT_VLAN_CFG(x) (0x7000 + 0x100 * (x))
#define ANA_PORT_VLAN_CFG_AWARE_ENA BIT(20)
#define ANA_PORT_VLAN_CFG_POP_CNT(x) ((x) << 18)
@ -173,13 +160,6 @@ struct ocelot_private {
struct ocelot_phy_port_t ports[MAX_PORT];
};
struct mscc_miim_dev {
void __iomem *regs;
phys_addr_t miim_base;
unsigned long miim_size;
struct mii_dev *bus;
};
static struct mscc_miim_dev miim[OCELOT_MIIM_BUS_COUNT];
static int miim_count = -1;
@ -209,85 +189,6 @@ static void mscc_phy_reset(void)
}
}
static int mscc_miim_wait_ready(struct mscc_miim_dev *miim)
{
return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS,
GCB_MIIM_STAT_BUSY, false, 250, false);
}
static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg)
{
struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv;
u32 val;
int ret;
ret = mscc_miim_wait_ready(miim);
if (ret)
goto out;
writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) |
GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ,
miim->regs + GCB_MIIM_MII_CMD);
ret = mscc_miim_wait_ready(miim);
if (ret)
goto out;
val = readl(miim->regs + GCB_MIIM_DATA);
if (val & GCB_MIIM_DATA_ERROR) {
ret = -EIO;
goto out;
}
ret = val & 0xFFFF;
out:
return ret;
}
static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg,
u16 val)
{
struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv;
int ret;
ret = mscc_miim_wait_ready(miim);
if (ret < 0)
goto out;
writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) |
GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) |
GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD);
out:
return ret;
}
static struct mii_dev *ocelot_mdiobus_init(phys_addr_t miim_base,
unsigned long miim_size)
{
struct mii_dev *bus;
bus = mdio_alloc();
if (!bus)
return NULL;
++miim_count;
sprintf(bus->name, "miim-bus%d", miim_count);
miim[miim_count].regs = ioremap(miim_base, miim_size);
miim[miim_count].miim_base = miim_base;
miim[miim_count].miim_size = miim_size;
bus->priv = &miim[miim_count];
bus->read = mscc_miim_read;
bus->write = mscc_miim_write;
if (mdio_register(bus))
return NULL;
miim[miim_count].bus = bus;
return bus;
}
__weak void mscc_switch_reset(void)
{
}
@ -682,7 +583,8 @@ static int ocelot_probe(struct udevice *dev)
/* If the bus is new then create a new bus */
if (!get_mdiobus(addr_base, addr_size))
priv->bus[miim_count] =
ocelot_mdiobus_init(addr_base, addr_size);
mscc_mdiobus_init(miim, &miim_count, addr_base,
addr_size);
/* Connect mdio bus with the port */
bus = get_mdiobus(addr_base, addr_size);

View File

@ -17,18 +17,7 @@
#include "mscc_xfer.h"
#include "mscc_mac_table.h"
#define GCB_MIIM_MII_STATUS 0x0
#define GCB_MIIM_STAT_BUSY BIT(3)
#define GCB_MIIM_MII_CMD 0x8
#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1)
#define GCB_MIIM_MII_CMD_OPR_READ BIT(2)
#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4)
#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20)
#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25)
#define GCB_MIIM_MII_CMD_VLD BIT(31)
#define GCB_MIIM_DATA 0xC
#define GCB_MIIM_DATA_ERROR (0x2 << 16)
#include "mscc_miim.h"
#define ANA_PORT_VLAN_CFG(x) (0xc000 + 0x100 * (x))
#define ANA_PORT_VLAN_CFG_AWARE_ENA BIT(20)
@ -156,13 +145,6 @@ struct serval_private {
struct serval_phy_port_t ports[MAX_PORT];
};
struct mscc_miim_dev {
void __iomem *regs;
phys_addr_t miim_base;
unsigned long miim_size;
struct mii_dev *bus;
};
static const unsigned long serval_regs_qs[] = {
[MSCC_QS_XTR_RD] = 0x8,
[MSCC_QS_XTR_FLUSH] = 0x18,
@ -180,84 +162,6 @@ static const unsigned long serval_regs_ana_table[] = {
static struct mscc_miim_dev miim[SERVAL_MIIM_BUS_COUNT];
static int miim_count = -1;
static int mscc_miim_wait_ready(struct mscc_miim_dev *miim)
{
return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS,
GCB_MIIM_STAT_BUSY, false, 250, false);
}
static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg)
{
struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv;
u32 val;
int ret;
ret = mscc_miim_wait_ready(miim);
if (ret)
goto out;
writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) |
GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ,
miim->regs + GCB_MIIM_MII_CMD);
ret = mscc_miim_wait_ready(miim);
if (ret)
goto out;
val = readl(miim->regs + GCB_MIIM_DATA);
if (val & GCB_MIIM_DATA_ERROR) {
ret = -EIO;
goto out;
}
ret = val & 0xFFFF;
out:
return ret;
}
static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg,
u16 val)
{
struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv;
int ret;
ret = mscc_miim_wait_ready(miim);
if (ret < 0)
goto out;
writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) |
GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) |
GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD);
out:
return ret;
}
static struct mii_dev *serval_mdiobus_init(phys_addr_t miim_base,
unsigned long miim_size)
{
struct mii_dev *bus;
bus = mdio_alloc();
if (!bus)
return NULL;
++miim_count;
sprintf(bus->name, "miim-bus%d", miim_count);
miim[miim_count].regs = ioremap(miim_base, miim_size);
miim[miim_count].miim_base = miim_base;
miim[miim_count].miim_size = miim_size;
bus->priv = &miim[miim_count];
bus->read = mscc_miim_read;
bus->write = mscc_miim_write;
if (mdio_register(bus))
return NULL;
miim[miim_count].bus = bus;
return bus;
}
static void serval_cpu_capture_setup(struct serval_private *priv)
{
int i;
@ -356,8 +260,6 @@ static void serdes_write(void __iomem *base, u32 addr)
do {
data = readl(base + HSIO_MCB_SERDES1G_CFG);
} while (data & HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT);
mdelay(100);
}
static void serdes1g_setup(void __iomem *base, uint32_t addr,
@ -636,7 +538,8 @@ static int serval_probe(struct udevice *dev)
/* If the bus is new then create a new bus */
if (!get_mdiobus(addr_base, addr_size))
priv->bus[miim_count] =
serval_mdiobus_init(addr_base, addr_size);
mscc_mdiobus_init(miim, &miim_count, addr_base,
addr_size);
/* Connect mdio bus with the port */
bus = get_mdiobus(addr_base, addr_size);

View File

@ -16,18 +16,7 @@
#include <wait_bit.h>
#include "mscc_xfer.h"
#define GCB_MIIM_MII_STATUS 0x0
#define GCB_MIIM_STAT_BUSY BIT(3)
#define GCB_MIIM_MII_CMD 0x8
#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1)
#define GCB_MIIM_MII_CMD_OPR_READ BIT(2)
#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4)
#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20)
#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25)
#define GCB_MIIM_MII_CMD_VLD BIT(31)
#define GCB_MIIM_DATA 0xC
#define GCB_MIIM_DATA_ERROR (0x3 << 16)
#include "mscc_miim.h"
#define PHY_CFG 0x0
#define PHY_CFG_ENA 0x3
@ -134,13 +123,6 @@ struct servalt_private {
struct servalt_phy_port_t ports[MAX_PORT];
};
struct mscc_miim_dev {
void __iomem *regs;
phys_addr_t miim_base;
unsigned long miim_size;
struct mii_dev *bus;
};
static const unsigned long servalt_regs_qs[] = {
[MSCC_QS_XTR_RD] = 0x8,
[MSCC_QS_XTR_FLUSH] = 0x18,
@ -152,85 +134,6 @@ static const unsigned long servalt_regs_qs[] = {
static struct mscc_miim_dev miim[SERVALT_MIIM_BUS_COUNT];
static int miim_count = -1;
static int mscc_miim_wait_ready(struct mscc_miim_dev *miim)
{
return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS,
GCB_MIIM_STAT_BUSY, false, 250, false);
}
static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg)
{
struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv;
u32 val;
int ret;
ret = mscc_miim_wait_ready(miim);
if (ret)
goto out;
writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) |
GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ,
miim->regs + GCB_MIIM_MII_CMD);
ret = mscc_miim_wait_ready(miim);
if (ret)
goto out;
val = readl(miim->regs + GCB_MIIM_DATA);
if (val & GCB_MIIM_DATA_ERROR) {
ret = -EIO;
goto out;
}
ret = val & 0xFFFF;
out:
return ret;
}
static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg,
u16 val)
{
struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv;
int ret;
ret = mscc_miim_wait_ready(miim);
if (ret < 0)
goto out;
writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) |
GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) |
GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD);
out:
return ret;
}
static struct mii_dev *servalt_mdiobus_init(phys_addr_t miim_base,
unsigned long miim_size)
{
struct mii_dev *bus;
bus = mdio_alloc();
if (!bus)
return NULL;
++miim_count;
sprintf(bus->name, "miim-bus%d", miim_count);
miim[miim_count].regs = ioremap(miim_base, miim_size);
miim[miim_count].miim_base = miim_base;
miim[miim_count].miim_size = miim_size;
bus->priv = &miim[miim_count];
bus->read = mscc_miim_read;
bus->write = mscc_miim_write;
if (mdio_register(bus))
return NULL;
miim[miim_count].bus = bus;
return bus;
}
static void mscc_phy_reset(void)
{
writel(0, BASE_DEVCPU_GCB + GCB_PHY_CFG + PHY_CFG);
@ -564,7 +467,8 @@ static int servalt_probe(struct udevice *dev)
/* If the bus is new then create a new bus */
if (!get_mdiobus(addr_base, addr_size))
priv->bus[miim_count] =
servalt_mdiobus_init(addr_base, addr_size);
mscc_mdiobus_init(miim, &miim_count, addr_base,
addr_size);
/* Connect mdio bus with the port */
bus = get_mdiobus(addr_base, addr_size);

View File

@ -176,8 +176,13 @@ void cs4340_upload_firmware(struct phy_device *phydev)
printf("MMC read: dev # %u, block # %u, count %u ...\n",
dev, blk, cnt);
mmc_init(mmc);
#ifdef CONFIG_BLK
(void)blk_dread(mmc_get_blk_desc(mmc), blk, cnt,
addr);
#else
(void)mmc->block_dev.block_read(&mmc->block_dev, blk, cnt,
addr);
#endif
}
#endif

View File

@ -103,7 +103,7 @@ struct dp83867_private {
int io_impedance;
bool rxctrl_strap_quirk;
int port_mirroring;
int clk_output_sel;
unsigned int clk_output_sel;
};
static int dp83867_config_port_mirroring(struct phy_device *phydev)
@ -136,17 +136,11 @@ static int dp83867_of_init(struct phy_device *phydev)
ofnode node;
u16 val;
/* Optional configuration */
node = phy_get_ofnode(phydev);
if (!ofnode_valid(node))
return -EINVAL;
/*
* Keep the default value if ti,clk-output-sel is not set
* or to high
*/
/* Keep the default value if ti,clk-output-sel is not set */
dp83867->clk_output_sel =
ofnode_read_u32_default(node, "ti,clk-output-sel",
DP83867_CLK_O_SEL_REF_CLK);
@ -162,14 +156,14 @@ static int dp83867_of_init(struct phy_device *phydev)
dp83867->rxctrl_strap_quirk = true;
dp83867->rx_id_delay = ofnode_read_u32_default(node,
"ti,rx-internal-delay",
-1);
DEFAULT_RX_ID_DELAY);
dp83867->tx_id_delay = ofnode_read_u32_default(node,
"ti,tx-internal-delay",
-1);
DEFAULT_TX_ID_DELAY);
dp83867->fifo_depth = ofnode_read_u32_default(node, "ti,fifo-depth",
-1);
DEFAULT_FIFO_DEPTH);
if (ofnode_read_bool(node, "enet-phy-lane-swap"))
dp83867->port_mirroring = DP83867_PORT_MIRRORING_EN;

View File

@ -18,3 +18,11 @@ config DRIVER_TI_KEYSTONE_NET
bool "TI Keystone 2 Ethernet"
help
This driver supports the TI Keystone 2 Ethernet subsystem
config TI_AM65_CPSW_NUSS
bool "TI K3 AM65x MCU CPSW Nuss Ethernet controller driver"
depends on ARCH_K3
select PHYLIB
help
This driver supports TI K3 MCU CPSW Nuss Ethernet controller
in Texas Instruments K3 AM65x SoCs.

View File

@ -5,3 +5,4 @@
obj-$(CONFIG_DRIVER_TI_CPSW) += cpsw.o cpsw-common.o cpsw_mdio.o
obj-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o
obj-$(CONFIG_DRIVER_TI_KEYSTONE_NET) += keystone_net.o cpsw_mdio.o
obj-$(CONFIG_TI_AM65_CPSW_NUSS) += am65-cpsw-nuss.o cpsw_mdio.o

View File

@ -0,0 +1,792 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Texas Instruments K3 AM65 Ethernet Switch SubSystem Driver
*
* Copyright (C) 2019, Texas Instruments, Incorporated
*
*/
#include <common.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <clk.h>
#include <dm.h>
#include <dm/lists.h>
#include <dma-uclass.h>
#include <dm/of_access.h>
#include <miiphy.h>
#include <net.h>
#include <phy.h>
#include <power-domain.h>
#include <linux/soc/ti/ti-udma.h>
#include "cpsw_mdio.h"
#define AM65_CPSW_CPSWNU_MAX_PORTS 2
#define AM65_CPSW_SS_BASE 0x0
#define AM65_CPSW_SGMII_BASE 0x100
#define AM65_CPSW_MDIO_BASE 0xf00
#define AM65_CPSW_XGMII_BASE 0x2100
#define AM65_CPSW_CPSW_NU_BASE 0x20000
#define AM65_CPSW_CPSW_NU_ALE_BASE 0x1e000
#define AM65_CPSW_CPSW_NU_PORTS_OFFSET 0x1000
#define AM65_CPSW_CPSW_NU_PORT_MACSL_OFFSET 0x330
#define AM65_CPSW_MDIO_BUS_FREQ_DEF 1000000
#define AM65_CPSW_CTL_REG 0x4
#define AM65_CPSW_STAT_PORT_EN_REG 0x14
#define AM65_CPSW_PTYPE_REG 0x18
#define AM65_CPSW_CTL_REG_P0_ENABLE BIT(2)
#define AM65_CPSW_CTL_REG_P0_TX_CRC_REMOVE BIT(13)
#define AM65_CPSW_CTL_REG_P0_RX_PAD BIT(14)
#define AM65_CPSW_P0_FLOW_ID_REG 0x8
#define AM65_CPSW_PN_RX_MAXLEN_REG 0x24
#define AM65_CPSW_PN_REG_SA_L 0x308
#define AM65_CPSW_PN_REG_SA_H 0x30c
#define AM65_CPSW_ALE_CTL_REG 0x8
#define AM65_CPSW_ALE_CTL_REG_ENABLE BIT(31)
#define AM65_CPSW_ALE_CTL_REG_RESET_TBL BIT(30)
#define AM65_CPSW_ALE_CTL_REG_BYPASS BIT(4)
#define AM65_CPSW_ALE_PN_CTL_REG(x) (0x40 + (x) * 4)
#define AM65_CPSW_ALE_PN_CTL_REG_MODE_FORWARD 0x3
#define AM65_CPSW_ALE_PN_CTL_REG_MAC_ONLY BIT(11)
#define AM65_CPSW_MACSL_CTL_REG 0x0
#define AM65_CPSW_MACSL_CTL_REG_IFCTL_A BIT(15)
#define AM65_CPSW_MACSL_CTL_REG_GIG BIT(7)
#define AM65_CPSW_MACSL_CTL_REG_GMII_EN BIT(5)
#define AM65_CPSW_MACSL_CTL_REG_LOOPBACK BIT(1)
#define AM65_CPSW_MACSL_CTL_REG_FULL_DUPLEX BIT(0)
#define AM65_CPSW_MACSL_RESET_REG 0x8
#define AM65_CPSW_MACSL_RESET_REG_RESET BIT(0)
#define AM65_CPSW_MACSL_STATUS_REG 0x4
#define AM65_CPSW_MACSL_RESET_REG_PN_IDLE BIT(31)
#define AM65_CPSW_MACSL_RESET_REG_PN_E_IDLE BIT(30)
#define AM65_CPSW_MACSL_RESET_REG_PN_P_IDLE BIT(29)
#define AM65_CPSW_MACSL_RESET_REG_PN_TX_IDLE BIT(28)
#define AM65_CPSW_MACSL_RESET_REG_IDLE_MASK \
(AM65_CPSW_MACSL_RESET_REG_PN_IDLE | \
AM65_CPSW_MACSL_RESET_REG_PN_E_IDLE | \
AM65_CPSW_MACSL_RESET_REG_PN_P_IDLE | \
AM65_CPSW_MACSL_RESET_REG_PN_TX_IDLE)
#define AM65_CPSW_CPPI_PKT_TYPE 0x7
struct am65_cpsw_port {
fdt_addr_t port_base;
fdt_addr_t macsl_base;
bool disabled;
u32 mac_control;
};
struct am65_cpsw_common {
struct udevice *dev;
fdt_addr_t ss_base;
fdt_addr_t cpsw_base;
fdt_addr_t mdio_base;
fdt_addr_t ale_base;
fdt_addr_t gmii_sel;
fdt_addr_t mac_efuse;
struct clk fclk;
struct power_domain pwrdmn;
u32 port_num;
struct am65_cpsw_port ports[AM65_CPSW_CPSWNU_MAX_PORTS];
u32 rflow_id_base;
struct mii_dev *bus;
u32 bus_freq;
struct dma dma_tx;
struct dma dma_rx;
u32 rx_next;
u32 rx_pend;
bool started;
};
struct am65_cpsw_priv {
struct udevice *dev;
struct am65_cpsw_common *cpsw_common;
u32 port_id;
struct phy_device *phydev;
bool has_phy;
ofnode phy_node;
u32 phy_addr;
};
#ifdef PKTSIZE_ALIGN
#define UDMA_RX_BUF_SIZE PKTSIZE_ALIGN
#else
#define UDMA_RX_BUF_SIZE ALIGN(1522, ARCH_DMA_MINALIGN)
#endif
#ifdef PKTBUFSRX
#define UDMA_RX_DESC_NUM PKTBUFSRX
#else
#define UDMA_RX_DESC_NUM 4
#endif
#define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \
((mac)[2] << 16) | ((mac)[3] << 24))
#define mac_lo(mac) (((mac)[4] << 0) | ((mac)[5] << 8))
static void am65_cpsw_set_sl_mac(struct am65_cpsw_port *slave,
unsigned char *addr)
{
writel(mac_hi(addr),
slave->port_base + AM65_CPSW_PN_REG_SA_H);
writel(mac_lo(addr),
slave->port_base + AM65_CPSW_PN_REG_SA_L);
}
int am65_cpsw_macsl_reset(struct am65_cpsw_port *slave)
{
u32 i = 100;
/* Set the soft reset bit */
writel(AM65_CPSW_MACSL_RESET_REG_RESET,
slave->macsl_base + AM65_CPSW_MACSL_RESET_REG);
while ((readl(slave->macsl_base + AM65_CPSW_MACSL_RESET_REG) &
AM65_CPSW_MACSL_RESET_REG_RESET) && i--)
cpu_relax();
/* Timeout on the reset */
return i;
}
static int am65_cpsw_macsl_wait_for_idle(struct am65_cpsw_port *slave)
{
u32 i = 100;
while ((readl(slave->macsl_base + AM65_CPSW_MACSL_STATUS_REG) &
AM65_CPSW_MACSL_RESET_REG_IDLE_MASK) && i--)
cpu_relax();
return i;
}
static int am65_cpsw_update_link(struct am65_cpsw_priv *priv)
{
struct am65_cpsw_common *common = priv->cpsw_common;
struct am65_cpsw_port *port = &common->ports[priv->port_id];
struct phy_device *phy = priv->phydev;
u32 mac_control = 0;
if (phy->link) { /* link up */
mac_control = /*AM65_CPSW_MACSL_CTL_REG_LOOPBACK |*/
AM65_CPSW_MACSL_CTL_REG_GMII_EN;
if (phy->speed == 1000)
mac_control |= AM65_CPSW_MACSL_CTL_REG_GIG;
if (phy->duplex == DUPLEX_FULL)
mac_control |= AM65_CPSW_MACSL_CTL_REG_FULL_DUPLEX;
if (phy->speed == 100)
mac_control |= AM65_CPSW_MACSL_CTL_REG_IFCTL_A;
}
if (mac_control == port->mac_control)
goto out;
if (mac_control) {
printf("link up on port %d, speed %d, %s duplex\n",
priv->port_id, phy->speed,
(phy->duplex == DUPLEX_FULL) ? "full" : "half");
} else {
printf("link down on port %d\n", priv->port_id);
}
writel(mac_control, port->macsl_base + AM65_CPSW_MACSL_CTL_REG);
port->mac_control = mac_control;
out:
return phy->link;
}
#define AM65_GMII_SEL_MODE_MII 0
#define AM65_GMII_SEL_MODE_RMII 1
#define AM65_GMII_SEL_MODE_RGMII 2
#define AM65_GMII_SEL_RGMII_IDMODE BIT(4)
static void am65_cpsw_gmii_sel_k3(struct am65_cpsw_priv *priv,
phy_interface_t phy_mode, int slave)
{
struct am65_cpsw_common *common = priv->cpsw_common;
u32 reg;
u32 mode = 0;
bool rgmii_id = false;
reg = readl(common->gmii_sel);
dev_dbg(common->dev, "old gmii_sel: %08x\n", reg);
switch (phy_mode) {
case PHY_INTERFACE_MODE_RMII:
mode = AM65_GMII_SEL_MODE_RMII;
break;
case PHY_INTERFACE_MODE_RGMII:
mode = AM65_GMII_SEL_MODE_RGMII;
break;
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
mode = AM65_GMII_SEL_MODE_RGMII;
rgmii_id = true;
break;
default:
dev_warn(common->dev,
"Unsupported PHY mode: %u. Defaulting to MII.\n",
phy_mode);
/* fallthrough */
case PHY_INTERFACE_MODE_MII:
mode = AM65_GMII_SEL_MODE_MII;
break;
};
if (rgmii_id)
mode |= AM65_GMII_SEL_RGMII_IDMODE;
reg = mode;
dev_dbg(common->dev, "gmii_sel PHY mode: %u, new gmii_sel: %08x\n",
phy_mode, reg);
writel(reg, common->gmii_sel);
reg = readl(common->gmii_sel);
if (reg != mode)
dev_err(common->dev,
"gmii_sel PHY mode NOT SET!: requested: %08x, gmii_sel: %08x\n",
mode, reg);
}
static int am65_cpsw_start(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_platdata(dev);
struct am65_cpsw_priv *priv = dev_get_priv(dev);
struct am65_cpsw_common *common = priv->cpsw_common;
struct am65_cpsw_port *port = &common->ports[priv->port_id];
struct am65_cpsw_port *port0 = &common->ports[0];
int ret, i;
ret = power_domain_on(&common->pwrdmn);
if (ret) {
dev_err(dev, "power_domain_on() failed %d\n", ret);
goto out;
}
ret = clk_enable(&common->fclk);
if (ret) {
dev_err(dev, "clk enabled failed %d\n", ret);
goto err_off_pwrdm;
}
common->rx_next = 0;
common->rx_pend = 0;
ret = dma_get_by_name(common->dev, "tx0", &common->dma_tx);
if (ret) {
dev_err(dev, "TX dma get failed %d\n", ret);
goto err_off_clk;
}
ret = dma_get_by_name(common->dev, "rx", &common->dma_rx);
if (ret) {
dev_err(dev, "RX dma get failed %d\n", ret);
goto err_free_tx;
}
for (i = 0; i < UDMA_RX_DESC_NUM; i++) {
ret = dma_prepare_rcv_buf(&common->dma_rx,
net_rx_packets[i],
UDMA_RX_BUF_SIZE);
if (ret) {
dev_err(dev, "RX dma add buf failed %d\n", ret);
goto err_free_tx;
}
}
ret = dma_enable(&common->dma_tx);
if (ret) {
dev_err(dev, "TX dma_enable failed %d\n", ret);
goto err_free_rx;
}
ret = dma_enable(&common->dma_rx);
if (ret) {
dev_err(dev, "RX dma_enable failed %d\n", ret);
goto err_dis_tx;
}
/* Control register */
writel(AM65_CPSW_CTL_REG_P0_ENABLE |
AM65_CPSW_CTL_REG_P0_TX_CRC_REMOVE |
AM65_CPSW_CTL_REG_P0_RX_PAD,
common->cpsw_base + AM65_CPSW_CTL_REG);
/* disable priority elevation */
writel(0, common->cpsw_base + AM65_CPSW_PTYPE_REG);
/* enable statistics */
writel(BIT(0) | BIT(priv->port_id),
common->cpsw_base + AM65_CPSW_STAT_PORT_EN_REG);
/* Port 0 length register */
writel(PKTSIZE_ALIGN, port0->port_base + AM65_CPSW_PN_RX_MAXLEN_REG);
/* set base flow_id */
writel(common->rflow_id_base,
port0->port_base + AM65_CPSW_P0_FLOW_ID_REG);
/* Reset and enable the ALE */
writel(AM65_CPSW_ALE_CTL_REG_ENABLE | AM65_CPSW_ALE_CTL_REG_RESET_TBL |
AM65_CPSW_ALE_CTL_REG_BYPASS,
common->ale_base + AM65_CPSW_ALE_CTL_REG);
/* port 0 put into forward mode */
writel(AM65_CPSW_ALE_PN_CTL_REG_MODE_FORWARD,
common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(0));
/* PORT x configuration */
/* Port x Max length register */
writel(PKTSIZE_ALIGN, port->port_base + AM65_CPSW_PN_RX_MAXLEN_REG);
/* Port x set mac */
am65_cpsw_set_sl_mac(port, pdata->enetaddr);
/* Port x ALE: mac_only, Forwarding */
writel(AM65_CPSW_ALE_PN_CTL_REG_MAC_ONLY |
AM65_CPSW_ALE_PN_CTL_REG_MODE_FORWARD,
common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(priv->port_id));
port->mac_control = 0;
if (!am65_cpsw_macsl_reset(port)) {
dev_err(dev, "mac_sl reset failed\n");
ret = -EFAULT;
goto err_dis_rx;
}
ret = phy_startup(priv->phydev);
if (ret) {
dev_err(dev, "phy_startup failed\n");
goto err_dis_rx;
}
ret = am65_cpsw_update_link(priv);
if (!ret) {
ret = -ENODEV;
goto err_phy_shutdown;
}
common->started = true;
return 0;
err_phy_shutdown:
phy_shutdown(priv->phydev);
err_dis_rx:
/* disable ports */
writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(priv->port_id));
writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(0));
if (!am65_cpsw_macsl_wait_for_idle(port))
dev_err(dev, "mac_sl idle timeout\n");
writel(0, port->macsl_base + AM65_CPSW_MACSL_CTL_REG);
writel(0, common->ale_base + AM65_CPSW_ALE_CTL_REG);
writel(0, common->cpsw_base + AM65_CPSW_CTL_REG);
dma_disable(&common->dma_rx);
err_dis_tx:
dma_disable(&common->dma_tx);
err_free_rx:
dma_free(&common->dma_rx);
err_free_tx:
dma_free(&common->dma_tx);
err_off_clk:
clk_disable(&common->fclk);
err_off_pwrdm:
power_domain_off(&common->pwrdmn);
out:
dev_err(dev, "%s end error\n", __func__);
return ret;
}
static int am65_cpsw_send(struct udevice *dev, void *packet, int length)
{
struct am65_cpsw_priv *priv = dev_get_priv(dev);
struct am65_cpsw_common *common = priv->cpsw_common;
struct ti_udma_drv_packet_data packet_data;
int ret;
packet_data.pkt_type = AM65_CPSW_CPPI_PKT_TYPE;
packet_data.dest_tag = priv->port_id;
ret = dma_send(&common->dma_tx, packet, length, &packet_data);
if (ret) {
dev_err(dev, "TX dma_send failed %d\n", ret);
return ret;
}
return 0;
}
static int am65_cpsw_recv(struct udevice *dev, int flags, uchar **packetp)
{
struct am65_cpsw_priv *priv = dev_get_priv(dev);
struct am65_cpsw_common *common = priv->cpsw_common;
/* try to receive a new packet */
return dma_receive(&common->dma_rx, (void **)packetp, NULL);
}
static int am65_cpsw_free_pkt(struct udevice *dev, uchar *packet, int length)
{
struct am65_cpsw_priv *priv = dev_get_priv(dev);
struct am65_cpsw_common *common = priv->cpsw_common;
int ret;
if (length > 0) {
u32 pkt = common->rx_next % UDMA_RX_DESC_NUM;
ret = dma_prepare_rcv_buf(&common->dma_rx,
net_rx_packets[pkt],
UDMA_RX_BUF_SIZE);
if (ret)
dev_err(dev, "RX dma free_pkt failed %d\n", ret);
common->rx_next++;
}
return 0;
}
static void am65_cpsw_stop(struct udevice *dev)
{
struct am65_cpsw_priv *priv = dev_get_priv(dev);
struct am65_cpsw_common *common = priv->cpsw_common;
struct am65_cpsw_port *port = &common->ports[priv->port_id];
if (!common->started)
return;
phy_shutdown(priv->phydev);
writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(priv->port_id));
writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(0));
if (!am65_cpsw_macsl_wait_for_idle(port))
dev_err(dev, "mac_sl idle timeout\n");
writel(0, port->macsl_base + AM65_CPSW_MACSL_CTL_REG);
writel(0, common->ale_base + AM65_CPSW_ALE_CTL_REG);
writel(0, common->cpsw_base + AM65_CPSW_CTL_REG);
dma_disable(&common->dma_tx);
dma_free(&common->dma_tx);
dma_disable(&common->dma_rx);
dma_free(&common->dma_rx);
common->started = false;
}
static int am65_cpsw_read_rom_hwaddr(struct udevice *dev)
{
struct am65_cpsw_priv *priv = dev_get_priv(dev);
struct am65_cpsw_common *common = priv->cpsw_common;
struct eth_pdata *pdata = dev_get_platdata(dev);
u32 mac_hi, mac_lo;
if (common->mac_efuse == FDT_ADDR_T_NONE)
return -1;
mac_lo = readl(common->mac_efuse);
mac_hi = readl(common->mac_efuse + 4);
pdata->enetaddr[0] = (mac_hi >> 8) & 0xff;
pdata->enetaddr[1] = mac_hi & 0xff;
pdata->enetaddr[2] = (mac_lo >> 24) & 0xff;
pdata->enetaddr[3] = (mac_lo >> 16) & 0xff;
pdata->enetaddr[4] = (mac_lo >> 8) & 0xff;
pdata->enetaddr[5] = mac_lo & 0xff;
return 0;
}
static const struct eth_ops am65_cpsw_ops = {
.start = am65_cpsw_start,
.send = am65_cpsw_send,
.recv = am65_cpsw_recv,
.free_pkt = am65_cpsw_free_pkt,
.stop = am65_cpsw_stop,
.read_rom_hwaddr = am65_cpsw_read_rom_hwaddr,
};
static int am65_cpsw_mdio_init(struct udevice *dev)
{
struct am65_cpsw_priv *priv = dev_get_priv(dev);
struct am65_cpsw_common *cpsw_common = priv->cpsw_common;
if (!priv->has_phy || cpsw_common->bus)
return 0;
cpsw_common->bus = cpsw_mdio_init(dev->name,
cpsw_common->mdio_base,
cpsw_common->bus_freq,
clk_get_rate(&cpsw_common->fclk));
if (!cpsw_common->bus)
return -EFAULT;
return 0;
}
static int am65_cpsw_phy_init(struct udevice *dev)
{
struct am65_cpsw_priv *priv = dev_get_priv(dev);
struct am65_cpsw_common *cpsw_common = priv->cpsw_common;
struct eth_pdata *pdata = dev_get_platdata(dev);
struct phy_device *phydev;
u32 supported = PHY_GBIT_FEATURES;
int ret;
phydev = phy_connect(cpsw_common->bus,
priv->phy_addr,
priv->dev,
pdata->phy_interface);
if (!phydev) {
dev_err(dev, "phy_connect() failed\n");
return -ENODEV;
}
phydev->supported &= supported;
if (pdata->max_speed) {
ret = phy_set_supported(phydev, pdata->max_speed);
if (ret)
return ret;
}
phydev->advertising = phydev->supported;
if (ofnode_valid(priv->phy_node))
phydev->node = priv->phy_node;
priv->phydev = phydev;
ret = phy_config(phydev);
if (ret < 0)
pr_err("phy_config() failed: %d", ret);
return ret;
}
static int am65_cpsw_ofdata_parse_phy(struct udevice *dev, ofnode port_np)
{
struct eth_pdata *pdata = dev_get_platdata(dev);
struct am65_cpsw_priv *priv = dev_get_priv(dev);
struct ofnode_phandle_args out_args;
const char *phy_mode;
int ret = 0;
phy_mode = ofnode_read_string(port_np, "phy-mode");
if (phy_mode) {
pdata->phy_interface =
phy_get_interface_by_name(phy_mode);
if (pdata->phy_interface == -1) {
dev_err(dev, "Invalid PHY mode '%s', port %u\n",
phy_mode, priv->port_id);
ret = -EINVAL;
goto out;
}
}
ofnode_read_u32(port_np, "max-speed", (u32 *)&pdata->max_speed);
if (pdata->max_speed)
dev_err(dev, "Port %u speed froced to %uMbit\n",
priv->port_id, pdata->max_speed);
priv->has_phy = true;
ret = ofnode_parse_phandle_with_args(port_np, "phy-handle",
NULL, 0, 0, &out_args);
if (ret) {
dev_err(dev, "can't parse phy-handle port %u (%d)\n",
priv->port_id, ret);
priv->has_phy = false;
ret = 0;
}
priv->phy_node = out_args.node;
if (priv->has_phy) {
ret = ofnode_read_u32(priv->phy_node, "reg", &priv->phy_addr);
if (ret) {
dev_err(dev, "failed to get phy_addr port %u (%d)\n",
priv->port_id, ret);
goto out;
}
}
out:
return ret;
}
static int am65_cpsw_probe_cpsw(struct udevice *dev)
{
struct am65_cpsw_priv *priv = dev_get_priv(dev);
struct eth_pdata *pdata = dev_get_platdata(dev);
struct am65_cpsw_common *cpsw_common;
ofnode ports_np, node;
int ret, i;
priv->dev = dev;
cpsw_common = calloc(1, sizeof(*priv->cpsw_common));
if (!cpsw_common)
return -ENOMEM;
priv->cpsw_common = cpsw_common;
cpsw_common->dev = dev;
cpsw_common->ss_base = dev_read_addr(dev);
if (cpsw_common->ss_base == FDT_ADDR_T_NONE)
return -EINVAL;
cpsw_common->mac_efuse = devfdt_get_addr_name(dev, "mac_efuse");
/* no err check - optional */
ret = power_domain_get_by_index(dev, &cpsw_common->pwrdmn, 0);
if (ret) {
dev_err(dev, "failed to get pwrdmn: %d\n", ret);
return ret;
}
ret = clk_get_by_name(dev, "fck", &cpsw_common->fclk);
if (ret) {
power_domain_free(&cpsw_common->pwrdmn);
dev_err(dev, "failed to get clock %d\n", ret);
return ret;
}
cpsw_common->cpsw_base = cpsw_common->ss_base + AM65_CPSW_CPSW_NU_BASE;
cpsw_common->ale_base = cpsw_common->cpsw_base +
AM65_CPSW_CPSW_NU_ALE_BASE;
cpsw_common->mdio_base = cpsw_common->ss_base + AM65_CPSW_MDIO_BASE;
cpsw_common->rflow_id_base = 0;
cpsw_common->rflow_id_base =
dev_read_u32_default(dev, "ti,rx-flow-id-base",
cpsw_common->rflow_id_base);
ports_np = dev_read_subnode(dev, "ports");
if (!ofnode_valid(ports_np)) {
ret = -ENOENT;
goto out;
}
ofnode_for_each_subnode(node, ports_np) {
const char *node_name;
u32 port_id;
bool disabled;
node_name = ofnode_get_name(node);
disabled = !ofnode_is_available(node);
ret = ofnode_read_u32(node, "reg", &port_id);
if (ret) {
dev_err(dev, "%s: failed to get port_id (%d)\n",
node_name, ret);
goto out;
}
if (port_id >= AM65_CPSW_CPSWNU_MAX_PORTS) {
dev_err(dev, "%s: invalid port_id (%d)\n",
node_name, port_id);
ret = -EINVAL;
goto out;
}
cpsw_common->port_num++;
if (!port_id)
continue;
priv->port_id = port_id;
cpsw_common->ports[port_id].disabled = disabled;
if (disabled)
continue;
ret = am65_cpsw_ofdata_parse_phy(dev, node);
if (ret)
goto out;
}
for (i = 0; i < AM65_CPSW_CPSWNU_MAX_PORTS; i++) {
struct am65_cpsw_port *port = &cpsw_common->ports[i];
port->port_base = cpsw_common->cpsw_base +
AM65_CPSW_CPSW_NU_PORTS_OFFSET +
(i * AM65_CPSW_CPSW_NU_PORTS_OFFSET);
port->macsl_base = port->port_base +
AM65_CPSW_CPSW_NU_PORT_MACSL_OFFSET;
}
node = dev_read_subnode(dev, "cpsw-phy-sel");
if (!ofnode_valid(node)) {
dev_err(dev, "can't find cpsw-phy-sel\n");
ret = -ENOENT;
goto out;
}
cpsw_common->gmii_sel = ofnode_get_addr(node);
if (cpsw_common->gmii_sel == FDT_ADDR_T_NONE) {
dev_err(dev, "failed to get gmii_sel base\n");
goto out;
}
node = dev_read_subnode(dev, "mdio");
if (!ofnode_valid(node)) {
dev_err(dev, "can't find mdio\n");
ret = -ENOENT;
goto out;
}
cpsw_common->bus_freq =
dev_read_u32_default(dev, "bus_freq",
AM65_CPSW_MDIO_BUS_FREQ_DEF);
am65_cpsw_gmii_sel_k3(priv, pdata->phy_interface, priv->port_id);
ret = am65_cpsw_mdio_init(dev);
if (ret)
goto out;
ret = am65_cpsw_phy_init(dev);
if (ret)
goto out;
dev_info(dev, "K3 CPSW: nuss_ver: 0x%08X cpsw_ver: 0x%08X ale_ver: 0x%08X Ports:%u rflow_id_base:%u mdio_freq:%u\n",
readl(cpsw_common->ss_base),
readl(cpsw_common->cpsw_base),
readl(cpsw_common->ale_base),
cpsw_common->port_num,
cpsw_common->rflow_id_base,
cpsw_common->bus_freq);
out:
clk_free(&cpsw_common->fclk);
power_domain_free(&cpsw_common->pwrdmn);
return ret;
}
static const struct udevice_id am65_cpsw_nuss_ids[] = {
{ .compatible = "ti,am654-cpsw-nuss" },
{ }
};
U_BOOT_DRIVER(am65_cpsw_nuss_slave) = {
.name = "am65_cpsw_nuss_slave",
.id = UCLASS_ETH,
.of_match = am65_cpsw_nuss_ids,
.probe = am65_cpsw_probe_cpsw,
.ops = &am65_cpsw_ops,
.priv_auto_alloc_size = sizeof(struct am65_cpsw_priv),
.platdata_auto_alloc_size = sizeof(struct eth_pdata),
.flags = DM_FLAG_ALLOC_PRIV_DMA,
};

View File

@ -125,7 +125,7 @@ u32 cpsw_mdio_get_alive(struct mii_dev *bus)
return val & GENMASK(15, 0);
}
struct mii_dev *cpsw_mdio_init(const char *name, u32 mdio_base,
struct mii_dev *cpsw_mdio_init(const char *name, phys_addr_t mdio_base,
u32 bus_freq, int fck_freq)
{
struct cpsw_mdio *cpsw_mdio;
@ -144,7 +144,7 @@ struct mii_dev *cpsw_mdio_init(const char *name, u32 mdio_base,
return NULL;
}
cpsw_mdio->regs = (struct cpsw_mdio_regs *)mdio_base;
cpsw_mdio->regs = (struct cpsw_mdio_regs *)(uintptr_t)mdio_base;
if (!bus_freq || !fck_freq)
cpsw_mdio->div = CPSW_MDIO_DIV_DEF;

View File

@ -10,7 +10,7 @@
struct cpsw_mdio;
struct mii_dev *cpsw_mdio_init(const char *name, u32 mdio_base,
struct mii_dev *cpsw_mdio_init(const char *name, phys_addr_t mdio_base,
u32 bus_freq, int fck_freq);
void cpsw_mdio_free(struct mii_dev *bus);
u32 cpsw_mdio_get_alive(struct mii_dev *bus);

View File

@ -58,6 +58,7 @@ enum uclass_id {
UCLASS_LPC, /* x86 'low pin count' interface */
UCLASS_MAILBOX, /* Mailbox controller */
UCLASS_MASS_STORAGE, /* Mass storage device */
UCLASS_MDIO, /* MDIO bus */
UCLASS_MISC, /* Miscellaneous device */
UCLASS_MMC, /* SD / MMC card or chip */
UCLASS_MOD_EXP, /* RSA Mod Exp device */

View File

@ -118,4 +118,53 @@ int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg,
#define ESTATUS_1000XF 0x8000
#define ESTATUS_1000XH 0x4000
#ifdef CONFIG_DM_MDIO
/**
* struct mdio_perdev_priv - Per-device class data for MDIO DM
*
* @mii_bus: Supporting MII legacy bus
*/
struct mdio_perdev_priv {
struct mii_dev *mii_bus;
};
/**
* struct mdio_ops - MDIO bus operations
*
* @read: Read from a PHY register
* @write: Write to a PHY register
* @reset: Reset the MDIO bus, NULL if not supported
*/
struct mdio_ops {
int (*read)(struct udevice *mdio_dev, int addr, int devad, int reg);
int (*write)(struct udevice *mdio_dev, int addr, int devad, int reg,
u16 val);
int (*reset)(struct udevice *mdio_dev);
};
#define mdio_get_ops(dev) ((struct mdio_ops *)(dev)->driver->ops)
/**
* dm_mdio_probe_devices - Call probe on all MII devices, currently used for
* MDIO console commands.
*/
void dm_mdio_probe_devices(void);
/**
* dm_mdio_phy_connect - Wrapper over phy_connect for DM MDIO
*
* @dev: mdio dev
* @addr: PHY address on MDIO bus
* @ethdev: ethernet device to connect to the PHY
* @interface: MAC-PHY protocol
*
* @return pointer to phy_device, or 0 on error
*/
struct phy_device *dm_mdio_phy_connect(struct udevice *dev, int addr,
struct udevice *ethdev,
phy_interface_t interface);
#endif
#endif

View File

@ -15,6 +15,7 @@ obj-$(CONFIG_NET) += eth-uclass.o
else
obj-$(CONFIG_NET) += eth_legacy.o
endif
obj-$(CONFIG_DM_MDIO) += mdio-uclass.o
obj-$(CONFIG_NET) += eth_common.o
obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o
obj-$(CONFIG_NET) += net.o

115
net/mdio-uclass.c Normal file
View File

@ -0,0 +1,115 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2019
* Alex Marginean, NXP
*/
#include <common.h>
#include <dm.h>
#include <miiphy.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
void dm_mdio_probe_devices(void)
{
struct udevice *it;
struct uclass *uc;
uclass_get(UCLASS_MDIO, &uc);
uclass_foreach_dev(it, uc) {
device_probe(it);
}
}
static int dm_mdio_post_bind(struct udevice *dev)
{
/*
* MDIO command doesn't like spaces in names, don't allow them to keep
* it happy
*/
if (strchr(dev->name, ' ')) {
debug("\nError: MDIO device name \"%s\" has a space!\n",
dev->name);
return -EINVAL;
}
return 0;
}
/*
* Following read/write/reset functions are registered with legacy MII code.
* These are called for PHY operations by upper layers and we further call the
* DM MDIO driver functions.
*/
static int mdio_read(struct mii_dev *mii_bus, int addr, int devad, int reg)
{
struct udevice *dev = mii_bus->priv;
return mdio_get_ops(dev)->read(dev, addr, devad, reg);
}
static int mdio_write(struct mii_dev *mii_bus, int addr, int devad, int reg,
u16 val)
{
struct udevice *dev = mii_bus->priv;
return mdio_get_ops(dev)->write(dev, addr, devad, reg, val);
}
static int mdio_reset(struct mii_dev *mii_bus)
{
struct udevice *dev = mii_bus->priv;
if (mdio_get_ops(dev)->reset)
return mdio_get_ops(dev)->reset(dev);
else
return 0;
}
static int dm_mdio_post_probe(struct udevice *dev)
{
struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev);
pdata->mii_bus = mdio_alloc();
pdata->mii_bus->read = mdio_read;
pdata->mii_bus->write = mdio_write;
pdata->mii_bus->reset = mdio_reset;
pdata->mii_bus->priv = dev;
strncpy(pdata->mii_bus->name, dev->name, MDIO_NAME_LEN);
return mdio_register(pdata->mii_bus);
}
static int dm_mdio_pre_remove(struct udevice *dev)
{
struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev);
struct mdio_ops *ops = mdio_get_ops(dev);
if (ops->reset)
ops->reset(dev);
mdio_unregister(pdata->mii_bus);
mdio_free(pdata->mii_bus);
return 0;
}
struct phy_device *dm_mdio_phy_connect(struct udevice *dev, int addr,
struct udevice *ethdev,
phy_interface_t interface)
{
struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev);
if (device_probe(dev))
return 0;
return phy_connect(pdata->mii_bus, addr, ethdev, interface);
}
UCLASS_DRIVER(mdio) = {
.id = UCLASS_MDIO,
.name = "mdio",
.post_bind = dm_mdio_post_bind,
.post_probe = dm_mdio_post_probe,
.pre_remove = dm_mdio_pre_remove,
.per_device_auto_alloc_size = sizeof(struct mdio_perdev_priv),
};

View File

@ -62,4 +62,5 @@ obj-$(CONFIG_SOUND) += sound.o
obj-$(CONFIG_TEE) += tee.o
obj-$(CONFIG_VIRTIO_SANDBOX) += virtio.o
obj-$(CONFIG_DMA) += dma.o
obj-$(CONFIG_DM_MDIO) += mdio.o
endif

53
test/dm/mdio.c Normal file
View File

@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2019
* Alex Marginean, NXP
*/
#include <common.h>
#include <dm.h>
#include <dm/test.h>
#include <misc.h>
#include <test/ut.h>
#include <miiphy.h>
/* macros copied over from mdio_sandbox.c */
#define SANDBOX_PHY_ADDR 5
#define SANDBOX_PHY_REG 0
#define TEST_REG_VALUE 0xabcd
static int dm_test_mdio(struct unit_test_state *uts)
{
struct uclass *uc;
struct udevice *dev;
struct mdio_ops *ops;
u16 reg;
ut_assertok(uclass_get(UCLASS_MDIO, &uc));
ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-test", &dev));
ops = mdio_get_ops(dev);
ut_assertnonnull(ops);
ut_assertnonnull(ops->read);
ut_assertnonnull(ops->write);
ut_assertok(ops->write(dev, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
SANDBOX_PHY_REG, TEST_REG_VALUE));
reg = ops->read(dev, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
SANDBOX_PHY_REG);
ut_asserteq(reg, TEST_REG_VALUE);
ut_assert(ops->read(dev, SANDBOX_PHY_ADDR + 1, MDIO_DEVAD_NONE,
SANDBOX_PHY_REG) != 0);
ut_assertok(ops->reset(dev));
reg = ops->read(dev, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
SANDBOX_PHY_REG);
ut_asserteq(reg, 0);
return 0;
}
DM_TEST(dm_test_mdio, DM_TESTF_SCAN_FDT);