diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-01 00:38:07 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-01 00:38:07 +0300 |
commit | 4ff12049d6b6cc79ad8ee092ae226434687062ec (patch) | |
tree | 1485a69f9dcffd4d4e0f3bd846c986a5c9ed38c7 /drivers/phy | |
parent | c2078402e479f963168bfcf7a8de78ab63748a98 (diff) | |
parent | 44840dec6127e4d7c5074f75d2dd96bc4ab85fe3 (diff) | |
download | linux-4ff12049d6b6cc79ad8ee092ae226434687062ec.tar.xz |
Merge tag 'usb-4.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB updates from Greg KH:
"Here's the big USB and PHY patchset for 4.3-rc1.
As usual, the majority of the changes are in the USB gadget portion of
the tree, lots of little changes all over the place for bugs and new
hardware. Other than that, the normal mix of new hardware support and
bugfixes.
All have been in linux-next with no reported issues"
* tag 'usb-4.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (261 commits)
USB: qcserial: add HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module
USB: ftdi_sio: Added custom PID for CustomWare products
USB: usb_wwan: silence read errors on disconnect
USB: option: silence interrupt errors
USB: symbolserial: Correct transferred data size
USB: symbolserial: Use usb_get_serial_port_data
usb: misc: usbtest: format max packet size for iso transfer
usb: host: ehci-sys: delete useless bus_to_hcd conversion
Revert "usb: interface authorization: Declare authorized attribute"
Revert "usb: interface authorization: Introduces the default interface authorization"
Revert "usb: interface authorization: Control interface probing and claiming"
Revert "usb: interface authorization: Introduces the USB interface authorization"
Revert "usb: interface authorization: SysFS part of USB interface authorization"
Revert "usb: interface authorization: Documentation part"
Revert "usb: interface authorization: Use a flag for the default device authorization"
usb: core: hub: Removed some warnings generated by checkpatch.pl
USB: host: ohci-at91: merge loops in ohci_hcd_at91_drv_probe
USB: host: ohci-at91: merge ohci_at91_of_init in ohci_hcd_at91_drv_probe
USB: host: ohci-at91: depend on OF
USB: host: ohci-at91: move at91_usbh_data definition in c file
...
Diffstat (limited to 'drivers/phy')
35 files changed, 590 insertions, 68 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 6b8dd162f644..47da573d0bab 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -54,6 +54,17 @@ config PHY_EXYNOS_MIPI_VIDEO Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P and EXYNOS SoCs. +config PHY_LPC18XX_USB_OTG + tristate "NXP LPC18xx/43xx SoC USB OTG PHY driver" + depends on OF && (ARCH_LPC18XX || COMPILE_TEST) + depends on MFD_SYSCON + select GENERIC_PHY + help + Enable this to support NXP LPC18xx/43xx internal USB OTG PHY. + + This driver is need for USB0 support on LPC18xx/43xx and takes + care of enabling and clock setup. + config PHY_PXA_28NM_HSIC tristate "Marvell USB HSIC 28nm PHY Driver" depends on HAS_IOMEM @@ -199,6 +210,8 @@ config PHY_SUN4I_USB tristate "Allwinner sunxi SoC USB PHY driver" depends on ARCH_SUNXI && HAS_IOMEM && OF depends on RESET_CONTROLLER + depends on EXTCON + depends on POWER_SUPPLY select GENERIC_PHY help Enable this to support the transceiver that is part of Allwinner diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f344e1b2e825..a5b18c18fc12 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o +obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o diff --git a/drivers/phy/phy-armada375-usb2.c b/drivers/phy/phy-armada375-usb2.c index 8ccc3952c13d..1a3db288c0a9 100644 --- a/drivers/phy/phy-armada375-usb2.c +++ b/drivers/phy/phy-armada375-usb2.c @@ -51,7 +51,7 @@ static int armada375_usb_phy_init(struct phy *phy) return 0; } -static struct phy_ops armada375_usb_phy_ops = { +static const struct phy_ops armada375_usb_phy_ops = { .init = armada375_usb_phy_init, .owner = THIS_MODULE, }; @@ -149,7 +149,6 @@ static struct platform_driver armada375_usb_phy_driver = { .driver = { .of_match_table = of_usb_cluster_table, .name = "armada-375-usb-cluster", - .owner = THIS_MODULE, } }; module_platform_driver(armada375_usb_phy_driver); diff --git a/drivers/phy/phy-bcm-kona-usb2.c b/drivers/phy/phy-bcm-kona-usb2.c index ef2dc1aab2b9..7b67fe49e30b 100644 --- a/drivers/phy/phy-bcm-kona-usb2.c +++ b/drivers/phy/phy-bcm-kona-usb2.c @@ -91,7 +91,7 @@ static int bcm_kona_usb_phy_power_off(struct phy *gphy) return 0; } -static struct phy_ops ops = { +static const struct phy_ops ops = { .init = bcm_kona_usb_phy_init, .power_on = bcm_kona_usb_phy_power_on, .power_off = bcm_kona_usb_phy_power_off, diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c index 6f3e06d687de..0062027afb1e 100644 --- a/drivers/phy/phy-berlin-sata.c +++ b/drivers/phy/phy-berlin-sata.c @@ -176,7 +176,7 @@ static struct phy *phy_berlin_sata_phy_xlate(struct device *dev, return priv->phys[i]->phy; } -static struct phy_ops phy_berlin_sata_ops = { +static const struct phy_ops phy_berlin_sata_ops = { .power_on = phy_berlin_sata_power_on, .power_off = phy_berlin_sata_power_off, .owner = THIS_MODULE, diff --git a/drivers/phy/phy-berlin-usb.c b/drivers/phy/phy-berlin-usb.c index 335e06d66ed9..797ba17c404f 100644 --- a/drivers/phy/phy-berlin-usb.c +++ b/drivers/phy/phy-berlin-usb.c @@ -147,12 +147,12 @@ static int phy_berlin_usb_power_on(struct phy *phy) return 0; } -static struct phy_ops phy_berlin_usb_ops = { +static const struct phy_ops phy_berlin_usb_ops = { .power_on = phy_berlin_usb_power_on, .owner = THIS_MODULE, }; -static const struct of_device_id phy_berlin_sata_of_match[] = { +static const struct of_device_id phy_berlin_usb_of_match[] = { { .compatible = "marvell,berlin2-usb-phy", .data = &phy_berlin_pll_dividers[0], @@ -163,12 +163,12 @@ static const struct of_device_id phy_berlin_sata_of_match[] = { }, { }, }; -MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match); +MODULE_DEVICE_TABLE(of, phy_berlin_usb_of_match); static int phy_berlin_usb_probe(struct platform_device *pdev) { const struct of_device_id *match = - of_match_device(phy_berlin_sata_of_match, &pdev->dev); + of_match_device(phy_berlin_usb_of_match, &pdev->dev); struct phy_berlin_usb_priv *priv; struct resource *res; struct phy *phy; @@ -207,9 +207,8 @@ static struct platform_driver phy_berlin_usb_driver = { .probe = phy_berlin_usb_probe, .driver = { .name = "phy-berlin-usb", - .owner = THIS_MODULE, - .of_match_table = phy_berlin_sata_of_match, - }, + .of_match_table = phy_berlin_usb_of_match, + }, }; module_platform_driver(phy_berlin_usb_driver); diff --git a/drivers/phy/phy-brcmstb-sata.c b/drivers/phy/phy-brcmstb-sata.c index b7e303d28caf..8a2cb16a1937 100644 --- a/drivers/phy/phy-brcmstb-sata.c +++ b/drivers/phy/phy-brcmstb-sata.c @@ -122,7 +122,7 @@ static int brcm_sata_phy_init(struct phy *phy) return 0; } -static struct phy_ops phy_ops_28nm = { +static const struct phy_ops phy_ops_28nm = { .init = brcm_sata_phy_init, .owner = THIS_MODULE, }; diff --git a/drivers/phy/phy-dm816x-usb.c b/drivers/phy/phy-dm816x-usb.c index 7b42555ddd51..b4bbef664d20 100644 --- a/drivers/phy/phy-dm816x-usb.c +++ b/drivers/phy/phy-dm816x-usb.c @@ -113,7 +113,7 @@ static int dm816x_usb_phy_init(struct phy *x) return 0; } -static struct phy_ops ops = { +static const struct phy_ops ops = { .init = dm816x_usb_phy_init, .owner = THIS_MODULE, }; diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c index 179cbf9451aa..34b06154e5d9 100644 --- a/drivers/phy/phy-exynos-dp-video.c +++ b/drivers/phy/phy-exynos-dp-video.c @@ -48,7 +48,7 @@ static int exynos_dp_video_phy_power_off(struct phy *phy) EXYNOS5_PHY_ENABLE, 0); } -static struct phy_ops exynos_dp_video_phy_ops = { +static const struct phy_ops exynos_dp_video_phy_ops = { .power_on = exynos_dp_video_phy_power_on, .power_off = exynos_dp_video_phy_power_off, .owner = THIS_MODULE, diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c index df7519a39ba0..2a54caba93b4 100644 --- a/drivers/phy/phy-exynos-mipi-video.c +++ b/drivers/phy/phy-exynos-mipi-video.c @@ -124,7 +124,7 @@ static struct phy *exynos_mipi_video_phy_xlate(struct device *dev, return state->phys[args->args[0]].phy; } -static struct phy_ops exynos_mipi_video_phy_ops = { +static const struct phy_ops exynos_mipi_video_phy_ops = { .power_on = exynos_mipi_video_phy_power_on, .power_off = exynos_mipi_video_phy_power_off, .owner = THIS_MODULE, diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c index d72ef15b0d68..20696f53303f 100644 --- a/drivers/phy/phy-exynos5-usbdrd.c +++ b/drivers/phy/phy-exynos5-usbdrd.c @@ -537,7 +537,7 @@ static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev, return phy_drd->phys[args->args[0]].phy; } -static struct phy_ops exynos5_usbdrd_phy_ops = { +static const struct phy_ops exynos5_usbdrd_phy_ops = { .init = exynos5_usbdrd_phy_init, .exit = exynos5_usbdrd_phy_exit, .power_on = exynos5_usbdrd_phy_power_on, diff --git a/drivers/phy/phy-exynos5250-sata.c b/drivers/phy/phy-exynos5250-sata.c index bc858cc800a1..60e13afcd9b8 100644 --- a/drivers/phy/phy-exynos5250-sata.c +++ b/drivers/phy/phy-exynos5250-sata.c @@ -154,7 +154,7 @@ static int exynos_sata_phy_init(struct phy *phy) return ret; } -static struct phy_ops exynos_sata_phy_ops = { +static const struct phy_ops exynos_sata_phy_ops = { .init = exynos_sata_phy_init, .power_on = exynos_sata_phy_power_on, .power_off = exynos_sata_phy_power_off, diff --git a/drivers/phy/phy-hix5hd2-sata.c b/drivers/phy/phy-hix5hd2-sata.c index d6b22659cac1..e5ab3aa78b9d 100644 --- a/drivers/phy/phy-hix5hd2-sata.c +++ b/drivers/phy/phy-hix5hd2-sata.c @@ -129,7 +129,7 @@ static int hix5hd2_sata_phy_init(struct phy *phy) return 0; } -static struct phy_ops hix5hd2_sata_phy_ops = { +static const struct phy_ops hix5hd2_sata_phy_ops = { .init = hix5hd2_sata_phy_init, .owner = THIS_MODULE, }; diff --git a/drivers/phy/phy-lpc18xx-usb-otg.c b/drivers/phy/phy-lpc18xx-usb-otg.c new file mode 100644 index 000000000000..3b7a71eb5b7e --- /dev/null +++ b/drivers/phy/phy-lpc18xx-usb-otg.c @@ -0,0 +1,143 @@ +/* + * PHY driver for NXP LPC18xx/43xx internal USB OTG PHY + * + * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +/* USB OTG PHY register offset and bit in CREG */ +#define LPC18XX_CREG_CREG0 0x004 +#define LPC18XX_CREG_CREG0_USB0PHY BIT(5) + +struct lpc18xx_usb_otg_phy { + struct phy *phy; + struct clk *clk; + struct regmap *reg; +}; + +static int lpc18xx_usb_otg_phy_init(struct phy *phy) +{ + struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy); + int ret; + + /* The PHY must be clocked at 480 MHz */ + ret = clk_set_rate(lpc->clk, 480000000); + if (ret) + return ret; + + return clk_prepare(lpc->clk); +} + +static int lpc18xx_usb_otg_phy_exit(struct phy *phy) +{ + struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy); + + clk_unprepare(lpc->clk); + + return 0; +} + +static int lpc18xx_usb_otg_phy_power_on(struct phy *phy) +{ + struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy); + int ret; + + ret = clk_enable(lpc->clk); + if (ret) + return ret; + + /* The bit in CREG is cleared to enable the PHY */ + return regmap_update_bits(lpc->reg, LPC18XX_CREG_CREG0, + LPC18XX_CREG_CREG0_USB0PHY, 0); +} + +static int lpc18xx_usb_otg_phy_power_off(struct phy *phy) +{ + struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy); + int ret; + + ret = regmap_update_bits(lpc->reg, LPC18XX_CREG_CREG0, + LPC18XX_CREG_CREG0_USB0PHY, + LPC18XX_CREG_CREG0_USB0PHY); + if (ret) + return ret; + + clk_disable(lpc->clk); + + return 0; +} + +static const struct phy_ops lpc18xx_usb_otg_phy_ops = { + .init = lpc18xx_usb_otg_phy_init, + .exit = lpc18xx_usb_otg_phy_exit, + .power_on = lpc18xx_usb_otg_phy_power_on, + .power_off = lpc18xx_usb_otg_phy_power_off, + .owner = THIS_MODULE, +}; + +static int lpc18xx_usb_otg_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct lpc18xx_usb_otg_phy *lpc; + + lpc = devm_kzalloc(&pdev->dev, sizeof(*lpc), GFP_KERNEL); + if (!lpc) + return -ENOMEM; + + lpc->reg = syscon_node_to_regmap(pdev->dev.of_node->parent); + if (IS_ERR(lpc->reg)) { + dev_err(&pdev->dev, "failed to get syscon\n"); + return PTR_ERR(lpc->reg); + } + + lpc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(lpc->clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + return PTR_ERR(lpc->clk); + } + + lpc->phy = devm_phy_create(&pdev->dev, NULL, &lpc18xx_usb_otg_phy_ops); + if (IS_ERR(lpc->phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + return PTR_ERR(lpc->phy); + } + + phy_set_drvdata(lpc->phy, lpc); + + 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 lpc18xx_usb_otg_phy_match[] = { + { .compatible = "nxp,lpc1850-usb-otg-phy" }, + { } +}; +MODULE_DEVICE_TABLE(of, lpc18xx_usb_otg_phy_match); + +static struct platform_driver lpc18xx_usb_otg_phy_driver = { + .probe = lpc18xx_usb_otg_phy_probe, + .driver = { + .name = "lpc18xx-usb-otg-phy", + .of_match_table = lpc18xx_usb_otg_phy_match, + }, +}; +module_platform_driver(lpc18xx_usb_otg_phy_driver); + +MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); +MODULE_DESCRIPTION("NXP LPC18xx/43xx USB OTG PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c index 5e257ef7ac05..c47b56b4a2b8 100644 --- a/drivers/phy/phy-miphy28lp.c +++ b/drivers/phy/phy-miphy28lp.c @@ -1132,7 +1132,7 @@ static struct phy *miphy28lp_xlate(struct device *dev, return miphy_phy->phy; } -static struct phy_ops miphy28lp_ops = { +static const struct phy_ops miphy28lp_ops = { .init = miphy28lp_init, .owner = THIS_MODULE, }; @@ -1268,7 +1268,6 @@ static struct platform_driver miphy28lp_driver = { .probe = miphy28lp_probe, .driver = { .name = "miphy28lp-phy", - .owner = THIS_MODULE, .of_match_table = miphy28lp_of_match, } }; diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c index 0ff354d6e183..00a686a073ed 100644 --- a/drivers/phy/phy-miphy365x.c +++ b/drivers/phy/phy-miphy365x.c @@ -510,7 +510,7 @@ static struct phy *miphy365x_xlate(struct device *dev, return miphy_phy->phy; } -static struct phy_ops miphy365x_ops = { +static const struct phy_ops miphy365x_ops = { .init = miphy365x_init, .owner = THIS_MODULE, }; diff --git a/drivers/phy/phy-mvebu-sata.c b/drivers/phy/phy-mvebu-sata.c index 03b94f92e6f1..768ce92e81ce 100644 --- a/drivers/phy/phy-mvebu-sata.c +++ b/drivers/phy/phy-mvebu-sata.c @@ -75,7 +75,7 @@ static int phy_mvebu_sata_power_off(struct phy *phy) return 0; } -static struct phy_ops phy_mvebu_sata_ops = { +static const struct phy_ops phy_mvebu_sata_ops = { .power_on = phy_mvebu_sata_power_on, .power_off = phy_mvebu_sata_power_off, .owner = THIS_MODULE, diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c index c1a468686bdc..0fe80589ffbe 100644 --- a/drivers/phy/phy-omap-usb2.c +++ b/drivers/phy/phy-omap-usb2.c @@ -137,7 +137,7 @@ static int omap_usb_init(struct phy *x) return 0; } -static struct phy_ops ops = { +static const struct phy_ops ops = { .init = omap_usb_init, .power_on = omap_usb_power_on, .power_off = omap_usb_power_off, diff --git a/drivers/phy/phy-qcom-apq8064-sata.c b/drivers/phy/phy-qcom-apq8064-sata.c index 4b243f7a10e4..69ce2afac015 100644 --- a/drivers/phy/phy-qcom-apq8064-sata.c +++ b/drivers/phy/phy-qcom-apq8064-sata.c @@ -204,7 +204,7 @@ static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy) return 0; } -static struct phy_ops qcom_apq8064_sata_phy_ops = { +static const struct phy_ops qcom_apq8064_sata_phy_ops = { .init = qcom_apq8064_sata_phy_init, .exit = qcom_apq8064_sata_phy_exit, .owner = THIS_MODULE, diff --git a/drivers/phy/phy-qcom-ipq806x-sata.c b/drivers/phy/phy-qcom-ipq806x-sata.c index 6f2fe2627916..0ad127cc9298 100644 --- a/drivers/phy/phy-qcom-ipq806x-sata.c +++ b/drivers/phy/phy-qcom-ipq806x-sata.c @@ -126,7 +126,7 @@ static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy) return 0; } -static struct phy_ops qcom_ipq806x_sata_phy_ops = { +static const struct phy_ops qcom_ipq806x_sata_phy_ops = { .init = qcom_ipq806x_sata_phy_init, .exit = qcom_ipq806x_sata_phy_exit, .owner = THIS_MODULE, diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h index 591a39175e8a..2bd5ce43a724 100644 --- a/drivers/phy/phy-qcom-ufs-i.h +++ b/drivers/phy/phy-qcom-ufs-i.h @@ -150,7 +150,7 @@ int ufs_qcom_phy_remove(struct phy *generic_phy, struct ufs_qcom_phy *ufs_qcom_phy); struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, struct ufs_qcom_phy *common_cfg, - struct phy_ops *ufs_qcom_phy_gen_ops, + const struct phy_ops *ufs_qcom_phy_gen_ops, struct ufs_qcom_phy_specific_ops *phy_spec_ops); int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A, diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/phy-qcom-ufs-qmp-14nm.c index f5fc50a9fce7..56631e77c11d 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-14nm.c +++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.c @@ -115,7 +115,7 @@ static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) return err; } -static struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { +static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { .init = ufs_qcom_phy_qmp_14nm_init, .exit = ufs_qcom_phy_exit, .power_on = ufs_qcom_phy_power_on, @@ -191,7 +191,6 @@ static struct platform_driver ufs_qcom_phy_qmp_14nm_driver = { .driver = { .of_match_table = ufs_qcom_phy_qmp_14nm_of_match, .name = "ufs_qcom_phy_qmp_14nm", - .owner = THIS_MODULE, }, }; diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/phy-qcom-ufs-qmp-20nm.c index 8332f96b2c4a..b16ea77d07b9 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-20nm.c +++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.c @@ -171,7 +171,7 @@ static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) return err; } -static struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { +static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { .init = ufs_qcom_phy_qmp_20nm_init, .exit = ufs_qcom_phy_exit, .power_on = ufs_qcom_phy_power_on, @@ -247,7 +247,6 @@ static struct platform_driver ufs_qcom_phy_qmp_20nm_driver = { .driver = { .of_match_table = ufs_qcom_phy_qmp_20nm_of_match, .name = "ufs_qcom_phy_qmp_20nm", - .owner = THIS_MODULE, }, }; diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c index f9c618f0ab6e..49a1ed0cef56 100644 --- a/drivers/phy/phy-qcom-ufs.c +++ b/drivers/phy/phy-qcom-ufs.c @@ -77,7 +77,7 @@ EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate); struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, struct ufs_qcom_phy *common_cfg, - struct phy_ops *ufs_qcom_phy_gen_ops, + const struct phy_ops *ufs_qcom_phy_gen_ops, struct ufs_qcom_phy_specific_ops *phy_spec_ops) { int err; diff --git a/drivers/phy/phy-rcar-gen2.c b/drivers/phy/phy-rcar-gen2.c index 39d9b2995435..6e0d9fa8e1d1 100644 --- a/drivers/phy/phy-rcar-gen2.c +++ b/drivers/phy/phy-rcar-gen2.c @@ -184,7 +184,7 @@ static int rcar_gen2_phy_power_off(struct phy *p) return 0; } -static struct phy_ops rcar_gen2_phy_ops = { +static const struct phy_ops rcar_gen2_phy_ops = { .init = rcar_gen2_phy_init, .exit = rcar_gen2_phy_exit, .power_on = rcar_gen2_phy_power_on, diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c index 7d4c33643768..5a5c073e72fe 100644 --- a/drivers/phy/phy-rockchip-usb.c +++ b/drivers/phy/phy-rockchip-usb.c @@ -84,7 +84,7 @@ static int rockchip_usb_phy_power_on(struct phy *_phy) return 0; } -static struct phy_ops ops = { +static const struct phy_ops ops = { .power_on = rockchip_usb_phy_power_on, .power_off = rockchip_usb_phy_power_off, .owner = THIS_MODULE, @@ -146,7 +146,6 @@ static struct platform_driver rockchip_usb_driver = { .probe = rockchip_usb_phy_probe, .driver = { .name = "rockchip-usb-phy", - .owner = THIS_MODULE, .of_match_table = rockchip_usb_phy_dt_ids, }, }; diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c index 55b6994932e3..f278a9c547e1 100644 --- a/drivers/phy/phy-samsung-usb2.c +++ b/drivers/phy/phy-samsung-usb2.c @@ -71,7 +71,7 @@ static int samsung_usb2_phy_power_off(struct phy *phy) return 0; } -static struct phy_ops samsung_usb2_phy_ops = { +static const struct phy_ops samsung_usb2_phy_ops = { .power_on = samsung_usb2_phy_power_on, .power_off = samsung_usb2_phy_power_off, .owner = THIS_MODULE, diff --git a/drivers/phy/phy-spear1310-miphy.c b/drivers/phy/phy-spear1310-miphy.c index 45d0005b2203..ed67e98e54ca 100644 --- a/drivers/phy/phy-spear1310-miphy.c +++ b/drivers/phy/phy-spear1310-miphy.c @@ -179,7 +179,7 @@ static const struct of_device_id spear1310_miphy_of_match[] = { }; MODULE_DEVICE_TABLE(of, spear1310_miphy_of_match); -static struct phy_ops spear1310_miphy_ops = { +static const struct phy_ops spear1310_miphy_ops = { .init = spear1310_miphy_init, .exit = spear1310_miphy_exit, .owner = THIS_MODULE, diff --git a/drivers/phy/phy-spear1340-miphy.c b/drivers/phy/phy-spear1340-miphy.c index 494240da4a39..97280c0cf612 100644 --- a/drivers/phy/phy-spear1340-miphy.c +++ b/drivers/phy/phy-spear1340-miphy.c @@ -189,7 +189,7 @@ static const struct of_device_id spear1340_miphy_of_match[] = { }; MODULE_DEVICE_TABLE(of, spear1340_miphy_of_match); -static struct phy_ops spear1340_miphy_ops = { +static const struct phy_ops spear1340_miphy_ops = { .init = spear1340_miphy_init, .exit = spear1340_miphy_exit, .owner = THIS_MODULE, diff --git a/drivers/phy/phy-stih41x-usb.c b/drivers/phy/phy-stih41x-usb.c index c093b472b57d..0ac74639ad02 100644 --- a/drivers/phy/phy-stih41x-usb.c +++ b/drivers/phy/phy-stih41x-usb.c @@ -112,7 +112,7 @@ static int stih41x_usb_phy_power_off(struct phy *phy) return 0; } -static struct phy_ops stih41x_usb_phy_ops = { +static const struct phy_ops stih41x_usb_phy_ops = { .init = stih41x_usb_phy_init, .power_on = stih41x_usb_phy_power_on, .power_off = stih41x_usb_phy_power_off, diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index 2dad7e820ff0..731b395d6e6a 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -1,7 +1,7 @@ /* * Allwinner sun4i USB phy driver * - * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede@redhat.com> * * Based on code from * Allwinner Technology Co., Ltd. <www.allwinnertech.com> @@ -22,23 +22,30 @@ */ #include <linux/clk.h> +#include <linux/delay.h> #include <linux/err.h> +#include <linux/extcon.h> #include <linux/io.h> +#include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_gpio.h> #include <linux/phy/phy.h> #include <linux/phy/phy-sun4i-usb.h> #include <linux/platform_device.h> +#include <linux/power_supply.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> +#include <linux/workqueue.h> #define REG_ISCR 0x00 -#define REG_PHYCTL 0x04 +#define REG_PHYCTL_A10 0x04 #define REG_PHYBIST 0x08 #define REG_PHYTUNE 0x0c +#define REG_PHYCTL_A33 0x10 #define PHYCTL_DATA BIT(7) @@ -47,6 +54,17 @@ #define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) #define SUNXI_ULPI_BYPASS_EN BIT(0) +/* ISCR, Interface Status and Control bits */ +#define ISCR_ID_PULLUP_EN (1 << 17) +#define ISCR_DPDM_PULLUP_EN (1 << 16) +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */ +#define ISCR_FORCE_ID_MASK (3 << 14) +#define ISCR_FORCE_ID_LOW (2 << 14) +#define ISCR_FORCE_ID_HIGH (3 << 14) +#define ISCR_FORCE_VBUS_MASK (3 << 12) +#define ISCR_FORCE_VBUS_LOW (2 << 12) +#define ISCR_FORCE_VBUS_HIGH (3 << 12) + /* Common Control Bits for Both PHYs */ #define PHY_PLL_BW 0x03 #define PHY_RES45_CAL_EN 0x0c @@ -63,60 +81,124 @@ #define MAX_PHYS 3 +/* + * Note do not raise the debounce time, we must report Vusb high within 100ms + * otherwise we get Vbus errors + */ +#define DEBOUNCE_TIME msecs_to_jiffies(50) +#define POLL_TIME msecs_to_jiffies(250) + struct sun4i_usb_phy_data { void __iomem *base; struct mutex mutex; int num_phys; u32 disc_thresh; + bool has_a33_phyctl; struct sun4i_usb_phy { struct phy *phy; void __iomem *pmu; struct regulator *vbus; struct reset_control *reset; struct clk *clk; + bool regulator_on; int index; } phys[MAX_PHYS]; + /* phy0 / otg related variables */ + struct extcon_dev *extcon; + bool phy0_init; + bool phy0_poll; + struct gpio_desc *id_det_gpio; + struct gpio_desc *vbus_det_gpio; + struct power_supply *vbus_power_supply; + struct notifier_block vbus_power_nb; + bool vbus_power_nb_registered; + int id_det_irq; + int vbus_det_irq; + int id_det; + int vbus_det; + struct delayed_work detect; }; #define to_sun4i_usb_phy_data(phy) \ container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index]) +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set) +{ + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); + u32 iscr; + + iscr = readl(data->base + REG_ISCR); + iscr &= ~clr; + iscr |= set; + writel(iscr, data->base + REG_ISCR); +} + +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val) +{ + if (val) + val = ISCR_FORCE_ID_HIGH; + else + val = ISCR_FORCE_ID_LOW; + + sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val); +} + +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val) +{ + if (val) + val = ISCR_FORCE_VBUS_HIGH; + else + val = ISCR_FORCE_VBUS_LOW; + + sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val); +} + static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data, int len) { struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy); u32 temp, usbc_bit = BIT(phy->index * 2); + void *phyctl; int i; mutex_lock(&phy_data->mutex); + if (phy_data->has_a33_phyctl) { + phyctl = phy_data->base + REG_PHYCTL_A33; + /* A33 needs us to set phyctl to 0 explicitly */ + writel(0, phyctl); + } else { + phyctl = phy_data->base + REG_PHYCTL_A10; + } + for (i = 0; i < len; i++) { - temp = readl(phy_data->base + REG_PHYCTL); + temp = readl(phyctl); /* clear the address portion */ temp &= ~(0xff << 8); /* set the address */ temp |= ((addr + i) << 8); - writel(temp, phy_data->base + REG_PHYCTL); + writel(temp, phyctl); /* set the data bit and clear usbc bit*/ - temp = readb(phy_data->base + REG_PHYCTL); + temp = readb(phyctl); if (data & 0x1) temp |= PHYCTL_DATA; else temp &= ~PHYCTL_DATA; temp &= ~usbc_bit; - writeb(temp, phy_data->base + REG_PHYCTL); + writeb(temp, phyctl); /* pulse usbc_bit */ - temp = readb(phy_data->base + REG_PHYCTL); + temp = readb(phyctl); temp |= usbc_bit; - writeb(temp, phy_data->base + REG_PHYCTL); + writeb(temp, phyctl); - temp = readb(phy_data->base + REG_PHYCTL); + temp = readb(phyctl); temp &= ~usbc_bit; - writeb(temp, phy_data->base + REG_PHYCTL); + writeb(temp, phyctl); data >>= 1; } @@ -171,12 +253,39 @@ static int sun4i_usb_phy_init(struct phy *_phy) sun4i_usb_phy_passby(phy, 1); + if (phy->index == 0) { + data->phy0_init = true; + + /* Enable pull-ups */ + sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN); + sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN); + + if (data->id_det_gpio) { + /* OTG mode, force ISCR and cable state updates */ + data->id_det = -1; + data->vbus_det = -1; + queue_delayed_work(system_wq, &data->detect, 0); + } else { + /* Host only mode */ + sun4i_usb_phy0_set_id_detect(_phy, 0); + sun4i_usb_phy0_set_vbus_detect(_phy, 1); + } + } + return 0; } static int sun4i_usb_phy_exit(struct phy *_phy) { struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); + + if (phy->index == 0) { + /* Disable pull-ups */ + sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0); + sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0); + data->phy0_init = false; + } sun4i_usb_phy_passby(phy, 0); reset_control_assert(phy->reset); @@ -185,23 +294,74 @@ static int sun4i_usb_phy_exit(struct phy *_phy) return 0; } +static int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data) +{ + if (data->vbus_det_gpio) + return gpiod_get_value_cansleep(data->vbus_det_gpio); + + if (data->vbus_power_supply) { + union power_supply_propval val; + int r; + + r = power_supply_get_property(data->vbus_power_supply, + POWER_SUPPLY_PROP_PRESENT, &val); + if (r == 0) + return val.intval; + } + + /* Fallback: report vbus as high */ + return 1; +} + +static bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data) +{ + return data->vbus_det_gpio || data->vbus_power_supply; +} + static int sun4i_usb_phy_power_on(struct phy *_phy) { struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); - int ret = 0; + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); + int ret; + + if (!phy->vbus || phy->regulator_on) + return 0; + + /* For phy0 only turn on Vbus if we don't have an ext. Vbus */ + if (phy->index == 0 && sun4i_usb_phy0_have_vbus_det(data) && + data->vbus_det) + return 0; + + ret = regulator_enable(phy->vbus); + if (ret) + return ret; + + phy->regulator_on = true; - if (phy->vbus) - ret = regulator_enable(phy->vbus); + /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */ + if (phy->index == 0 && data->vbus_det_gpio && data->phy0_poll) + mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); - return ret; + return 0; } static int sun4i_usb_phy_power_off(struct phy *_phy) { struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); + + if (!phy->vbus || !phy->regulator_on) + return 0; + + regulator_disable(phy->vbus); + phy->regulator_on = false; - if (phy->vbus) - regulator_disable(phy->vbus); + /* + * phy0 vbus typically slowly discharges, sometimes this causes the + * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan. + */ + if (phy->index == 0 && data->vbus_det_gpio && !data->phy0_poll) + mod_delayed_work(system_wq, &data->detect, POLL_TIME); return 0; } @@ -214,7 +374,7 @@ void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled) } EXPORT_SYMBOL_GPL(sun4i_usb_phy_set_squelch_detect); -static struct phy_ops sun4i_usb_phy_ops = { +static const struct phy_ops sun4i_usb_phy_ops = { .init = sun4i_usb_phy_init, .exit = sun4i_usb_phy_exit, .power_on = sun4i_usb_phy_power_on, @@ -222,6 +382,95 @@ static struct phy_ops sun4i_usb_phy_ops = { .owner = THIS_MODULE, }; +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) +{ + struct sun4i_usb_phy_data *data = + container_of(work, struct sun4i_usb_phy_data, detect.work); + struct phy *phy0 = data->phys[0].phy; + int id_det, vbus_det, id_notify = 0, vbus_notify = 0; + + id_det = gpiod_get_value_cansleep(data->id_det_gpio); + vbus_det = sun4i_usb_phy0_get_vbus_det(data); + + mutex_lock(&phy0->mutex); + + if (!data->phy0_init) { + mutex_unlock(&phy0->mutex); + return; + } + + if (id_det != data->id_det) { + /* + * When a host cable (id == 0) gets plugged in on systems + * without vbus detection report vbus low for long enough for + * the musb-ip to end the current device session. + */ + if (!sun4i_usb_phy0_have_vbus_det(data) && id_det == 0) { + sun4i_usb_phy0_set_vbus_detect(phy0, 0); + msleep(200); + sun4i_usb_phy0_set_vbus_detect(phy0, 1); + } + sun4i_usb_phy0_set_id_detect(phy0, id_det); + data->id_det = id_det; + id_notify = 1; + } + + if (vbus_det != data->vbus_det) { + sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det); + data->vbus_det = vbus_det; + vbus_notify = 1; + } + + mutex_unlock(&phy0->mutex); + + if (id_notify) { + extcon_set_cable_state_(data->extcon, EXTCON_USB_HOST, + !id_det); + /* + * When a host cable gets unplugged (id == 1) on systems + * without vbus detection report vbus low for long enough to + * the musb-ip to end the current host session. + */ + if (!sun4i_usb_phy0_have_vbus_det(data) && id_det == 1) { + mutex_lock(&phy0->mutex); + sun4i_usb_phy0_set_vbus_detect(phy0, 0); + msleep(1000); + sun4i_usb_phy0_set_vbus_detect(phy0, 1); + mutex_unlock(&phy0->mutex); + } + } + + if (vbus_notify) + extcon_set_cable_state_(data->extcon, EXTCON_USB, vbus_det); + + if (data->phy0_poll) + queue_delayed_work(system_wq, &data->detect, POLL_TIME); +} + +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id) +{ + struct sun4i_usb_phy_data *data = dev_id; + + /* vbus or id changed, let the pins settle and then scan them */ + mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); + + return IRQ_HANDLED; +} + +static int sun4i_usb_phy0_vbus_notify(struct notifier_block *nb, + unsigned long val, void *v) +{ + struct sun4i_usb_phy_data *data = + container_of(nb, struct sun4i_usb_phy_data, vbus_power_nb); + struct power_supply *psy = v; + + /* Properties on the vbus_power_supply changed, scan vbus_det */ + if (val == PSY_EVENT_PROP_CHANGED && psy == data->vbus_power_supply) + mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); + + return NOTIFY_OK; +} + static struct phy *sun4i_usb_phy_xlate(struct device *dev, struct of_phandle_args *args) { @@ -233,6 +482,29 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev, return data->phys[args->args[0]].phy; } +static int sun4i_usb_phy_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); + + if (data->vbus_power_nb_registered) + power_supply_unreg_notifier(&data->vbus_power_nb); + if (data->id_det_irq >= 0) + devm_free_irq(dev, data->id_det_irq, data); + if (data->vbus_det_irq >= 0) + devm_free_irq(dev, data->vbus_det_irq, data); + + cancel_delayed_work_sync(&data->detect); + + return 0; +} + +static const unsigned int sun4i_usb_phy0_cable[] = { + EXTCON_USB, + EXTCON_USB_HOST, + EXTCON_NONE, +}; + static int sun4i_usb_phy_probe(struct platform_device *pdev) { struct sun4i_usb_phy_data *data; @@ -241,35 +513,87 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) struct phy_provider *phy_provider; bool dedicated_clocks; struct resource *res; - int i; + int i, ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; mutex_init(&data->mutex); + INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan); + dev_set_drvdata(dev, data); - if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy")) + if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy") || + of_device_is_compatible(np, "allwinner,sun8i-a23-usb-phy") || + of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy")) data->num_phys = 2; else data->num_phys = 3; - if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy") || - of_device_is_compatible(np, "allwinner,sun6i-a31-usb-phy")) - data->disc_thresh = 3; - else + if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy") || + of_device_is_compatible(np, "allwinner,sun7i-a20-usb-phy")) data->disc_thresh = 2; + else + data->disc_thresh = 3; - if (of_device_is_compatible(np, "allwinner,sun6i-a31-usb-phy")) + if (of_device_is_compatible(np, "allwinner,sun6i-a31-usb-phy") || + of_device_is_compatible(np, "allwinner,sun8i-a23-usb-phy") || + of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy")) dedicated_clocks = true; else dedicated_clocks = false; + if (of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy")) + data->has_a33_phyctl = true; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl"); data->base = devm_ioremap_resource(dev, res); if (IS_ERR(data->base)) return PTR_ERR(data->base); + data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN); + if (IS_ERR(data->id_det_gpio)) { + if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + data->id_det_gpio = NULL; + } + + data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN); + if (IS_ERR(data->vbus_det_gpio)) { + if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + data->vbus_det_gpio = NULL; + } + + if (of_find_property(np, "usb0_vbus_power-supply", NULL)) { + data->vbus_power_supply = devm_power_supply_get_by_phandle(dev, + "usb0_vbus_power-supply"); + if (IS_ERR(data->vbus_power_supply)) + return PTR_ERR(data->vbus_power_supply); + + if (!data->vbus_power_supply) + return -EPROBE_DEFER; + } + + /* vbus_det without id_det makes no sense, and is not supported */ + if (sun4i_usb_phy0_have_vbus_det(data) && !data->id_det_gpio) { + dev_err(dev, "usb0_id_det missing or invalid\n"); + return -ENODEV; + } + + if (data->id_det_gpio) { + data->extcon = devm_extcon_dev_allocate(dev, + sun4i_usb_phy0_cable); + if (IS_ERR(data->extcon)) + return PTR_ERR(data->extcon); + + ret = devm_extcon_dev_register(dev, data->extcon); + if (ret) { + dev_err(dev, "failed to register extcon: %d\n", ret); + return ret; + } + } + for (i = 0; i < data->num_phys; i++) { struct sun4i_usb_phy *phy = data->phys + i; char name[16]; @@ -319,10 +643,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) phy_set_drvdata(phy->phy, &data->phys[i]); } - dev_set_drvdata(dev, data); + data->id_det_irq = gpiod_to_irq(data->id_det_gpio); + data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio); + if ((data->id_det_gpio && data->id_det_irq < 0) || + (data->vbus_det_gpio && data->vbus_det_irq < 0)) + data->phy0_poll = true; + + if (data->id_det_irq >= 0) { + ret = devm_request_irq(dev, data->id_det_irq, + sun4i_usb_phy0_id_vbus_det_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "usb0-id-det", data); + if (ret) { + dev_err(dev, "Err requesting id-det-irq: %d\n", ret); + return ret; + } + } + + if (data->vbus_det_irq >= 0) { + ret = devm_request_irq(dev, data->vbus_det_irq, + sun4i_usb_phy0_id_vbus_det_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "usb0-vbus-det", data); + if (ret) { + dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret); + data->vbus_det_irq = -1; + sun4i_usb_phy_remove(pdev); /* Stop detect work */ + return ret; + } + } + + if (data->vbus_power_supply) { + data->vbus_power_nb.notifier_call = sun4i_usb_phy0_vbus_notify; + data->vbus_power_nb.priority = 0; + ret = power_supply_reg_notifier(&data->vbus_power_nb); + if (ret) { + sun4i_usb_phy_remove(pdev); /* Stop detect work */ + return ret; + } + data->vbus_power_nb_registered = true; + } + phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate); + if (IS_ERR(phy_provider)) { + sun4i_usb_phy_remove(pdev); /* Stop detect work */ + return PTR_ERR(phy_provider); + } - return PTR_ERR_OR_ZERO(phy_provider); + return 0; } static const struct of_device_id sun4i_usb_phy_of_match[] = { @@ -330,12 +698,15 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = { { .compatible = "allwinner,sun5i-a13-usb-phy" }, { .compatible = "allwinner,sun6i-a31-usb-phy" }, { .compatible = "allwinner,sun7i-a20-usb-phy" }, + { .compatible = "allwinner,sun8i-a23-usb-phy" }, + { .compatible = "allwinner,sun8i-a33-usb-phy" }, { }, }; MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match); static struct platform_driver sun4i_usb_phy_driver = { .probe = sun4i_usb_phy_probe, + .remove = sun4i_usb_phy_remove, .driver = { .of_match_table = sun4i_usb_phy_of_match, .name = "sun4i-usb-phy", diff --git a/drivers/phy/phy-sun9i-usb.c b/drivers/phy/phy-sun9i-usb.c index 0095914a662c..ac4f31abefe3 100644 --- a/drivers/phy/phy-sun9i-usb.c +++ b/drivers/phy/phy-sun9i-usb.c @@ -114,7 +114,7 @@ static int sun9i_usb_phy_exit(struct phy *_phy) return 0; } -static struct phy_ops sun9i_usb_phy_ops = { +static const struct phy_ops sun9i_usb_phy_ops = { .init = sun9i_usb_phy_init, .exit = sun9i_usb_phy_exit, .owner = THIS_MODULE, diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index 08020dc2c7c8..93bc1120af12 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c @@ -298,7 +298,7 @@ static int ti_pipe3_exit(struct phy *x) return 0; } -static struct phy_ops ops = { +static const struct phy_ops ops = { .init = ti_pipe3_init, .exit = ti_pipe3_exit, .power_on = ti_pipe3_power_on, diff --git a/drivers/phy/phy-tusb1210.c b/drivers/phy/phy-tusb1210.c index 07efdd318bdc..2535d792d57a 100644 --- a/drivers/phy/phy-tusb1210.c +++ b/drivers/phy/phy-tusb1210.c @@ -53,7 +53,7 @@ static int tusb1210_power_off(struct phy *phy) return 0; } -static struct phy_ops phy_ops = { +static const struct phy_ops phy_ops = { .power_on = tusb1210_power_on, .power_off = tusb1210_power_off, .owner = THIS_MODULE, diff --git a/drivers/phy/ulpi_phy.h b/drivers/phy/ulpi_phy.h index ac49fb6285ee..f2ebe490a4bc 100644 --- a/drivers/phy/ulpi_phy.h +++ b/drivers/phy/ulpi_phy.h @@ -5,7 +5,7 @@ * and it's controller, which is always the parent. */ static inline struct phy -*ulpi_phy_create(struct ulpi *ulpi, struct phy_ops *ops) +*ulpi_phy_create(struct ulpi *ulpi, const struct phy_ops *ops) { struct phy *phy; int ret; |