phy: for 4.8 -rc1

*) Add a new phy_ops for setting the phy mode
 *) Add a new phy driver for DA8xx SoC USB PHY
 *) Minor fixes and cleanups
 
 Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIcBAABAgAGBQJXel4TAAoJEA5ceFyATYLZrXYP/1RcGZCktZxW43MQNNtGUjlR
 4jhc36FlVQ+9Lrlp5ewLW6RPl/Z1D2ImYyTvxsWDRZhlj0T0N7F/AgwFWQXKPiKs
 l5opMvBL04dIy98M0Za68sr9SlPNxTZNyirejVm8oMtv2vA0+b18/YDZM2YFbt0N
 dTzVfJqvqrvFMSg2avVcTjARRQBnT5zNnKEjniDetVCSZ2yaD+yabrZ3OLh2JhZX
 AJdjvvxkOAVHqF1hBB1D/cNpf5N6fjgUu+xTI8MDDkYf81A3mOm8w7MjYDpusLjO
 Ix/AcuJqzg2216BzdyGF7zzg4wfgl3WmITXqUy+G9wuW4JPOZmeakTfppErAJPHr
 Quw1fIl0Je6l3xuSGGKIyPy3L4v5YvJeeZkhDzxATzhstKgk1fL4KJBN4lEjdRcs
 SeME8e1GVTiKFjFy4RTiP8bGBuUYRSHZVhLK7eeqcyE0hzQUD2LEyGQgLaj1s86k
 U+zFQoTNJwSenZsWINb/Skzwjg2E3kqEWsTbgLNDy02llvBaEykfVdjh6qk+4J5x
 1GbywhJiba5x/1G1v5M4U2A1Fnw3u4Rhb5k64Qqur2mWNIDEUc85M0G8+m6z9io3
 LqRitKkXxP6t/sp7KK3YAas6WnD5jRALgZpRBCsCYXW2lbTGb9Odi54LH4165xMP
 UFBoZLM1VDnK1Ja5fLbX
 =5bMI
 -----END PGP SIGNATURE-----

Merge tag 'phy-for-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy into usb-testing

Kishon writes:

phy: for 4.8 -rc1

*) Add a new phy_ops for setting the phy mode
*) Add a new phy driver for DA8xx SoC USB PHY
*) Minor fixes and cleanups

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
This commit is contained in:
Greg Kroah-Hartman 2016-07-14 12:03:50 +09:00
commit ca2b6faeb9
13 changed files with 408 additions and 36 deletions

View File

@ -0,0 +1,40 @@
TI DA8xx/OMAP-L1xx/AM18xx USB PHY
Required properties:
- compatible: must be "ti,da830-usb-phy".
- #phy-cells: must be 1.
This device controls the PHY for both the USB 1.1 OHCI and USB 2.0 OTG
controllers on DA8xx SoCs. Consumers of this device should use index 0 for
the USB 2.0 phy device and index 1 for the USB 1.1 phy device.
It also requires a "syscon" node with compatible = "ti,da830-cfgchip", "syscon"
to access the CFGCHIP2 register.
Example:
cfgchip: cfgchip@1417c {
compatible = "ti,da830-cfgchip", "syscon";
reg = <0x1417c 0x14>;
};
usb_phy: usb-phy {
compatible = "ti,da830-usb-phy";
#phy-cells = <1>;
};
usb20: usb@200000 {
compatible = "ti,da830-musb";
reg = <0x200000 0x1000>;
interrupts = <58>;
phys = <&usb_phy 0>;
phy-names = "usb-phy";
};
usb11: usb@225000 {
compatible = "ti,da830-ohci";
reg = <0x225000 0x1000>;
interrupts = <59>;
phys = <&usb_phy 1>;
phy-names = "usb-phy";
};

View File

