#include #include #define VLYNQ_BASE (vlynq_controller ? AR7_VLYNQ1_BASE : AR7_VLYNQ0_BASE) #define VLYNQ_OHCI_CFG_BASEADDR (VLYNQ_BASE + 0x00010000) #define VLYNQ_OHCI_CFG_RESETBASE (VLYNQ_BASE + 0x00000200) #define VLYNQ_OHCI_CFG_CLKBASE (VLYNQ_BASE + 0x00000208) #define VLYNQ_OHCI_CFG_ENDIAN_REG (VLYNQ_BASE + 0x0000021c) #define VLYNQ_OHCI_RESET_BIT (2) #define VLYNQ_OHCI_CLOCK_VAL (0x03) #define VLYNQ_OHCI_ENDIAN_VAL (0x00) static int usb_host_vlynq_interrupt = 2; static int usb_host_vlynq_vector = 0; static short vlynq_controller = 1; #ifdef CONFIG_USB_OHCI_DMA_ADDRESS_TRANSLATION static const u64 ohci_dma_translation_offset = CPHYSADDR (AR7_CS1_BASE); #else #error CONFIG_USB_OHCI_DMA_ADDRESS_TRANSLATION needed for VLYNQ #endif static void vlynq_start_hc (struct ohci_hcd *ohci) { ohci_dbg (ohci, "Start hc.\n"); *(volatile unsigned int*) VLYNQ_OHCI_CFG_RESETBASE &= ~(1 << VLYNQ_OHCI_RESET_BIT); udelay(5000); *(volatile unsigned int*) VLYNQ_OHCI_CFG_RESETBASE |= (1 << VLYNQ_OHCI_RESET_BIT); udelay(5000); *(volatile unsigned int*) VLYNQ_OHCI_CFG_CLKBASE |= VLYNQ_OHCI_CLOCK_VAL; udelay(5000); *(volatile unsigned int*) VLYNQ_OHCI_CFG_ENDIAN_REG |= VLYNQ_OHCI_ENDIAN_VAL; udelay(5000); } static int vlynq_init (struct ohci_hcd *ohci) { struct _vlynq_context *vlynq_context; struct _vlynq_memory_windows *mem_window; struct _vlynq_interrupts *interrupt; vlynq_context = avm_vlynq_alloc_context (vlynq_controller); if (vlynq_context == NULL) return -ENOMEM; mem_window = &vlynq_context->Remote2Local; mem_window->Tx_Address = 0x00; mem_window->WindowCount = 1; mem_window->Windows[0].Offset = CPHYSADDR (AR7_CS1_BASE); mem_window->Windows[0].Size = 0x4000000; mem_window = &vlynq_context->Local2Remote; mem_window->Tx_Address = CPHYSADDR (VLYNQ_BASE); mem_window->WindowCount = 1; mem_window->Windows[0].Offset = 0x0C000000; mem_window->Windows[0].Size = 0x30000; interrupt = &vlynq_context->LocalIrq; interrupt->LocalEnable = VLYNQ_INT_LOCAL; interrupt->RemoteEnable = VLYNQ_INT_ROOT_ISR; interrupt->vector = usb_host_vlynq_vector; interrupt = &vlynq_context->RemoteIrq; interrupt->LocalEnable = VLYNQ_INT_REMOTE; interrupt->RemoteEnable = VLYNQ_INT_ROOT_ISR; interrupt->vector = usb_host_vlynq_vector; if (avm_vlynq_init (vlynq_controller) != 0) { ohci_err (ohci, "'avm_vlynq_init' failed.\n"); return -EIO; } if (avm_vlynq_init_link (vlynq_controller) != 0) { ohci_err (ohci, "'avm_vlynq_init_link' failed.\n"); return -EIO; } if (vlynq_irq_vector_setup (vlynq_controller, usb_host_vlynq_interrupt, usb_host_vlynq_vector, VLYNQ_INT_REMOTE, VLYNQ_VECTOR_LEVEL, VLYNQ_VECTOR_ACTIVE_LOW) != 0) { ohci_err (ohci, "'vlynq_irq_vector_setup' failed.\n"); return -EIO; } if (vlynq_irq_enable (vlynq_controller, VLYNQ_INT_REMOTE, usb_host_vlynq_interrupt) != 0) { ohci_err (ohci, "'vlynq_irq_enable' failed.\n"); return -EIO; } vlynq_start_hc (ohci); return 0; } static int __devinit ohci_vlynq_start (struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ret; ohci_dbg (ohci, "Start ohci-vlynq.\n"); if ((ret = ohci_init (ohci)) < 0) { ohci_err (ohci, "ohci_init failed.\n"); return ret; } if ((ret = ohci_run (ohci)) < 0) { ohci_err (ohci, "ohci_run failed.\n"); ohci_stop (hcd); return ret; } return 0; } /* The dmamask must be set for OHCI to work */ static u64 ohci_dmamask = ~(u32)0; static struct device ohci_vlynq_fake_device = { .dma_mask = &ohci_dmamask, .coherent_dma_mask = 0xffffffff, }; static struct device_driver ohci_vlynq_driver = { .name = "ohci-vlynq", }; static const struct hc_driver ohci_vlynq_hc_driver = { .description = hcd_name, .product_desc = "Vlynq OHCI", .hcd_priv_size = sizeof(struct ohci_hcd), /* * generic hardware linkage */ .irq = ohci_irq, .flags = HCD_USB11 | HCD_MEMORY, /* * basic lifecycle operations */ .start = ohci_vlynq_start, #ifdef CONFIG_PM /* suspend: ohci_vlynq_suspend, -- tbd */ /* resume: ohci_vlynq_resume, -- tbd */ #endif /*CONFIG_PM*/ .stop = ohci_stop, /* * managing i/o requests and associated device resources */ .urb_enqueue = ohci_urb_enqueue, .urb_dequeue = ohci_urb_dequeue, .endpoint_disable = ohci_endpoint_disable, /* * scheduling support */ .get_frame_number = ohci_get_frame, /* * root hub support */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, }; static int __init ohci_hcd_vlynq_init (void) { struct usb_hcd *hcd; char serial_buffer[20]; printk (KERN_INFO DRIVER_INFO "\n"); device_initialize (&ohci_vlynq_fake_device); ohci_vlynq_fake_device.driver = &ohci_vlynq_driver; kobject_set_name (&ohci_vlynq_fake_device.kobj, "ohci-vlynq"); kobject_add (&ohci_vlynq_fake_device.kobj); sprintf (serial_buffer, "%X", VLYNQ_BASE); hcd = usb_create_hcd (&ohci_vlynq_hc_driver, &ohci_vlynq_fake_device, serial_buffer); if (!hcd) return -ENOMEM; hcd->rsrc_start = VLYNQ_OHCI_CFG_BASEADDR; hcd->rsrc_len = 0x500; hcd->regs = (void*) VLYNQ_OHCI_CFG_BASEADDR; vlynq_init (hcd_to_ohci (hcd)); ohci_hcd_init (hcd_to_ohci (hcd)); if (usb_add_hcd (hcd, vlynq_vector_to_irq (vlynq_controller, usb_host_vlynq_vector), 0) < 0) return -EIO; return 0; } static void __exit ohci_hcd_vlynq_cleanup (void) { struct usb_hcd *hcd; hcd = dev_get_drvdata (&ohci_vlynq_fake_device); usb_remove_hcd (hcd); usb_put_hcd (hcd); ar7_vlynq_free_context (vlynq_controller); kobject_del (&ohci_vlynq_fake_device.kobj); } module_init (ohci_hcd_vlynq_init); module_exit (ohci_hcd_vlynq_cleanup);