linux/drivers/phy/phy-brcmstb-sata.c
<<
>>
Prefs
   1/*
   2 * Broadcom SATA3 AHCI Controller PHY Driver
   3 *
   4 * Copyright © 2009-2015 Broadcom Corporation
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2, or (at your option)
   9 * any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 */
  16
  17#include <linux/device.h>
  18#include <linux/init.h>
  19#include <linux/interrupt.h>
  20#include <linux/io.h>
  21#include <linux/kernel.h>
  22#include <linux/module.h>
  23#include <linux/of.h>
  24#include <linux/phy/phy.h>
  25#include <linux/platform_device.h>
  26
  27#define SATA_MDIO_BANK_OFFSET                           0x23c
  28#define SATA_MDIO_REG_OFFSET(ofs)                       ((ofs) * 4)
  29
  30#define MAX_PORTS                                       2
  31
  32/* Register offset between PHYs in PCB space */
  33#define SATA_MDIO_REG_28NM_SPACE_SIZE                   0x1000
  34
  35/* The older SATA PHY registers duplicated per port registers within the map,
  36 * rather than having a separate map per port.
  37 */
  38#define SATA_MDIO_REG_40NM_SPACE_SIZE                   0x10
  39
  40enum brcm_sata_phy_version {
  41        BRCM_SATA_PHY_28NM,
  42        BRCM_SATA_PHY_40NM,
  43};
  44
  45struct brcm_sata_port {
  46        int portnum;
  47        struct phy *phy;
  48        struct brcm_sata_phy *phy_priv;
  49        bool ssc_en;
  50};
  51
  52struct brcm_sata_phy {
  53        struct device *dev;
  54        void __iomem *phy_base;
  55        enum brcm_sata_phy_version version;
  56
  57        struct brcm_sata_port phys[MAX_PORTS];
  58};
  59
  60enum sata_mdio_phy_regs {
  61        PLL_REG_BANK_0                          = 0x50,
  62        PLL_REG_BANK_0_PLLCONTROL_0             = 0x81,
  63
  64        TXPMD_REG_BANK                          = 0x1a0,
  65        TXPMD_CONTROL1                          = 0x81,
  66        TXPMD_CONTROL1_TX_SSC_EN_FRC            = BIT(0),
  67        TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL        = BIT(1),
  68        TXPMD_TX_FREQ_CTRL_CONTROL1             = 0x82,
  69        TXPMD_TX_FREQ_CTRL_CONTROL2             = 0x83,
  70        TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK   = 0x3ff,
  71        TXPMD_TX_FREQ_CTRL_CONTROL3             = 0x84,
  72        TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK   = 0x3ff,
  73};
  74
  75static inline void __iomem *brcm_sata_phy_base(struct brcm_sata_port *port)
  76{
  77        struct brcm_sata_phy *priv = port->phy_priv;
  78        u32 offset = 0;
  79
  80        if (priv->version == BRCM_SATA_PHY_28NM)
  81                offset = SATA_MDIO_REG_28NM_SPACE_SIZE;
  82        else if (priv->version == BRCM_SATA_PHY_40NM)
  83                offset = SATA_MDIO_REG_40NM_SPACE_SIZE;
  84        else
  85                dev_err(priv->dev, "invalid phy version\n");
  86
  87        return priv->phy_base + (port->portnum * offset);
  88}
  89
  90static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs,
  91                              u32 msk, u32 value)
  92{
  93        u32 tmp;
  94
  95        writel(bank, addr + SATA_MDIO_BANK_OFFSET);
  96        tmp = readl(addr + SATA_MDIO_REG_OFFSET(ofs));
  97        tmp = (tmp & msk) | value;
  98        writel(tmp, addr + SATA_MDIO_REG_OFFSET(ofs));
  99}
 100
 101/* These defaults were characterized by H/W group */
 102#define FMIN_VAL_DEFAULT        0x3df
 103#define FMAX_VAL_DEFAULT        0x3df
 104#define FMAX_VAL_SSC            0x83
 105
 106static void brcm_sata_cfg_ssc(struct brcm_sata_port *port)
 107{
 108        void __iomem *base = brcm_sata_phy_base(port);
 109        struct brcm_sata_phy *priv = port->phy_priv;
 110        u32 tmp;
 111
 112        /* override the TX spread spectrum setting */
 113        tmp = TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL | TXPMD_CONTROL1_TX_SSC_EN_FRC;
 114        brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_CONTROL1, ~tmp, tmp);
 115
 116        /* set fixed min freq */
 117        brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL2,
 118                          ~TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK,
 119                          FMIN_VAL_DEFAULT);
 120
 121        /* set fixed max freq depending on SSC config */
 122        if (port->ssc_en) {
 123                dev_info(priv->dev, "enabling SSC on port %d\n", port->portnum);
 124                tmp = FMAX_VAL_SSC;
 125        } else {
 126                tmp = FMAX_VAL_DEFAULT;
 127        }
 128
 129        brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3,
 130                          ~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp);
 131}
 132
 133static int brcm_sata_phy_init(struct phy *phy)
 134{
 135        struct brcm_sata_port *port = phy_get_drvdata(phy);
 136
 137        brcm_sata_cfg_ssc(port);
 138
 139        return 0;
 140}
 141
 142static const struct phy_ops phy_ops = {
 143        .init           = brcm_sata_phy_init,
 144        .owner          = THIS_MODULE,
 145};
 146
 147static const struct of_device_id brcm_sata_phy_of_match[] = {
 148        { .compatible   = "brcm,bcm7445-sata-phy",
 149          .data = (void *)BRCM_SATA_PHY_28NM },
 150        { .compatible   = "brcm,bcm7425-sata-phy",
 151          .data = (void *)BRCM_SATA_PHY_40NM },
 152        {},
 153};
 154MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match);
 155
 156static int brcm_sata_phy_probe(struct platform_device *pdev)
 157{
 158        struct device *dev = &pdev->dev;
 159        struct device_node *dn = dev->of_node, *child;
 160        const struct of_device_id *of_id;
 161        struct brcm_sata_phy *priv;
 162        struct resource *res;
 163        struct phy_provider *provider;
 164        int ret, count = 0;
 165
 166        if (of_get_child_count(dn) == 0)
 167                return -ENODEV;
 168
 169        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 170        if (!priv)
 171                return -ENOMEM;
 172        dev_set_drvdata(dev, priv);
 173        priv->dev = dev;
 174
 175        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
 176        priv->phy_base = devm_ioremap_resource(dev, res);
 177        if (IS_ERR(priv->phy_base))
 178                return PTR_ERR(priv->phy_base);
 179
 180        of_id = of_match_node(brcm_sata_phy_of_match, dn);
 181        if (of_id)
 182                priv->version = (enum brcm_sata_phy_version)of_id->data;
 183        else
 184                priv->version = BRCM_SATA_PHY_28NM;
 185
 186        for_each_available_child_of_node(dn, child) {
 187                unsigned int id;
 188                struct brcm_sata_port *port;
 189
 190                if (of_property_read_u32(child, "reg", &id)) {
 191                        dev_err(dev, "missing reg property in node %s\n",
 192                                        child->name);
 193                        ret = -EINVAL;
 194                        goto put_child;
 195                }
 196
 197                if (id >= MAX_PORTS) {
 198                        dev_err(dev, "invalid reg: %u\n", id);
 199                        ret = -EINVAL;
 200                        goto put_child;
 201                }
 202                if (priv->phys[id].phy) {
 203                        dev_err(dev, "already registered port %u\n", id);
 204                        ret = -EINVAL;
 205                        goto put_child;
 206                }
 207
 208                port = &priv->phys[id];
 209                port->portnum = id;
 210                port->phy_priv = priv;
 211                port->phy = devm_phy_create(dev, child, &phy_ops);
 212                port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc");
 213                if (IS_ERR(port->phy)) {
 214                        dev_err(dev, "failed to create PHY\n");
 215                        ret = PTR_ERR(port->phy);
 216                        goto put_child;
 217                }
 218
 219                phy_set_drvdata(port->phy, port);
 220                count++;
 221        }
 222
 223        provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 224        if (IS_ERR(provider)) {
 225                dev_err(dev, "could not register PHY provider\n");
 226                return PTR_ERR(provider);
 227        }
 228
 229        dev_info(dev, "registered %d port(s)\n", count);
 230
 231        return 0;
 232put_child:
 233        of_node_put(child);
 234        return ret;
 235}
 236
 237static struct platform_driver brcm_sata_phy_driver = {
 238        .probe  = brcm_sata_phy_probe,
 239        .driver = {
 240                .of_match_table = brcm_sata_phy_of_match,
 241                .name           = "brcmstb-sata-phy",
 242        }
 243};
 244module_platform_driver(brcm_sata_phy_driver);
 245
 246MODULE_DESCRIPTION("Broadcom STB SATA PHY driver");
 247MODULE_LICENSE("GPL");
 248MODULE_AUTHOR("Marc Carino");
 249MODULE_AUTHOR("Brian Norris");
 250MODULE_ALIAS("platform:phy-brcmstb-sata");
 251