diff --git a/Documentation/devicetree/bindings/misc/fsl,mpc83xx-serdes.txt b/Documentation/devicetree/bindings/misc/fsl,mpc83xx-serdes.txt new file mode 100644 index 0000000..64a9b5b --- /dev/null +++ b/Documentation/devicetree/bindings/misc/fsl,mpc83xx-serdes.txt @@ -0,0 +1,24 @@ +MPC83xx SerDes controller devices + +MPC83xx SoCs contain a built-in SerDes controller that determines which +protocols (SATA, PCI Express, SGMII, ...) are used on the system's serdes lines +and how the lines are configured. + +Required properties: +- compatible: must be "fsl,mpc83xx-serdes" +- reg: must point to the serdes controller's register map +- proto: selects for which protocol the serdes lines are configured. One of + "sata", "pex", "pex-x2", "sgmii" +- serdes-clk: determines the frequency the serdes lines are configured for. One + of 100, 125, 150. +- vdd: determines whether 1.0V core VDD is used or not + +Example: + +SERDES: serdes@e3000 { + reg = <0xe3000 0x200>; + compatible = "fsl,mpc83xx-serdes"; + proto = "pex"; + serdes-clk = <100>; + vdd; +}; diff --git a/MAINTAINERS b/MAINTAINERS index 9ff3bbe..237a022 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -529,6 +529,7 @@ F: include/dt-bindings/clk/mpc83xx-clk.h F: drivers/timer/mpc83xx_timer.c F: drivers/cpu/mpc83xx_cpu.c F: drivers/cpu/mpc83xx_cpu.h +F: drivers/misc/mpc83xx_serdes.c F: arch/powerpc/cpu/mpc83xx/ F: arch/powerpc/include/asm/arch-mpc83xx/ diff --git a/arch/powerpc/cpu/mpc83xx/serdes.c b/arch/powerpc/cpu/mpc83xx/serdes.c index 982a447..8242f95 100644 --- a/arch/powerpc/cpu/mpc83xx/serdes.c +++ b/arch/powerpc/cpu/mpc83xx/serdes.c @@ -8,6 +8,8 @@ * Author: Li Yang */ +#ifndef CONFIG_MPC83XX_SERDES + #include #include #include @@ -148,3 +150,5 @@ void fsl_setup_serdes(u32 offset, char proto, u32 rfcks, char vdd) tmp |= FSL_SRDSRSTCTL_RST; out_be32(regs + FSL_SRDSRSTCTL_OFFS, tmp); } + +#endif /* !CONFIG_MPC83XX_SERDES */ diff --git a/arch/powerpc/include/asm/fsl_mpc83xx_serdes.h b/arch/powerpc/include/asm/fsl_mpc83xx_serdes.h index e51d060..a02b599 100644 --- a/arch/powerpc/include/asm/fsl_mpc83xx_serdes.h +++ b/arch/powerpc/include/asm/fsl_mpc83xx_serdes.h @@ -6,6 +6,8 @@ #ifndef __FSL_MPC83XX_SERDES_H #define __FSL_MPC83XX_SERDES_H +#ifndef CONFIG_MPC83XX_SERDES + #include #define FSL_SERDES_CLK_100 (0 << 28) @@ -19,4 +21,6 @@ extern void fsl_setup_serdes(u32 offset, char proto, u32 rfcks, char vdd); +#endif /* !CONFIG_MPC83XX_SERDES */ + #endif /* __FSL_MPC83XX_SERDES_H */ diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c2b7cc1..bfa5c91 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -288,4 +288,11 @@ config GDSYS_IOEP depends on MISC help Support gdsys FPGA's IO endpoint driver. + +config MPC83XX_SERDES + bool "Enable MPC83xx serdes driver" + depends on MISC + help + Support for serdes found on MPC83xx SoCs. + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 32ef4a5..da4666f 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -55,3 +55,4 @@ obj-$(CONFIG_STM32MP_FUSE) += stm32mp_fuse.o obj-$(CONFIG_SYS_DPAA_QBMAN) += fsl_portals.o obj-$(CONFIG_GDSYS_IOEP) += gdsys_ioep.o obj-$(CONFIG_GDSYS_RXAUI_CTRL) += gdsys_rxaui_ctrl.o +obj-$(CONFIG_MPC83XX_SERDES) += mpc83xx_serdes.o diff --git a/drivers/misc/mpc83xx_serdes.c b/drivers/misc/mpc83xx_serdes.c new file mode 100644 index 0000000..d572dda --- /dev/null +++ b/drivers/misc/mpc83xx_serdes.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + * + * base on the MPC83xx serdes initialization, which is + * + * Copyright 2007,2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008 MontaVista Software, Inc. + */ + +#include +#include +#include +#include + +#include "mpc83xx_serdes.h" + +/** + * struct mpc83xx_serdes_priv - Private structure for MPC83xx serdes + * @regs: The device's register map + * @rfcks: Variable to keep the serdes reference clock selection set during + * initialization in (is or'd to every value written to SRDSCR4) + */ +struct mpc83xx_serdes_priv { + struct mpc83xx_serdes_regs *regs; + u32 rfcks; +}; + +/** + * setup_sata() - Configure the SerDes device to SATA mode + * @dev: The device to configure + */ +static void setup_sata(struct udevice *dev) +{ + struct mpc83xx_serdes_priv *priv = dev_get_priv(dev); + + /* Set and clear reset bits */ + setbits_be32(&priv->regs->srdsrstctl, SRDSRSTCTL_SATA_RESET); + udelay(1000); + clrbits_be32(&priv->regs->srdsrstctl, SRDSRSTCTL_SATA_RESET); + + /* Configure SRDSCR0 */ + clrsetbits_be32(&priv->regs->srdscr0, + SRDSCR0_TXEQA_MASK | SRDSCR0_TXEQE_MASK, + SRDSCR0_TXEQA_SATA | SRDSCR0_TXEQE_SATA); + + /* Configure SRDSCR1 */ + clrbits_be32(&priv->regs->srdscr1, SRDSCR1_PLLBW); + + /* Configure SRDSCR2 */ + clrsetbits_be32(&priv->regs->srdscr2, + SRDSCR2_SEIC_MASK, + SRDSCR2_SEIC_SATA); + + /* Configure SRDSCR3 */ + out_be32(&priv->regs->srdscr3, + SRDSCR3_KFR_SATA | SRDSCR3_KPH_SATA | + SRDSCR3_SDFM_SATA_PEX | SRDSCR3_SDTXL_SATA); + + /* Configure SRDSCR4 */ + out_be32(&priv->regs->srdscr4, priv->rfcks | SRDSCR4_PROT_SATA); +} + +/** + * setup_pex() - Configure the SerDes device to PCI Express mode + * @dev: The device to configure + * @type: The PCI Express type to configure for (x1 or x2) + */ +static void setup_pex(struct udevice *dev, enum pex_type type) +{ + struct mpc83xx_serdes_priv *priv = dev_get_priv(dev); + + /* Configure SRDSCR1 */ + setbits_be32(&priv->regs->srdscr1, SRDSCR1_PLLBW); + + /* Configure SRDSCR2 */ + clrsetbits_be32(&priv->regs->srdscr2, + SRDSCR2_SEIC_MASK, + SRDSCR2_SEIC_PEX); + + /* Configure SRDSCR3 */ + out_be32(&priv->regs->srdscr3, SRDSCR3_SDFM_SATA_PEX); + + /* Configure SRDSCR4 */ + if (type == PEX_X2) + out_be32(&priv->regs->srdscr4, + priv->rfcks | SRDSCR4_PROT_PEX | SRDSCR4_PLANE_X2); + else + out_be32(&priv->regs->srdscr4, + priv->rfcks | SRDSCR4_PROT_PEX); +} + +/** + * setup_sgmii() - Configure the SerDes device to SGMII mode + * @dev: The device to configure + */ +static void setup_sgmii(struct udevice *dev) +{ + struct mpc83xx_serdes_priv *priv = dev_get_priv(dev); + + /* Configure SRDSCR1 */ + clrbits_be32(&priv->regs->srdscr1, SRDSCR1_PLLBW); + + /* Configure SRDSCR2 */ + clrsetbits_be32(&priv->regs->srdscr2, + SRDSCR2_SEIC_MASK, + SRDSCR2_SEIC_SGMII); + + /* Configure SRDSCR3 */ + out_be32(&priv->regs->srdscr3, 0); + + /* Configure SRDSCR4 */ + out_be32(&priv->regs->srdscr4, priv->rfcks | SRDSCR4_PROT_SGMII); +} + +static int mpc83xx_serdes_probe(struct udevice *dev) +{ + struct mpc83xx_serdes_priv *priv = dev_get_priv(dev); + bool vdd; + const char *proto; + + priv->regs = map_sysmem(dev_read_addr(dev), + sizeof(struct mpc83xx_serdes_regs)); + + switch (dev_read_u32_default(dev, "serdes-clk", -1)) { + case 100: + priv->rfcks = SRDSCR4_RFCKS_100; + break; + case 125: + priv->rfcks = SRDSCR4_RFCKS_125; + break; + case 150: + priv->rfcks = SRDSCR4_RFCKS_150; + break; + default: + debug("%s: Could not read serdes clock value\n", dev->name); + return -EINVAL; + } + + vdd = dev_read_bool(dev, "vdd"); + + /* 1.0V corevdd */ + if (vdd) { + /* DPPE/DPPA = 0 */ + clrbits_be32(&priv->regs->srdscr0, SRDSCR0_DPP_1V2); + + /* VDD = 0 */ + clrbits_be32(&priv->regs->srdscr0, SRDSCR2_VDD_1V2); + } + + proto = dev_read_string(dev, "proto"); + + /* protocol specific configuration */ + if (!strcmp(proto, "sata")) { + setup_sata(dev); + } else if (!strcmp(proto, "pex")) { + setup_pex(dev, PEX_X1); + } else if (!strcmp(proto, "pex-x2")) { + setup_pex(dev, PEX_X2); + } else if (!strcmp(proto, "sgmii")) { + setup_sgmii(dev); + } else { + debug("%s: Invalid protocol value %s\n", dev->name, proto); + return -EINVAL; + } + + /* Do a software reset */ + setbits_be32(&priv->regs->srdsrstctl, SRDSRSTCTL_RST); + + return 0; +} + +static const struct udevice_id mpc83xx_serdes_ids[] = { + { .compatible = "fsl,mpc83xx-serdes" }, + { } +}; + +U_BOOT_DRIVER(mpc83xx_serdes) = { + .name = "mpc83xx_serdes", + .id = UCLASS_MISC, + .of_match = mpc83xx_serdes_ids, + .probe = mpc83xx_serdes_probe, + .priv_auto_alloc_size = sizeof(struct mpc83xx_serdes_priv), +}; diff --git a/drivers/misc/mpc83xx_serdes.h b/drivers/misc/mpc83xx_serdes.h new file mode 100644 index 0000000..89ea1db --- /dev/null +++ b/drivers/misc/mpc83xx_serdes.h @@ -0,0 +1,232 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +/** + * enum srdscr0_mask - Bit masks for SRDSCR0 (SerDes Control Register 0) + * @SRDSCR0_DPPA: Bitmask for the DPPA (diff pk-pk swing for lane A) + * field of the SRCSCR0 + * @SRDSCR0_DPPE: Bitmask for the DPPE (diff pk-pk swing for lane E) + * field of the SRCSCR0 + * @SRDSCR0_DPP_1V2: Combined bitmask to set diff pk-pk swing for both lanes + * @SRDSCR0_TXEQA_MASK: Bitmask for the TXEQA (transmit equalization for + * lane A) field of the SRCSCR0 + * @SRDSCR0_TXEQA_SATA: Bitmask to set the TXEQA to the value used for SATA + * @SRDSCR0_TXEQE_MASK: Bitmask for the TXEQE (transmit equalization for + * lane E) field of the SRCSCR0 + * @SRDSCR0_TXEQE_SATA: Bitmask to set the TXEQE to the value used for SATA + */ +enum srdscr0_mask { + SRDSCR0_DPPA = BIT(31 - 16), + SRDSCR0_DPPE = BIT(31 - 20), + SRDSCR0_DPP_1V2 = SRDSCR0_DPPE | SRDSCR0_DPPA, + + SRDSCR0_TXEQA_MASK = 0x00007000, + SRDSCR0_TXEQA_SATA = 0x00001000, + SRDSCR0_TXEQE_MASK = 0x00000700, + SRDSCR0_TXEQE_SATA = 0x00000100, +}; + +/** + * enum srdscr1_mask - Bit masks for SRDSCR1 (SerDes Control Register 1) + * @SRDSCR1_PLLBW: Bitmask for the PLLBW (PLL bandwidth) field of SRDSCR1 + */ +enum srdscr1_mask { + SRDSCR1_PLLBW = BIT(31 - 25), +}; + +/** + * enum srdscr2_mask - Bit masks for SRDSCR2 (SerDes Control Register 2) + * @SRDSCR2_VDD_1V2: Bit mask to to set the VDD field of the SCRSCR2 + * @SRDSCR2_SEICA_MASK: Bitmask for the SEICA (Receiver electrical idle + * detection control for lane A) field of the SRCSCR2 + * @SRDSCR2_SEICE_MASK: Bitmask for the SEICE (Receiver electrical idle + * detection control for lane E) field of the SRCSCR2 + * @SRDSCR2_SEIC_MASK: Combined bitmask to set the receiver electrical idle + * detection control for both lanes + * @SRDSCR2_SEICA_SATA: Bitmask to set the SEICA field to the value used for + * SATA + * @SRDSCR2_SEICE_SATA: Bitmask to set the SEICE field to the value used for + * SATA + * @SRDSCR2_SEIC_SATA: Combined bitmask to set the value of both SEIC fields + * to the value used for SATA + * @SRDSCR2_SEICA_PEX: Bitmask to set the SEICA field to the value used for + * PCI Express + * @SRDSCR2_SEICE_PEX: Bitmask to set the SEICE field to the value used for + * PCI Express + * @SRDSCR2_SEIC_PEX: Combined bitmask to set the value of both SEIC fields + * to the value used for PCI Express + * @SRDSCR2_SEICA_SGMII: Bitmask to set the SEICA field to the value used for + * SGMII + * @SRDSCR2_SEICE_SGMII: Bitmask to set the SEICE field to the value used for + * SGMII + * @SRDSCR2_SEIC_SGMII: Combined bitmask to set the value of both SEIC fields + * to the value used for SGMII + */ +enum srdscr2_mask { + SRDSCR2_VDD_1V2 = 0x00800000, + + SRDSCR2_SEICA_MASK = 0x00001c00, + SRDSCR2_SEICE_MASK = 0x0000001c, + SRDSCR2_SEIC_MASK = SRDSCR2_SEICA_MASK | SRDSCR2_SEICE_MASK, + + SRDSCR2_SEICA_SATA = 0x00001400, + SRDSCR2_SEICE_SATA = 0x00000014, + SRDSCR2_SEIC_SATA = SRDSCR2_SEICA_SATA | SRDSCR2_SEICE_SATA, + + SRDSCR2_SEICA_PEX = 0x00001000, + SRDSCR2_SEICE_PEX = 0x00000010, + SRDSCR2_SEIC_PEX = SRDSCR2_SEICA_PEX | SRDSCR2_SEICE_PEX, + + SRDSCR2_SEICA_SGMII = 0x00000100, + SRDSCR2_SEICE_SGMII = 0x00000001, + SRDSCR2_SEIC_SGMII = SRDSCR2_SEICA_SGMII | SRDSCR2_SEICE_SGMII, +}; + +/** + * enum srdscr3_mask - Bit masks for SRDSCR3 (SerDes Control Register 3) + * @SRDSCR3_KFRA_SATA: Bitmask to set the KFRA field of SRDSCR3 to the + * value used by SATA + * @SRDSCR3_KFRE_SATA: Bitmask to set the KFRE field of SRDSCR3 to the + * value used by SATA + * @SRDSCR3_KFR_SATA: Combined bitmask to set both KFR fields to the + * value used by SATA + * @SRDSCR3_KPHA_SATA: Bitmask to set the KPHA field of SRDSCR3 to the + * value used by SATA + * @SRDSCR3_KPHE_SATA: Bitmask to set the KPHE field of SRDSCR3 to the + * value used by SATA + * @SRDSCR3_KPH_SATA: Combined bitmask to set both KPH fields to the + * value used by SATA + * @SRDSCR3_SDFMA_SATA_PEX: Bitmask to set the SDFMA field of SRDSCR3 to the + * value used by SATA and PCI Express + * @SRDSCR3_SDFME_SATA_PEX: Bitmask to set the SDFME field of SRDSCR3 to the + * value used by SATA and PCI Express + * @SRDSCR3_SDFM_SATA_PEX: Combined bitmask to set both SDFM fields to the + * value used by SATA and PCI Express + * @SRDSCR3_SDTXLA_SATA: Bitmask to set the SDTXLA field of SRDSCR3 to the + * value used by SATA + * @SRDSCR3_SDTXLE_SATA: Bitmask to set the SDTXLE field of SRDSCR3 to the + * value used by SATA + * @SRDSCR3_SDTXL_SATA: Combined bitmask to set both SDTXL fields to the + * value used by SATA + * + * KFRA = 'Kfr' gain selection in the CDR for lane A + * KFRE = 'Kfr' gain selection in the CDR for lane E + * SDFMA = Bandwidth of digital filter for lane A + * SDFME = Bandwidth of digital filter for lane E + * SDTXLA = Lane A transmitter amplitude levels + * SDTXLE = Lane E transmitter amplitude levels + */ +enum srdscr3_mask { + SRDSCR3_KFRA_SATA = 0x10000000, + SRDSCR3_KFRE_SATA = 0x00100000, + SRDSCR3_KFR_SATA = SRDSCR3_KFRA_SATA | SRDSCR3_KFRE_SATA, + + SRDSCR3_KPHA_SATA = 0x04000000, + SRDSCR3_KPHE_SATA = 0x00040000, + SRDSCR3_KPH_SATA = SRDSCR3_KPHA_SATA | SRDSCR3_KPHE_SATA, + + SRDSCR3_SDFMA_SATA_PEX = 0x01000000, + SRDSCR3_SDFME_SATA_PEX = 0x00010000, + SRDSCR3_SDFM_SATA_PEX = SRDSCR3_SDFMA_SATA_PEX | SRDSCR3_SDFME_SATA_PEX, + + SRDSCR3_SDTXLA_SATA = 0x00000500, + SRDSCR3_SDTXLE_SATA = 0x00000005, + SRDSCR3_SDTXL_SATA = SRDSCR3_SDTXLA_SATA | SRDSCR3_SDTXLE_SATA, +}; + +/** + * enum srdscr4_mask - Bit masks for SRDSCR4 (SerDes Control Register 4) + * @SRDSCR4_PROTA_SATA: Bitmask to set the PROTA field of SRDSCR4 to the + * value used by SATA + * @SRDSCR4_PROTE_SATA: Bitmask to set the PROTE field of SRDSCR4 to the + * value used by SATA + * @SRDSCR4_PROT_SATA: Combined bitmask to set both PROT fields to the + * value used by SATA + * @SRDSCR4_PROTA_PEX: Bitmask to set the PROTA field of SRDSCR4 to the + * value used by PCI Express + * @SRDSCR4_PROTE_PEX: Bitmask to set the PROTE field of SRDSCR4 to the + * value used by PCI Express + * @SRDSCR4_PROT_PEX: Combined bitmask to set both PROT fields to the + * value used by PCI Express + * @SRDSCR4_PROTA_SGMII: Bitmask to set the PROTA field of SRDSCR4 to the + * value used by SGMII + * @SRDSCR4_PROTE_SGMII: Bitmask to set the PROTE field of SRDSCR4 to the + * value used by SGMII + * @SRDSCR4_PROT_SGMII: Combined bitmask to set both PROT fields to the + * value used by SGMII + * @SRDSCR4_PLANE_X2: Bitmask to set the PLANE field of SRDSCR4 + * @SRDSCR4_RFCKS_100: Bitmask to set the RFCKS field of SRDSCR4 to the + * value 100Mhz + * @SRDSCR4_RFCKS_125: Bitmask to set the RFCKS field of SRDSCR4 to the + * value 125Mhz + * @SRDSCR4_RFCKS_150: Bitmask to set the RFCKS field of SRDSCR4 to the + * value 150Mhz + * + * PROTA = Lane A protocol select + * PROTE = Lane E protocol select + * PLAME = Number of PCI Express lanes + */ +enum srdscr4_mask { + SRDSCR4_PROTA_SATA = 0x00000800, + SRDSCR4_PROTE_SATA = 0x00000008, + SRDSCR4_PROT_SATA = SRDSCR4_PROTA_SATA | SRDSCR4_PROTE_SATA, + + SRDSCR4_PROTA_PEX = 0x00000100, + SRDSCR4_PROTE_PEX = 0x00000001, + SRDSCR4_PROT_PEX = SRDSCR4_PROTA_PEX | SRDSCR4_PROTE_PEX, + + SRDSCR4_PROTA_SGMII = 0x00000500, + SRDSCR4_PROTE_SGMII = 0x00000005, + SRDSCR4_PROT_SGMII = SRDSCR4_PROTA_SGMII | SRDSCR4_PROTE_SGMII, + + SRDSCR4_PLANE_X2 = 0x01000000, + + SRDSCR4_RFCKS_100 = (0 << 28), + SRDSCR4_RFCKS_125 = (1 << 28), + SRDSCR4_RFCKS_150 = (3 << 28), +}; + +/** + * enum srdsrstctl_mask - Bit masks for SRDSRSTCTL (SerDes Reset Control Register) + * @SRDSRSTCTL_RST: Bitmask for the RST (Software reset) field of the + * SRDSRSTCTL + * @SRDSRSTCTL_SATA_RESET: Bitmask for the SATA_RESET (SATA reset) field of the + * SRDSRSTCTL + */ +enum srdsrstctl_mask { + SRDSRSTCTL_RST = 0x80000000, + SRDSRSTCTL_SATA_RESET = 0xf, +}; + +/** + * struct mpc83xx_serdes_regs - Register map of the SerDes controller + * @srdscr0: SerDes Control Register 0 + * @srdscr1: SerDes Control Register 1 + * @srdscr2: SerDes Control Register 2 + * @srdscr3: SerDes Control Register 3 + * @srdscr4: SerDes Control Register 4 + * @fill0: Reserved space in the register map + * @srdsrstctl: SerDes Reset Control Register + */ +struct mpc83xx_serdes_regs { + u32 srdscr0; + u32 srdscr1; + u32 srdscr2; + u32 srdscr3; + u32 srdscr4; + u8 fill0[12]; + u32 srdsrstctl; +}; + +/** + * enum pex_type - Types of PCI Express + * @PEX_X1: PCI Express in x1 mode + * @PEX_X2: PCI Express in x2 mode + */ +enum pex_type { + PEX_X1, + PEX_X2, +};