@ -5,11 +5,13 @@ Required properties:
"rockchip,rk3066a-usb-phy" "rockchip,rk3066a-usb-phy"
"rockchip,rk3188-usb-phy" "rockchip,rk3188-usb-phy"
"rockchip,rk3288-usb-phy" "rockchip,rk3288-usb-phy"
- rockchip,grf : phandle to the syscon managing the "general
register files"
- #address-cells: should be 1 - #address-cells: should be 1
- #size-cells: should be 0 - #size-cells: should be 0
Deprecated properties:
- rockchip,grf : phandle to the syscon managing the "general
register files" - phy should be a child of the GRF instead
Sub-nodes: Sub-nodes:
Each PHY should be represented as a sub-node. Each PHY should be represented as a sub-node.
@ -28,14 +30,19 @@ Optional Properties:
Example: Example:
usbphy: phy { grf: syscon@ff770000 {
compatible = "rockchip,rk3288-usb-phy"; compatible = "rockchip,rk3288-grf", "syscon", "simple-mfd";
rockchip,grf = <&grf>;
#address-cells = <1>;
#size-cells = <0>;
usbphy0: usb-phy0 { ...
#phy-cells = <0>;
reg = <0x320>; usbphy: phy {
compatible = "rockchip,rk3288-usb-phy";
#address-cells = <1>;
#size-cells = <0>;
usbphy0: usb-phy0 {
#phy-cells = <0>;
reg = <0x320>;
};
}; };
}; };

View File

@ -44,6 +44,16 @@ config ARMADA375_USBCLUSTER_PHY
depends on OF && HAS_IOMEM depends on OF && HAS_IOMEM
select GENERIC_PHY select GENERIC_PHY
config PHY_DA8XX_USB
tristate "TI DA8xx USB PHY Driver"
depends on ARCH_DAVINCI_DA8XX
select GENERIC_PHY
select MFD_SYSCON
help
Enable this to support the USB PHY on DA8xx SoCs.
This driver controls both the USB 1.1 PHY and the USB 2.0 PHY.
config PHY_DM816X_USB config PHY_DM816X_USB
tristate "TI dm816x USB PHY driver" tristate "TI dm816x USB PHY driver"
depends on ARCH_OMAP2PLUS depends on ARCH_OMAP2PLUS

View File

@ -6,6 +6,7 @@ obj-$(CONFIG_GENERIC_PHY) += phy-core.o
obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o
obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o
obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o
obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o

View File

