1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2012 Calxeda, Inc.
4 */
5
6#include <ahci.h>
7#include <asm/io.h>
8#include <linux/delay.h>
9
10#define CPHY_MAP(dev, addr) ((((dev) & 0x1f) << 7) | (((addr) >> 9) & 0x7f))
11#define CPHY_ADDR(base, dev, addr) ((base) | (((addr) & 0x1ff) << 2))
12#define CPHY_BASE			0xfff58000
13#define CPHY_WIDTH			0x1000
14#define CPHY_DTE_XS			5
15#define CPHY_MII			31
16#define SERDES_CR_CTL			0x80a0
17#define SERDES_CR_ADDR			0x80a1
18#define SERDES_CR_DATA			0x80a2
19#define CR_BUSY				0x0001
20#define CR_START			0x0001
21#define CR_WR_RDN			0x0002
22#define CPHY_TX_INPUT_STS		0x2001
23#define CPHY_RX_INPUT_STS		0x2002
24#define CPHY_SATA_TX_OVERRIDE_BIT	0x8000
25#define CPHY_SATA_RX_OVERRIDE_BIT	0x4000
26#define CPHY_TX_INPUT_OVERRIDE		0x2004
27#define CPHY_RX_INPUT_OVERRIDE		0x2005
28#define SPHY_LANE			0x100
29#define SPHY_HALF_RATE			0x0001
30#define CPHY_SATA_DPLL_MODE		0x0700
31#define CPHY_SATA_DPLL_SHIFT		8
32#define CPHY_SATA_TX_ATTEN		0x1c00
33#define CPHY_SATA_TX_ATTEN_SHIFT	10
34
35#define HB_SREG_SATA_ATTEN		0xfff3cf24
36
37#define SATA_PORT_BASE			0xffe08000
38#define SATA_VERSIONR			0xf8
39#define SATA_HB_VERSION			0x3332302a
40
41static u32 __combo_phy_reg_read(u8 phy, u8 dev, u32 addr)
42{
43	u32 data;
44	writel(CPHY_MAP(dev, addr), CPHY_BASE + 0x800 + CPHY_WIDTH * phy);
45	data = readl(CPHY_ADDR(CPHY_BASE + CPHY_WIDTH * phy, dev, addr));
46	return data;
47}
48
49static void __combo_phy_reg_write(u8 phy, u8 dev, u32 addr, u32 data)
50{
51	writel(CPHY_MAP(dev, addr), CPHY_BASE + 0x800 + CPHY_WIDTH * phy);
52	writel(data, CPHY_ADDR(CPHY_BASE + CPHY_WIDTH * phy, dev, addr));
53}
54
55static u32 combo_phy_read(u8 phy, u32 addr)
56{
57	u8 dev = CPHY_DTE_XS;
58	if (phy == 5)
59		dev = CPHY_MII;
60	while (__combo_phy_reg_read(phy, dev, SERDES_CR_CTL) & CR_BUSY)
61		udelay(5);
62	__combo_phy_reg_write(phy, dev, SERDES_CR_ADDR, addr);
63	__combo_phy_reg_write(phy, dev, SERDES_CR_CTL, CR_START);
64	while (__combo_phy_reg_read(phy, dev, SERDES_CR_CTL) & CR_BUSY)
65		udelay(5);
66	return __combo_phy_reg_read(phy, dev, SERDES_CR_DATA);
67}
68
69static void combo_phy_write(u8 phy, u32 addr, u32 data)
70{
71	u8 dev = CPHY_DTE_XS;
72	if (phy == 5)
73		dev = CPHY_MII;
74	while (__combo_phy_reg_read(phy, dev, SERDES_CR_CTL) & CR_BUSY)
75		udelay(5);
76	__combo_phy_reg_write(phy, dev, SERDES_CR_ADDR, addr);
77	__combo_phy_reg_write(phy, dev, SERDES_CR_DATA, data);
78	__combo_phy_reg_write(phy, dev, SERDES_CR_CTL, CR_WR_RDN | CR_START);
79}
80
81static void cphy_spread_spectrum_override(u8 phy, u8 lane, u32 val)
82{
83	u32 tmp;
84	tmp = combo_phy_read(phy, CPHY_RX_INPUT_STS + lane * SPHY_LANE);
85	tmp &= ~CPHY_SATA_RX_OVERRIDE_BIT;
86	combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
87
88	tmp |= CPHY_SATA_RX_OVERRIDE_BIT;
89	combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
90
91	tmp &= ~CPHY_SATA_DPLL_MODE;
92	tmp |= (val << CPHY_SATA_DPLL_SHIFT) & CPHY_SATA_DPLL_MODE;
93	combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
94}
95
96static void cphy_tx_attenuation_override(u8 phy, u8 lane)
97{
98	u32 val;
99	u32 tmp;
100	u8  shift;
101
102	shift = ((phy == 5) ? 4 : lane) * 4;
103
104	val = (readl(HB_SREG_SATA_ATTEN) >> shift) & 0xf;
105
106	if (val & 0x8)
107		return;
108
109	tmp = combo_phy_read(phy, CPHY_TX_INPUT_STS + lane * SPHY_LANE);
110	tmp &= ~CPHY_SATA_TX_OVERRIDE_BIT;
111	combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
112
113	tmp |= CPHY_SATA_TX_OVERRIDE_BIT;
114	combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
115
116	tmp |= (val << CPHY_SATA_TX_ATTEN_SHIFT) & CPHY_SATA_TX_ATTEN;
117	combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
118}
119
120static void cphy_disable_port_overrides(u8 port)
121{
122	u32 tmp;
123	u8 lane = 0, phy = 0;
124
125	if (port == 0)
126		phy = 5;
127	else if (port < 5)
128		lane = port - 1;
129	else
130		return;
131	tmp = combo_phy_read(phy, CPHY_RX_INPUT_STS + lane * SPHY_LANE);
132	tmp &= ~CPHY_SATA_RX_OVERRIDE_BIT;
133	combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
134
135	tmp = combo_phy_read(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE);
136	tmp &= ~CPHY_SATA_TX_OVERRIDE_BIT;
137	combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
138}
139
140void cphy_disable_overrides(void)
141{
142	int i;
143	u32 port_map;
144
145	port_map = readl(0xffe08000 + HOST_PORTS_IMPL);
146	for (i = 0; i < 5; i++) {
147		if (port_map & (1 << i))
148			cphy_disable_port_overrides(i);
149	}
150}
151
152static void cphy_override_lane(u8 port)
153{
154	u32 tmp, k = 0;
155	u8 lane = 0, phy = 0;
156
157	if (port == 0)
158		phy = 5;
159	else if (port < 5)
160		lane = port - 1;
161	else
162		return;
163
164	do {
165		tmp = combo_phy_read(0, CPHY_RX_INPUT_STS +
166					lane * SPHY_LANE);
167	} while ((tmp & SPHY_HALF_RATE) && (k++ < 1000));
168	cphy_spread_spectrum_override(phy, lane, 3);
169	cphy_tx_attenuation_override(phy, lane);
170}
171
172#define WAIT_MS_LINKUP	4
173
174int ahci_link_up(struct ahci_uc_priv *probe_ent, int port)
175{
176	u32 tmp;
177	int j = 0;
178	u8 *port_mmio = (u8 *)probe_ent->port[port].port_mmio;
179	u32 is_highbank = readl(SATA_PORT_BASE + SATA_VERSIONR) ==
180				SATA_HB_VERSION ? 1 : 0;
181
182	/* Bring up SATA link.
183	 * SATA link bringup time is usually less than 1 ms; only very
184	 * rarely has it taken between 1-2 ms. Never seen it above 2 ms.
185	 */
186	while (j < WAIT_MS_LINKUP) {
187		if (is_highbank && (j == 0)) {
188			cphy_disable_port_overrides(port);
189			writel(0x301, port_mmio + PORT_SCR_CTL);
190			udelay(1000);
191			writel(0x300, port_mmio + PORT_SCR_CTL);
192			udelay(1000);
193			cphy_override_lane(port);
194		}
195
196		tmp = readl(port_mmio + PORT_SCR_STAT);
197		if ((tmp & 0xf) == 0x3)
198			return 0;
199		udelay(1000);
200		j++;
201
202		if ((j == WAIT_MS_LINKUP) && (tmp & 0xf))
203			j = 0;	/* retry phy reset */
204	}
205	return 1;
206}
207