// SPDX-License-Identifier: GPL-1.0+ /* * OHCI HCD (Host Controller Driver) for USB. * * Copyright (C) 2004 SAN People (Pty) Ltd. * Copyright (C) 2005 Thibaut VARENE <varenet@parisc-linux.org> * * AT91 Bus Glue * * Based on fragments of 2.4 driver by Rick Bronson. * Based on ohci-omap.c * * This file is licenced under the GPL.
*/
/* interface, function and usb clocks; sometimes also an AHB clock */ #define hcd_to_ohci_at91_priv(h) \
((struct ohci_at91_priv *)hcd_to_ohci(h)->priv)
#define AT91_MAX_USBH_PORTS 3 struct at91_usbh_data { struct gpio_desc *vbus_pin[AT91_MAX_USBH_PORTS]; struct gpio_desc *overcurrent_pin[AT91_MAX_USBH_PORTS];
u8 ports; /* number of ports on root hub */
u8 overcurrent_supported;
u8 overcurrent_status[AT91_MAX_USBH_PORTS];
u8 overcurrent_changed[AT91_MAX_USBH_PORTS];
};
struct ohci_at91_priv { struct clk *iclk; struct clk *fclk; struct clk *hclk; bool clocked; bool wakeup; /* Saved wake-up state for resume */ struct regmap *sfr_regmap;
u32 suspend_smc_id;
}; /* interface and function clocks; sometimes also an AHB clock */
regmap = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr"); if (IS_ERR(regmap)) {
regmap = syscon_regmap_lookup_by_compatible("microchip,sam9x60-sfr"); if (IS_ERR(regmap))
regmap = NULL;
}
return regmap;
}
/* configure so an HC device and id are always provided */ /* always called with process context; sleeping is OK */
/* * usb_hcd_at91_probe - initialize AT91-based HCDs * @driver: Pointer to hc driver instance * @pdev: USB controller to probe * * Context: task context, might sleep * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data.
*/ staticint usb_hcd_at91_probe(conststruct hc_driver *driver, struct platform_device *pdev)
{ struct at91_usbh_data *board; struct ohci_hcd *ohci; int retval; struct usb_hcd *hcd; struct ohci_at91_priv *ohci_at91; struct device *dev = &pdev->dev; struct resource *res; int irq;
irq = platform_get_irq(pdev, 0); if (irq < 0) return irq;
/* * The RemoteWakeupConnected bit has to be set explicitly * before calling ohci_run. The reset value of this bit is 0.
*/
ohci->hc_control = OHCI_CTRL_RWC;
/* may be called with controller, bus, and devices active */
/* * usb_hcd_at91_remove - shutdown processing for AT91-based HCDs * @hcd: USB controller to remove * @pdev: Platform device required for cleanup * * Context: task context, might sleep * * Reverses the effect of usb_hcd_at91_probe(), first invoking * the HCD's stop() method. It is always called from a thread * context, "rmmod" or something similar.
*/ staticvoid usb_hcd_at91_remove(struct usb_hcd *hcd, struct platform_device *pdev)
{
usb_remove_hcd(hcd);
at91_stop_hc(pdev);
usb_put_hcd(hcd);
}
/*-------------------------------------------------------------------------*/ staticvoid ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int enable)
{ if (!valid_port(port)) return;
gpiod_set_value(pdata->vbus_pin[port], enable);
}
staticint ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port)
{ if (!valid_port(port)) return -EINVAL;
return gpiod_get_value(pdata->vbus_pin[port]);
}
/* * Update the status data from the hub with the over-current indicator change.
*/ staticint ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf)
{ struct at91_usbh_data *pdata = hcd->self.controller->platform_data; int length = ohci_hub_status_data(hcd, buf); int port;
at91_for_each_port(port) { if (pdata->overcurrent_changed[port]) { if (!length)
length = 1;
buf[0] |= 1 << (port + 1);
}
}
if (ohci_at91->suspend_smc_id) { struct arm_smccc_res res;
arm_smccc_smc(ohci_at91->suspend_smc_id, set, 0, 0, 0, 0, 0, 0, &res); if (res.a0) return -EINVAL;
} elseif (regmap) {
ret = regmap_read(regmap, AT91_SFR_OHCIICR, ®val); if (ret) return ret;
if (set)
regval |= AT91_OHCIICR_USB_SUSPEND; else
regval &= ~AT91_OHCIICR_USB_SUSPEND;
regmap_write(regmap, AT91_SFR_OHCIICR, regval);
}
return 0;
}
/* * Look at the control requests to the root hub and see if we need to override.
*/ staticint ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{ struct at91_usbh_data *pdata = dev_get_platdata(hcd->self.controller); struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd); struct usb_hub_descriptor *desc; int ret = -EINVAL;
u32 *data = (u32 *)buf;
/* From the GPIO notifying the over-current situation, find
* out the corresponding port */
at91_for_each_port(port) { if (gpiod_to_irq(pdata->overcurrent_pin[port]) == irq) break;
}
if (port == AT91_MAX_USBH_PORTS) {
dev_err(& pdev->dev, "overcurrent interrupt from unknown GPIO\n"); return IRQ_HANDLED;
}
val = gpiod_get_value(pdata->overcurrent_pin[port]);
/* When notified of an over-current situation, disable power on the corresponding port, and mark this port in
over-current. */ if (!val) {
ohci_at91_usb_set_power(pdata, port, 0);
pdata->overcurrent_status[port] = 1;
pdata->overcurrent_changed[port] = 1;
}
dev_dbg(& pdev->dev, "overcurrent situation %s\n",
val ? "exited" : "notified");
staticint ohci_hcd_at91_drv_probe(struct platform_device *pdev)
{ struct device_node *np = pdev->dev.of_node; struct at91_usbh_data *pdata; int i; int ret; int err;
u32 ports;
/* Right now device-tree probed devices don't get dma_mask set. * Since shared usb code relies on it, set it here for now. * Once we have dma capability bindings this can go away.
*/
ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (ret) return ret;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM;
pdev->dev.platform_data = pdata;
if (!of_property_read_u32(np, "num-ports", &ports))
pdata->ports = ports;
at91_for_each_port(i) { if (i >= pdata->ports) break;
pdata->vbus_pin[i] =
devm_gpiod_get_index_optional(&pdev->dev, "atmel,vbus",
i, GPIOD_OUT_HIGH); if (IS_ERR(pdata->vbus_pin[i])) {
err = PTR_ERR(pdata->vbus_pin[i]);
dev_err(&pdev->dev, "unable to claim gpio \"vbus\": %d\n", err); continue;
}
}
at91_for_each_port(i) { if (i >= pdata->ports) break;
pdata->overcurrent_pin[i] =
devm_gpiod_get_index_optional(&pdev->dev, "atmel,oc",
i, GPIOD_IN); if (!pdata->overcurrent_pin[i]) continue; if (IS_ERR(pdata->overcurrent_pin[i])) {
err = PTR_ERR(pdata->overcurrent_pin[i]);
dev_err(&pdev->dev, "unable to claim gpio \"overcurrent\": %d\n", err); continue;
}
ret = devm_request_irq(&pdev->dev,
gpiod_to_irq(pdata->overcurrent_pin[i]),
ohci_hcd_at91_overcurrent_irq,
IRQF_SHARED, "ohci_overcurrent", pdev); if (ret)
dev_info(&pdev->dev, "failed to request gpio \"overcurrent\" IRQ\n");
}
/* * Disable wakeup if we are going to sleep with slow clock mode * enabled.
*/
ohci_at91->wakeup = device_may_wakeup(dev)
&& !at91_suspend_entering_slow_clock();
if (ohci_at91->wakeup)
enable_irq_wake(hcd->irq);
ret = ohci_suspend(hcd, ohci_at91->wakeup); if (ret) { if (ohci_at91->wakeup)
disable_irq_wake(hcd->irq); return ret;
} /* * The integrated transceivers seem unable to notice disconnect, * reconnect, or wakeup without the 48 MHz clock active. so for * correctness, always discard connection state (using reset). * * REVISIT: some boards will be able to turn VBUS off...
*/ if (!ohci_at91->wakeup) {
ohci->rh_state = OHCI_RH_HALTED;
if (ohci_at91->wakeup)
disable_irq_wake(hcd->irq); else
at91_start_clock(ohci_at91);
/* * According to the comment in ohci_hcd_at91_drv_suspend() * we need to do a reset if the 48Mhz clock was stopped, * that is, if ohci_at91->wakeup is clear. Tell ohci_resume() * to reset in this case by setting its "hibernated" flag.
*/
ohci_resume(hcd, !ohci_at91->wakeup);
/* * The Atmel HW has some unusual quirks, which require Atmel-specific * workarounds. We override certain hc_driver functions here to * achieve that. We explicitly do not enhance ohci_driver_overrides to * allow this more easily, since this is an unusual case, and we don't * want to encourage others to override these functions by making it * too easy.
*/
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.