diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 5f0df54f73..0715f52e4f 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -86,3 +86,4 @@ obj-$(CONFIG_IPQ807X_EDMA) += ipq807x/ipq807x_uniphy.o obj-$(CONFIG_IPQ_MDIO) += ipq_common/ipq_mdio.o obj-$(CONFIG_QCA8075_PHY) += ipq_common/ipq_qca8075.o obj-$(CONFIG_QCA8033_PHY) += ipq_common/ipq_qca8033.o +obj-$(CONFIG_QCA_AQUANTIA_PHY) += ipq807x/ipq807x_aquantia_phy.o diff --git a/drivers/net/ipq807x/ipq807x_aquantia_phy.c b/drivers/net/ipq807x/ipq807x_aquantia_phy.c new file mode 100644 index 0000000000..41349509d1 --- /dev/null +++ b/drivers/net/ipq807x/ipq807x_aquantia_phy.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include "ipq_phy.h" +#include "ipq807x_aquantia_phy.h" + +extern int ipq_mdio_write(int mii_id, + int regnum, u16 value); +extern int ipq_mdio_read(int mii_id, + int regnum, ushort *data); + +u16 aq_phy_reg_write(u32 dev_id, u32 phy_id, + u32 reg_id, u16 reg_val) +{ + ipq_mdio_write(phy_id, reg_id, reg_val); + return 0; +} + +u16 aq_phy_reg_read(u32 dev_id, u32 phy_id, u32 reg_id) +{ + return ipq_mdio_read(phy_id, reg_id, NULL); +} + +u8 aq_phy_get_link_status(u32 dev_id, u32 phy_id) +{ + u16 phy_data; + uint32_t reg; + + reg = AQ_PHY_AUTO_STATUS_REG | AQUANTIA_MII_ADDR_C45; + phy_data = aq_phy_reg_read(dev_id, phy_id, reg); + phy_data = aq_phy_reg_read(dev_id, phy_id, reg); + + if (((phy_data >> 2) & 0x1) & PORT_LINK_UP) + return 0; + + return 1; +} + +u32 aq_phy_get_duplex(u32 dev_id, u32 phy_id, fal_port_duplex_t *duplex) +{ + u16 phy_data; + uint32_t reg; + + reg = AQ_PHY_LINK_STATUS_REG | AQUANTIA_MII_ADDR_C45; + phy_data = aq_phy_reg_read(dev_id, phy_id, reg); + + /* + * Read duplex + */ + phy_data = phy_data & 0x1; + if (phy_data & 0x1) + *duplex = FAL_FULL_DUPLEX; + else + *duplex = FAL_HALF_DUPLEX; + + return 0; +} + +u32 aq_phy_get_speed(u32 dev_id, u32 phy_id, fal_port_speed_t *speed) +{ + u16 phy_data; + uint32_t reg; + + reg = AQ_PHY_LINK_STATUS_REG | AQUANTIA_MII_ADDR_C45; + phy_data = aq_phy_reg_read(dev_id, phy_id, reg); + + switch ((phy_data >> 1) & 0x7) { + case SPEED_10G: + *speed = FAL_SPEED_10000; + break; + case SPEED_5G: + *speed = FAL_SPEED_5000; + break; + case SPEED_2_5G: + *speed = FAL_SPEED_2500; + break; + case SPEED_1000MBS: + *speed = FAL_SPEED_1000; + break; + case SPEED_100MBS: + *speed = FAL_SPEED_100; + break; + case SPEED_10MBS: + *speed = FAL_SPEED_10; + break; + default: + return -EINVAL; + } + return 0; +} + +int ipq_qca_aquantia_phy_init(struct phy_ops **ops, u32 phy_id) +{ + u16 phy_data; + struct phy_ops *aq_phy_ops; + aq_phy_ops = (struct phy_ops *)malloc(sizeof(struct phy_ops)); + if (!aq_phy_ops) + return -ENOMEM; + aq_phy_ops->phy_get_link_status = aq_phy_get_link_status; + aq_phy_ops->phy_get_speed = aq_phy_get_speed; + aq_phy_ops->phy_get_duplex = aq_phy_get_duplex; + *ops = aq_phy_ops; + + phy_data = aq_phy_reg_read(0x0, phy_id, AQUANTIA_REG_ADDRESS(1, QCA_PHY_ID1)); + printf ("PHY ID1: 0x%x\n", phy_data); + phy_data = aq_phy_reg_read(0x0, phy_id, AQUANTIA_REG_ADDRESS(1, QCA_PHY_ID2)); + printf ("PHY ID2: 0x%x\n", phy_data); + phy_data = aq_phy_reg_read(0x0, phy_id, AQUANTIA_REG_ADDRESS(AQUANTIA_MMD_PHY_XS_REGISTERS, + AQUANTIA_PHY_XS_USX_TRANSMIT)); + phy_data |= AQUANTIA_PHY_USX_AUTONEG_ENABLE; + aq_phy_reg_write(0x0, phy_id, AQUANTIA_REG_ADDRESS(AQUANTIA_MMD_PHY_XS_REGISTERS, + AQUANTIA_PHY_XS_USX_TRANSMIT), phy_data); + phy_data = aq_phy_reg_read(0x0, phy_id, AQUANTIA_REG_ADDRESS(AQUANTIA_MMD_AUTONEG, + AQUANTIA_AUTONEG_TRANSMIT_VENDOR_INTR_MASK)); + phy_data |= AQUANTIA_INTR_LINK_STATUS_CHANGE; + aq_phy_reg_write(0x0, phy_id, AQUANTIA_REG_ADDRESS(AQUANTIA_MMD_AUTONEG, + AQUANTIA_AUTONEG_TRANSMIT_VENDOR_INTR_MASK), phy_data); + phy_data = aq_phy_reg_read(0x0, phy_id, AQUANTIA_REG_ADDRESS(AQUANTIA_MMD_GLOABLE_REGISTERS, + AQUANTIA_GLOBAL_INTR_STANDARD_MASK)); + phy_data |= AQUANTIA_ALL_VENDOR_ALARMS_INTERRUPT_MASK; + aq_phy_reg_write(0x0, phy_id, AQUANTIA_REG_ADDRESS(AQUANTIA_MMD_GLOABLE_REGISTERS, + AQUANTIA_GLOBAL_INTR_STANDARD_MASK), phy_data); + phy_data = aq_phy_reg_read(0x0, phy_id, AQUANTIA_REG_ADDRESS(AQUANTIA_MMD_GLOABLE_REGISTERS, + AQUANTIA_GLOBAL_INTR_VENDOR_MASK)); + phy_data |= AQUANTIA_AUTO_AND_ALARMS_INTR_MASK; + aq_phy_reg_write(0x0, phy_id, AQUANTIA_REG_ADDRESS(AQUANTIA_MMD_GLOABLE_REGISTERS, + AQUANTIA_GLOBAL_INTR_VENDOR_MASK), phy_data); + + return 0; +} + diff --git a/drivers/net/ipq807x/ipq807x_aquantia_phy.h b/drivers/net/ipq807x/ipq807x_aquantia_phy.h new file mode 100644 index 0000000000..39ac24055e --- /dev/null +++ b/drivers/net/ipq807x/ipq807x_aquantia_phy.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#define AQUANTIA_MII_ADDR_C45 (1<<30) +#define AQUANTIA_REG_ADDRESS(dev_ad, reg_num) (AQUANTIA_MII_ADDR_C45 |\ + ((dev_ad & 0x1f) << 16) | (reg_num & 0xFFFF)) + +#define AQUANTIA_MMD_PHY_XS_REGISTERS 4 +#define AQUANTIA_PHY_XS_USX_TRANSMIT 0xc441 +#define AQUANTIA_PHY_USX_AUTONEG_ENABLE 0x8 + +#define AQUANTIA_MMD_AUTONEG 0x7 +#define AQUANTIA_AUTONEG_TRANSMIT_VENDOR_INTR_MASK 0xD401 +#define AQUANTIA_INTR_LINK_STATUS_CHANGE 0x0001 + +#define AQUANTIA_MMD_GLOABLE_REGISTERS 0x1E +#define AQUANTIA_GLOBAL_INTR_STANDARD_MASK 0xff00 +#define AQUANTIA_ALL_VENDOR_ALARMS_INTERRUPT_MASK 0x0001 + +#define AQUANTIA_GLOBAL_INTR_VENDOR_MASK 0xff01 +#define AQUANTIA_AUTO_AND_ALARMS_INTR_MASK 0x1001 + +#define AQ_PHY_AUTO_STATUS_REG 0x70001 +#define PORT_LINK_DOWN 0 +#define PORT_LINK_UP 1 + +#define AQ_PHY_LINK_STATUS_REG 0x7c800 +#define SPEED_5G 5 +#define SPEED_2_5G 4 +#define SPEED_10G 3 +#define SPEED_1000MBS 2 +#define SPEED_100MBS 1 +#define SPEED_10MBS 0 + diff --git a/drivers/net/ipq_common/ipq_phy.h b/drivers/net/ipq_common/ipq_phy.h index 3fe7eabb4f..562476ef37 100644 --- a/drivers/net/ipq_common/ipq_phy.h +++ b/drivers/net/ipq_common/ipq_phy.h @@ -63,6 +63,8 @@ typedef enum { FAL_SPEED_10 = 10, FAL_SPEED_100 = 100, FAL_SPEED_1000 = 1000, + FAL_SPEED_2500 = 2500, + FAL_SPEED_5000 = 5000, FAL_SPEED_10000 = 10000, FAL_SPEED_BUTT = 0xffff, } fal_port_speed_t; diff --git a/include/configs/ipq807x.h b/include/configs/ipq807x.h index a72cc5826f..92337c2153 100644 --- a/include/configs/ipq807x.h +++ b/include/configs/ipq807x.h @@ -286,6 +286,7 @@ extern loff_t board_env_size; #define CONFIG_IPQ_MDIO 1 #define CONFIG_QCA8075_PHY 1 #define CONFIG_QCA8033_PHY 1 +#define CONFIG_QCA_AQUANTIA_PHY 1 #define CONFIG_IPQ_ETH_INIT_DEFER /*