9
0
Fork 0
barebox/drivers/pci/pci-mvebu-phy.c

209 lines
6.4 KiB
C

/*
* SoC specific PCIe PHY setup for Marvell MVEBU SoCs
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*
* based on Marvell BSP code (C) Marvell International Ltd.
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <common.h>
#include <of.h>
#include <of_address.h>
#include "pci-mvebu.h"
static u32 mvebu_pcie_phy_indirect(void __iomem *phybase, u8 lane,
u8 off, u16 val, bool is_read)
{
u32 reg = (lane << 24) | (off << 16) | val;
if (is_read)
reg |= BIT(31);
writel(reg, phybase);
return (is_read) ? readl(phybase) & 0xffff : 0;
}
static inline u32 mvebu_pcie_phy_read(void __iomem *phybase, u8 lane,
u8 off)
{
return mvebu_pcie_phy_indirect(phybase, lane, off, 0, true);
}
static inline void mvebu_pcie_phy_write(void __iomem *phybase, u8 lane,
u8 off, u16 val)
{
mvebu_pcie_phy_indirect(phybase, lane, off, val, false);
}
/* PCIe registers */
#define ARMADA_370_XP_PCIE_LINK_CAPS 0x6c
#define MAX_LINK_WIDTH_MASK MAX_LINK_WIDTH(0x3f)
#define MAX_LINK_WIDTH(x) ((x) << 4)
#define MAX_LINK_SPEED_MASK 0xf
#define MAX_LINK_SPEED_5G0 0x2
#define MAX_LINK_SPEED_2G5 0x1
#define ARMADA_370_XP_PHY_OFFSET 0x1b00
/* System Control registers */
#define ARMADA_370_XP_SOC_CTRL 0x04
#define PCIE1_QUADX1_EN BIT(8) /* Armada XP */
#define PCIE0_QUADX1_EN BIT(7) /* Armada XP */
#define PCIE0_EN BIT(0)
#define ARMADA_370_XP_SERDES03_SEL 0x70
#define ARMADA_370_XP_SERDES47_SEL 0x74
#define SERDES(x, v) ((v) << ((x) * 0x4))
#define SERDES_MASK(x) SERDES((x), 0xf)
int armada_370_phy_setup(struct mvebu_pcie *pcie)
{
struct device_node *np = of_find_compatible_node(NULL, NULL,
"marvell,armada-370-xp-system-controller");
void __iomem *sysctrl = of_iomap(np, 0);
void __iomem *phybase = pcie->base + ARMADA_370_XP_PHY_OFFSET;
u32 reg;
if (!sysctrl)
return -ENODEV;
/* Enable PEX */
reg = readl(sysctrl + ARMADA_370_XP_SOC_CTRL);
reg |= PCIE0_EN << pcie->port;
writel(reg, sysctrl + ARMADA_370_XP_SOC_CTRL);
/* Set SERDES selector */
reg = readl(sysctrl + ARMADA_370_XP_SERDES03_SEL);
reg &= ~SERDES_MASK(pcie->port);
reg |= SERDES(pcie->port, 0x1);
writel(reg, sysctrl + ARMADA_370_XP_SERDES03_SEL);
/* BTS #232 - PCIe clock (undocumented) */
writel(0x00000077, sysctrl + 0x2f0);
/* Set x1 Link Capability */
reg = readl(pcie->base + ARMADA_370_XP_PCIE_LINK_CAPS);
reg &= ~(MAX_LINK_WIDTH_MASK | MAX_LINK_SPEED_MASK);
reg |= MAX_LINK_WIDTH(0x1) | MAX_LINK_SPEED_5G0;
writel(reg, pcie->base + ARMADA_370_XP_PCIE_LINK_CAPS);
/* PEX pipe configuration */
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc1, 0x0025);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc3, 0x000f);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc8, 0x0005);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xd0, 0x0100);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xd1, 0x3014);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc5, 0x011f);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x80, 0x1000);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x81, 0x0011);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x0f, 0x2a21);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x45, 0x00df);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x4f, 0x6219);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x01, 0xfc60);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x46, 0x0000);
reg = mvebu_pcie_phy_read(phybase, pcie->lane, 0x48) & ~0x4;
mvebu_pcie_phy_write(phybase, pcie->lane, 0x48, reg & 0xffff);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x02, 0x0040);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc1, 0x0024);
mdelay(15);
return 0;
}
/*
* MV78230: 2 PCIe units Gen2.0, one unit 1x4 or 4x1, one unit 1x1
* MV78260: 3 PCIe units Gen2.0, two units 1x4 or 4x1, one unit 1x1/1x4
* MV78460: 4 PCIe units Gen2.0, two units 1x4 or 4x1, two units 1x1/1x4
*/
#define ARMADA_XP_COMM_PHY_REFCLK_ALIGN 0xf8
#define REFCLK_ALIGN(x) (0xf << ((x) * 0x4))
int armada_xp_phy_setup(struct mvebu_pcie *pcie)
{
struct device_node *np = of_find_compatible_node(NULL, NULL,
"marvell,armada-370-xp-system-controller");
void __iomem *sysctrl = of_iomap(np, 0);
void __iomem *phybase = pcie->base + ARMADA_370_XP_PHY_OFFSET;
u32 serdes_off = (pcie->port < 2) ? ARMADA_370_XP_SERDES03_SEL :
ARMADA_370_XP_SERDES47_SEL;
bool single_x4 = (pcie->lane_mask == 0xf);
u32 reg, mask;
if (!sysctrl)
return -ENODEV;
/* Prepare PEX */
reg = readl(sysctrl + ARMADA_370_XP_SOC_CTRL);
reg &= ~(PCIE0_EN << pcie->port);
writel(reg, sysctrl + ARMADA_370_XP_SOC_CTRL);
if (pcie->port < 2) {
mask = PCIE0_QUADX1_EN << pcie->port;
if (single_x4)
reg &= ~mask;
else
reg |= mask;
}
reg |= PCIE0_EN << pcie->port;
writel(reg, sysctrl + ARMADA_370_XP_SOC_CTRL);
/* Set SERDES selector */
reg = readl(sysctrl + serdes_off);
for (mask = pcie->lane_mask; mask;) {
u32 l = ffs(mask)-1;
u32 off = 4 * (pcie->port % 2);
reg &= ~SERDES_MASK(off + l);
reg |= SERDES(off + l, 0x1);
mask &= ~BIT(l);
}
reg &= ~SERDES_MASK(pcie->port % 2);
reg |= SERDES(pcie->port % 2, 0x1);
writel(reg, sysctrl + serdes_off);
/* Reference Clock Alignment for 1x4 */
reg = readl(sysctrl + ARMADA_XP_COMM_PHY_REFCLK_ALIGN);
if (single_x4)
reg |= REFCLK_ALIGN(pcie->port);
else
reg &= ~REFCLK_ALIGN(pcie->port);
writel(reg, sysctrl + ARMADA_XP_COMM_PHY_REFCLK_ALIGN);
/* Set x1/x4 Link Capability */
reg = readl(pcie->base + ARMADA_370_XP_PCIE_LINK_CAPS);
reg &= ~(MAX_LINK_WIDTH_MASK | MAX_LINK_SPEED_MASK);
if (single_x4)
reg |= MAX_LINK_WIDTH(0x4);
else
reg |= MAX_LINK_WIDTH(0x1);
reg |= MAX_LINK_SPEED_5G0;
writel(reg, pcie->base + ARMADA_370_XP_PCIE_LINK_CAPS);
/* PEX pipe configuration */
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc1, 0x0025);
if (single_x4) {
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc2, 0x0200);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc3, 0x0001);
} else {
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc2, 0x0000);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc3, 0x000f);
}
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc8, 0x0005);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x01, 0xfc60);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x46, 0x0000);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x02, 0x0040);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc1, 0x0024);
if (single_x4)
mvebu_pcie_phy_write(phybase, pcie->lane, 0x48, 0x1080);
else
mvebu_pcie_phy_write(phybase, pcie->lane, 0x48, 0x9080);
mdelay(15);
return 0;
}