static int tegra_xusb_probe()

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, &regs);
	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;
}