Blame view
drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
9.15 KB
133add5b5 drm/sun4i: Add Al... |
1 2 3 4 5 6 7 8 9 10 |
// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2016 Allwinnertech Co., Ltd. * Copyright (C) 2017-2018 Bootlin * * Maxime Ripard <maxime.ripard@free-electrons.com> */ #include <linux/bitops.h> #include <linux/clk.h> |
bb3b6fcb6 sun6i: dsi: Conve... |
11 |
#include <linux/module.h> |
133add5b5 drm/sun4i: Add Al... |
12 |
#include <linux/of_address.h> |
bb3b6fcb6 sun6i: dsi: Conve... |
13 |
#include <linux/platform_device.h> |
133add5b5 drm/sun4i: Add Al... |
14 15 |
#include <linux/regmap.h> #include <linux/reset.h> |
bb3b6fcb6 sun6i: dsi: Conve... |
16 17 |
#include <linux/phy/phy.h> #include <linux/phy/phy-mipi-dphy.h> |
133add5b5 drm/sun4i: Add Al... |
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
#define SUN6I_DPHY_GCTL_REG 0x00 #define SUN6I_DPHY_GCTL_LANE_NUM(n) ((((n) - 1) & 3) << 4) #define SUN6I_DPHY_GCTL_EN BIT(0) #define SUN6I_DPHY_TX_CTL_REG 0x04 #define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT BIT(28) #define SUN6I_DPHY_TX_TIME0_REG 0x10 #define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n) (((n) & 0xff) << 24) #define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n) (((n) & 0xff) << 16) #define SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(n) ((n) & 0xff) #define SUN6I_DPHY_TX_TIME1_REG 0x14 #define SUN6I_DPHY_TX_TIME1_CLK_POST(n) (((n) & 0xff) << 24) #define SUN6I_DPHY_TX_TIME1_CLK_PRE(n) (((n) & 0xff) << 16) #define SUN6I_DPHY_TX_TIME1_CLK_ZERO(n) (((n) & 0xff) << 8) #define SUN6I_DPHY_TX_TIME1_CLK_PREPARE(n) ((n) & 0xff) #define SUN6I_DPHY_TX_TIME2_REG 0x18 #define SUN6I_DPHY_TX_TIME2_CLK_TRAIL(n) ((n) & 0xff) #define SUN6I_DPHY_TX_TIME3_REG 0x1c #define SUN6I_DPHY_TX_TIME4_REG 0x20 #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n) (((n) & 0xff) << 8) #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n) ((n) & 0xff) #define SUN6I_DPHY_ANA0_REG 0x4c #define SUN6I_DPHY_ANA0_REG_PWS BIT(31) #define SUN6I_DPHY_ANA0_REG_DMPC BIT(28) #define SUN6I_DPHY_ANA0_REG_DMPD(n) (((n) & 0xf) << 24) #define SUN6I_DPHY_ANA0_REG_SLV(n) (((n) & 7) << 12) #define SUN6I_DPHY_ANA0_REG_DEN(n) (((n) & 0xf) << 8) #define SUN6I_DPHY_ANA1_REG 0x50 #define SUN6I_DPHY_ANA1_REG_VTTMODE BIT(31) #define SUN6I_DPHY_ANA1_REG_CSMPS(n) (((n) & 3) << 28) #define SUN6I_DPHY_ANA1_REG_SVTT(n) (((n) & 0xf) << 24) #define SUN6I_DPHY_ANA2_REG 0x54 #define SUN6I_DPHY_ANA2_EN_P2S_CPU(n) (((n) & 0xf) << 24) #define SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK GENMASK(27, 24) #define SUN6I_DPHY_ANA2_EN_CK_CPU BIT(4) #define SUN6I_DPHY_ANA2_REG_ENIB BIT(1) #define SUN6I_DPHY_ANA3_REG 0x58 #define SUN6I_DPHY_ANA3_EN_VTTD(n) (((n) & 0xf) << 28) #define SUN6I_DPHY_ANA3_EN_VTTD_MASK GENMASK(31, 28) #define SUN6I_DPHY_ANA3_EN_VTTC BIT(27) #define SUN6I_DPHY_ANA3_EN_DIV BIT(26) #define SUN6I_DPHY_ANA3_EN_LDOC BIT(25) #define SUN6I_DPHY_ANA3_EN_LDOD BIT(24) #define SUN6I_DPHY_ANA3_EN_LDOR BIT(18) #define SUN6I_DPHY_ANA4_REG 0x5c #define SUN6I_DPHY_ANA4_REG_DMPLVC BIT(24) #define SUN6I_DPHY_ANA4_REG_DMPLVD(n) (((n) & 0xf) << 20) #define SUN6I_DPHY_ANA4_REG_CKDV(n) (((n) & 0x1f) << 12) #define SUN6I_DPHY_ANA4_REG_TMSC(n) (((n) & 3) << 10) #define SUN6I_DPHY_ANA4_REG_TMSD(n) (((n) & 3) << 8) #define SUN6I_DPHY_ANA4_REG_TXDNSC(n) (((n) & 3) << 6) #define SUN6I_DPHY_ANA4_REG_TXDNSD(n) (((n) & 3) << 4) #define SUN6I_DPHY_ANA4_REG_TXPUSC(n) (((n) & 3) << 2) #define SUN6I_DPHY_ANA4_REG_TXPUSD(n) ((n) & 3) #define SUN6I_DPHY_DBG5_REG 0xf4 |
bb3b6fcb6 sun6i: dsi: Conve... |
85 86 87 88 89 90 91 92 93 94 95 |
struct sun6i_dphy { struct clk *bus_clk; struct clk *mod_clk; struct regmap *regs; struct reset_control *reset; struct phy *phy; struct phy_configure_opts_mipi_dphy config; }; static int sun6i_dphy_init(struct phy *phy) |
133add5b5 drm/sun4i: Add Al... |
96 |
{ |
bb3b6fcb6 sun6i: dsi: Conve... |
97 |
struct sun6i_dphy *dphy = phy_get_drvdata(phy); |
133add5b5 drm/sun4i: Add Al... |
98 99 100 |
reset_control_deassert(dphy->reset); clk_prepare_enable(dphy->mod_clk); clk_set_rate_exclusive(dphy->mod_clk, 150000000); |
bb3b6fcb6 sun6i: dsi: Conve... |
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
return 0; } static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts) { struct sun6i_dphy *dphy = phy_get_drvdata(phy); int ret; ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy); if (ret) return ret; memcpy(&dphy->config, opts, sizeof(dphy->config)); return 0; } static int sun6i_dphy_power_on(struct phy *phy) { struct sun6i_dphy *dphy = phy_get_drvdata(phy); u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0); |
133add5b5 drm/sun4i: Add Al... |
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG, SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT); regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME0_REG, SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(14) | SUN6I_DPHY_TX_TIME0_HS_PREPARE(6) | SUN6I_DPHY_TX_TIME0_HS_TRAIL(10)); regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME1_REG, SUN6I_DPHY_TX_TIME1_CLK_PREPARE(7) | SUN6I_DPHY_TX_TIME1_CLK_ZERO(50) | SUN6I_DPHY_TX_TIME1_CLK_PRE(3) | SUN6I_DPHY_TX_TIME1_CLK_POST(10)); regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME2_REG, SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30)); regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME3_REG, 0); regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME4_REG, SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(3) | SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3)); regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG, |
bb3b6fcb6 sun6i: dsi: Conve... |
146 |
SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) | |
133add5b5 drm/sun4i: Add Al... |
147 |
SUN6I_DPHY_GCTL_EN); |
133add5b5 drm/sun4i: Add Al... |
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG, SUN6I_DPHY_ANA0_REG_PWS | SUN6I_DPHY_ANA0_REG_DMPC | SUN6I_DPHY_ANA0_REG_SLV(7) | SUN6I_DPHY_ANA0_REG_DMPD(lanes_mask) | SUN6I_DPHY_ANA0_REG_DEN(lanes_mask)); regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG, SUN6I_DPHY_ANA1_REG_CSMPS(1) | SUN6I_DPHY_ANA1_REG_SVTT(7)); regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG, SUN6I_DPHY_ANA4_REG_CKDV(1) | SUN6I_DPHY_ANA4_REG_TMSC(1) | SUN6I_DPHY_ANA4_REG_TMSD(1) | SUN6I_DPHY_ANA4_REG_TXDNSC(1) | SUN6I_DPHY_ANA4_REG_TXDNSD(1) | SUN6I_DPHY_ANA4_REG_TXPUSC(1) | SUN6I_DPHY_ANA4_REG_TXPUSD(1) | SUN6I_DPHY_ANA4_REG_DMPLVC | SUN6I_DPHY_ANA4_REG_DMPLVD(lanes_mask)); regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG, SUN6I_DPHY_ANA2_REG_ENIB); udelay(5); regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG, SUN6I_DPHY_ANA3_EN_LDOR | SUN6I_DPHY_ANA3_EN_LDOC | SUN6I_DPHY_ANA3_EN_LDOD); udelay(1); regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG, SUN6I_DPHY_ANA3_EN_VTTC | SUN6I_DPHY_ANA3_EN_VTTD_MASK, SUN6I_DPHY_ANA3_EN_VTTC | SUN6I_DPHY_ANA3_EN_VTTD(lanes_mask)); udelay(1); regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG, SUN6I_DPHY_ANA3_EN_DIV, SUN6I_DPHY_ANA3_EN_DIV); udelay(1); regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG, SUN6I_DPHY_ANA2_EN_CK_CPU, SUN6I_DPHY_ANA2_EN_CK_CPU); udelay(1); regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG, SUN6I_DPHY_ANA1_REG_VTTMODE, SUN6I_DPHY_ANA1_REG_VTTMODE); regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG, SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK, SUN6I_DPHY_ANA2_EN_P2S_CPU(lanes_mask)); return 0; } |
bb3b6fcb6 sun6i: dsi: Conve... |
207 |
static int sun6i_dphy_power_off(struct phy *phy) |
133add5b5 drm/sun4i: Add Al... |
208 |
{ |
bb3b6fcb6 sun6i: dsi: Conve... |
209 |
struct sun6i_dphy *dphy = phy_get_drvdata(phy); |
133add5b5 drm/sun4i: Add Al... |
210 211 212 213 214 |
regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG, SUN6I_DPHY_ANA1_REG_VTTMODE, 0); return 0; } |
bb3b6fcb6 sun6i: dsi: Conve... |
215 |
static int sun6i_dphy_exit(struct phy *phy) |
133add5b5 drm/sun4i: Add Al... |
216 |
{ |
bb3b6fcb6 sun6i: dsi: Conve... |
217 |
struct sun6i_dphy *dphy = phy_get_drvdata(phy); |
133add5b5 drm/sun4i: Add Al... |
218 219 220 221 222 223 |
clk_rate_exclusive_put(dphy->mod_clk); clk_disable_unprepare(dphy->mod_clk); reset_control_assert(dphy->reset); return 0; } |
bb3b6fcb6 sun6i: dsi: Conve... |
224 225 226 227 228 229 230 231 |
static struct phy_ops sun6i_dphy_ops = { .configure = sun6i_dphy_configure, .power_on = sun6i_dphy_power_on, .power_off = sun6i_dphy_power_off, .init = sun6i_dphy_init, .exit = sun6i_dphy_exit, }; |
133add5b5 drm/sun4i: Add Al... |
232 233 234 235 236 237 238 |
static struct regmap_config sun6i_dphy_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, .max_register = SUN6I_DPHY_DBG5_REG, .name = "mipi-dphy", }; |
bb3b6fcb6 sun6i: dsi: Conve... |
239 |
static int sun6i_dphy_probe(struct platform_device *pdev) |
133add5b5 drm/sun4i: Add Al... |
240 |
{ |
bb3b6fcb6 sun6i: dsi: Conve... |
241 |
struct phy_provider *phy_provider; |
133add5b5 drm/sun4i: Add Al... |
242 |
struct sun6i_dphy *dphy; |
bb3b6fcb6 sun6i: dsi: Conve... |
243 |
struct resource *res; |
133add5b5 drm/sun4i: Add Al... |
244 |
void __iomem *regs; |
133add5b5 drm/sun4i: Add Al... |
245 |
|
bb3b6fcb6 sun6i: dsi: Conve... |
246 |
dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); |
133add5b5 drm/sun4i: Add Al... |
247 248 |
if (!dphy) return -ENOMEM; |
bb3b6fcb6 sun6i: dsi: Conve... |
249 250 |
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(&pdev->dev, res); |
133add5b5 drm/sun4i: Add Al... |
251 |
if (IS_ERR(regs)) { |
bb3b6fcb6 sun6i: dsi: Conve... |
252 253 |
dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers "); |
133add5b5 drm/sun4i: Add Al... |
254 255 |
return PTR_ERR(regs); } |
bb3b6fcb6 sun6i: dsi: Conve... |
256 257 |
dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus", regs, &sun6i_dphy_regmap_config); |
133add5b5 drm/sun4i: Add Al... |
258 |
if (IS_ERR(dphy->regs)) { |
bb3b6fcb6 sun6i: dsi: Conve... |
259 260 |
dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap "); |
133add5b5 drm/sun4i: Add Al... |
261 262 |
return PTR_ERR(dphy->regs); } |
bb3b6fcb6 sun6i: dsi: Conve... |
263 |
dphy->reset = devm_reset_control_get_shared(&pdev->dev, NULL); |
133add5b5 drm/sun4i: Add Al... |
264 |
if (IS_ERR(dphy->reset)) { |
bb3b6fcb6 sun6i: dsi: Conve... |
265 266 |
dev_err(&pdev->dev, "Couldn't get our reset line "); |
133add5b5 drm/sun4i: Add Al... |
267 268 |
return PTR_ERR(dphy->reset); } |
bb3b6fcb6 sun6i: dsi: Conve... |
269 |
dphy->mod_clk = devm_clk_get(&pdev->dev, "mod"); |
133add5b5 drm/sun4i: Add Al... |
270 |
if (IS_ERR(dphy->mod_clk)) { |
bb3b6fcb6 sun6i: dsi: Conve... |
271 272 273 |
dev_err(&pdev->dev, "Couldn't get the DPHY mod clock "); return PTR_ERR(dphy->mod_clk); |
133add5b5 drm/sun4i: Add Al... |
274 |
} |
bb3b6fcb6 sun6i: dsi: Conve... |
275 276 277 278 279 280 |
dphy->phy = devm_phy_create(&pdev->dev, NULL, &sun6i_dphy_ops); if (IS_ERR(dphy->phy)) { dev_err(&pdev->dev, "failed to create PHY "); return PTR_ERR(dphy->phy); } |
133add5b5 drm/sun4i: Add Al... |
281 |
|
bb3b6fcb6 sun6i: dsi: Conve... |
282 283 |
phy_set_drvdata(dphy->phy, dphy); phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); |
133add5b5 drm/sun4i: Add Al... |
284 |
|
bb3b6fcb6 sun6i: dsi: Conve... |
285 |
return PTR_ERR_OR_ZERO(phy_provider); |
133add5b5 drm/sun4i: Add Al... |
286 |
} |
bb3b6fcb6 sun6i: dsi: Conve... |
287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
static const struct of_device_id sun6i_dphy_of_table[] = { { .compatible = "allwinner,sun6i-a31-mipi-dphy" }, { } }; MODULE_DEVICE_TABLE(of, sun6i_dphy_of_table); static struct platform_driver sun6i_dphy_platform_driver = { .probe = sun6i_dphy_probe, .driver = { .name = "sun6i-mipi-dphy", .of_match_table = sun6i_dphy_of_table, }, }; module_platform_driver(sun6i_dphy_platform_driver); |
133add5b5 drm/sun4i: Add Al... |
301 |
|
bb3b6fcb6 sun6i: dsi: Conve... |
302 303 304 |
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>"); MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver"); MODULE_LICENSE("GPL"); |