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