207 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			207 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0+
 | 
						|
/*
 | 
						|
 * Copyright 2012 Calxeda, Inc.
 | 
						|
 */
 | 
						|
 | 
						|
#include <common.h>
 | 
						|
#include <ahci.h>
 | 
						|
#include <asm/io.h>
 | 
						|
 | 
						|
#define CPHY_MAP(dev, addr) ((((dev) & 0x1f) << 7) | (((addr) >> 9) & 0x7f))
 | 
						|
#define CPHY_ADDR(base, dev, addr) ((base) | (((addr) & 0x1ff) << 2))
 | 
						|
#define CPHY_BASE			0xfff58000
 | 
						|
#define CPHY_WIDTH			0x1000
 | 
						|
#define CPHY_DTE_XS			5
 | 
						|
#define CPHY_MII			31
 | 
						|
#define SERDES_CR_CTL			0x80a0
 | 
						|
#define SERDES_CR_ADDR			0x80a1
 | 
						|
#define SERDES_CR_DATA			0x80a2
 | 
						|
#define CR_BUSY				0x0001
 | 
						|
#define CR_START			0x0001
 | 
						|
#define CR_WR_RDN			0x0002
 | 
						|
#define CPHY_TX_INPUT_STS		0x2001
 | 
						|
#define CPHY_RX_INPUT_STS		0x2002
 | 
						|
#define CPHY_SATA_TX_OVERRIDE_BIT	0x8000
 | 
						|
#define CPHY_SATA_RX_OVERRIDE_BIT	0x4000
 | 
						|
#define CPHY_TX_INPUT_OVERRIDE		0x2004
 | 
						|
#define CPHY_RX_INPUT_OVERRIDE		0x2005
 | 
						|
#define SPHY_LANE			0x100
 | 
						|
#define SPHY_HALF_RATE			0x0001
 | 
						|
#define CPHY_SATA_DPLL_MODE		0x0700
 | 
						|
#define CPHY_SATA_DPLL_SHIFT		8
 | 
						|
#define CPHY_SATA_TX_ATTEN		0x1c00
 | 
						|
#define CPHY_SATA_TX_ATTEN_SHIFT	10
 | 
						|
 | 
						|
#define HB_SREG_SATA_ATTEN		0xfff3cf24
 | 
						|
 | 
						|
#define SATA_PORT_BASE			0xffe08000
 | 
						|
#define SATA_VERSIONR			0xf8
 | 
						|
#define SATA_HB_VERSION			0x3332302a
 | 
						|
 | 
						|
static u32 __combo_phy_reg_read(u8 phy, u8 dev, u32 addr)
 | 
						|
{
 | 
						|
	u32 data;
 | 
						|
	writel(CPHY_MAP(dev, addr), CPHY_BASE + 0x800 + CPHY_WIDTH * phy);
 | 
						|
	data = readl(CPHY_ADDR(CPHY_BASE + CPHY_WIDTH * phy, dev, addr));
 | 
						|
	return data;
 | 
						|
}
 | 
						|
 | 
						|
static void __combo_phy_reg_write(u8 phy, u8 dev, u32 addr, u32 data)
 | 
						|
{
 | 
						|
	writel(CPHY_MAP(dev, addr), CPHY_BASE + 0x800 + CPHY_WIDTH * phy);
 | 
						|
	writel(data, CPHY_ADDR(CPHY_BASE + CPHY_WIDTH * phy, dev, addr));
 | 
						|
}
 | 
						|
 | 
						|
static u32 combo_phy_read(u8 phy, u32 addr)
 | 
						|
{
 | 
						|
	u8 dev = CPHY_DTE_XS;
 | 
						|
	if (phy == 5)
 | 
						|
		dev = CPHY_MII;
 | 
						|
	while (__combo_phy_reg_read(phy, dev, SERDES_CR_CTL) & CR_BUSY)
 | 
						|
		udelay(5);
 | 
						|
	__combo_phy_reg_write(phy, dev, SERDES_CR_ADDR, addr);
 | 
						|
	__combo_phy_reg_write(phy, dev, SERDES_CR_CTL, CR_START);
 | 
						|
	while (__combo_phy_reg_read(phy, dev, SERDES_CR_CTL) & CR_BUSY)
 | 
						|
		udelay(5);
 | 
						|
	return __combo_phy_reg_read(phy, dev, SERDES_CR_DATA);
 | 
						|
}
 | 
						|
 | 
						|
static void combo_phy_write(u8 phy, u32 addr, u32 data)
 | 
						|
{
 | 
						|
	u8 dev = CPHY_DTE_XS;
 | 
						|
	if (phy == 5)
 | 
						|
		dev = CPHY_MII;
 | 
						|
	while (__combo_phy_reg_read(phy, dev, SERDES_CR_CTL) & CR_BUSY)
 | 
						|
		udelay(5);
 | 
						|
	__combo_phy_reg_write(phy, dev, SERDES_CR_ADDR, addr);
 | 
						|
	__combo_phy_reg_write(phy, dev, SERDES_CR_DATA, data);
 | 
						|
	__combo_phy_reg_write(phy, dev, SERDES_CR_CTL, CR_WR_RDN | CR_START);
 | 
						|
}
 | 
						|
 | 
						|
static void cphy_spread_spectrum_override(u8 phy, u8 lane, u32 val)
 | 
						|
{
 | 
						|
	u32 tmp;
 | 
						|
	tmp = combo_phy_read(phy, CPHY_RX_INPUT_STS + lane * SPHY_LANE);
 | 
						|
	tmp &= ~CPHY_SATA_RX_OVERRIDE_BIT;
 | 
						|
	combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
 | 
						|
 | 
						|
	tmp |= CPHY_SATA_RX_OVERRIDE_BIT;
 | 
						|
	combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
 | 
						|
 | 
						|
	tmp &= ~CPHY_SATA_DPLL_MODE;
 | 
						|
	tmp |= (val << CPHY_SATA_DPLL_SHIFT) & CPHY_SATA_DPLL_MODE;
 | 
						|
	combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
 | 
						|
}
 | 
						|
 | 
						|