@ -342,6 +342,21 @@ int phy_power_off(struct phy *phy)
} }
EXPORT_SYMBOL_GPL(phy_power_off); EXPORT_SYMBOL_GPL(phy_power_off);
int phy_set_mode(struct phy *phy, enum phy_mode mode)
{
int ret;
if (!phy || !phy->ops->set_mode)
return 0;
mutex_lock(&phy->mutex);
ret = phy->ops->set_mode(phy, mode);
mutex_unlock(&phy->mutex);
return ret;
}
EXPORT_SYMBOL_GPL(phy_set_mode);
/** /**
* _of_phy_get() - lookup and obtain a reference to a phy by phandle * _of_phy_get() - lookup and obtain a reference to a phy by phandle
* @np: device_node for which to get the phy * @np: device_node for which to get the phy

245
drivers/phy/phy-da8xx-usb.c Normal file
View File

@ -0,0 +1,245 @@
/*
* phy-da8xx-usb - TI DaVinci DA8xx USB PHY driver
*
* Copyright (C) 2016 David Lechner <david@lechnology.com>
*
* 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; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/mfd/da8xx-cfgchip.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
struct da8xx_usb_phy {
struct phy_provider *phy_provider;
struct phy *usb11_phy;
struct phy *usb20_phy;
struct clk *usb11_clk;
struct clk *usb20_clk;
struct regmap *regmap;
};
static int da8xx_usb11_phy_power_on(struct phy *phy)
{
struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
int ret;
ret = clk_prepare_enable(d_phy->usb11_clk);
if (ret)
return ret;
regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM,
CFGCHIP2_USB1SUSPENDM);
return 0;
}
static int da8xx_usb11_phy_power_off(struct phy *phy)
{
struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, 0);
clk_disable_unprepare(d_phy->usb11_clk);
return 0;
}
static const struct phy_ops da8xx_usb11_phy_ops = {
.power_on = da8xx_usb11_phy_power_on,
.power_off = da8xx_usb11_phy_power_off,
.owner = THIS_MODULE,
};
static int da8xx_usb20_phy_power_on(struct phy *phy)
{
struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
int ret;
ret = clk_prepare_enable(d_phy->usb20_clk);
if (ret)
return ret;
regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, 0);
return 0;
}
static int da8xx_usb20_phy_power_off(struct phy *phy)
{
struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN,
CFGCHIP2_OTGPWRDN);
clk_disable_unprepare(d_phy->usb20_clk);
return 0;
}
static int da8xx_usb20_phy_set_mode(struct phy *phy, enum phy_mode mode)
{
struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
u32 val;
switch (mode) {
case PHY_MODE_USB_HOST: /* Force VBUS valid, ID = 0 */
val = CFGCHIP2_OTGMODE_FORCE_HOST;
break;
case PHY_MODE_USB_DEVICE: /* Force VBUS valid, ID = 1 */
val = CFGCHIP2_OTGMODE_FORCE_DEVICE;
break;
case PHY_MODE_USB_OTG: /* Don't override the VBUS/ID comparators */
val = CFGCHIP2_OTGMODE_NO_OVERRIDE;
break;
default:
return -EINVAL;
}
regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGMODE_MASK,
val);
return 0;
}
static const struct phy_ops da8xx_usb20_phy_ops = {
.power_on = da8xx_usb20_phy_power_on,
.power_off = da8xx_usb20_phy_power_off,
.set_mode = da8xx_usb20_phy_set_mode,
.owner = THIS_MODULE,
};
static struct phy *da8xx_usb_phy_of_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev);
if (!d_phy)
return ERR_PTR(-ENODEV);
switch (args->args[0]) {
case 0:
return d_phy->usb20_phy;
case 1:
return d_phy->usb11_phy;
default:
return ERR_PTR(-EINVAL);
}
}
static int da8xx_usb_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct da8xx_usb_phy *d_phy;
d_phy = devm_kzalloc(dev, sizeof(*d_phy), GFP_KERNEL);
if (!d_phy)
return -ENOMEM;
if (node)
d_phy->regmap = syscon_regmap_lookup_by_compatible(
"ti,da830-cfgchip");
else
d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon.0");
if (IS_ERR(d_phy->regmap)) {
dev_err(dev, "Failed to get syscon\n");
return PTR_ERR(d_phy->regmap);
}
d_phy->usb11_clk = devm_clk_get(dev, "usb11_phy");
if (IS_ERR(d_phy->usb11_clk)) {
dev_err(dev, "Failed to get usb11_phy clock\n");
return PTR_ERR(d_phy->usb11_clk);
}
d_phy->usb20_clk = devm_clk_get(dev, "usb20_phy");
if (IS_ERR(d_phy->usb20_clk)) {
dev_err(dev, "Failed to get usb20_phy clock\n");
return PTR_ERR(d_phy->usb20_clk);
}
d_phy->usb11_phy = devm_phy_create(dev, node, &da8xx_usb11_phy_ops);
if (IS_ERR(d_phy->usb11_phy)) {
dev_err(dev, "Failed to create usb11 phy\n");
return PTR_ERR(d_phy->usb11_phy);
}
d_phy->usb20_phy = devm_phy_create(dev, node, &da8xx_usb20_phy_ops);
if (IS_ERR(d_phy->usb20_phy)) {
dev_err(dev, "Failed to create usb20 phy\n");
return PTR_ERR(d_phy->usb20_phy);
}
platform_set_drvdata(pdev, d_phy);
phy_set_drvdata(d_phy->usb11_phy, d_phy);
phy_set_drvdata(d_phy->usb20_phy, d_phy);
if (node) {
d_phy->phy_provider = devm_of_phy_provider_register(dev,
da8xx_usb_phy_of_xlate);
if (IS_ERR(d_phy->phy_provider)) {
dev_err(dev, "Failed to create phy provider\n");
return PTR_ERR(d_phy->phy_provider);
}
} else {
int ret;
ret = phy_create_lookup(d_phy->usb11_phy, "usb-phy", "ohci.0");
if (ret)
dev_warn(dev, "Failed to create usb11 phy lookup\n");
ret = phy_create_lookup(d_phy->usb20_phy, "usb-phy",
"musb-da8xx");
if (ret)
dev_warn(dev, "Failed to create usb20 phy lookup\n");
}
return 0;
}
static int da8xx_usb_phy_remove(struct platform_device *pdev)
{
struct da8xx_usb_phy *d_phy = platform_get_drvdata(pdev);
if (!pdev->dev.of_node) {
phy_remove_lookup(d_phy->usb20_phy, "usb-phy", "musb-da8xx");
phy_remove_lookup(d_phy->usb11_phy, "usb-phy", "ohci.0");
}
return 0;
}
static const struct of_device_id da8xx_usb_phy_ids[] = {
{ .compatible = "ti,da830-usb-phy" },
{ }
};
MODULE_DEVICE_TABLE(of, da8xx_usb_phy_ids);
static struct platform_driver da8xx_usb_phy_driver = {
.probe = da8xx_usb_phy_probe,
.remove = da8xx_usb_phy_remove,
.driver = {
.name = "da8xx-usb-phy",
.of_match_table = da8xx_usb_phy_ids,
},
};
module_platform_driver(da8xx_usb_phy_driver);
MODULE_ALIAS("platform:da8xx-usb-phy");
MODULE_AUTHOR("David Lechner <david@lechnology.com>");
MODULE_DESCRIPTION("TI DA8xx USB PHY driver");
MODULE_LICENSE("GPL v2");

