blob: 276e6004e5ee224005da9d3f0b47e34e9c7aa6e3 [file] [log] [blame]
Neil Armstrong45475512020-12-29 14:59:01 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Amlogic AXG MIPI + PCIE analog PHY driver
4 *
5 * Copyright (C) 2019 Remi Pommarel <repk@triplefau.lt>
6 * Copyright (C) 2020 BayLibre, SAS
7 * Author: Neil Armstrong <narmstrong@baylibre.com>
8 */
9
10#include <common.h>
11#include <log.h>
12#include <malloc.h>
13#include <asm/io.h>
14#include <bitfield.h>
15#include <dm.h>
16#include <errno.h>
17#include <generic-phy.h>
18#include <regmap.h>
19#include <syscon.h>
20#include <linux/delay.h>
21#include <power/regulator.h>
22#include <reset.h>
23#include <clk.h>
24#include <phy-mipi-dphy.h>
25
26#include <linux/bitops.h>
27#include <linux/compat.h>
28#include <linux/bitfield.h>
29
30#define HHI_MIPI_CNTL0 0x00
31#define HHI_MIPI_CNTL0_COMMON_BLOCK GENMASK(31, 28)
32#define HHI_MIPI_CNTL0_ENABLE BIT(29)
33#define HHI_MIPI_CNTL0_BANDGAP BIT(26)
34#define HHI_MIPI_CNTL0_DIF_REF_CTL1 GENMASK(25, 16)
35#define HHI_MIPI_CNTL0_DIF_REF_CTL0 GENMASK(15, 0)
36
37#define HHI_MIPI_CNTL1 0x04
38#define HHI_MIPI_CNTL1_CH0_CML_PDR_EN BIT(12)
39#define HHI_MIPI_CNTL1_LP_ABILITY GENMASK(5, 4)
40#define HHI_MIPI_CNTL1_LP_RESISTER BIT(3)
41#define HHI_MIPI_CNTL1_INPUT_SETTING BIT(2)
42#define HHI_MIPI_CNTL1_INPUT_SEL BIT(1)
43#define HHI_MIPI_CNTL1_PRBS7_EN BIT(0)
44
45#define HHI_MIPI_CNTL2 0x08
46#define HHI_MIPI_CNTL2_CH_PU GENMASK(31, 25)
47#define HHI_MIPI_CNTL2_CH_CTL GENMASK(24, 19)
48#define HHI_MIPI_CNTL2_CH0_DIGDR_EN BIT(18)
49#define HHI_MIPI_CNTL2_CH_DIGDR_EN BIT(17)
50#define HHI_MIPI_CNTL2_LPULPS_EN BIT(16)
51#define HHI_MIPI_CNTL2_CH_EN GENMASK(15, 11)
52#define HHI_MIPI_CNTL2_CH0_LP_CTL GENMASK(10, 1)
53
54#define DSI_LANE_0 (1 << 4)
55#define DSI_LANE_1 (1 << 3)
56#define DSI_LANE_CLK (1 << 2)
57#define DSI_LANE_2 (1 << 1)
58#define DSI_LANE_3 (1 << 0)
59#define DSI_LANE_MASK (0x1F)
60
61struct phy_meson_axg_mipi_pcie_analog_priv {
62 struct regmap *regmap;
63 struct phy_configure_opts_mipi_dphy config;
64 bool dsi_configured;
65 bool dsi_enabled;
66 bool powered;
67};
68
69static void phy_bandgap_enable(struct phy_meson_axg_mipi_pcie_analog_priv *priv)
70{
71 regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
72 HHI_MIPI_CNTL0_BANDGAP, HHI_MIPI_CNTL0_BANDGAP);
73
74 regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
75 HHI_MIPI_CNTL0_ENABLE, HHI_MIPI_CNTL0_ENABLE);
76}
77
78static void phy_bandgap_disable(struct phy_meson_axg_mipi_pcie_analog_priv *priv)
79{
80 regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
81 HHI_MIPI_CNTL0_BANDGAP, 0);
82 regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
83 HHI_MIPI_CNTL0_ENABLE, 0);
84}
85
86static void phy_dsi_analog_enable(struct phy_meson_axg_mipi_pcie_analog_priv *priv)
87{
88 u32 reg;
89
90 regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
91 HHI_MIPI_CNTL0_DIF_REF_CTL1,
92 FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0x1b8));
93 regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
94 BIT(31), BIT(31));
95 regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
96 HHI_MIPI_CNTL0_DIF_REF_CTL0,
97 FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL0, 0x8));
98
99 regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0x001e);
100
101 regmap_write(priv->regmap, HHI_MIPI_CNTL2,
102 (0x26e0 << 16) | (0x459 << 0));
103
104 reg = DSI_LANE_CLK;
105 switch (priv->config.lanes) {
106 case 4:
107 reg |= DSI_LANE_3;
108 fallthrough;
109 case 3:
110 reg |= DSI_LANE_2;
111 fallthrough;
112 case 2:
113 reg |= DSI_LANE_1;
114 fallthrough;
115 case 1:
116 reg |= DSI_LANE_0;
117 break;
118 default:
119 reg = 0;
120 }
121
122 regmap_update_bits(priv->regmap, HHI_MIPI_CNTL2,
123 HHI_MIPI_CNTL2_CH_EN,
124 FIELD_PREP(HHI_MIPI_CNTL2_CH_EN, reg));
125
126 priv->dsi_enabled = true;
127}
128
129static void phy_dsi_analog_disable(struct phy_meson_axg_mipi_pcie_analog_priv *priv)
130{
131 regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
132 HHI_MIPI_CNTL0_DIF_REF_CTL1,
133 FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0));
134 regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, BIT(31), 0);
135 regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
136 HHI_MIPI_CNTL0_DIF_REF_CTL1, 0);
137
138 regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0x6);
139
140 regmap_write(priv->regmap, HHI_MIPI_CNTL2, 0x00200000);
141
142 priv->dsi_enabled = false;
143}
144
145static int phy_meson_axg_mipi_pcie_analog_configure(struct phy *phy, void *params)
146{
147 struct udevice *dev = phy->dev;
148 struct phy_meson_axg_mipi_pcie_analog_priv *priv = dev_get_priv(dev);
149 struct phy_configure_opts_mipi_dphy *config = params;
150 int ret;
151
152 ret = phy_mipi_dphy_config_validate(config);
153 if (ret)
154 return ret;
155
156 memcpy(&priv->config, config, sizeof(priv->config));
157
158 priv->dsi_configured = true;
159
160 /* If PHY was already powered on, setup the DSI analog part */
161 if (priv->powered) {
162 /* If reconfiguring, disable & reconfigure */
163 if (priv->dsi_enabled)
164 phy_dsi_analog_disable(priv);
165
166 udelay(100);
167
168 phy_dsi_analog_enable(priv);
169 }
170
171 return 0;
172}
173
174static int phy_meson_axg_mipi_pcie_analog_power_on(struct phy *phy)
175{
176 struct udevice *dev = phy->dev;
177 struct phy_meson_axg_mipi_pcie_analog_priv *priv = dev_get_priv(dev);
178
179 phy_bandgap_enable(priv);
180
181 if (priv->dsi_configured)
182 phy_dsi_analog_enable(priv);
183
184 priv->powered = true;
185
186 return 0;
187}
188
189static int phy_meson_axg_mipi_pcie_analog_power_off(struct phy *phy)
190{
191 struct udevice *dev = phy->dev;
192 struct phy_meson_axg_mipi_pcie_analog_priv *priv = dev_get_priv(dev);
193
194 phy_bandgap_disable(priv);
195
196 if (priv->dsi_enabled)
197 phy_dsi_analog_disable(priv);
198
199 priv->powered = false;
200
201 return 0;
202}
203
204struct phy_ops meson_axg_mipi_pcie_analog_ops = {
205 .power_on = phy_meson_axg_mipi_pcie_analog_power_on,
206 .power_off = phy_meson_axg_mipi_pcie_analog_power_off,
207 .configure = phy_meson_axg_mipi_pcie_analog_configure,
208};
209
210int meson_axg_mipi_pcie_analog_probe(struct udevice *dev)
211{
212 struct phy_meson_axg_mipi_pcie_analog_priv *priv = dev_get_priv(dev);
213
214 priv->regmap = syscon_node_to_regmap(dev_get_parent(dev)->node);
215 if (IS_ERR(priv->regmap))
216 return PTR_ERR(priv->regmap);
217
218 return 0;
219}
220
221static const struct udevice_id meson_axg_mipi_pcie_analog_ids[] = {
222 { .compatible = "amlogic,axg-mipi-pcie-analog-phy" },
223 { }
224};
225
226U_BOOT_DRIVER(meson_axg_mipi_pcie_analog) = {
227 .name = "meson_axg_mipi_pcie_analog",
228 .id = UCLASS_PHY,
229 .of_match = meson_axg_mipi_pcie_analog_ids,
230 .probe = meson_axg_mipi_pcie_analog_probe,
231 .ops = &meson_axg_mipi_pcie_analog_ops,
232 .priv_auto_alloc_size = sizeof(struct phy_meson_axg_mipi_pcie_analog_priv),
233};