phy-hix5hd2-sata.c (5175B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2014 Linaro Ltd. 4 * Copyright (c) 2014 HiSilicon Limited. 5 */ 6 7#include <linux/delay.h> 8#include <linux/io.h> 9#include <linux/mfd/syscon.h> 10#include <linux/module.h> 11#include <linux/phy/phy.h> 12#include <linux/platform_device.h> 13#include <linux/regmap.h> 14 15#define SATA_PHY0_CTLL 0xa0 16#define MPLL_MULTIPLIER_SHIFT 1 17#define MPLL_MULTIPLIER_MASK 0xfe 18#define MPLL_MULTIPLIER_50M 0x3c 19#define MPLL_MULTIPLIER_100M 0x1e 20#define PHY_RESET BIT(0) 21#define REF_SSP_EN BIT(9) 22#define SSC_EN BIT(10) 23#define REF_USE_PAD BIT(23) 24 25#define SATA_PORT_PHYCTL 0x174 26#define SPEED_MODE_MASK 0x6f0000 27#define HALF_RATE_SHIFT 16 28#define PHY_CONFIG_SHIFT 18 29#define GEN2_EN_SHIFT 21 30#define SPEED_CTRL BIT(20) 31 32#define SATA_PORT_PHYCTL1 0x148 33#define AMPLITUDE_MASK 0x3ffffe 34#define AMPLITUDE_GEN3 0x68 35#define AMPLITUDE_GEN3_SHIFT 15 36#define AMPLITUDE_GEN2 0x56 37#define AMPLITUDE_GEN2_SHIFT 8 38#define AMPLITUDE_GEN1 0x56 39#define AMPLITUDE_GEN1_SHIFT 1 40 41#define SATA_PORT_PHYCTL2 0x14c 42#define PREEMPH_MASK 0x3ffff 43#define PREEMPH_GEN3 0x20 44#define PREEMPH_GEN3_SHIFT 12 45#define PREEMPH_GEN2 0x15 46#define PREEMPH_GEN2_SHIFT 6 47#define PREEMPH_GEN1 0x5 48#define PREEMPH_GEN1_SHIFT 0 49 50struct hix5hd2_priv { 51 void __iomem *base; 52 struct regmap *peri_ctrl; 53}; 54 55enum phy_speed_mode { 56 SPEED_MODE_GEN1 = 0, 57 SPEED_MODE_GEN2 = 1, 58 SPEED_MODE_GEN3 = 2, 59}; 60 61static int hix5hd2_sata_phy_init(struct phy *phy) 62{ 63 struct hix5hd2_priv *priv = phy_get_drvdata(phy); 64 u32 val, data[2]; 65 int ret; 66 67 if (priv->peri_ctrl) { 68 ret = of_property_read_u32_array(phy->dev.of_node, 69 "hisilicon,power-reg", 70 &data[0], 2); 71 if (ret) { 72 dev_err(&phy->dev, "Fail read hisilicon,power-reg\n"); 73 return ret; 74 } 75 76 regmap_update_bits(priv->peri_ctrl, data[0], 77 BIT(data[1]), BIT(data[1])); 78 } 79 80 /* reset phy */ 81 val = readl_relaxed(priv->base + SATA_PHY0_CTLL); 82 val &= ~(MPLL_MULTIPLIER_MASK | REF_USE_PAD); 83 val |= MPLL_MULTIPLIER_50M << MPLL_MULTIPLIER_SHIFT | 84 REF_SSP_EN | PHY_RESET; 85 writel_relaxed(val, priv->base + SATA_PHY0_CTLL); 86 msleep(20); 87 val &= ~PHY_RESET; 88 writel_relaxed(val, priv->base + SATA_PHY0_CTLL); 89 90 val = readl_relaxed(priv->base + SATA_PORT_PHYCTL1); 91 val &= ~AMPLITUDE_MASK; 92 val |= AMPLITUDE_GEN3 << AMPLITUDE_GEN3_SHIFT | 93 AMPLITUDE_GEN2 << AMPLITUDE_GEN2_SHIFT | 94 AMPLITUDE_GEN1 << AMPLITUDE_GEN1_SHIFT; 95 writel_relaxed(val, priv->base + SATA_PORT_PHYCTL1); 96 97 val = readl_relaxed(priv->base + SATA_PORT_PHYCTL2); 98 val &= ~PREEMPH_MASK; 99 val |= PREEMPH_GEN3 << PREEMPH_GEN3_SHIFT | 100 PREEMPH_GEN2 << PREEMPH_GEN2_SHIFT | 101 PREEMPH_GEN1 << PREEMPH_GEN1_SHIFT; 102 writel_relaxed(val, priv->base + SATA_PORT_PHYCTL2); 103 104 /* ensure PHYCTRL setting takes effect */ 105 val = readl_relaxed(priv->base + SATA_PORT_PHYCTL); 106 val &= ~SPEED_MODE_MASK; 107 val |= SPEED_MODE_GEN1 << HALF_RATE_SHIFT | 108 SPEED_MODE_GEN1 << PHY_CONFIG_SHIFT | 109 SPEED_MODE_GEN1 << GEN2_EN_SHIFT | SPEED_CTRL; 110 writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); 111 112 msleep(20); 113 val &= ~SPEED_MODE_MASK; 114 val |= SPEED_MODE_GEN3 << HALF_RATE_SHIFT | 115 SPEED_MODE_GEN3 << PHY_CONFIG_SHIFT | 116 SPEED_MODE_GEN3 << GEN2_EN_SHIFT | SPEED_CTRL; 117 writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); 118 119 val &= ~(SPEED_MODE_MASK | SPEED_CTRL); 120 val |= SPEED_MODE_GEN2 << HALF_RATE_SHIFT | 121 SPEED_MODE_GEN2 << PHY_CONFIG_SHIFT | 122 SPEED_MODE_GEN2 << GEN2_EN_SHIFT; 123 writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); 124 125 return 0; 126} 127 128static const struct phy_ops hix5hd2_sata_phy_ops = { 129 .init = hix5hd2_sata_phy_init, 130 .owner = THIS_MODULE, 131}; 132 133static int hix5hd2_sata_phy_probe(struct platform_device *pdev) 134{ 135 struct phy_provider *phy_provider; 136 struct device *dev = &pdev->dev; 137 struct resource *res; 138 struct phy *phy; 139 struct hix5hd2_priv *priv; 140 141 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 142 if (!priv) 143 return -ENOMEM; 144 145 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 146 if (!res) 147 return -EINVAL; 148 149 priv->base = devm_ioremap(dev, res->start, resource_size(res)); 150 if (!priv->base) 151 return -ENOMEM; 152 153 priv->peri_ctrl = syscon_regmap_lookup_by_phandle(dev->of_node, 154 "hisilicon,peripheral-syscon"); 155 if (IS_ERR(priv->peri_ctrl)) 156 priv->peri_ctrl = NULL; 157 158 phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops); 159 if (IS_ERR(phy)) { 160 dev_err(dev, "failed to create PHY\n"); 161 return PTR_ERR(phy); 162 } 163 164 phy_set_drvdata(phy, priv); 165 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 166 return PTR_ERR_OR_ZERO(phy_provider); 167} 168 169static const struct of_device_id hix5hd2_sata_phy_of_match[] = { 170 {.compatible = "hisilicon,hix5hd2-sata-phy",}, 171 { }, 172}; 173MODULE_DEVICE_TABLE(of, hix5hd2_sata_phy_of_match); 174 175static struct platform_driver hix5hd2_sata_phy_driver = { 176 .probe = hix5hd2_sata_phy_probe, 177 .driver = { 178 .name = "hix5hd2-sata-phy", 179 .of_match_table = hix5hd2_sata_phy_of_match, 180 } 181}; 182module_platform_driver(hix5hd2_sata_phy_driver); 183 184MODULE_AUTHOR("Jiancheng Xue <xuejiancheng@huawei.com>"); 185MODULE_DESCRIPTION("HISILICON HIX5HD2 SATA PHY driver"); 186MODULE_ALIAS("platform:hix5hd2-sata-phy"); 187MODULE_LICENSE("GPL v2");