diff --git a/board/ipq9574/Kconfig b/board/ipq9574/Kconfig index d40904fc3c..4e6d5aa7cf 100644 --- a/board/ipq9574/Kconfig +++ b/board/ipq9574/Kconfig @@ -18,4 +18,6 @@ config IPQ9574_QCA_AQUANTIA_PHY config IPQ9574_QCA8075_PHY bool "Enable Malibu PHY support for ipq9574" +config QCA8084_PHY + bool "Enable QCA8084 PHY support for ipq9574" endif diff --git a/configs/ipq9574_defconfig b/configs/ipq9574_defconfig index 57be3284fd..50aeddc019 100644 --- a/configs/ipq9574_defconfig +++ b/configs/ipq9574_defconfig @@ -102,6 +102,7 @@ CONFIG_CMD_NFS=y CONFIG_IPQ9574_QCA_AQUANTIA_PHY=y # CONFIG_QCA8033_PHY is not set CONFIG_QCA8081_PHY=y +CONFIG_QCA8084_PHY=y CONFIG_IPQ9574_QCA8075_PHY=y # diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 1a18efa30f..8f0b4daa69 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -96,6 +96,9 @@ obj-$(CONFIG_IPQ5018_GMAC) += ipq5018/athrs17_phy.o obj-$(CONFIG_IPQ_MDIO) += ipq_common/ipq_mdio.o obj-$(CONFIG_GEPHY) += ipq_common/ipq_gephy.o obj-$(CONFIG_QCA8075_PHY) += ipq_common/ipq_qca8075.o +obj-$(CONFIG_QCA8084_PHY) += ipq_common/ipq_qca8084.o +obj-$(CONFIG_QCA8084_PHY) += ipq_common/ipq_qca8084_clk.o +obj-$(CONFIG_QCA8084_PHY) += ipq_common/ipq_qca8084_interface_ctrl.o obj-$(CONFIG_IPQ9574_QCA8075_PHY) += ipq9574/ipq9574_qca8075.o obj-$(CONFIG_QCA8033_PHY) += ipq_common/ipq_qca8033.o obj-$(CONFIG_QCA8081_PHY) += ipq_common/ipq_qca8081.o diff --git a/drivers/net/ipq_common/ipq_mdio.c b/drivers/net/ipq_common/ipq_mdio.c index 28a710e307..8ed8c3fb00 100644 --- a/drivers/net/ipq_common/ipq_mdio.c +++ b/drivers/net/ipq_common/ipq_mdio.c @@ -11,6 +11,8 @@ * GNU General Public License for more details. */ +#include +#include #include #include #include @@ -19,6 +21,12 @@ #include #include "ipq_mdio.h" +#ifdef DEBUG +#define pr_debug(fmt, args...) printf(fmt, ##args); +#else +#define pr_debug(fmt, args...) +#endif + struct ipq_mdio_data { struct mii_bus *bus; int phy_irq[PHY_MAX_ADDR]; @@ -162,6 +170,202 @@ int ipq_phy_read(struct mii_dev *bus, return ipq_mdio_read(addr, regnum, NULL); } +#ifdef CONFIG_QCA8084_PHY +static inline void split_addr(uint32_t regaddr, uint16_t *r1, uint16_t *r2, + uint16_t *page, uint16_t *switch_phy_id) +{ + *r1 = regaddr & 0x1c; + + regaddr >>= 5; + *r2 = regaddr & 0x7; + + regaddr >>= 3; + *page = regaddr & 0xffff; + + regaddr >>= 16; + *switch_phy_id = regaddr & 0xff; +} + +uint32_t ipq_mii_read(uint32_t reg) +{ + uint16_t r1, r2, page, switch_phy_id; + uint16_t lo, hi; + + split_addr((uint32_t) reg, &r1, &r2, &page, &switch_phy_id); + + mutex_lock(&switch_mdio_lock); + ipq_mdio_write(0x18 | (switch_phy_id >> 5), switch_phy_id & 0x1f, page); + udelay(100); + lo = ipq_mdio_read(0x10 | r2, r1, NULL); + hi = ipq_mdio_read(0x10 | r2, r1 + 2, NULL); + mutex_unlock(&switch_mdio_lock); + + return (hi << 16) | lo; +} + +void ipq_mii_write(uint32_t reg, uint32_t val) +{ + uint16_t r1, r2, page, switch_phy_id; + uint16_t lo, hi; + + split_addr((uint32_t) reg, &r1, &r2, &page, &switch_phy_id); + lo = val & 0xffff; + hi = (uint16_t) (val >> 16); + + mutex_lock(&switch_mdio_lock); + ipq_mdio_write(0x18 | (switch_phy_id >> 5), switch_phy_id & 0x1f, page); + udelay(100); + ipq_mdio_write(0x10 | r2, r1, lo); + ipq_mdio_write(0x10 | r2, r1 + 2, hi); + mutex_unlock(&switch_mdio_lock); +} + +void ipq_mii_update(uint32_t reg, uint32_t mask, uint32_t val) +{ + uint32_t new_val = 0, org_val = 0; + + org_val = ipq_mii_read(reg); + + new_val = org_val & ~mask; + new_val |= val & mask; + + if (new_val != org_val) + ipq_mii_write(reg, new_val); +} + +static inline void ipq_clk_enable(uint32_t reg) +{ + u32 val; + + val = ipq_mii_read(reg); + val |= BIT(0); + ipq_mii_write(reg, val); +} + +static inline void ipq_clk_disable(uint32_t reg) +{ + u32 val; + + val = ipq_mii_read(reg); + val &= ~BIT(0); + ipq_mii_write(reg, val); +} + +static inline void ipq_clk_reset(uint32_t reg) +{ + u32 val; + + val = ipq_mii_read(reg); + val |= BIT(2); + ipq_mii_write(reg, val); + + udelay(21000); + + val &= ~BIT(2); + ipq_mii_write(reg, val); +} + +void ipq_clock_init(void) +{ + u32 val = 0; + int i; + + /* Enable serdes */ + ipq_clk_enable(SRDS0_SYS_CBCR); + ipq_clk_enable(SRDS1_SYS_CBCR); + + /* Reset serdes */ + ipq_clk_reset(SRDS0_SYS_CBCR); + ipq_clk_reset(SRDS1_SYS_CBCR); + + /* Disable EPHY GMII clock */ + i = 0; + while (i < 2 * PHY_ADDR_NUM) { + ipq_clk_disable(GEPHY0_TX_CBCR + i*0x20); + i++; + } + + /* Enable ephy */ + ipq_clk_enable(EPHY0_SYS_CBCR); + ipq_clk_enable(EPHY1_SYS_CBCR); + ipq_clk_enable(EPHY2_SYS_CBCR); + ipq_clk_enable(EPHY3_SYS_CBCR); + + /* Reset ephy */ + ipq_clk_reset(EPHY0_SYS_CBCR); + ipq_clk_reset(EPHY1_SYS_CBCR); + ipq_clk_reset(EPHY2_SYS_CBCR); + ipq_clk_reset(EPHY3_SYS_CBCR); + + /* Deassert EPHY DSP */ + val = ipq_mii_read(GCC_GEPHY_MISC); + val &= ~GENMASK(4, 0); + ipq_mii_write(GCC_GEPHY_MISC, val); + + /* Enable efuse loading into analog circuit */ + val = ipq_mii_read(EPHY_CFG); + /* BIT20 for PHY0 and PHY1, BIT21 for PHY2 and PHY3 */ + val &= ~GENMASK(21, 20); + ipq_mii_write(EPHY_CFG, val); + + udelay(11000); +} + +void ipq_phy_addr_fixup(void) +{ + int phy_index, addr; + u32 val; + unsigned long phyaddr_mask = 0; + + val = ipq_mii_read(EPHY_CFG); + + for (phy_index = 0, addr = 1; addr <= 4; phy_index++, addr++) { + phyaddr_mask |= BIT(addr); + addr &= GENMASK(4, 0); + val &= ~(GENMASK(4, 0) << (phy_index * PHY_ADDR_LENGTH)); + val |= addr << (phy_index * PHY_ADDR_LENGTH); + } + + pr_debug("programme EPHY reg 0x%x with 0x%x\n", EPHY_CFG, val); + ipq_mii_write(EPHY_CFG, val); + + /* Programe the UNIPHY address if uniphyaddr_fixup specified. + * the UNIPHY address will select three MDIO address from + * unoccupied MDIO address space. + */ + val = ipq_mii_read(UNIPHY_CFG); + + /* For qca8386, the switch occupies the other 16 MDIO address, + * for example, if the phy address is in the range of 0 to 15, + * the switch will occupy the MDIO address from 16 to 31. + */ + phyaddr_mask |= GENMASK(31, 16); + + phy_index = 0; + + for_each_clear_bit_from(addr, &phyaddr_mask, PHY_MAX_ADDR) { + if (phy_index >= UNIPHY_ADDR_NUM) + break; + val &= ~(GENMASK(4, 0) << (phy_index * PHY_ADDR_LENGTH)); + val |= addr << (phy_index * PHY_ADDR_LENGTH); + phy_index++; + } + + if (phy_index < UNIPHY_ADDR_NUM) { + for_each_clear_bit(addr, &phyaddr_mask, PHY_MAX_ADDR) { + if (phy_index >= UNIPHY_ADDR_NUM) + break; + val &= ~(GENMASK(4, 0) << (phy_index * PHY_ADDR_LENGTH)); + val |= addr << (phy_index * PHY_ADDR_LENGTH); + phy_index++; + } + } + + pr_debug("programme UNIPHY reg 0x%x with 0x%x\n", UNIPHY_CFG, val); + ipq_mii_write(UNIPHY_CFG, val); +} +#endif + int ipq_sw_mdio_init(const char *name) { struct mii_dev *bus = mdio_alloc(); @@ -169,6 +373,7 @@ int ipq_sw_mdio_init(const char *name) printf("Failed to allocate IPQ MDIO bus\n"); return -1; } + bus->read = ipq_phy_read; bus->write = ipq_phy_write; bus->reset = NULL; @@ -176,6 +381,47 @@ int ipq_sw_mdio_init(const char *name) return mdio_register(bus); } +#ifdef CONFIG_QCA8084_PHY +static int do_ipq_mii(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char op[2]; + unsigned int reg = 0; + unsigned short data = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + op[0] = argv[1][0]; + if (strlen(argv[1]) > 1) + op[1] = argv[1][1]; + else + op[1] = '\0'; + + if (argc >= 3) + reg = simple_strtoul(argv[2], NULL, 16); + if (argc >= 4) + data = simple_strtoul(argv[3], NULL, 16); + + if (op[0] == 'r') { + data = ipq_mii_read(reg); + printf("0x%x\n", data); + } else if (op[0] == 'w') { + ipq_mii_write(reg, data); + } else { + return CMD_RET_USAGE; + } + + return 0; +} + +U_BOOT_CMD( + ipq_mii, 4, 1, do_ipq_mii, + "IPQ mii utility commands", + "ipq_mii read - read IPQ MII register \n" + "ipq_mii write - write IPQ MII register with \n" +); +#endif + static int do_ipq_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { char op[2]; diff --git a/drivers/net/ipq_common/ipq_mdio.h b/drivers/net/ipq_common/ipq_mdio.h index db98d516f1..fdd79d3914 100644 --- a/drivers/net/ipq_common/ipq_mdio.h +++ b/drivers/net/ipq_common/ipq_mdio.h @@ -34,4 +34,24 @@ #endif #define IPQ_MDIO_RETRY 1000 #define IPQ_MDIO_DELAY 5 + +/* QCA8084 related MDIO Init macros */ +#define UNIPHY_CFG 0xC90F014 +#define EPHY_CFG 0xC90F018 +#define GEPHY0_TX_CBCR 0xC800058 +#define SRDS0_SYS_CBCR 0xC8001A8 +#define SRDS1_SYS_CBCR 0xC8001AC +#define EPHY0_SYS_CBCR 0xC8001B0 +#define EPHY1_SYS_CBCR 0xC8001B4 +#define EPHY2_SYS_CBCR 0xC8001B8 +#define EPHY3_SYS_CBCR 0xC8001BC +#define GCC_GEPHY_MISC 0xC800304 +#define PHY_ADDR_LENGTH 5 +#define PHY_ADDR_NUM 4 +#define UNIPHY_ADDR_NUM 3 +#define MII_HIGH_ADDR_PREFIX 0x18 +#define MII_LOW_ADDR_PREFIX 0x10 + +DEFINE_MUTEX(switch_mdio_lock); + #endif /* End _IPQ_MDIO_H */ diff --git a/drivers/net/ipq_common/ipq_phy.h b/drivers/net/ipq_common/ipq_phy.h index c22d39e479..b393fcebdf 100755 --- a/drivers/net/ipq_common/ipq_phy.h +++ b/drivers/net/ipq_common/ipq_phy.h @@ -41,6 +41,7 @@ #define QCA8033_PHY_ADDR 0x6 #define QCA8081_PHY 0x004DD100 #define QCA8081_1_1_PHY 0x004DD101 +#define QCA8084_PHY 0x004DD180 #define AQUANTIA_PHY_107 0x03a1b4e2 #define AQUANTIA_PHY_109 0x03a1b502 #define AQUANTIA_PHY_111 0x03a1b610 @@ -113,6 +114,8 @@ enum eport_wrapper_cfg { EPORT_WRAPPER_RGMII, EPORT_WRAPPER_PSGMII_FIBER, EPORT_WRAPPER_SGMII_FIBER, + EPORT_WRAPPER_UQXGMII, /* for four channels qca8084 phy mode*/ + EPORT_WRAPPER_UQXGMII_3CHANNELS, /* for three channels qca8084 phy mode */ EPORT_WRAPPER_MAX = 0xFF }; @@ -134,6 +137,8 @@ enum phy_mode { AQ_PHY_TYPE = 3, QCA8033_PHY_TYPE = 4, SFP_PHY_TYPE = 5, + QCA8084_PHY_TYPE = 6, + UNUSED_PHY_TYPE = 0xFF, }; typedef struct { diff --git a/drivers/net/ipq_common/ipq_qca8084.c b/drivers/net/ipq_common/ipq_qca8084.c new file mode 100644 index 0000000000..2a7be7633f --- /dev/null +++ b/drivers/net/ipq_common/ipq_qca8084.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2022, 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 "ipq_phy.h" +#include "ipq_qca8081.h" +#include "ipq_qca8084.h" +#include "ipq_qca8084_clk.h" +#include +#include + +#ifdef DEBUG +#define pr_debug(fmt, args...) printf(fmt, ##args); +#else +#define pr_debug(fmt, args...) +#endif + +extern int ipq_mdio_read(int mii_id, + int regnum, ushort *data); +extern int ipq_mdio_write(int mii_id, + int regnum, u16 data); +extern void qca8084_interface_uqxgmii_mode_set(void); +extern void qca8084_gcc_clock_init(void); +extern u8 qca8081_phy_get_link_status(u32 dev_id, u32 phy_id); +extern u32 qca8081_phy_get_duplex(u32 dev_id, u32 phy_id, fal_port_duplex_t *duplex); +extern u32 qca8081_phy_get_speed(u32 dev_id, u32 phy_id, fal_port_speed_t *speed); +extern void qca8084_uniphy_xpcs_autoneg_restart(uint32_t qca8084_port_id); +extern void qca8084_port_speed_clock_set(uint32_t qca8084_port_id, + fal_port_speed_t speed); +extern void qca8084_uniphy_xpcs_speed_set(uint32_t qca8084_port_id, + fal_port_speed_t speed); +extern void qca8084_port_clk_en_set(uint32_t qca8084_port_id, uint8_t mask, + uint8_t enable); +extern void qca8084_port_clk_reset(uint32_t qca8084_port_id, uint8_t mask); +extern void qca8084_uniphy_uqxgmii_function_reset(uint32_t qca8084_port_id); + +u16 qca8084_phy_reg_read(u32 phy_addr, u32 reg_id) +{ + return ipq_mdio_read(phy_addr, reg_id, NULL); +} + +u16 qca8084_phy_reg_write(u32 phy_addr, u32 reg_id, u16 value) +{ + return ipq_mdio_write(phy_addr, reg_id, value); +} + +u16 qca8084_phy_mmd_read(u32 phy_addr, u16 mmd_num, u16 reg_id) +{ + uint32_t reg_id_c45 = QCA8084_REG_C45_ADDRESS(mmd_num, reg_id); + + return ipq_mdio_read(phy_addr, reg_id_c45, NULL); +} + +u16 qca8084_phy_mmd_write(u32 phy_addr, u16 mmd_num, u16 reg_id, u16 value) +{ + uint32_t reg_id_c45 = QCA8084_REG_C45_ADDRESS(mmd_num, reg_id); + + return ipq_mdio_write(phy_addr, reg_id_c45, value); +} + +void qca8084_phy_modify_mii(uint32_t phy_addr, uint32_t mii_reg, uint32_t mask, + uint32_t value) +{ + uint16_t phy_data = 0, new_phy_data = 0; + + phy_data = qca8084_phy_reg_read(phy_addr, mii_reg); + new_phy_data = (phy_data & ~mask) | value; + qca8084_phy_reg_write(phy_addr, mii_reg, new_phy_data); + /*check the mii register value*/ + phy_data = qca8084_phy_reg_read(phy_addr, mii_reg); + pr_debug("phy_addr:0x%x, mii_reg:0x%x, phy_data:0x%x\n", + phy_addr, mii_reg, phy_data); +} + +void qca8084_phy_modify_mmd(uint32_t phy_addr, uint32_t mmd_num, + uint32_t mmd_reg, uint32_t mask, uint32_t value) +{ + uint16_t phy_data = 0, new_phy_data = 0; + + phy_data = qca8084_phy_mmd_read(phy_addr, mmd_num, mmd_reg); + new_phy_data = (phy_data & ~mask) | value; + qca8084_phy_mmd_write(phy_addr, mmd_num, mmd_reg, new_phy_data); + /* check the mmd register value */ + phy_data = qca8084_phy_mmd_read(phy_addr, mmd_num, mmd_reg); + pr_debug("phy_addr:0x%x, mmd_reg:0x%x, phy_data:0x%x\n", + phy_addr, mmd_reg, phy_data); +} + +void qca8084_phy_ipg_config(uint32_t phy_id, fal_port_speed_t speed) +{ + uint16_t phy_data = 0; + + phy_data = qca8084_phy_mmd_read(phy_id, QCA8084_PHY_MMD7_NUM, + QCA8084_PHY_MMD7_IPG_10_11_ENABLE); + + phy_data &= ~QCA8084_PHY_MMD7_IPG_11_EN; + + /*If speed is 1G, enable 11 ipg tuning*/ + pr_debug("if speed is 1G, enable 11 ipg tuning\n"); + if (speed == FAL_SPEED_1000) + phy_data |= QCA8084_PHY_MMD7_IPG_11_EN; + + qca8084_phy_mmd_write(phy_id, QCA8084_PHY_MMD7_NUM, + QCA8084_PHY_MMD7_IPG_10_11_ENABLE, phy_data); +} + +void qca8084_phy_function_reset(uint32_t phy_id) +{ + uint16_t phy_data = 0; + + phy_data = qca8084_phy_reg_read(phy_id, QCA8084_PHY_FIFO_CONTROL); + + qca8084_phy_reg_write(phy_id, QCA8084_PHY_FIFO_CONTROL, + phy_data & (~QCA8084_PHY_FIFO_RESET)); + + mdelay(50); + + qca8084_phy_reg_write(phy_id, QCA8084_PHY_FIFO_CONTROL, + phy_data | QCA8084_PHY_FIFO_RESET); +} + +void qca8084_phy_uqxgmii_speed_fixup(uint32_t phy_addr, uint32_t qca8084_port_id, + uint32_t status, fal_port_speed_t new_speed) +{ + uint32_t port_clock_en = 0; + + /*Restart the auto-neg of uniphy*/ + pr_debug("Restart the auto-neg of uniphy\n"); + qca8084_uniphy_xpcs_autoneg_restart(qca8084_port_id); + + /*set gmii+ clock to uniphy1 and ethphy*/ + pr_debug("set gmii,xgmii clock to uniphy and gmii to ethphy\n"); + qca8084_port_speed_clock_set(qca8084_port_id, new_speed); + + /*set xpcs speed*/ + pr_debug("set xpcs speed\n"); + qca8084_uniphy_xpcs_speed_set(qca8084_port_id, new_speed); + + /*GMII/XGMII clock and ETHPHY GMII clock enable/disable*/ + pr_debug("GMII/XGMII clock and ETHPHY GMII clock enable/disable\n"); + if (status == 0) + port_clock_en = 1; + qca8084_port_clk_en_set(qca8084_port_id, + QCA8084_CLK_TYPE_UNIPHY | QCA8084_CLK_TYPE_EPHY, + port_clock_en); + + pr_debug("UNIPHY GMII/XGMII interface and ETHPHY GMII interface reset and release\n"); + qca8084_port_clk_reset(qca8084_port_id, + QCA8084_CLK_TYPE_UNIPHY | QCA8084_CLK_TYPE_EPHY); + + pr_debug("ipg_tune and xgmii2gmii reset for uniphy and ETHPHY, function reset\n"); + qca8084_uniphy_uqxgmii_function_reset(qca8084_port_id); + + /*do ethphy function reset: PHY_FIFO_RESET*/ + pr_debug("do ethphy function reset\n"); + qca8084_phy_function_reset(phy_addr); + + /*change IPG from 10 to 11 for 1G speed*/ + qca8084_phy_ipg_config(phy_addr, new_speed); +} + +void qca8084_phy_interface_mode_set(void) +{ + pr_debug("Configure QCA8084 as PORT_UQXGMII..\n"); + /*the work mode is PORT_UQXGMII in default*/ + qca8084_interface_uqxgmii_mode_set(); + + /*init clock for PORT_UQXGMII*/ + qca8084_gcc_clock_init(); + + /*init pinctrl for phy mode to be added later*/ +} + +void qca8084_cdt_thresh_init(u32 phy_id) +{ + qca8084_phy_mmd_write(phy_id, QCA8084_PHY_MMD3_NUM, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL3, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL3_VAL); + qca8084_phy_mmd_write(phy_id, QCA8084_PHY_MMD3_NUM, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL4, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL4_VAL); + qca8084_phy_mmd_write(phy_id, QCA8084_PHY_MMD3_NUM, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL5, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL5_VAL); + qca8084_phy_mmd_write(phy_id, QCA8084_PHY_MMD3_NUM, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL6, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL6_VAL); + qca8084_phy_mmd_write(phy_id, QCA8084_PHY_MMD3_NUM, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL7, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL7_VAL); + qca8084_phy_mmd_write(phy_id, QCA8084_PHY_MMD3_NUM, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL9, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL9_VAL); + qca8084_phy_mmd_write(phy_id, QCA8084_PHY_MMD3_NUM, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL13, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL13_VAL); + qca8084_phy_mmd_write(phy_id, QCA8084_PHY_MMD3_NUM, + QCA8084_PHY_MMD3_CDT_THRESH_CTRL14, + QCA8084_PHY_MMD3_NEAR_ECHO_THRESH_VAL); +} + +void qca8084_phy_modify_debug(u32 phy_addr, u32 debug_reg, + u32 mask, u32 value) +{ + u16 phy_data = 0, new_phy_data = 0; + + qca8084_phy_reg_write(phy_addr, QCA8084_DEBUG_PORT_ADDRESS, debug_reg); + phy_data = qca8084_phy_reg_read(phy_addr, QCA8084_DEBUG_PORT_DATA); + if (phy_data == PHY_INVALID_DATA) + pr_debug("qca8084_phy_reg_read failed\n"); + + new_phy_data = (phy_data & ~mask) | value; + qca8084_phy_reg_write(phy_addr, QCA8084_DEBUG_PORT_ADDRESS, debug_reg); + qca8084_phy_reg_write(phy_addr, QCA8084_DEBUG_PORT_DATA, new_phy_data); + + /* check debug register value */ + qca8084_phy_reg_write(phy_addr, QCA8084_DEBUG_PORT_ADDRESS, debug_reg); + phy_data = qca8084_phy_reg_read(phy_addr, QCA8084_DEBUG_PORT_DATA); + pr_debug("phy_addr:0x%x, debug_reg:0x%x, phy_data:0x%x\n", + phy_addr, debug_reg, phy_data); +} + +void qca8084_phy_reset(u32 phy_addr) +{ + u16 phy_data; + + phy_data = qca8084_phy_reg_read(phy_addr, QCA8084_PHY_CONTROL); + qca8084_phy_reg_write(phy_addr, QCA8084_PHY_CONTROL, + phy_data | QCA8084_CTRL_SOFTWARE_RESET); +} + +void qca8084_phy_adc_edge_set(u32 phy_addr, u32 adc_edge) +{ + qca8084_phy_modify_debug(phy_addr, + QCA8084_PHY_DEBUG_ANA_INTERFACE_CLK_SEL, 0xf0, adc_edge); + qca8084_phy_reset(phy_addr); +} + +void ipq_qca8084_phy_hw_init(struct phy_ops **ops, u32 phy_addr) +{ +#ifdef DEBUG + u16 phy_data; +#endif + struct phy_ops *qca8084_ops; + + qca8084_ops = (struct phy_ops *)malloc(sizeof(struct phy_ops)); + if (!qca8084_ops) { + pr_debug("Error allocating memory for phy ops\n"); + return; + } + + /* Note that qca8084 PHY is based on qca8081 PHY and so the following + * ops functions required would be re-used from qca8081 */ + + qca8084_ops->phy_get_link_status = qca8081_phy_get_link_status; + qca8084_ops->phy_get_speed = qca8081_phy_get_speed; + qca8084_ops->phy_get_duplex = qca8081_phy_get_duplex; + *ops = qca8084_ops; + +#ifdef DEBUG + phy_data = qca8084_phy_reg_read(phy_addr, QCA8081_PHY_ID1); + printf("PHY ID1: 0x%x\n", phy_data); + phy_data = qca8084_phy_reg_read(phy_addr, QCA8081_PHY_ID2); + printf("PHY ID2: 0x%x\n", phy_data); +#endif + + /* adjust CDT threshold */ + qca8084_cdt_thresh_init(phy_addr); + + /* invert ADC clock edge as falling edge to fix link issue */ + qca8084_phy_adc_edge_set(phy_addr, ADC_FALLING); +} diff --git a/drivers/net/ipq_common/ipq_qca8084.h b/drivers/net/ipq_common/ipq_qca8084.h new file mode 100644 index 0000000000..4f93c47e75 --- /dev/null +++ b/drivers/net/ipq_common/ipq_qca8084.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022, 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. +*/ + +#ifndef _QCA8084_PHY_H_ +#define _QCA8084_PHY_H_ + +/*MII register*/ +#define QCA8084_PHY_FIFO_CONTROL 0x19 + +/*MII register field*/ +#define QCA8084_PHY_FIFO_RESET 0x3 + +/*MMD1 register*/ +#define QCA8084_PHY_MMD1_NUM 0x1 + +/*MMD3 register*/ +#define QCA8084_PHY_MMD3_NUM 0x3 +#define QCA8084_PHY_MMD3_ADDR_8023AZ_EEE_2500M_CAPABILITY 0x15 +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL3 0x8074 +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL4 0x8075 +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL5 0x8076 +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL6 0x8077 +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL7 0x8078 +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL9 0x807a +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL13 0x807e +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL14 0x807f + +/*MMD3 register field*/ +#define QCA8084_PHY_EEE_CAPABILITY_2500M 0x1 +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL3_VAL 0xc040 +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL4_VAL 0xa060 +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL5_VAL 0xc040 +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL6_VAL 0xa060 +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL7_VAL 0xc050 +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL9_VAL 0xc060 +#define QCA8084_PHY_MMD3_CDT_THRESH_CTRL13_VAL 0xb060 +#define QCA8084_PHY_MMD3_NEAR_ECHO_THRESH_VAL 0x1eb0 + +/*MMD7 register*/ +#define QCA8084_PHY_MMD7_NUM 0x7 +#define QCA8084_PHY_MMD7_ADDR_8023AZ_EEE_2500M_CTRL 0x3e +#define QCA8084_PHY_MMD7_ADDR_8023AZ_EEE_2500M_PARTNER 0x3f +#define QCA8084_PHY_MMD7_IPG_10_11_ENABLE 0x901d + +/*MMD7 register field*/ +#define QCA8084_PHY_8023AZ_EEE_2500BT 0x1 +#define QCA8084_PHY_MMD7_IPG_10_EN 0 +#define QCA8084_PHY_MMD7_IPG_11_EN 0x1 + +/*DEBUG port analog register*/ +#define QCA8084_PHY_DEBUG_ANA_INTERFACE_CLK_SEL 0x8b80 +#define QCA8084_DEBUG_PORT_ADDRESS 29 +#define QCA8084_DEBUG_PORT_DATA 30 + +#define QCA8084_PHY_CONTROL 0 +#define QCA8084_CTRL_SOFTWARE_RESET 0x8000 + +#define PHY_INVALID_DATA 0xffff + +#define QCA8084_MII_ADDR_C45 (1<<30) +#define QCA8084_REG_C45_ADDRESS(dev_type, reg_num) (QCA8084_MII_ADDR_C45 | \ + ((dev_type & 0x1f) << 16) | (reg_num & 0xffff)) + +typedef enum { + ADC_RISING = 0, + ADC_FALLING = 0xf0, +} +qca8084_adc_edge_t; + +#endif diff --git a/drivers/net/ipq_common/ipq_qca8084_clk.c b/drivers/net/ipq_common/ipq_qca8084_clk.c new file mode 100644 index 0000000000..0cfebbeb6e --- /dev/null +++ b/drivers/net/ipq_common/ipq_qca8084_clk.c @@ -0,0 +1,1210 @@ +/* + * Copyright (c) 2022, 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 "ipq_phy.h" +#include "ipq_qca8084_clk.h" +#include +#include +#include +#include + +#define QCA8084_PORT_CLK_CBC_MAX 8 +/* 2 uniphy with rx and tx */ +#define QCA8084_UNIPHY_INSTANCE 2 + +#ifdef DEBUG +#define pr_debug(fmt, args...) printf(fmt, ##args); +#else +#define pr_debug(fmt, args...) +#endif + +extern uint32_t ipq_mii_read(uint32_t reg); +extern void ipq_mii_write(uint32_t reg, uint32_t val); +extern void ipq_mii_update(uint32_t reg, uint32_t mask, uint32_t val); + +static uint64_t qca8084_uniphy_raw_clock[QCA8084_UNIPHY_INSTANCE * 2] = {0}; + +static const unsigned long qca8084_switch_core_support_rates[] = { + UQXGMII_SPEED_2500M_CLK, +}; + +static const unsigned long qca8084_cpuport_clk_support_rates[] = { + UQXGMII_SPEED_10M_CLK, + UQXGMII_SPEED_100M_CLK, + UQXGMII_SPEED_1000M_CLK, + UQXGMII_SPEED_2500M_CLK, +}; + +static const unsigned long qca8084_phyport_clk_support_rates[] = { + UQXGMII_SPEED_10M_CLK, + UQXGMII_SPEED_100M_CLK, + UQXGMII_SPEED_1000M_CLK, + UQXGMII_SPEED_2500M_CLK, + UQXGMII_XPCS_SPEED_2500M_CLK, +}; + +static const unsigned long qca8084_ahb_clk_support_rates[] = { + QCA8084_AHB_CLK_RATE_104P17M +}; + +static const unsigned long qca8084_sys_clk_support_rates[] = { + QCA8084_SYS_CLK_RATE_25M, +}; + +static const struct qca8084_parent_data qca8084_switch_core_pdata[] = { + { QCA8084_XO_CLK_RATE_50M, QCA8084_P_XO, 0 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY1_TX312P5M, 1 }, +}; + +static const struct qca8084_parent_data qca8084_mac0_tx_clk_pdata[] = { + { QCA8084_XO_CLK_RATE_50M, QCA8084_P_XO, 0 } , + { UQXGMII_SPEED_1000M_CLK, QCA8084_P_UNIPHY1_TX, 2 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY1_TX, 2 }, +}; + +static const struct qca8084_parent_data qca8084_mac0_rx_clk_pdata[] = { + { QCA8084_XO_CLK_RATE_50M, QCA8084_P_XO, 0 } , + { UQXGMII_SPEED_1000M_CLK, QCA8084_P_UNIPHY1_RX, 1 }, + { UQXGMII_SPEED_1000M_CLK, QCA8084_P_UNIPHY1_TX, 2 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY1_RX, 1 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY1_TX, 2 }, +}; + +/* port 1, 2, 3 rx/tx clock have the same parents */ +static const struct qca8084_parent_data qca8084_mac1_tx_clk_pdata[] = { + { QCA8084_XO_CLK_RATE_50M, QCA8084_P_XO, 0 } , + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY1_TX312P5M, 6 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY1_RX312P5M, 7 }, +}; + +static const struct qca8084_parent_data qca8084_mac1_rx_clk_pdata[] = { + { QCA8084_XO_CLK_RATE_50M, QCA8084_P_XO, 0 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY1_TX312P5M, 6 }, +}; + +static const struct qca8084_parent_data qca8084_mac4_tx_clk_pdata[] = { + { QCA8084_XO_CLK_RATE_50M, QCA8084_P_XO, 0 }, + { UQXGMII_SPEED_1000M_CLK, QCA8084_P_UNIPHY0_RX, 1 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY0_RX, 1 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY1_TX312P5M, 3 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY1_RX312P5M, 7 }, +}; + +static const struct qca8084_parent_data qca8084_mac4_rx_clk_pdata[] = { + { QCA8084_XO_CLK_RATE_50M, QCA8084_P_XO, 0 }, + { UQXGMII_SPEED_1000M_CLK, QCA8084_P_UNIPHY0_TX, 2 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY0_TX, 2 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY1_TX312P5M, 3 }, +}; + +static const struct qca8084_parent_data qca8084_mac5_tx_clk_pdata[] = { + { QCA8084_XO_CLK_RATE_50M, QCA8084_P_XO, 0 }, + { UQXGMII_SPEED_1000M_CLK, QCA8084_P_UNIPHY0_TX, 2 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY0_TX, 2 }, +}; + +static const struct qca8084_parent_data qca8084_mac5_rx_clk_pdata[] = { + { QCA8084_XO_CLK_RATE_50M, QCA8084_P_XO, 0 }, + { UQXGMII_SPEED_1000M_CLK, QCA8084_P_UNIPHY0_RX, 1 }, + { UQXGMII_SPEED_1000M_CLK, QCA8084_P_UNIPHY0_TX, 2 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY0_RX, 1 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY0_TX, 2 }, +}; + +static const struct qca8084_parent_data qca8084_ahb_clk_pdata[] = { + { QCA8084_XO_CLK_RATE_50M, QCA8084_P_XO, 0 }, + { UQXGMII_SPEED_2500M_CLK, QCA8084_P_UNIPHY1_TX312P5M, 2 }, +}; + +static const struct qca8084_parent_data qca8084_sys_clk_pdata[] = { + { QCA8084_XO_CLK_RATE_50M, QCA8084_P_XO, 0 }, +}; + +static struct clk_lookup qca8084_clk_lookup_table[] = { + /* switch core clock */ + CLK_LOOKUP(4, 0, 8, CBCR_CLK_RESET, + QCA8084_SWITCH_CORE_CLK, + qca8084_switch_core_support_rates, ARRAY_SIZE(qca8084_switch_core_support_rates), + qca8084_switch_core_pdata, ARRAY_SIZE(qca8084_switch_core_pdata)), + CLK_LOOKUP(4, 0, 0x10, CBCR_CLK_RESET, + QCA8084_APB_BRIDGE_CLK, + qca8084_switch_core_support_rates, ARRAY_SIZE(qca8084_switch_core_support_rates), + qca8084_switch_core_pdata, ARRAY_SIZE(qca8084_switch_core_pdata)), + /* port 0 tx clock */ + CLK_LOOKUP(0x18, 0x1c, 0x20, CBCR_CLK_RESET, + QCA8084_MAC0_TX_CLK, + qca8084_cpuport_clk_support_rates, ARRAY_SIZE(qca8084_cpuport_clk_support_rates), + qca8084_mac0_tx_clk_pdata, ARRAY_SIZE(qca8084_mac0_tx_clk_pdata)), + CLK_LOOKUP(0x18, 0x1c, 0x24, CBCR_CLK_RESET, + QCA8084_MAC0_TX_UNIPHY1_CLK, + qca8084_cpuport_clk_support_rates, ARRAY_SIZE(qca8084_cpuport_clk_support_rates), + qca8084_mac0_tx_clk_pdata, ARRAY_SIZE(qca8084_mac0_tx_clk_pdata)), + /* port 0 rx clock */ + CLK_LOOKUP(0x2c, 0x30, 0x34, CBCR_CLK_RESET, + QCA8084_MAC0_RX_CLK, + qca8084_cpuport_clk_support_rates, ARRAY_SIZE(qca8084_cpuport_clk_support_rates), + qca8084_mac0_rx_clk_pdata, ARRAY_SIZE(qca8084_mac0_rx_clk_pdata)), + CLK_LOOKUP(0x2c, 0x30, 0x3c, CBCR_CLK_RESET, + QCA8084_MAC0_RX_UNIPHY1_CLK, + qca8084_cpuport_clk_support_rates, ARRAY_SIZE(qca8084_cpuport_clk_support_rates), + qca8084_mac0_rx_clk_pdata, ARRAY_SIZE(qca8084_mac0_rx_clk_pdata)), + /* port 1 tx clock */ + CLK_LOOKUP(0x44, 0x48, 0x50, CBCR_CLK_RESET, + QCA8084_MAC1_UNIPHY1_CH0_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_tx_clk_pdata, ARRAY_SIZE(qca8084_mac1_tx_clk_pdata)), + CLK_LOOKUP(0x44, 0x48, 0x54, CBCR_CLK_RESET, + QCA8084_MAC1_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_tx_clk_pdata, ARRAY_SIZE(qca8084_mac1_tx_clk_pdata)), + CLK_LOOKUP(0x44, 0x48, 0x58, CBCR_CLK_RESET, + QCA8084_MAC1_GEPHY0_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_tx_clk_pdata, ARRAY_SIZE(qca8084_mac1_tx_clk_pdata)), + CLK_LOOKUP(0x44, 0x4c, 0x5c, CBCR_CLK_RESET, + QCA8084_MAC1_UNIPHY1_CH0_XGMII_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_tx_clk_pdata, ARRAY_SIZE(qca8084_mac1_tx_clk_pdata)), + /* port 1 rx clock */ + CLK_LOOKUP(0x64, 0x68, 0x70, CBCR_CLK_RESET, + QCA8084_MAC1_UNIPHY1_CH0_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_rx_clk_pdata, ARRAY_SIZE(qca8084_mac1_rx_clk_pdata)), + CLK_LOOKUP(0x64, 0x68, 0x74, CBCR_CLK_RESET, + QCA8084_MAC1_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_rx_clk_pdata, ARRAY_SIZE(qca8084_mac1_rx_clk_pdata)), + CLK_LOOKUP(0x64, 0x68, 0x78, CBCR_CLK_RESET, + QCA8084_MAC1_GEPHY0_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_rx_clk_pdata, ARRAY_SIZE(qca8084_mac1_rx_clk_pdata)), + CLK_LOOKUP(0x64, 0x6c, 0x7c, CBCR_CLK_RESET, + QCA8084_MAC1_UNIPHY1_CH0_XGMII_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_rx_clk_pdata, ARRAY_SIZE(qca8084_mac1_rx_clk_pdata)), + /* port 2 tx clock */ + CLK_LOOKUP(0x84, 0x88, 0x90, CBCR_CLK_RESET, + QCA8084_MAC2_UNIPHY1_CH1_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_tx_clk_pdata, ARRAY_SIZE(qca8084_mac1_tx_clk_pdata)), + CLK_LOOKUP(0x84, 0x88, 0x94, CBCR_CLK_RESET, + QCA8084_MAC2_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_tx_clk_pdata, ARRAY_SIZE(qca8084_mac1_tx_clk_pdata)), + CLK_LOOKUP(0x84, 0x88, 0x98, CBCR_CLK_RESET, + QCA8084_MAC2_GEPHY1_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_tx_clk_pdata, ARRAY_SIZE(qca8084_mac1_tx_clk_pdata)), + CLK_LOOKUP(0x84, 0x8c, 0x9c, CBCR_CLK_RESET, + QCA8084_MAC2_UNIPHY1_CH1_XGMII_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_tx_clk_pdata, ARRAY_SIZE(qca8084_mac1_tx_clk_pdata)), + /* port 2 rx clock */ + CLK_LOOKUP(0xa4, 0xa8, 0xb0, CBCR_CLK_RESET, + QCA8084_MAC2_UNIPHY1_CH1_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_rx_clk_pdata, ARRAY_SIZE(qca8084_mac1_rx_clk_pdata)), + CLK_LOOKUP(0xa4, 0xa8, 0xb4, CBCR_CLK_RESET, + QCA8084_MAC2_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_rx_clk_pdata, ARRAY_SIZE(qca8084_mac1_rx_clk_pdata)), + CLK_LOOKUP(0xa4, 0xa8, 0xb8, CBCR_CLK_RESET, + QCA8084_MAC2_GEPHY1_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_rx_clk_pdata, ARRAY_SIZE(qca8084_mac1_rx_clk_pdata)), + CLK_LOOKUP(0xa4, 0xac, 0xbc, CBCR_CLK_RESET, + QCA8084_MAC2_UNIPHY1_CH1_XGMII_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_rx_clk_pdata, ARRAY_SIZE(qca8084_mac1_rx_clk_pdata)), + /* port 3 tx clock */ + CLK_LOOKUP(0xc4, 0xc8, 0xd0, CBCR_CLK_RESET, + QCA8084_MAC3_UNIPHY1_CH2_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_tx_clk_pdata, ARRAY_SIZE(qca8084_mac1_tx_clk_pdata)), + CLK_LOOKUP(0xc4, 0xc8, 0xd4, CBCR_CLK_RESET, + QCA8084_MAC3_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_tx_clk_pdata, ARRAY_SIZE(qca8084_mac1_tx_clk_pdata)), + CLK_LOOKUP(0xc4, 0xc8, 0xd8, CBCR_CLK_RESET, + QCA8084_MAC3_GEPHY2_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_tx_clk_pdata, ARRAY_SIZE(qca8084_mac1_tx_clk_pdata)), + CLK_LOOKUP(0xc4, 0xcc, 0xdc, CBCR_CLK_RESET, + QCA8084_MAC3_UNIPHY1_CH2_XGMII_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_tx_clk_pdata, ARRAY_SIZE(qca8084_mac1_tx_clk_pdata)), + /* port 3 rx clock */ + CLK_LOOKUP(0xe4, 0xe8, 0xf0, CBCR_CLK_RESET, + QCA8084_MAC3_UNIPHY1_CH2_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_rx_clk_pdata, ARRAY_SIZE(qca8084_mac1_rx_clk_pdata)), + CLK_LOOKUP(0xe4, 0xe8, 0xf4, CBCR_CLK_RESET, + QCA8084_MAC3_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_rx_clk_pdata, ARRAY_SIZE(qca8084_mac1_rx_clk_pdata)), + CLK_LOOKUP(0xe4, 0xe8, 0xf8, CBCR_CLK_RESET, + QCA8084_MAC3_GEPHY2_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_rx_clk_pdata, ARRAY_SIZE(qca8084_mac1_rx_clk_pdata)), + CLK_LOOKUP(0xe4, 0xec, 0xfc, CBCR_CLK_RESET, + QCA8084_MAC3_UNIPHY1_CH2_XGMII_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac1_rx_clk_pdata, ARRAY_SIZE(qca8084_mac1_rx_clk_pdata)), + /* port 4 tx clock */ + CLK_LOOKUP(0x104, 0x108, 0x110, CBCR_CLK_RESET, + QCA8084_MAC4_UNIPHY1_CH3_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac4_tx_clk_pdata, ARRAY_SIZE(qca8084_mac4_tx_clk_pdata)), + CLK_LOOKUP(0x104, 0x108, 0x114, CBCR_CLK_RESET, + QCA8084_MAC4_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac4_tx_clk_pdata, ARRAY_SIZE(qca8084_mac4_tx_clk_pdata)), + CLK_LOOKUP(0x104, 0x108, 0x118, CBCR_CLK_RESET, + QCA8084_MAC4_GEPHY3_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac4_tx_clk_pdata, ARRAY_SIZE(qca8084_mac4_tx_clk_pdata)), + CLK_LOOKUP(0x104, 0x10c, 0x11c, CBCR_CLK_RESET, + QCA8084_MAC4_UNIPHY1_CH3_XGMII_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac4_tx_clk_pdata, ARRAY_SIZE(qca8084_mac4_tx_clk_pdata)), + /* port 4 rx clock */ + CLK_LOOKUP(0x124, 0x128, 0x130, CBCR_CLK_RESET, + QCA8084_MAC4_UNIPHY1_CH3_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac4_rx_clk_pdata, ARRAY_SIZE(qca8084_mac4_rx_clk_pdata)), + CLK_LOOKUP(0x124, 0x128, 0x134, CBCR_CLK_RESET, + QCA8084_MAC4_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac4_rx_clk_pdata, ARRAY_SIZE(qca8084_mac4_rx_clk_pdata)), + CLK_LOOKUP(0x124, 0x128, 0x138, CBCR_CLK_RESET, + QCA8084_MAC4_GEPHY3_RX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac4_rx_clk_pdata, ARRAY_SIZE(qca8084_mac4_rx_clk_pdata)), + CLK_LOOKUP(0x124, 0x12c, 0x13c, CBCR_CLK_RESET, + QCA8084_MAC4_UNIPHY1_CH3_XGMII_TX_CLK, + qca8084_phyport_clk_support_rates, ARRAY_SIZE(qca8084_phyport_clk_support_rates), + qca8084_mac4_rx_clk_pdata, ARRAY_SIZE(qca8084_mac4_rx_clk_pdata)), + /* port 5 tx clock */ + CLK_LOOKUP(0x144, 0x148, 0x14c, CBCR_CLK_RESET, + QCA8084_MAC5_TX_CLK, + qca8084_cpuport_clk_support_rates, ARRAY_SIZE(qca8084_cpuport_clk_support_rates), + qca8084_mac5_tx_clk_pdata, ARRAY_SIZE(qca8084_mac5_tx_clk_pdata)), + CLK_LOOKUP(0x144, 0x148, 0x150, CBCR_CLK_RESET, + QCA8084_MAC5_TX_UNIPHY0_CLK, + qca8084_cpuport_clk_support_rates, ARRAY_SIZE(qca8084_cpuport_clk_support_rates), + qca8084_mac5_tx_clk_pdata, ARRAY_SIZE(qca8084_mac5_tx_clk_pdata)), + /* port 5 rx clock */ + CLK_LOOKUP(0x158, 0x15c, 0x160, CBCR_CLK_RESET, + QCA8084_MAC5_RX_CLK, + qca8084_cpuport_clk_support_rates, ARRAY_SIZE(qca8084_cpuport_clk_support_rates), + qca8084_mac5_rx_clk_pdata, ARRAY_SIZE(qca8084_mac5_rx_clk_pdata)), + CLK_LOOKUP(0x158, 0x15c, 0x164, CBCR_CLK_RESET, + QCA8084_MAC5_RX_UNIPHY0_CLK, + qca8084_cpuport_clk_support_rates, ARRAY_SIZE(qca8084_cpuport_clk_support_rates), + qca8084_mac5_rx_clk_pdata, ARRAY_SIZE(qca8084_mac5_rx_clk_pdata)), + /* AHB bridge clock */ + CLK_LOOKUP(0x16c, 0, 0x170, CBCR_CLK_RESET, + QCA8084_AHB_CLK, + qca8084_ahb_clk_support_rates, ARRAY_SIZE(qca8084_ahb_clk_support_rates), + qca8084_ahb_clk_pdata, ARRAY_SIZE(qca8084_ahb_clk_pdata)), + CLK_LOOKUP(0x16c, 0, 0x174, CBCR_CLK_RESET, + QCA8084_SEC_CTRL_AHB_CLK, + qca8084_ahb_clk_support_rates, ARRAY_SIZE(qca8084_ahb_clk_support_rates), + qca8084_ahb_clk_pdata, ARRAY_SIZE(qca8084_ahb_clk_pdata)), + CLK_LOOKUP(0x16c, 0, 0x178, CBCR_CLK_RESET, + QCA8084_TLMM_CLK, + qca8084_ahb_clk_support_rates, ARRAY_SIZE(qca8084_ahb_clk_support_rates), + qca8084_ahb_clk_pdata, ARRAY_SIZE(qca8084_ahb_clk_pdata)), + CLK_LOOKUP(0x16c, 0, 0x190, CBCR_CLK_RESET, + QCA8084_TLMM_AHB_CLK, + qca8084_ahb_clk_support_rates, ARRAY_SIZE(qca8084_ahb_clk_support_rates), + qca8084_ahb_clk_pdata, ARRAY_SIZE(qca8084_ahb_clk_pdata)), + CLK_LOOKUP(0x16c, 0, 0x194, CBCR_CLK_RESET, + QCA8084_CNOC_AHB_CLK, + qca8084_ahb_clk_support_rates, ARRAY_SIZE(qca8084_ahb_clk_support_rates), + qca8084_ahb_clk_pdata, ARRAY_SIZE(qca8084_ahb_clk_pdata)), + CLK_LOOKUP(0x16c, 0, 0x198, CBCR_CLK_RESET, + QCA8084_MDIO_AHB_CLK, + qca8084_ahb_clk_support_rates, ARRAY_SIZE(qca8084_ahb_clk_support_rates), + qca8084_ahb_clk_pdata, ARRAY_SIZE(qca8084_ahb_clk_pdata)), + CLK_LOOKUP(0x16c, 0, 0x19c, CBCR_CLK_RESET, + QCA8084_MDIO_MASTER_AHB_CLK, + qca8084_ahb_clk_support_rates, ARRAY_SIZE(qca8084_ahb_clk_support_rates), + qca8084_ahb_clk_pdata, ARRAY_SIZE(qca8084_ahb_clk_pdata)), + /* SYS clock */ + CLK_LOOKUP(0x1a4, 0, 0x1a8, CBCR_CLK_RESET, + QCA8084_SRDS0_SYS_CLK, + qca8084_sys_clk_support_rates, ARRAY_SIZE(qca8084_sys_clk_support_rates), + qca8084_sys_clk_pdata, ARRAY_SIZE(qca8084_sys_clk_pdata)), + CLK_LOOKUP(0x1a4, 0, 0x1ac, CBCR_CLK_RESET, + QCA8084_SRDS1_SYS_CLK, + qca8084_sys_clk_support_rates, ARRAY_SIZE(qca8084_sys_clk_support_rates), + qca8084_sys_clk_pdata, ARRAY_SIZE(qca8084_sys_clk_pdata)), + CLK_LOOKUP(0x1a4, 0, 0x1b0, CBCR_CLK_RESET, + QCA8084_GEPHY0_SYS_CLK, + qca8084_sys_clk_support_rates, ARRAY_SIZE(qca8084_sys_clk_support_rates), + qca8084_sys_clk_pdata, ARRAY_SIZE(qca8084_sys_clk_pdata)), + CLK_LOOKUP(0x1a4, 0, 0x1b4, CBCR_CLK_RESET, + QCA8084_GEPHY1_SYS_CLK, + qca8084_sys_clk_support_rates, ARRAY_SIZE(qca8084_sys_clk_support_rates), + qca8084_sys_clk_pdata, ARRAY_SIZE(qca8084_sys_clk_pdata)), + CLK_LOOKUP(0x1a4, 0, 0x1b8, CBCR_CLK_RESET, + QCA8084_GEPHY2_SYS_CLK, + qca8084_sys_clk_support_rates, ARRAY_SIZE(qca8084_sys_clk_support_rates), + qca8084_sys_clk_pdata, ARRAY_SIZE(qca8084_sys_clk_pdata)), + CLK_LOOKUP(0x1a4, 0, 0x1bc, CBCR_CLK_RESET, + QCA8084_GEPHY3_SYS_CLK, + qca8084_sys_clk_support_rates, ARRAY_SIZE(qca8084_sys_clk_support_rates), + qca8084_sys_clk_pdata, ARRAY_SIZE(qca8084_sys_clk_pdata)), + + /* SEC control clock */ + CLK_LOOKUP(0x1c4, 0, 0x1c8, CBCR_CLK_RESET, + QCA8084_SEC_CTRL_CLK, + qca8084_sys_clk_support_rates, ARRAY_SIZE(qca8084_sys_clk_support_rates), + qca8084_sys_clk_pdata, ARRAY_SIZE(qca8084_sys_clk_pdata)), + CLK_LOOKUP(0x1c4, 0, 0x1d0, CBCR_CLK_RESET, + QCA8084_SEC_CTRL_SENSE_CLK, + qca8084_sys_clk_support_rates, ARRAY_SIZE(qca8084_sys_clk_support_rates), + qca8084_sys_clk_pdata, ARRAY_SIZE(qca8084_sys_clk_pdata)), + + /* GEPHY reset */ + CLK_LOOKUP(0, 0, 0x304, BIT(0), QCA8084_GEPHY_P0_MDC_SW_RST, NULL, 0, NULL, 0), + CLK_LOOKUP(0, 0, 0x304, BIT(1), QCA8084_GEPHY_P1_MDC_SW_RST, NULL, 0, NULL, 0), + CLK_LOOKUP(0, 0, 0x304, BIT(2), QCA8084_GEPHY_P2_MDC_SW_RST, NULL, 0, NULL, 0), + CLK_LOOKUP(0, 0, 0x304, BIT(3), QCA8084_GEPHY_P3_MDC_SW_RST, NULL, 0, NULL, 0), + CLK_LOOKUP(0, 0, 0x304, BIT(4), QCA8084_GEPHY_DSP_HW_RST, NULL, 0, NULL, 0), + + /* Global reset */ + CLK_LOOKUP(0, 0, 0x308, BIT(0), QCA8084_GLOBAL_RST, NULL, 0, NULL, 0), + + /* XPCS reset */ + CLK_LOOKUP(0, 0, 0x30c, BIT(0), QCA8084_UNIPHY_XPCS_RST, NULL, 0, NULL, 0), +}; + +static inline struct clk_lookup *qca8084_clk_find(const char *clock_id) +{ + int i; + struct clk_lookup *clk; + + for (i = 0; i < ARRAY_SIZE(qca8084_clk_lookup_table); i++) { + clk = &qca8084_clk_lookup_table[i]; + if (!strncmp(clock_id, clk->clk_name, strlen(clock_id))) + return clk; + } + + return NULL; +} + +static inline void qca8084_clk_update(uint32_t cmd_reg) +{ + uint32_t i, reg_val; + + /* update RCG to the new programmed configuration */ + reg_val = ipq_mii_read(cmd_reg); + reg_val |= RCGR_CMD_UPDATE; + ipq_mii_write(cmd_reg, reg_val); + + for (i = 1000; i > 0; i--) { + reg_val = ipq_mii_read(cmd_reg); + if (!(reg_val & RCGR_CMD_UPDATE)) + return; + + udelay(1); + } + + pr_debug("CLK cmd reg 0x%x fails updating to new configurations\n", cmd_reg); + return; +} + +void qca8084_clk_assert(const char *clock_id) +{ + struct clk_lookup *clk; + uint32_t cbc_reg = 0; + + clk = qca8084_clk_find(clock_id); + if (!clk) { + pr_debug("CLK %s is not found!\n", clock_id); + return; + } + + cbc_reg = QCA8084_CLK_BASE_REG + clk->cbc; + + ipq_mii_update(cbc_reg, clk->rst_bit, clk->rst_bit); + return; +} + +void qca8084_clk_deassert(const char *clock_id) +{ + struct clk_lookup *clk; + uint32_t cbc_reg = 0; + + clk = qca8084_clk_find(clock_id); + if (!clk) { + pr_debug("CLK %s is not found!\n", clock_id); + return; + } + + cbc_reg = QCA8084_CLK_BASE_REG + clk->cbc; + + ipq_mii_update(cbc_reg, clk->rst_bit, 0); + return; +} + +void qca8084_clk_reset(const char *clock_id) +{ + qca8084_clk_assert(clock_id); + + /* Time required by HW to complete assert */ + udelay(10); + + qca8084_clk_deassert(clock_id); + + return; +} + +uint8_t qca8084_clk_is_enabled(const char *clock_id) +{ + struct clk_lookup *clk; + uint32_t reg_val = 0; + + clk = qca8084_clk_find(clock_id); + if (!clk) { + pr_debug("CLK %s is not found!\n", clock_id); + return 0; + } + + reg_val = ipq_mii_read(QCA8084_CLK_BASE_REG + clk->rcg - 4); + return (reg_val & RCGR_CMD_ROOT_OFF) == 0; +} + +void qca8084_clk_enable(const char *clock_id) +{ + struct clk_lookup *clk; + uint32_t cbc_reg = 0, reg_val = 0; + + clk = qca8084_clk_find(clock_id); + if (!clk) { + pr_debug("CLK %s is not found!\n", clock_id); + return; + } + + cbc_reg = QCA8084_CLK_BASE_REG + clk->cbc; + ipq_mii_update(cbc_reg, CBCR_CLK_ENABLE, CBCR_CLK_ENABLE); + + udelay(1); + reg_val = ipq_mii_read(cbc_reg); + if (reg_val & CBCR_CLK_OFF) { + pr_debug("CLK %s is not enabled!\n", clock_id); + return; + } + + return; +} + +void qca8084_clk_disable(const char *clock_id) +{ + struct clk_lookup *clk; + uint32_t cbc_reg = 0; + + clk = qca8084_clk_find(clock_id); + if (!clk) { + pr_debug("CLK %s is not found!\n", clock_id); + return; + } + + cbc_reg = QCA8084_CLK_BASE_REG + clk->cbc; + + ipq_mii_update(cbc_reg, CBCR_CLK_ENABLE, 0); + return; +} + +void qca8084_clk_parent_set(const char *clock_id, qca8084_clk_parent_t parent) +{ + struct clk_lookup *clk; + uint32_t i, reg_val; + uint32_t rcg_reg = 0, cmd_reg = 0, cfg = 0, cur_cfg = 0; + const struct qca8084_parent_data *pdata = NULL; + + clk = qca8084_clk_find(clock_id); + if (!clk) { + pr_debug("CLK %s is not found!\n", clock_id); + return; + } + + for (i = 0; i < clk->num_parent; i++) { + pdata = &(clk->pdata[i]); + if (pdata->parent == parent) + break; + } + + if (i == clk->num_parent) { + pr_debug("CLK %s is configured as incorrect parent %d\n", clock_id, parent); + return; + } + + rcg_reg = QCA8084_CLK_BASE_REG + clk->rcg; + cmd_reg = QCA8084_CLK_BASE_REG + clk->rcg - 4; + + reg_val = ipq_mii_read(rcg_reg); + cur_cfg = (reg_val & RCGR_SRC_SEL) >> RCGR_SRC_SEL_SHIFT; + cfg = pdata->cfg; + + if (cfg == cur_cfg) { + pr_debug("CLK %s parent %d is already configured correctly\n", clock_id, parent); + return; + } + + /* update clock parent */ + reg_val &= ~RCGR_SRC_SEL; + reg_val |= cfg << RCGR_SRC_SEL_SHIFT; + ipq_mii_write(rcg_reg, reg_val); + + /* update RCG to the new programmed configuration */ + qca8084_clk_update(cmd_reg); +} + +void qca8084_uniphy_raw_clock_set(qca8084_clk_parent_t uniphy_clk, uint64_t rate) +{ + switch (uniphy_clk) { + case QCA8084_P_UNIPHY0_RX: + case QCA8084_P_UNIPHY0_TX: + case QCA8084_P_UNIPHY1_RX: + case QCA8084_P_UNIPHY1_TX: + break; + default: + pr_debug("Invalid uniphy_clk %d\n", uniphy_clk); + return; + } + + qca8084_uniphy_raw_clock[uniphy_clk - QCA8084_P_UNIPHY0_RX] = rate; + return; +} + +uint64_t qca8084_uniphy_raw_clock_get(qca8084_clk_parent_t uniphy_clk) +{ + switch (uniphy_clk) { + case QCA8084_P_UNIPHY0_RX: + case QCA8084_P_UNIPHY0_TX: + case QCA8084_P_UNIPHY1_RX: + case QCA8084_P_UNIPHY1_TX: + break; + default: + pr_debug("Invalid uniphy_clk %d\n", uniphy_clk); + return QCA8084_XO_CLK_RATE_50M; + } + + return qca8084_uniphy_raw_clock[uniphy_clk - QCA8084_P_UNIPHY0_RX]; +} + +void qca8084_clk_rate_set(const char *clock_id, uint32_t rate) +{ + struct clk_lookup *clk; + uint64_t div, prate = 0; + uint32_t i, reg_val, parent_index = 0; + uint32_t rcg_reg = 0, cmd_reg = 0, cdiv_reg = 0, cdiv_val = 0; + const struct qca8084_parent_data *pdata = NULL; + + clk = qca8084_clk_find(clock_id); + if (!clk) { + pr_debug("CLK %s is not found!\n", clock_id); + return; + } + + for (i = 0; i < clk->num_rate; i++) + if (rate == clk->support_rate[i]) + break; + + if (i == clk->num_rate) { + pr_debug("CLK %s does not support to configure rate %d\n", clock_id, rate); + return; + } + + rcg_reg = QCA8084_CLK_BASE_REG + clk->rcg; + cmd_reg = QCA8084_CLK_BASE_REG + clk->rcg - 4; + if (clk->cdiv != 0) + cdiv_reg = QCA8084_CLK_BASE_REG + clk->cdiv; + + reg_val = ipq_mii_read(rcg_reg); + + /* get the parent rate of clock */ + parent_index = (reg_val & RCGR_SRC_SEL) >> RCGR_SRC_SEL_SHIFT; + for (i = 0; i < clk->num_parent; i++) { + pdata = &(clk->pdata[i]); + if (pdata->cfg == parent_index) { + /* uniphy0 rx, tx and unphy1 rx, tx clock can be 125M or 312.5M, which + * depends on the current link speed, the clock rate needs to be acquired + * dynamically. + */ + switch (pdata->parent) { + case QCA8084_P_UNIPHY0_RX: + case QCA8084_P_UNIPHY0_TX: + case QCA8084_P_UNIPHY1_RX: + case QCA8084_P_UNIPHY1_TX: + prate = qca8084_uniphy_raw_clock_get(pdata->parent); + break; + default: + /* XO 50M or 315P5M fix clock rate */ + prate = pdata->prate; + break; + } + /* find the parent clock rate */ + break; + } + } + + if (i == clk->num_parent || prate == 0) { + pr_debug("CLK %s is configured as unsupported parent value %d\n", + clock_id, parent_index); + return; + } + + /* when configuring XPSC clock to UQXGMII_XPCS_SPEED_2500M_CLK, the RCGR divider + * need to be bypassed, since there are two dividers from the same RCGR, one is + * for XPCS clock, the other is for EPHY port clock. + */ + if (rate == UQXGMII_XPCS_SPEED_2500M_CLK) { + if (prate != UQXGMII_SPEED_2500M_CLK) { + pr_debug("CLK %s parent(%lld) needs to be updated to %d\n", + clock_id, prate, UQXGMII_SPEED_2500M_CLK); + return; + } + div = RCGR_DIV_BYPASS; + cdiv_val = (UQXGMII_SPEED_2500M_CLK / UQXGMII_XPCS_SPEED_2500M_CLK) - 1; + } else { + + /* calculate the RCGR divider prate/rate = (rcg_divider + 1)/2 */ + div = prate * 2; + do_div(div, rate); + div--; + + /* if the RCG divider can't meet the requirement, the CDIV reg can be simply + * divided by 10 to satisfy the required clock rate. + */ + if (div > RCGR_DIV_MAX) { + /* update CDIV Reg to be divided by 10(N+1) */ + cdiv_val = CDIVR_DIVIDER_10; + + /* caculate the new RCG divider */ + do_div(prate, CDIVR_DIVIDER_10 + 1); + div = prate * 2; + do_div(div, rate); + div--; + } + } + + /* update CDIV Reg to be divided by N(N-1 for reg value) */ + if (cdiv_reg != 0) + ipq_mii_update(cdiv_reg, + CDIVR_DIVIDER, cdiv_val << CDIVR_DIVIDER_SHIFT); + + if (cdiv_reg == 0 && cdiv_val > 0) { + pr_debug("CLK %s needs CDIVR to generate rate %d from prate %lld\n", + clock_id, rate, prate); + return; + } + + /* update RCGR */ + reg_val &= ~RCGR_HDIV; + reg_val |= div << RCGR_HDIV_SHIFT; + ipq_mii_write(rcg_reg, reg_val); + + /* update RCG to the new programmed configuration */ + qca8084_clk_update(cmd_reg); +} + +void qca8084_port5_uniphy0_clk_src_get(uint8_t *bypass_en) +{ + uint32_t reg_val = 0; + + /* In switch mode, uniphy0 rx clock is from mac5 rx, uniphy0 tx clock is from mac5 tx; + * In bypass mode, uniphy0 rx clock is from mac4 tx, uniphy0 tx clock is from mac4 rx; + */ + reg_val = ipq_mii_read(QCA8084_CLK_BASE_REG + QCA8084_CLK_MUX_SEL); + *bypass_en = (reg_val & QCA8084_UNIPHY0_SEL_MAC5) ? 0 : 1; + + return; +} + +int qca8084_clk_rate_get(const char *clock_id, + struct qca8084_clk_data *clk_data) +{ + struct clk_lookup *clk; + uint64_t div, prate = 0; + uint32_t i, reg_val, parent_index = 0; + const struct qca8084_parent_data *pdata = NULL; + char clk_id[64] = {0}; + uint8_t bypass_en = 0; + + strlcpy(clk_id, clock_id, sizeof(clk_id)); + + qca8084_port5_uniphy0_clk_src_get(&bypass_en); + if (bypass_en == 1) { + if (strncasecmp(clock_id, QCA8084_MAC5_TX_UNIPHY0_CLK, + strlen(QCA8084_MAC5_TX_UNIPHY0_CLK)) == 0) + strlcpy(clk_id, QCA8084_MAC4_RX_CLK, sizeof(clk_id)); + else if (strncasecmp(clock_id, QCA8084_MAC5_RX_UNIPHY0_CLK, + strlen(QCA8084_MAC5_RX_UNIPHY0_CLK)) == 0) + strlcpy(clk_id, QCA8084_MAC4_TX_CLK, sizeof(clk_id)); + } + + clk = qca8084_clk_find(clk_id); + if (!clk) { + pr_debug("CLK %s is not found!\n", clk_id); + return -1; + } + + reg_val = ipq_mii_read(QCA8084_CLK_BASE_REG + clk->rcg); + + /* get the parent rate of clock */ + parent_index = (reg_val & RCGR_SRC_SEL) >> RCGR_SRC_SEL_SHIFT; + for (i = 0; i < clk->num_parent; i++) { + pdata = &(clk->pdata[i]); + if (pdata->cfg == parent_index) { + /* uniphy0 rx, tx and unphy1 rx, tx clock can be 125M or 312.5M, which + * depends on the current link speed, the clock rate needs to be acquired + * dynamically. + */ + switch (pdata->parent) { + case QCA8084_P_UNIPHY0_RX: + case QCA8084_P_UNIPHY0_TX: + case QCA8084_P_UNIPHY1_RX: + case QCA8084_P_UNIPHY1_TX: + prate = qca8084_uniphy_raw_clock_get(pdata->parent); + break; + default: + /* XO 50M or 315P5M fix clock rate */ + prate = pdata->prate; + break; + } + /* find the parent clock rate */ + break; + } + } + + if (i == clk->num_parent || prate == 0) { + pr_debug("CLK %s is configured as unsupported parent value %d\n", + clk_id, parent_index); + return -1; + } + + /* calculate the current clock rate */ + div = (reg_val >> RCGR_HDIV_SHIFT) & RCGR_HDIV; + if (div != 0) { + /* RCG divider is bypassed if the div value is 0 */ + prate *= 2; + do_div(prate, div + 1); + } + + clk_data->rcg_val = reg_val; + + reg_val = ipq_mii_read(QCA8084_CLK_BASE_REG + clk->cbc); + clk_data->cbc_val = reg_val; + + if (clk->cdiv != 0) { + reg_val = ipq_mii_read(QCA8084_CLK_BASE_REG + clk->cdiv); + clk_data->cdiv_val = reg_val; + do_div(prate, ((reg_val >> CDIVR_DIVIDER_SHIFT) & CDIVR_DIVIDER) + 1); + } + + clk_data->rate = prate; + + return 0; +} + +void qca8084_clk_dump(void) +{ + uint32_t i; + struct clk_lookup *clk; + struct qca8084_clk_data clk_data; + int ret; + + pr_debug("%-31s Frequency RCG_VAL CDIV_VAL CBC_VAL\n", "Clock Name"); + + for (i = 0; i < ARRAY_SIZE(qca8084_clk_lookup_table); i++) { + clk = &qca8084_clk_lookup_table[i]; + if (clk->rcg != 0) { + ret = qca8084_clk_rate_get(clk->clk_name, &clk_data); + if (ret != 0) + continue; + pr_debug("%-31s %-9ld 0x%-5x 0x%-6x 0x%-5x\n", + clk->clk_name + 8, clk_data.rate, + clk_data.rcg_val, clk_data.cdiv_val, clk_data.cbc_val); + } + } +} + +static int do_qca8084_clk_dump(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + qca8084_clk_dump(); + + return 0; +} + +U_BOOT_CMD( + qca8084_clk_dump, 1, 1, do_qca8084_clk_dump, + "QCA8084 utility command to dump clocks\n", + "qca8084_clk_dump - dump all the qca8084 clocks\n" + "This command can be used to check if clocks are all as expected\n" +); + +void qca8084_port5_uniphy0_clk_src_set(uint8_t bypass_en) +{ + uint32_t mux_sel = 0; + + /* In switch mode, uniphy0 rx clock is from mac5 rx, uniphy0 tx clock is from mac5 tx; + * In bypass mode, uniphy0 rx clock is from mac4 tx, uniphy0 tx clock is from mac4 rx; + */ + + if (bypass_en) + mux_sel = QCA8084_UNIPHY0_SEL_MAC4; + else + mux_sel = QCA8084_UNIPHY0_SEL_MAC5; + + ipq_mii_update(QCA8084_CLK_BASE_REG + QCA8084_CLK_MUX_SEL, + QCA8084_UNIPHY0_MUX_SEL_MASK, mux_sel); + return; +} + +void qca8084_port_clk_rate_set(uint32_t qca8084_port_id, uint32_t rate) +{ + char *mac_rx_clk = NULL, *mac_tx_clk = NULL; + char *xgmii_tx_clk = NULL, *xgmii_rx_clk = NULL; + + switch (qca8084_port_id) { + case PORT0: + mac_rx_clk = QCA8084_MAC0_RX_CLK; + mac_tx_clk = QCA8084_MAC0_TX_CLK; + break; + case PORT1: + mac_rx_clk = QCA8084_MAC1_RX_CLK; + mac_tx_clk = QCA8084_MAC1_TX_CLK; + xgmii_rx_clk = QCA8084_MAC1_UNIPHY1_CH0_XGMII_RX_CLK; + xgmii_tx_clk = QCA8084_MAC1_UNIPHY1_CH0_XGMII_TX_CLK; + break; + case PORT2: + mac_rx_clk = QCA8084_MAC2_RX_CLK; + mac_tx_clk = QCA8084_MAC2_TX_CLK; + xgmii_rx_clk = QCA8084_MAC2_UNIPHY1_CH1_XGMII_RX_CLK; + xgmii_tx_clk = QCA8084_MAC2_UNIPHY1_CH1_XGMII_TX_CLK; + break; + case PORT3: + mac_rx_clk = QCA8084_MAC3_RX_CLK; + mac_tx_clk = QCA8084_MAC3_TX_CLK; + xgmii_rx_clk = QCA8084_MAC3_UNIPHY1_CH2_XGMII_RX_CLK; + xgmii_tx_clk = QCA8084_MAC3_UNIPHY1_CH2_XGMII_TX_CLK; + break; + case PORT4: + mac_rx_clk = QCA8084_MAC4_RX_CLK; + mac_tx_clk = QCA8084_MAC4_TX_CLK; + xgmii_rx_clk = QCA8084_MAC4_UNIPHY1_CH3_XGMII_RX_CLK; + xgmii_tx_clk = QCA8084_MAC4_UNIPHY1_CH3_XGMII_TX_CLK; + break; + case PORT5: + mac_rx_clk = QCA8084_MAC5_RX_CLK; + mac_tx_clk = QCA8084_MAC5_TX_CLK; + break; + default: + pr_debug("Unsupported qca8084_port_id %d\n", qca8084_port_id); + return; + } + + qca8084_clk_rate_set(mac_rx_clk, rate); + qca8084_clk_rate_set(mac_tx_clk, rate); + + if (xgmii_rx_clk != NULL && xgmii_tx_clk != NULL) { + /* XGMII take the different clock rate from MAC clock when the link + * speed is 2.5G. + */ + if (rate == UQXGMII_SPEED_2500M_CLK) + rate = UQXGMII_XPCS_SPEED_2500M_CLK; + qca8084_clk_rate_set(xgmii_rx_clk, rate); + qca8084_clk_rate_set(xgmii_tx_clk, rate); + } + + return; +} + +static inline void qca8084_clk_ids_get(uint32_t qca8084_port_id, + uint8_t mask, char **clk_ids) +{ + switch (qca8084_port_id) { + case PORT0: + if (mask & QCA8084_CLK_TYPE_MAC) { + *clk_ids++ = QCA8084_MAC0_TX_CLK; + *clk_ids++ = QCA8084_MAC0_RX_CLK; + } + + if (mask & QCA8084_CLK_TYPE_UNIPHY) { + *clk_ids++ = QCA8084_MAC0_TX_UNIPHY1_CLK; + *clk_ids++ = QCA8084_MAC0_RX_UNIPHY1_CLK; + } + break; + case PORT1: + if (mask & QCA8084_CLK_TYPE_MAC) { + *clk_ids++ = QCA8084_MAC1_TX_CLK; + *clk_ids++ = QCA8084_MAC1_RX_CLK; + } + + if (mask & QCA8084_CLK_TYPE_UNIPHY) { + *clk_ids++ = QCA8084_MAC1_UNIPHY1_CH0_RX_CLK; + *clk_ids++ = QCA8084_MAC1_UNIPHY1_CH0_TX_CLK; + *clk_ids++ = QCA8084_MAC1_UNIPHY1_CH0_XGMII_RX_CLK; + *clk_ids++ = QCA8084_MAC1_UNIPHY1_CH0_XGMII_TX_CLK; + } + + if (mask & QCA8084_CLK_TYPE_EPHY) { + *clk_ids++ = QCA8084_MAC1_GEPHY0_TX_CLK; + *clk_ids++ = QCA8084_MAC1_GEPHY0_RX_CLK; + } + break; + case PORT2: + if (mask & QCA8084_CLK_TYPE_MAC) { + *clk_ids++ = QCA8084_MAC2_TX_CLK; + *clk_ids++ = QCA8084_MAC2_RX_CLK; + } + + if (mask & QCA8084_CLK_TYPE_UNIPHY) { + *clk_ids++ = QCA8084_MAC2_UNIPHY1_CH1_RX_CLK; + *clk_ids++ = QCA8084_MAC2_UNIPHY1_CH1_TX_CLK; + *clk_ids++ = QCA8084_MAC2_UNIPHY1_CH1_XGMII_RX_CLK; + *clk_ids++ = QCA8084_MAC2_UNIPHY1_CH1_XGMII_TX_CLK; + } + + if (mask & QCA8084_CLK_TYPE_EPHY) { + *clk_ids++ = QCA8084_MAC2_GEPHY1_TX_CLK; + *clk_ids++ = QCA8084_MAC2_GEPHY1_RX_CLK; + } + break; + case PORT3: + if (mask & QCA8084_CLK_TYPE_MAC) { + *clk_ids++ = QCA8084_MAC3_TX_CLK; + *clk_ids++ = QCA8084_MAC3_RX_CLK; + } + + if (mask & QCA8084_CLK_TYPE_UNIPHY) { + *clk_ids++ = QCA8084_MAC3_UNIPHY1_CH2_RX_CLK; + *clk_ids++ = QCA8084_MAC3_UNIPHY1_CH2_TX_CLK; + *clk_ids++ = QCA8084_MAC3_UNIPHY1_CH2_XGMII_RX_CLK; + *clk_ids++ = QCA8084_MAC3_UNIPHY1_CH2_XGMII_TX_CLK; + } + + if (mask & QCA8084_CLK_TYPE_EPHY) { + *clk_ids++ = QCA8084_MAC3_GEPHY2_TX_CLK; + *clk_ids++ = QCA8084_MAC3_GEPHY2_RX_CLK; + } + break; + case PORT4: + if (mask & QCA8084_CLK_TYPE_MAC) { + *clk_ids++ = QCA8084_MAC4_TX_CLK; + *clk_ids++ = QCA8084_MAC4_RX_CLK; + } + + if (mask & QCA8084_CLK_TYPE_UNIPHY) { + *clk_ids++ = QCA8084_MAC4_UNIPHY1_CH3_RX_CLK; + *clk_ids++ = QCA8084_MAC4_UNIPHY1_CH3_TX_CLK; + *clk_ids++ = QCA8084_MAC4_UNIPHY1_CH3_XGMII_RX_CLK; + *clk_ids++ = QCA8084_MAC4_UNIPHY1_CH3_XGMII_TX_CLK; + } + + if (mask & QCA8084_CLK_TYPE_EPHY) { + *clk_ids++ = QCA8084_MAC4_GEPHY3_TX_CLK; + *clk_ids++ = QCA8084_MAC4_GEPHY3_RX_CLK; + } + break; + case PORT5: + if (mask & QCA8084_CLK_TYPE_MAC) { + *clk_ids++ = QCA8084_MAC5_TX_CLK; + *clk_ids++ = QCA8084_MAC5_RX_CLK; + } + + if (mask & QCA8084_CLK_TYPE_UNIPHY) { + *clk_ids++ = QCA8084_MAC5_TX_UNIPHY0_CLK; + *clk_ids++ = QCA8084_MAC5_RX_UNIPHY0_CLK; + } + break; + default: + pr_debug("Unsupported qca8084_port_id %d\n", qca8084_port_id); + return; + } + + return; +} + +void qca8084_port_clk_reset(uint32_t qca8084_port_id, uint8_t mask) +{ + char *clk_ids[QCA8084_PORT_CLK_CBC_MAX + 1] = {NULL}; + uint32_t i = 0; + + qca8084_clk_ids_get(qca8084_port_id, mask, clk_ids); + + while(clk_ids[i] != NULL) { + qca8084_clk_reset(clk_ids[i]); + i++; + } + + return; +} + +void qca8084_port_clk_en_set(uint32_t qca8084_port_id, uint8_t mask, + uint8_t enable) +{ + char *clk_ids[QCA8084_PORT_CLK_CBC_MAX + 1] = {NULL}; + uint32_t i = 0; + + qca8084_clk_ids_get(qca8084_port_id, mask, clk_ids); + + while(clk_ids[i] != NULL) { + if (enable) + qca8084_clk_enable(clk_ids[i]); + else + qca8084_clk_disable(clk_ids[i]); + i++; + } + + return; +} + +void qca8084_gcc_common_clk_parent_enable(void) +{ + /* Switch core */ + qca8084_clk_parent_set(QCA8084_SWITCH_CORE_CLK, QCA8084_P_UNIPHY1_TX312P5M); + qca8084_clk_rate_set(QCA8084_SWITCH_CORE_CLK, UQXGMII_SPEED_2500M_CLK); + + /* Disable switch core clock to save power in phy mode */ + qca8084_clk_disable(QCA8084_SWITCH_CORE_CLK); + + qca8084_clk_enable(QCA8084_APB_BRIDGE_CLK); + + /* AHB bridge */ + qca8084_clk_parent_set(QCA8084_AHB_CLK, QCA8084_P_UNIPHY1_TX312P5M); + qca8084_clk_rate_set(QCA8084_AHB_CLK, QCA8084_AHB_CLK_RATE_104P17M); + qca8084_clk_enable(QCA8084_AHB_CLK); + qca8084_clk_enable(QCA8084_SEC_CTRL_AHB_CLK); + qca8084_clk_enable(QCA8084_TLMM_CLK); + qca8084_clk_enable(QCA8084_TLMM_AHB_CLK); + qca8084_clk_enable(QCA8084_CNOC_AHB_CLK); + qca8084_clk_enable(QCA8084_MDIO_AHB_CLK); + qca8084_clk_enable(QCA8084_MDIO_MASTER_AHB_CLK); + + /* System */ + qca8084_clk_parent_set(QCA8084_SRDS0_SYS_CLK, QCA8084_P_XO); + qca8084_clk_rate_set(QCA8084_SRDS0_SYS_CLK, QCA8084_SYS_CLK_RATE_25M); + + /* Disable serdes0 clock to save power in phy mode */ + qca8084_clk_disable(QCA8084_SRDS0_SYS_CLK); + + qca8084_clk_enable(QCA8084_SRDS1_SYS_CLK); + qca8084_clk_enable(QCA8084_GEPHY0_SYS_CLK); + qca8084_clk_enable(QCA8084_GEPHY1_SYS_CLK); + qca8084_clk_enable(QCA8084_GEPHY2_SYS_CLK); + qca8084_clk_enable(QCA8084_GEPHY3_SYS_CLK); + + /* Sec control */ + qca8084_clk_parent_set(QCA8084_SEC_CTRL_CLK, QCA8084_P_XO); + qca8084_clk_rate_set(QCA8084_SEC_CTRL_CLK, QCA8084_SYS_CLK_RATE_25M); + qca8084_clk_enable(QCA8084_SEC_CTRL_CLK); + qca8084_clk_enable(QCA8084_SEC_CTRL_SENSE_CLK); +} + +void qca8084_gcc_port_clk_parent_set(uint32_t qca8084_port_id) +{ + qca8084_clk_parent_t port_tx_parent, port_rx_parent; + char *tx_clk_id, *rx_clk_id; + + port_tx_parent = QCA8084_P_UNIPHY1_RX312P5M; + port_rx_parent = QCA8084_P_UNIPHY1_TX312P5M; + + switch (qca8084_port_id) { + case PORT0: + port_tx_parent = QCA8084_P_UNIPHY1_TX; + port_rx_parent = QCA8084_P_UNIPHY1_RX; + tx_clk_id = QCA8084_MAC0_TX_CLK; + rx_clk_id = QCA8084_MAC0_RX_CLK; + break; + case PORT1: + tx_clk_id = QCA8084_MAC1_TX_CLK; + rx_clk_id = QCA8084_MAC1_RX_CLK; + break; + case PORT2: + tx_clk_id = QCA8084_MAC2_TX_CLK; + rx_clk_id = QCA8084_MAC2_RX_CLK; + break; + case PORT3: + tx_clk_id = QCA8084_MAC3_TX_CLK; + rx_clk_id = QCA8084_MAC3_RX_CLK; + break; + case PORT4: + port_tx_parent = QCA8084_P_UNIPHY1_RX312P5M; + port_rx_parent = QCA8084_P_UNIPHY1_TX312P5M; + tx_clk_id = QCA8084_MAC4_TX_CLK; + rx_clk_id = QCA8084_MAC4_RX_CLK; + break; + case PORT5: + port_tx_parent = QCA8084_P_UNIPHY0_TX; + port_rx_parent = QCA8084_P_UNIPHY0_RX; + tx_clk_id = QCA8084_MAC5_TX_CLK; + rx_clk_id = QCA8084_MAC5_RX_CLK; + qca8084_port5_uniphy0_clk_src_set(0); + break; + default: + pr_debug("Unsupported qca8084_port_id %d\n", qca8084_port_id); + return; + } + + qca8084_clk_parent_set(tx_clk_id, port_tx_parent); + qca8084_clk_parent_set(rx_clk_id, port_rx_parent); +} + +void qca8084_gcc_clock_init(void) +{ + uint32_t qca8084_port_id = 0; + /* clock type mask value for 6 manhattan ports */ + uint8_t clk_mask[PORT5 + 1] = {0}; + static uint8_t gcc_common_clk_init = 0; + uint8_t switch_flag = 0; + qca8084_clk_parent_t uniphy_index = QCA8084_P_UNIPHY0_RX; + + clk_mask[PORT1] = QCA8084_CLK_TYPE_UNIPHY | QCA8084_CLK_TYPE_EPHY; + clk_mask[PORT2] = QCA8084_CLK_TYPE_UNIPHY | QCA8084_CLK_TYPE_EPHY; + clk_mask[PORT3] = QCA8084_CLK_TYPE_UNIPHY | QCA8084_CLK_TYPE_EPHY; + clk_mask[PORT4] = QCA8084_CLK_TYPE_UNIPHY | QCA8084_CLK_TYPE_EPHY; + + if (!gcc_common_clk_init) { + qca8084_gcc_common_clk_parent_enable(); + gcc_common_clk_init = 1; + + /* Initialize the uniphy raw clock, if the port4 is in bypass mode, the uniphy0 + * raw clock need to be dynamically updated between UQXGMII_SPEED_2500M_CLK and + * UQXGMII_SPEED_1000M_CLK according to the realtime link speed. + */ + uniphy_index = QCA8084_P_UNIPHY0_RX; + while (uniphy_index <= QCA8084_P_UNIPHY1_TX) { + /* the uniphy raw clock may be already initialized. */ + if (0 == qca8084_uniphy_raw_clock_get(uniphy_index)) + qca8084_uniphy_raw_clock_set(uniphy_index, + UQXGMII_SPEED_2500M_CLK); + uniphy_index++; + } + } + + qca8084_port_id = 0; + while (qca8084_port_id < ARRAY_SIZE(clk_mask)) { + if (clk_mask[qca8084_port_id] != 0) { + qca8084_gcc_port_clk_parent_set(qca8084_port_id); + if (clk_mask[qca8084_port_id] & QCA8084_CLK_TYPE_MAC) + qca8084_port_clk_en_set(qca8084_port_id, QCA8084_CLK_TYPE_MAC, 1); + if (clk_mask[qca8084_port_id] & QCA8084_CLK_TYPE_UNIPHY && switch_flag == 1) + qca8084_port_clk_en_set(qca8084_port_id, QCA8084_CLK_TYPE_UNIPHY, 1); + } + qca8084_port_id++; + } +} diff --git a/drivers/net/ipq_common/ipq_qca8084_clk.h b/drivers/net/ipq_common/ipq_qca8084_clk.h new file mode 100644 index 0000000000..9ab4cb6051 --- /dev/null +++ b/drivers/net/ipq_common/ipq_qca8084_clk.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2022, 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. +*/ +#ifndef _QCA8084_CLK_H_ +#define _QCA8084_CLK_H_ + +#define QCA8084_SWITCH_CORE_CLK "qca8084_gcc_switch_core_clk" +#define QCA8084_APB_BRIDGE_CLK "qca8084_gcc_apb_bridge_clk" + +#define QCA8084_MAC0_TX_CLK "qca8084_gcc_mac0_tx_clk" +#define QCA8084_MAC0_TX_UNIPHY1_CLK "qca8084_gcc_mac0_tx_srds1_clk" + +#define QCA8084_MAC0_RX_CLK "qca8084_gcc_mac0_rx_clk" +#define QCA8084_MAC0_RX_UNIPHY1_CLK "qca8084_gcc_mac0_rx_srds1_clk" + +#define QCA8084_MAC1_TX_CLK "qca8084_gcc_mac1_tx_clk" +#define QCA8084_MAC1_GEPHY0_TX_CLK "qca8084_gcc_mac1_gephy0_tx_clk" +#define QCA8084_MAC1_UNIPHY1_CH0_RX_CLK "qca8084_gcc_mac1_srds1_ch0_rx_clk" +#define QCA8084_MAC1_UNIPHY1_CH0_XGMII_RX_CLK "qca8084_gcc_mac1_srds1_ch0_xgmii_rx_clk" + +#define QCA8084_MAC1_RX_CLK "qca8084_gcc_mac1_rx_clk" +#define QCA8084_MAC1_GEPHY0_RX_CLK "qca8084_gcc_mac1_gephy0_rx_clk" +#define QCA8084_MAC1_UNIPHY1_CH0_TX_CLK "qca8084_gcc_mac1_srds1_ch0_tx_clk" +#define QCA8084_MAC1_UNIPHY1_CH0_XGMII_TX_CLK "qca8084_gcc_mac1_srds1_ch0_xgmii_tx_clk" + +#define QCA8084_MAC2_TX_CLK "qca8084_gcc_mac2_tx_clk" +#define QCA8084_MAC2_GEPHY1_TX_CLK "qca8084_gcc_mac2_gephy1_tx_clk" +#define QCA8084_MAC2_UNIPHY1_CH1_RX_CLK "qca8084_gcc_mac2_srds1_ch1_rx_clk" +#define QCA8084_MAC2_UNIPHY1_CH1_XGMII_RX_CLK "qca8084_gcc_mac2_srds1_ch1_xgmii_rx_clk" + +#define QCA8084_MAC2_RX_CLK "qca8084_gcc_mac2_rx_clk" +#define QCA8084_MAC2_GEPHY1_RX_CLK "qca8084_gcc_mac2_gephy1_rx_clk" +#define QCA8084_MAC2_UNIPHY1_CH1_TX_CLK "qca8084_gcc_mac2_srds1_ch1_tx_clk" +#define QCA8084_MAC2_UNIPHY1_CH1_XGMII_TX_CLK "qca8084_gcc_mac2_srds1_ch1_xgmii_tx_clk" + +#define QCA8084_MAC3_TX_CLK "qca8084_gcc_mac3_tx_clk" +#define QCA8084_MAC3_GEPHY2_TX_CLK "qca8084_gcc_mac3_gephy2_tx_clk" +#define QCA8084_MAC3_UNIPHY1_CH2_RX_CLK "qca8084_gcc_mac3_srds1_ch2_rx_clk" +#define QCA8084_MAC3_UNIPHY1_CH2_XGMII_RX_CLK "qca8084_gcc_mac3_srds1_ch2_xgmii_rx_clk" + +#define QCA8084_MAC3_RX_CLK "qca8084_gcc_mac3_rx_clk" +#define QCA8084_MAC3_GEPHY2_RX_CLK "qca8084_gcc_mac3_gephy2_rx_clk" +#define QCA8084_MAC3_UNIPHY1_CH2_TX_CLK "qca8084_gcc_mac3_srds1_ch2_tx_clk" +#define QCA8084_MAC3_UNIPHY1_CH2_XGMII_TX_CLK "qca8084_gcc_mac3_srds1_ch2_xgmii_tx_clk" + +#define QCA8084_MAC4_TX_CLK "qca8084_gcc_mac4_tx_clk" +#define QCA8084_MAC4_GEPHY3_TX_CLK "qca8084_gcc_mac4_gephy3_tx_clk" +#define QCA8084_MAC4_UNIPHY1_CH3_RX_CLK "qca8084_gcc_mac4_srds1_ch3_rx_clk" +#define QCA8084_MAC4_UNIPHY1_CH3_XGMII_RX_CLK "qca8084_gcc_mac4_srds1_ch3_xgmii_rx_clk" + +#define QCA8084_MAC4_RX_CLK "qca8084_gcc_mac4_rx_clk" +#define QCA8084_MAC4_GEPHY3_RX_CLK "qca8084_gcc_mac4_gephy3_rx_clk" +#define QCA8084_MAC4_UNIPHY1_CH3_TX_CLK "qca8084_gcc_mac4_srds1_ch3_tx_clk" +#define QCA8084_MAC4_UNIPHY1_CH3_XGMII_TX_CLK "qca8084_gcc_mac4_srds1_ch3_xgmii_tx_clk" + +#define QCA8084_MAC5_TX_CLK "qca8084_gcc_mac5_tx_clk" +#define QCA8084_MAC5_TX_UNIPHY0_CLK "qca8084_gcc_mac5_tx_srds0_clk" +#define QCA8084_MAC5_TX_SRDS0_CLK_SRC "qca8084_gcc_mac5_tx_srds0_clk_src" + +#define QCA8084_MAC5_RX_CLK "qca8084_gcc_mac5_rx_clk" +#define QCA8084_MAC5_RX_UNIPHY0_CLK "qca8084_gcc_mac5_rx_srds0_clk" +#define QCA8084_MAC5_RX_SRDS0_CLK_SRC "qca8084_gcc_mac5_rx_srds0_clk_src" + +#define QCA8084_SEC_CTRL_CLK "qca8084_gcc_sec_ctrl_clk" +#define QCA8084_SEC_CTRL_SENSE_CLK "qca8084_gcc_sec_ctrl_sense_clk" + +#define QCA8084_SRDS0_SYS_CLK "qca8084_gcc_srds0_sys_clk" +#define QCA8084_SRDS1_SYS_CLK "qca8084_gcc_srds1_sys_clk" +#define QCA8084_GEPHY0_SYS_CLK "qca8084_gcc_gephy0_sys_clk" +#define QCA8084_GEPHY1_SYS_CLK "qca8084_gcc_gephy1_sys_clk" +#define QCA8084_GEPHY2_SYS_CLK "qca8084_gcc_gephy2_sys_clk" +#define QCA8084_GEPHY3_SYS_CLK "qca8084_gcc_gephy3_sys_clk" + +#define QCA8084_AHB_CLK "qca8084_gcc_ahb_clk" +#define QCA8084_SEC_CTRL_AHB_CLK "qca8084_gcc_sec_ctrl_ahb_clk" +#define QCA8084_TLMM_CLK "qca8084_gcc_tlmm_clk" +#define QCA8084_TLMM_AHB_CLK "qca8084_gcc_tlmm_ahb_clk" +#define QCA8084_CNOC_AHB_CLK "qca8084_gcc_cnoc_ahb_clk" +#define QCA8084_MDIO_AHB_CLK "qca8084_gcc_mdio_ahb_clk" +#define QCA8084_MDIO_MASTER_AHB_CLK "qca8084_gcc_mdio_master_ahb_clk" + +#define QCA8084_GLOBAL_RST "qca8084_gcc_global_rst" +#define QCA8084_UNIPHY_XPCS_RST "qca8084_uniphy_xpcs_rst" +#define QCA8084_GEPHY_DSP_HW_RST "qca8084_gephy_dsp_hw_rst" +#define QCA8084_GEPHY_P3_MDC_SW_RST "qca8084_gephy_p3_mdc_sw_rst" +#define QCA8084_GEPHY_P2_MDC_SW_RST "qca8084_gephy_p2_mdc_sw_rst" +#define QCA8084_GEPHY_P1_MDC_SW_RST "qca8084_gephy_p1_mdc_sw_rst" +#define QCA8084_GEPHY_P0_MDC_SW_RST "qca8084_gephy_p0_mdc_sw_rst" + +typedef enum { + QCA8084_P_XO, + QCA8084_P_UNIPHY0_RX, + QCA8084_P_UNIPHY0_TX, + QCA8084_P_UNIPHY1_RX, + QCA8084_P_UNIPHY1_TX, + QCA8084_P_UNIPHY1_RX312P5M, + QCA8084_P_UNIPHY1_TX312P5M, + QCA8084_P_MAX, +} qca8084_clk_parent_t; + +struct qca8084_clk_data { + unsigned long rate; + unsigned int rcg_val; + unsigned int cdiv_val; + unsigned int cbc_val; +}; + +struct qca8084_parent_data { + unsigned long prate; /* RCG input clock rate */ + qca8084_clk_parent_t parent; /* RCG parent clock id */ + int cfg; /* RCG clock src value */ +}; + +struct clk_lookup { + unsigned int rcg; + unsigned int cdiv; + unsigned int cbc; + unsigned int rst_bit; + const char *clk_name; + const unsigned long *support_rate; + unsigned int num_rate; + const struct qca8084_parent_data *pdata; + unsigned int num_parent; +}; + +#define CLK_LOOKUP(_rcg, _cdiv, _cbc, _rst_bit, _clk_name, \ + _rate, _num_rate, _pdata, _num_parent) \ +{ \ + .rcg = _rcg, \ + .cdiv = _cdiv, \ + .cbc = _cbc, \ + .rst_bit = _rst_bit, \ + .clk_name = _clk_name, \ + .support_rate = _rate, \ + .num_rate = _num_rate, \ + .pdata = _pdata, \ + .num_parent = _num_parent, \ +} + +#define QCA8084_CLK_TYPE_EPHY BIT(0) +#define QCA8084_CLK_TYPE_UNIPHY BIT(1) +#define QCA8084_CLK_TYPE_MAC BIT(2) + +#define UQXGMII_SPEED_2500M_CLK 312500000 +#define UQXGMII_SPEED_1000M_CLK 125000000 +#define UQXGMII_SPEED_100M_CLK 25000000 +#define UQXGMII_SPEED_10M_CLK 2500000 +#define UQXGMII_XPCS_SPEED_2500M_CLK 78125000 +#define QCA8084_AHB_CLK_RATE_104P17M 104160000 +#define QCA8084_SYS_CLK_RATE_25M 25000000 +#define QCA8084_XO_CLK_RATE_50M 50000000 + +#define QCA8084_CLK_BASE_REG 0x0c800000 +#define QCA8084_CLK_MUX_SEL 0x300 +#define QCA8084_UNIPHY0_MUX_SEL_MASK BITS_MASK(0, 2) +#define QCA8084_UNIPHY0_SEL_MAC5 0x3 +#define QCA8084_UNIPHY0_SEL_MAC4 0 + +#define RCGR_CMD_ROOT_OFF BIT(31) +#define RCGR_CMD_UPDATE BIT(0) +#define RCGR_SRC_SEL BITS_MASK(8, 3) +#define RCGR_SRC_SEL_SHIFT 8 +#define RCGR_HDIV BITS_MASK(0, 5) +#define RCGR_HDIV_SHIFT 0 +#define RCGR_DIV_BYPASS 0 +#define RCGR_DIV_MAX 0x1f +#define CDIVR_DIVIDER_10 9 /* CDIVR divided by N + 1 */ +#define CDIVR_DIVIDER BITS_MASK(0, 4) +#define CDIVR_DIVIDER_SHIFT 0 +#define CBCR_CLK_OFF BIT(31) +#define CBCR_CLK_RESET BIT(2) +#define CBCR_CLK_ENABLE BIT(0) + +#endif /* _QCA8084_CLK_H_ */ diff --git a/drivers/net/ipq_common/ipq_qca8084_interface_ctrl.c b/drivers/net/ipq_common/ipq_qca8084_interface_ctrl.c new file mode 100644 index 0000000000..6fbde125c8 --- /dev/null +++ b/drivers/net/ipq_common/ipq_qca8084_interface_ctrl.c @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2022, 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 "ipq_phy.h" +#include "ipq_qca8084.h" +#include "ipq_qca8084_clk.h" +#include "ipq_qca8084_interface_ctrl.h" +#include + +#ifdef DEBUG +#define pr_debug(fmt, args...) printf(fmt, ##args); +#else +#define pr_debug(fmt, args...) +#endif + +extern void qca8084_phy_reset(u32 phy_id); +extern u16 qca8084_phy_reg_read(u32 phy_id, u32 reg_id); +extern u16 qca8084_phy_reg_write(u32 phy_id, u32 reg_id, u16 value); +extern u16 qca8084_phy_mmd_read(u32 phy_id, u16 mmd_num, u16 reg_id); +extern u16 qca8084_phy_mmd_write(u32 phy_id, u16 mmd_num, u16 reg_id, + u16 value); +extern void qca8084_phy_modify_mmd(uint32_t phy_addr, uint32_t mmd_num, + uint32_t mmd_reg, uint32_t mask, uint32_t value); +extern void qca8084_phy_modify_mii(uint32_t phy_addr, uint32_t mii_reg, + uint32_t mask, uint32_t value); +extern uint32_t ipq_mii_read(uint32_t reg); +extern void ipq_mii_write(uint32_t reg, uint32_t val); +extern void ipq_mii_update(uint32_t reg, uint32_t mask, uint32_t val); +extern void qca8084_port_clk_rate_set(uint32_t qca8084_port_id, uint32_t rate); +extern void qca8084_port_clk_en_set(uint32_t qca8084_port_id, uint8_t mask, + uint8_t enable); +extern void qca8084_clk_assert(const char *clock_id); +extern void qca8084_clk_deassert(const char *clock_id); +extern void qca8084_port_clk_reset(uint32_t qca8084_port_id, uint8_t mask); + +void qca8084_serdes_addr_get(uint32_t serdes_id, uint32_t *address) +{ + uint32_t data = 0; + + data = ipq_mii_read(SERDES_CFG_OFFSET); + switch(serdes_id) + { + case QCA8084_UNIPHY_SGMII_0: + *address = (data >> SERDES_CFG_S0_ADDR_BOFFSET) & 0x1f; + break; + case QCA8084_UNIPHY_SGMII_1: + *address = (data >> SERDES_CFG_S1_ADDR_BOFFSET) & 0x1f; + break; + case QCA8084_UNIPHY_XPCS: + *address = (data >> SERDES_CFG_S1_XPCS_ADDR_BOFFSET) & 0x1f; + break; + default: + pr_debug("Serdes id not matching\n"); + break; + } +} + +void qca8084_ephy_addr_get(uint32_t qca8084_port_id, uint32_t *phy_addr) +{ + uint32_t data = 0; + + data = ipq_mii_read(EPHY_CFG_OFFSET); + switch(qca8084_port_id) + { + case PORT1: + *phy_addr = (data >> EPHY_CFG_EPHY0_ADDR_BOFFSET) & 0x1f; + break; + case PORT2: + *phy_addr = (data >> EPHY_CFG_EPHY1_ADDR_BOFFSET) & 0x1f; + break; + case PORT3: + *phy_addr = (data >> EPHY_CFG_EPHY2_ADDR_BOFFSET) & 0x1f; + break; + case PORT4: + *phy_addr = (data >> EPHY_CFG_EPHY3_ADDR_BOFFSET) & 0x1f; + break; + default: + pr_debug("qca8084_port_id not matching\n"); + break; + } +} + +static uint16_t qca8084_uniphy_xpcs_mmd_read(uint16_t mmd_num, uint16_t mmd_reg) +{ + uint32_t uniphy_xpcs_addr = 0; + + qca8084_serdes_addr_get(QCA8084_UNIPHY_XPCS, &uniphy_xpcs_addr); + + return qca8084_phy_mmd_read(uniphy_xpcs_addr, mmd_num, mmd_reg); +} + +static void qca8084_uniphy_xpcs_mmd_write(uint16_t mmd_num, uint16_t mmd_reg, + uint16_t reg_val) +{ + uint32_t uniphy_xpcs_addr = 0; +#ifdef DEBUG + uint16_t phy_data = 0; +#endif + + qca8084_serdes_addr_get(QCA8084_UNIPHY_XPCS, &uniphy_xpcs_addr); + + qca8084_phy_mmd_write(uniphy_xpcs_addr, mmd_num, mmd_reg, reg_val); + /*check the mmd register value*/ +#ifdef DEBUG + phy_data = +#endif + qca8084_uniphy_xpcs_mmd_read(mmd_num, mmd_reg); + + pr_debug("phy_addr:0x%x, mmd_num:0x%x, mmd_reg:0x%x, phy_data:0x%x\n", + uniphy_xpcs_addr, mmd_num, mmd_reg, phy_data); +} + +static void qca8084_uniphy_xpcs_modify_mmd(uint32_t mmd_num, uint32_t mmd_reg, + uint32_t mask, uint32_t value) +{ + uint16_t phy_data = 0, new_phy_data = 0; + + phy_data = qca8084_uniphy_xpcs_mmd_read(mmd_num, mmd_reg); + new_phy_data = (phy_data & ~mask) | value; + qca8084_uniphy_xpcs_mmd_write(mmd_num, mmd_reg, new_phy_data); +} + +uint8_t qca8084_uniphy_mode_check(uint32_t uniphy_index, + qca8084_uniphy_mode_t uniphy_mode) +{ + uint32_t uniphy_addr = 0; + uint16_t uniphy_mode_ctrl_data = 0; + + qca8084_serdes_addr_get(uniphy_index, &uniphy_addr); + + uniphy_mode_ctrl_data = qca8084_phy_mmd_read(uniphy_addr, + QCA8084_UNIPHY_MMD1, QCA8084_UNIPHY_MMD1_MODE_CTRL); + if(uniphy_mode_ctrl_data == PHY_INVALID_DATA) + return 0; + + if(!(uniphy_mode & uniphy_mode_ctrl_data)) + return 0; + + return 1; +} + +static uint32_t qca8084_uniphy_xpcs_port_to_mmd(uint32_t qca8084_port_id) +{ + uint32_t mmd_id = 0; + + switch(qca8084_port_id) + { + case PORT1: + mmd_id = QCA8084_UNIPHY_MMD31; + break; + case PORT2: + mmd_id = QCA8084_UNIPHY_MMD26; + break; + case PORT3: + mmd_id = QCA8084_UNIPHY_MMD27; + break; + case PORT4: + mmd_id = QCA8084_UNIPHY_MMD28; + break; + default: + pr_debug("Port not matching qca8084 ports\n"); + } + + return mmd_id; +} + +static void qca8084_uniphy_xpcs_modify_port_mmd(uint32_t qca8084_port_id, + uint32_t mmd_reg, uint32_t mask, + uint32_t value) +{ + uint32_t mmd_id = 0; + + mmd_id = qca8084_uniphy_xpcs_port_to_mmd(qca8084_port_id); + + qca8084_uniphy_xpcs_modify_mmd(mmd_id, mmd_reg, mask, value); +} + +void qca8084_port_speed_clock_set(uint32_t qca8084_port_id, + fal_port_speed_t speed) +{ + uint32_t clk_rate = 0; + + switch(speed) + { + case FAL_SPEED_2500: + clk_rate = UQXGMII_SPEED_2500M_CLK; + break; + case FAL_SPEED_1000: + clk_rate = UQXGMII_SPEED_1000M_CLK; + break; + case FAL_SPEED_100: + clk_rate = UQXGMII_SPEED_100M_CLK; + break; + case FAL_SPEED_10: + clk_rate = UQXGMII_SPEED_10M_CLK; + break; + default: + pr_debug("Unknown speed\n"); + return; + } + + qca8084_port_clk_rate_set(qca8084_port_id, clk_rate); +} + +static void qca8084_uniphy_xpcs_8023az_enable(void) +{ + uint16_t uniphy_data = 0; + + uniphy_data = qca8084_uniphy_xpcs_mmd_read(QCA8084_UNIPHY_MMD3, + QCA8084_UNIPHY_MMD3_AN_LP_BASE_ABL2); + if(!(uniphy_data & QCA8084_UNIPHY_MMD3_XPCS_EEE_CAP)) + return; + + /*Configure the EEE related timer*/ + qca8084_uniphy_xpcs_modify_mmd(QCA8084_UNIPHY_MMD3, + QCA8084_UNIPHY_MMD3_EEE_MODE_CTRL, + 0x0f40, QCA8084_UNIPHY_MMD3_EEE_RES_REGS | + QCA8084_UNIPHY_MMD3_EEE_SIGN_BIT_REGS); + + qca8084_uniphy_xpcs_modify_mmd(QCA8084_UNIPHY_MMD3, + QCA8084_UNIPHY_MMD3_EEE_TX_TIMER, + 0x1fff, QCA8084_UNIPHY_MMD3_EEE_TSL_REGS| + QCA8084_UNIPHY_MMD3_EEE_TLU_REGS | + QCA8084_UNIPHY_MMD3_EEE_TWL_REGS); + + qca8084_uniphy_xpcs_modify_mmd(QCA8084_UNIPHY_MMD3, + QCA8084_UNIPHY_MMD3_EEE_RX_TIMER, + 0x1fff, QCA8084_UNIPHY_MMD3_EEE_100US_REG_REGS| + QCA8084_UNIPHY_MMD3_EEE_RWR_REG_REGS); + + /*enable TRN_LPI*/ + qca8084_uniphy_xpcs_modify_mmd(QCA8084_UNIPHY_MMD3, + QCA8084_UNIPHY_MMD3_EEE_MODE_CTRL1, + 0x101, QCA8084_UNIPHY_MMD3_EEE_TRANS_LPI_MODE| + QCA8084_UNIPHY_MMD3_EEE_TRANS_RX_LPI_MODE); + + /*enable TX/RX LPI pattern*/ + qca8084_uniphy_xpcs_modify_mmd(QCA8084_UNIPHY_MMD3, + QCA8084_UNIPHY_MMD3_EEE_MODE_CTRL, + 0x3, QCA8084_UNIPHY_MMD3_EEE_EN); +} + +static void qca8084_uniphy_calibration(uint32_t uniphy_addr) +{ + uint16_t uniphy_data = 0; + uint32_t retries = 100, calibration_done = 0; + + /* wait calibration done to uniphy*/ + while (calibration_done != QCA8084_UNIPHY_MMD1_CALIBRATION_DONE) { + mdelay(1); + if (retries-- == 0) + pr_debug("uniphy callibration time out!\n"); + uniphy_data = qca8084_phy_mmd_read(uniphy_addr, QCA8084_UNIPHY_MMD1, + QCA8084_UNIPHY_MMD1_CALIBRATION4); + + calibration_done = (uniphy_data & QCA8084_UNIPHY_MMD1_CALIBRATION_DONE); + } +} + +static void qca8084_uniphy_xpcs_10g_r_linkup(void) +{ + uint16_t uniphy_data = 0; + uint32_t retries = 100, linkup = 0; + + /* wait 10G_R link up */ + while (linkup != QCA8084_UNIPHY_MMD3_10GBASE_R_UP) { + mdelay(1); + if (retries-- == 0) + pr_debug("10g_r link up timeout\n"); + uniphy_data = qca8084_uniphy_xpcs_mmd_read(QCA8084_UNIPHY_MMD3, + QCA8084_UNIPHY_MMD3_10GBASE_R_PCS_STATUS1); + + linkup = (uniphy_data & QCA8084_UNIPHY_MMD3_10GBASE_R_UP); + } +} + +static void qca8084_uniphy_xpcs_soft_reset(void) +{ + uint16_t uniphy_data = 0; + uint32_t retries = 100, reset_done = QCA8084_UNIPHY_MMD3_XPCS_SOFT_RESET; + + qca8084_uniphy_xpcs_modify_mmd(QCA8084_UNIPHY_MMD3, + QCA8084_UNIPHY_MMD3_DIG_CTRL1, 0x8000, + QCA8084_UNIPHY_MMD3_XPCS_SOFT_RESET); + + while (reset_done) { + mdelay(1); + if (retries-- == 0) + pr_debug("xpcs soft reset timeout\n"); + uniphy_data = qca8084_uniphy_xpcs_mmd_read(QCA8084_UNIPHY_MMD3, + QCA8084_UNIPHY_MMD3_DIG_CTRL1); + + reset_done = (uniphy_data & QCA8084_UNIPHY_MMD3_XPCS_SOFT_RESET); + } +} + +void qca8084_uniphy_xpcs_speed_set(uint32_t qca8084_port_id, + fal_port_speed_t speed) +{ + uint32_t xpcs_speed = 0; + + switch(speed) + { + case FAL_SPEED_2500: + xpcs_speed = QCA8084_UNIPHY_MMD_XPC_SPEED_2500; + break; + case FAL_SPEED_1000: + xpcs_speed = QCA8084_UNIPHY_MMD_XPC_SPEED_1000; + break; + case FAL_SPEED_100: + xpcs_speed = QCA8084_UNIPHY_MMD_XPC_SPEED_100; + break; + case FAL_SPEED_10: + xpcs_speed = QCA8084_UNIPHY_MMD_XPC_SPEED_10; + break; + default: + pr_debug("Unknown speed\n"); + return; + } + qca8084_uniphy_xpcs_modify_port_mmd(qca8084_port_id, + QCA8084_UNIPHY_MMD_MII_CTRL, + QCA8084_UNIPHY_MMD_XPC_SPEED_MASK, + xpcs_speed); +} + +void qca8084_uniphy_uqxgmii_function_reset(uint32_t qca8084_port_id) +{ + uint32_t uniphy_addr = 0; + + qca8084_serdes_addr_get(QCA8084_UNIPHY_SGMII_1, &uniphy_addr); + + qca8084_phy_modify_mmd(uniphy_addr, QCA8084_UNIPHY_MMD1, + QCA8084_UNIPHY_MMD1_USXGMII_RESET, BIT(qca8084_port_id-1), 0); + mdelay(1); + qca8084_phy_modify_mmd(uniphy_addr, QCA8084_UNIPHY_MMD1, + QCA8084_UNIPHY_MMD1_USXGMII_RESET, BIT(qca8084_port_id-1), + BIT(qca8084_port_id-1)); + if(qca8084_port_id == PORT1) + qca8084_uniphy_xpcs_modify_mmd(QCA8084_UNIPHY_MMD3, + QCA8084_UNIPHY_MMD_MII_DIG_CTRL, + 0x400, QCA8084_UNIPHY_MMD3_USXG_FIFO_RESET); + else + qca8084_uniphy_xpcs_modify_port_mmd(qca8084_port_id, + QCA8084_UNIPHY_MMD_MII_DIG_CTRL, + 0x20, QCA8084_UNIPHY_MMD_USXG_FIFO_RESET); +} + +void qca8084_uniphy_xpcs_autoneg_restart(uint32_t qca8084_port_id) +{ + uint32_t retries = 500, uniphy_data = 0, mmd_id = 0; + + mmd_id = qca8084_uniphy_xpcs_port_to_mmd(qca8084_port_id); + qca8084_uniphy_xpcs_modify_mmd(mmd_id, QCA8084_UNIPHY_MMD_MII_CTRL, + QCA8084_UNIPHY_MMD_MII_AN_RESTART, QCA8084_UNIPHY_MMD_MII_AN_RESTART); + mdelay(1); + uniphy_data = qca8084_uniphy_xpcs_mmd_read(mmd_id, + QCA8084_UNIPHY_MMD_MII_ERR_SEL); + while(!(uniphy_data & QCA8084_UNIPHY_MMD_MII_AN_COMPLETE_INT)) + { + mdelay(1); + if (retries-- == 0) + { + pr_debug("xpcs uniphy autoneg restart timeout\n"); + } + uniphy_data = qca8084_uniphy_xpcs_mmd_read(mmd_id, + QCA8084_UNIPHY_MMD_MII_ERR_SEL); + } +} + +static void _qca8084_interface_uqxgmii_mode_set(uint32_t uniphy_addr) +{ + uint32_t qca8084_port_id = 0, phy_addr = 0; + + /*reset xpcs*/ + pr_debug("reset xpcs\n"); + qca8084_clk_assert(QCA8084_UNIPHY_XPCS_RST); + /*select xpcs mode*/ + pr_debug("select xpcs mode\n"); + qca8084_phy_modify_mmd(uniphy_addr, QCA8084_UNIPHY_MMD1, + QCA8084_UNIPHY_MMD1_MODE_CTRL, 0x1f00, QCA8084_UNIPHY_MMD1_XPCS_MODE); + /*config dapa pass as usxgmii*/ + pr_debug("config dapa pass as usxgmii\n"); + qca8084_phy_modify_mmd(uniphy_addr, QCA8084_UNIPHY_MMD1, + QCA8084_UNIPHY_MMD1_GMII_DATAPASS_SEL, QCA8084_UNIPHY_MMD1_DATAPASS_MASK, + QCA8084_UNIPHY_MMD1_DATAPASS_USXGMII); + /*reset and release uniphy GMII/XGMII and ethphy GMII*/ + pr_debug("reset and release uniphy GMII/XGMII and ethphy GMII\n"); + for(qca8084_port_id = PORT1; qca8084_port_id <= PORT4; + qca8084_port_id++) + { + qca8084_port_clk_reset(qca8084_port_id, + QCA8084_CLK_TYPE_UNIPHY|QCA8084_CLK_TYPE_EPHY); + } + + /*ana sw reset and release*/ + pr_debug("ana sw reset and release\n"); + qca8084_phy_modify_mii(uniphy_addr, + QCA8084_UNIPHY_PLL_POWER_ON_AND_RESET, 0x40, QCA8084_UNIPHY_ANA_SOFT_RESET); + mdelay(10); + qca8084_phy_modify_mii(uniphy_addr, + QCA8084_UNIPHY_PLL_POWER_ON_AND_RESET, 0x40, QCA8084_UNIPHY_ANA_SOFT_RELEASE); + + /*Wait calibration done*/ + pr_debug("Wait calibration done\n"); + qca8084_uniphy_calibration(uniphy_addr); + /*Enable SSCG(Spread Spectrum Clock Generator)*/ + pr_debug("enable uniphy sscg\n"); + qca8084_phy_modify_mmd(uniphy_addr, QCA8084_UNIPHY_MMD1, + QCA8084_UNIPHY_MMD1_CDA_CONTROL1, 0x8, QCA8084_UNIPHY_MMD1_SSCG_ENABLE); + /*release XPCS*/ + pr_debug("release XPCS\n"); + qca8084_clk_deassert(QCA8084_UNIPHY_XPCS_RST); + /*ethphy software reset*/ + pr_debug("ethphy software reset\n"); + for(qca8084_port_id = PORT1; qca8084_port_id <= PORT4; + qca8084_port_id++) + { + qca8084_ephy_addr_get(qca8084_port_id, &phy_addr); + qca8084_phy_reset(phy_addr); + } + /*Set BaseR mode*/ + pr_debug("Set BaseR mode\n"); + qca8084_uniphy_xpcs_modify_mmd(QCA8084_UNIPHY_MMD3, + QCA8084_UNIPHY_MMD3_PCS_CTRL2, 0xf, QCA8084_UNIPHY_MMD3_PCS_TYPE_10GBASE_R); + /*wait 10G base_r link up*/ + pr_debug("wait 10G base_r link up\n"); + qca8084_uniphy_xpcs_10g_r_linkup(); + /*enable UQXGMII mode*/ + pr_debug("enable UQSXGMII mode\n"); + qca8084_uniphy_xpcs_modify_mmd(QCA8084_UNIPHY_MMD3, + QCA8084_UNIPHY_MMD3_DIG_CTRL1, 0x200, QCA8084_UNIPHY_MMD3_USXGMII_EN); + /*set UQXGMII mode*/ + pr_debug("set QXGMII mode\n"); + qca8084_uniphy_xpcs_modify_mmd(QCA8084_UNIPHY_MMD3, + QCA8084_UNIPHY_MMD3_VR_RPCS_TPC, 0x1c00, QCA8084_UNIPHY_MMD3_QXGMII_EN); + /*set AM interval*/ + pr_debug("set AM interval\n"); + qca8084_uniphy_xpcs_mmd_write(QCA8084_UNIPHY_MMD3, + QCA8084_UNIPHY_MMD3_MII_AM_INTERVAL, QCA8084_UNIPHY_MMD3_MII_AM_INTERVAL_VAL); + /*xpcs software reset*/ + pr_debug("xpcs software reset\n"); + qca8084_uniphy_xpcs_soft_reset(); +} + +void qca8084_interface_uqxgmii_mode_set(void) +{ + uint32_t uniphy_addr = 0, qca8084_port_id = 0; + + qca8084_serdes_addr_get(QCA8084_UNIPHY_SGMII_1, &uniphy_addr); + + /*disable IPG_tuning bypass*/ + pr_debug("disable IPG_tuning bypass\n"); + qca8084_phy_modify_mmd(uniphy_addr, QCA8084_UNIPHY_MMD1, + QCA8084_UNIPHY_MMD1_BYPASS_TUNING_IPG, + QCA8084_UNIPHY_MMD1_BYPASS_TUNING_IPG_EN, 0); + /*disable uniphy GMII/XGMII clock and disable ethphy GMII clock*/ + pr_debug("disable uniphy GMII/XGMII clock and ethphy GMII clock\n"); + for(qca8084_port_id = PORT1; qca8084_port_id <= PORT4; + qca8084_port_id++) + { + qca8084_port_clk_en_set(qca8084_port_id, + QCA8084_CLK_TYPE_UNIPHY|QCA8084_CLK_TYPE_EPHY, 0); + } + /*configure uqxgmii mode*/ + pr_debug("configure uqxgmii mode\n"); + _qca8084_interface_uqxgmii_mode_set(uniphy_addr); + /*enable auto-neg complete interrupt,Mii using mii-4bits, + configure as PHY mode, enable autoneg ability*/ + pr_debug("enable auto-neg complete interrupt, Mii using mii-4bits," + " configure as PHY mode, enable autoneg ability, disable TICD\n"); + for (qca8084_port_id = PORT1; qca8084_port_id <= PORT4; + qca8084_port_id++) + { + /*enable auto-neg complete interrupt,Mii using mii-4bits,configure as PHY mode*/ + qca8084_uniphy_xpcs_modify_port_mmd(qca8084_port_id, + QCA8084_UNIPHY_MMD_MII_AN_INT_MSK, 0x109, + QCA8084_UNIPHY_MMD_AN_COMPLETE_INT | + QCA8084_UNIPHY_MMD_MII_4BITS_CTRL | + QCA8084_UNIPHY_MMD_TX_CONFIG_CTRL); + + /*enable autoneg ability*/ + qca8084_uniphy_xpcs_modify_port_mmd(qca8084_port_id, + QCA8084_UNIPHY_MMD_MII_CTRL, 0x3060, QCA8084_UNIPHY_MMD_MII_AN_ENABLE | + QCA8084_UNIPHY_MMD_XPC_SPEED_1000); + + /*disable TICD*/ + qca8084_uniphy_xpcs_modify_port_mmd(qca8084_port_id, + QCA8084_UNIPHY_MMD_MII_XAUI_MODE_CTRL, 0x1, + QCA8084_UNIPHY_MMD_TX_IPG_CHECK_DISABLE); + } + + /*enable EEE for xpcs*/ + pr_debug("enable EEE for xpcs\n"); + qca8084_uniphy_xpcs_8023az_enable(); +} diff --git a/drivers/net/ipq_common/ipq_qca8084_interface_ctrl.h b/drivers/net/ipq_common/ipq_qca8084_interface_ctrl.h new file mode 100644 index 0000000000..88eccce4f6 --- /dev/null +++ b/drivers/net/ipq_common/ipq_qca8084_interface_ctrl.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2022, 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. +*/ +#ifndef __QCA8084_IF_CTRL_H_ +#define __QCA8084_IF_CTRL_H_ + +#define EPHY_CFG_OFFSET 0xC90F018 +#define EPHY_CFG_EPHY0_ADDR_BOFFSET 0 +#define EPHY_CFG_EPHY1_ADDR_BOFFSET 5 +#define EPHY_CFG_EPHY2_ADDR_BOFFSET 10 +#define EPHY_CFG_EPHY3_ADDR_BOFFSET 15 + +#define SERDES_CFG_OFFSET 0xC90F014 +#define SERDES_CFG_S0_ADDR_BOFFSET 0 +#define SERDES_CFG_S1_ADDR_BOFFSET 5 +#define SERDES_CFG_S1_XPCS_ADDR_BOFFSET 10 + +#define QCA8084_UNIPHY_SGMII_0 0 +#define QCA8084_UNIPHY_SGMII_1 1 +#define QCA8084_UNIPHY_XPCS 2 + +/*UNIPHY MII registers*/ +#define QCA8084_UNIPHY_PLL_POWER_ON_AND_RESET 0 + +/*UNIPHY MII register field*/ +#define QCA8084_UNIPHY_ANA_SOFT_RESET 0 +#define QCA8084_UNIPHY_ANA_SOFT_RELEASE 0x40 + +/*UNIPHY MMD*/ +#define QCA8084_UNIPHY_MMD1 0x1 +#define QCA8084_UNIPHY_MMD3 0x3 +#define QCA8084_UNIPHY_MMD26 0x1a +#define QCA8084_UNIPHY_MMD27 0x1b +#define QCA8084_UNIPHY_MMD28 0x1c +#define QCA8084_UNIPHY_MMD31 0x1f + +/*UNIPHY MMD1 registers*/ +#define QCA8084_UNIPHY_MMD1_CDA_CONTROL1 0x20 +#define QCA8084_UNIPHY_MMD1_CALIBRATION4 0x78 +#define QCA8084_UNIPHY_MMD1_BYPASS_TUNING_IPG 0x189 +#define QCA8084_UNIPHY_MMD1_MODE_CTRL 0x11b +#define QCA8084_UNIPHY_MMD1_CHANNEL0_CFG 0x120 +#define QCA8084_UNIPHY_MMD1_GMII_DATAPASS_SEL 0x180 +#define QCA8084_UNIPHY_MMD1_USXGMII_RESET 0x18c + +/*UNIPHY MMD1 register field*/ +#define QCA8084_UNIPHY_MMD1_BYPASS_TUNING_IPG_EN 0x0fff +#define QCA8084_UNIPHY_MMD1_XPCS_MODE 0x1000 +#define QCA8084_UNIPHY_MMD1_SGMII_MODE 0x400 +#define QCA8084_UNIPHY_MMD1_SGMII_PLUS_MODE 0x800 +#define QCA8084_UNIPHY_MMD1_1000BASE_X 0x0 +#define QCA8084_UNIPHY_MMD1_SGMII_PHY_MODE 0x10 +#define QCA8084_UNIPHY_MMD1_SGMII_MAC_MODE 0x20 +#define QCA8084_UNIPHY_MMD1_SGMII_MODE_CTRL_MASK 0x1f70 +#define QCA8084_UNIPHY_MMD1_CH0_FORCE_SPEED_MASK 0xe +#define QCA8084_UNIPHY_MMD1_CH0_AUTONEG_ENABLE 0x0 +#define QCA8084_UNIPHY_MMD1_CH0_FORCE_ENABLE 0x8 +#define QCA8084_UNIPHY_MMD1_CH0_FORCE_SPEED_1G 0x4 +#define QCA8084_UNIPHY_MMD1_CH0_FORCE_SPEED_100M 0x2 +#define QCA8084_UNIPHY_MMD1_CH0_FORCE_SPEED_10M 0x0 +#define QCA8084_UNIPHY_MMD1_DATAPASS_MASK 0x1 +#define QCA8084_UNIPHY_MMD1_DATAPASS_USXGMII 0x1 +#define QCA8084_UNIPHY_MMD1_DATAPASS_SGMII 0x0 +#define QCA8084_UNIPHY_MMD1_CALIBRATION_DONE 0x80 +#define QCA8084_UNIPHY_MMD1_SGMII_FUNC_RESET 0x10 +#define QCA8084_UNIPHY_MMD1_SGMII_ADPT_RESET 0x800 +#define QCA8084_UNIPHY_MMD1_SSCG_ENABLE 0x8 + +/*UNIPHY MMD3 registers*/ +#define QCA8084_UNIPHY_MMD3_PCS_CTRL2 0x7 +#define QCA8084_UNIPHY_MMD3_AN_LP_BASE_ABL2 0x14 +#define QCA8084_UNIPHY_MMD3_10GBASE_R_PCS_STATUS1 0x20 +#define QCA8084_UNIPHY_MMD3_DIG_CTRL1 0x8000 +#define QCA8084_UNIPHY_MMD3_EEE_MODE_CTRL 0x8006 +#define QCA8084_UNIPHY_MMD3_VR_RPCS_TPC 0x8007 +#define QCA8084_UNIPHY_MMD3_EEE_TX_TIMER 0x8008 +#define QCA8084_UNIPHY_MMD3_EEE_RX_TIMER 0x8009 +#define QCA8084_UNIPHY_MMD3_MII_AM_INTERVAL 0x800a +#define QCA8084_UNIPHY_MMD3_EEE_MODE_CTRL1 0x800b + +/*UNIPHY MMD3 register field*/ +#define QCA8084_UNIPHY_MMD3_PCS_TYPE_10GBASE_R 0 +#define QCA8084_UNIPHY_MMD3_10GBASE_R_UP 0x1000 +#define QCA8084_UNIPHY_MMD3_USXGMII_EN 0x200 +#define QCA8084_UNIPHY_MMD3_QXGMII_EN 0x1400 +#define QCA8084_UNIPHY_MMD3_MII_AM_INTERVAL_VAL 0x6018 +#define QCA8084_UNIPHY_MMD3_XPCS_SOFT_RESET 0x8000 +#define QCA8084_UNIPHY_MMD3_XPCS_EEE_CAP 0x40 +#define QCA8084_UNIPHY_MMD3_EEE_RES_REGS 0x100 +#define QCA8084_UNIPHY_MMD3_EEE_SIGN_BIT_REGS 0x40 +#define QCA8084_UNIPHY_MMD3_EEE_EN 0x3 +#define QCA8084_UNIPHY_MMD3_EEE_TSL_REGS 0xa +#define QCA8084_UNIPHY_MMD3_EEE_TLU_REGS 0xc0 +#define QCA8084_UNIPHY_MMD3_EEE_TWL_REGS 0x1600 +#define QCA8084_UNIPHY_MMD3_EEE_100US_REG_REGS 0xc8 +#define QCA8084_UNIPHY_MMD3_EEE_RWR_REG_REGS 0x1c00 +#define QCA8084_UNIPHY_MMD3_EEE_TRANS_LPI_MODE 0x1 +#define QCA8084_UNIPHY_MMD3_EEE_TRANS_RX_LPI_MODE 0x100 +#define QCA8084_UNIPHY_MMD3_USXG_FIFO_RESET 0x400 + +/*UNIPHY MMD26 27 28 31 registers*/ +#define QCA8084_UNIPHY_MMD_MII_CTRL 0 +#define QCA8084_UNIPHY_MMD_MII_DIG_CTRL 0x8000 +#define QCA8084_UNIPHY_MMD_MII_AN_INT_MSK 0x8001 +#define QCA8084_UNIPHY_MMD_MII_ERR_SEL 0x8002 +#define QCA8084_UNIPHY_MMD_MII_XAUI_MODE_CTRL 0x8004 + +/*UNIPHY MMD26 27 28 31 register field*/ +#define QCA8084_UNIPHY_MMD_AN_COMPLETE_INT 0x1 +#define QCA8084_UNIPHY_MMD_MII_4BITS_CTRL 0x0 +#define QCA8084_UNIPHY_MMD_TX_CONFIG_CTRL 0x8 +#define QCA8084_UNIPHY_MMD_MII_AN_ENABLE 0x1000 +#define QCA8084_UNIPHY_MMD_MII_AN_RESTART 0x200 +#define QCA8084_UNIPHY_MMD_MII_AN_COMPLETE_INT 0x1 +#define QCA8084_UNIPHY_MMD_USXG_FIFO_RESET 0x20 +#define QCA8084_UNIPHY_MMD_XPC_SPEED_MASK 0x2060 +#define QCA8084_UNIPHY_MMD_XPC_SPEED_2500 0x20 +#define QCA8084_UNIPHY_MMD_XPC_SPEED_1000 0x40 +#define QCA8084_UNIPHY_MMD_XPC_SPEED_100 0x2000 +#define QCA8084_UNIPHY_MMD_XPC_SPEED_10 0 +#define QCA8084_UNIPHY_MMD_TX_IPG_CHECK_DISABLE 0x1 + +typedef enum { + QCA8084_UNIPHY_MAC = QCA8084_UNIPHY_MMD1_SGMII_MAC_MODE, + QCA8084_UNIPHY_PHY = QCA8084_UNIPHY_MMD1_SGMII_PHY_MODE, + QCA8084_UNIPHY_SGMII = QCA8084_UNIPHY_MMD1_SGMII_MODE, + QCA8084_UNIPHY_SGMII_PLUS = QCA8084_UNIPHY_MMD1_SGMII_PLUS_MODE, + QCA8084_UNIPHY_UQXGMII = QCA8084_UNIPHY_MMD1_XPCS_MODE, +} qca8084_uniphy_mode_t; + +#endif diff --git a/include/dt-bindings/qcom/eth-ipq9574.h b/include/dt-bindings/qcom/eth-ipq9574.h index 6d4d064538..a49de0039c 100644 --- a/include/dt-bindings/qcom/eth-ipq9574.h +++ b/include/dt-bindings/qcom/eth-ipq9574.h @@ -36,6 +36,8 @@ #define PORT_WRAPPER_RGMII 18 #define PORT_WRAPPER_PSGMII_FIBER 19 #define PORT_WRAPPER_SGMII_FIBER 20 +#define PORT_WRAPPER_UQXGMII 21 +#define PORT_WRAPPER_UQXGMII_3CHANNELS 22 #define PORT_WRAPPER_MAX 0xFF /* ETH PHY Types */ @@ -44,4 +46,6 @@ #define AQ_PHY_TYPE 0x3 #define QCA8033_PHY_TYPE 0x4 #define SFP_PHY_TYPE 0x5 +#define QCA8084_PHY_TYPE 0x6 +#define UNUSED_PHY_TYPE 0xFF #endif diff --git a/include/linux/bitops.h b/include/linux/bitops.h index 1b2e4915a0..10b0c50e5f 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -6,6 +6,7 @@ #define BIT(nr) (1UL << (nr)) #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BITS_MASK(_s, _n) (((1UL << (_n)) - 1) << _s) #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) /* @@ -122,6 +123,17 @@ static inline unsigned int generic_hweight8(unsigned int w) #include +#define for_each_clear_bit(bit, addr, size) \ + for ((bit) = find_first_zero_bit((addr), (size)); \ + (bit) < (size); \ + (bit) = find_next_zero_bit((addr), (size), (bit) + 1)) + +/* same as for_each_clear_bit() but use bit as value to start with */ +#define for_each_clear_bit_from(bit, addr, size) \ + for ((bit) = find_next_zero_bit((addr), (size), (bit)); \ + (bit) < (size); \ + (bit) = find_next_zero_bit((addr), (size), (bit) + 1)) + /* linux/include/asm-generic/bitops/non-atomic.h */ #ifndef PLATFORM__SET_BIT diff --git a/include/net.h b/include/net.h index b1a9bc22c3..f73af6336e 100644 --- a/include/net.h +++ b/include/net.h @@ -19,6 +19,14 @@ #include #include /* for nton* / ntoh* stuff */ +#define PORT0 0 +#define PORT1 1 +#define PORT2 2 +#define PORT3 3 +#define PORT4 4 +#define PORT5 5 +#define PORT6 6 + #define DEBUG_LL_STATE 0 /* Link local state machine changes */ #define DEBUG_DEV_PKT 0 /* Packets or info directed to the device */ #define DEBUG_NET_PKT 0 /* Packets on info on the network at large */