10b56e9a7SVivek Gautam /* 20b56e9a7SVivek Gautam * Copyright (C) 2015 Broadcom Corporation 30b56e9a7SVivek Gautam * 40b56e9a7SVivek Gautam * This program is free software; you can redistribute it and/or 50b56e9a7SVivek Gautam * modify it under the terms of the GNU General Public License as 60b56e9a7SVivek Gautam * published by the Free Software Foundation version 2. 70b56e9a7SVivek Gautam * 80b56e9a7SVivek Gautam * This program is distributed "as is" WITHOUT ANY WARRANTY of any 90b56e9a7SVivek Gautam * kind, whether express or implied; without even the implied warranty 100b56e9a7SVivek Gautam * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 110b56e9a7SVivek Gautam * GNU General Public License for more details. 120b56e9a7SVivek Gautam */ 130b56e9a7SVivek Gautam 140b56e9a7SVivek Gautam #include <linux/delay.h> 150b56e9a7SVivek Gautam #include <linux/io.h> 160b56e9a7SVivek Gautam #include <linux/module.h> 170b56e9a7SVivek Gautam #include <linux/of.h> 180b56e9a7SVivek Gautam #include <linux/phy/phy.h> 190b56e9a7SVivek Gautam #include <linux/platform_device.h> 200b56e9a7SVivek Gautam 210b56e9a7SVivek Gautam #define PCIE_CFG_OFFSET 0x00 220b56e9a7SVivek Gautam #define PCIE1_PHY_IDDQ_SHIFT 10 230b56e9a7SVivek Gautam #define PCIE0_PHY_IDDQ_SHIFT 2 240b56e9a7SVivek Gautam 250b56e9a7SVivek Gautam enum cygnus_pcie_phy_id { 260b56e9a7SVivek Gautam CYGNUS_PHY_PCIE0 = 0, 270b56e9a7SVivek Gautam CYGNUS_PHY_PCIE1, 280b56e9a7SVivek Gautam MAX_NUM_PHYS, 290b56e9a7SVivek Gautam }; 300b56e9a7SVivek Gautam 310b56e9a7SVivek Gautam struct cygnus_pcie_phy_core; 320b56e9a7SVivek Gautam 330b56e9a7SVivek Gautam /** 340b56e9a7SVivek Gautam * struct cygnus_pcie_phy - Cygnus PCIe PHY device 350b56e9a7SVivek Gautam * @core: pointer to the Cygnus PCIe PHY core control 360b56e9a7SVivek Gautam * @id: internal ID to identify the Cygnus PCIe PHY 370b56e9a7SVivek Gautam * @phy: pointer to the kernel PHY device 380b56e9a7SVivek Gautam */ 390b56e9a7SVivek Gautam struct cygnus_pcie_phy { 400b56e9a7SVivek Gautam struct cygnus_pcie_phy_core *core; 410b56e9a7SVivek Gautam enum cygnus_pcie_phy_id id; 420b56e9a7SVivek Gautam struct phy *phy; 430b56e9a7SVivek Gautam }; 440b56e9a7SVivek Gautam 450b56e9a7SVivek Gautam /** 460b56e9a7SVivek Gautam * struct cygnus_pcie_phy_core - Cygnus PCIe PHY core control 470b56e9a7SVivek Gautam * @dev: pointer to device 480b56e9a7SVivek Gautam * @base: base register 490b56e9a7SVivek Gautam * @lock: mutex to protect access to individual PHYs 500b56e9a7SVivek Gautam * @phys: pointer to Cygnus PHY device 510b56e9a7SVivek Gautam */ 520b56e9a7SVivek Gautam struct cygnus_pcie_phy_core { 530b56e9a7SVivek Gautam struct device *dev; 540b56e9a7SVivek Gautam void __iomem *base; 550b56e9a7SVivek Gautam struct mutex lock; 560b56e9a7SVivek Gautam struct cygnus_pcie_phy phys[MAX_NUM_PHYS]; 570b56e9a7SVivek Gautam }; 580b56e9a7SVivek Gautam 590b56e9a7SVivek Gautam static int cygnus_pcie_power_config(struct cygnus_pcie_phy *phy, bool enable) 600b56e9a7SVivek Gautam { 610b56e9a7SVivek Gautam struct cygnus_pcie_phy_core *core = phy->core; 620b56e9a7SVivek Gautam unsigned shift; 630b56e9a7SVivek Gautam u32 val; 640b56e9a7SVivek Gautam 650b56e9a7SVivek Gautam mutex_lock(&core->lock); 660b56e9a7SVivek Gautam 670b56e9a7SVivek Gautam switch (phy->id) { 680b56e9a7SVivek Gautam case CYGNUS_PHY_PCIE0: 690b56e9a7SVivek Gautam shift = PCIE0_PHY_IDDQ_SHIFT; 700b56e9a7SVivek Gautam break; 710b56e9a7SVivek Gautam 720b56e9a7SVivek Gautam case CYGNUS_PHY_PCIE1: 730b56e9a7SVivek Gautam shift = PCIE1_PHY_IDDQ_SHIFT; 740b56e9a7SVivek Gautam break; 750b56e9a7SVivek Gautam 760b56e9a7SVivek Gautam default: 770b56e9a7SVivek Gautam mutex_unlock(&core->lock); 780b56e9a7SVivek Gautam dev_err(core->dev, "PCIe PHY %d invalid\n", phy->id); 790b56e9a7SVivek Gautam return -EINVAL; 800b56e9a7SVivek Gautam } 810b56e9a7SVivek Gautam 820b56e9a7SVivek Gautam if (enable) { 830b56e9a7SVivek Gautam val = readl(core->base + PCIE_CFG_OFFSET); 840b56e9a7SVivek Gautam val &= ~BIT(shift); 850b56e9a7SVivek Gautam writel(val, core->base + PCIE_CFG_OFFSET); 860b56e9a7SVivek Gautam /* 870b56e9a7SVivek Gautam * Wait 50 ms for the PCIe Serdes to stabilize after the analog 880b56e9a7SVivek Gautam * front end is brought up 890b56e9a7SVivek Gautam */ 900b56e9a7SVivek Gautam msleep(50); 910b56e9a7SVivek Gautam } else { 920b56e9a7SVivek Gautam val = readl(core->base + PCIE_CFG_OFFSET); 930b56e9a7SVivek Gautam val |= BIT(shift); 940b56e9a7SVivek Gautam writel(val, core->base + PCIE_CFG_OFFSET); 950b56e9a7SVivek Gautam } 960b56e9a7SVivek Gautam 970b56e9a7SVivek Gautam mutex_unlock(&core->lock); 980b56e9a7SVivek Gautam dev_dbg(core->dev, "PCIe PHY %d %s\n", phy->id, 990b56e9a7SVivek Gautam enable ? "enabled" : "disabled"); 1000b56e9a7SVivek Gautam return 0; 1010b56e9a7SVivek Gautam } 1020b56e9a7SVivek Gautam 1030b56e9a7SVivek Gautam static int cygnus_pcie_phy_power_on(struct phy *p) 1040b56e9a7SVivek Gautam { 1050b56e9a7SVivek Gautam struct cygnus_pcie_phy *phy = phy_get_drvdata(p); 1060b56e9a7SVivek Gautam 1070b56e9a7SVivek Gautam return cygnus_pcie_power_config(phy, true); 1080b56e9a7SVivek Gautam } 1090b56e9a7SVivek Gautam 1100b56e9a7SVivek Gautam static int cygnus_pcie_phy_power_off(struct phy *p) 1110b56e9a7SVivek Gautam { 1120b56e9a7SVivek Gautam struct cygnus_pcie_phy *phy = phy_get_drvdata(p); 1130b56e9a7SVivek Gautam 1140b56e9a7SVivek Gautam return cygnus_pcie_power_config(phy, false); 1150b56e9a7SVivek Gautam } 1160b56e9a7SVivek Gautam 1170b56e9a7SVivek Gautam static const struct phy_ops cygnus_pcie_phy_ops = { 1180b56e9a7SVivek Gautam .power_on = cygnus_pcie_phy_power_on, 1190b56e9a7SVivek Gautam .power_off = cygnus_pcie_phy_power_off, 1200b56e9a7SVivek Gautam .owner = THIS_MODULE, 1210b56e9a7SVivek Gautam }; 1220b56e9a7SVivek Gautam 1230b56e9a7SVivek Gautam static int cygnus_pcie_phy_probe(struct platform_device *pdev) 1240b56e9a7SVivek Gautam { 1250b56e9a7SVivek Gautam struct device *dev = &pdev->dev; 1260b56e9a7SVivek Gautam struct device_node *node = dev->of_node, *child; 1270b56e9a7SVivek Gautam struct cygnus_pcie_phy_core *core; 1280b56e9a7SVivek Gautam struct phy_provider *provider; 1290b56e9a7SVivek Gautam unsigned cnt = 0; 1300b56e9a7SVivek Gautam int ret; 1310b56e9a7SVivek Gautam 1320b56e9a7SVivek Gautam if (of_get_child_count(node) == 0) { 1330b56e9a7SVivek Gautam dev_err(dev, "PHY no child node\n"); 1340b56e9a7SVivek Gautam return -ENODEV; 1350b56e9a7SVivek Gautam } 1360b56e9a7SVivek Gautam 1370b56e9a7SVivek Gautam core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); 1380b56e9a7SVivek Gautam if (!core) 1390b56e9a7SVivek Gautam return -ENOMEM; 1400b56e9a7SVivek Gautam 1410b56e9a7SVivek Gautam core->dev = dev; 1420b56e9a7SVivek Gautam 143*f669bc8bSChunfeng Yun core->base = devm_platform_ioremap_resource(pdev, 0); 1440b56e9a7SVivek Gautam if (IS_ERR(core->base)) 1450b56e9a7SVivek Gautam return PTR_ERR(core->base); 1460b56e9a7SVivek Gautam 1470b56e9a7SVivek Gautam mutex_init(&core->lock); 1480b56e9a7SVivek Gautam 1490b56e9a7SVivek Gautam for_each_available_child_of_node(node, child) { 1500b56e9a7SVivek Gautam unsigned int id; 1510b56e9a7SVivek Gautam struct cygnus_pcie_phy *p; 1520b56e9a7SVivek Gautam 1530b56e9a7SVivek Gautam if (of_property_read_u32(child, "reg", &id)) { 154ac9ba7dcSRob Herring dev_err(dev, "missing reg property for %pOFn\n", 155ac9ba7dcSRob Herring child); 1560b56e9a7SVivek Gautam ret = -EINVAL; 1570b56e9a7SVivek Gautam goto put_child; 1580b56e9a7SVivek Gautam } 1590b56e9a7SVivek Gautam 1600b56e9a7SVivek Gautam if (id >= MAX_NUM_PHYS) { 1610b56e9a7SVivek Gautam dev_err(dev, "invalid PHY id: %u\n", id); 1620b56e9a7SVivek Gautam ret = -EINVAL; 1630b56e9a7SVivek Gautam goto put_child; 1640b56e9a7SVivek Gautam } 1650b56e9a7SVivek Gautam 1660b56e9a7SVivek Gautam if (core->phys[id].phy) { 1670b56e9a7SVivek Gautam dev_err(dev, "duplicated PHY id: %u\n", id); 1680b56e9a7SVivek Gautam ret = -EINVAL; 1690b56e9a7SVivek Gautam goto put_child; 1700b56e9a7SVivek Gautam } 1710b56e9a7SVivek Gautam 1720b56e9a7SVivek Gautam p = &core->phys[id]; 1730b56e9a7SVivek Gautam p->phy = devm_phy_create(dev, child, &cygnus_pcie_phy_ops); 1740b56e9a7SVivek Gautam if (IS_ERR(p->phy)) { 1750b56e9a7SVivek Gautam dev_err(dev, "failed to create PHY\n"); 1760b56e9a7SVivek Gautam ret = PTR_ERR(p->phy); 1770b56e9a7SVivek Gautam goto put_child; 1780b56e9a7SVivek Gautam } 1790b56e9a7SVivek Gautam 1800b56e9a7SVivek Gautam p->core = core; 1810b56e9a7SVivek Gautam p->id = id; 1820b56e9a7SVivek Gautam phy_set_drvdata(p->phy, p); 1830b56e9a7SVivek Gautam cnt++; 1840b56e9a7SVivek Gautam } 1850b56e9a7SVivek Gautam 1860b56e9a7SVivek Gautam dev_set_drvdata(dev, core); 1870b56e9a7SVivek Gautam 1880b56e9a7SVivek Gautam provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 1890b56e9a7SVivek Gautam if (IS_ERR(provider)) { 1900b56e9a7SVivek Gautam dev_err(dev, "failed to register PHY provider\n"); 1910b56e9a7SVivek Gautam return PTR_ERR(provider); 1920b56e9a7SVivek Gautam } 1930b56e9a7SVivek Gautam 1940b56e9a7SVivek Gautam dev_dbg(dev, "registered %u PCIe PHY(s)\n", cnt); 1950b56e9a7SVivek Gautam 1960b56e9a7SVivek Gautam return 0; 1970b56e9a7SVivek Gautam put_child: 1980b56e9a7SVivek Gautam of_node_put(child); 1990b56e9a7SVivek Gautam return ret; 2000b56e9a7SVivek Gautam } 2010b56e9a7SVivek Gautam 2020b56e9a7SVivek Gautam static const struct of_device_id cygnus_pcie_phy_match_table[] = { 2030b56e9a7SVivek Gautam { .compatible = "brcm,cygnus-pcie-phy" }, 2040b56e9a7SVivek Gautam { /* sentinel */ } 2050b56e9a7SVivek Gautam }; 2060b56e9a7SVivek Gautam MODULE_DEVICE_TABLE(of, cygnus_pcie_phy_match_table); 2070b56e9a7SVivek Gautam 2080b56e9a7SVivek Gautam static struct platform_driver cygnus_pcie_phy_driver = { 2090b56e9a7SVivek Gautam .driver = { 2100b56e9a7SVivek Gautam .name = "cygnus-pcie-phy", 2110b56e9a7SVivek Gautam .of_match_table = cygnus_pcie_phy_match_table, 2120b56e9a7SVivek Gautam }, 2130b56e9a7SVivek Gautam .probe = cygnus_pcie_phy_probe, 2140b56e9a7SVivek Gautam }; 2150b56e9a7SVivek Gautam module_platform_driver(cygnus_pcie_phy_driver); 2160b56e9a7SVivek Gautam 2170b56e9a7SVivek Gautam MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); 2180b56e9a7SVivek Gautam MODULE_DESCRIPTION("Broadcom Cygnus PCIe PHY driver"); 2190b56e9a7SVivek Gautam MODULE_LICENSE("GPL v2"); 220