misc: tegra-baseband: enable DT EHCI load/unload
[linux-3.10.git] / drivers / misc / tegra-baseband / tegra_usb_modem_power.c
index 374d85f..80106ef 100644 (file)
@@ -36,9 +36,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/sysedp.h>
 #include <linux/platform_data/tegra_usb_modem_power.h>
-#include <linux/dma-mapping.h>
 #include <linux/delay.h>
-#include "../../../arch/arm/mach-tegra/iomap.h"
 #include <linux/fs.h>
 #include <asm/segment.h>
 #include <asm/uaccess.h>
 #include <linux/platform_data/sysedp_modem.h>
 #include <linux/system-wakeup.h>
 
+#if defined(CONFIG_ARCH_TEGRA_21x_SOC)
+#include <linux/of_platform.h>
+#else
+#include <linux/dma-mapping.h>
+#include "../../../arch/arm/mach-tegra/iomap.h"
+#endif
+
 #define BOOST_CPU_FREQ_MIN     1200000
 #define BOOST_CPU_FREQ_TIMEOUT 5000
 
@@ -54,9 +59,8 @@
 
 #define MAX_MODEM_EDP_STATES 10
 
-/* default autosuspend and short autosuspend delay in ms */
+/* default autosuspend delay in ms */
 #define DEFAULT_AUTOSUSPEND            2000
-#define DEFAULT_SHORT_AUTOSUSPEND      50
 
 #define XHCI_HSIC_POWER "/sys/bus/platform/devices/tegra-xhci/hsic0_power"
 
@@ -86,6 +90,7 @@ static int hsic_power(bool on)
        return 0;
 }
 
+#ifndef CONFIG_ARCH_TEGRA_21x_SOC
 static u64 tegra_ehci_dmamask = DMA_BIT_MASK(64);
 
 static struct resource tegra_usb2_resources[] = {
@@ -125,6 +130,7 @@ static struct tegra_usb_platform_data tegra_ehci2_hsic_baseband_pdata = {
                .skip_resume = true,
        },
 };
