in host/xhci-tegra.c [1401:1771]
static int tegra_xusb_probe(struct platform_device *pdev)
{
struct of_phandle_args args;
struct tegra_xusb *tegra;
struct device_node *np;
struct resource *regs;
struct xhci_hcd *xhci;
unsigned int i, j, k;
struct phy *phy;
int err;
BUILD_BUG_ON(sizeof(struct tegra_xusb_fw_header) != 256);
tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
if (!tegra)
return -ENOMEM;
tegra->soc = of_device_get_match_data(&pdev->dev);
mutex_init(&tegra->lock);
tegra->dev = &pdev->dev;
err = tegra_xusb_init_context(tegra);
if (err < 0)
return err;
tegra->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ®s);
if (IS_ERR(tegra->regs))
return PTR_ERR(tegra->regs);
tegra->fpci_base = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(tegra->fpci_base))
return PTR_ERR(tegra->fpci_base);
if (tegra->soc->has_ipfs) {
tegra->ipfs_base = devm_platform_ioremap_resource(pdev, 2);
if (IS_ERR(tegra->ipfs_base))
return PTR_ERR(tegra->ipfs_base);
}
tegra->xhci_irq = platform_get_irq(pdev, 0);
if (tegra->xhci_irq < 0)
return tegra->xhci_irq;
tegra->mbox_irq = platform_get_irq(pdev, 1);
if (tegra->mbox_irq < 0)
return tegra->mbox_irq;
tegra->padctl = tegra_xusb_padctl_get(&pdev->dev);
if (IS_ERR(tegra->padctl))
return PTR_ERR(tegra->padctl);
np = of_parse_phandle(pdev->dev.of_node, "nvidia,xusb-padctl", 0);
if (!np) {
err = -ENODEV;
goto put_padctl;
}
/* Older device-trees don't have padctrl interrupt */
err = of_irq_parse_one(np, 0, &args);
if (!err) {
tegra->padctl_irq = of_irq_get(np, 0);
if (tegra->padctl_irq <= 0) {
err = (tegra->padctl_irq == 0) ? -ENODEV : tegra->padctl_irq;
goto put_padctl;
}
} else {
dev_dbg(&pdev->dev,
"%pOF is missing an interrupt, disabling PM support\n", np);
}
tegra->host_clk = devm_clk_get(&pdev->dev, "xusb_host");
if (IS_ERR(tegra->host_clk)) {
err = PTR_ERR(tegra->host_clk);
dev_err(&pdev->dev, "failed to get xusb_host: %d\n", err);
goto put_padctl;
}
tegra->falcon_clk = devm_clk_get(&pdev->dev, "xusb_falcon_src");
if (IS_ERR(tegra->falcon_clk)) {
err = PTR_ERR(tegra->falcon_clk);
dev_err(&pdev->dev, "failed to get xusb_falcon_src: %d\n", err);
goto put_padctl;
}
tegra->ss_clk = devm_clk_get(&pdev->dev, "xusb_ss");
if (IS_ERR(tegra->ss_clk)) {
err = PTR_ERR(tegra->ss_clk);
dev_err(&pdev->dev, "failed to get xusb_ss: %d\n", err);
goto put_padctl;
}
tegra->ss_src_clk = devm_clk_get(&pdev->dev, "xusb_ss_src");
if (IS_ERR(tegra->ss_src_clk)) {
err = PTR_ERR(tegra->ss_src_clk);
dev_err(&pdev->dev, "failed to get xusb_ss_src: %d\n", err);
goto put_padctl;
}
tegra->hs_src_clk = devm_clk_get(&pdev->dev, "xusb_hs_src");
if (IS_ERR(tegra->hs_src_clk)) {
err = PTR_ERR(tegra->hs_src_clk);
dev_err(&pdev->dev, "failed to get xusb_hs_src: %d\n", err);
goto put_padctl;
}
tegra->fs_src_clk = devm_clk_get(&pdev->dev, "xusb_fs_src");
if (IS_ERR(tegra->fs_src_clk)) {
err = PTR_ERR(tegra->fs_src_clk);
dev_err(&pdev->dev, "failed to get xusb_fs_src: %d\n", err);
goto put_padctl;
}
tegra->pll_u_480m = devm_clk_get(&pdev->dev, "pll_u_480m");
if (IS_ERR(tegra->pll_u_480m)) {
err = PTR_ERR(tegra->pll_u_480m);
dev_err(&pdev->dev, "failed to get pll_u_480m: %d\n", err);
goto put_padctl;
}
tegra->clk_m = devm_clk_get(&pdev->dev, "clk_m");
if (IS_ERR(tegra->clk_m)) {
err = PTR_ERR(tegra->clk_m);
dev_err(&pdev->dev, "failed to get clk_m: %d\n", err);
goto put_padctl;
}
tegra->pll_e = devm_clk_get(&pdev->dev, "pll_e");
if (IS_ERR(tegra->pll_e)) {
err = PTR_ERR(tegra->pll_e);
dev_err(&pdev->dev, "failed to get pll_e: %d\n", err);
goto put_padctl;
}
if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
tegra->host_rst = devm_reset_control_get(&pdev->dev,
"xusb_host");
if (IS_ERR(tegra->host_rst)) {
err = PTR_ERR(tegra->host_rst);
dev_err(&pdev->dev,
"failed to get xusb_host reset: %d\n", err);
goto put_padctl;
}
tegra->ss_rst = devm_reset_control_get(&pdev->dev, "xusb_ss");
if (IS_ERR(tegra->ss_rst)) {
err = PTR_ERR(tegra->ss_rst);
dev_err(&pdev->dev, "failed to get xusb_ss reset: %d\n",
err);
goto put_padctl;
}
} else {
err = tegra_xusb_powerdomain_init(&pdev->dev, tegra);
if (err)
goto put_powerdomains;
}
tegra->supplies = devm_kcalloc(&pdev->dev, tegra->soc->num_supplies,
sizeof(*tegra->supplies), GFP_KERNEL);
if (!tegra->supplies) {
err = -ENOMEM;
goto put_powerdomains;
}
regulator_bulk_set_supply_names(tegra->supplies,
tegra->soc->supply_names,
tegra->soc->num_supplies);
err = devm_regulator_bulk_get(&pdev->dev, tegra->soc->num_supplies,
tegra->supplies);
if (err) {
dev_err(&pdev->dev, "failed to get regulators: %d\n", err);
goto put_powerdomains;
}
for (i = 0; i < tegra->soc->num_types; i++) {
if (!strncmp(tegra->soc->phy_types[i].name, "usb2", 4))
tegra->num_usb_phys = tegra->soc->phy_types[i].num;
tegra->num_phys += tegra->soc->phy_types[i].num;
}
tegra->phys = devm_kcalloc(&pdev->dev, tegra->num_phys,
sizeof(*tegra->phys), GFP_KERNEL);
if (!tegra->phys) {
err = -ENOMEM;
goto put_powerdomains;
}
for (i = 0, k = 0; i < tegra->soc->num_types; i++) {
char prop[8];
for (j = 0; j < tegra->soc->phy_types[i].num; j++) {
snprintf(prop, sizeof(prop), "%s-%d",
tegra->soc->phy_types[i].name, j);
phy = devm_phy_optional_get(&pdev->dev, prop);
if (IS_ERR(phy)) {
dev_err(&pdev->dev,
"failed to get PHY %s: %ld\n", prop,
PTR_ERR(phy));
err = PTR_ERR(phy);
goto put_powerdomains;
}
tegra->phys[k++] = phy;
}
}
tegra->hcd = usb_create_hcd(&tegra_xhci_hc_driver, &pdev->dev,
dev_name(&pdev->dev));
if (!tegra->hcd) {
err = -ENOMEM;
goto put_powerdomains;
}
tegra->hcd->skip_phy_initialization = 1;
tegra->hcd->regs = tegra->regs;
tegra->hcd->rsrc_start = regs->start;
tegra->hcd->rsrc_len = resource_size(regs);
/*
* This must happen after usb_create_hcd(), because usb_create_hcd()
* will overwrite the drvdata of the device with the hcd it creates.
*/
platform_set_drvdata(pdev, tegra);
err = tegra_xusb_clk_enable(tegra);
if (err) {
dev_err(tegra->dev, "failed to enable clocks: %d\n", err);
goto put_hcd;
}
err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies);
if (err) {
dev_err(tegra->dev, "failed to enable regulators: %d\n", err);
goto disable_clk;
}
err = tegra_xusb_phy_enable(tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to enable PHYs: %d\n", err);
goto disable_regulator;
}
/*
* The XUSB Falcon microcontroller can only address 40 bits, so set
* the DMA mask accordingly.
*/
err = dma_set_mask_and_coherent(tegra->dev, DMA_BIT_MASK(40));
if (err < 0) {
dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
goto disable_phy;
}
err = tegra_xusb_request_firmware(tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to request firmware: %d\n", err);
goto disable_phy;
}
err = tegra_xusb_unpowergate_partitions(tegra);
if (err)
goto free_firmware;
tegra_xusb_config(tegra);
err = tegra_xusb_load_firmware(tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to load firmware: %d\n", err);
goto powergate;
}
err = usb_add_hcd(tegra->hcd, tegra->xhci_irq, IRQF_SHARED);
if (err < 0) {
dev_err(&pdev->dev, "failed to add USB HCD: %d\n", err);
goto powergate;
}
device_wakeup_enable(tegra->hcd->self.controller);
xhci = hcd_to_xhci(tegra->hcd);
xhci->shared_hcd = usb_create_shared_hcd(&tegra_xhci_hc_driver,
&pdev->dev,
dev_name(&pdev->dev),
tegra->hcd);
if (!xhci->shared_hcd) {
dev_err(&pdev->dev, "failed to create shared HCD\n");
err = -ENOMEM;
goto remove_usb2;
}
err = usb_add_hcd(xhci->shared_hcd, tegra->xhci_irq, IRQF_SHARED);
if (err < 0) {
dev_err(&pdev->dev, "failed to add shared HCD: %d\n", err);
goto put_usb3;
}
err = devm_request_threaded_irq(&pdev->dev, tegra->mbox_irq,
tegra_xusb_mbox_irq,
tegra_xusb_mbox_thread, 0,
dev_name(&pdev->dev), tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
goto remove_usb3;
}
if (tegra->padctl_irq) {
err = devm_request_threaded_irq(&pdev->dev, tegra->padctl_irq,
NULL, tegra_xusb_padctl_irq,
IRQF_ONESHOT, dev_name(&pdev->dev),
tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to request padctl IRQ: %d\n", err);
goto remove_usb3;
}
}
err = tegra_xusb_enable_firmware_messages(tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to enable messages: %d\n", err);
goto remove_usb3;
}
err = tegra_xusb_init_usb_phy(tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to init USB PHY: %d\n", err);
goto remove_usb3;
}
/* Enable wake for both USB 2.0 and USB 3.0 roothubs */
device_init_wakeup(&tegra->hcd->self.root_hub->dev, true);
device_init_wakeup(&xhci->shared_hcd->self.root_hub->dev, true);
pm_runtime_use_autosuspend(tegra->dev);
pm_runtime_set_autosuspend_delay(tegra->dev, 2000);
pm_runtime_mark_last_busy(tegra->dev);
pm_runtime_set_active(tegra->dev);
if (tegra->padctl_irq) {
device_init_wakeup(tegra->dev, true);
pm_runtime_enable(tegra->dev);
}
return 0;
remove_usb3:
usb_remove_hcd(xhci->shared_hcd);
put_usb3:
usb_put_hcd(xhci->shared_hcd);
remove_usb2:
usb_remove_hcd(tegra->hcd);
powergate:
tegra_xusb_powergate_partitions(tegra);
free_firmware:
dma_free_coherent(&pdev->dev, tegra->fw.size, tegra->fw.virt,
tegra->fw.phys);
disable_phy:
tegra_xusb_phy_disable(tegra);
disable_regulator:
regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
disable_clk:
tegra_xusb_clk_disable(tegra);
put_hcd:
usb_put_hcd(tegra->hcd);
put_powerdomains:
tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
put_padctl:
of_node_put(np);
tegra_xusb_padctl_put(tegra->padctl);
return err;
}