summaryrefslogtreecommitdiff
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorJoy Wang <joyw@nvidia.com>2014-01-13 18:27:17 +0800
committerBharat Nihalani <bnihalani@nvidia.com>2014-01-14 20:53:40 -0800
commita93a31e4901c9364bc9779b68551c7ad908cdcb3 (patch)
tree7c16442ebc6485229a85d63c8a7a32f768afad9a /drivers/usb/host
parent354f963e3440e266059eab46ac57ec6154497a04 (diff)
xhci: tegra: Support HSIC device power on/off
Support for HSIC device power on/off by sending MBOX_CMD_AIRPLANE_MODE_DISABLED/MBOX_CMD_AIRPLANE_MODE_ENABLED mailbox message to firmware. Bug 1390301 Change-Id: If1f314b09fd0887305765cb5bda9c420b11fafa1 Signed-off-by: Joy Wang <joyw@nvidia.com> Reviewed-on: http://git-master/r/355041 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: WK Tsai <wtsai@nvidia.com> Reviewed-by: Jui Chang Kuo <jckuo@nvidia.com> GVS: Gerrit_Virtual_Submit Reviewed-by: Ashutosh Jha <ajha@nvidia.com> Reviewed-by: Henry Lin <henryl@nvidia.com>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/xhci-tegra.c114
1 files changed, 110 insertions, 4 deletions
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 102d59687b32..8b99687f253d 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -416,6 +416,7 @@ struct tegra_xhci_hcd {
struct tegra_xhci_firmware firmware;
struct tegra_xhci_firmware_log log;
+ struct device_attribute hsic_power_attr[XUSB_HSIC_COUNT];
bool init_done;
};
@@ -2445,6 +2446,7 @@ static int load_firmware(struct tegra_xhci_hcd *tegra, bool resetARU)
u32 usbsts, count = 0xff;
struct xhci_cap_regs __iomem *cap_regs;
struct xhci_op_regs __iomem *op_regs;
+ int pad;
/* enable mbox interrupt */
writel(readl(tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD) | MBOX_INT_EN,
@@ -2533,6 +2535,10 @@ static int load_firmware(struct tegra_xhci_hcd *tegra, bool resetARU)
fw_tm.tm_min, fw_tm.tm_sec,
csb_read(tegra, XUSB_FALC_CPUCTL));
+ cfg_tbl->num_hsic_port = 0;
+ for_each_enabled_hsic_pad(pad, tegra)
+ cfg_tbl->num_hsic_port++;
+
dev_dbg(&pdev->dev, "num_hsic_port %d\n", cfg_tbl->num_hsic_port);
/* return fail if firmware status is not good */
@@ -2552,6 +2558,8 @@ static int load_firmware(struct tegra_xhci_hcd *tegra, bool resetARU)
dev_err(&pdev->dev, "Controller not ready\n");
return -EFAULT;
}
+ for_each_enabled_hsic_pad(pad, tegra)
+ hsic_pad_pupd_set(tegra, pad, PUPD_IDLE);
return 0;
}
@@ -3126,6 +3134,7 @@ tegra_xhci_process_mbox_message(struct work_struct *work)
struct xhci_hcd *xhci = tegra->xhci;
int pad, port;
unsigned long ports;
+ enum MBOX_CMD_TYPE response;
mutex_lock(&tegra->mbox_lock);
@@ -3200,7 +3209,7 @@ tegra_xhci_process_mbox_message(struct work_struct *work)
if (!ret)
sw_resp |= CMD_TYPE(MBOX_CMD_ACK);
else
- sw_resp |= CMD_TYPE(MBOX_CMD_ACK);
+ sw_resp |= CMD_TYPE(MBOX_CMD_NACK);
goto send_sw_response;
@@ -3245,9 +3254,16 @@ tegra_xhci_process_mbox_message(struct work_struct *work)
return;
send_sw_response:
- if (((sw_resp >> CMD_TYPE_SHIFT) & CMD_TYPE_MASK) == MBOX_CMD_NACK)
- xhci_err(xhci, "%s respond fw message 0x%x with NAK\n",
- __func__, fw_msg);
+ response = (sw_resp >> CMD_TYPE_SHIFT) & CMD_TYPE_MASK;
+ if (response == MBOX_CMD_NACK)
+ xhci_warn(xhci, "%s respond fw message 0x%x with NACK\n",
+ __func__, fw_msg);
+ else if (response == MBOX_CMD_ACK)
+ xhci_dbg(xhci, "%s respond fw message 0x%x with ACK\n",
+ __func__, fw_msg);
+ else
+ xhci_err(xhci, "%s respond fw message 0x%x with %d\n",
+ __func__, fw_msg, response);
writel(sw_resp, tegra->fpci_base + XUSB_CFG_ARU_MBOX_DATA_IN);
cmd = readl(tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD);
@@ -4161,6 +4177,94 @@ static struct of_device_id tegra_xhci_of_match[] = {
{ },
};
+static ssize_t hsic_power_show(struct device *dev,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tegra_xhci_hcd *tegra = platform_get_drvdata(pdev);
+ int pad;
+
+ for_each_enabled_hsic_pad(pad, tegra) {
+ if (&tegra->hsic_power_attr[pad] == attr)
+ return sprintf(buf, "%d\n", pad);
+ }
+
+ return sprintf(buf, "-1\n");
+}
+
+static ssize_t hsic_power_store(struct device *dev,
+ struct kobj_attribute *attr, const char *buf, size_t n)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tegra_xhci_hcd *tegra = platform_get_drvdata(pdev);
+ enum MBOX_CMD_TYPE msg;
+ unsigned int on;
+ int pad, port;
+ int ret;
+
+ if (sscanf(buf, "%u", &on) != 1)
+ return -EINVAL;
+
+ if (on)
+ msg = MBOX_CMD_AIRPLANE_MODE_DISABLED;
+ else
+ msg = MBOX_CMD_AIRPLANE_MODE_ENABLED;
+
+ for_each_enabled_hsic_pad(pad, tegra) {
+ port = hsic_pad_to_port(pad);
+
+ if (&tegra->hsic_power_attr[pad] == attr) {
+ hsic_pad_pupd_set(tegra, pad, PUPD_IDLE);
+ ret = fw_message_send(tegra, msg, BIT(port + 1));
+ }
+ }
+
+ return n;
+}
+
+static void hsic_power_remove_file(struct tegra_xhci_hcd *tegra)
+{
+ struct device *dev = &tegra->pdev->dev;
+ int p;
+
+ for_each_enabled_hsic_pad(p, tegra) {
+ if (attr_name(tegra->hsic_power_attr[p])) {
+ device_remove_file(dev, &tegra->hsic_power_attr[p]);
+ kzfree(attr_name(tegra->hsic_power_attr[p]));
+ }
+ }
+
+}
+
+static int hsic_power_create_file(struct tegra_xhci_hcd *tegra)
+{
+ struct device *dev = &tegra->pdev->dev;
+ int p;
+ int err;
+
+ for_each_enabled_hsic_pad(p, tegra) {
+ attr_name(tegra->hsic_power_attr[p]) = kzalloc(16, GFP_KERNEL);
+ if (!attr_name(tegra->hsic_power_attr[p]))
+ return -ENOMEM;
+
+ snprintf(attr_name(tegra->hsic_power_attr[p]), 16,
+ "hsic%d_power", p);
+ tegra->hsic_power_attr[p].show = hsic_power_show;
+ tegra->hsic_power_attr[p].store = hsic_power_store;
+ tegra->hsic_power_attr[p].attr.mode = (S_IRUGO | S_IWUSR);
+ sysfs_attr_init(&tegra->hsic_power_attr[p]);
+
+ err = device_create_file(dev, &tegra->hsic_power_attr[p]);
+ if (err) {
+ kzfree(attr_name(tegra->hsic_power_attr[p]));
+ attr_name(tegra->hsic_power_attr[p]) = 0;
+ return err;
+ }
+ }
+
+ return 0;
+}
+
/* TODO: we have to refine error handling in tegra_xhci_probe() */
static int tegra_xhci_probe(struct platform_device *pdev)
{
@@ -4539,6 +4643,7 @@ static int tegra_xhci_probe2(struct tegra_xhci_hcd *tegra)
tegra_pd_add_device(&pdev->dev);
pm_runtime_enable(&pdev->dev);
+ hsic_power_create_file(tegra);
tegra->init_done = true;
return 0;
@@ -4606,6 +4711,7 @@ static int tegra_xhci_remove(struct platform_device *pdev)
tegra_pd_remove_device(&pdev->dev);
platform_set_drvdata(pdev, NULL);
+ hsic_power_remove_file(tegra);
mutex_unlock(&tegra->sync_lock);
return 0;