18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2014 Marvell Technology Group Ltd. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Antoine Tenart <antoine.tenart@free-electrons.com> 68c2ecf20Sopenharmony_ci * Jisheng Zhang <jszhang@marvell.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/of_device.h> 128c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/reset.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define USB_PHY_PLL 0x04 178c2ecf20Sopenharmony_ci#define USB_PHY_PLL_CONTROL 0x08 188c2ecf20Sopenharmony_ci#define USB_PHY_TX_CTRL0 0x10 198c2ecf20Sopenharmony_ci#define USB_PHY_TX_CTRL1 0x14 208c2ecf20Sopenharmony_ci#define USB_PHY_TX_CTRL2 0x18 218c2ecf20Sopenharmony_ci#define USB_PHY_RX_CTRL 0x20 228c2ecf20Sopenharmony_ci#define USB_PHY_ANALOG 0x34 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* USB_PHY_PLL */ 258c2ecf20Sopenharmony_ci#define CLK_REF_DIV(x) ((x) << 4) 268c2ecf20Sopenharmony_ci#define FEEDBACK_CLK_DIV(x) ((x) << 8) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* USB_PHY_PLL_CONTROL */ 298c2ecf20Sopenharmony_ci#define CLK_STABLE BIT(0) 308c2ecf20Sopenharmony_ci#define PLL_CTRL_PIN BIT(1) 318c2ecf20Sopenharmony_ci#define PLL_CTRL_REG BIT(2) 328c2ecf20Sopenharmony_ci#define PLL_ON BIT(3) 338c2ecf20Sopenharmony_ci#define PHASE_OFF_TOL_125 (0x0 << 5) 348c2ecf20Sopenharmony_ci#define PHASE_OFF_TOL_250 BIT(5) 358c2ecf20Sopenharmony_ci#define KVC0_CALIB (0x0 << 9) 368c2ecf20Sopenharmony_ci#define KVC0_REG_CTRL BIT(9) 378c2ecf20Sopenharmony_ci#define KVC0_HIGH (0x0 << 10) 388c2ecf20Sopenharmony_ci#define KVC0_LOW (0x3 << 10) 398c2ecf20Sopenharmony_ci#define CLK_BLK_EN BIT(13) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* USB_PHY_TX_CTRL0 */ 428c2ecf20Sopenharmony_ci#define EXT_HS_RCAL_EN BIT(3) 438c2ecf20Sopenharmony_ci#define EXT_FS_RCAL_EN BIT(4) 448c2ecf20Sopenharmony_ci#define IMPCAL_VTH_DIV(x) ((x) << 5) 458c2ecf20Sopenharmony_ci#define EXT_RS_RCAL_DIV(x) ((x) << 8) 468c2ecf20Sopenharmony_ci#define EXT_FS_RCAL_DIV(x) ((x) << 12) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* USB_PHY_TX_CTRL1 */ 498c2ecf20Sopenharmony_ci#define TX_VDD15_14 (0x0 << 4) 508c2ecf20Sopenharmony_ci#define TX_VDD15_15 BIT(4) 518c2ecf20Sopenharmony_ci#define TX_VDD15_16 (0x2 << 4) 528c2ecf20Sopenharmony_ci#define TX_VDD15_17 (0x3 << 4) 538c2ecf20Sopenharmony_ci#define TX_VDD12_VDD (0x0 << 6) 548c2ecf20Sopenharmony_ci#define TX_VDD12_11 BIT(6) 558c2ecf20Sopenharmony_ci#define TX_VDD12_12 (0x2 << 6) 568c2ecf20Sopenharmony_ci#define TX_VDD12_13 (0x3 << 6) 578c2ecf20Sopenharmony_ci#define LOW_VDD_EN BIT(8) 588c2ecf20Sopenharmony_ci#define TX_OUT_AMP(x) ((x) << 9) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* USB_PHY_TX_CTRL2 */ 618c2ecf20Sopenharmony_ci#define TX_CHAN_CTRL_REG(x) ((x) << 0) 628c2ecf20Sopenharmony_ci#define DRV_SLEWRATE(x) ((x) << 4) 638c2ecf20Sopenharmony_ci#define IMP_CAL_FS_HS_DLY_0 (0x0 << 6) 648c2ecf20Sopenharmony_ci#define IMP_CAL_FS_HS_DLY_1 BIT(6) 658c2ecf20Sopenharmony_ci#define IMP_CAL_FS_HS_DLY_2 (0x2 << 6) 668c2ecf20Sopenharmony_ci#define IMP_CAL_FS_HS_DLY_3 (0x3 << 6) 678c2ecf20Sopenharmony_ci#define FS_DRV_EN_MASK(x) ((x) << 8) 688c2ecf20Sopenharmony_ci#define HS_DRV_EN_MASK(x) ((x) << 12) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* USB_PHY_RX_CTRL */ 718c2ecf20Sopenharmony_ci#define PHASE_FREEZE_DLY_2_CL (0x0 << 0) 728c2ecf20Sopenharmony_ci#define PHASE_FREEZE_DLY_4_CL BIT(0) 738c2ecf20Sopenharmony_ci#define ACK_LENGTH_8_CL (0x0 << 2) 748c2ecf20Sopenharmony_ci#define ACK_LENGTH_12_CL BIT(2) 758c2ecf20Sopenharmony_ci#define ACK_LENGTH_16_CL (0x2 << 2) 768c2ecf20Sopenharmony_ci#define ACK_LENGTH_20_CL (0x3 << 2) 778c2ecf20Sopenharmony_ci#define SQ_LENGTH_3 (0x0 << 4) 788c2ecf20Sopenharmony_ci#define SQ_LENGTH_6 BIT(4) 798c2ecf20Sopenharmony_ci#define SQ_LENGTH_9 (0x2 << 4) 808c2ecf20Sopenharmony_ci#define SQ_LENGTH_12 (0x3 << 4) 818c2ecf20Sopenharmony_ci#define DISCON_THRESHOLD_260 (0x0 << 6) 828c2ecf20Sopenharmony_ci#define DISCON_THRESHOLD_270 BIT(6) 838c2ecf20Sopenharmony_ci#define DISCON_THRESHOLD_280 (0x2 << 6) 848c2ecf20Sopenharmony_ci#define DISCON_THRESHOLD_290 (0x3 << 6) 858c2ecf20Sopenharmony_ci#define SQ_THRESHOLD(x) ((x) << 8) 868c2ecf20Sopenharmony_ci#define LPF_COEF(x) ((x) << 12) 878c2ecf20Sopenharmony_ci#define INTPL_CUR_10 (0x0 << 14) 888c2ecf20Sopenharmony_ci#define INTPL_CUR_20 BIT(14) 898c2ecf20Sopenharmony_ci#define INTPL_CUR_30 (0x2 << 14) 908c2ecf20Sopenharmony_ci#define INTPL_CUR_40 (0x3 << 14) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* USB_PHY_ANALOG */ 938c2ecf20Sopenharmony_ci#define ANA_PWR_UP BIT(1) 948c2ecf20Sopenharmony_ci#define ANA_PWR_DOWN BIT(2) 958c2ecf20Sopenharmony_ci#define V2I_VCO_RATIO(x) ((x) << 7) 968c2ecf20Sopenharmony_ci#define R_ROTATE_90 (0x0 << 10) 978c2ecf20Sopenharmony_ci#define R_ROTATE_0 BIT(10) 988c2ecf20Sopenharmony_ci#define MODE_TEST_EN BIT(11) 998c2ecf20Sopenharmony_ci#define ANA_TEST_DC_CTRL(x) ((x) << 12) 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic const u32 phy_berlin_pll_dividers[] = { 1028c2ecf20Sopenharmony_ci /* Berlin 2 */ 1038c2ecf20Sopenharmony_ci CLK_REF_DIV(0x6) | FEEDBACK_CLK_DIV(0x55), 1048c2ecf20Sopenharmony_ci /* Berlin 2CD/Q */ 1058c2ecf20Sopenharmony_ci CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54), 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistruct phy_berlin_usb_priv { 1098c2ecf20Sopenharmony_ci void __iomem *base; 1108c2ecf20Sopenharmony_ci struct reset_control *rst_ctrl; 1118c2ecf20Sopenharmony_ci u32 pll_divider; 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int phy_berlin_usb_power_on(struct phy *phy) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct phy_berlin_usb_priv *priv = phy_get_drvdata(phy); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci reset_control_reset(priv->rst_ctrl); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci writel(priv->pll_divider, 1218c2ecf20Sopenharmony_ci priv->base + USB_PHY_PLL); 1228c2ecf20Sopenharmony_ci writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL | 1238c2ecf20Sopenharmony_ci CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL); 1248c2ecf20Sopenharmony_ci writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5), 1258c2ecf20Sopenharmony_ci priv->base + USB_PHY_ANALOG); 1268c2ecf20Sopenharmony_ci writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 | 1278c2ecf20Sopenharmony_ci DISCON_THRESHOLD_270 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) | 1288c2ecf20Sopenharmony_ci INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1); 1318c2ecf20Sopenharmony_ci writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), 1328c2ecf20Sopenharmony_ci priv->base + USB_PHY_TX_CTRL0); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4) | 1358c2ecf20Sopenharmony_ci EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), 1388c2ecf20Sopenharmony_ci priv->base + USB_PHY_TX_CTRL0); 1398c2ecf20Sopenharmony_ci writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) | IMP_CAL_FS_HS_DLY_3 | 1408c2ecf20Sopenharmony_ci FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic const struct phy_ops phy_berlin_usb_ops = { 1468c2ecf20Sopenharmony_ci .power_on = phy_berlin_usb_power_on, 1478c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic const struct of_device_id phy_berlin_usb_of_match[] = { 1518c2ecf20Sopenharmony_ci { 1528c2ecf20Sopenharmony_ci .compatible = "marvell,berlin2-usb-phy", 1538c2ecf20Sopenharmony_ci .data = &phy_berlin_pll_dividers[0], 1548c2ecf20Sopenharmony_ci }, 1558c2ecf20Sopenharmony_ci { 1568c2ecf20Sopenharmony_ci .compatible = "marvell,berlin2cd-usb-phy", 1578c2ecf20Sopenharmony_ci .data = &phy_berlin_pll_dividers[1], 1588c2ecf20Sopenharmony_ci }, 1598c2ecf20Sopenharmony_ci { }, 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, phy_berlin_usb_of_match); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int phy_berlin_usb_probe(struct platform_device *pdev) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci const struct of_device_id *match = 1668c2ecf20Sopenharmony_ci of_match_device(phy_berlin_usb_of_match, &pdev->dev); 1678c2ecf20Sopenharmony_ci struct phy_berlin_usb_priv *priv; 1688c2ecf20Sopenharmony_ci struct resource *res; 1698c2ecf20Sopenharmony_ci struct phy *phy; 1708c2ecf20Sopenharmony_ci struct phy_provider *phy_provider; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 1738c2ecf20Sopenharmony_ci if (!priv) 1748c2ecf20Sopenharmony_ci return -ENOMEM; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1778c2ecf20Sopenharmony_ci priv->base = devm_ioremap_resource(&pdev->dev, res); 1788c2ecf20Sopenharmony_ci if (IS_ERR(priv->base)) 1798c2ecf20Sopenharmony_ci return PTR_ERR(priv->base); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL); 1828c2ecf20Sopenharmony_ci if (IS_ERR(priv->rst_ctrl)) 1838c2ecf20Sopenharmony_ci return PTR_ERR(priv->rst_ctrl); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci priv->pll_divider = *((u32 *)match->data); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops); 1888c2ecf20Sopenharmony_ci if (IS_ERR(phy)) { 1898c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to create PHY\n"); 1908c2ecf20Sopenharmony_ci return PTR_ERR(phy); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci phy_set_drvdata(phy, priv); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci phy_provider = 1968c2ecf20Sopenharmony_ci devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); 1978c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic struct platform_driver phy_berlin_usb_driver = { 2018c2ecf20Sopenharmony_ci .probe = phy_berlin_usb_probe, 2028c2ecf20Sopenharmony_ci .driver = { 2038c2ecf20Sopenharmony_ci .name = "phy-berlin-usb", 2048c2ecf20Sopenharmony_ci .of_match_table = phy_berlin_usb_of_match, 2058c2ecf20Sopenharmony_ci }, 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_cimodule_platform_driver(phy_berlin_usb_driver); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ciMODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>"); 2108c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell Berlin PHY driver for USB"); 2118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 212