| From 08c9d6ceef01893678a5d2e8a15517c745417f21 Mon Sep 17 00:00:00 2001 |
| From: John Crispin <john@phrozen.org> |
| Date: Tue, 6 Mar 2018 10:04:05 +0100 |
| Subject: [PATCH 04/27] phy: add ath79 usb phys |
| |
| Signed-off-by: John Crispin <john@phrozen.org> |
| --- |
| drivers/phy/Kconfig | 16 ++++++ |
| drivers/phy/Makefile | 2 + |
| drivers/phy/phy-ar7100-usb.c | 124 +++++++++++++++++++++++++++++++++++++++++++ |
| drivers/phy/phy-ar7200-usb.c | 108 +++++++++++++++++++++++++++++++++++++ |
| 4 files changed, 250 insertions(+) |
| create mode 100644 drivers/phy/phy-ar7100-usb.c |
| create mode 100644 drivers/phy/phy-ar7200-usb.c |
| |
| --- a/drivers/phy/Kconfig |
| +++ b/drivers/phy/Kconfig |
| @@ -24,6 +24,22 @@ config GENERIC_PHY_MIPI_DPHY |
| Provides a number of helpers a core functions for MIPI D-PHY |
| drivers to us. |
| |
| +config PHY_AR7100_USB |
| + tristate "Atheros AR7100 USB PHY driver" |
| + depends on ATH79 || COMPILE_TEST |
| + default y if USB_EHCI_HCD_PLATFORM |
| + select PHY_SIMPLE |
| + help |
| + Enable this to support the USB PHY on Atheros AR7100 SoCs. |
| + |
| +config PHY_AR7200_USB |
| + tristate "Atheros AR7200 USB PHY driver" |
| + depends on ATH79 || COMPILE_TEST |
| + default y if USB_EHCI_HCD_PLATFORM |
| + select PHY_SIMPLE |
| + help |
| + Enable this to support the USB PHY on Atheros AR7200 SoCs. |
| + |
| config PHY_LPC18XX_USB_OTG |
| tristate "NXP LPC18xx/43xx SoC USB OTG PHY driver" |
| depends on OF && (ARCH_LPC18XX || COMPILE_TEST) |
| --- a/drivers/phy/Makefile |
| +++ b/drivers/phy/Makefile |
| @@ -4,6 +4,8 @@ |
| # |
| |
| obj-$(CONFIG_GENERIC_PHY) += phy-core.o |
| +obj-$(CONFIG_PHY_AR7100_USB) += phy-ar7100-usb.o |
| +obj-$(CONFIG_PHY_AR7200_USB) += phy-ar7200-usb.o |
| obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o |
| obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o |
| obj-$(CONFIG_PHY_XGENE) += phy-xgene.o |
| --- /dev/null |
| +++ b/drivers/phy/phy-ar7100-usb.c |
| @@ -0,0 +1,140 @@ |
| +/* |
| + * Copyright (C) 2018 John Crispin <john@phrozen.org> |
| + * |
| + * 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; either version 2 of the License, or |
| + * (at your option) any later version. |
| + */ |
| + |
| +#include <linux/module.h> |
| +#include <linux/platform_device.h> |
| +#include <linux/phy/phy.h> |
| +#include <linux/delay.h> |
| +#include <linux/reset.h> |
| +#include <linux/of_gpio.h> |
| + |
| +#include <asm/mach-ath79/ath79.h> |
| +#include <asm/mach-ath79/ar71xx_regs.h> |
| + |
| +struct ar7100_usb_phy { |
| + struct reset_control *rst_phy; |
| + struct reset_control *rst_host; |
| + struct reset_control *rst_ohci_dll; |
| + void __iomem *io_base; |
| + struct phy *phy; |
| + int gpio; |
| +}; |
| + |
| +static int ar7100_usb_phy_power_off(struct phy *phy) |
| +{ |
| + struct ar7100_usb_phy *priv = phy_get_drvdata(phy); |
| + int err = 0; |
| + |
| + err |= reset_control_assert(priv->rst_host); |
| + err |= reset_control_assert(priv->rst_phy); |
| + err |= reset_control_assert(priv->rst_ohci_dll); |
| + |
| + return err; |
| +} |
| + |
| +static int ar7100_usb_phy_power_on(struct phy *phy) |
| +{ |
| + struct ar7100_usb_phy *priv = phy_get_drvdata(phy); |
| + int err = 0; |
| + |
| + err |= ar7100_usb_phy_power_off(phy); |
| + mdelay(100); |
| + err |= reset_control_deassert(priv->rst_ohci_dll); |
| + err |= reset_control_deassert(priv->rst_phy); |
| + err |= reset_control_deassert(priv->rst_host); |
| + mdelay(500); |
| + iowrite32(0xf0000, priv->io_base + AR71XX_USB_CTRL_REG_CONFIG); |
| + iowrite32(0x20c00, priv->io_base + AR71XX_USB_CTRL_REG_FLADJ); |
| + |
| + return err; |
| +} |
| + |
| +static const struct phy_ops ar7100_usb_phy_ops = { |
| + .power_on = ar7100_usb_phy_power_on, |
| + .power_off = ar7100_usb_phy_power_off, |
| + .owner = THIS_MODULE, |
| +}; |
| + |
| +static int ar7100_usb_phy_probe(struct platform_device *pdev) |
| +{ |
| + struct phy_provider *phy_provider; |
| + struct resource *res; |
| + struct ar7100_usb_phy *priv; |
| + |
| + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); |
| + if (!priv) |
| + return -ENOMEM; |
| + |
| + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| + priv->io_base = devm_ioremap_resource(&pdev->dev, res); |
| + if (IS_ERR(priv->io_base)) |
| + return PTR_ERR(priv->io_base); |
| + |
| + priv->rst_phy = devm_reset_control_get(&pdev->dev, "usb-phy"); |
| + if (IS_ERR(priv->rst_phy)) { |
| + dev_err(&pdev->dev, "phy reset is missing\n"); |
| + return PTR_ERR(priv->rst_phy); |
| + } |
| + |
| + priv->rst_host = devm_reset_control_get(&pdev->dev, "usb-host"); |
| + if (IS_ERR(priv->rst_host)) { |
| + dev_err(&pdev->dev, "host reset is missing\n"); |
| + return PTR_ERR(priv->rst_host); |
| + } |
| + |
| + priv->rst_ohci_dll = devm_reset_control_get(&pdev->dev, "usb-ohci-dll"); |
| + if (IS_ERR(priv->rst_ohci_dll)) { |
| + dev_err(&pdev->dev, "ohci-dll reset is missing\n"); |
| + return PTR_ERR(priv->rst_host); |
| + } |
| + |
| + priv->phy = devm_phy_create(&pdev->dev, NULL, &ar7100_usb_phy_ops); |
| + if (IS_ERR(priv->phy)) { |
| + dev_err(&pdev->dev, "failed to create PHY\n"); |
| + return PTR_ERR(priv->phy); |
| + } |
| + |
| + priv->gpio = of_get_gpio(pdev->dev.of_node, 0); |
| + if (priv->gpio >= 0) { |
| + int ret = devm_gpio_request(&pdev->dev, priv->gpio, dev_name(&pdev->dev)); |
| + |
| + if (ret) { |
| + dev_err(&pdev->dev, "failed to request gpio\n"); |
| + return ret; |
| + } |
| + gpio_export_with_name(priv->gpio, 0, dev_name(&pdev->dev)); |
| + gpio_set_value(priv->gpio, 1); |
| + } |
| + |
| + phy_set_drvdata(priv->phy, priv); |
| + |
| + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); |
| + |
| + |
| + return PTR_ERR_OR_ZERO(phy_provider); |
| +} |
| + |
| +static const struct of_device_id ar7100_usb_phy_of_match[] = { |
| + { .compatible = "qca,ar7100-usb-phy" }, |
| + {} |
| +}; |
| +MODULE_DEVICE_TABLE(of, ar7100_usb_phy_of_match); |
| + |
| +static struct platform_driver ar7100_usb_phy_driver = { |
| + .probe = ar7100_usb_phy_probe, |
| + .driver = { |
| + .of_match_table = ar7100_usb_phy_of_match, |
| + .name = "ar7100-usb-phy", |
| + } |
| +}; |
| +module_platform_driver(ar7100_usb_phy_driver); |
| + |
| +MODULE_DESCRIPTION("ATH79 USB PHY driver"); |
| +MODULE_AUTHOR("Alban Bedel <albeu@free.fr>"); |
| +MODULE_LICENSE("GPL"); |
| --- /dev/null |
| +++ b/drivers/phy/phy-ar7200-usb.c |
| @@ -0,0 +1,136 @@ |
| +/* |
| + * Copyright (C) 2015 Alban Bedel <albeu@free.fr> |
| + * |
| + * 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; either version 2 of the License, or |
| + * (at your option) any later version. |
| + */ |
| + |
| +#include <linux/module.h> |
| +#include <linux/platform_device.h> |
| +#include <linux/phy/phy.h> |
| +#include <linux/reset.h> |
| +#include <linux/of_gpio.h> |
| + |
| +struct ar7200_usb_phy { |
| + struct reset_control *rst_phy; |
| + struct reset_control *rst_phy_analog; |
| + struct reset_control *suspend_override; |
| + struct phy *phy; |
| + int gpio; |
| +}; |
| + |
| +static int ar7200_usb_phy_power_on(struct phy *phy) |
| +{ |
| + struct ar7200_usb_phy *priv = phy_get_drvdata(phy); |
| + int err = 0; |
| + |
| + if (priv->suspend_override) |
| + err = reset_control_assert(priv->suspend_override); |
| + if (priv->rst_phy) |
| + err |= reset_control_deassert(priv->rst_phy); |
| + if (priv->rst_phy_analog) |
| + err |= reset_control_deassert(priv->rst_phy_analog); |
| + |
| + return err; |
| +} |
| + |
| +static int ar7200_usb_phy_power_off(struct phy *phy) |
| +{ |
| + struct ar7200_usb_phy *priv = phy_get_drvdata(phy); |
| + int err = 0; |
| + |
| + if (priv->suspend_override) |
| + err = reset_control_deassert(priv->suspend_override); |
| + if (priv->rst_phy) |
| + err |= reset_control_assert(priv->rst_phy); |
| + if (priv->rst_phy_analog) |
| + err |= reset_control_assert(priv->rst_phy_analog); |
| + |
| + return err; |
| +} |
| + |
| +static const struct phy_ops ar7200_usb_phy_ops = { |
| + .power_on = ar7200_usb_phy_power_on, |
| + .power_off = ar7200_usb_phy_power_off, |
| + .owner = THIS_MODULE, |
| +}; |
| + |
| +static int ar7200_usb_phy_probe(struct platform_device *pdev) |
| +{ |
| + struct phy_provider *phy_provider; |
| + struct ar7200_usb_phy *priv; |
| + |
| + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); |
| + if (!priv) |
| + return -ENOMEM; |
| + |
| + priv->rst_phy = devm_reset_control_get(&pdev->dev, "usb-phy"); |
| + if (IS_ERR(priv->rst_phy)) { |
| + if (PTR_ERR(priv->rst_phy) != -EPROBE_DEFER) |
| + dev_err(&pdev->dev, "phy reset is missing\n"); |
| + return PTR_ERR(priv->rst_phy); |
| + } |
| + |
| + priv->rst_phy_analog = devm_reset_control_get_optional( |
| + &pdev->dev, "usb-phy-analog"); |
| + if (IS_ERR(priv->rst_phy_analog)) { |
| + if (PTR_ERR(priv->rst_phy_analog) == -ENOENT) |
| + priv->rst_phy_analog = NULL; |
| + else |
| + return PTR_ERR(priv->rst_phy_analog); |
| + } |
| + |
| + priv->suspend_override = devm_reset_control_get_optional( |
| + &pdev->dev, "usb-suspend-override"); |
| + if (IS_ERR(priv->suspend_override)) { |
| + if (PTR_ERR(priv->suspend_override) == -ENOENT) |
| + priv->suspend_override = NULL; |
| + else |
| + return PTR_ERR(priv->suspend_override); |
| + } |
| + |
| + priv->phy = devm_phy_create(&pdev->dev, NULL, &ar7200_usb_phy_ops); |
| + if (IS_ERR(priv->phy)) { |
| + dev_err(&pdev->dev, "failed to create PHY\n"); |
| + return PTR_ERR(priv->phy); |
| + } |
| + |
| + priv->gpio = of_get_gpio(pdev->dev.of_node, 0); |
| + if (priv->gpio >= 0) { |
| + int ret = devm_gpio_request(&pdev->dev, priv->gpio, dev_name(&pdev->dev)); |
| + |
| + if (ret) { |
| + dev_err(&pdev->dev, "failed to request gpio\n"); |
| + return ret; |
| + } |
| + gpio_export_with_name(priv->gpio, 0, dev_name(&pdev->dev)); |
| + gpio_set_value(priv->gpio, 1); |
| + } |
| + |
| + phy_set_drvdata(priv->phy, priv); |
| + |
| + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); |
| + |
| + return PTR_ERR_OR_ZERO(phy_provider); |
| +} |
| + |
| +static const struct of_device_id ar7200_usb_phy_of_match[] = { |
| + { .compatible = "qca,ar7200-usb-phy" }, |
| + {} |
| +}; |
| +MODULE_DEVICE_TABLE(of, ar7200_usb_phy_of_match); |
| + |
| +static struct platform_driver ar7200_usb_phy_driver = { |
| + .probe = ar7200_usb_phy_probe, |
| + .driver = { |
| + .of_match_table = ar7200_usb_phy_of_match, |
| + .name = "ar7200-usb-phy", |
| + } |
| +}; |
| +module_platform_driver(ar7200_usb_phy_driver); |
| + |
| +MODULE_DESCRIPTION("ATH79 USB PHY driver"); |
| +MODULE_AUTHOR("Alban Bedel <albeu@free.fr>"); |
| +MODULE_LICENSE("GPL"); |