xref: /openbmc/u-boot/arch/arm/mach-omap2/pipe3-phy.c (revision e8f80a5a)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * TI PIPE3 PHY
4  *
5  * (C) Copyright 2013
6  * Texas Instruments, <www.ti.com>
7  */
8 
9 #include <common.h>
10 #include <sata.h>
11 #include <asm/arch/clock.h>
12 #include <asm/arch/sys_proto.h>
13 #include <asm/io.h>
14 #include <linux/errno.h>
15 #include "pipe3-phy.h"
16 
17 /* PLLCTRL Registers */
18 #define PLL_STATUS              0x00000004
19 #define PLL_GO                  0x00000008
20 #define PLL_CONFIGURATION1      0x0000000C
21 #define PLL_CONFIGURATION2      0x00000010
22 #define PLL_CONFIGURATION3      0x00000014
23 #define PLL_CONFIGURATION4      0x00000020
24 
25 #define PLL_REGM_MASK           0x001FFE00
26 #define PLL_REGM_SHIFT          9
27 #define PLL_REGM_F_MASK         0x0003FFFF
28 #define PLL_REGM_F_SHIFT        0
29 #define PLL_REGN_MASK           0x000001FE
30 #define PLL_REGN_SHIFT          1
31 #define PLL_SELFREQDCO_MASK     0x0000000E
32 #define PLL_SELFREQDCO_SHIFT    1
33 #define PLL_SD_MASK             0x0003FC00
34 #define PLL_SD_SHIFT            10
35 #define SET_PLL_GO              0x1
36 #define PLL_TICOPWDN            BIT(16)
37 #define PLL_LDOPWDN             BIT(15)
38 #define PLL_LOCK                0x2
39 #define PLL_IDLE                0x1
40 
41 /* PHY POWER CONTROL Register */
42 #define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK         0x003FC000
43 #define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT        0xE
44 
45 #define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK        0xFFC00000
46 #define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT       0x16
47 
48 #define OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON       0x3
49 #define OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF      0x0
50 
51 
52 #define PLL_IDLE_TIME   100     /* in milliseconds */
53 #define PLL_LOCK_TIME   100     /* in milliseconds */
54 
omap_pipe3_readl(void __iomem * addr,unsigned offset)55 static inline u32 omap_pipe3_readl(void __iomem *addr, unsigned offset)
56 {
57 	return __raw_readl(addr + offset);
58 }
59 
omap_pipe3_writel(void __iomem * addr,unsigned offset,u32 data)60 static inline void omap_pipe3_writel(void __iomem *addr, unsigned offset,
61 		u32 data)
62 {
63 	__raw_writel(data, addr + offset);
64 }
65 
omap_pipe3_get_dpll_params(struct omap_pipe3 * pipe3)66 static struct pipe3_dpll_params *omap_pipe3_get_dpll_params(struct omap_pipe3
67 									*pipe3)
68 {
69 	u32 rate;
70 	struct pipe3_dpll_map *dpll_map = pipe3->dpll_map;
71 
72 	rate = get_sys_clk_freq();
73 
74 	for (; dpll_map->rate; dpll_map++) {
75 		if (rate == dpll_map->rate)
76 			return &dpll_map->params;
77 	}
78 
79 	printf("%s: No DPLL configuration for %u Hz SYS CLK\n",
80 	       __func__, rate);
81 	return NULL;
82 }
83 
84 
omap_pipe3_wait_lock(struct omap_pipe3 * phy)85 static int omap_pipe3_wait_lock(struct omap_pipe3 *phy)
86 {
87 	u32 val;
88 	int timeout = PLL_LOCK_TIME;
89 
90 	do {
91 		mdelay(1);
92 		val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
93 		if (val & PLL_LOCK)
94 			break;
95 	} while (--timeout);
96 
97 	if (!(val & PLL_LOCK)) {
98 		printf("%s: DPLL failed to lock\n", __func__);
99 		return -EBUSY;
100 	}
101 
102 	return 0;
103 }
104 
omap_pipe3_dpll_program(struct omap_pipe3 * phy)105 static int omap_pipe3_dpll_program(struct omap_pipe3 *phy)
106 {
107 	u32                     val;
108 	struct pipe3_dpll_params *dpll_params;
109 
110 	dpll_params = omap_pipe3_get_dpll_params(phy);
111 	if (!dpll_params) {
112 		printf("%s: Invalid DPLL parameters\n", __func__);
113 		return -EINVAL;
114 	}
115 
116 	val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
117 	val &= ~PLL_REGN_MASK;
118 	val |= dpll_params->n << PLL_REGN_SHIFT;
119 	omap_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
120 
121 	val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
122 	val &= ~PLL_SELFREQDCO_MASK;
123 	val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT;
124 	omap_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
125 
126 	val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
127 	val &= ~PLL_REGM_MASK;
128 	val |= dpll_params->m << PLL_REGM_SHIFT;
129 	omap_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
130 
131 	val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4);
132 	val &= ~PLL_REGM_F_MASK;
133 	val |= dpll_params->mf << PLL_REGM_F_SHIFT;
134 	omap_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val);
135 
136 	val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3);
137 	val &= ~PLL_SD_MASK;
138 	val |= dpll_params->sd << PLL_SD_SHIFT;
139 	omap_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val);
140 
141 	omap_pipe3_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO);
142 
143 	return omap_pipe3_wait_lock(phy);
144 }
145 
omap_control_phy_power(struct omap_pipe3 * phy,int on)146 static void omap_control_phy_power(struct omap_pipe3 *phy, int on)
147 {
148 	u32 val, rate;
149 
150 	val = readl(phy->power_reg);
151 
152 	rate = get_sys_clk_freq();
153 	rate = rate/1000000;
154 
155 	if (on) {
156 		val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |
157 				OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK);
158 		val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON <<
159 			OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
160 		val |= rate <<
161 			OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
162 	} else {
163 		val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK;
164 		val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF <<
165 			OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
166 	}
167 
168 	writel(val, phy->power_reg);
169 }
170 
phy_pipe3_power_on(struct omap_pipe3 * phy)171 int phy_pipe3_power_on(struct omap_pipe3 *phy)
172 {
173 	int ret;
174 	u32 val;
175 
176 	/* Program the DPLL only if not locked */
177 	val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
178 	if (!(val & PLL_LOCK)) {
179 		ret = omap_pipe3_dpll_program(phy);
180 		if (ret)
181 			return ret;
182 	} else {
183 		/* else just bring it out of IDLE mode */
184 		val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
185 		if (val & PLL_IDLE) {
186 			val &= ~PLL_IDLE;
187 			omap_pipe3_writel(phy->pll_ctrl_base,
188 					  PLL_CONFIGURATION2, val);
189 			ret = omap_pipe3_wait_lock(phy);
190 			if (ret)
191 				return ret;
192 		}
193 	}
194 
195 	/* Power up the PHY */
196 	omap_control_phy_power(phy, 1);
197 
198 	return 0;
199 }
200 
phy_pipe3_power_off(struct omap_pipe3 * phy)201 int phy_pipe3_power_off(struct omap_pipe3 *phy)
202 {
203 	u32 val;
204 	int timeout = PLL_IDLE_TIME;
205 
206 	/* Power down the PHY */
207 	omap_control_phy_power(phy, 0);
208 
209 	/* Put DPLL in IDLE mode */
210 	val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
211 	val |= PLL_IDLE;
212 	omap_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
213 
214 	/* wait for LDO and Oscillator to power down */
215 	do {
216 		mdelay(1);
217 		val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
218 		if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN))
219 			break;
220 	} while (--timeout);
221 
222 	if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) {
223 		printf("%s: Failed to power down DPLL: PLL_STATUS 0x%x\n",
224 		       __func__, val);
225 		return -EBUSY;
226 	}
227 
228 	return 0;
229 }
230 
231