#include <asm/mach-types.h>
#include <linux/usb/ehci_def.h>
#include <linux/usb/tegra_usb_phy.h>
+#include <linux/regulator/consumer.h>
#define ULPI_VIEWPORT 0x170
},
};
-static struct tegra_utmip_config utmip_default[] = {
- [0] = {
- .hssync_start_delay = 9,
- .idle_wait_delay = 17,
- .elastic_limit = 16,
- .term_range_adj = 6,
- .xcvr_setup = 9,
- .xcvr_lsfslew = 1,
- .xcvr_lsrslew = 1,
- },
- [2] = {
- .hssync_start_delay = 9,
- .idle_wait_delay = 17,
- .elastic_limit = 16,
- .term_range_adj = 6,
- .xcvr_setup = 9,
- .xcvr_lsfslew = 2,
- .xcvr_lsrslew = 2,
- },
-};
-
static void set_pts(struct tegra_usb_phy *phy, u8 pts_val)
{
void __iomem *base = phy->regs;
static int utmip_pad_open(struct tegra_usb_phy *phy)
{
- phy->pad_clk = devm_clk_get(phy->dev, "utmi-pads");
+ phy->pad_clk = devm_clk_get(phy->u_phy.dev, "utmi-pads");
if (IS_ERR(phy->pad_clk)) {
pr_err("%s: can't get utmip pad clock\n", __func__);
return PTR_ERR(phy->pad_clk);
ret = gpio_direction_output(phy->reset_gpio, 0);
if (ret < 0) {
- dev_err(phy->dev, "gpio %d not set to 0\n", phy->reset_gpio);
+ dev_err(phy->u_phy.dev, "gpio %d not set to 0\n",
+ phy->reset_gpio);
return ret;
}
msleep(5);
ret = gpio_direction_output(phy->reset_gpio, 1);
if (ret < 0) {
- dev_err(phy->dev, "gpio %d not set to 1\n", phy->reset_gpio);
+ dev_err(phy->u_phy.dev, "gpio %d not set to 1\n",
+ phy->reset_gpio);
return ret;
}
{
struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+ if (!IS_ERR(phy->vbus))
+ regulator_disable(phy->vbus);
+
clk_disable_unprepare(phy->pll_u);
}
{
int err;
- phy->clk = devm_clk_get(phy->dev, "ulpi-link");
+ phy->clk = devm_clk_get(phy->u_phy.dev, "ulpi-link");
if (IS_ERR(phy->clk)) {
pr_err("%s: can't get ulpi clock\n", __func__);
return PTR_ERR(phy->clk);
}
- err = devm_gpio_request(phy->dev, phy->reset_gpio, "ulpi_phy_reset_b");
+ err = devm_gpio_request(phy->u_phy.dev, phy->reset_gpio,
+ "ulpi_phy_reset_b");
if (err < 0) {
- dev_err(phy->dev, "request failed for gpio: %d\n",
+ dev_err(phy->u_phy.dev, "request failed for gpio: %d\n",
phy->reset_gpio);
return err;
}
err = gpio_direction_output(phy->reset_gpio, 0);
if (err < 0) {
- dev_err(phy->dev, "gpio %d direction not set to output\n",
+ dev_err(phy->u_phy.dev, "gpio %d direction not set to output\n",
phy->reset_gpio);
return err;
}
phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0);
if (!phy->ulpi) {
- dev_err(phy->dev, "otg_ulpi_create returned NULL\n");
+ dev_err(phy->u_phy.dev, "otg_ulpi_create returned NULL\n");
err = -ENOMEM;
return err;
}
int i;
int err;
- if (!phy->is_ulpi_phy) {
- if (phy->is_legacy_phy)
- phy->config = &utmip_default[0];
- else
- phy->config = &utmip_default[2];
- }
-
- phy->pll_u = devm_clk_get(phy->dev, "pll_u");
+ phy->pll_u = devm_clk_get(phy->u_phy.dev, "pll_u");
if (IS_ERR(phy->pll_u)) {
pr_err("Can't get pll_u clock\n");
return PTR_ERR(phy->pll_u);
goto fail;
}
+ if (!IS_ERR(phy->vbus)) {
+ err = regulator_enable(phy->vbus);
+ if (err) {
+ dev_err(phy->u_phy.dev,
+ "failed to enable usb vbus regulator: %d\n",
+ err);
+ goto fail;
+ }
+ }
+
if (phy->is_ulpi_phy)
err = ulpi_open(phy);
else
}
EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end);
+static int read_utmi_param(struct platform_device *pdev, const char *param,
+ u8 *dest)
+{
+ u32 value;
+ int err = of_property_read_u32(pdev->dev.of_node, param, &value);
+ *dest = (u8)value;
+ if (err < 0)
+ dev_err(&pdev->dev, "Failed to read USB UTMI parameter %s: %d\n",
+ param, err);
+ return err;
+}
+
+static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+ int err;
+ struct tegra_utmip_config *config;
+
+ tegra_phy->is_ulpi_phy = false;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get UTMI Pad regs\n");
+ return -ENXIO;
+ }
+
+ tegra_phy->pad_regs = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!tegra_phy->regs) {
+ dev_err(&pdev->dev, "Failed to remap UTMI Pad regs\n");
+ return -ENOMEM;
+ }
+
+ tegra_phy->config = devm_kzalloc(&pdev->dev,
+ sizeof(*tegra_phy->config), GFP_KERNEL);
+ if (!tegra_phy->config) {
+ dev_err(&pdev->dev,
+ "unable to allocate memory for USB UTMIP config\n");
+ return -ENOMEM;
+ }
+
+ config = tegra_phy->config;
+
+ err = read_utmi_param(pdev, "nvidia,hssync-start-delay",
+ &config->hssync_start_delay);
+ if (err < 0)
+ return err;
+
+ err = read_utmi_param(pdev, "nvidia,elastic-limit",
+ &config->elastic_limit);
+ if (err < 0)
+ return err;
+
+ err = read_utmi_param(pdev, "nvidia,idle-wait-delay",
+ &config->idle_wait_delay);
+ if (err < 0)
+ return err;
+
+ err = read_utmi_param(pdev, "nvidia,term-range-adj",
+ &config->term_range_adj);
+ if (err < 0)
+ return err;
+
+ err = read_utmi_param(pdev, "nvidia,xcvr-setup",
+ &config->xcvr_setup);
+ if (err < 0)
+ return err;
+
+ err = read_utmi_param(pdev, "nvidia,xcvr-lsfslew",
+ &config->xcvr_lsfslew);
+ if (err < 0)
+ return err;
+
+ err = read_utmi_param(pdev, "nvidia,xcvr-lsrslew",
+ &config->xcvr_lsrslew);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
static int tegra_usb_phy_probe(struct platform_device *pdev)
{
struct resource *res;
err = of_property_match_string(np, "phy_type", "ulpi");
if (err < 0) {
- tegra_phy->is_ulpi_phy = false;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!res) {
- dev_err(&pdev->dev, "Failed to get UTMI Pad regs\n");
- return -ENXIO;
- }
-
- tegra_phy->pad_regs = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!tegra_phy->regs) {
- dev_err(&pdev->dev, "Failed to remap UTMI Pad regs\n");
- return -ENOMEM;
- }
+ err = utmi_phy_probe(tegra_phy, pdev);
+ if (err < 0)
+ return err;
} else {
tegra_phy->is_ulpi_phy = true;
tegra_phy->reset_gpio);
return tegra_phy->reset_gpio;
}
+
+ tegra_phy->config = NULL;
}
err = of_property_match_string(np, "dr_mode", "otg");
} else
tegra_phy->mode = TEGRA_USB_PHY_MODE_OTG;
- tegra_phy->dev = &pdev->dev;
+ /* On some boards, the VBUS regulator doesn't need to be controlled */
+ if (of_find_property(np, "vbus-supply", NULL)) {
+ tegra_phy->vbus = devm_regulator_get(&pdev->dev, "vbus");
+ if (IS_ERR(tegra_phy->vbus))
+ return PTR_ERR(tegra_phy->vbus);
+ } else {
+ dev_notice(&pdev->dev, "no vbus regulator");
+ tegra_phy->vbus = ERR_PTR(-ENODEV);
+ }
+
+ tegra_phy->u_phy.dev = &pdev->dev;
err = tegra_usb_phy_init(tegra_phy);
if (err < 0)
return err;
tegra_phy->u_phy.set_suspend = tegra_usb_phy_suspend;
dev_set_drvdata(&pdev->dev, tegra_phy);
+
+ err = usb_add_phy_dev(&tegra_phy->u_phy);
+ if (err < 0) {
+ tegra_usb_phy_close(&tegra_phy->u_phy);
+ return err;
+ }
+
+ return 0;
+}
+
+static int tegra_usb_phy_remove(struct platform_device *pdev)
+{
+ struct tegra_usb_phy *tegra_phy = platform_get_drvdata(pdev);
+
+ usb_remove_phy(&tegra_phy->u_phy);
+
return 0;
}
static struct platform_driver tegra_usb_phy_driver = {
.probe = tegra_usb_phy_probe,
+ .remove = tegra_usb_phy_remove,
.driver = {
.name = "tegra-phy",
.owner = THIS_MODULE,
};
module_platform_driver(tegra_usb_phy_driver);
-static int tegra_usb_phy_match(struct device *dev, void *data)
-{
- struct tegra_usb_phy *tegra_phy = dev_get_drvdata(dev);
- struct device_node *dn = data;
-
- return (tegra_phy->dev->of_node == dn) ? 1 : 0;
-}
-
-struct usb_phy *tegra_usb_get_phy(struct device_node *dn)
-{
- struct device *dev;
- struct tegra_usb_phy *tegra_phy;
-
- dev = driver_find_device(&tegra_usb_phy_driver.driver, NULL, dn,
- tegra_usb_phy_match);
- if (!dev)
- return ERR_PTR(-EPROBE_DEFER);
-
- tegra_phy = dev_get_drvdata(dev);
-
- return &tegra_phy->u_phy;
-}
-EXPORT_SYMBOL_GPL(tegra_usb_get_phy);
-
MODULE_DESCRIPTION("Tegra USB PHY driver");
MODULE_LICENSE("GPL v2");