alistair23-linux/drivers/phy/marvell/phy-armada375-usb2.c
Vivek Gautam 0b56e9a7e8 phy: Group vendor specific phy drivers
Adding vendor specific directories in phy to group
phy drivers under their respective vendor umbrella.

Also updated the MAINTAINERS file to reflect the correct
directory structure for phy drivers.

Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
Acked-by: Heiko Stuebner <heiko@sntech.de>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Acked-by: Krzysztof Kozlowski <krzk@kernel.org>
Acked-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com>
Cc: Kishon Vijay Abraham I <kishon@ti.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Heiko Stuebner <heiko@sntech.de>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Sylwester Nawrocki <s.nawrocki@samsung.com>
Cc: Krzysztof Kozlowski <krzk@kernel.org>
Cc: Jaehoon Chung <jh80.chung@samsung.com>
Cc: Stephen Boyd <stephen.boyd@linaro.org>
Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-arm-msm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-omap@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org
Cc: linux-rockchip@lists.infradead.org
Cc: linux-samsung-soc@vger.kernel.org
Cc: linux-usb@vger.kernel.org
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
2017-06-01 15:28:33 +05:30

159 lines
4.3 KiB
C

/*
* USB cluster support for Armada 375 platform.
*
* Copyright (C) 2014 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2 or later. This program is licensed "as is"
* without any warranty of any kind, whether express or implied.
*
* Armada 375 comes with an USB2 host and device controller and an
* USB3 controller. The USB cluster control register allows to manage
* common features of both USB controllers.
*/
#include <dt-bindings/phy/phy.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#define USB2_PHY_CONFIG_DISABLE BIT(0)
struct armada375_cluster_phy {
struct phy *phy;
void __iomem *reg;
bool use_usb3;
int phy_provided;
};
static int armada375_usb_phy_init(struct phy *phy)
{
struct armada375_cluster_phy *cluster_phy;
u32 reg;
cluster_phy = phy_get_drvdata(phy);
if (!cluster_phy)
return -ENODEV;
reg = readl(cluster_phy->reg);
if (cluster_phy->use_usb3)
reg |= USB2_PHY_CONFIG_DISABLE;
else
reg &= ~USB2_PHY_CONFIG_DISABLE;
writel(reg, cluster_phy->reg);
return 0;
}
static const struct phy_ops armada375_usb_phy_ops = {
.init = armada375_usb_phy_init,
.owner = THIS_MODULE,
};
/*
* Only one controller can use this PHY. We shouldn't have the case
* when two controllers want to use this PHY. But if this case occurs
* then we provide a phy to the first one and return an error for the
* next one. This error has also to be an error returned by
* devm_phy_optional_get() so different from ENODEV for USB2. In the
* USB3 case it still optional and we use ENODEV.
*/
static struct phy *armada375_usb_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct armada375_cluster_phy *cluster_phy = dev_get_drvdata(dev);
if (!cluster_phy)
return ERR_PTR(-ENODEV);
/*
* Either the phy had never been requested and then the first
* usb claiming it can get it, or it had already been
* requested in this case, we only allow to use it with the
* same configuration.
*/
if (WARN_ON((cluster_phy->phy_provided != PHY_NONE) &&
(cluster_phy->phy_provided != args->args[0]))) {
dev_err(dev, "This PHY has already been provided!\n");
dev_err(dev, "Check your device tree, only one controller can use it\n.");
if (args->args[0] == PHY_TYPE_USB2)
return ERR_PTR(-EBUSY);
else
return ERR_PTR(-ENODEV);
}
if (args->args[0] == PHY_TYPE_USB2)
cluster_phy->use_usb3 = false;
else if (args->args[0] == PHY_TYPE_USB3)
cluster_phy->use_usb3 = true;
else {
dev_err(dev, "Invalid PHY mode\n");
return ERR_PTR(-ENODEV);
}
/* Store which phy mode is used for next test */
cluster_phy->phy_provided = args->args[0];
return cluster_phy->phy;
}
static int armada375_usb_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy *phy;
struct phy_provider *phy_provider;
void __iomem *usb_cluster_base;
struct resource *res;
struct armada375_cluster_phy *cluster_phy;
cluster_phy = devm_kzalloc(dev, sizeof(*cluster_phy), GFP_KERNEL);
if (!cluster_phy)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
usb_cluster_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(usb_cluster_base))
return PTR_ERR(usb_cluster_base);
phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create PHY\n");
return PTR_ERR(phy);
}
cluster_phy->phy = phy;
cluster_phy->reg = usb_cluster_base;
dev_set_drvdata(dev, cluster_phy);
phy_set_drvdata(phy, cluster_phy);
phy_provider = devm_of_phy_provider_register(&pdev->dev,
armada375_usb_phy_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id of_usb_cluster_table[] = {
{ .compatible = "marvell,armada-375-usb-cluster", },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, of_usb_cluster_table);
static struct platform_driver armada375_usb_phy_driver = {
.probe = armada375_usb_phy_probe,
.driver = {
.of_match_table = of_usb_cluster_table,
.name = "armada-375-usb-cluster",
}
};
module_platform_driver(armada375_usb_phy_driver);
MODULE_DESCRIPTION("Armada 375 USB cluster driver");
MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
MODULE_LICENSE("GPL");