Files

168 lines
4.1 KiB
C

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/device.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/property.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/string.h>
#include <linux/string_helpers.h>
#include <linux/ctype.h>
#include <linux/gpio/consumer.h>
#include <linux/phy/phy.h>
#include <linux/reset.h>
#include <linux/kprobes.h>
#include <linux/highmem.h>
#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);