Commit 415060b2 authored by Al Cooper's avatar Al Cooper Committed by Kishon Vijay Abraham I

phy: usb: phy-brcm-usb: Add ability to force DRD mode to host or device

When the usb phy device mode is set to "drd", the USB port will
switch between device and host modes depending on what's plugged
into the port. Customers have asked for the ability to force
host or device mode from software. This commit adds sysfs
entries to the phy device that allow this. The sysfs for the phy
device can be found at:
/sys/bus/platform/drivers/brcmstb-usb-phy/*.usb-phy

The following sysfs entries were added:
- "dr_mode" (RO) - The current phy "dr_mode" setting.
  It will be set to one of the following values:
  - "host" - host mode
  - "peripheral " - device mode
  - "drd" - switch between device and host mode based on
    installed device
  - "typec-pd" - device/host mode is controller by the USB
    Type-C PD protocol.

If "dr_mode" is "drd"
- "drd_select" (RW) -
  It will be set to one of the following values:
  - "host" - force host mode
  - "device" - force device mode
  - "auto" - allow normal auto selection of host/device based on
    inserted USB device
Signed-off-by: default avatarAl Cooper <alcooperx@gmail.com>
Signed-off-by: default avatarKishon Vijay Abraham I <kishon@ti.com>
parent 49859e55
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
#include "phy-brcm-usb-init.h" #include "phy-brcm-usb-init.h"
static DEFINE_MUTEX(sysfs_lock);
enum brcm_usb_phy_id { enum brcm_usb_phy_id {
BRCM_USB_PHY_2_0 = 0, BRCM_USB_PHY_2_0 = 0,
BRCM_USB_PHY_3_0, BRCM_USB_PHY_3_0,
...@@ -45,6 +47,12 @@ static struct value_to_name_map brcm_dr_mode_to_name[] = { ...@@ -45,6 +47,12 @@ static struct value_to_name_map brcm_dr_mode_to_name[] = {
{ USB_CTLR_MODE_TYPEC_PD, "typec-pd" } { USB_CTLR_MODE_TYPEC_PD, "typec-pd" }
}; };
static struct value_to_name_map brcm_dual_mode_to_name[] = {
{ 0, "host" },
{ 1, "device" },
{ 2, "auto" },
};
struct brcm_usb_phy { struct brcm_usb_phy {
struct phy *phy; struct phy *phy;
unsigned int id; unsigned int id;
...@@ -161,6 +169,73 @@ static int name_to_value(struct value_to_name_map *table, int count, ...@@ -161,6 +169,73 @@ static int name_to_value(struct value_to_name_map *table, int count,
return -EINVAL; return -EINVAL;
} }
static const char *value_to_name(struct value_to_name_map *table, int count,
int value)
{
if (value >= count)
return "unknown";
return table[value].name;
}
static ssize_t dr_mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
return sprintf(buf, "%s\n",
value_to_name(&brcm_dr_mode_to_name[0],
ARRAY_SIZE(brcm_dr_mode_to_name),
priv->ini.mode));
}
static DEVICE_ATTR_RO(dr_mode);
static ssize_t dual_select_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
int value;
int res;
mutex_lock(&sysfs_lock);
res = name_to_value(&brcm_dual_mode_to_name[0],
ARRAY_SIZE(brcm_dual_mode_to_name), buf, &value);
if (!res) {
brcm_usb_init_set_dual_select(&priv->ini, value);
res = len;
}
mutex_unlock(&sysfs_lock);
return res;
}
static ssize_t dual_select_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
int value;
mutex_lock(&sysfs_lock);
value = brcm_usb_init_get_dual_select(&priv->ini);
mutex_unlock(&sysfs_lock);
return sprintf(buf, "%s\n",
value_to_name(&brcm_dual_mode_to_name[0],
ARRAY_SIZE(brcm_dual_mode_to_name),
value));
}
static DEVICE_ATTR_RW(dual_select);
static struct attribute *brcm_usb_phy_attrs[] = {
&dev_attr_dr_mode.attr,
&dev_attr_dual_select.attr,
NULL
};
static const struct attribute_group brcm_usb_phy_group = {
.attrs = brcm_usb_phy_attrs,
};
static int brcm_usb_phy_dvr_init(struct device *dev, static int brcm_usb_phy_dvr_init(struct device *dev,
struct brcm_usb_phy_data *priv, struct brcm_usb_phy_data *priv,
struct device_node *dn) struct device_node *dn)
...@@ -277,6 +352,16 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) ...@@ -277,6 +352,16 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
/* make sure invert settings are correct */ /* make sure invert settings are correct */
brcm_usb_init_ipp(&priv->ini); brcm_usb_init_ipp(&priv->ini);
/*
* Create sysfs entries for mode.
* Remove "dual_select" attribute if not in dual mode
*/
if (priv->ini.mode != USB_CTLR_MODE_DRD)
brcm_usb_phy_attrs[1] = NULL;
err = sysfs_create_group(&dev->kobj, &brcm_usb_phy_group);
if (err)
dev_warn(dev, "Error creating sysfs attributes\n");
/* start with everything off */ /* start with everything off */
if (priv->has_xhci) if (priv->has_xhci)
brcm_usb_uninit_xhci(&priv->ini); brcm_usb_uninit_xhci(&priv->ini);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment