root/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cygnus_pcie_power_config
  2. cygnus_pcie_phy_power_on
  3. cygnus_pcie_phy_power_off
  4. cygnus_pcie_phy_probe

   1 /*
   2  * Copyright (C) 2015 Broadcom Corporation
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU General Public License as
   6  * published by the Free Software Foundation version 2.
   7  *
   8  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
   9  * kind, whether express or implied; without even the implied warranty
  10  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11  * GNU General Public License for more details.
  12  */
  13 
  14 #include <linux/delay.h>
  15 #include <linux/io.h>
  16 #include <linux/module.h>
  17 #include <linux/of.h>
  18 #include <linux/phy/phy.h>
  19 #include <linux/platform_device.h>
  20 
  21 #define PCIE_CFG_OFFSET         0x00
  22 #define PCIE1_PHY_IDDQ_SHIFT    10
  23 #define PCIE0_PHY_IDDQ_SHIFT    2
  24 
  25 enum cygnus_pcie_phy_id {
  26         CYGNUS_PHY_PCIE0 = 0,
  27         CYGNUS_PHY_PCIE1,
  28         MAX_NUM_PHYS,
  29 };
  30 
  31 struct cygnus_pcie_phy_core;
  32 
  33 /**
  34  * struct cygnus_pcie_phy - Cygnus PCIe PHY device
  35  * @core: pointer to the Cygnus PCIe PHY core control
  36  * @id: internal ID to identify the Cygnus PCIe PHY
  37  * @phy: pointer to the kernel PHY device
  38  */
  39 struct cygnus_pcie_phy {
  40         struct cygnus_pcie_phy_core *core;
  41         enum cygnus_pcie_phy_id id;
  42         struct phy *phy;
  43 };
  44 
  45 /**
  46  * struct cygnus_pcie_phy_core - Cygnus PCIe PHY core control
  47  * @dev: pointer to device
  48  * @base: base register
  49  * @lock: mutex to protect access to individual PHYs
  50  * @phys: pointer to Cygnus PHY device
  51  */
  52 struct cygnus_pcie_phy_core {
  53         struct device *dev;
  54         void __iomem *base;
  55         struct mutex lock;
  56         struct cygnus_pcie_phy phys[MAX_NUM_PHYS];
  57 };
  58 
  59 static int cygnus_pcie_power_config(struct cygnus_pcie_phy *phy, bool enable)
  60 {
  61         struct cygnus_pcie_phy_core *core = phy->core;
  62         unsigned shift;
  63         u32 val;
  64 
  65         mutex_lock(&core->lock);
  66 
  67         switch (phy->id) {
  68         case CYGNUS_PHY_PCIE0:
  69                 shift = PCIE0_PHY_IDDQ_SHIFT;
  70                 break;
  71 
  72         case CYGNUS_PHY_PCIE1:
  73                 shift = PCIE1_PHY_IDDQ_SHIFT;
  74                 break;
  75 
  76         default:
  77                 mutex_unlock(&core->lock);
  78                 dev_err(core->dev, "PCIe PHY %d invalid\n", phy->id);
  79                 return -EINVAL;
  80         }
  81 
  82         if (enable) {
  83                 val = readl(core->base + PCIE_CFG_OFFSET);
  84                 val &= ~BIT(shift);
  85                 writel(val, core->base + PCIE_CFG_OFFSET);
  86                 /*
  87                  * Wait 50 ms for the PCIe Serdes to stabilize after the analog
  88                  * front end is brought up
  89                  */
  90                 msleep(50);
  91         } else {
  92                 val = readl(core->base + PCIE_CFG_OFFSET);
  93                 val |= BIT(shift);
  94                 writel(val, core->base + PCIE_CFG_OFFSET);
  95         }
  96 
  97         mutex_unlock(&core->lock);
  98         dev_dbg(core->dev, "PCIe PHY %d %s\n", phy->id,
  99                 enable ? "enabled" : "disabled");
 100         return 0;
 101 }
 102 
 103 static int cygnus_pcie_phy_power_on(struct phy *p)
 104 {
 105         struct cygnus_pcie_phy *phy = phy_get_drvdata(p);
 106 
 107         return cygnus_pcie_power_config(phy, true);
 108 }
 109 
 110 static int cygnus_pcie_phy_power_off(struct phy *p)
 111 {
 112         struct cygnus_pcie_phy *phy = phy_get_drvdata(p);
 113 
 114         return cygnus_pcie_power_config(phy, false);
 115 }
 116 
 117 static const struct phy_ops cygnus_pcie_phy_ops = {
 118         .power_on = cygnus_pcie_phy_power_on,
 119         .power_off = cygnus_pcie_phy_power_off,
 120         .owner = THIS_MODULE,
 121 };
 122 
 123 static int cygnus_pcie_phy_probe(struct platform_device *pdev)
 124 {
 125         struct device *dev = &pdev->dev;
 126         struct device_node *node = dev->of_node, *child;
 127         struct cygnus_pcie_phy_core *core;
 128         struct phy_provider *provider;
 129         struct resource *res;
 130         unsigned cnt = 0;
 131         int ret;
 132 
 133         if (of_get_child_count(node) == 0) {
 134                 dev_err(dev, "PHY no child node\n");
 135                 return -ENODEV;
 136         }
 137 
 138         core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL);
 139         if (!core)
 140                 return -ENOMEM;
 141 
 142         core->dev = dev;
 143 
 144         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 145         core->base = devm_ioremap_resource(dev, res);
 146         if (IS_ERR(core->base))
 147                 return PTR_ERR(core->base);
 148 
 149         mutex_init(&core->lock);
 150 
 151         for_each_available_child_of_node(node, child) {
 152                 unsigned int id;
 153                 struct cygnus_pcie_phy *p;
 154 
 155                 if (of_property_read_u32(child, "reg", &id)) {
 156                         dev_err(dev, "missing reg property for %pOFn\n",
 157                                 child);
 158                         ret = -EINVAL;
 159                         goto put_child;
 160                 }
 161 
 162                 if (id >= MAX_NUM_PHYS) {
 163                         dev_err(dev, "invalid PHY id: %u\n", id);
 164                         ret = -EINVAL;
 165                         goto put_child;
 166                 }
 167 
 168                 if (core->phys[id].phy) {
 169                         dev_err(dev, "duplicated PHY id: %u\n", id);
 170                         ret = -EINVAL;
 171                         goto put_child;
 172                 }
 173 
 174                 p = &core->phys[id];
 175                 p->phy = devm_phy_create(dev, child, &cygnus_pcie_phy_ops);
 176                 if (IS_ERR(p->phy)) {
 177                         dev_err(dev, "failed to create PHY\n");
 178                         ret = PTR_ERR(p->phy);
 179                         goto put_child;
 180                 }
 181 
 182                 p->core = core;
 183                 p->id = id;
 184                 phy_set_drvdata(p->phy, p);
 185                 cnt++;
 186         }
 187 
 188         dev_set_drvdata(dev, core);
 189 
 190         provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 191         if (IS_ERR(provider)) {
 192                 dev_err(dev, "failed to register PHY provider\n");
 193                 return PTR_ERR(provider);
 194         }
 195 
 196         dev_dbg(dev, "registered %u PCIe PHY(s)\n", cnt);
 197 
 198         return 0;
 199 put_child:
 200         of_node_put(child);
 201         return ret;
 202 }
 203 
 204 static const struct of_device_id cygnus_pcie_phy_match_table[] = {
 205         { .compatible = "brcm,cygnus-pcie-phy" },
 206         { /* sentinel */ }
 207 };
 208 MODULE_DEVICE_TABLE(of, cygnus_pcie_phy_match_table);
 209 
 210 static struct platform_driver cygnus_pcie_phy_driver = {
 211         .driver = {
 212                 .name = "cygnus-pcie-phy",
 213                 .of_match_table = cygnus_pcie_phy_match_table,
 214         },
 215         .probe = cygnus_pcie_phy_probe,
 216 };
 217 module_platform_driver(cygnus_pcie_phy_driver);
 218 
 219 MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
 220 MODULE_DESCRIPTION("Broadcom Cygnus PCIe PHY driver");
 221 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */