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