Messages in this thread Patch in this message | | | From | Ray Jui <> | Subject | [PATCH v3 2/2] phy: cygnus: pcie: Add Cygnus PCIe PHY support | Date | Tue, 15 Sep 2015 10:56:37 -0700 |
| |
This patch adds the PCIe PHY support for the Broadcom PCIe RC interface on Cygnus
Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Arun Parameswaran <aparames@broadcom.com> Reviewed-by: JD (Jiandong) Zheng <jdzheng@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- drivers/phy/Kconfig | 9 ++ drivers/phy/Makefile | 1 + drivers/phy/phy-bcm-cygnus-pcie.c | 198 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 drivers/phy/phy-bcm-cygnus-pcie.c
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 47da573..947bae2 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -371,4 +371,13 @@ config PHY_BRCMSTB_SATA Enable this to support the SATA3 PHY on 28nm Broadcom STB SoCs. Likely useful only with CONFIG_SATA_BRCMSTB enabled. +config PHY_CYGNUS_PCIE + tristate "Broadcom Cygnus PCIe PHY driver" + depends on OF && (ARCH_BCM_CYGNUS || COMPILE_TEST) + select GENERIC_PHY + default ARCH_BCM_CYGNUS + help + Enable this to support the Broadcom Cygnus PCIe PHY. + If unsure, say N. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index a5b18c1..fdce78d 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -46,3 +46,4 @@ obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o +obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o diff --git a/drivers/phy/phy-bcm-cygnus-pcie.c b/drivers/phy/phy-bcm-cygnus-pcie.c new file mode 100644 index 0000000..0fb6750 --- /dev/null +++ b/drivers/phy/phy-bcm-cygnus-pcie.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> + +#define PCIE_CFG_OFFSET 0x00 +#define PCIE1_PHY_IDDQ_SHIFT 10 +#define PCIE0_PHY_IDDQ_SHIFT 2 + +enum cygnus_pcie_phy_id { + CYGNUS_PHY_PCIE0 = 0, + CYGNUS_PHY_PCIE1, + MAX_NUM_PHYS, +}; + +struct cygnus_pcie_phy_core; + +/** + * struct cygnus_pcie_phy - Cygnus PCIe PHY device + * @core: pointer to the Cygnus PCIe PHY core control + * @id: internal ID to identify the Cygnus PCIe PHY + * @phy: pointer to the kernel PHY device + */ +struct cygnus_pcie_phy { + struct cygnus_pcie_phy_core *core; + enum cygnus_pcie_phy_id id; + struct phy *phy; +}; + +/** + * struct cygnus_pcie_phy_core - Cygnus PCIe PHY core control + * @dev: pointer to device + * @base: base register + * @lock: mutex to protect access to individual PHYs + * @phys: pointer to Cygnus PHY device + */ +struct cygnus_pcie_phy_core { + struct device *dev; + void __iomem *base; + struct mutex lock; + struct cygnus_pcie_phy phys[MAX_NUM_PHYS]; +}; + +static int cygnus_pcie_power_config(struct cygnus_pcie_phy *phy, bool enable) +{ + struct cygnus_pcie_phy_core *core = phy->core; + unsigned shift; + u32 val; + + mutex_lock(&core->lock); + + switch (phy->id) { + case CYGNUS_PHY_PCIE0: + shift = PCIE0_PHY_IDDQ_SHIFT; + break; + + case CYGNUS_PHY_PCIE1: + shift = PCIE1_PHY_IDDQ_SHIFT; + break; + + default: + mutex_unlock(&core->lock); + dev_err(core->dev, "PCIe PHY id%d invalid\n", phy->id); + return -EINVAL; + } + + if (enable) { + val = readl(core->base + PCIE_CFG_OFFSET); + val &= ~BIT(shift); + writel(val, core->base + PCIE_CFG_OFFSET); + msleep(50); + } else { + val = readl(core->base + PCIE_CFG_OFFSET); + val |= BIT(shift); + writel(val, core->base + PCIE_CFG_OFFSET); + } + + mutex_unlock(&core->lock); + dev_info(core->dev, "PCIe PHY %s\n", enable ? "enabled" : "disabled"); + return 0; +} + +static int cygnus_pcie_phy_power_on(struct phy *p) +{ + struct cygnus_pcie_phy *phy = phy_get_drvdata(p); + + return cygnus_pcie_power_config(phy, true); +} + +static int cygnus_pcie_phy_power_off(struct phy *p) +{ + struct cygnus_pcie_phy *phy = phy_get_drvdata(p); + + return cygnus_pcie_power_config(phy, false); +} + +static struct phy_ops cygnus_pcie_phy_ops = { + .power_on = cygnus_pcie_phy_power_on, + .power_off = cygnus_pcie_phy_power_off, +}; + +static struct phy *cygnus_pcie_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct cygnus_pcie_phy_core *core; + int id; + + core = dev_get_drvdata(dev); + if (!core) + return ERR_PTR(-EINVAL); + + id = args->args[0]; + + if (WARN_ON(id >= MAX_NUM_PHYS)) + return ERR_PTR(-ENODEV); + + return core->phys[id].phy; +} + +static int cygnus_pcie_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cygnus_pcie_phy_core *core; + struct phy_provider *provider; + struct resource *res; + int i = 0; + + core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); + if (!core) + return -ENOMEM; + + core->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + core->base = devm_ioremap_resource(dev, res); + if (IS_ERR(core->base)) + return PTR_ERR(core->base); + + mutex_init(&core->lock); + + for (i = 0; i < MAX_NUM_PHYS; i++) { + struct cygnus_pcie_phy *p = &core->phys[i]; + + p->phy = devm_phy_create(dev, NULL, &cygnus_pcie_phy_ops); + if (IS_ERR(p->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(p->phy); + } + + p->core = core; + p->id = i; + phy_set_drvdata(p->phy, p); + } + + dev_set_drvdata(dev, core); + + provider = devm_of_phy_provider_register(dev, cygnus_pcie_phy_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "failed to register PHY provider\n"); + return PTR_ERR(provider); + } + + return 0; +} + +static const struct of_device_id cygnus_pcie_phy_match_table[] = { + { .compatible = "brcm,cygnus-pcie-phy" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cygnus_pcie_phy_match_table); + +static struct platform_driver cygnus_pcie_phy_driver = { + .driver = { + .name = "cygnus-pcie-phy", + .of_match_table = cygnus_pcie_phy_match_table, + }, + .probe = cygnus_pcie_phy_probe, +}; +module_platform_driver(cygnus_pcie_phy_driver); + +MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom Cygnus PCIe PHY driver"); +MODULE_LICENSE("GPL v2"); -- 1.9.1
| |