對於mtk和sprd的usb控制器驅動都是musb,qcom的是dwc2/dwc3,拿musb來學習下。
現在嵌入式設備USB都支持主從了,控制器也分爲兩部分,一部分是HCD,一部分是UDC。
OHCI(open host controller inferface)
UHCI(universal host controller interface)
EHCI(enhanced host controller interface)
Multipoint USB Highspeed Dual-Role Controller (MUSB HDRC)
UDC(usb device controllerr/從)
HCD(host controller device/主)
struct usb_udc {
struct usb_gadget_driver *driver;
struct usb_gadget *gadget;
struct device dev;
struct list_head list;
bool vbus;
};
struct usb_hcd {
...
const struct hc_driver *driver; /* hw-specific hooks */
...
}
某主板的dts中usb控制器描述
usb: usb@20200000 {
compatible = "sprd,usb";
reg = <0x20200000 0x2000>;
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "mc";
clocks = <&clk_ap_ahb_gates 4>;
clock-names = "core_clk";
phy-type = "usb20_sprd_phy";
usb-phy = <&hsphy>;
phy-names = "usb";
};
static int musb_sprd_probe(struct platform_device *pdev){
struct platform_device_info pinfo;
pinfo.name = "musb-hdrc";
platform_device_register_full(&pinfo);
}
static const struct of_device_id usb_ids[] = {
{ .compatible = "sprd,usb" },
{}
};
static struct platform_driver musb_sprd_driver = {
.driver = {
.name = "musb-sprd",
.of_match_table = usb_ids,
},
.probe = musb_sprd_probe,
.remove = musb_sprd_remove,
};
#define MUSB_DRIVER_NAME "musb-hdrc"
const char musb_driver_name[] = MUSB_DRIVER_NAME;
static struct platform_driver musb_driver = {
.driver = {
.name = (char *)musb_driver_name,
.bus = &platform_bus_type,
.pm = MUSB_DEV_PM_OPS,
},
.probe = musb_probe,
};
module_platform_driver(musb_driver);
static int musb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int irq = platform_get_irq_byname(pdev, "mc");
struct resource *iomem;
void __iomem *base;
if (irq <= 0)
return -ENODEV;
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, iomem);
if (IS_ERR(base))
return PTR_ERR(base);
return musb_init_controller(dev, irq, base);
}
分別註冊主從控制器
static int musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
{
struct musb *musb;
musb = allocate_instance(dev, plat->config, ctrl);--->musb_host_alloc
request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)
switch (musb->port_mode) {
case MUSB_PORT_MODE_DUAL_ROLE://雙角色/主從
musb_host_setup(musb, plat->power);//主
musb_gadget_setup(musb);//從
}
HCD的註冊過程
UDC的註冊過程
int musb_gadget_setup(struct musb *musb)
{
int status;
/* REVISIT minor race: if (erroneously) setting up two
* musb peripherals at the same time, only the bus lock
* is probably held.
*/
musb->g.ops = &musb_gadget_operations;
musb->g.max_speed = USB_SPEED_HIGH;
musb->g.speed = USB_SPEED_UNKNOWN;
musb->g.sg_supported = true;
MUSB_DEV_MODE(musb);
musb->xceiv->otg->default_a = 0;
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
/* this "gadget" abstracts/virtualizes the controller */
musb->g.name = musb_driver_name;
#if IS_ENABLED(CONFIG_USB_MUSB_DUAL_ROLE)
musb->g.is_otg = 1;
#elif IS_ENABLED(CONFIG_USB_MUSB_GADGET)
musb->g.is_otg = 0;
#endif
musb_g_init_endpoints(musb);
musb->is_active = 0;
musb_platform_try_idle(musb, 0);
status = usb_add_gadget_udc(musb->controller, &musb->g);
if (status)
goto err;
return 0;
err:
musb->g.dev.parent = NULL;
device_unregister(&musb->g.dev);
return status;
}
/**
* usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
* @parent: the parent device to this udc. Usually the controller driver's
* device.
* @gadget: the gadget to be added to the list.
* @release: a gadget release function.
*
* Returns zero on success, negative errno otherwise.
*/
int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
void (*release)(struct device *dev))
{
struct usb_udc *udc;
int ret = -ENOMEM;
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
if (!udc)
goto err1;
dev_set_name(&gadget->dev, "gadget");
INIT_WORK(&gadget->work, usb_gadget_state_work);
gadget->dev.parent = parent;
#ifdef CONFIG_HAS_DMA
dma_set_coherent_mask(&gadget->dev, parent->coherent_dma_mask);
gadget->dev.dma_parms = parent->dma_parms;
gadget->dev.dma_mask = parent->dma_mask;
#endif
if (release)
gadget->dev.release = release;
else
gadget->dev.release = usb_udc_nop_release;
ret = device_register(&gadget->dev);
if (ret)
goto err2;
device_initialize(&udc->dev);
udc->dev.release = usb_udc_release;
udc->dev.class = udc_class;
udc->dev.groups = usb_udc_attr_groups;
udc->dev.parent = parent;
ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj));
if (ret)
goto err3;
udc->gadget = gadget;
gadget->udc = udc;
mutex_lock(&udc_lock);
list_add_tail(&udc->list, &udc_list);
ret = device_add(&udc->dev);
if (ret)
goto err4;
ret = usb_charger_init(gadget);
if (ret)
goto err5;
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
udc->vbus = true;
mutex_unlock(&udc_lock);
return 0;
err5:
device_del(&udc->dev);
err4:
list_del(&udc->list);
mutex_unlock(&udc_lock);
err3:
put_device(&udc->dev);
device_del(&gadget->dev);
err2:
put_device(&gadget->dev);
kfree(udc);
err1:
return ret;
}
EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
/**
* usb_add_gadget_udc - adds a new gadget to the udc class driver list
* @parent: the parent device to this udc. Usually the controller
* driver's device.
* @gadget: the gadget to be added to the list
*
* Returns zero on success, negative errno otherwise.
*/
int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
{
return usb_add_gadget_udc_release(parent, gadget, NULL);
}
EXPORT_SYMBOL_GPL(usb_add_gadget_udc);