mirror of
https://git.codelinaro.org/clo/qsdk/oss/boot/u-boot-2016.git
synced 2026-03-14 21:10:27 +01:00
Currently if CONFIG_QCA8084_PHY is enabled means, it will build all qca8084 functions required for both PHY & switch mode. But, some ipq devices might uses anyone of them. So, add configs to seperate the PHY & switch mode and define it in corresponding defconfig file as per the need. Also, some of the qca8084 functions will be used only for debug purpose, those functions are moved under the config QCA8084_DEBUG. Thereby, we can save some space in the u-boot. Change-Id: I7e5f53869629a0c7cbbb12daf04ed782c9693623 Signed-off-by: Ram Kumar D <quic_ramd@quicinc.com>
1413 lines
37 KiB
C
1413 lines
37 KiB
C
/*
|
|
* 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 "ipq_qca8084_interface_ctrl.h"
|
|
#include <asm/global_data.h>
|
|
#include <linux/compat.h>
|
|
#include <fdtdec.h>
|
|
#include <malloc.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
#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 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_gcc_clock_init(qca8084_work_mode_t clk_mode, u32 pbmp);
|
|
extern void qca8084_port_speed_clock_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);
|
|
|
|
#ifdef CONFIG_QCA8084_PHY_MODE
|
|
extern void qca8084_uniphy_xpcs_autoneg_restart(uint32_t qca8084_port_id);
|
|
extern void qca8084_uniphy_xpcs_speed_set(uint32_t qca8084_port_id,
|
|
fal_port_speed_t speed);
|
|
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_interface_uqxgmii_mode_set(void);
|
|
extern void qca8084_uniphy_uqxgmii_function_reset(uint32_t qca8084_port_id);
|
|
#endif /* CONFIG_QCA8084_PHY_MODE */
|
|
|
|
#ifdef CONFIG_QCA8084_SWT_MODE
|
|
extern void qca8084_gcc_port_clk_parent_set(qca8084_work_mode_t clk_mode,
|
|
uint32_t qca8084_port_id);
|
|
extern void qca8084_uniphy_sgmii_function_reset(u32 uniphy_index);
|
|
extern void qca8084_interface_sgmii_mode_set(u32 uniphy_index, u32
|
|
qca8084_port_id, mac_config_t *config);
|
|
extern void qca8084_clk_reset(const char *clock_id);
|
|
|
|
bool qca8084_port_txfc_forcemode[QCA8084_MAX_PORTS] = {};
|
|
bool qca8084_port_rxfc_forcemode[QCA8084_MAX_PORTS] = {};
|
|
#endif /* CONFIG_QCA8084_SWT_MODE */
|
|
|
|
static int qca8084_reg_field_get(u32 reg_addr, u32 bit_offset,
|
|
u32 field_len, u8 value[]);
|
|
static int qca8084_reg_field_set(u32 reg_addr, u32 bit_offset,
|
|
u32 field_len, const u8 value[]);
|
|
|
|
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_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);
|
|
}
|
|
|
|
/***************************** QCA8084 Pinctrl APIs *************************/
|
|
/****************************************************************************
|
|
*
|
|
* 1) PINs default Setting
|
|
*
|
|
****************************************************************************/
|
|
#ifdef IN_PINCTRL_DEF_CONFIG
|
|
static u64 pin_configs[] = {
|
|
QCA8084_PIN_CONFIG_OUTPUT_ENABLE,
|
|
QCA8084_PIN_CONFIG_BIAS_PULL_DOWN,
|
|
};
|
|
#endif
|
|
|
|
static struct qca8084_pinctrl_setting qca8084_pin_settings[] = {
|
|
/*PINs default MUX Setting*/
|
|
QCA8084_PIN_SETTING_MUX(0, QCA8084_PIN_FUNC_INTN_WOL),
|
|
QCA8084_PIN_SETTING_MUX(1, QCA8084_PIN_FUNC_INTN),
|
|
QCA8084_PIN_SETTING_MUX(2, QCA8084_PIN_FUNC_P0_LED_0),
|
|
QCA8084_PIN_SETTING_MUX(3, QCA8084_PIN_FUNC_P1_LED_0),
|
|
QCA8084_PIN_SETTING_MUX(4, QCA8084_PIN_FUNC_P2_LED_0),
|
|
QCA8084_PIN_SETTING_MUX(5, QCA8084_PIN_FUNC_P3_LED_0),
|
|
QCA8084_PIN_SETTING_MUX(6, QCA8084_PIN_FUNC_PPS_IN),
|
|
QCA8084_PIN_SETTING_MUX(7, QCA8084_PIN_FUNC_TOD_IN),
|
|
QCA8084_PIN_SETTING_MUX(8, QCA8084_PIN_FUNC_RTC_REFCLK_IN),
|
|
QCA8084_PIN_SETTING_MUX(9, QCA8084_PIN_FUNC_P0_PPS_OUT),
|
|
QCA8084_PIN_SETTING_MUX(10, QCA8084_PIN_FUNC_P1_PPS_OUT),
|
|
QCA8084_PIN_SETTING_MUX(11, QCA8084_PIN_FUNC_P2_PPS_OUT),
|
|
QCA8084_PIN_SETTING_MUX(12, QCA8084_PIN_FUNC_P3_PPS_OUT),
|
|
QCA8084_PIN_SETTING_MUX(13, QCA8084_PIN_FUNC_P0_TOD_OUT),
|
|
QCA8084_PIN_SETTING_MUX(14, QCA8084_PIN_FUNC_P0_CLK125_TDI),
|
|
QCA8084_PIN_SETTING_MUX(15, QCA8084_PIN_FUNC_P0_SYNC_CLKO_PTP),
|
|
QCA8084_PIN_SETTING_MUX(16, QCA8084_PIN_FUNC_P0_LED_1),
|
|
QCA8084_PIN_SETTING_MUX(17, QCA8084_PIN_FUNC_P1_LED_1),
|
|
QCA8084_PIN_SETTING_MUX(18, QCA8084_PIN_FUNC_P2_LED_1),
|
|
QCA8084_PIN_SETTING_MUX(19, QCA8084_PIN_FUNC_P3_LED_1),
|
|
QCA8084_PIN_SETTING_MUX(20, QCA8084_PIN_FUNC_MDC_M),
|
|
QCA8084_PIN_SETTING_MUX(21, QCA8084_PIN_FUNC_MDC_M),
|
|
|
|
#ifdef IN_PINCTRL_DEF_CONFIG
|
|
/*PINs default Config Setting*/
|
|
QCA8084_PIN_SETTING_CONFIG(0, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(1, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(2, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(3, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(4, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(5, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(6, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(7, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(8, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(9, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(10, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(11, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(12, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(13, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(14, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(15, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(16, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(17, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(18, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(19, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(20, pin_configs),
|
|
QCA8084_PIN_SETTING_CONFIG(21, pin_configs),
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
*
|
|
* 2) PINs Operations
|
|
*
|
|
****************************************************************************/
|
|
int qca8084_gpio_set_bit(u32 pin, u32 value)
|
|
{
|
|
int rv = 0;
|
|
|
|
QCA8084_REG_FIELD_SET(TLMM_GPIO_IN_OUTN, pin, GPIO_OUTE, (u8 *) (&value));
|
|
pr_debug("[%s] select pin:%d value:%d\n", __func__, pin, value);
|
|
|
|
return rv;
|
|
}
|
|
|
|
int qca8084_gpio_get_bit(u32 pin, u32 *data)
|
|
{
|
|
int rv = 0;
|
|
|
|
QCA8084_REG_FIELD_GET(TLMM_GPIO_IN_OUTN, pin, GPIO_IN, (u8 *) (data));
|
|
pr_debug("[%s] select pin:%d value:%d\n", __func__, pin, *data);
|
|
|
|
return rv;
|
|
}
|
|
|
|
int qca8084_gpio_pin_mux_set(u32 pin, u32 func)
|
|
{
|
|
int rv = 0;
|
|
|
|
pr_debug("[%s] select pin:%d func:%d\n", __func__, pin, func);
|
|
QCA8084_REG_FIELD_SET(TLMM_GPIO_CFGN, pin, FUNC_SEL, (u8 *) (&func));
|
|
|
|
return rv;
|
|
}
|
|
|
|
int qca8084_gpio_pin_cfg_set_bias(u32 pin, enum qca8084_pin_config_param bias)
|
|
{
|
|
int rv = 0;
|
|
u32 data = 0;
|
|
|
|
switch (bias)
|
|
{
|
|
case QCA8084_PIN_CONFIG_BIAS_DISABLE:
|
|
data = QCA8084_TLMM_GPIO_CFGN_GPIO_PULL_DISABLE;
|
|
break;
|
|
case QCA8084_PIN_CONFIG_BIAS_PULL_DOWN:
|
|
data = QCA8084_TLMM_GPIO_CFGN_GPIO_PULL_DOWN;
|
|
break;
|
|
case QCA8084_PIN_CONFIG_BIAS_BUS_HOLD:
|
|
data = QCA8084_TLMM_GPIO_CFGN_GPIO_PULL_BUS_HOLD;
|
|
break;
|
|
case QCA8084_PIN_CONFIG_BIAS_PULL_UP:
|
|
data = QCA8084_TLMM_GPIO_CFGN_GPIO_PULL_UP;
|
|
break;
|
|
default:
|
|
printf("[%s] doesn't support bias:%d\n", __func__, bias);
|
|
return -1;
|
|
}
|
|
|
|
QCA8084_REG_FIELD_SET(TLMM_GPIO_CFGN, pin, GPIO_PULL,
|
|
(u8 *) (&data));
|
|
pr_debug("[%s]pin:%d bias:%d", __func__, pin, bias);
|
|
|
|
return rv;
|
|
}
|
|
|
|
int qca8084_gpio_pin_cfg_get_bias(u32 pin, enum qca8084_pin_config_param *bias)
|
|
{
|
|
int rv = 0;
|
|
u32 data = 0;
|
|
|
|
QCA8084_REG_FIELD_GET(TLMM_GPIO_CFGN, pin, GPIO_PULL,
|
|
(u8 *) (&data));
|
|
switch (data)
|
|
{
|
|
case QCA8084_TLMM_GPIO_CFGN_GPIO_PULL_DISABLE:
|
|
*bias = QCA8084_PIN_CONFIG_BIAS_DISABLE;
|
|
break;
|
|
case QCA8084_TLMM_GPIO_CFGN_GPIO_PULL_DOWN:
|
|
*bias = QCA8084_PIN_CONFIG_BIAS_PULL_DOWN;
|
|
break;
|
|
case QCA8084_TLMM_GPIO_CFGN_GPIO_PULL_BUS_HOLD:
|
|
*bias = QCA8084_PIN_CONFIG_BIAS_BUS_HOLD;
|
|
break;
|
|
case QCA8084_TLMM_GPIO_CFGN_GPIO_PULL_UP:
|
|
*bias = QCA8084_PIN_CONFIG_BIAS_PULL_UP;
|
|
break;
|
|
default:
|
|
printf("[%s] doesn't support bias:%d\n", __func__, data);
|
|
return -1;
|
|
}
|
|
pr_debug("[%s]pin:%d bias:%d", __func__, pin, *bias);
|
|
|
|
return rv;
|
|
}
|
|
|
|
int qca8084_gpio_pin_cfg_set_drvs(u32 pin, u32 drvs)
|
|
{
|
|
int rv = 0;
|
|
|
|
if((drvs < QCA8084_TLMM_GPIO_CFGN_DRV_STRENGTH_2_MA) ||
|
|
(drvs > QCA8084_TLMM_GPIO_CFGN_DRV_STRENGTH_16_MA)) {
|
|
printf("[%s] doesn't support drvs:%d\n", __func__, drvs);
|
|
return -1;
|
|
}
|
|
|
|
QCA8084_REG_FIELD_SET(TLMM_GPIO_CFGN, pin, DRV_STRENGTH,
|
|
(u8 *) (&drvs));
|
|
pr_debug("[%s]%d", __func__, pin);
|
|
|
|
return rv;
|
|
}
|
|
|
|
int qca8084_gpio_pin_cfg_get_drvs(u32 pin, u32 *drvs)
|
|
{
|
|
int rv = 0;
|
|
|
|
QCA8084_REG_FIELD_GET(TLMM_GPIO_CFGN, pin, DRV_STRENGTH,
|
|
(u8 *) (drvs));
|
|
pr_debug("[%s]%d", __func__, pin);
|
|
|
|
return rv;
|
|
}
|
|
|
|
int qca8084_gpio_pin_cfg_set_oe(u32 pin, bool oe)
|
|
{
|
|
int rv = 0;
|
|
|
|
pr_debug("[%s]%d oe:%d", __func__, pin, oe);
|
|
|
|
QCA8084_REG_FIELD_SET(TLMM_GPIO_CFGN, pin, GPIO_OEA,
|
|
(u8 *) (&oe));
|
|
|
|
return rv;
|
|
}
|
|
|
|
int qca8084_gpio_pin_cfg_get_oe(u32 pin, bool *oe)
|
|
{
|
|
int rv = 0;
|
|
u32 data = 0;
|
|
|
|
QCA8084_REG_FIELD_GET(TLMM_GPIO_CFGN, pin, GPIO_OEA,
|
|
(u8 *) (&data));
|
|
*oe = data ? true : false;
|
|
|
|
pr_debug("[%s]%d oe:%d", __func__, pin, *oe);
|
|
|
|
return rv;
|
|
}
|
|
|
|
static enum qca8084_pin_config_param pinconf_to_config_param(unsigned long config)
|
|
{
|
|
return (enum qca8084_pin_config_param) (config & 0xffUL);
|
|
}
|
|
|
|
static u32 pinconf_to_config_argument(unsigned long config)
|
|
{
|
|
return (u32) ((config >> 8) & 0xffffffUL);
|
|
}
|
|
|
|
static int qca8084_gpio_pin_cfg_set(u32 pin,
|
|
u64 *configs, u32 num_configs)
|
|
{
|
|
enum qca8084_pin_config_param param;
|
|
u32 i, arg;
|
|
int rv = 0;
|
|
|
|
for (i = 0; i < num_configs; i++) {
|
|
param = pinconf_to_config_param(configs[i]);
|
|
arg = pinconf_to_config_argument(configs[i]);
|
|
|
|
switch (param) {
|
|
case QCA8084_PIN_CONFIG_BIAS_BUS_HOLD:
|
|
case QCA8084_PIN_CONFIG_BIAS_DISABLE:
|
|
case QCA8084_PIN_CONFIG_BIAS_PULL_DOWN:
|
|
case QCA8084_PIN_CONFIG_BIAS_PULL_UP:
|
|
rv = qca8084_gpio_pin_cfg_set_bias(pin, param);
|
|
break;
|
|
|
|
case QCA8084_PIN_CONFIG_DRIVE_STRENGTH:
|
|
rv = qca8084_gpio_pin_cfg_set_drvs(pin, arg);
|
|
break;
|
|
|
|
case QCA8084_PIN_CONFIG_OUTPUT:
|
|
rv = qca8084_gpio_pin_cfg_set_oe(pin, true);
|
|
rv = qca8084_gpio_set_bit(pin, arg);
|
|
break;
|
|
|
|
case QCA8084_PIN_CONFIG_INPUT_ENABLE:
|
|
rv = qca8084_gpio_pin_cfg_set_oe(pin, false);
|
|
break;
|
|
|
|
case QCA8084_PIN_CONFIG_OUTPUT_ENABLE:
|
|
rv = qca8084_gpio_pin_cfg_set_oe(pin, true);
|
|
break;
|
|
|
|
default:
|
|
printf("%s %d doesn't support:%d \n", __func__, __LINE__, param);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* 3) PINs Init
|
|
*
|
|
****************************************************************************/
|
|
int qca8084_pinctrl_clk_gate_set(bool gate_en)
|
|
{
|
|
int rv = 0;
|
|
|
|
QCA8084_REG_FIELD_SET(TLMM_CLK_GATE_EN, 0, AHB_HCLK_EN,
|
|
(u8 *) (&gate_en));
|
|
QCA8084_REG_FIELD_SET(TLMM_CLK_GATE_EN, 0, SUMMARY_INTR_EN,
|
|
(u8 *) (&gate_en));
|
|
QCA8084_REG_FIELD_SET(TLMM_CLK_GATE_EN, 0, CRIF_READ_EN,
|
|
(u8 *) (&gate_en));
|
|
|
|
pr_debug("[%s] gate_en:%d", __func__, gate_en);
|
|
|
|
return rv;
|
|
}
|
|
|
|
static int qca8084_pinctrl_rev_check(void)
|
|
{
|
|
int rv = 0;
|
|
u32 version_id = 0, mfg_id = 0, start_bit = 0;
|
|
|
|
QCA8084_REG_FIELD_GET(TLMM_HW_REVISION_NUMBER, 0, VERSION_ID,
|
|
(u8 *) (&version_id));
|
|
QCA8084_REG_FIELD_GET(TLMM_HW_REVISION_NUMBER, 0, MFG_ID,
|
|
(u8 *) (&mfg_id));
|
|
QCA8084_REG_FIELD_GET(TLMM_HW_REVISION_NUMBER, 0, START_BIT,
|
|
(u8 *) (&start_bit));
|
|
|
|
pr_debug("[%s] version_id:0x%x mfg_id:0x%x start_bit:0x%x",
|
|
__func__, version_id, mfg_id, start_bit);
|
|
|
|
if((version_id == 0x0) && (mfg_id == 0x70) && (start_bit == 0x1)) {
|
|
pr_debug(" Pinctrl Version Check Pass\n");
|
|
} else {
|
|
printf("Error: Pinctrl Version Check Fail\n");
|
|
rv = -1;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
static int qca8084_pinctrl_hw_init(void)
|
|
{
|
|
int rv = 0;
|
|
|
|
rv = qca8084_pinctrl_clk_gate_set(true);
|
|
rv = qca8084_pinctrl_rev_check();
|
|
|
|
return rv;
|
|
}
|
|
|
|
static int qca8084_pinctrl_setting_init(const struct qca8084_pinctrl_setting *pin_settings,
|
|
u32 num_setting)
|
|
{
|
|
int rv = 0;
|
|
u32 i;
|
|
|
|
for(i = 0; i < num_setting; i++) {
|
|
const struct qca8084_pinctrl_setting *setting = &pin_settings[i];
|
|
if (setting->type == QCA8084_PIN_MAP_TYPE_MUX_GROUP) {
|
|
rv = qca8084_gpio_pin_mux_set(setting->data.mux.pin, setting->data.mux.func);
|
|
|
|
} else if (setting->type == QCA8084_PIN_MAP_TYPE_CONFIGS_PIN) {
|
|
rv = qca8084_gpio_pin_cfg_set(setting->data.configs.pin,
|
|
setting->data.configs.configs,
|
|
setting->data.configs.num_configs);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
int ipq_qca8084_pinctrl_init(void)
|
|
{
|
|
qca8084_pinctrl_hw_init();
|
|
qca8084_pinctrl_setting_init(qca8084_pin_settings, ARRAY_SIZE(qca8084_pin_settings));
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_QCA8084_PHY_MODE
|
|
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_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(QCA8084_PHY_UQXGMII_MODE, 0);
|
|
|
|
/*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);
|
|
}
|
|
#endif /* CONFIG_QCA8084_PHY_MODE */
|
|
|
|
static int qca8084_reg_field_get(u32 reg_addr, u32 bit_offset,
|
|
u32 field_len, u8 value[])
|
|
{
|
|
u32 reg_val = ipq_mii_read(reg_addr);
|
|
|
|
*((u32 *) value) = SW_REG_2_FIELD(reg_val, bit_offset, field_len);
|
|
return 0;
|
|
}
|
|
|
|
static int qca8084_reg_field_set(u32 reg_addr, u32 bit_offset,
|
|
u32 field_len, const u8 value[])
|
|
{
|
|
u32 field_val = *((u32 *) value);
|
|
u32 reg_val = ipq_mii_read(reg_addr);
|
|
|
|
SW_REG_SET_BY_FIELD_U32(reg_val, field_val, bit_offset, field_len);
|
|
|
|
ipq_mii_write(reg_addr, reg_val);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_QCA8084_SWT_MODE
|
|
static void ipq_qca8084_switch_reset(void)
|
|
{
|
|
/* Reset switch core */
|
|
qca8084_clk_reset(QCA8084_SWITCH_CORE_CLK);
|
|
|
|
/* Reset MAC ports */
|
|
qca8084_clk_reset(QCA8084_MAC0_TX_CLK);
|
|
qca8084_clk_reset(QCA8084_MAC0_RX_CLK);
|
|
qca8084_clk_reset(QCA8084_MAC1_TX_CLK);
|
|
qca8084_clk_reset(QCA8084_MAC1_RX_CLK);
|
|
qca8084_clk_reset(QCA8084_MAC2_TX_CLK);
|
|
qca8084_clk_reset(QCA8084_MAC2_RX_CLK);
|
|
qca8084_clk_reset(QCA8084_MAC3_TX_CLK);
|
|
qca8084_clk_reset(QCA8084_MAC3_RX_CLK);
|
|
qca8084_clk_reset(QCA8084_MAC4_TX_CLK);
|
|
qca8084_clk_reset(QCA8084_MAC4_RX_CLK);
|
|
qca8084_clk_reset(QCA8084_MAC5_TX_CLK);
|
|
qca8084_clk_reset(QCA8084_MAC5_RX_CLK);
|
|
return;
|
|
}
|
|
|
|
static void ipq_qca8084_work_mode_set(qca8084_work_mode_t work_mode)
|
|
{
|
|
u32 data = 0;
|
|
|
|
data = ipq_mii_read(WORK_MODE_OFFSET);
|
|
data &= ~QCA8084_WORK_MODE_MASK;
|
|
data |= work_mode;
|
|
|
|
ipq_mii_write(WORK_MODE_OFFSET, data);
|
|
return;
|
|
}
|
|
|
|
static void ipq_qca8084_work_mode_get(qca8084_work_mode_t *work_mode)
|
|
{
|
|
u32 data = 0;
|
|
|
|
data = ipq_mii_read(WORK_MODE_OFFSET);
|
|
pr_debug("work mode reg is 0x%x\n", data);
|
|
|
|
*work_mode = data & QCA8084_WORK_MODE_MASK;
|
|
return;
|
|
}
|
|
|
|
static int ipq_qca8084_work_mode_init(int mac_mode0, int mac_mode1)
|
|
{
|
|
int ret = 0;
|
|
|
|
switch (mac_mode0) {
|
|
case EPORT_WRAPPER_SGMII_PLUS:
|
|
case EPORT_WRAPPER_SGMII_CHANNEL0:
|
|
break;
|
|
default:
|
|
/** not supported */
|
|
printf("%s %d Error: Unsupported mac_mode0 \n", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
switch (mac_mode1) {
|
|
case EPORT_WRAPPER_SGMII_PLUS:
|
|
case EPORT_WRAPPER_SGMII_CHANNEL0:
|
|
ipq_qca8084_work_mode_set(QCA8084_SWITCH_MODE);
|
|
break;
|
|
case EPORT_WRAPPER_MAX:
|
|
ipq_qca8084_work_mode_set(QCA8084_SWITCH_BYPASS_PORT5_MODE);
|
|
default:
|
|
printf("%s %d Error: Unsupported mac_mode1 \n", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int chip_ver_get(void)
|
|
{
|
|
int ret = 0;
|
|
u8 chip_ver;
|
|
u32 reg_val = ipq_mii_read(0);
|
|
|
|
chip_ver = (reg_val & 0xFF00) >> 8;
|
|
|
|
/*qca8084_start*/
|
|
switch (chip_ver) {
|
|
case QCA_VER_QCA8084:
|
|
ret = CHIP_QCA8084;
|
|
break;
|
|
default:
|
|
printf("Error: Unsupported chip \n");
|
|
ret = -1;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
bool qca8084_port_phy_connected(u32 port_id)
|
|
{
|
|
u32 cpu_bmp = 0x1;
|
|
if ((cpu_bmp & BIT(port_id)) || (port_id == PORT1) ||
|
|
(port_id == PORT5))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static void qca8084_port_txmac_status_set(u32 port_id, bool enable)
|
|
{
|
|
u32 reg, force, val = 0, tmp;
|
|
|
|
QCA8084_REG_ENTRY_GET(PORT_STATUS, port_id, (u8 *) (®));
|
|
|
|
if (true == enable)
|
|
{
|
|
val = 1;
|
|
}
|
|
else if (false == enable)
|
|
{
|
|
val = 0;
|
|
}
|
|
tmp = reg;
|
|
|
|
/* for those ports without PHY device we set MAC register */
|
|
if (false == qca8084_port_phy_connected(port_id))
|
|
{
|
|
SW_SET_REG_BY_FIELD(PORT_STATUS, LINK_EN, 0, reg);
|
|
SW_SET_REG_BY_FIELD(PORT_STATUS, TXMAC_EN, val, reg);
|
|
}
|
|
else
|
|
{
|
|
SW_GET_FIELD_BY_REG(PORT_STATUS, LINK_EN, force, reg);
|
|
if (force)
|
|
{
|
|
/* link isn't in force mode so can't set */
|
|
printf("%s %d Error: SW disable \n", __func__, __LINE__);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
SW_SET_REG_BY_FIELD(PORT_STATUS, TXMAC_EN, val, reg);
|
|
}
|
|
}
|
|
|
|
if (tmp == reg)
|
|
return;
|
|
|
|
QCA8084_REG_ENTRY_SET(PORT_STATUS, port_id, (u8 *) (®));
|
|
return;
|
|
}
|
|
|
|
static void qca8084_port_rxmac_status_set(u32 port_id, bool enable)
|
|
{
|
|
u32 reg = 0, force, val = 0, tmp;
|
|
|
|
QCA8084_REG_ENTRY_GET(PORT_STATUS, port_id, (u8 *) (®));
|
|
|
|
if (true == enable)
|
|
{
|
|
val = 1;
|
|
}
|
|
else if (false == enable)
|
|
{
|
|
val = 0;
|
|
}
|
|
tmp = reg;
|
|
|
|
/* for those ports without PHY device we set MAC register */
|
|
if (false == qca8084_port_phy_connected(port_id))
|
|
{
|
|
SW_SET_REG_BY_FIELD(PORT_STATUS, LINK_EN, 0, reg);
|
|
SW_SET_REG_BY_FIELD(PORT_STATUS, RXMAC_EN, val, reg);
|
|
}
|
|
else
|
|
{
|
|
SW_GET_FIELD_BY_REG(PORT_STATUS, LINK_EN, force, reg);
|
|
if (force)
|
|
{
|
|
/* link isn't in force mode so can't set */
|
|
printf("%s %d Error: SW disable \n", __func__, __LINE__);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
SW_SET_REG_BY_FIELD(PORT_STATUS, RXMAC_EN, val, reg);
|
|
}
|
|
}
|
|
if (tmp == reg)
|
|
return;
|
|
QCA8084_REG_ENTRY_SET(PORT_STATUS, port_id, (u8 *) (®));
|
|
return;
|
|
}
|
|
|
|
static void qca8084_port_rxfc_status_set(u32 port_id, bool enable)
|
|
{
|
|
u32 val = 0, reg, tmp;
|
|
|
|
if (true == enable)
|
|
{
|
|
val = 1;
|
|
}
|
|
else if (false == enable)
|
|
{
|
|
val = 0;
|
|
}
|
|
|
|
QCA8084_REG_ENTRY_GET(PORT_STATUS, port_id, (u8 *) (®));
|
|
tmp = reg;
|
|
|
|
SW_SET_REG_BY_FIELD(PORT_STATUS, RX_FLOW_EN, val, reg);
|
|
|
|
if ( tmp == reg)
|
|
return;
|
|
|
|
QCA8084_REG_ENTRY_SET(PORT_STATUS, port_id, (u8 *) (®));
|
|
return;
|
|
}
|
|
|
|
static void qca8084_port_txfc_status_set(u32 port_id, bool enable)
|
|
{
|
|
u32 val, reg = 0, tmp;
|
|
|
|
if (true == enable)
|
|
{
|
|
val = 1;
|
|
}
|
|
else if (false == enable)
|
|
{
|
|
val = 0;
|
|
}
|
|
|
|
QCA8084_REG_ENTRY_GET(PORT_STATUS, port_id, (u8 *) (®));
|
|
tmp = reg;
|
|
|
|
SW_SET_REG_BY_FIELD(PORT_STATUS, TX_FLOW_EN, val, reg);
|
|
SW_SET_REG_BY_FIELD(PORT_STATUS, TX_HALF_FLOW_EN, val, reg);
|
|
|
|
if (tmp == reg)
|
|
return;
|
|
QCA8084_REG_ENTRY_SET(PORT_STATUS, port_id, (u8 *) (®));
|
|
|
|
return;
|
|
}
|
|
|
|
static void qca8084_port_flowctrl_set(u32 port_id, bool enable)
|
|
{
|
|
qca8084_port_txfc_status_set(port_id, enable);
|
|
qca8084_port_rxfc_status_set(port_id, enable);
|
|
return;
|
|
}
|
|
|
|
static void qca8084_port_flowctrl_forcemode_set(u32 port_id, bool enable)
|
|
{
|
|
qca8084_port_txfc_forcemode[port_id] = enable;
|
|
qca8084_port_rxfc_forcemode[port_id] = enable;
|
|
return;
|
|
}
|
|
|
|
static void header_type_set(bool enable, u32 type)
|
|
{
|
|
u32 reg = 0;
|
|
|
|
QCA8084_REG_ENTRY_GET(HEADER_CTL, 0, (u8 *) (®));
|
|
|
|
if (true == enable)
|
|
{
|
|
if (0xffff < type)
|
|
{
|
|
printf("%s %d Error: Bad param \n", __func__, __LINE__);
|
|
return;
|
|
}
|
|
SW_SET_REG_BY_FIELD(HEADER_CTL, TYPE_LEN, 1, reg);
|
|
SW_SET_REG_BY_FIELD(HEADER_CTL, TYPE_VAL, type, reg);
|
|
}
|
|
else if (false == enable)
|
|
{
|
|
SW_SET_REG_BY_FIELD(HEADER_CTL, TYPE_LEN, 0, reg);
|
|
SW_SET_REG_BY_FIELD(HEADER_CTL, TYPE_VAL, 0, reg);
|
|
}
|
|
|
|
QCA8084_REG_ENTRY_SET(HEADER_CTL, 0, (u8 *) (®));
|
|
return;
|
|
}
|
|
|
|
static void port_rxhdr_mode_set(u32 port_id, port_header_mode_t mode)
|
|
{
|
|
u32 val = 0;
|
|
if (FAL_NO_HEADER_EN == mode)
|
|
{
|
|
val = 0;
|
|
}
|
|
else if (FAL_ONLY_MANAGE_FRAME_EN == mode)
|
|
{
|
|
val = 1;
|
|
}
|
|
else if (FAL_ALL_TYPE_FRAME_EN == mode)
|
|
{
|
|
val = 2;
|
|
}
|
|
else
|
|
{
|
|
printf("%s %d Error: Bad param \n", __func__, __LINE__);
|
|
return;
|
|
}
|
|
|
|
QCA8084_REG_FIELD_SET(PORT_HDR_CTL, port_id, RXHDR_MODE,(u8 *) (&val));
|
|
return;
|
|
}
|
|
|
|
static void port_txhdr_mode_set(u32 port_id, port_header_mode_t mode)
|
|
{
|
|
u32 val = 0;
|
|
if (FAL_NO_HEADER_EN == mode)
|
|
{
|
|
val = 0;
|
|
}
|
|
else if (FAL_ONLY_MANAGE_FRAME_EN == mode)
|
|
{
|
|
val = 1;
|
|
}
|
|
else if (FAL_ALL_TYPE_FRAME_EN == mode)
|
|
{
|
|
val = 2;
|
|
}
|
|
else
|
|
{
|
|
printf("%s %d Error: Bad param \n", __func__, __LINE__);
|
|
return;
|
|
}
|
|
|
|
QCA8084_REG_FIELD_SET(PORT_HDR_CTL, port_id, TXHDR_MODE, (u8 *) (&val));
|
|
return;
|
|
}
|
|
|
|
|
|
int qca8084_phy_get_status(u32 phy_id, struct port_phy_status *phy_status)
|
|
{
|
|
u16 phy_data;
|
|
|
|
phy_data = qca8084_phy_reg_read(phy_id, QCA8084_PHY_SPEC_STATUS);
|
|
|
|
/*get phy link status*/
|
|
if (phy_data & QCA8084_STATUS_LINK_PASS) {
|
|
phy_status->link_status = true;
|
|
}
|
|
else {
|
|
phy_status->link_status = false;
|
|
|
|
/*when link down, phy speed is set as 10M*/
|
|
phy_status->speed = FAL_SPEED_10;
|
|
return 0;
|
|
}
|
|
|
|
/*get phy speed*/
|
|
switch (phy_data & QCA8084_STATUS_SPEED_MASK) {
|
|
case QCA8084_STATUS_SPEED_2500MBS:
|
|
phy_status->speed = FAL_SPEED_2500;
|
|
break;
|
|
case QCA8084_STATUS_SPEED_1000MBS:
|
|
phy_status->speed = FAL_SPEED_1000;
|
|
break;
|
|
case QCA8084_STATUS_SPEED_100MBS:
|
|
phy_status->speed = FAL_SPEED_100;
|
|
break;
|
|
case QCA8084_STATUS_SPEED_10MBS:
|
|
phy_status->speed = FAL_SPEED_10;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
/*get phy duplex*/
|
|
if (phy_data & QCA8084_STATUS_FULL_DUPLEX) {
|
|
phy_status->duplex = FAL_FULL_DUPLEX;
|
|
} else {
|
|
phy_status->duplex = FAL_HALF_DUPLEX;
|
|
}
|
|
|
|
/* get phy flowctrl resolution status */
|
|
if (phy_data & QCA8084_PHY_RX_FLOWCTRL_STATUS) {
|
|
phy_status->rx_flowctrl = true;
|
|
} else {
|
|
phy_status->rx_flowctrl = false;
|
|
}
|
|
|
|
if (phy_data & QCA8084_PHY_TX_FLOWCTRL_STATUS) {
|
|
phy_status->tx_flowctrl = true;
|
|
} else {
|
|
phy_status->tx_flowctrl = false;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void qca8084_port_mac_dupex_set(u32 port_id, u32 duplex)
|
|
{
|
|
u32 reg_val = 0, tmp;
|
|
u32 duplex_val;
|
|
|
|
QCA8084_REG_ENTRY_GET(PORT_STATUS, port_id, (u8 *) (®_val));
|
|
tmp = reg_val;
|
|
|
|
if (FAL_HALF_DUPLEX == duplex) {
|
|
duplex_val = QCA8084_PORT_HALF_DUPLEX;
|
|
} else {
|
|
duplex_val = QCA8084_PORT_FULL_DUPLEX;
|
|
}
|
|
SW_SET_REG_BY_FIELD(PORT_STATUS, DUPLEX_MODE, duplex_val, reg_val);
|
|
|
|
if (tmp == reg_val)
|
|
return;
|
|
|
|
QCA8084_REG_ENTRY_SET(PORT_STATUS, port_id, (u8 *) (®_val));
|
|
return;
|
|
}
|
|
|
|
static void qca8084_port_duplex_set(u32 port_id, fal_port_duplex_t duplex,
|
|
phy_info_t * phy_info)
|
|
{
|
|
/* for those ports without PHY device we set MAC register */
|
|
if (false == qca8084_port_phy_connected(port_id))
|
|
{
|
|
qca8084_port_mac_dupex_set(port_id, duplex);
|
|
}
|
|
else
|
|
{
|
|
printf("%s %d Error: Duplex/Speed set for QCA8084 PORT1-4"
|
|
"is not implemented \n", __func__, __LINE__);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void qca8084_port_mac_speed_set(u32 port_id, u32 speed)
|
|
{
|
|
u32 reg_val = 0, tmp;
|
|
u32 speed_val;
|
|
|
|
QCA8084_REG_ENTRY_GET(PORT_STATUS, port_id, (u8 *) (®_val));
|
|
tmp = reg_val;
|
|
|
|
if (FAL_SPEED_10 == speed) {
|
|
speed_val = QCA8084_PORT_SPEED_10M;
|
|
} else if (FAL_SPEED_100 == speed) {
|
|
speed_val = QCA8084_PORT_SPEED_100M;
|
|
} else if (FAL_SPEED_1000 == speed) {
|
|
speed_val = QCA8084_PORT_SPEED_1000M;
|
|
} else if (FAL_SPEED_2500 == speed) {
|
|
speed_val = QCA8084_PORT_SPEED_2500M;
|
|
} else {
|
|
printf("%s %d Bad param \n",__func__, __LINE__);
|
|
return;
|
|
}
|
|
SW_SET_REG_BY_FIELD(PORT_STATUS, SPEED_MODE, speed_val, reg_val);
|
|
|
|
if (tmp == reg_val)
|
|
return;
|
|
|
|
QCA8084_REG_ENTRY_SET(PORT_STATUS, port_id, (u8 *) (®_val));
|
|
return;
|
|
}
|
|
|
|
static void qca8084_port_speed_set(u32 port_id, fal_port_speed_t speed,
|
|
phy_info_t * phy_info)
|
|
{
|
|
/* for those ports without PHY device we set MAC register */
|
|
if (false == qca8084_port_phy_connected(port_id))
|
|
{
|
|
qca8084_port_mac_speed_set(port_id, speed);
|
|
}
|
|
else
|
|
{
|
|
printf("%s %d Error: Duplex/Speed set for QCA8084 PORT1-4"
|
|
"is not implemented \n", __func__, __LINE__);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static int _qca8084_interface_mode_init(u32 port_id, u32 mac_mode,
|
|
phy_info_t * phy_info)
|
|
{
|
|
u32 uniphy_index = 0;
|
|
mac_config_t config;
|
|
u32 force_speed = FAL_SPEED_BUTT;
|
|
qca8084_work_mode_t work_mode = QCA8084_SWITCH_MODE;
|
|
|
|
if (phy_info->forced_speed) {
|
|
force_speed = phy_info->forced_speed;
|
|
|
|
qca8084_port_speed_set(port_id, force_speed, phy_info);
|
|
|
|
qca8084_port_duplex_set(port_id, FAL_FULL_DUPLEX, phy_info);
|
|
|
|
/* The clock parent need to be configured before initializing
|
|
* the interface mode. */
|
|
ipq_qca8084_work_mode_get(&work_mode);
|
|
qca8084_gcc_port_clk_parent_set(work_mode, port_id);
|
|
}
|
|
|
|
|
|
if(mac_mode == EPORT_WRAPPER_SGMII_PLUS)
|
|
config.mac_mode = QCA8084_MAC_MODE_SGMII_PLUS;
|
|
else if(mac_mode == EPORT_WRAPPER_SGMII_CHANNEL0)
|
|
config.mac_mode = QCA8084_MAC_MODE_SGMII;
|
|
else {
|
|
printf("%s %d Unsupported mac mode \n", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
/*get uniphy index*/
|
|
if(port_id == PORT0)
|
|
uniphy_index = QCA8084_UNIPHY_SGMII_1;
|
|
else if(port_id == PORT5)
|
|
uniphy_index = QCA8084_UNIPHY_SGMII_0;
|
|
else {
|
|
printf("%s %d Unsupported mac_mode \n", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
config.clock_mode = QCA8084_INTERFACE_CLOCK_MAC_MODE;
|
|
config.auto_neg = !(phy_info->forced_speed);
|
|
config.force_speed = force_speed;
|
|
|
|
qca8084_interface_sgmii_mode_set(uniphy_index, port_id, &config);
|
|
|
|
/*do sgmii function reset*/
|
|
pr_debug("ipg_tune reset and function reset\n");
|
|
qca8084_uniphy_sgmii_function_reset(uniphy_index);
|
|
return 0;
|
|
}
|
|
|
|
static int ipq_qca8084_interface_mode_init(u32 mac_mode0, u32 mac_mode1, phy_info_t * phy_info[])
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = _qca8084_interface_mode_init(PORT0, mac_mode0, phy_info[PORT0]);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = _qca8084_interface_mode_init(PORT5, mac_mode1, phy_info[PORT5]);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (phy_info[PORT0]->forced_speed) {
|
|
qca8084_port_txmac_status_set(PORT0, true);
|
|
qca8084_port_rxmac_status_set(PORT0, true);
|
|
}
|
|
|
|
if (phy_info[PORT5]->forced_speed) {
|
|
qca8084_port_txmac_status_set(PORT5, true);
|
|
qca8084_port_rxmac_status_set(PORT5, true);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
void port_link_update(u32 port_id, struct port_phy_status phy_status)
|
|
{
|
|
/* configure gcc uniphy and mac speed frequency*/
|
|
qca8084_port_speed_clock_set(port_id, phy_status.speed);
|
|
|
|
/* configure mac speed and duplex */
|
|
qca8084_port_mac_speed_set(port_id, phy_status.speed);
|
|
qca8084_port_mac_dupex_set(port_id, phy_status.duplex);
|
|
pr_debug("mht port %d link %d update speed %d duplex %d\n",
|
|
port_id, phy_status.speed,
|
|
phy_status.speed, phy_status.duplex);
|
|
if (phy_status.link_status == PORT_LINK_UP)
|
|
{
|
|
/* sync mac flowctrl */
|
|
if (qca8084_port_txfc_forcemode[port_id] != true) {
|
|
qca8084_port_txfc_status_set(port_id, phy_status.tx_flowctrl);
|
|
pr_debug("mht port %d link up update txfc %d\n",
|
|
port_id, phy_status.tx_flowctrl);
|
|
}
|
|
if (qca8084_port_rxfc_forcemode[port_id] != true) {
|
|
qca8084_port_rxfc_status_set(port_id, phy_status.rx_flowctrl);
|
|
pr_debug("mht port %d link up update rxfc %d\n",
|
|
port_id, phy_status.rx_flowctrl);
|
|
}
|
|
if (port_id != PORT5) {
|
|
/* enable eth phy clock */
|
|
qca8084_port_clk_en_set(port_id, QCA8084_CLK_TYPE_EPHY, true);
|
|
}
|
|
}
|
|
if (port_id != PORT5) {
|
|
if (phy_status.link_status == PORT_LINK_DOWN) {
|
|
/* disable eth phy clock */
|
|
qca8084_port_clk_en_set(port_id, QCA8084_CLK_TYPE_EPHY, false);
|
|
}
|
|
/* reset eth phy clock */
|
|
qca8084_port_clk_reset(port_id, QCA8084_CLK_TYPE_EPHY);
|
|
/* reset eth phy fifo */
|
|
qca8084_phy_function_reset(port_id);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void qca_switch_init(u32 port_bmp, u32 cpu_bmp, phy_info_t * phy_info[])
|
|
{
|
|
int i = 0;
|
|
|
|
port_bmp |= cpu_bmp;
|
|
while (port_bmp) {
|
|
pr_debug("configuring port: %d \n", i);
|
|
if (port_bmp & 1) {
|
|
qca8084_port_txmac_status_set(i, false);
|
|
qca8084_port_rxmac_status_set(i, false);
|
|
|
|
if (cpu_bmp & BIT(i)) {
|
|
qca8084_port_flowctrl_set(i, false);
|
|
qca8084_port_flowctrl_forcemode_set(i, true);
|
|
|
|
header_type_set(true, QCA8084_HEADER_TYPE_VAL);
|
|
port_rxhdr_mode_set(i, FAL_ONLY_MANAGE_FRAME_EN);
|
|
port_txhdr_mode_set(i, FAL_NO_HEADER_EN);
|
|
} else {
|
|
qca8084_port_flowctrl_set(i, true);
|
|
qca8084_port_flowctrl_forcemode_set(i, false);
|
|
}
|
|
}
|
|
port_bmp >>=1;
|
|
i++;
|
|
}
|
|
return;
|
|
}
|
|
|
|
int ipq_qca8084_link_update(phy_info_t * phy_info[])
|
|
{
|
|
struct port_phy_status phy_status = {0};
|
|
int rv, port_id, status = 1;
|
|
|
|
for (int i=PORT1; i<PORT5; i++) {
|
|
port_id = phy_info[i]->phy_address;
|
|
rv = qca8084_phy_get_status(port_id, &phy_status);
|
|
if (rv < 0) {
|
|
printf("%s %d failed get phy status of idx %d \n",
|
|
__func__, __LINE__, port_id);
|
|
return status;
|
|
}
|
|
|
|
printf("QCA8084-switch PORT%d %s Speed :%d %s duplex\n", port_id,
|
|
(phy_status.link_status?"Up":"Down"),
|
|
phy_status.speed, (phy_status.duplex?"Half":"Full"));
|
|
|
|
if (phy_status.link_status == PORT_LINK_DOWN) {
|
|
/* enable mac rx function */
|
|
qca8084_port_rxmac_status_set(port_id, false);
|
|
/* enable mac tx function */
|
|
qca8084_port_txmac_status_set(port_id, false);
|
|
/* update gcc, mac speed, mac duplex and phy stauts */
|
|
port_link_update(port_id, phy_status);
|
|
}
|
|
|
|
if (phy_status.link_status == PORT_LINK_UP) {
|
|
/* update gcc, mac speed, mac duplex and phy stauts */
|
|
port_link_update(port_id, phy_status);
|
|
/* enable mac tx function */
|
|
qca8084_port_txmac_status_set(port_id, true);
|
|
/* enable mac rx function */
|
|
qca8084_port_rxmac_status_set(port_id, true);
|
|
|
|
status = 0;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
int ipq_qca8084_hw_init(phy_info_t * phy_info[])
|
|
{
|
|
int ret = 0;
|
|
int mode0 = -1, mode1 = -1, node = -1;
|
|
qca8084_work_mode_t work_mode;
|
|
u32 port_bmp = 0x3e, cpu_bmp = 0x1;
|
|
|
|
int chip_type = chip_ver_get();
|
|
|
|
if (chip_type != CHIP_QCA8084) {
|
|
printf("Error: Unsupported chip_type \n");
|
|
return -1;
|
|
}
|
|
|
|
node = fdt_path_offset(gd->fdt_blob, "/ess-switch/qca8084_swt_info");
|
|
mode0 = fdtdec_get_uint(gd->fdt_blob, node, "switch_mac_mode0", -1);
|
|
if (mode0 < 0) {
|
|
printf("Error: switch_mac_mode0 not specified in dts\n");
|
|
return mode0;
|
|
}
|
|
|
|
mode1 = fdtdec_get_uint(gd->fdt_blob, node, "switch_mac_mode1", -1);
|
|
if (mode1 < 0) {
|
|
printf("Error: switch_mac_mode1 not specified in dts\n");
|
|
return mode1;
|
|
}
|
|
|
|
ipq_qca8084_switch_reset();
|
|
|
|
ret = ipq_qca8084_work_mode_init(mode0, mode1);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
qca_switch_init(port_bmp, cpu_bmp, phy_info);
|
|
|
|
ret = ipq_qca8084_interface_mode_init(mode0, mode1, phy_info);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
port_bmp |= cpu_bmp;
|
|
|
|
ipq_qca8084_work_mode_get(&work_mode);
|
|
qca8084_gcc_clock_init(work_mode, port_bmp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void ipq_qca8084_switch_hw_reset(int gpio)
|
|
{
|
|
unsigned int *switch_gpio_base =
|
|
(unsigned int *)GPIO_CONFIG_ADDR(gpio);
|
|
|
|
writel(0x203, switch_gpio_base);
|
|
writel(0x0, GPIO_IN_OUT_ADDR(gpio));
|
|
mdelay(500);
|
|
writel(0x2, GPIO_IN_OUT_ADDR(gpio));
|
|
}
|
|
#endif /* CONFIG_QCA8084_SWT_MODE */
|