linux/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2014, The Linux Foundation. All rights reserved.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 and
   6 * only version 2 as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 */
  13
  14#include <linux/io.h>
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/of.h>
  18#include <linux/of_address.h>
  19#include <linux/time.h>
  20#include <linux/delay.h>
  21#include <linux/clk.h>
  22#include <linux/slab.h>
  23#include <linux/platform_device.h>
  24#include <linux/phy/phy.h>
  25
  26struct qcom_ipq806x_sata_phy {
  27        void __iomem *mmio;
  28        struct clk *cfg_clk;
  29        struct device *dev;
  30};
  31
  32#define __set(v, a, b)  (((v) << (b)) & GENMASK(a, b))
  33
  34#define SATA_PHY_P0_PARAM0              0x200
  35#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x)        __set(x, 17, 12)
  36#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK      GENMASK(17, 12)
  37#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x)        __set(x, 11, 6)
  38#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK      GENMASK(11, 6)
  39#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x)        __set(x, 5, 0)
  40#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK      GENMASK(5, 0)
  41
  42#define SATA_PHY_P0_PARAM1              0x204
  43#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x)        __set(x, 31, 21)
  44#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x)      __set(x, 20, 14)
  45#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK    GENMASK(20, 14)
  46#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x)      __set(x, 13, 7)
  47#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK    GENMASK(13, 7)
  48#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x)      __set(x, 6, 0)
  49#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK    GENMASK(6, 0)
  50
  51#define SATA_PHY_P0_PARAM2              0x208
  52#define SATA_PHY_P0_PARAM2_RX_EQ(x)     __set(x, 20, 18)
  53#define SATA_PHY_P0_PARAM2_RX_EQ_MASK   GENMASK(20, 18)
  54
  55#define SATA_PHY_P0_PARAM3              0x20C
  56#define SATA_PHY_SSC_EN                 0x8
  57#define SATA_PHY_P0_PARAM4              0x210
  58#define SATA_PHY_REF_SSP_EN             0x2
  59#define SATA_PHY_RESET                  0x1
  60
  61static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy)
  62{
  63        struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
  64        u32 reg;
  65
  66        /* Setting SSC_EN to 1 */
  67        reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3);
  68        reg = reg | SATA_PHY_SSC_EN;
  69        writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3);
  70
  71        reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) &
  72                        ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK |
  73                          SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK |
  74                          SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK);
  75        reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf);
  76        writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0);
  77
  78        reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) &
  79                        ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK |
  80                          SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK |
  81                          SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK);
  82        reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) |
  83                SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) |
  84                SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55);
  85        writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1);
  86
  87        reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) &
  88                ~SATA_PHY_P0_PARAM2_RX_EQ_MASK;
  89        reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3);
  90        writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2);
  91
  92        /* Setting PHY_RESET to 1 */
  93        reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
  94        reg = reg | SATA_PHY_RESET;
  95        writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
  96
  97        /* Setting REF_SSP_EN to 1 */
  98        reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
  99        reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET;
 100        writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
 101
 102        /* make sure all changes complete before we let the PHY out of reset */
 103        mb();
 104
 105        /* sleep for max. 50us more to combine processor wakeups */
 106        usleep_range(20, 20 + 50);
 107
 108        /* Clearing PHY_RESET to 0 */
 109        reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
 110        reg = reg & ~SATA_PHY_RESET;
 111        writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
 112
 113        return 0;
 114}
 115
 116static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy)
 117{
 118        struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
 119        u32 reg;
 120
 121        /* Setting PHY_RESET to 1 */
 122        reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
 123        reg = reg | SATA_PHY_RESET;
 124        writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
 125
 126        return 0;
 127}
 128
 129static const struct phy_ops qcom_ipq806x_sata_phy_ops = {
 130        .init           = qcom_ipq806x_sata_phy_init,
 131        .exit           = qcom_ipq806x_sata_phy_exit,
 132        .owner          = THIS_MODULE,
 133};
 134
 135static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
 136{
 137        struct qcom_ipq806x_sata_phy *phy;
 138        struct device *dev = &pdev->dev;
 139        struct resource *res;
 140        struct phy_provider *phy_provider;
 141        struct phy *generic_phy;
 142        int ret;
 143
 144        phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
 145        if (!phy)
 146                return -ENOMEM;
 147
 148        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 149        phy->mmio = devm_ioremap_resource(dev, res);
 150        if (IS_ERR(phy->mmio))
 151                return PTR_ERR(phy->mmio);
 152
 153        generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops);
 154        if (IS_ERR(generic_phy)) {
 155                dev_err(dev, "%s: failed to create phy\n", __func__);
 156                return PTR_ERR(generic_phy);
 157        }
 158
 159        phy->dev = dev;
 160        phy_set_drvdata(generic_phy, phy);
 161        platform_set_drvdata(pdev, phy);
 162
 163        phy->cfg_clk = devm_clk_get(dev, "cfg");
 164        if (IS_ERR(phy->cfg_clk)) {
 165                dev_err(dev, "Failed to get sata cfg clock\n");
 166                return PTR_ERR(phy->cfg_clk);
 167        }
 168
 169        ret = clk_prepare_enable(phy->cfg_clk);
 170        if (ret)
 171                return ret;
 172
 173        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 174        if (IS_ERR(phy_provider)) {
 175                clk_disable_unprepare(phy->cfg_clk);
 176                dev_err(dev, "%s: failed to register phy\n", __func__);
 177                return PTR_ERR(phy_provider);
 178        }
 179
 180        return 0;
 181}
 182
 183static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev)
 184{
 185        struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev);
 186
 187        clk_disable_unprepare(phy->cfg_clk);
 188
 189        return 0;
 190}
 191
 192static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = {
 193        { .compatible = "qcom,ipq806x-sata-phy" },
 194        { },
 195};
 196MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match);
 197
 198static struct platform_driver qcom_ipq806x_sata_phy_driver = {
 199        .probe  = qcom_ipq806x_sata_phy_probe,
 200        .remove = qcom_ipq806x_sata_phy_remove,
 201        .driver = {
 202                .name   = "qcom-ipq806x-sata-phy",
 203                .of_match_table = qcom_ipq806x_sata_phy_of_match,
 204        }
 205};
 206module_platform_driver(qcom_ipq806x_sata_phy_driver);
 207
 208MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver");
 209MODULE_LICENSE("GPL v2");
 210