Add support for Cortina CS4315/CS4340 10G PHY. - This driver loads CS43xx firmware to initialize Cortina PHY. - To define macro CONFIG_PHY_CORTINA will enable this driver. - Cortina PHY has non-standard offset of PHY ID registers, so define own get_phy_id().
Signed-off-by: Shengzhou Liu <shengzhou....@freescale.com> --- v2: no change. drivers/net/phy/Makefile | 1 + drivers/net/phy/cortina.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/net/phy/phy.c | 3 + include/cortina.h | 73 +++++++++++++ include/phy.h | 2 + 5 files changed, 344 insertions(+) create mode 100644 drivers/net/phy/cortina.c create mode 100644 include/cortina.h diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index dbf7bf7..b091962 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_PHY_REALTEK) += realtek.o obj-$(CONFIG_PHY_SMSC) += smsc.o obj-$(CONFIG_PHY_TERANETICS) += teranetics.o obj-$(CONFIG_PHY_VITESSE) += vitesse.o +obj-$(CONFIG_PHY_CORTINA) += cortina.o diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c new file mode 100644 index 0000000..a023b6c --- /dev/null +++ b/drivers/net/phy/cortina.c @@ -0,0 +1,265 @@ +/* + * Cortina CS4315/CS4340 10G PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2014 Freescale Semiconductor, Inc. + * + */ + +#include <config.h> +#include <common.h> +#include <linux/ctype.h> +#include <linux/string.h> +#include <linux/err.h> +#include <phy.h> +#include <cortina.h> + +#ifndef CONFIG_PHYLIB_10G +#error The Cortina PHY needs 10G support +#endif + +struct cortina_reg_config cortina_reg_cfg[] = { + /* CS4315_enable_sr_mode */ + {VILLA_GLOBAL_MSEQCLKCTRL, 0x8004}, + {VILLA_MSEQ_OPTIONS, 0xf}, + {VILLA_MSEQ_PC, 0x0}, + {VILLA_MSEQ_BANKSELECT, 0x4}, + {VILLA_LINE_SDS_COMMON_SRX0_RX_CPA, 0x55}, + {VILLA_LINE_SDS_COMMON_SRX0_RX_LOOP_FILTER, 0x30}, + {VILLA_DSP_SDS_SERDES_SRX_DFE0_SELECT, 0x1}, + {VILLA_DSP_SDS_DSP_COEF_DFE0_SELECT, 0x2}, + {VILLA_LINE_SDS_COMMON_SRX0_RX_CPB, 0x2003}, + {VILLA_DSP_SDS_SERDES_SRX_FFE_DELAY_CTRL, 0xF047}, + {VILLA_MSEQ_ENABLE_MSB, 0x0000}, + {VILLA_MSEQ_SPARE21_LSB, 0x6}, + {VILLA_MSEQ_RESET_COUNT_LSB, 0x0}, + {VILLA_MSEQ_SPARE12_MSB, 0x0000}, + /* + * to invert the receiver path, uncomment the next line + * write (VILLA_MSEQ_SPARE12_MSB, 0x4000) + * + * SPARE2_LSB is used to configure the device while in sr mode to + * enable power savings and to use the optical module LOS signal. + * in power savings mode, the internal prbs checker can not be used. + * if the optical module LOS signal is used as an input to the micro + * code, then the micro code will wait until the optical module + * LOS = 0 before turning on the adaptive equalizer. + * Setting SPARE2_LSB bit 0 to 1 places the devie in power savings mode + * while setting bit 0 to 0 disables power savings mode. + * Setting SPARE2_LSB bit 2 to 0 configures the device to use the + * optical module LOS signal while setting bit 2 to 1 configures the + * device so that it will ignore the optical module LOS SPARE2_LSB = 0 + */ + + /* enable power savings, ignore optical module LOS */ + {VILLA_MSEQ_SPARE2_LSB, 0x5}, + + {VILLA_MSEQ_SPARE7_LSB, 0x1e}, + {VILLA_MSEQ_BANKSELECT, 0x4}, + {VILLA_MSEQ_SPARE9_LSB, 0x2}, + {VILLA_MSEQ_SPARE3_LSB, 0x0F53}, + {VILLA_MSEQ_SPARE3_MSB, 0x2006}, + {VILLA_MSEQ_SPARE8_LSB, 0x3FF7}, + {VILLA_MSEQ_SPARE8_MSB, 0x0A46}, + {VILLA_MSEQ_COEF8_FFE0_LSB, 0xD500}, + {VILLA_MSEQ_COEF8_FFE1_LSB, 0x0200}, + {VILLA_MSEQ_COEF8_FFE2_LSB, 0xBA00}, + {VILLA_MSEQ_COEF8_FFE3_LSB, 0x0100}, + {VILLA_MSEQ_COEF8_FFE4_LSB, 0x0300}, + {VILLA_MSEQ_COEF8_FFE5_LSB, 0x0300}, + {VILLA_MSEQ_COEF8_DFE0_LSB, 0x0700}, + {VILLA_MSEQ_COEF8_DFE0N_LSB, 0x0E00}, + {VILLA_MSEQ_COEF8_DFE1_LSB, 0x0B00}, + {VILLA_DSP_SDS_DSP_COEF_LARGE_LEAK, 0x2}, + {VILLA_DSP_SDS_SERDES_SRX_DAC_ENABLEB_LSB, 0xD000}, + {VILLA_MSEQ_POWER_DOWN_LSB, 0xFFFF}, + {VILLA_MSEQ_POWER_DOWN_MSB, 0x0}, + {VILLA_MSEQ_CAL_RX_SLICER, 0x80}, + {VILLA_DSP_SDS_SERDES_SRX_DAC_BIAS_SELECT1_MSB, 0x3f}, + {VILLA_GLOBAL_MSEQCLKCTRL, 0x4}, + {VILLA_MSEQ_OPTIONS, 0x7}, + + /* set up min value for ffe1 */ + {VILLA_MSEQ_COEF_INIT_SEL, 0x2}, + {VILLA_DSP_SDS_DSP_PRECODEDINITFFE21, 0x41}, + + /* CS4315_sr_rx_pre_eq_set_4in */ + {VILLA_GLOBAL_MSEQCLKCTRL, 0x8004}, + {VILLA_MSEQ_OPTIONS, 0xf}, + {VILLA_MSEQ_BANKSELECT, 0x4}, + {VILLA_MSEQ_PC, 0x0}, + + /* for lengths from 3.5 to 4.5inches */ + {VILLA_MSEQ_SERDES_PARAM_LSB, 0x0306}, + {VILLA_MSEQ_SPARE25_LSB, 0x0306}, + {VILLA_MSEQ_SPARE21_LSB, 0x2}, + {VILLA_MSEQ_SPARE23_LSB, 0x2}, + {VILLA_MSEQ_CAL_RX_DFE_EQ, 0x0}, + + {VILLA_GLOBAL_MSEQCLKCTRL, 0x4}, + {VILLA_MSEQ_OPTIONS, 0x7}, + + /* CS4315_rx_drive_4inch */ + /* for length 4inches */ + {VILLA_GLOBAL_VILLA2_COMPATIBLE, 0x0000}, + {VILLA_HOST_SDS_COMMON_STX0_TX_OUTPUT_CTRLA, 0x3023}, + {VILLA_LINE_SDS_COMMON_STX0_TX_OUTPUT_CTRLB, 0xc01E}, + + /* CS4315_tx_drive_4inch */ + /* for length 4inches */ + {VILLA_GLOBAL_VILLA2_COMPATIBLE, 0x0000}, + {VILLA_LINE_SDS_COMMON_STX0_TX_OUTPUT_CTRLA, 0x3023}, + {VILLA_LINE_SDS_COMMON_STX0_TX_OUTPUT_CTRLB, 0xc01E}, +}; + +void cs4340_upload_firmware(struct phy_device *phydev) +{ + char *pby_temp = NULL; + char line_temp[0x50] = {0}; + char reg_addr[0x50] = {0}; + char reg_data[0x50] = {0}; + int i = 0; + int line_cnt = 0; + int column_cnt = 0; + struct cortina_reg_config fw_temp; + + pby_temp = (char *)CONFIG_CORTINA_FW_ADDR; + + if (*pby_temp == 0xff) + printf("No firmware at %x for Cortina CS4340/CS4315\n", + CONFIG_CORTINA_FW_ADDR); + else + debug("Firmware exists at %x for Cortina CS4340/CS4315\n", + CONFIG_CORTINA_FW_ADDR); + + while (*pby_temp != 'Q') { + i = 0; + + while (*pby_temp != 0x0a) { + line_temp[i++] = *pby_temp++; + if (0x50 < i) + return; + } + + pby_temp++; /* skip '\n' */ + line_cnt++; + column_cnt = i; + line_temp[column_cnt] = '\0'; + + if (CONFIG_CORTINA_FW_LENGTH < line_cnt) + return; + + for (i = 0; i < column_cnt; i++) { + if (isspace(line_temp[i++])) + break; + } + + memcpy(reg_addr, line_temp, i); + memcpy(reg_data, &line_temp[i], column_cnt - i); + strim(reg_addr); + strim(reg_data); + fw_temp.reg_addr = (simple_strtoul(reg_addr, NULL, 0)) & 0xffff; + fw_temp.reg_value = (simple_strtoul(reg_data, NULL, 0)) & + 0xffff; + phy_write(phydev, 0x00, fw_temp.reg_addr, fw_temp.reg_value); + } +} + +int cs4340_phy_init(struct phy_device *phydev) +{ + int timeout = 100; /* 100ms */ + int reg_value = 0; + + /* step1: BIST test */ + phy_write(phydev, 0x00, VILLA_GLOBAL_MSEQCLKCTRL, 0x0004); + phy_write(phydev, 0x00, VILLA_GLOBAL_LINE_SOFT_RESET, 0x0000); + phy_write(phydev, 0x00, VILLA_GLOBAL_BIST_CONTROL, 0x0001); + while (--timeout) { + reg_value = phy_read(phydev, 0x00, VILLA_GLOBAL_BIST_STATUS); + if (reg_value & mseq_edc_bist_done) { + if (0 == (reg_value & mseq_edc_bist_fail)) + break; + } + udelay(1000); + } + + if (!timeout) { + printf("%s BIST mseq_edc_bist_done timeout!\n", __func__); + return 1; + } + + /* setp2: download ucode, this step will spend about 30 seconds. */ + cs4340_upload_firmware(phydev); + reg_value = phy_read(phydev, 0x00, VILLA_GLOBAL_DWNLD_CHECKSUM_STATUS); + if (0x0 != reg_value) { + printf("%s error.\n", __func__); + return 1; + } + + return 0; +} + +int cs4340_config(struct phy_device *phydev) +{ + cs4340_phy_init(phydev); + return 0; +} + +int cs4340_startup(struct phy_device *phydev) +{ + phydev->link = 1; + + /* For now just lie and say it's 10G all the time */ + phydev->speed = SPEED_10000; + phydev->duplex = DUPLEX_FULL; + return 0; +} + +struct phy_driver cs4340_driver = { + .name = "Cortina CS4315/CS4340", + .uid = PHY_UID_CS4340, + .mask = 0xfffffff0, + .features = PHY_10G_FEATURES, + .mmds = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | + MDIO_DEVS_PHYXS | MDIO_DEVS_AN | + MDIO_DEVS_VEND1 | MDIO_DEVS_VEND2), + .config = &cs4340_config, + .startup = &cs4340_startup, + .shutdown = &gen10g_shutdown, +}; + +int phy_cortina_init(void) +{ + phy_register(&cs4340_driver); + return 0; +} + +int get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id) +{ + int phy_reg; + +#if defined(CORTINA_PHY_ADDR1) || defined(CORTINA_PHY_ADDR2) + /* Cortina PHY has non-standard offset of PHY ID registers */ + if (addr == CORTINA_PHY_ADDR1 || addr == CORTINA_PHY_ADDR2) + phy_reg = bus->read(bus, addr, devad, VILLA_GLOBAL_CHIP_ID_LSB); + else +#endif + phy_reg = bus->read(bus, addr, devad, MII_PHYSID1); + + if (phy_reg < 0) + return -EIO; + + *phy_id = (phy_reg & 0xffff) << 16; + if (addr == CORTINA_PHY_ADDR1 || addr == CORTINA_PHY_ADDR2) + phy_reg = bus->read(bus, addr, devad, VILLA_GLOBAL_CHIP_ID_MSB); + else + phy_reg = bus->read(bus, addr, devad, MII_PHYSID2); + + if (phy_reg < 0) + return -EIO; + + *phy_id |= (phy_reg & 0xffff); + + return 0; +} diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 6d4c05f..7472aa3 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -481,6 +481,9 @@ int phy_init(void) #ifdef CONFIG_PHY_VITESSE phy_vitesse_init(); #endif +#ifdef CONFIG_PHY_CORTINA + phy_cortina_init(); +#endif return 0; } diff --git a/include/cortina.h b/include/cortina.h new file mode 100644 index 0000000..3e4ac62 --- /dev/null +++ b/include/cortina.h @@ -0,0 +1,73 @@ +/* + * Cortina PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2014 Freescale Semiconductor, Inc. + */ + +#ifndef _CORTINA_H_ +#define _CORTINA_H_ + +#define VILLA_GLOBAL_CHIP_ID_LSB 0x000 +#define VILLA_GLOBAL_CHIP_ID_MSB 0x001 +#define VILLA_GLOBAL_BIST_CONTROL 0x002 +#define VILLA_GLOBAL_BIST_STATUS 0x003 +#define VILLA_GLOBAL_LINE_SOFT_RESET 0x007 +#define VILLA_GLOBAL_DWNLD_CHECKSUM_STATUS 0x00B +#define VILLA_GLOBAL_MSEQCLKCTRL 0x00E +#define VILLA_MSEQ_OPTIONS 0x1D0 +#define VILLA_MSEQ_PC 0x1D3 +#define VILLA_MSEQ_BANKSELECT 0x1DF +#define VILLA_DSP_SDS_DSP_COEF_DFE0_SELECT 0x2DB +#define VILLA_DSP_SDS_SERDES_SRX_DFE0_SELECT 0x36E +#define VILLA_LINE_SDS_COMMON_SRX0_RX_LOOP_FILTER 0x403 +#define VILLA_LINE_SDS_COMMON_SRX0_RX_CPA 0x404 +#define VILLA_LINE_SDS_COMMON_SRX0_RX_CPB 0x405 +#define VILLA_DSP_SDS_SERDES_SRX_FFE_DELAY_CTRL 0x369 +#define VILLA_MSEQ_ENABLE_MSB 0x194 +#define VILLA_MSEQ_SPARE21_LSB 0x226 +#define VILLA_MSEQ_RESET_COUNT_LSB 0x1E0 +#define VILLA_MSEQ_SPARE12_MSB 0x215 +#define VILLA_MSEQ_SPARE2_LSB 0x200 +#define VILLA_MSEQ_SPARE7_LSB 0x20A +#define VILLA_MSEQ_SPARE9_LSB 0x20E +#define VILLA_MSEQ_SPARE3_LSB 0x202 +#define VILLA_MSEQ_SPARE3_MSB 0x203 +#define VILLA_MSEQ_SPARE8_LSB 0x20C +#define VILLA_MSEQ_SPARE8_MSB 0x20D +#define VILLA_MSEQ_COEF8_FFE0_LSB 0x1E2 +#define VILLA_MSEQ_COEF8_FFE1_LSB 0x1E4 +#define VILLA_MSEQ_COEF8_FFE2_LSB 0x1E6 +#define VILLA_MSEQ_COEF8_FFE3_LSB 0x1E8 +#define VILLA_MSEQ_COEF8_FFE4_LSB 0x1EA +#define VILLA_MSEQ_COEF8_FFE5_LSB 0x1EC +#define VILLA_MSEQ_COEF8_DFE0_LSB 0x1F0 +#define VILLA_MSEQ_COEF8_DFE0N_LSB 0x1EE +#define VILLA_MSEQ_COEF8_DFE1_LSB 0x1F2 +#define VILLA_DSP_SDS_DSP_COEF_LARGE_LEAK 0x2E2 +#define VILLA_DSP_SDS_SERDES_SRX_DAC_ENABLEB_LSB 0x360 +#define VILLA_MSEQ_POWER_DOWN_LSB 0x198 +#define VILLA_MSEQ_POWER_DOWN_MSB 0x199 +#define VILLA_MSEQ_CAL_RX_SLICER 0x1B8 +#define VILLA_DSP_SDS_SERDES_SRX_DAC_BIAS_SELECT1_MSB 0x365 +#define VILLA_MSEQ_COEF_INIT_SEL 0x1AE +#define VILLA_DSP_SDS_DSP_PRECODEDINITFFE21 0x26A +#define VILLA_MSEQ_SERDES_PARAM_LSB 0x195 +#define VILLA_MSEQ_SPARE25_LSB 0x22E +#define VILLA_MSEQ_SPARE23_LSB 0x22A +#define VILLA_MSEQ_CAL_RX_DFE_EQ 0x1BA +#define VILLA_GLOBAL_VILLA2_COMPATIBLE 0x030 +#define VILLA_HOST_SDS_COMMON_STX0_TX_OUTPUT_CTRLA 0x812 +#define VILLA_HOST_SDS_COMMON_STX0_TX_OUTPUT_CTRLB 0x813 +#define VILLA_LINE_SDS_COMMON_STX0_TX_OUTPUT_CTRLA 0x427 +#define VILLA_LINE_SDS_COMMON_STX0_TX_OUTPUT_CTRLB 0x428 + +#define mseq_edc_bist_done (0x1<<0) +#define mseq_edc_bist_fail (0x1<<8) + +struct cortina_reg_config { + unsigned short reg_addr; + unsigned short reg_value; +}; +#endif diff --git a/include/phy.h b/include/phy.h index 1f22fa1..73269a9 100644 --- a/include/phy.h +++ b/include/phy.h @@ -233,8 +233,10 @@ int phy_realtek_init(void); int phy_smsc_init(void); int phy_teranetics_init(void); int phy_vitesse_init(void); +int phy_cortina_init(void); /* PHY UIDs for various PHYs that are referenced in external code */ #define PHY_UID_TN2020 0x00a19410 +#define PHY_UID_CS4340 0x13e51002 #endif -- 1.8.0 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot