// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "amlogic-pcie-phy.h" static struct regmap_config amlogic_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, }; static struct regmap *amlogic_map_resource(struct amlogic_pcie_phy *pcie_phy, struct device_node *node, char *name) { struct resource res; void __iomem *base; int i; i = of_property_match_string(node, "reg-names", name); if (of_address_to_resource(node, i, &res)) return NULL; base = devm_ioremap(pcie_phy->dev, res.start, resource_size(&res)); if (IS_ERR(base)) return ERR_CAST(base); amlogic_regmap_config.max_register = resource_size(&res) - 4; amlogic_regmap_config.name = devm_kasprintf(pcie_phy->dev, GFP_KERNEL, "%pOFn-%s", node, name); if (!amlogic_regmap_config.name) return ERR_PTR(-ENOMEM); return devm_regmap_init_mmio(pcie_phy->dev, base, &amlogic_regmap_config); } static int amlogic_pcie_phy_parse_dt(struct amlogic_pcie_phy *pcie_phy, struct device_node *node) { pcie_phy->regmap = amlogic_map_resource(pcie_phy, node, "phy"); if (IS_ERR_OR_NULL(pcie_phy->regmap)) { dev_err(pcie_phy->dev, "failed map phy reg base\n"); return pcie_phy->regmap ? PTR_ERR(pcie_phy->regmap) : -ENOENT; } if (pcie_phy->data->parse_dt) return pcie_phy->data->parse_dt(pcie_phy); return 0; } static int amlogic_pcie_phy_reset(struct phy *phy) { struct amlogic_pcie_phy *pcie_phy = phy_get_drvdata(phy); if (pcie_phy->data->phy_reset) return pcie_phy->data->phy_reset(pcie_phy); return 0; } static int amlogic_pcie_phy_exit(struct phy *phy) { struct amlogic_pcie_phy *pcie_phy = phy_get_drvdata(phy); if (pcie_phy->data->phy_exit) return pcie_phy->data->phy_exit(pcie_phy); return 0; } static int amlogic_pcie_phy_init(struct phy *phy) { struct amlogic_pcie_phy *pcie_phy = phy_get_drvdata(phy); if (pcie_phy->data->phy_init) return pcie_phy->data->phy_init(pcie_phy); return 0; } static int amlogic_pcie_phy_power_on(struct phy *phy) { struct amlogic_pcie_phy *pcie_phy = phy_get_drvdata(phy); if (pcie_phy->data->phy_power_on) return pcie_phy->data->phy_power_on(pcie_phy); return 0; } static int amlogic_pcie_phy_power_off(struct phy *phy) { struct amlogic_pcie_phy *pcie_phy = phy_get_drvdata(phy); if (pcie_phy->data->phy_power_off) return pcie_phy->data->phy_power_off(pcie_phy); return 0; } static const struct phy_ops pcie_phy_ops = { .init = amlogic_pcie_phy_init, .exit = amlogic_pcie_phy_exit, .power_on = amlogic_pcie_phy_power_on, .power_off = amlogic_pcie_phy_power_off, .reset = amlogic_pcie_phy_reset, .owner = THIS_MODULE, }; int amlogic_pcie_phy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct amlogic_pcie_phy *pcie_phy; struct phy_provider *pphy; int ret; pcie_phy = devm_kzalloc(dev, sizeof(struct amlogic_pcie_phy), GFP_KERNEL); if (!pcie_phy) return -ENOMEM; pcie_phy->dev = dev; pcie_phy->data = (struct amlogic_pcie_phy_data *)of_device_get_match_data(dev); if (!pcie_phy->data) return -EINVAL; pm_runtime_enable(pcie_phy->dev); ret = amlogic_pcie_phy_parse_dt(pcie_phy, np); if (ret) return ret; pcie_phy->phy = devm_phy_create(dev, np, &pcie_phy_ops); if (IS_ERR(pcie_phy->phy)) { ret = PTR_ERR(pcie_phy->phy); if (ret != -EPROBE_DEFER) dev_err(dev, "failed to create PCIE PHY\n"); return ret; } phy_set_drvdata(pcie_phy->phy, pcie_phy); dev_set_drvdata(dev, pcie_phy); pphy = devm_of_phy_provider_register(dev, of_phy_simple_xlate); return PTR_ERR_OR_ZERO(pphy); } EXPORT_SYMBOL(amlogic_pcie_phy_probe);