View File

@ -140,7 +140,6 @@ static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev)
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
if (!phy) { if (!phy) {
dev_err(dev, "%s: failed to allocate phy\n", __func__);
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
} }

View File

@ -196,7 +196,6 @@ static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev)
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
if (!phy) { if (!phy) {
dev_err(dev, "%s: failed to allocate phy\n", __func__);
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
} }

View File

@ -21,6 +21,7 @@
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/workqueue.h>
/******* USB2.0 Host registers (original offset is +0x200) *******/ /******* USB2.0 Host registers (original offset is +0x200) *******/
#define USB2_INT_ENABLE 0x000 #define USB2_INT_ENABLE 0x000
@ -81,9 +82,25 @@ struct rcar_gen3_chan {
struct extcon_dev *extcon; struct extcon_dev *extcon;
struct phy *phy; struct phy *phy;
struct regulator *vbus; struct regulator *vbus;
struct work_struct work;
bool extcon_host;
bool has_otg; bool has_otg;
}; };
static void rcar_gen3_phy_usb2_work(struct work_struct *work)
{
struct rcar_gen3_chan *ch = container_of(work, struct rcar_gen3_chan,
work);
if (ch->extcon_host) {
extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, true);
extcon_set_cable_state_(ch->extcon, EXTCON_USB, false);
} else {
extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, false);
extcon_set_cable_state_(ch->extcon, EXTCON_USB, true);
}
}
static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host) static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host)
{ {
void __iomem *usb2_base = ch->base; void __iomem *usb2_base = ch->base;
@ -130,8 +147,8 @@ static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch)
rcar_gen3_set_host_mode(ch, 1); rcar_gen3_set_host_mode(ch, 1);
rcar_gen3_enable_vbus_ctrl(ch, 1); rcar_gen3_enable_vbus_ctrl(ch, 1);
extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, true); ch->extcon_host = true;
extcon_set_cable_state_(ch->extcon, EXTCON_USB, false); schedule_work(&ch->work);
} }
static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch) static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch)
@ -140,8 +157,8 @@ static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch)
rcar_gen3_set_host_mode(ch, 0); rcar_gen3_set_host_mode(ch, 0);
rcar_gen3_enable_vbus_ctrl(ch, 0); rcar_gen3_enable_vbus_ctrl(ch, 0);
extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, false); ch->extcon_host = false;
extcon_set_cable_state_(ch->extcon, EXTCON_USB, true); schedule_work(&ch->work);
} }
static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch) static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch)
@ -301,6 +318,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
if (irq >= 0) { if (irq >= 0) {
int ret; int ret;
INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work);
irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq, irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
IRQF_SHARED, dev_name(dev), channel); IRQF_SHARED, dev_name(dev), channel);
if (irq < 0) if (irq < 0)

View File