+#endif
 
 struct tegra_usb_modem {
        struct tegra_usb_modem_power_platform_data *pdata;
@@ -158,7 +164,6 @@ struct tegra_usb_modem {
        struct notifier_block pm_notifier;      /* pm event notifier */
        struct notifier_block usb_notifier;     /* usb event notifier */
        int sysfs_file_created;
-       int short_autosuspend_enabled;
        struct platform_device *hc;     /* USB host controller */
        struct mutex hc_lock;
        enum { EHCI_HSIC = 0, XHCI_HSIC, XHCI_UTMI } phy_type;
@@ -224,9 +229,6 @@ static irqreturn_t tegra_usb_modem_wake_thread(int irq, void *data)
 
        mutex_lock(&modem->lock);
        if (modem->udev && modem->udev->state != USB_STATE_NOTATTACHED) {
-               wake_lock_timeout(&modem->wake_lock,
-                                 WAKELOCK_TIMEOUT_FOR_REMOTE_WAKE);
-
                dev_info(&modem->pdev->dev, "remote wake (%u)\n",
                         ++(modem->wake_cnt));
 
@@ -236,14 +238,6 @@ static irqreturn_t tegra_usb_modem_wake_thread(int irq, void *data)
                                usb_autopm_put_interface_async(modem->intf);
                        usb_unlock_device(modem->udev);
                }
-#ifdef CONFIG_PM
-               if (modem->capability & TEGRA_MODEM_AUTOSUSPEND &&
-                   modem->short_autosuspend_enabled) {
-                       pm_runtime_set_autosuspend_delay(&modem->udev->dev,
-                                       modem->pdata->autosuspend_delay);
-                       modem->short_autosuspend_enabled = 0;
-               }
-#endif
        }
        mutex_unlock(&modem->lock);
 
@@ -371,7 +365,6 @@ static void modem_device_add_handler(struct tegra_usb_modem *modem,
                if (modem->capability & TEGRA_MODEM_AUTOSUSPEND) {
                        pm_runtime_set_autosuspend_delay(&udev->dev,
                                        modem->pdata->autosuspend_delay);
-                       modem->short_autosuspend_enabled = 0;
                        usb_enable_autosuspend(udev);
                        pr_info("enable autosuspend for %s %s\n",
                                udev->manufacturer, udev->product);
@@ -493,15 +486,6 @@ static int mdm_pm_notifier(struct notifier_block *notifier,
                }
 
                modem->system_suspend = 1;
-#ifdef CONFIG_PM
-               if (modem->capability & TEGRA_MODEM_AUTOSUSPEND &&
-                   modem->udev &&
-                   modem->udev->state != USB_STATE_NOTATTACHED) {
-                       pm_runtime_set_autosuspend_delay(&modem->udev->dev,
-                                       modem->pdata->short_autosuspend_delay);
-                       modem->short_autosuspend_enabled = 1;
-               }
-#endif
                mutex_unlock(&modem->lock);
                return NOTIFY_OK;
        case PM_POST_SUSPEND:
@@ -541,37 +525,6 @@ static int mdm_request_irq(struct tegra_usb_modem *modem,
        return 0;
 }
 
-static void tegra_usb_modem_post_remote_wakeup(void)
-{
-       struct device *dev;
-       struct tegra_usb_modem *modem;
-
-       dev = bus_find_device_by_name(&platform_bus_type, NULL,
-                                       "MDM");
-       if (!dev) {
-               pr_warn("%s unable to find device name\n", __func__);
-               return;
-       }
-
-       modem = dev_get_drvdata(dev);
-
-       mutex_lock(&modem->lock);
-#ifdef CONFIG_PM
-       if (modem->capability & TEGRA_MODEM_AUTOSUSPEND &&
-           modem->udev &&
-           modem->udev->state != USB_STATE_NOTATTACHED &&
-           modem->short_autosuspend_enabled) {
-               pm_runtime_set_autosuspend_delay(&modem->udev->dev,
-                               modem->pdata->autosuspend_delay);
-               modem->short_autosuspend_enabled = 0;
-       }
-#endif
-       wake_lock_timeout(&modem->wake_lock, WAKELOCK_TIMEOUT_FOR_REMOTE_WAKE);
-       mutex_unlock(&modem->lock);
-
-       return;
-}
-
 /* load USB host controller */
 static struct platform_device *tegra_usb_host_register(
                                const struct tegra_usb_modem *modem)
@@ -581,6 +534,36 @@ static struct platform_device *tegra_usb_host_register(
        struct platform_device *pdev;
        int val;
 
+#if defined(CONFIG_ARCH_TEGRA_21x_SOC)
+       struct property *status_prop;
+
+       /* we're registering EHCI through DT here */
+
+       /* If ehci node is unavailable, we're unable to register it later.
+        * So make it available by setting status to "okay" */
+       if (!of_device_is_available(modem->pdata->ehci_node)) {
+               pr_info("%s(): enable ehci node\n", __func__);
+               status_prop = of_find_property(modem->pdata->ehci_node,
+                                               "status", NULL);
+               if (!status_prop) {
+                       pr_err("%s(): error getting status in ehci node\n",
+                               __func__);
+                       return NULL;
+               }
+               strcpy((char *)status_prop->value, "okay");
+       }
+
+       /* we have to hard-code EHCI device name here for now */
+       pdev = of_platform_device_create(modem->pdata->ehci_node,
+                                       "tegra-ehci.1", NULL);
+       if (!pdev) {
+               pr_info("%s: error registering EHCI\n", __func__);
+               return NULL;
+       }
+
+       return pdev;
+#endif
+
        pdev = platform_device_alloc(hc_device->name, hc_device->id);
        if (!pdev)
                return NULL;
@@ -611,9 +594,13 @@ error:
 }
 
 /* unload USB host controller */
-static void tegra_usb_host_unregister(struct platform_device *pdev)
+static void tegra_usb_host_unregister(const struct tegra_usb_modem *modem)
 {
-       platform_device_unregister(pdev);
+       struct platform_device *pdev = modem->hc;
+       if (unlikely(modem->pdata->tegra_ehci_device))
+               platform_device_unregister(pdev);
+       else
+               of_device_unregister(pdev);
 }
 
 static ssize_t show_usb_host(struct device *dev,
@@ -638,6 +625,7 @@ static ssize_t load_unload_usb_host(struct device *dev,
        mutex_lock(&modem->hc_lock);
        switch (modem->phy_type) {
        case XHCI_HSIC:
+               dev_info(&modem->pdev->dev, "using XHCI_HSIC\n");
                if (host) {
                        if (modem->xusb_roothub &&
                                        !modem->nvhsic_work_queued) {
@@ -660,15 +648,17 @@ static ssize_t load_unload_usb_host(struct device *dev,
                }
                break;
        case EHCI_HSIC:
+               dev_info(&modem->pdev->dev, "using EHCI_HSIC\n");
                pr_info("%s EHCI\n", host ? "Load" : "Unload");
                if (host && !modem->hc) {
                        modem->hc = tegra_usb_host_register(modem);
                } else if (!host && modem->hc) {
-                       tegra_usb_host_unregister(modem->hc);
+                       tegra_usb_host_unregister(modem);
                        modem->hc = NULL;
                }
                break;
        case XHCI_UTMI:
+               dev_info(&modem->pdev->dev, "using XHCI_UTMI\n");
                modem->hc = host ? (struct platform_device *)1 : NULL;
                break;
        default:
@@ -758,10 +748,6 @@ static ssize_t set_modem_state(struct device *dev,
 static DEVICE_ATTR(modem_state, S_IRUSR | S_IWUSR, show_modem_state,
                        set_modem_state);
 
-static struct tegra_usb_phy_platform_ops tegra_usb_modem_platform_ops = {
-       .post_remote_wakeup = tegra_usb_modem_post_remote_wakeup,
-};
-
 static struct modem_thermal_platform_data thermdata = {
        .num_zones = 0,
 };
@@ -798,12 +784,13 @@ static int mdm_init(struct tegra_usb_modem *modem, struct platform_device *pdev)
        modem->pdata = pdata;
        modem->pdev = pdev;
 
+#ifndef CONFIG_ARCH_TEGRA_21x_SOC
        /* WAR to load statically defined ehci device/pdata */
        pdata->tegra_ehci_device = &tegra_ehci2_device;
        pdata->tegra_ehci_pdata = &tegra_ehci2_hsic_baseband_pdata;
+#endif
 
        pdata->autosuspend_delay = DEFAULT_AUTOSUSPEND;
-       pdata->short_autosuspend_delay = DEFAULT_SHORT_AUTOSUSPEND;
 
        /* get modem operations from platform data */
        modem->ops = (const struct tegra_modem_operations *)pdata->ops;
@@ -833,9 +820,6 @@ static int mdm_init(struct tegra_usb_modem *modem, struct platform_device *pdev)
                        dev_err(&pdev->dev, "request wake irq error\n");
                        goto error;
                }
-       } else {
-               modem->pdata->tegra_ehci_pdata->ops =
-                                               &tegra_usb_modem_platform_ops;
        }
 
        if (gpio_is_valid(pdata->boot_gpio)) {
@@ -937,51 +921,43 @@ error:
 static int tegra_usb_modem_parse_dt(struct tegra_usb_modem *modem,
                struct platform_device *pdev)
 {
-       struct tegra_usb_modem_power_platform_data *pdata =
-               pdev->dev.platform_data;
+       struct tegra_usb_modem_power_platform_data pdata;
        struct device_node *node = pdev->dev.of_node;
+       struct device_node *phy_node;
        const unsigned int *prop;
        int gpio;
        int ret;
-       u32 use_xhci_hsic = 0;
-
-       if (!node)
-               return 0;
-
-       dev_dbg(&pdev->dev, "read platform data from DT\n");
+       const char *node_status;
+#if defined(CONFIG_ARCH_TEGRA_21x_SOC)
+       struct device_node *ehci_node = NULL;
+#endif
 
-       /* allocate platform data if necessary */
-       if (!pdata) {
-               pdata = devm_kzalloc(&pdev->dev,
-                       sizeof(struct tegra_usb_modem_power_platform_data),
-                       GFP_KERNEL);
-               if (!pdata) {
-                       dev_warn(&pdev->dev, "failed to allocate memory\n");
-                       return -ENOMEM;
-               }
-               pdev->dev.platform_data = pdata;
+       if (!node) {
+               dev_err(&pdev->dev, "Missing device tree node\n");
+               return -EINVAL;
        }
 
+       memset(&pdata, 0, sizeof(pdata));
+
        /* turn on modem regulator if required */
-       pdata->regulator_name = of_get_property(node, "nvidia,regulator", NULL);
-       if (pdata->regulator_name) {
-               modem->regulator = regulator_get(&pdev->dev,
-                               pdata->regulator_name);
+       pdata.regulator_name = of_get_property(node, "nvidia,regulator", NULL);
+       if (pdata.regulator_name) {
+               modem->regulator = devm_regulator_get(&pdev->dev,
+                               pdata.regulator_name);
                if (IS_ERR(modem->regulator)) {
                        dev_err(&pdev->dev, "failed to get regulator %s\n",
-                               pdata->regulator_name);
+                               pdata.regulator_name);
                        return PTR_ERR(modem->regulator);
                }
                ret = regulator_enable(modem->regulator);
                if (ret) {
                        dev_err(&pdev->dev, "failed to enable regulator %s\n",
-                               pdata->regulator_name);
-                       regulator_put(modem->regulator);
+                               pdata.regulator_name);
                        return ret;
                }
 
                dev_info(&pdev->dev, "set modem regulator:%s\n",
-               pdata->regulator_name);
+               pdata.regulator_name);
 
                /* Enable regulator bypass */
                if (regulator_allow_bypass(modem->regulator, true))
@@ -1004,90 +980,157 @@ static int tegra_usb_modem_parse_dt(struct tegra_usb_modem *modem,
        }
 
        /* determine phy type */
+       modem->phy_type = -1;
        ret = of_property_read_u32(node, "nvidia,phy-type", &modem->phy_type);
-       if (modem->phy_type != XHCI_UTMI) {
-               ret = of_property_read_u32(node, "nvidia,use-xhci-hsic",
-                       &use_xhci_hsic);
-               modem->phy_type = (ret == 0 && use_xhci_hsic) ? XHCI_HSIC :
-                       EHCI_HSIC;
+       if (0 == ret) {
+               dev_info(&pdev->dev,
+                       "set phy type with property 'nvidia,phy-type'\n");
+       } else {
+               dev_info(&pdev->dev,
+                       "set phy type with child node 'nvidia,phy-*hci-*'\n");
+               for_each_child_of_node(node, phy_node) {
+                       ret = of_property_read_string(phy_node,
+                                                       "status", &node_status);
+                       if (ret != 0) {
+                               dev_err(&pdev->dev,
+                                       "DT property '%s/status' read fail!\n",
+                                       phy_node->full_name);
+                               goto error;
+                       }
+
+                       if (strcmp(node_status, "okay") == 0) {
+                               if (strcmp(phy_node->name,
+                                               "nvidia,phy-ehci-hsic") == 0)
+                                       modem->phy_type = EHCI_HSIC;
+                               else if (strcmp(phy_node->name,
+                                               "nvidia,phy-xhci-hsic") == 0)
+                                       modem->phy_type = XHCI_HSIC;
+                               else if (strcmp(phy_node->name,
+                                               "nvidia,phy-xhci-utmi") == 0)
+                                       modem->phy_type = XHCI_UTMI;
+                               else {
+                                       dev_err(&pdev->dev,
+                                               "Unrecognized phy type node!!\n");
+                                       ret = -EINVAL;
+                                       goto error;
+                               }
+                       }
+               }
+       }
+       if (-1 == modem->phy_type) {
+               dev_err(&pdev->dev,
+                       "Unable to set phy type!!\n");
+               ret = -EINVAL;
+               goto error;
        }
 
-       dev_info(&pdev->dev, "using %s\n",
-               modem->phy_type == EHCI_HSIC ? "EHCI HSIC" :
-               modem->phy_type == XHCI_HSIC ? "XHCI HSIC" : "XHCI UTMI");
+#if defined(CONFIG_ARCH_TEGRA_21x_SOC)
+       /* get EHCI device/pdata handle */
+       if (modem->phy_type == EHCI_HSIC) {
+               ehci_node = of_parse_phandle(node, "nvidia,ehci-device", 0);
+               if (!ehci_node) {
+                       dev_err(&pdev->dev, "can't find nvidia,ehci-device\n");
+                       return -EINVAL;
+               }
+               /* set ehci device/pdata */
+               pdata.ehci_node = ehci_node;
+       }
+#endif
 
        prop = of_get_property(node, "nvidia,num-temp-sensors", NULL);
        if (prop)
-               pdata->num_temp_sensors = be32_to_cpup(prop);
+               pdata.num_temp_sensors = be32_to_cpup(prop);
 
        /* Configure input GPIOs */
        gpio = of_get_named_gpio(node, "nvidia,wake-gpio", 0);
-       pdata->wake_gpio = gpio_is_valid(gpio) ? gpio : -1;
-       dev_info(&pdev->dev, "set MDM_WAKE_AP gpio:%d\n", pdata->wake_gpio);
+       if (gpio == -EPROBE_DEFER) {
+               ret = -EPROBE_DEFER;
+               goto error;
+       }
+       pdata.wake_gpio = gpio_is_valid(gpio) ? gpio : -1;
+       dev_info(&pdev->dev, "set MDM_WAKE_AP gpio:%d\n", pdata.wake_gpio);
        if (gpio_is_valid(gpio)) {
-               ret = gpio_request(gpio, "MDM_WAKE_AP");
+               ret = devm_gpio_request(&pdev->dev, gpio, "MDM_WAKE_AP");
                if (ret) {
                        dev_err(&pdev->dev, "request gpio %d failed\n", gpio);
-                       return ret;
+                       goto error;
                }
                gpio_direction_input(gpio);
        }
 
        gpio = of_get_named_gpio(node, "nvidia,boot-gpio", 0);
-       pdata->boot_gpio = gpio_is_valid(gpio) ? gpio : -1;
-       dev_info(&pdev->dev, "set MDM_COLDBOOT gpio:%d\n", pdata->boot_gpio);
+       if (gpio == -EPROBE_DEFER) {
+               ret = -EPROBE_DEFER;
+               goto error;
+       }
+       pdata.boot_gpio = gpio_is_valid(gpio) ? gpio : -1;
+       dev_info(&pdev->dev, "set MDM_COLDBOOT gpio:%d\n", pdata.boot_gpio);
        if (gpio_is_valid(gpio)) {
-               ret = gpio_request(gpio, "MDM_COLDBOOT");
+               ret = devm_gpio_request(&pdev->dev, gpio, "MDM_COLDBOOT");
                if (ret) {
                        dev_err(&pdev->dev, "request gpio %d failed\n", gpio);
-                       return ret;
+                       goto error;
                }
                gpio_direction_input(gpio);
        }
 
        gpio = of_get_named_gpio(node, "nvidia,mdm-power-report-gpio", 0);
-       pdata->mdm_power_report_gpio = gpio_is_valid(gpio) ? gpio : -1;
+       if (gpio == -EPROBE_DEFER) {
+               ret = -EPROBE_DEFER;
+               goto error;
+       }
+       pdata.mdm_power_report_gpio = gpio_is_valid(gpio) ? gpio : -1;
        dev_info(&pdev->dev, "set MDM_PWR_REPORT gpio:%d\n",
-               pdata->mdm_power_report_gpio);
+               pdata.mdm_power_report_gpio);
 
        gpio = of_get_named_gpio(node, "nvidia,pre-boost-gpio", 0);
+       if (gpio == -EPROBE_DEFER) {
+               ret = -EPROBE_DEFER;
+               goto error;
+       }
        if (gpio_is_valid(gpio)) {
                dev_info(&pdev->dev, "set pre boost gpio (%d) to 1\n", gpio);
-               ret = gpio_request(gpio, "MODEM PREBOOST");
+               ret = devm_gpio_request(&pdev->dev, gpio, "MODEM PREBOOST");
                if (ret) {
                        dev_err(&pdev->dev, "request gpio %d failed\n", gpio);
-                       return ret;
+                       goto error;
                }
                gpio_direction_output(gpio, 1);
                modem->pre_boost_gpio = gpio;
        } else
                modem->pre_boost_gpio = -1;
 
-
-
        /* set GPIO IRQ flags */
-       pdata->wake_irq_flags = pdata->boot_irq_flags =
-               pdata->mdm_power_irq_flags =
+       pdata.wake_irq_flags = pdata.boot_irq_flags =
+               pdata.mdm_power_irq_flags =
                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
 
        /* initialize necessary output GPIO and start modem here */
        gpio = of_get_named_gpio(node, "nvidia,mdm-en-gpio", 0);
+       if (gpio == -EPROBE_DEFER) {
+               ret = -EPROBE_DEFER;
+               goto error;
+       }
        if (gpio_is_valid(gpio)) {
                dev_info(&pdev->dev, "set MODEM EN (%d) to 1\n", gpio);
-               ret = gpio_request(gpio, "MODEM EN");
+               ret = devm_gpio_request(&pdev->dev, gpio, "MODEM EN");
                if (ret) {
                        dev_err(&pdev->dev, "request gpio %d failed\n", gpio);
-                       return ret;
+                       goto error;
                }
                gpio_direction_output(gpio, 1);
        }
 
        gpio = of_get_named_gpio(node, "nvidia,reset-gpio", 0);
+       if (gpio == -EPROBE_DEFER) {
+               ret = -EPROBE_DEFER;
+               goto error;
+       }
        if (gpio_is_valid(gpio)) {
-               ret = gpio_request(gpio, "MODEM RESET");
+               ret = devm_gpio_request(&pdev->dev, gpio, "MODEM RESET");
                if (ret) {
                        dev_err(&pdev->dev, "request gpio %d failed\n", gpio);
-                       return ret;
+                       goto error;
                }
                /* boot modem now */
                /* Modem requires at least 10ms between MDM_EN assertion
@@ -1102,7 +1145,14 @@ static int tegra_usb_modem_parse_dt(struct tegra_usb_modem *modem,
                gpio_export_link(&pdev->dev, "modem_reset", gpio);
        }
 
+       /* add platform data to pdev */
+       platform_device_add_data(pdev, &pdata, sizeof(pdata));
        return 0;
+
+error:
+       if (modem->regulator)
+               regulator_disable(modem->regulator);
+       return ret;
 }
 
 static int tegra_usb_modem_probe(struct platform_device *pdev)