1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * phy-mvebu-sata.c: SATA Phy driver for the Marvell mvebu SoCs. |
4 | * |
5 | * Copyright (C) 2013 Andrew Lunn <andrew@lunn.ch> |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/init.h> |
10 | #include <linux/clk.h> |
11 | #include <linux/phy/phy.h> |
12 | #include <linux/io.h> |
13 | #include <linux/mod_devicetable.h> |
14 | #include <linux/platform_device.h> |
15 | |
16 | struct priv { |
17 | struct clk *clk; |
18 | void __iomem *base; |
19 | }; |
20 | |
21 | #define SATA_PHY_MODE_2 0x0330 |
22 | #define MODE_2_FORCE_PU_TX BIT(0) |
23 | #define MODE_2_FORCE_PU_RX BIT(1) |
24 | #define MODE_2_PU_PLL BIT(2) |
25 | #define MODE_2_PU_IVREF BIT(3) |
26 | #define SATA_IF_CTRL 0x0050 |
27 | #define CTRL_PHY_SHUTDOWN BIT(9) |
28 | |
29 | static int phy_mvebu_sata_power_on(struct phy *phy) |
30 | { |
31 | struct priv *priv = phy_get_drvdata(phy); |
32 | u32 reg; |
33 | |
34 | clk_prepare_enable(clk: priv->clk); |
35 | |
36 | /* Enable PLL and IVREF */ |
37 | reg = readl(addr: priv->base + SATA_PHY_MODE_2); |
38 | reg |= (MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX | |
39 | MODE_2_PU_PLL | MODE_2_PU_IVREF); |
40 | writel(val: reg , addr: priv->base + SATA_PHY_MODE_2); |
41 | |
42 | /* Enable PHY */ |
43 | reg = readl(addr: priv->base + SATA_IF_CTRL); |
44 | reg &= ~CTRL_PHY_SHUTDOWN; |
45 | writel(val: reg, addr: priv->base + SATA_IF_CTRL); |
46 | |
47 | clk_disable_unprepare(clk: priv->clk); |
48 | |
49 | return 0; |
50 | } |
51 | |
52 | static int phy_mvebu_sata_power_off(struct phy *phy) |
53 | { |
54 | struct priv *priv = phy_get_drvdata(phy); |
55 | u32 reg; |
56 | |
57 | clk_prepare_enable(clk: priv->clk); |
58 | |
59 | /* Disable PLL and IVREF */ |
60 | reg = readl(addr: priv->base + SATA_PHY_MODE_2); |
61 | reg &= ~(MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX | |
62 | MODE_2_PU_PLL | MODE_2_PU_IVREF); |
63 | writel(val: reg, addr: priv->base + SATA_PHY_MODE_2); |
64 | |
65 | /* Disable PHY */ |
66 | reg = readl(addr: priv->base + SATA_IF_CTRL); |
67 | reg |= CTRL_PHY_SHUTDOWN; |
68 | writel(val: reg, addr: priv->base + SATA_IF_CTRL); |
69 | |
70 | clk_disable_unprepare(clk: priv->clk); |
71 | |
72 | return 0; |
73 | } |
74 | |
75 | static const struct phy_ops phy_mvebu_sata_ops = { |
76 | .power_on = phy_mvebu_sata_power_on, |
77 | .power_off = phy_mvebu_sata_power_off, |
78 | .owner = THIS_MODULE, |
79 | }; |
80 | |
81 | static int phy_mvebu_sata_probe(struct platform_device *pdev) |
82 | { |
83 | struct phy_provider *phy_provider; |
84 | struct priv *priv; |
85 | struct phy *phy; |
86 | |
87 | priv = devm_kzalloc(dev: &pdev->dev, size: sizeof(*priv), GFP_KERNEL); |
88 | if (!priv) |
89 | return -ENOMEM; |
90 | |
91 | priv->base = devm_platform_ioremap_resource(pdev, index: 0); |
92 | if (IS_ERR(ptr: priv->base)) |
93 | return PTR_ERR(ptr: priv->base); |
94 | |
95 | priv->clk = devm_clk_get(dev: &pdev->dev, id: "sata" ); |
96 | if (IS_ERR(ptr: priv->clk)) |
97 | return PTR_ERR(ptr: priv->clk); |
98 | |
99 | phy = devm_phy_create(dev: &pdev->dev, NULL, ops: &phy_mvebu_sata_ops); |
100 | if (IS_ERR(ptr: phy)) |
101 | return PTR_ERR(ptr: phy); |
102 | |
103 | phy_set_drvdata(phy, data: priv); |
104 | |
105 | phy_provider = devm_of_phy_provider_register(&pdev->dev, |
106 | of_phy_simple_xlate); |
107 | if (IS_ERR(ptr: phy_provider)) |
108 | return PTR_ERR(ptr: phy_provider); |
109 | |
110 | /* The boot loader may of left it on. Turn it off. */ |
111 | phy_mvebu_sata_power_off(phy); |
112 | |
113 | return 0; |
114 | } |
115 | |
116 | static const struct of_device_id phy_mvebu_sata_of_match[] = { |
117 | { .compatible = "marvell,mvebu-sata-phy" }, |
118 | { }, |
119 | }; |
120 | |
121 | static struct platform_driver phy_mvebu_sata_driver = { |
122 | .probe = phy_mvebu_sata_probe, |
123 | .driver = { |
124 | .name = "phy-mvebu-sata" , |
125 | .of_match_table = phy_mvebu_sata_of_match, |
126 | } |
127 | }; |
128 | builtin_platform_driver(phy_mvebu_sata_driver); |
129 | |