@ -236,9 +236,10 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
goto err_clk_prov; goto err_clk_prov;
} }
err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy); err = devm_add_action_or_reset(base->dev, rockchip_usb_phy_action,
rk_phy);
if (err) if (err)
goto err_devm_action; return err;
rk_phy->phy = devm_phy_create(base->dev, child, &ops); rk_phy->phy = devm_phy_create(base->dev, child, &ops);
if (IS_ERR(rk_phy->phy)) { if (IS_ERR(rk_phy->phy)) {
@ -256,9 +257,6 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
else else
return rockchip_usb_phy_power(rk_phy, 1); return rockchip_usb_phy_power(rk_phy, 1);
err_devm_action:
if (!rk_phy->uart_enabled)
of_clk_del_provider(child);
err_clk_prov: err_clk_prov:
if (!rk_phy->uart_enabled) if (!rk_phy->uart_enabled)
clk_unregister(rk_phy->clk480m); clk_unregister(rk_phy->clk480m);
@ -397,8 +395,13 @@ static int rockchip_usb_phy_probe(struct platform_device *pdev)
phy_base->pdata = match->data; phy_base->pdata = match->data;
phy_base->dev = dev; phy_base->dev = dev;
phy_base->reg_base = syscon_regmap_lookup_by_phandle(dev->of_node, phy_base->reg_base = ERR_PTR(-ENODEV);
"rockchip,grf"); if (dev->parent && dev->parent->of_node)
phy_base->reg_base = syscon_node_to_regmap(
dev->parent->of_node);
if (IS_ERR(phy_base->reg_base))
phy_base->reg_base = syscon_regmap_lookup_by_phandle(
dev->of_node, "rockchip,grf");
if (IS_ERR(phy_base->reg_base)) { if (IS_ERR(phy_base->reg_base)) {
dev_err(&pdev->dev, "Missing rockchip,grf property\n"); dev_err(&pdev->dev, "Missing rockchip,grf property\n");
return PTR_ERR(phy_base->reg_base); return PTR_ERR(phy_base->reg_base);
@ -463,7 +466,11 @@ static int __init rockchip_init_usb_uart(void)
return -ENOTSUPP; return -ENOTSUPP;
} }
grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); grf = ERR_PTR(-ENODEV);
if (np->parent)
grf = syscon_node_to_regmap(np->parent);
if (IS_ERR(grf))
grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
if (IS_ERR(grf)) { if (IS_ERR(grf)) {
pr_err("%s: Missing rockchip,grf property, %lu\n", pr_err("%s: Missing rockchip,grf property, %lu\n",
__func__, PTR_ERR(grf)); __func__, PTR_ERR(grf));

View File

@ -94,6 +94,7 @@
enum sun4i_usb_phy_type { enum sun4i_usb_phy_type {
sun4i_a10_phy, sun4i_a10_phy,
sun6i_a31_phy,
sun8i_a33_phy, sun8i_a33_phy,
sun8i_h3_phy, sun8i_h3_phy,
}; };
@ -122,7 +123,6 @@ struct sun4i_usb_phy_data {
/* phy0 / otg related variables */ /* phy0 / otg related variables */
struct extcon_dev *extcon; struct extcon_dev *extcon;
bool phy0_init; bool phy0_init;
bool phy0_poll;
struct gpio_desc *id_det_gpio; struct gpio_desc *id_det_gpio;
struct gpio_desc *vbus_det_gpio; struct gpio_desc *vbus_det_gpio;
struct power_supply *vbus_power_supply; struct power_supply *vbus_power_supply;
@ -343,6 +343,24 @@ static bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data)
return data->vbus_det_gpio || data->vbus_power_supply; return data->vbus_det_gpio || data->vbus_power_supply;
} }
static bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data)
{
if ((data->id_det_gpio && data->id_det_irq <= 0) ||
(data->vbus_det_gpio && data->vbus_det_irq <= 0))
return true;
/*
* The A31 companion pmic (axp221) does not generate vbus change
* interrupts when the board is driving vbus, so we must poll
* when using the pmic for vbus-det _and_ we're driving vbus.
*/
if (data->cfg->type == sun6i_a31_phy &&
data->vbus_power_supply && data->phys[0].regulator_on)
return true;
return false;
}
static int sun4i_usb_phy_power_on(struct phy *_phy) static int sun4i_usb_phy_power_on(struct phy *_phy)
{ {
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
@ -364,7 +382,7 @@ static int sun4i_usb_phy_power_on(struct phy *_phy)
phy->regulator_on = true; phy->regulator_on = true;
/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */ /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
if (phy->index == 0 && data->vbus_det_gpio && data->phy0_poll) if (phy->index == 0 && sun4i_usb_phy0_poll(data))
mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
return 0; return 0;
@ -385,7 +403,7 @@ static int sun4i_usb_phy_power_off(struct phy *_phy)
* phy0 vbus typically slowly discharges, sometimes this causes the * phy0 vbus typically slowly discharges, sometimes this causes the
* Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan. * 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) if (phy->index == 0 && !sun4i_usb_phy0_poll(data))
mod_delayed_work(system_wq, &data->detect, POLL_TIME); mod_delayed_work(system_wq, &data->detect, POLL_TIME);
return 0; return 0;
@ -468,7 +486,7 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
if (vbus_notify) if (vbus_notify)
extcon_set_cable_state_(data->extcon, EXTCON_USB, vbus_det); extcon_set_cable_state_(data->extcon, EXTCON_USB, vbus_det);
if (data->phy0_poll) if (sun4i_usb_phy0_poll(data))
queue_delayed_work(system_wq, &data->detect, POLL_TIME); queue_delayed_work(system_wq, &data->detect, POLL_TIME);
} }
@ -644,11 +662,6 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
} }
data->id_det_irq = gpiod_to_irq(data->id_det_gpio); 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) { if (data->id_det_irq > 0) {
ret = devm_request_irq(dev, data->id_det_irq, ret = devm_request_irq(dev, data->id_det_irq,
sun4i_usb_phy0_id_vbus_det_irq, sun4i_usb_phy0_id_vbus_det_irq,
@ -660,6 +673,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
} }
} }
data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
if (data->vbus_det_irq > 0) { if (data->vbus_det_irq > 0) {
ret = devm_request_irq(dev, data->vbus_det_irq, ret = devm_request_irq(dev, data->vbus_det_irq,
sun4i_usb_phy0_id_vbus_det_irq, sun4i_usb_phy0_id_vbus_det_irq,
@ -711,7 +725,7 @@ static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
.num_phys = 3, .num_phys = 3,
.type = sun4i_a10_phy, .type = sun6i_a31_phy,
.disc_thresh = 3, .disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10, .phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = true, .dedicated_clocks = true,

View File

@ -518,7 +518,7 @@ enum clk_type_t {
CLK_INT_SING = 2, /* Internal single ended */ CLK_INT_SING = 2, /* Internal single ended */
}; };
enum phy_mode { enum xgene_phy_mode {
MODE_SATA = 0, /* List them for simple reference */ MODE_SATA = 0, /* List them for simple reference */
MODE_SGMII = 1, MODE_SGMII = 1,
MODE_PCIE = 2, MODE_PCIE = 2,
@ -542,7 +542,7 @@ struct xgene_sata_override_param {
struct xgene_phy_ctx { struct xgene_phy_ctx {
struct device *dev; struct device *dev;
struct phy *phy; struct phy *phy;
enum phy_mode mode; /* Mode of operation */ enum xgene_phy_mode mode; /* Mode of operation */
enum clk_type_t clk_type; /* Input clock selection */ enum clk_type_t clk_type; /* Input clock selection */
void __iomem *sds_base; /* PHY CSR base addr */ void __iomem *sds_base; /* PHY CSR base addr */
struct clk *clk; /* Optional clock */ struct clk *clk; /* Optional clock */

View File

@ -22,12 +22,20 @@
struct phy; struct phy;
enum phy_mode {
PHY_MODE_INVALID,
PHY_MODE_USB_HOST,
PHY_MODE_USB_DEVICE,
PHY_MODE_USB_OTG,
};
/** /**
* struct phy_ops - set of function pointers for performing phy operations * struct phy_ops - set of function pointers for performing phy operations
* @init: operation to be performed for initializing phy * @init: operation to be performed for initializing phy
* @exit: operation to be performed while exiting * @exit: operation to be performed while exiting
* @power_on: powering on the phy * @power_on: powering on the phy
* @power_off: powering off the phy * @power_off: powering off the phy
* @set_mode: set the mode of the phy
* @owner: the module owner containing the ops * @owner: the module owner containing the ops
*/ */
struct phy_ops { struct phy_ops {
@ -35,6 +43,7 @@ struct phy_ops {
int (*exit)(struct phy *phy); int (*exit)(struct phy *phy);
int (*power_on)(struct phy *phy); int (*power_on)(struct phy *phy);
int (*power_off)(struct phy *phy); int (*power_off)(struct phy *phy);
int (*set_mode)(struct phy *phy, enum phy_mode mode);
struct module *owner; struct module *owner;
}; };
@ -126,6 +135,7 @@ int phy_init(struct phy *phy);
int phy_exit(struct phy *phy); int phy_exit(struct phy *phy);
int phy_power_on(struct phy *phy); int phy_power_on(struct phy *phy);
int phy_power_off(struct phy *phy); int phy_power_off(struct phy *phy);
int phy_set_mode(struct phy *phy, enum phy_mode mode);
static inline int phy_get_bus_width(struct phy *phy) static inline int phy_get_bus_width(struct phy *phy)
{ {
return phy->attrs.bus_width; return phy->attrs.bus_width;
@ -233,6 +243,13 @@ static inline int phy_power_off(struct phy *phy)
return -ENOSYS; return -ENOSYS;
} }
static inline int phy_set_mode(struct phy *phy, enum phy_mode mode)
{
if (!phy)
return 0;
return -ENOSYS;
}
static inline int phy_get_bus_width(struct phy *phy) static inline int phy_get_bus_width(struct phy *phy)
{ {
return -ENOSYS; return -ENOSYS;