18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016-2018 Broadcom 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/clk.h> 78c2ecf20Sopenharmony_ci#include <linux/delay.h> 88c2ecf20Sopenharmony_ci#include <linux/io.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/regmap.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* we have up to 8 PAXB based RC. The 9th one is always PAXC */ 178c2ecf20Sopenharmony_ci#define SR_NR_PCIE_PHYS 9 188c2ecf20Sopenharmony_ci#define SR_PAXC_PHY_IDX (SR_NR_PCIE_PHYS - 1) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define PCIE_PIPEMUX_CFG_OFFSET 0x10c 218c2ecf20Sopenharmony_ci#define PCIE_PIPEMUX_SELECT_STRAP 0xf 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define CDRU_STRAP_DATA_LSW_OFFSET 0x5c 248c2ecf20Sopenharmony_ci#define PCIE_PIPEMUX_SHIFT 19 258c2ecf20Sopenharmony_ci#define PCIE_PIPEMUX_MASK 0xf 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define MHB_MEM_PW_PAXC_OFFSET 0x1c0 288c2ecf20Sopenharmony_ci#define MHB_PWR_ARR_POWERON 0x8 298c2ecf20Sopenharmony_ci#define MHB_PWR_ARR_POWEROK 0x4 308c2ecf20Sopenharmony_ci#define MHB_PWR_POWERON 0x2 318c2ecf20Sopenharmony_ci#define MHB_PWR_POWEROK 0x1 328c2ecf20Sopenharmony_ci#define MHB_PWR_STATUS_MASK (MHB_PWR_ARR_POWERON | \ 338c2ecf20Sopenharmony_ci MHB_PWR_ARR_POWEROK | \ 348c2ecf20Sopenharmony_ci MHB_PWR_POWERON | \ 358c2ecf20Sopenharmony_ci MHB_PWR_POWEROK) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct sr_pcie_phy_core; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/** 408c2ecf20Sopenharmony_ci * struct sr_pcie_phy - Stingray PCIe PHY 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * @core: pointer to the Stingray PCIe PHY core control 438c2ecf20Sopenharmony_ci * @index: PHY index 448c2ecf20Sopenharmony_ci * @phy: pointer to the kernel PHY device 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_cistruct sr_pcie_phy { 478c2ecf20Sopenharmony_ci struct sr_pcie_phy_core *core; 488c2ecf20Sopenharmony_ci unsigned int index; 498c2ecf20Sopenharmony_ci struct phy *phy; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/** 538c2ecf20Sopenharmony_ci * struct sr_pcie_phy_core - Stingray PCIe PHY core control 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * @dev: pointer to device 568c2ecf20Sopenharmony_ci * @base: base register of PCIe SS 578c2ecf20Sopenharmony_ci * @cdru: regmap to the CDRU device 588c2ecf20Sopenharmony_ci * @mhb: regmap to the MHB device 598c2ecf20Sopenharmony_ci * @pipemux: pipemuex strap 608c2ecf20Sopenharmony_ci * @phys: array of PCIe PHYs 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cistruct sr_pcie_phy_core { 638c2ecf20Sopenharmony_ci struct device *dev; 648c2ecf20Sopenharmony_ci void __iomem *base; 658c2ecf20Sopenharmony_ci struct regmap *cdru; 668c2ecf20Sopenharmony_ci struct regmap *mhb; 678c2ecf20Sopenharmony_ci u32 pipemux; 688c2ecf20Sopenharmony_ci struct sr_pcie_phy phys[SR_NR_PCIE_PHYS]; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * PCIe PIPEMUX lookup table 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * Each array index represents a PIPEMUX strap setting 758c2ecf20Sopenharmony_ci * The array element represents a bitmap where a set bit means the PCIe 768c2ecf20Sopenharmony_ci * core and associated serdes has been enabled as RC and is available for use 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_cistatic const u8 pipemux_table[] = { 798c2ecf20Sopenharmony_ci /* PIPEMUX = 0, EP 1x16 */ 808c2ecf20Sopenharmony_ci 0x00, 818c2ecf20Sopenharmony_ci /* PIPEMUX = 1, EP 1x8 + RC 1x8, core 7 */ 828c2ecf20Sopenharmony_ci 0x80, 838c2ecf20Sopenharmony_ci /* PIPEMUX = 2, EP 4x4 */ 848c2ecf20Sopenharmony_ci 0x00, 858c2ecf20Sopenharmony_ci /* PIPEMUX = 3, RC 2x8, cores 0, 7 */ 868c2ecf20Sopenharmony_ci 0x81, 878c2ecf20Sopenharmony_ci /* PIPEMUX = 4, RC 4x4, cores 0, 1, 6, 7 */ 888c2ecf20Sopenharmony_ci 0xc3, 898c2ecf20Sopenharmony_ci /* PIPEMUX = 5, RC 8x2, all 8 cores */ 908c2ecf20Sopenharmony_ci 0xff, 918c2ecf20Sopenharmony_ci /* PIPEMUX = 6, RC 3x4 + 2x2, cores 0, 2, 3, 6, 7 */ 928c2ecf20Sopenharmony_ci 0xcd, 938c2ecf20Sopenharmony_ci /* PIPEMUX = 7, RC 1x4 + 6x2, cores 0, 2, 3, 4, 5, 6, 7 */ 948c2ecf20Sopenharmony_ci 0xfd, 958c2ecf20Sopenharmony_ci /* PIPEMUX = 8, EP 1x8 + RC 4x2, cores 4, 5, 6, 7 */ 968c2ecf20Sopenharmony_ci 0xf0, 978c2ecf20Sopenharmony_ci /* PIPEMUX = 9, EP 1x8 + RC 2x4, cores 6, 7 */ 988c2ecf20Sopenharmony_ci 0xc0, 998c2ecf20Sopenharmony_ci /* PIPEMUX = 10, EP 2x4 + RC 2x4, cores 1, 6 */ 1008c2ecf20Sopenharmony_ci 0x42, 1018c2ecf20Sopenharmony_ci /* PIPEMUX = 11, EP 2x4 + RC 4x2, cores 2, 3, 4, 5 */ 1028c2ecf20Sopenharmony_ci 0x3c, 1038c2ecf20Sopenharmony_ci /* PIPEMUX = 12, EP 1x4 + RC 6x2, cores 2, 3, 4, 5, 6, 7 */ 1048c2ecf20Sopenharmony_ci 0xfc, 1058c2ecf20Sopenharmony_ci /* PIPEMUX = 13, RC 2x4 + RC 1x4 + 2x2, cores 2, 3, 6 */ 1068c2ecf20Sopenharmony_ci 0x4c, 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* 1108c2ecf20Sopenharmony_ci * Return true if the strap setting is valid 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_cistatic bool pipemux_strap_is_valid(u32 pipemux) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci return !!(pipemux < ARRAY_SIZE(pipemux_table)); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* 1188c2ecf20Sopenharmony_ci * Read the PCIe PIPEMUX from strap 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_cistatic u32 pipemux_strap_read(struct sr_pcie_phy_core *core) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci u32 pipemux; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* 1258c2ecf20Sopenharmony_ci * Read PIPEMUX configuration register to determine the pipemux setting 1268c2ecf20Sopenharmony_ci * 1278c2ecf20Sopenharmony_ci * In the case when the value indicates using HW strap, fall back to 1288c2ecf20Sopenharmony_ci * use HW strap 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci pipemux = readl(core->base + PCIE_PIPEMUX_CFG_OFFSET); 1318c2ecf20Sopenharmony_ci pipemux &= PCIE_PIPEMUX_MASK; 1328c2ecf20Sopenharmony_ci if (pipemux == PCIE_PIPEMUX_SELECT_STRAP) { 1338c2ecf20Sopenharmony_ci regmap_read(core->cdru, CDRU_STRAP_DATA_LSW_OFFSET, &pipemux); 1348c2ecf20Sopenharmony_ci pipemux >>= PCIE_PIPEMUX_SHIFT; 1358c2ecf20Sopenharmony_ci pipemux &= PCIE_PIPEMUX_MASK; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return pipemux; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* 1428c2ecf20Sopenharmony_ci * Given a PIPEMUX strap and PCIe core index, this function returns true if the 1438c2ecf20Sopenharmony_ci * PCIe core needs to be enabled 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_cistatic bool pcie_core_is_for_rc(struct sr_pcie_phy *phy) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct sr_pcie_phy_core *core = phy->core; 1488c2ecf20Sopenharmony_ci unsigned int core_idx = phy->index; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return !!((pipemux_table[core->pipemux] >> core_idx) & 0x1); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int sr_pcie_phy_init(struct phy *p) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct sr_pcie_phy *phy = phy_get_drvdata(p); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* 1588c2ecf20Sopenharmony_ci * Check whether this PHY is for root complex or not. If yes, return 1598c2ecf20Sopenharmony_ci * zero so the host driver can proceed to enumeration. If not, return 1608c2ecf20Sopenharmony_ci * an error and that will force the host driver to bail out 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_ci if (pcie_core_is_for_rc(phy)) 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return -ENODEV; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int sr_paxc_phy_init(struct phy *p) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct sr_pcie_phy *phy = phy_get_drvdata(p); 1718c2ecf20Sopenharmony_ci struct sr_pcie_phy_core *core = phy->core; 1728c2ecf20Sopenharmony_ci unsigned int core_idx = phy->index; 1738c2ecf20Sopenharmony_ci u32 val; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (core_idx != SR_PAXC_PHY_IDX) 1768c2ecf20Sopenharmony_ci return -EINVAL; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci regmap_read(core->mhb, MHB_MEM_PW_PAXC_OFFSET, &val); 1798c2ecf20Sopenharmony_ci if ((val & MHB_PWR_STATUS_MASK) != MHB_PWR_STATUS_MASK) { 1808c2ecf20Sopenharmony_ci dev_err(core->dev, "PAXC is not powered up\n"); 1818c2ecf20Sopenharmony_ci return -ENODEV; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic const struct phy_ops sr_pcie_phy_ops = { 1888c2ecf20Sopenharmony_ci .init = sr_pcie_phy_init, 1898c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic const struct phy_ops sr_paxc_phy_ops = { 1938c2ecf20Sopenharmony_ci .init = sr_paxc_phy_init, 1948c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic struct phy *sr_pcie_phy_xlate(struct device *dev, 1988c2ecf20Sopenharmony_ci struct of_phandle_args *args) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct sr_pcie_phy_core *core; 2018c2ecf20Sopenharmony_ci int phy_idx; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci core = dev_get_drvdata(dev); 2048c2ecf20Sopenharmony_ci if (!core) 2058c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci phy_idx = args->args[0]; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (WARN_ON(phy_idx >= SR_NR_PCIE_PHYS)) 2108c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return core->phys[phy_idx].phy; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int sr_pcie_phy_probe(struct platform_device *pdev) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2188c2ecf20Sopenharmony_ci struct device_node *node = dev->of_node; 2198c2ecf20Sopenharmony_ci struct sr_pcie_phy_core *core; 2208c2ecf20Sopenharmony_ci struct resource *res; 2218c2ecf20Sopenharmony_ci struct phy_provider *provider; 2228c2ecf20Sopenharmony_ci unsigned int phy_idx = 0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); 2258c2ecf20Sopenharmony_ci if (!core) 2268c2ecf20Sopenharmony_ci return -ENOMEM; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci core->dev = dev; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2318c2ecf20Sopenharmony_ci core->base = devm_ioremap_resource(core->dev, res); 2328c2ecf20Sopenharmony_ci if (IS_ERR(core->base)) 2338c2ecf20Sopenharmony_ci return PTR_ERR(core->base); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci core->cdru = syscon_regmap_lookup_by_phandle(node, "brcm,sr-cdru"); 2368c2ecf20Sopenharmony_ci if (IS_ERR(core->cdru)) { 2378c2ecf20Sopenharmony_ci dev_err(core->dev, "unable to find CDRU device\n"); 2388c2ecf20Sopenharmony_ci return PTR_ERR(core->cdru); 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci core->mhb = syscon_regmap_lookup_by_phandle(node, "brcm,sr-mhb"); 2428c2ecf20Sopenharmony_ci if (IS_ERR(core->mhb)) { 2438c2ecf20Sopenharmony_ci dev_err(core->dev, "unable to find MHB device\n"); 2448c2ecf20Sopenharmony_ci return PTR_ERR(core->mhb); 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* read the PCIe PIPEMUX strap setting */ 2488c2ecf20Sopenharmony_ci core->pipemux = pipemux_strap_read(core); 2498c2ecf20Sopenharmony_ci if (!pipemux_strap_is_valid(core->pipemux)) { 2508c2ecf20Sopenharmony_ci dev_err(core->dev, "invalid PCIe PIPEMUX strap %u\n", 2518c2ecf20Sopenharmony_ci core->pipemux); 2528c2ecf20Sopenharmony_ci return -EIO; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci for (phy_idx = 0; phy_idx < SR_NR_PCIE_PHYS; phy_idx++) { 2568c2ecf20Sopenharmony_ci struct sr_pcie_phy *p = &core->phys[phy_idx]; 2578c2ecf20Sopenharmony_ci const struct phy_ops *ops; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (phy_idx == SR_PAXC_PHY_IDX) 2608c2ecf20Sopenharmony_ci ops = &sr_paxc_phy_ops; 2618c2ecf20Sopenharmony_ci else 2628c2ecf20Sopenharmony_ci ops = &sr_pcie_phy_ops; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci p->phy = devm_phy_create(dev, NULL, ops); 2658c2ecf20Sopenharmony_ci if (IS_ERR(p->phy)) { 2668c2ecf20Sopenharmony_ci dev_err(dev, "failed to create PCIe PHY\n"); 2678c2ecf20Sopenharmony_ci return PTR_ERR(p->phy); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci p->core = core; 2718c2ecf20Sopenharmony_ci p->index = phy_idx; 2728c2ecf20Sopenharmony_ci phy_set_drvdata(p->phy, p); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci dev_set_drvdata(dev, core); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci provider = devm_of_phy_provider_register(dev, sr_pcie_phy_xlate); 2788c2ecf20Sopenharmony_ci if (IS_ERR(provider)) { 2798c2ecf20Sopenharmony_ci dev_err(dev, "failed to register PHY provider\n"); 2808c2ecf20Sopenharmony_ci return PTR_ERR(provider); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci dev_info(dev, "Stingray PCIe PHY driver initialized\n"); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic const struct of_device_id sr_pcie_phy_match_table[] = { 2898c2ecf20Sopenharmony_ci { .compatible = "brcm,sr-pcie-phy" }, 2908c2ecf20Sopenharmony_ci { } 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sr_pcie_phy_match_table); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic struct platform_driver sr_pcie_phy_driver = { 2958c2ecf20Sopenharmony_ci .driver = { 2968c2ecf20Sopenharmony_ci .name = "sr-pcie-phy", 2978c2ecf20Sopenharmony_ci .of_match_table = sr_pcie_phy_match_table, 2988c2ecf20Sopenharmony_ci }, 2998c2ecf20Sopenharmony_ci .probe = sr_pcie_phy_probe, 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_cimodule_platform_driver(sr_pcie_phy_driver); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ray Jui <ray.jui@broadcom.com>"); 3048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Broadcom Stingray PCIe PHY driver"); 3058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 306