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