static int of_fhci_probe()

in host/fhci-hcd.c [561:780]


static int of_fhci_probe(struct platform_device *ofdev)
{
	struct device *dev = &ofdev->dev;
	struct device_node *node = dev->of_node;
	struct usb_hcd *hcd;
	struct fhci_hcd *fhci;
	struct resource usb_regs;
	unsigned long pram_addr;
	unsigned int usb_irq;
	const char *sprop;
	const u32 *iprop;
	int size;
	int ret;
	int i;
	int j;

	if (usb_disabled())
		return -ENODEV;

	sprop = of_get_property(node, "mode", NULL);
	if (sprop && strcmp(sprop, "host"))
		return -ENODEV;

	hcd = usb_create_hcd(&fhci_driver, dev, dev_name(dev));
	if (!hcd) {
		dev_err(dev, "could not create hcd\n");
		return -ENOMEM;
	}

	fhci = hcd_to_fhci(hcd);
	hcd->self.controller = dev;
	dev_set_drvdata(dev, hcd);

	iprop = of_get_property(node, "hub-power-budget", &size);
	if (iprop && size == sizeof(*iprop))
		hcd->power_budget = *iprop;

	/* FHCI registers. */
	ret = of_address_to_resource(node, 0, &usb_regs);
	if (ret) {
		dev_err(dev, "could not get regs\n");
		goto err_regs;
	}

	hcd->regs = ioremap(usb_regs.start, resource_size(&usb_regs));
	if (!hcd->regs) {
		dev_err(dev, "could not ioremap regs\n");
		ret = -ENOMEM;
		goto err_regs;
	}
	fhci->regs = hcd->regs;

	/* Parameter RAM. */
	iprop = of_get_property(node, "reg", &size);
	if (!iprop || size < sizeof(*iprop) * 4) {
		dev_err(dev, "can't get pram offset\n");
		ret = -EINVAL;
		goto err_pram;
	}

	pram_addr = cpm_muram_alloc(FHCI_PRAM_SIZE, 64);
	if (IS_ERR_VALUE(pram_addr)) {
		dev_err(dev, "failed to allocate usb pram\n");
		ret = -ENOMEM;
		goto err_pram;
	}

	qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, QE_CR_SUBBLOCK_USB,
		     QE_CR_PROTOCOL_UNSPECIFIED, pram_addr);
	fhci->pram = cpm_muram_addr(pram_addr);

	/* GPIOs and pins */
	for (i = 0; i < NUM_GPIOS; i++) {
		int gpio;
		enum of_gpio_flags flags;

		gpio = of_get_gpio_flags(node, i, &flags);
		fhci->gpios[i] = gpio;
		fhci->alow_gpios[i] = flags & OF_GPIO_ACTIVE_LOW;

		if (!gpio_is_valid(gpio)) {
			if (i < GPIO_SPEED) {
				dev_err(dev, "incorrect GPIO%d: %d\n",
					i, gpio);
				goto err_gpios;
			} else {
				dev_info(dev, "assuming board doesn't have "
					"%s gpio\n", i == GPIO_SPEED ?
					"speed" : "power");
				continue;
			}
		}

		ret = gpio_request(gpio, dev_name(dev));
		if (ret) {
			dev_err(dev, "failed to request gpio %d", i);
			goto err_gpios;
		}

		if (i >= GPIO_SPEED) {
			ret = gpio_direction_output(gpio, 0);
			if (ret) {
				dev_err(dev, "failed to set gpio %d as "
					"an output\n", i);
				i++;
				goto err_gpios;
			}
		}
	}

	for (j = 0; j < NUM_PINS; j++) {
		fhci->pins[j] = qe_pin_request(node, j);
		if (IS_ERR(fhci->pins[j])) {
			ret = PTR_ERR(fhci->pins[j]);
			dev_err(dev, "can't get pin %d: %d\n", j, ret);
			goto err_pins;
		}
	}

	/* Frame limit timer and its interrupt. */
	fhci->timer = gtm_get_timer16();
	if (IS_ERR(fhci->timer)) {
		ret = PTR_ERR(fhci->timer);
		dev_err(dev, "failed to request qe timer: %i", ret);
		goto err_get_timer;
	}

	ret = request_irq(fhci->timer->irq, fhci_frame_limit_timer_irq,
			  0, "qe timer (usb)", hcd);
	if (ret) {
		dev_err(dev, "failed to request timer irq");
		goto err_timer_irq;
	}

	/* USB Host interrupt. */
	usb_irq = irq_of_parse_and_map(node, 0);
	if (usb_irq == NO_IRQ) {
		dev_err(dev, "could not get usb irq\n");
		ret = -EINVAL;
		goto err_usb_irq;
	}

	/* Clocks. */
	sprop = of_get_property(node, "fsl,fullspeed-clock", NULL);
	if (sprop) {
		fhci->fullspeed_clk = qe_clock_source(sprop);
		if (fhci->fullspeed_clk == QE_CLK_DUMMY) {
			dev_err(dev, "wrong fullspeed-clock\n");
			ret = -EINVAL;
			goto err_clocks;
		}
	}

	sprop = of_get_property(node, "fsl,lowspeed-clock", NULL);
	if (sprop) {
		fhci->lowspeed_clk = qe_clock_source(sprop);
		if (fhci->lowspeed_clk == QE_CLK_DUMMY) {
			dev_err(dev, "wrong lowspeed-clock\n");
			ret = -EINVAL;
			goto err_clocks;
		}
	}

	if (fhci->fullspeed_clk == QE_CLK_NONE &&
			fhci->lowspeed_clk == QE_CLK_NONE) {
		dev_err(dev, "no clocks specified\n");
		ret = -EINVAL;
		goto err_clocks;
	}

	dev_info(dev, "at 0x%p, irq %d\n", hcd->regs, usb_irq);

	fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF);

	/* Start with full-speed, if possible. */
	if (fhci->fullspeed_clk != QE_CLK_NONE) {
		fhci_config_transceiver(fhci, FHCI_PORT_FULL);
		qe_usb_clock_set(fhci->fullspeed_clk, USB_CLOCK);
	} else {
		fhci_config_transceiver(fhci, FHCI_PORT_LOW);
		qe_usb_clock_set(fhci->lowspeed_clk, USB_CLOCK >> 3);
	}

	/* Clear and disable any pending interrupts. */
	out_be16(&fhci->regs->usb_usber, 0xffff);
	out_be16(&fhci->regs->usb_usbmr, 0);

	ret = usb_add_hcd(hcd, usb_irq, 0);
	if (ret < 0)
		goto err_add_hcd;

	device_wakeup_enable(hcd->self.controller);

	fhci_dfs_create(fhci);

	return 0;

err_add_hcd:
err_clocks:
	irq_dispose_mapping(usb_irq);
err_usb_irq:
	free_irq(fhci->timer->irq, hcd);
err_timer_irq:
	gtm_put_timer16(fhci->timer);
err_get_timer:
err_pins:
	while (--j >= 0)
		qe_pin_free(fhci->pins[j]);
err_gpios:
	while (--i >= 0) {
		if (gpio_is_valid(fhci->gpios[i]))
			gpio_free(fhci->gpios[i]);
	}
	cpm_muram_free(pram_addr);
err_pram:
	iounmap(hcd->regs);
err_regs:
	usb_put_hcd(hcd);
	return ret;
}