| /* |
| * Copyright (c) 2015, 2017-2021, The Linux Foundation. All rights reserved. |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all copies. |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
| * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| /*qca808x_start*/ |
| #include "sw.h" |
| #include "hsl_phy.h" |
| #include "hsl.h" |
| /*qca808x_end*/ |
| #include "ssdk_dts.h" |
| #if defined(ISIS) ||defined(ISISC) ||defined(GARUDA) |
| #include <f1_phy.h> |
| #endif |
| #if defined(ATHENA) ||defined(SHIVA) ||defined(HORUS) |
| #include <f2_phy.h> |
| #endif |
| #ifdef MP |
| #include "mpge_phy.h" |
| #endif |
| #ifdef IN_MALIBU_PHY |
| #include <malibu_phy.h> |
| #endif |
| #ifdef IN_AQUANTIA_PHY |
| #include <aquantia_phy.h> |
| #endif |
| #ifdef IN_QCA803X_PHY |
| #include <qca803x_phy.h> |
| #endif |
| #ifdef IN_SFP_PHY |
| #include <sfp_phy.h> |
| #endif |
| #ifdef IN_QCA808X_PHY |
| /*qca808x_start*/ |
| #include <qca808x_phy.h> |
| /*qca808x_end*/ |
| #endif |
| #include <linux/of_gpio.h> |
| /*qca808x_start*/ |
| #include "sw.h" |
| #include "ssdk_plat.h" |
| #include "hsl_port_prop.h" |
| |
| phy_info_t *phy_info[SW_MAX_NR_DEV] = {0}; |
| a_uint32_t port_bmp[SW_MAX_NR_DEV] = {0}; |
| |
| |
| phy_driver_instance_t ssdk_phy_driver[] = |
| { |
| /*qca808x_end*/ |
| #if defined(ISIS) ||defined(ISISC) ||defined(GARUDA) |
| {F1_PHY_CHIP, {0}, NULL, f1_phy_init, NULL}, |
| #else |
| {F1_PHY_CHIP, {0}, NULL, NULL, NULL}, |
| #endif |
| #if defined(ATHENA) ||defined(SHIVA) ||defined(HORUS) |
| {F2_PHY_CHIP, {0}, NULL, f2_phy_init, NULL}, |
| #else |
| {F2_PHY_CHIP, {0}, NULL, NULL, NULL}, |
| #endif |
| #ifdef IN_MALIBU_PHY |
| {MALIBU_PHY_CHIP, {0}, NULL, malibu_phy_init, NULL}, |
| #else |
| {MALIBU_PHY_CHIP, {0}, NULL, NULL, NULL}, |
| #endif |
| #ifdef IN_AQUANTIA_PHY |
| {AQUANTIA_PHY_CHIP, {0}, NULL, aquantia_phy_init, NULL}, |
| #else |
| {AQUANTIA_PHY_CHIP, {0}, NULL, NULL, NULL}, |
| #endif |
| #ifdef IN_QCA803X_PHY |
| {QCA803X_PHY_CHIP, {0}, NULL, qca803x_phy_init, NULL}, |
| #else |
| {QCA803X_PHY_CHIP, {0}, NULL, NULL, NULL}, |
| #endif |
| #ifdef IN_SFP_PHY |
| {SFP_PHY_CHIP, {0}, NULL, sfp_phy_init, sfp_phy_exit}, |
| #else |
| {SFP_PHY_CHIP, {0}, NULL, NULL, NULL}, |
| #endif |
| #ifdef MP |
| {MPGE_PHY_CHIP, {0}, NULL, mpge_phy_init, NULL}, |
| #else |
| {MPGE_PHY_CHIP, {0}, NULL, NULL, NULL}, |
| #endif |
| #ifdef IN_QCA808X_PHY |
| /*qca808x_start*/ |
| {QCA808X_PHY_CHIP, {0}, NULL, qca808x_phy_init, qca808x_phy_exit}, |
| /*qca808x_end*/ |
| #else |
| {QCA808X_PHY_CHIP, {0}, NULL, NULL, NULL}, |
| #endif |
| /*qca808x_start*/ |
| {MAX_PHY_CHIP, {0}, NULL, NULL, NULL} |
| }; |
| sw_error_t hsl_phy_api_ops_register(phy_type_t phy_type, hsl_phy_ops_t * phy_api_ops) |
| { |
| |
| ssdk_phy_driver[phy_type].phy_ops = phy_api_ops; |
| |
| return SW_OK; |
| |
| } |
| |
| sw_error_t hsl_phy_api_ops_unregister(phy_type_t phy_type, hsl_phy_ops_t * phy_api_ops) |
| { |
| |
| ssdk_phy_driver[phy_type].phy_ops = NULL; |
| |
| return SW_OK; |
| |
| } |
| |
| hsl_phy_ops_t *hsl_phy_api_ops_get(a_uint32_t dev_id, a_uint32_t port_id) |
| { |
| phy_type_t phytype = 0; |
| |
| if (dev_id >= SW_MAX_NR_DEV) |
| return NULL; |
| |
| phytype = phy_info[dev_id]->phy_type[port_id]; |
| if(phytype == MAX_PHY_CHIP) |
| { |
| return NULL; |
| } |
| |
| return ssdk_phy_driver[phytype].phy_ops; |
| |
| } |
| |
| sw_error_t phy_api_ops_init(phy_type_t phy_type) |
| { |
| |
| if (MAX_PHY_CHIP <= phy_type) |
| return SW_BAD_PARAM; |
| |
| if(ssdk_phy_driver[phy_type].phy_ops != NULL) |
| { |
| kfree(ssdk_phy_driver[phy_type].phy_ops); |
| ssdk_phy_driver[phy_type].phy_ops = NULL; |
| } |
| return SW_OK; |
| } |
| /*qca808x_end*/ |
| a_bool_t hsl_port_is_sfp(a_uint32_t dev_id, a_uint32_t port_id) |
| { |
| a_bool_t sfp_port = 0; |
| a_uint32_t mode1, mode2; |
| |
| sfp_port = ssdk_port_feature_get(dev_id, port_id, PHY_F_SFP); |
| if (sfp_port == A_TRUE) { |
| return A_TRUE; |
| } |
| |
| mode1 = ssdk_dt_global_get_mac_mode(dev_id, SSDK_UNIPHY_INSTANCE1); |
| mode2 = ssdk_dt_global_get_mac_mode(dev_id, SSDK_UNIPHY_INSTANCE2); |
| |
| if (((SSDK_PHYSICAL_PORT5 == port_id) && |
| ((mode1 == PORT_WRAPPER_10GBASE_R) || |
| (mode1 == PORT_WRAPPER_SGMII_FIBER))) || |
| ((SSDK_PHYSICAL_PORT6 == port_id) && |
| ((mode2 == PORT_WRAPPER_10GBASE_R) || |
| (mode2 == PORT_WRAPPER_SGMII_FIBER)))) |
| return A_TRUE; |
| else |
| return A_FALSE; |
| } |
| /*qca808x_start*/ |
| a_uint32_t hsl_phyid_get(a_uint32_t dev_id, |
| a_uint32_t port_id, ssdk_init_cfg *cfg) |
| { |
| a_uint16_t org_id = 0, rev_id = 0; |
| a_uint32_t reg_pad = 0, phy_id = 0; |
| |
| /*qca808x_end*/ |
| if(ssdk_is_emulation(dev_id) && ssdk_emu_chip_ver_get(dev_id) == MP_GEPHY){ |
| return MP_GEPHY; |
| } |
| if (hsl_port_is_sfp(dev_id, port_id)){ |
| return SFP_PHY; |
| } |
| /*qca808x_start*/ |
| if (phy_info[dev_id]->phy_c45[port_id] == A_TRUE){ |
| reg_pad = BIT(30) | BIT(16); |
| } |
| |
| #if defined(IN_PHY_I2C_MODE) |
| if (hsl_port_phy_access_type_get(dev_id, port_id) == PHY_I2C_ACCESS) { |
| cfg->reg_func.i2c_get(dev_id, |
| phy_info[dev_id]->phy_address[port_id], reg_pad | 2, &org_id); |
| cfg->reg_func.i2c_get(dev_id, |
| phy_info[dev_id]->phy_address[port_id], reg_pad | 3, &rev_id); |
| if(((org_id << 16) | rev_id) == INVALID_PHY_ID) { |
| return QCA8081_PHY_V1_1; |
| } |
| } |
| else |
| #endif |
| { |
| cfg->reg_func.mdio_get(dev_id, |
| phy_info[dev_id]->phy_address[port_id], reg_pad | 2, &org_id); |
| cfg->reg_func.mdio_get(dev_id, |
| phy_info[dev_id]->phy_address[port_id], reg_pad | 3, &rev_id); |
| } |
| |
| phy_id = (org_id<<16) | rev_id; |
| |
| return phy_id; |
| } |
| |
| phy_type_t hsl_phytype_get_by_phyid(a_uint32_t dev_id, a_uint32_t phy_id) |
| { |
| phy_type_t phytype = MAX_PHY_CHIP; |
| |
| switch (phy_id) |
| { |
| /*qca808x_end*/ |
| case F1V1_PHY: |
| case F1V2_PHY: |
| case F1V3_PHY: |
| case F1V4_PHY: |
| phytype = F1_PHY_CHIP; |
| break; |
| case F2V1_PHY: |
| phytype = F2_PHY_CHIP; |
| break; |
| case MALIBU2PORT_PHY: |
| case MALIBU5PORT_PHY: |
| phytype = MALIBU_PHY_CHIP; |
| break; |
| case AQUANTIA_PHY_107: |
| case AQUANTIA_PHY_108: |
| case AQUANTIA_PHY_109: |
| case AQUANTIA_PHY_111: |
| case AQUANTIA_PHY_111B0: |
| case AQUANTIA_PHY_112: |
| case AQUANTIA_PHY_113C_A0: |
| case AQUANTIA_PHY_113C_A1: |
| case AQUANTIA_PHY_112C: |
| phytype = AQUANTIA_PHY_CHIP; |
| break; |
| case QCA8030_PHY: |
| case QCA8033_PHY: |
| case QCA8035_PHY: |
| phytype = QCA803X_PHY_CHIP; |
| break; |
| case SFP_PHY: |
| phytype = SFP_PHY_CHIP; |
| break; |
| case MP_GEPHY: |
| phytype = MPGE_PHY_CHIP; |
| break; |
| /*qca808x_start*/ |
| case QCA8081_PHY_V1_1: |
| phytype = QCA808X_PHY_CHIP; |
| break; |
| default: |
| phytype = MAX_PHY_CHIP; |
| } |
| |
| return phytype; |
| } |
| /*qca808x_end*/ |
| sw_error_t hsl_phydriver_update(a_uint32_t dev_id, a_uint32_t port_id, |
| a_uint32_t mode) |
| { |
| a_uint32_t phy_id; |
| phy_type_t phytype; |
| ssdk_init_cfg cfg; |
| |
| cfg.chip_type = CHIP_HPPE; |
| if(port_id == SSDK_PHYSICAL_PORT5) |
| { |
| cfg.mac_mode1 = mode; |
| } |
| else if(port_id == SSDK_PHYSICAL_PORT6) |
| { |
| cfg.mac_mode2 = mode; |
| } |
| else |
| { |
| return SW_NOT_SUPPORTED; |
| } |
| if (hsl_port_phy_access_type_get(dev_id, port_id) == PHY_I2C_ACCESS) |
| { |
| cfg.reg_func.i2c_get = hsl_phy_i2c_get; |
| } |
| else |
| { |
| cfg.reg_func.mdio_get = reduce_hsl_phy_get; |
| } |
| phy_id = hsl_phyid_get(dev_id, port_id, &cfg); |
| phytype = hsl_phytype_get_by_phyid(dev_id, phy_id); |
| SSDK_DEBUG("port_id is %x, phy_id is %x, phy_type is:%x\n", |
| port_id, phy_id, phytype); |
| if (MAX_PHY_CHIP != phytype) |
| { |
| phy_info[dev_id]->phy_type[port_id] = phytype; |
| ssdk_phy_driver[phytype].port_bmp[dev_id] |= (0x1 << port_id); |
| } |
| |
| return SW_OK; |
| } |
| /*qca808x_start*/ |
| int ssdk_phy_driver_init(a_uint32_t dev_id, ssdk_init_cfg *cfg) |
| { |
| |
| int i = 0; |
| a_uint32_t phy_id = 0; |
| phy_type_t phytype = MAX_PHY_CHIP; |
| |
| for (i = 0; i < SW_MAX_NR_PORT; i++) |
| { |
| if (port_bmp[dev_id] & (0x1 << i)) |
| { |
| /*qca808x_end*/ |
| if(ssdk_port_feature_get(dev_id, i, PHY_F_FORCE)) { |
| continue; |
| } |
| /*qca808x_start*/ |
| phy_id = hsl_phyid_get(dev_id, i, cfg); |
| phytype = hsl_phytype_get_by_phyid(dev_id, phy_id); |
| if (MAX_PHY_CHIP != phytype) { |
| phy_info[dev_id]->phy_type[i] = phytype; |
| ssdk_phy_driver[phytype].port_bmp[dev_id] |= (0x1 << i); |
| } else { |
| SSDK_INFO("dev_id = %d, phy_adress = %d, phy_id = 0x%x phy" |
| "type doesn't match\n", dev_id, |
| phy_info[dev_id]->phy_address[i], phy_id); |
| } |
| } |
| } |
| |
| for(i = 0; i < MAX_PHY_CHIP;i++) { |
| if(ssdk_phy_driver[i].port_bmp[dev_id] != 0 && |
| ssdk_phy_driver[i].init != NULL) { |
| ssdk_phy_driver[i].init(dev_id, ssdk_phy_driver[i].port_bmp[dev_id]); |
| } |
| } |
| return 0; |
| } |
| |
| #ifdef QCA808X_PORTS_INFO |
| typedef struct { |
| a_uint32_t port_id; |
| a_uint32_t phy_address; |
| a_uint8_t phy_access_type; |
| } qca808x_phy_info_t; |
| /*5 is port_id, 0x1c is qca808x phy address, PHY_MDIO_ACCESS is mdio |
| mode to access qca808x phy, PHY_I2C_ACCESS is I2C mode to access |
| qca808x phy*/ |
| static qca808x_phy_info_t qca808x_phy_info[] = { |
| {5,0x7c,PHY_I2C_ACCESS} |
| }; |
| static int qca_ssdk_qca808x_phy_info_init(a_uint32_t dev_id) |
| { |
| a_uint32_t port_bmp = 0, port_id = 0, port_index = 0, port_index_max = 0; |
| |
| port_index_max = sizeof(qca808x_phy_info)/(sizeof(qca808x_phy_info_t)); |
| for(port_index = 0; port_index < port_index_max; port_index++) { |
| port_id = qca808x_phy_info[port_index].port_id; |
| port_bmp |= (1 << port_id); |
| /*qca808x phy address*/ |
| phy_info[dev_id]->phy_address[port_id] = |
| qca808x_phy_info[port_index].phy_address; |
| /*qca808x access mode, 1:i2c, 0:mdio*/ |
| phy_info[dev_id]->phy_access_type[port_id] = |
| qca808x_phy_info[port_index].phy_access_type; |
| } |
| qca_ssdk_port_bmp_set(dev_id, port_bmp); |
| |
| return 0; |
| } |
| #endif |
| |
| int qca_ssdk_phy_info_init(a_uint32_t dev_id) |
| { |
| a_uint32_t j = 0; |
| phy_info_t *phy_information; |
| |
| phy_information = kzalloc(sizeof(phy_info_t), GFP_KERNEL); |
| if (phy_information == NULL) { |
| SSDK_ERROR("phy_information kzalloc failed!\n"); |
| return -ENOMEM; |
| } |
| memset(phy_information, 0, sizeof(*phy_information)); |
| phy_info[dev_id] = phy_information; |
| |
| for (j = SSDK_PHYSICAL_PORT0; j < SW_MAX_NR_PORT; j ++) |
| { |
| phy_info[dev_id]->phy_type[j] = MAX_PHY_CHIP; |
| if(j == SSDK_PHYSICAL_PORT0) |
| { |
| phy_info[dev_id]->phy_address[j] = INVALID_PHY_ADDR; |
| } |
| else |
| { |
| phy_info[dev_id]->phy_address[j] = j - 1; |
| } |
| } |
| #ifdef QCA808X_PORTS_INFO |
| qca_ssdk_qca808x_phy_info_init(dev_id); |
| #endif |
| |
| return 0; |
| } |
| void qca_ssdk_port_bmp_init(a_uint32_t dev_id) |
| { |
| port_bmp[dev_id] = 0x3e; |
| |
| return; |
| } |
| /*qca808x_end*/ |
| void hsl_phy_address_init(a_uint32_t dev_id, a_uint32_t i, |
| a_uint32_t value) |
| { |
| phy_info[dev_id]->phy_address[i] = value; |
| |
| return; |
| } |
| /*qca808x_start*/ |
| void qca_ssdk_port_bmp_set(a_uint32_t dev_id, a_uint32_t value) |
| { |
| port_bmp[dev_id] = value; |
| |
| return; |
| } |
| |
| a_uint32_t qca_ssdk_port_bmp_get(a_uint32_t dev_id) |
| { |
| |
| return port_bmp[dev_id]; |
| } |
| /*qca808x_end*/ |
| a_uint32_t qca_ssdk_phy_type_port_bmp_get(a_uint32_t dev_id, |
| phy_type_t phy_type) |
| { |
| |
| return ssdk_phy_driver[phy_type].port_bmp[dev_id]; |
| } |
| |
| void |
| qca_ssdk_phy_address_set(a_uint32_t dev_id, a_uint32_t port_id, |
| a_uint32_t phy_addr) |
| { |
| phy_info[dev_id]->phy_address[port_id] = phy_addr; |
| |
| return; |
| } |
| |
| a_uint32_t |
| qca_ssdk_port_to_phy_mdio_fake_addr(a_uint32_t dev_id, a_uint32_t port_id) |
| { |
| return phy_info[dev_id]->phy_mdio_fake_address[port_id]; |
| } |
| |
| void qca_ssdk_phy_mdio_fake_address_set(a_uint32_t dev_id, a_uint32_t i, |
| a_uint32_t value) |
| { |
| phy_info[dev_id]->phy_mdio_fake_address[i] = value; |
| |
| return; |
| } |
| |
| a_uint32_t |
| qca_ssdk_phy_mdio_fake_addr_to_port(a_uint32_t dev_id, a_uint32_t phy_mdio_fake_addr) |
| { |
| a_uint32_t i = 0; |
| |
| for (i = 0; i < SW_MAX_NR_PORT; i ++) |
| { |
| if (phy_info[dev_id]->phy_mdio_fake_address[i] == phy_mdio_fake_addr) |
| return i; |
| } |
| SSDK_ERROR("doesn't match port_id to specified phy_mdio_fake_addr !\n"); |
| return 0; |
| } |
| /*qca808x_start*/ |
| a_uint32_t |
| qca_ssdk_port_to_phy_addr(a_uint32_t dev_id, a_uint32_t port_id) |
| { |
| return phy_info[dev_id]->phy_address[port_id]; |
| } |
| |
| a_uint32_t |
| qca_ssdk_phy_addr_to_port(a_uint32_t dev_id, a_uint32_t phy_addr) |
| { |
| a_uint32_t i = 0; |
| |
| for (i = 0; i < SW_MAX_NR_PORT; i ++) |
| { |
| if (phy_info[dev_id]->phy_address[i] == phy_addr) |
| return i; |
| } |
| SSDK_ERROR("doesn't match port_id to specified phy_addr !\n"); |
| return 0; |
| } |
| |
| a_bool_t |
| hsl_port_phy_combo_capability_get(a_uint32_t dev_id, a_uint32_t port_id) |
| { |
| if (dev_id >= SW_MAX_NR_DEV) |
| return A_FALSE; |
| |
| return phy_info[dev_id]->phy_combo[port_id]; |
| } |
| |
| void |
| hsl_port_phy_combo_capability_set(a_uint32_t dev_id, a_uint32_t port_id, |
| a_bool_t enable) |
| { |
| if (dev_id >= SW_MAX_NR_DEV) |
| return; |
| |
| phy_info[dev_id]->phy_combo[port_id] = enable; |
| |
| return; |
| } |
| |
| a_uint8_t |
| hsl_port_phy_access_type_get(a_uint32_t dev_id, a_uint32_t port_id) |
| { |
| if (dev_id >= SW_MAX_NR_DEV) |
| return 0; |
| |
| return phy_info[dev_id]->phy_access_type[port_id]; |
| } |
| |
| void |
| hsl_port_phy_access_type_set(a_uint32_t dev_id, a_uint32_t port_id, |
| a_uint8_t access_type) |
| { |
| if (dev_id >= SW_MAX_NR_DEV) |
| return; |
| |
| phy_info[dev_id]->phy_access_type[port_id] = access_type; |
| |
| return; |
| } |
| /*qca808x_end*/ |
| void |
| hsl_port_phy_c45_capability_set(a_uint32_t dev_id, a_uint32_t port_id, |
| a_bool_t enable) |
| { |
| phy_info[dev_id]->phy_c45[port_id] = enable; |
| |
| return; |
| } |
| sw_error_t |
| hsl_port_phy_serdes_reset(a_uint32_t dev_id) |
| { |
| sw_error_t rv; |
| int i = 0; |
| hsl_phy_ops_t *phy_drv; |
| |
| for (i = 0; i < SW_MAX_NR_PORT; i++) |
| { |
| if (phy_info[dev_id]->phy_type[i] == MALIBU_PHY_CHIP) |
| { |
| SW_RTN_ON_NULL(phy_drv = hsl_phy_api_ops_get (dev_id, i)); |
| if (NULL == phy_drv->phy_serdes_reset) |
| return SW_NOT_SUPPORTED; |
| rv = phy_drv->phy_serdes_reset(dev_id); |
| return rv; |
| } |
| } |
| |
| return SW_OK; |
| } |
| |
| sw_error_t hsl_port_phy_hw_init(a_uint32_t dev_id, a_uint32_t port_id) |
| { |
| phy_type_t phytype; |
| |
| phytype = hsl_phy_type_get(dev_id, port_id); |
| |
| if(ssdk_phy_driver[phytype].port_bmp[dev_id] != 0 && |
| ssdk_phy_driver[phytype].init != NULL) |
| { |
| ssdk_phy_driver[phytype].init(dev_id, |
| ssdk_phy_driver[phytype].port_bmp[dev_id]); |
| } |
| |
| return SW_OK; |
| } |
| |
| a_uint32_t |
| hsl_port_phyid_get(a_uint32_t dev_id, fal_port_t port_id) |
| { |
| sw_error_t rv = SW_OK; |
| a_uint32_t phy_addr, phy_id; |
| hsl_phy_ops_t *phy_drv; |
| |
| phy_drv = hsl_phy_api_ops_get (dev_id, port_id); |
| if (phy_drv == NULL) { |
| return INVALID_PHY_ID; |
| } |
| if (NULL == phy_drv->phy_id_get) { |
| return INVALID_PHY_ID; |
| } |
| |
| rv = hsl_port_prop_get_phyid (dev_id, port_id, &phy_addr); |
| if(rv) { |
| return INVALID_PHY_ID; |
| } |
| |
| rv = phy_drv->phy_id_get (dev_id, phy_addr, &phy_id); |
| if(rv) { |
| return INVALID_PHY_ID; |
| } |
| |
| return phy_id; |
| } |
| |
| sw_error_t |
| hsl_port_phy_mode_set(a_uint32_t dev_id, fal_port_interface_mode_t mode) |
| { |
| sw_error_t rv; |
| a_uint32_t i = 0, phy_addr = 0; |
| hsl_phy_ops_t *phy_drv; |
| |
| for (i = 0; i < SW_MAX_NR_PORT; i++) |
| { |
| if (phy_info[dev_id]->phy_type[i] == MALIBU_PHY_CHIP) |
| { |
| SW_RTN_ON_NULL(phy_drv = hsl_phy_api_ops_get (dev_id, i)); |
| if (NULL == phy_drv->phy_interface_mode_set) |
| return SW_NOT_SUPPORTED; |
| |
| phy_addr = qca_ssdk_port_to_phy_addr(dev_id, i); |
| rv = phy_drv->phy_interface_mode_set(dev_id, phy_addr, mode); |
| return rv; |
| } |
| } |
| |
| return SW_OK; |
| } |
| phy_type_t hsl_phy_type_get(a_uint32_t dev_id, a_uint32_t port_id) |
| { |
| |
| if (dev_id >= SW_MAX_NR_DEV) |
| return MAX_PHY_CHIP; |
| |
| return phy_info[dev_id]->phy_type[port_id]; |
| } |
| |
| a_uint32_t hsl_port_phy_reset_gpio_get(a_uint32_t dev_id, a_uint32_t port_id) |
| { |
| return phy_info[dev_id]->phy_reset_gpio[port_id]; |
| } |
| |
| void hsl_port_phy_reset_gpio_set(a_uint32_t dev_id, a_uint32_t port_id, |
| a_uint32_t phy_reset_gpio) |
| { |
| phy_info[dev_id]->phy_reset_gpio[port_id] = phy_reset_gpio; |
| |
| return; |
| } |
| |
| void hsl_port_phy_gpio_reset(a_uint32_t dev_id, a_uint32_t port_id) |
| |
| { |
| a_uint32_t gpio_num, ret = 0; |
| |
| gpio_num = hsl_port_phy_reset_gpio_get(dev_id, port_id); |
| |
| if(gpio_num == SSDK_INVALID_GPIO) |
| { |
| return; |
| } |
| ret = gpio_request(gpio_num, "phy_reset_gpio"); |
| if(ret) |
| { |
| SSDK_ERROR("gpio request failed, ret:%d\n", ret); |
| return; |
| } |
| ret = gpio_direction_output(gpio_num, SSDK_GPIO_RESET); |
| if(ret) |
| { |
| SSDK_ERROR("when reset, gpio set failed, ret:%d\n", |
| ret); |
| return; |
| } |
| msleep(200); |
| gpio_set_value(gpio_num, SSDK_GPIO_RELEASE); |
| SSDK_INFO("GPIO%d reset PHY done\n", gpio_num); |
| |
| gpio_free(gpio_num); |
| |
| return; |
| } |
| |
| void |
| hsl_port_phy_dac_get(a_uint32_t dev_id, a_uint32_t port_id, |
| phy_dac_t *phy_dac) |
| { |
| phy_dac->mdac = phy_info[dev_id]->phy_dac[port_id].mdac; |
| phy_dac->edac = phy_info[dev_id]->phy_dac[port_id].edac; |
| |
| return; |
| } |
| |
| void |
| hsl_port_phy_dac_set(a_uint32_t dev_id, a_uint32_t port_id, |
| phy_dac_t phy_dac) |
| { |
| phy_info[dev_id]->phy_dac[port_id].mdac = phy_dac.mdac; |
| phy_info[dev_id]->phy_dac[port_id].edac = phy_dac.edac; |
| |
| return; |
| } |
| |
| sw_error_t |
| hsl_port_phydev_get(a_uint32_t dev_id, a_uint32_t port_id, |
| struct phy_device **phydev) |
| { |
| struct qca_phy_priv *priv; |
| a_uint32_t phy_addr, pdev_addr; |
| const char *pdev_name; |
| |
| priv = ssdk_phy_priv_data_get(dev_id); |
| SW_RTN_ON_NULL(priv); |
| |
| #if defined(IN_PHY_I2C_MODE) |
| if (hsl_port_phy_access_type_get(dev_id, port_id) == PHY_I2C_ACCESS) |
| { |
| phy_addr = qca_ssdk_port_to_phy_mdio_fake_addr(dev_id, port_id); |
| } |
| else |
| #endif |
| { |
| phy_addr = qca_ssdk_port_to_phy_addr(dev_id, port_id); |
| } |
| SW_RTN_ON_NULL(phydev); |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION (5, 0, 0)) |
| *phydev = priv->miibus->phy_map[phy_addr]; |
| SW_RTN_ON_NULL(*phydev); |
| pdev_addr = (*phydev)->addr; |
| pdev_name = dev_name(&((*phydev)->dev)); |
| #else |
| *phydev = mdiobus_get_phy(priv->miibus, phy_addr); |
| SW_RTN_ON_NULL(*phydev); |
| pdev_addr = (*phydev)->mdio.addr; |
| pdev_name = phydev_name(*phydev); |
| #endif |
| if(*phydev == NULL) |
| { |
| SSDK_ERROR("port %d phydev is NULL\n", port_id); |
| return SW_NOT_INITIALIZED; |
| } |
| SSDK_DEBUG("phy[%d]: device %s, driver %s\n", |
| pdev_addr, pdev_name, |
| (*phydev)->drv ? (*phydev)->drv->name : "unknown"); |
| |
| return SW_OK; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) |
| static sw_error_t |
| hsl_phy_adv_to_linkmode_adv(a_uint32_t autoadv, a_ulong_t *advertising) |
| { |
| linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, |
| advertising, autoadv & FAL_PHY_ADV_PAUSE); |
| linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, |
| advertising, autoadv & FAL_PHY_ADV_ASY_PAUSE); |
| |
| linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, |
| advertising, autoadv & FAL_PHY_ADV_10T_HD); |
| linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, |
| advertising, autoadv & FAL_PHY_ADV_10T_FD); |
| |
| linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, |
| advertising, autoadv & FAL_PHY_ADV_100TX_HD); |
| linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, |
| advertising, autoadv & FAL_PHY_ADV_100TX_FD); |
| |
| linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, |
| advertising, autoadv & FAL_PHY_ADV_1000T_FD); |
| |
| linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, |
| advertising, autoadv & FAL_PHY_ADV_2500T_FD); |
| |
| linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, |
| advertising, autoadv & FAL_PHY_ADV_5000T_FD); |
| |
| linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, |
| advertising, autoadv & FAL_PHY_ADV_10000T_FD); |
| |
| SSDK_DEBUG("autoadv:0x%x, advertising::0x%lx\n", |
| autoadv, *advertising); |
| |
| return SW_OK; |
| } |
| |
| sw_error_t |
| hsl_port_phydev_adv_update(a_uint32_t dev_id, a_uint32_t port_id, |
| a_uint32_t autoadv) |
| { |
| sw_error_t rv = SW_OK; |
| struct phy_device *phydev; |
| |
| rv = hsl_port_phydev_get(dev_id, port_id, &phydev); |
| SW_RTN_ON_ERROR(rv); |
| rv = hsl_phy_adv_to_linkmode_adv(autoadv, phydev->advertising); |
| |
| return rv; |
| } |
| #endif |
| |
| /*qca808x_start*/ |
| sw_error_t ssdk_phy_driver_cleanup(void) |
| { |
| a_uint32_t i = 0, j = 0; |
| |
| for (i = 0; i < MAX_PHY_CHIP;i++) { |
| for (j = 0; j < SW_MAX_NR_DEV; j++) { |
| if (ssdk_phy_driver[i].port_bmp[j] != 0 && |
| ssdk_phy_driver[i].exit != NULL) { |
| ssdk_phy_driver[i].exit(j, ssdk_phy_driver[i].port_bmp[j]); |
| } |
| } |
| if(ssdk_phy_driver[i].phy_ops != NULL) |
| { |
| kfree(ssdk_phy_driver[i].phy_ops); |
| ssdk_phy_driver[i].phy_ops = NULL; |
| } |
| } |
| |
| for(i = 0; i < SW_MAX_NR_DEV;i++) { |
| |
| if(phy_info[i] != NULL) |
| { |
| kfree(phy_info[i]); |
| phy_info[i] = NULL; |
| } |
| } |
| return SW_OK; |
| } |
| /*qca808x_end*/ |