static void cphy_tx_attenuation_override(u8 phy, u8 lane)
 | 
						|
{
 | 
						|
	u32 val;
 | 
						|
	u32 tmp;
 | 
						|
	u8  shift;
 | 
						|
 | 
						|
	shift = ((phy == 5) ? 4 : lane) * 4;
 | 
						|
 | 
						|
	val = (readl(HB_SREG_SATA_ATTEN) >> shift) & 0xf;
 | 
						|
 | 
						|
	if (val & 0x8)
 | 
						|
		return;
 | 
						|
 | 
						|
	tmp = combo_phy_read(phy, CPHY_TX_INPUT_STS + lane * SPHY_LANE);
 | 
						|
	tmp &= ~CPHY_SATA_TX_OVERRIDE_BIT;
 | 
						|
	combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
 | 
						|
 | 
						|
	tmp |= CPHY_SATA_TX_OVERRIDE_BIT;
 | 
						|
	combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
 | 
						|
 | 
						|
	tmp |= (val << CPHY_SATA_TX_ATTEN_SHIFT) & CPHY_SATA_TX_ATTEN;
 | 
						|
	combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
 | 
						|
}
 | 
						|
 | 
						|
static void cphy_disable_port_overrides(u8 port)
 | 
						|
{
 | 
						|
	u32 tmp;
 | 
						|
	u8 lane = 0, phy = 0;
 | 
						|
 | 
						|
	if (port == 0)
 | 
						|
		phy = 5;
 | 
						|
	else if (port < 5)
 | 
						|
		lane = port - 1;
 | 
						|
	else
 | 
						|
		return;
 | 
						|
	tmp = combo_phy_read(phy, CPHY_RX_INPUT_STS + lane * SPHY_LANE);
 | 
						|
	tmp &= ~CPHY_SATA_RX_OVERRIDE_BIT;
 | 
						|
	combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
 | 
						|
 | 
						|
	tmp = combo_phy_read(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE);
 | 
						|
	tmp &= ~CPHY_SATA_TX_OVERRIDE_BIT;
 | 
						|
	combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
 | 
						|
}
 | 
						|
 | 
						|
void cphy_disable_overrides(void)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	u32 port_map;
 | 
						|
 | 
						|
	port_map = readl(0xffe08000 + HOST_PORTS_IMPL);
 | 
						|
	for (i = 0; i < 5; i++) {
 | 
						|
		if (port_map & (1 << i))
 | 
						|
			cphy_disable_port_overrides(i);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void cphy_override_lane(u8 port)
 | 
						|
{
 | 
						|
	u32 tmp, k = 0;
 | 
						|
	u8 lane = 0, phy = 0;
 | 
						|
 | 
						|
	if (port == 0)
 | 
						|
		phy = 5;
 | 
						|
	else if (port < 5)
 | 
						|
		lane = port - 1;
 | 
						|
	else
 | 
						|
		return;
 | 
						|
 | 
						|
	do {
 | 
						|
		tmp = combo_phy_read(0, CPHY_RX_INPUT_STS +
 | 
						|
					lane * SPHY_LANE);
 | 
						|
	} while ((tmp & SPHY_HALF_RATE) && (k++ < 1000));
 | 
						|
	cphy_spread_spectrum_override(phy, lane, 3);
 | 
						|
	cphy_tx_attenuation_override(phy, lane);
 | 
						|
}
 | 
						|
 | 
						|
#define WAIT_MS_LINKUP	4
 | 
						|
 | 
						|
int ahci_link_up(struct ahci_uc_priv *probe_ent, int port)
 | 
						|
{
 | 
						|
	u32 tmp;
 | 
						|
	int j = 0;
 | 
						|
	u8 *port_mmio = (u8 *)probe_ent->port[port].port_mmio;
 | 
						|
	u32 is_highbank = readl(SATA_PORT_BASE + SATA_VERSIONR) ==
 | 
						|
				SATA_HB_VERSION ? 1 : 0;
 | 
						|
 | 
						|
	/* Bring up SATA link.
 | 
						|
	 * SATA link bringup time is usually less than 1 ms; only very
 | 
						|
	 * rarely has it taken between 1-2 ms. Never seen it above 2 ms.
 | 
						|
	 */
 | 
						|
	while (j < WAIT_MS_LINKUP) {
 | 
						|
		if (is_highbank && (j == 0)) {
 | 
						|
			cphy_disable_port_overrides(port);
 | 
						|
			writel(0x301, port_mmio + PORT_SCR_CTL);
 | 
						|
			udelay(1000);
 | 
						|
			writel(0x300, port_mmio + PORT_SCR_CTL);
 | 
						|
			udelay(1000);
 | 
						|
			cphy_override_lane(port);
 | 
						|
		}
 | 
						|
 | 
						|
		tmp = readl(port_mmio + PORT_SCR_STAT);
 | 
						|
		if ((tmp & 0xf) == 0x3)
 | 
						|
			return 0;
 | 
						|
		udelay(1000);
 | 
						|
		j++;
 | 
						|
 | 
						|
		if ((j == WAIT_MS_LINKUP) && (tmp & 0xf))
 | 
						|
			j = 0;	/* retry phy reset */
 | 
						|
	}
 | 
						|
	return 1;
 | 
						|
}
 |