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