From ed3391fd23ff207422a1c5534bbba73bce249a76 Mon Sep 17 00:00:00 2001 From: Kees Jongenburger Date: Mon, 26 May 2014 16:47:47 +0200 Subject: [PATCH] arm:adding the usbd source code. Change-Id: Ia3c50a8c5e11bf20100354de266913112cc236f9 http://gerrit.minix3.org/#/c/2689/ --- drivers/usbd/Makefile | 11 + drivers/usbd/README.txt | 16 + drivers/usbd/base/earm/Makefile | 19 + drivers/usbd/base/earm/usbd_earm.c | 59 ++ drivers/usbd/base/usbd.c | 140 +++++ drivers/usbd/hcd/hcd.c | 537 ++++++++++++++++ drivers/usbd/hcd/hcd_common.c | 490 +++++++++++++++ drivers/usbd/hcd/hcd_ddekit.c | 277 +++++++++ drivers/usbd/hcd/musb/musb_am335x.c | 718 ++++++++++++++++++++++ drivers/usbd/hcd/musb/musb_core.c | 570 +++++++++++++++++ drivers/usbd/hcd/musb/musb_core.h | 47 ++ drivers/usbd/hcd/musb/musb_regs.h | 126 ++++ drivers/usbd/include/usb/hcd_common.h | 214 +++++++ drivers/usbd/include/usb/hcd_ddekit.h | 18 + drivers/usbd/include/usb/hcd_interface.h | 73 +++ drivers/usbd/include/usb/hcd_platforms.h | 20 + drivers/usbd/include/usb/usb_common.h | 65 ++ drivers/usbd/include/usb/usbd_interface.h | 24 + drivers/usbd/usbd.conf | 14 + 19 files changed, 3438 insertions(+) create mode 100755 drivers/usbd/Makefile create mode 100755 drivers/usbd/README.txt create mode 100755 drivers/usbd/base/earm/Makefile create mode 100755 drivers/usbd/base/earm/usbd_earm.c create mode 100755 drivers/usbd/base/usbd.c create mode 100755 drivers/usbd/hcd/hcd.c create mode 100755 drivers/usbd/hcd/hcd_common.c create mode 100755 drivers/usbd/hcd/hcd_ddekit.c create mode 100755 drivers/usbd/hcd/musb/musb_am335x.c create mode 100755 drivers/usbd/hcd/musb/musb_core.c create mode 100755 drivers/usbd/hcd/musb/musb_core.h create mode 100755 drivers/usbd/hcd/musb/musb_regs.h create mode 100755 drivers/usbd/include/usb/hcd_common.h create mode 100755 drivers/usbd/include/usb/hcd_ddekit.h create mode 100755 drivers/usbd/include/usb/hcd_interface.h create mode 100755 drivers/usbd/include/usb/hcd_platforms.h create mode 100755 drivers/usbd/include/usb/usb_common.h create mode 100755 drivers/usbd/include/usb/usbd_interface.h create mode 100755 drivers/usbd/usbd.conf diff --git a/drivers/usbd/Makefile b/drivers/usbd/Makefile new file mode 100755 index 000000000..61048cdc8 --- /dev/null +++ b/drivers/usbd/Makefile @@ -0,0 +1,11 @@ +# Makefile for usb host controllers + +.if ${MACHINE_ARCH} == "earm" + +.include + +SUBDIR= .WAIT base/earm + +.include + +.endif diff --git a/drivers/usbd/README.txt b/drivers/usbd/README.txt new file mode 100755 index 000000000..e18565bf9 --- /dev/null +++ b/drivers/usbd/README.txt @@ -0,0 +1,16 @@ +------------------------------------------------------------------------------- +* INFORMATION: * +------------------------------------------------------------------------------- +README file for "USBD" USB host controller driver. + +created march-may 2014, JPEmbedded (info@jpembedded.eu) + +------------------------------------------------------------------------------- +* KNOWN LIMITATIONS: * +------------------------------------------------------------------------------- +- Only first configuration can be selected for attached device +- Only one device can be handled at a time, no hub functionality +- DDEKit does not implement resource deallocation for corresponding thread + creation (see ddekit_thread_terminate, ddekit_thread_create) thus resources + are spilled +- Driver assumes that there is no preemption for DDEKit threading \ No newline at end of file diff --git a/drivers/usbd/base/earm/Makefile b/drivers/usbd/base/earm/Makefile new file mode 100755 index 000000000..adc1615f2 --- /dev/null +++ b/drivers/usbd/base/earm/Makefile @@ -0,0 +1,19 @@ +# +# Makefile for the EARM USBD +# + +PROG= usbd +SRCS= usbd.c usbd_earm.c hcd.c hcd_common.c hcd_ddekit.c musb_am335x.c musb_core.c + +.PATH: ${.CURDIR}/../ ${.CURDIR}/../../hcd ${.CURDIR}/../../hcd/musb + +CPPFLAGS+= -I${.CURDIR}/../../include + +DPADD+= ${LIBDDEKIT} ${LIBDDEKIT_USB_SERVER} ${LIBDEVMAN} ${LIBUSB} ${LIBMINC} ${LIBCLKCONF} +LDADD+= -lddekit -lddekit_usb_server -ldevman -lusb -lminc -lclkconf + +MAN= + +BINDIR?= /usr/sbin + +.include \ No newline at end of file diff --git a/drivers/usbd/base/earm/usbd_earm.c b/drivers/usbd/base/earm/usbd_earm.c new file mode 100755 index 000000000..79d8df3fe --- /dev/null +++ b/drivers/usbd/base/earm/usbd_earm.c @@ -0,0 +1,59 @@ +/* + * EARM USBD setup + */ + +#include +#include + +#include +#include +#include + + +/*===========================================================================* + * usbd_init_hcd * + *===========================================================================*/ +int +usbd_init_hcd(void) +{ + /* More specific platform type than just EARM */ + static struct machine platform; + + DEBUG_DUMP; + + if (sys_getmachine(&platform)) { + USB_MSG("Getting machine type, failed"); + return EXIT_FAILURE; + } + + if (BOARD_IS_BB(platform.board_id)) { + USB_MSG("Using AM335x driver"); + return musb_am335x_init(); + } else { + USB_MSG("Only AM335x driver available"); + return EXIT_FAILURE; + } +} + + +/*===========================================================================* + * usbd_deinit_hcd * + *===========================================================================*/ +void +usbd_deinit_hcd(void) +{ + /* More specific platform type than just EARM */ + static struct machine platform; + + DEBUG_DUMP; + + if (sys_getmachine(&platform)) { + USB_MSG("Getting machine type, failed"); + return; + } + + if (BOARD_IS_BB(platform.board_id)) + musb_am335x_deinit(); + else + USB_MSG("Only AM335x driver available"); +} diff --git a/drivers/usbd/base/usbd.c b/drivers/usbd/base/usbd.c new file mode 100755 index 000000000..7a78293d1 --- /dev/null +++ b/drivers/usbd/base/usbd.c @@ -0,0 +1,140 @@ +/* + * Entry point for USBD service, that handles USB HCDs + */ + +#include /* ddekit_init */ +#include /* DDEKit threading */ + +#include /* Initializing 'devman' */ +#include /* SEF handling */ + +#include +#include + + +/*===========================================================================* + * Local declarations * + *===========================================================================*/ +static int usbd_sef_handler(int, sef_init_info_t *); +static int usbd_start(void); +static void usbd_init(void); +static void usbd_server_thread(void *); + +/* TODO: No headers for these... */ +extern void ddekit_minix_wait_exit(void); /* dde.c */ +extern void ddekit_usb_server_init(void); /* usb_server.c */ + + +/*===========================================================================* + * main * + *===========================================================================*/ +int +main(int UNUSED(argc), char * UNUSED(argv[])) +{ + int ret_val; + + USB_MSG("Starting USBD"); + + /* Basic SEF,DDE,... initialization */ + usbd_init(); + + /* Assume failure unless usbd_start exits gracefully */ + ret_val = EXIT_FAILURE; + + /* USB host controllers initialization */ + if (EXIT_SUCCESS == usbd_init_hcd()) { + + /* Try initializing 'devman' */ + if (EXIT_SUCCESS == devman_init()) { + + /* Run USB driver (actually DDEKit threads) + * until this call returns: */ + ret_val = usbd_start(); + + } else + USB_MSG("Initializing devman, failed"); + + /* Clean whatever was initialized */ + usbd_deinit_hcd(); + + } else + USB_MSG("Initializing HCDs, failed"); + + return ret_val; +} + + +/*===========================================================================* + * usbd_sef_handler * + *===========================================================================*/ +static int +usbd_sef_handler(int type, sef_init_info_t * UNUSED(info)) +{ + DEBUG_DUMP; + + switch (type) { + case SEF_INIT_FRESH: + USB_MSG("Initializing"); + return EXIT_SUCCESS; + + case SEF_INIT_LU: + USB_MSG("Updating, not implemented"); + return EXIT_FAILURE; + + case SEF_INIT_RESTART: + USB_MSG("Restarting, not implemented"); + return EXIT_FAILURE; + + default: + USB_MSG("illegal SEF type"); + return EXIT_FAILURE; + } +} + + +/*===========================================================================* + * usbd_start * + *===========================================================================*/ +static int +usbd_start(void) +{ + DEBUG_DUMP; + + /* Driver's "main loop" is within DDEKit server thread */ + if (NULL != ddekit_thread_create(usbd_server_thread, NULL, "USBD")) { + /* This will lock current thread until DDEKit terminates */ + ddekit_minix_wait_exit(); + return EXIT_SUCCESS; + } else + return EXIT_FAILURE; +} + + +/*===========================================================================* + * usbd_init * + *===========================================================================*/ +static void +usbd_init(void) +{ + DEBUG_DUMP; + + /* Set one handler for all messages */ + sef_setcb_init_fresh(usbd_sef_handler); + sef_setcb_init_lu(usbd_sef_handler); + sef_setcb_init_restart(usbd_sef_handler); + + /* Initialize DDEkit (involves sef_startup()) */ + ddekit_init(); +} + + +/*===========================================================================* + * usbd_server_thread * + *===========================================================================*/ +static void +usbd_server_thread(void * UNUSED(unused)) +{ + DEBUG_DUMP; + + ddekit_usb_server_init(); +} diff --git a/drivers/usbd/hcd/hcd.c b/drivers/usbd/hcd/hcd.c new file mode 100755 index 000000000..e2ba40989 --- /dev/null +++ b/drivers/usbd/hcd/hcd.c @@ -0,0 +1,537 @@ +/* + * Implementation of generic HCD + */ + +#include /* nanosleep */ +#include /* memcpy */ + +#include +#include +#include +#include + + +/*===========================================================================* + * Local declarations * + *===========================================================================*/ +/* Thread to handle device logic */ +static void hcd_device_thread(void *); + +/* Procedure that locks device thread forever in case of error/completion */ +static void hcd_device_finish(hcd_device_state *, const char *); + +/* Typical USD device communication procedures */ +static int hcd_enumerate(hcd_device_state *); +static int hcd_get_device_descriptor(hcd_device_state *); +static int hcd_set_address(hcd_device_state *, int); +static int hcd_get_descriptor_tree(hcd_device_state *); +static int hcd_set_configuration(hcd_device_state *, int); +static int hcd_handle_urb(hcd_device_state *); + +/* For internal use by more general methods */ +static int hcd_setup_packet(hcd_device_state *, hcd_ctrlrequest *); + + +/*===========================================================================* + * Local definitions * + *===========================================================================*/ +/* TODO: Only one device at a time */ +static hcd_device_state hcd_device[1]; + + +/*===========================================================================* + * hcd_handle_event * + *===========================================================================*/ +void +hcd_handle_event(hcd_driver_state * driver) +{ + hcd_device_state * this_device; + + DEBUG_DUMP; + + /* TODO: Finding which hcd_device is in use should be performed here */ + this_device = &(hcd_device[0]); + + /* Sometimes interrupts occur in a weird order (EP after disconnect) + * This helps finding ordering errors in DEBUG */ + USB_DBG("Event: 0x%02X, state: 0x%02X", + driver->event, this_device->state); + + /* Set what was received for device thread to use */ + this_device->driver = driver; + + /* Handle event and forward control to device thread when required */ + switch (driver->event) { + case HCD_EVENT_CONNECTED: + if (HCD_STATE_DISCONNECTED == this_device->state) { + if (EXIT_SUCCESS != hcd_connect_device( + this_device, + hcd_device_thread)) + USB_MSG("Device creation failed"); + } else + USB_MSG("Device not marked as 'disconnected' " + "for 'connection' event"); + + break; + + case HCD_EVENT_DISCONNECTED: + if (HCD_STATE_DISCONNECTED != this_device->state) { + /* If connect callback was used before, call + * it's equivalent to signal disconnection */ + if (HCD_STATE_CONNECTED == this_device->state) + hcd_disconnect_cb(this_device); + hcd_disconnect_device(this_device); + this_device->state = HCD_STATE_DISCONNECTED; + } else + USB_MSG("Device is marked as 'disconnected' " + "for 'disconnection' event"); + + break; + + case HCD_EVENT_ENDPOINT: + /* Allow device thread to continue with it's logic */ + if (HCD_STATE_DISCONNECTED != this_device->state) + hcd_device_continue(this_device); + else + USB_MSG("Device is marked as 'disconnected' " + "for 'EP' event"); + + break; + + default: + USB_ASSERT(0, "Illegal HCD event"); + break; + } +} + + +/*===========================================================================* + * hcd_device_thread * + *===========================================================================*/ +static void +hcd_device_thread(void * thread_args) +{ + hcd_device_state * this_device; + + DEBUG_DUMP; + + /* Retrieve structures from generic data */ + this_device = (hcd_device_state *)thread_args; + + /* Plugged in */ + this_device->state = HCD_STATE_CONNECTION_PENDING; + + /* Enumeration sequence */ + if (EXIT_SUCCESS != hcd_enumerate(this_device)) + hcd_device_finish(this_device, "USB device enumeration failed"); + + /* Tell everyone that device was connected */ + hcd_connect_cb(this_device); + + /* Fully configured */ + this_device->state = HCD_STATE_CONNECTED; + + USB_DBG("Waiting for URBs"); + + /* No URB's yet */ + this_device->urb = NULL; + + /* Start handling URB's */ + for(;;) { + /* Block and wait for something like 'submit URB' */ + hcd_device_wait(this_device); + + if (EXIT_SUCCESS != hcd_handle_urb(this_device)) + hcd_device_finish(this_device, "URB handling failed"); + } + + /* Finish device handling to avoid leaving thread */ + hcd_device_finish(this_device, "USB device handling completed"); +} + + +/*===========================================================================* + * hcd_device_finish * + *===========================================================================*/ +static void +hcd_device_finish(hcd_device_state * this_device, const char * finish_msg) +{ + DEBUG_DUMP; + + USB_MSG("USB device handling finished with message: '%s'", finish_msg); + + /* Lock forever */ + for (;;) { + hcd_device_wait(this_device); + USB_MSG("Failed attempt to continue finished thread"); + } +} + + +/*===========================================================================* + * hcd_enumerate * + *===========================================================================*/ +static int +hcd_enumerate(hcd_device_state * this_device) +{ + hcd_driver_state * d; + + DEBUG_DUMP; + + d = this_device->driver; + + /* First let driver reset device */ + d->reset_device(d->private_data); + + /* Set parameters for further communication */ + d->setup_device(d->private_data, HCD_DEFAULT_EP, HCD_DEFAULT_ADDR); + + /* Get device descriptor */ + if (EXIT_SUCCESS != hcd_get_device_descriptor(this_device)) { + USB_MSG("Failed to get device descriptor"); + return EXIT_FAILURE; + } + + /* TODO: dynamic device address when more devices are available */ + + /* Set address */ + if (EXIT_SUCCESS != hcd_set_address(this_device, HCD_ATTACHED_ADDR)) { + USB_MSG("Failed to set device address"); + return EXIT_FAILURE; + } + + /* Set parameters for further communication */ + d->setup_device(d->private_data, HCD_DEFAULT_EP, HCD_ATTACHED_ADDR); + + /* Get other descriptors */ + if (EXIT_SUCCESS != hcd_get_descriptor_tree(this_device)) { + USB_MSG("Failed to get configuration descriptor tree"); + return EXIT_FAILURE; + } + + /* TODO: always first configuration */ + /* Set configuration */ + if (EXIT_SUCCESS != hcd_set_configuration(this_device, 0x01)) { + USB_MSG("Failed to set configuration"); + return EXIT_FAILURE; + } + + USB_DBG("Enumeration completed"); + + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * hcd_get_device_descriptor * + *===========================================================================*/ +static int +hcd_get_device_descriptor(hcd_device_state * this_device) +{ + hcd_ctrlrequest setup; + + DEBUG_DUMP; + + /* TODO: magic numbers, no header for these */ + + /* Format setup packet */ + setup.bRequestType = 0x80; /* IN */ + setup.bRequest = 0x06; /* Get descriptor */ + setup.wValue = 0x0100; /* Device */ + setup.wIndex = 0x0000; + setup.wLength = sizeof(this_device->device_desc); + + /* Handle formatted setup packet */ + if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup)) { + USB_MSG("Handling setup packet failed"); + return EXIT_FAILURE; + } + + /* Put what was read in device descriptor */ + memcpy(&(this_device->device_desc), this_device->buffer, + sizeof(this_device->device_desc)); + + /* Remember max packet size from device descriptor */ + this_device->max_packet_size = this_device->device_desc.bMaxPacketSize; + + /* Output VID/PID when debugging */ + USB_DBG("idVendor: %02X%02X", this_device->device_desc.idVendor[1], + this_device->device_desc.idVendor[0]); + USB_DBG("idProduct: %02X%02X", this_device->device_desc.idProduct[1], + this_device->device_desc.idProduct[0]); + + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * hcd_set_address * + *===========================================================================*/ +static int +hcd_set_address(hcd_device_state * this_device, int address) +{ + hcd_ctrlrequest setup; + + DEBUG_DUMP; + + USB_ASSERT((address > 0) && (address < 128), "Illegal address"); + + /* TODO: magic numbers, no header for these */ + setup.bRequestType = 0x00; /* OUT */ + setup.bRequest = 0x05; /* Set address */ + setup.wValue = address; + setup.wIndex = 0x0000; + setup.wLength = 0x0000; + + /* Handle formatted setup packet */ + if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup)) { + USB_MSG("Handling setup packet failed"); + return EXIT_FAILURE; + } + + { + /* Sleep 5ms for proper addressing */ + struct timespec nanotm = {0, HCD_NANOSLEEP_MSEC(5)}; + nanosleep(&nanotm, NULL); + } + + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * hcd_get_descriptor_tree * + *===========================================================================*/ +static int +hcd_get_descriptor_tree(hcd_device_state * this_device) +{ + hcd_config_descriptor config_descriptor; + hcd_ctrlrequest setup; + int completed; + int total_length; + int buffer_length; + + DEBUG_DUMP; + + /* First, ask only for configuration itself to get length info */ + buffer_length = sizeof(config_descriptor); + completed = 0; + + do { + /* TODO: configuration 0 is hard-coded + * but others are rarely used anyway */ + /* TODO: magic numbers, no header for these */ + setup.bRequestType = 0x80; /* IN */ + setup.bRequest = 0x06; /* Get descriptor */ + setup.wValue = 0x0200; /* Configuration 0 */ + setup.wIndex = 0x0000; + setup.wLength = buffer_length; + + /* Handle formatted setup packet */ + if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup)) { + USB_MSG("Handling setup packet failed"); + return EXIT_FAILURE; + } + + /* If we only asked for configuration itself + * then ask again for other descriptors */ + if (sizeof(config_descriptor) == buffer_length) { + + /* Put what was read in configuration descriptor */ + memcpy(&config_descriptor, this_device->buffer, + sizeof(config_descriptor)); + + /* Continue only if there is more data */ + total_length = config_descriptor.wTotalLength[0] + + (config_descriptor.wTotalLength[1] << 8); + + if (total_length < (int)sizeof(config_descriptor)) { + /* This should never happen for a fine device */ + USB_MSG("Illegal wTotalLength value"); + return EXIT_FAILURE; + } + else if (sizeof(config_descriptor) == total_length) { + /* Nothing more was in descriptor anyway */ + completed = 1; + } + else { + /* Read whatever is needed */ + buffer_length = total_length; + } + + } else { + /* All data for given configuration was read */ + completed = 1; + } + } + while (!completed); + + /* Create tree based on received buffer */ + if (EXIT_SUCCESS != hcd_buffer_to_tree(this_device->buffer, + this_device->data_len, + &(this_device->config_tree))) { + /* This should never happen for a fine device */ + USB_MSG("Illegal descriptor values"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * hcd_set_configuration * + *===========================================================================*/ +static int +hcd_set_configuration(hcd_device_state * this_device, int configuration) +{ + hcd_ctrlrequest setup; + + DEBUG_DUMP; + + /* TODO: magic numbers, no header for these */ + setup.bRequestType = 0x00; /* OUT */ + setup.bRequest = 0x09; /* Set configuration */ + setup.wValue = configuration; + setup.wIndex = 0x0000; + setup.wLength = 0x0000; + + /* Handle formatted setup packet */ + if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup)) { + USB_MSG("Handling setup packet failed"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * hcd_handle_urb * + *===========================================================================*/ +static int +hcd_handle_urb(hcd_device_state * this_device) +{ + DEBUG_DUMP; + + USB_ASSERT(NULL != this_device->urb, "NULL URB received"); + + /* TODO: URB handling will be here */ + + /* TODO: call completion */ + /* hcd_completion_cb */ + + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * hcd_setup_packet * + *===========================================================================*/ +static int +hcd_setup_packet(hcd_device_state * this_device, hcd_ctrlrequest * setup) +{ + hcd_driver_state * d; + hcd_reg1 * current_byte; + int expected_len; + int received_len; + + DEBUG_DUMP; + + /* Initially... */ + d = this_device->driver; + expected_len = (int)setup->wLength; + current_byte = this_device->buffer; + + /* Send setup packet */ + d->setup_stage(d->private_data, setup); + + /* Wait for response */ + hcd_device_wait(this_device); + + /* Check response */ + if (EXIT_SUCCESS != d->check_error(d->private_data)) + return EXIT_FAILURE; + + /* For data packets... */ + if (expected_len > 0) { + + /* TODO: magic number */ + /* ...IN data packets */ + if (setup->bRequestType & 0x80) { + + /* What was received until now */ + this_device->data_len = 0; + + do { + /* Try getting data */ + d->in_data_stage(d->private_data); + + /* Wait for response */ + hcd_device_wait(this_device); + + /* Check response */ + if (EXIT_SUCCESS != d->check_error( + d->private_data)) + return EXIT_FAILURE; + + /* Read data received as response */ + received_len = d->read_data(d->private_data, + current_byte, 0); + + /* Data reading should always yield positive + * results for proper setup packet */ + if (received_len > 0) { + /* Try next packet */ + this_device->data_len += received_len; + current_byte += received_len; + } else + return EXIT_FAILURE; + + } while (expected_len > this_device->data_len); + + /* Should be exactly what we requested, no more */ + if (this_device->data_len != expected_len) { + USB_MSG("Received more data than expected"); + return EXIT_FAILURE; + } + + } else { + /* TODO: unimplemented */ + USB_MSG("Illegal non-zero length OUT setup packet"); + return EXIT_FAILURE; + } + } + + /* Status stages */ + if (setup->bRequestType & 0x80) { + + /* Try confirming data receive */ + d->out_status_stage(d->private_data); + + /* Wait for response */ + hcd_device_wait(this_device); + + /* Check response */ + if (EXIT_SUCCESS != d->check_error(d->private_data)) + return EXIT_FAILURE; + + } else { + + /* Try getting status confirmation */ + d->in_status_stage(d->private_data); + + /* Wait for response */ + hcd_device_wait(this_device); + + /* Check response */ + if (EXIT_SUCCESS != d->check_error(d->private_data)) + return EXIT_FAILURE; + + /* Read zero data from response to clear registers */ + if (0 != d->read_data(d->private_data, NULL, 0)) + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/drivers/usbd/hcd/hcd_common.c b/drivers/usbd/hcd/hcd_common.c new file mode 100755 index 000000000..d93088d64 --- /dev/null +++ b/drivers/usbd/hcd/hcd_common.c @@ -0,0 +1,490 @@ +/* + * Implementation of commonly used procedures for HCD handling/initialization + * If possible, everything OS specific should be here + */ + +#include /* memset... */ + +#include /* Physical to virtual memory mapping */ + +#include /* DDEKit based interrupt handling */ + +#include /* clkconf_* */ +#include /* sys_privctl */ + +#include +#include + + +/*===========================================================================* + * Local prototypes * + *===========================================================================*/ +static int hcd_fill_configuration(hcd_reg1 *, int, hcd_configuration *, int); +static int hcd_fill_interface(hcd_reg1 *, int, hcd_interface *, int); +static int hcd_fill_endpoint(hcd_reg1 *, int, hcd_endpoint *, int); + + +/*===========================================================================* + * hcd_os_interrupt_attach * + *===========================================================================*/ +int +hcd_os_interrupt_attach(int irq, void (*init)(void *), + void (*isr)(void *), void *priv) +{ + DEBUG_DUMP; + + if (NULL == ddekit_interrupt_attach(irq, 0, init, isr, priv)) { + USB_MSG("Attaching interrupt %d failed", irq); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * hcd_os_interrupt_detach * + *===========================================================================*/ +void +hcd_os_interrupt_detach(int irq) +{ + DEBUG_DUMP; + ddekit_interrupt_detach(irq); +} + + +/*===========================================================================* + * hcd_os_interrupt_enable * + *===========================================================================*/ +void +hcd_os_interrupt_enable(int irq) +{ + DEBUG_DUMP; + ddekit_interrupt_enable(irq); +} + + +/*===========================================================================* + * hcd_os_interrupt_disable * + *===========================================================================*/ +void +hcd_os_interrupt_disable(int irq) +{ + DEBUG_DUMP; + ddekit_interrupt_disable(irq); +} + + +/*===========================================================================* + * hcd_os_regs_init * + *===========================================================================*/ +void * +hcd_os_regs_init(unsigned long base_addr, unsigned long addr_len) +{ + /* Memory range where we need privileged access */ + struct minix_mem_range mr; + + /* NULL unless initialization was fully completed */ + void * virt_reg_base; + + DEBUG_DUMP; + + virt_reg_base = NULL; + + /* Must have been set before */ + USB_ASSERT(0 != base_addr, "Invalid base address!"); + USB_ASSERT(0 != addr_len, "Invalid base length!"); + + /* Set memory range for peripheral */ + mr.mr_base = base_addr; + mr.mr_limit = base_addr + addr_len; + + /* Try getting access to memory range */ + if (EXIT_SUCCESS == sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr)) { + + /* And map it where we want it */ + virt_reg_base = vm_map_phys(SELF, (void *)base_addr, addr_len); + + /* Check for mapping errors to allow us returning NULL */ + if (MAP_FAILED == virt_reg_base) { + USB_MSG("Mapping memory with vm_map_phys() failed"); + virt_reg_base = NULL; + } + + } else + USB_MSG("Acquiring memory with sys_privctl() failed"); + + return virt_reg_base; +} + + +/*===========================================================================* + * hcd_os_regs_deinit * + *===========================================================================*/ +int +hcd_os_regs_deinit(unsigned long base_addr, unsigned long addr_len) +{ + DEBUG_DUMP; + + /* To keep USBD return value convention */ + return (0 == vm_unmap_phys(SELF, (void*)base_addr, addr_len)) ? + EXIT_SUCCESS : EXIT_FAILURE; +} + + +/*===========================================================================* + * hcd_os_clkconf * + *===========================================================================*/ +int +hcd_os_clkconf(unsigned long clk, unsigned long mask, unsigned long value) +{ + DEBUG_DUMP; + + /* Apparently clkconf_init may be called more than once anyway */ + if ((0 == clkconf_init()) && (0 == clkconf_set(clk, mask, value))) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + + +/*===========================================================================* + * hcd_os_clkconf_release * + *===========================================================================*/ +int +hcd_os_clkconf_release(void) +{ + DEBUG_DUMP; + return clkconf_release(); +} + + +/*===========================================================================* + * hcd_init_device * + *===========================================================================*/ +int +hcd_connect_device(hcd_device_state * this_device, hcd_thread_function funct) +{ + DEBUG_DUMP; + + if ((NULL != this_device->lock) || (NULL != this_device->thread)) { + USB_MSG("Device data already allocated"); + return EXIT_FAILURE; + } + + if (NULL == (this_device->lock = ddekit_sem_init(0))) + return EXIT_FAILURE; + + if (NULL == (this_device->thread = ddekit_thread_create(funct, + this_device, + "Device"))) { + ddekit_sem_deinit(this_device->lock); + return EXIT_FAILURE; + } + + /* Allow device thread to work */ + ddekit_yield(); + + return EXIT_SUCCESS; +} + +/*===========================================================================* + * hcd_deinit_device * + *===========================================================================*/ +void +hcd_disconnect_device(hcd_device_state * this_device) +{ + DEBUG_DUMP; + + hcd_tree_cleanup(&(this_device->config_tree)); + + /* TODO: spilled resources */ + ddekit_thread_terminate(this_device->thread); + ddekit_sem_deinit(this_device->lock); + + this_device->thread = NULL; + this_device->lock = NULL; +} + + +/*===========================================================================* + * hcd_device_wait * + *===========================================================================*/ +void +hcd_device_wait(hcd_device_state * this_device) +{ + DEBUG_DUMP; + + ddekit_sem_down(this_device->lock); +} + + +/*===========================================================================* + * hcd_device_continue * + *===========================================================================*/ +void +hcd_device_continue(hcd_device_state * this_device) +{ + DEBUG_DUMP; + + ddekit_sem_up(this_device->lock); +} + + +/*===========================================================================* + * hcd_buffer_to_tree * + *===========================================================================*/ +int +hcd_buffer_to_tree(hcd_reg1 * buf, int len, hcd_configuration * c) +{ + hcd_interface * i; + hcd_endpoint * e; + hcd_descriptor * desc; + int cfg_num; + int if_num; + int ep_num; + + DEBUG_DUMP; + + cfg_num = 0; + if_num = 0; + ep_num = 0; + + i = NULL; + e = NULL; + + /* Cleanup initially to NULL pointers before any allocation */ + memset(c, 0, sizeof(*c)); + + while (len > (int)sizeof(*desc)) { + /* Check descriptor type */ + desc = (hcd_descriptor *)buf; + + if (UDESC_CONFIG == desc->bDescriptorType) { + if (EXIT_SUCCESS != hcd_fill_configuration(buf, len, + c, cfg_num++)) + goto PARSE_ERROR; + + if_num = 0; + } + else if (UDESC_INTERFACE == desc->bDescriptorType) { + if (NULL == c->interface) + goto PARSE_ERROR; + + i = &(c->interface[if_num]); + + if (EXIT_SUCCESS != hcd_fill_interface(buf, len, + i, if_num++)) + goto PARSE_ERROR; + + ep_num = 0; + } + else if (UDESC_ENDPOINT == desc->bDescriptorType) { + if (NULL == c->interface) + goto PARSE_ERROR; + + if (NULL == i) + goto PARSE_ERROR; + + e = &(i->endpoint[ep_num]); + + if (EXIT_SUCCESS != hcd_fill_endpoint(buf, len, + e, ep_num++)) + goto PARSE_ERROR; + } else + USB_DBG("Unhandled descriptor type 0x%02X", + desc->bDescriptorType); + + len -= desc->bLength; + buf += desc->bLength; + } + + if (0 != len) { + USB_MSG("After parsing, some descriptor data remains"); + goto PARSE_ERROR; + } + + return EXIT_SUCCESS; + + PARSE_ERROR: + hcd_tree_cleanup(c); + return EXIT_FAILURE; +} + + +/*===========================================================================* + * hcd_tree_cleanup * + *===========================================================================*/ +void +hcd_tree_cleanup(hcd_configuration * c) +{ + int if_idx; + + DEBUG_DUMP; + + /* Free if anything was allocated */ + if (NULL != c->interface) { + + USB_ASSERT(c->num_interfaces > 0, "Interface number error"); + + for (if_idx = 0; if_idx < c->num_interfaces; if_idx++) { + if (NULL != c->interface[if_idx].endpoint) { + USB_DBG("Freeing ep for interface #%d", if_idx); + free(c->interface[if_idx].endpoint); + } + } + + USB_DBG("Freeing interfaces"); + free(c->interface); + c->interface = NULL; + } +} + + +/*===========================================================================* + * hcd_fill_configuration * + *===========================================================================*/ +static int +hcd_fill_configuration(hcd_reg1 * buf, int len, hcd_configuration * c, int num) +{ + hcd_config_descriptor * desc; + int interfaces_size; + + DEBUG_DUMP; + + desc = (hcd_config_descriptor *)buf; + + USB_DBG("Configuration #%d", num); + + if (num > 0) { + USB_DBG("Only one configuration possible"); + return EXIT_SUCCESS; + } + + if (UDESC_CONFIG != desc->bDescriptorType) + return EXIT_FAILURE; + + if (desc->bLength > len) + return EXIT_FAILURE; + + if (sizeof(*desc) != desc->bLength) + return EXIT_FAILURE; + + memcpy(&(c->descriptor), buf, sizeof(c->descriptor)); + + c->num_interfaces = c->descriptor.bNumInterface; + + interfaces_size = c->num_interfaces * sizeof(*(c->interface)); + + USB_DBG("Allocating interfaces, %dB", interfaces_size); + c->interface = malloc(interfaces_size); + + memset(c->interface, 0, interfaces_size); + + /* Dump configuration in debug mode */ + USB_DBG("<>"); + USB_DBG("bLength %02X", desc->bLength); + USB_DBG("bDescriptorType %02X", desc->bDescriptorType); + USB_DBG("wTotalLength %02X%02X", desc->wTotalLength[1], + desc->wTotalLength[0]); + USB_DBG("bNumInterface %02X", desc->bNumInterface); + USB_DBG("bConfigurationValue %02X", desc->bConfigurationValue); + USB_DBG("iConfiguration %02X", desc->iConfiguration); + USB_DBG("bmAttributes %02X", desc->bmAttributes); + USB_DBG("bMaxPower %02X", desc->bMaxPower); + + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * hcd_fill_interface * + *===========================================================================*/ +static int +hcd_fill_interface(hcd_reg1 * buf, int len, hcd_interface * i, int num) +{ + hcd_interface_descriptor * desc; + int endpoints_size; + + DEBUG_DUMP; + + desc = (hcd_interface_descriptor *)buf; + + USB_DBG("Interface #%d", num); + + if (UDESC_INTERFACE != desc->bDescriptorType) + return EXIT_FAILURE; + + if (desc->bLength > len) + return EXIT_FAILURE; + + if (sizeof(*desc) != desc->bLength) + return EXIT_FAILURE; + + if (desc->bInterfaceNumber != num) + return EXIT_FAILURE; + + memcpy(&(i->descriptor), buf, sizeof(i->descriptor)); + + i->num_endpoints = i->descriptor.bNumEndpoints; + + endpoints_size = i->num_endpoints * sizeof(*(i->endpoint)); + + USB_DBG("Allocating endpoints, %dB", endpoints_size); + i->endpoint = malloc(endpoints_size); + + memset(i->endpoint, 0, endpoints_size); + + /* Dump interface in debug mode */ + USB_DBG("<>"); + USB_DBG("bLength %02X", desc->bLength); + USB_DBG("bDescriptorType %02X", desc->bDescriptorType); + USB_DBG("bInterfaceNumber %02X", desc->bInterfaceNumber); + USB_DBG("bAlternateSetting %02X", desc->bAlternateSetting); + USB_DBG("bNumEndpoints %02X", desc->bNumEndpoints); + USB_DBG("bInterfaceClass %02X", desc->bInterfaceClass); + USB_DBG("bInterfaceSubClass %02X", desc->bInterfaceSubClass); + USB_DBG("bInterfaceProtocol %02X", desc->bInterfaceProtocol); + USB_DBG("iInterface %02X", desc->iInterface); + + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * hcd_fill_endpoint * + *===========================================================================*/ +static int +hcd_fill_endpoint(hcd_reg1 * buf, int len, hcd_endpoint * e, int num) +{ + hcd_endpoint_descriptor * desc; + + DEBUG_DUMP; + + desc = (hcd_endpoint_descriptor *)buf; + + USB_DBG("Endpoint #%d", num); + + if (UDESC_ENDPOINT != desc->bDescriptorType) + return EXIT_FAILURE; + + if (desc->bLength > len) + return EXIT_FAILURE; + + if (sizeof(*desc) != desc->bLength) + return EXIT_FAILURE; + + memcpy(&(e->descriptor), buf, sizeof(e->descriptor)); + + /* Dump endpoint in debug mode */ + USB_DBG("<>"); + USB_DBG("bLength %02X", desc->bLength); + USB_DBG("bDescriptorType %02X", desc->bDescriptorType); + USB_DBG("bEndpointAddress %02X", desc->bEndpointAddress); + USB_DBG("bmAttributes %02X", desc->bmAttributes); + USB_DBG("wMaxPacketSize %02X%02X", desc->wMaxPacketSize[1], + desc->wMaxPacketSize[0]); + USB_DBG("bInterval %02X", desc->bInterval); + + return EXIT_SUCCESS; +} diff --git a/drivers/usbd/hcd/hcd_ddekit.c b/drivers/usbd/hcd/hcd_ddekit.c new file mode 100755 index 000000000..c58a7476e --- /dev/null +++ b/drivers/usbd/hcd/hcd_ddekit.c @@ -0,0 +1,277 @@ +/* + * Implementation of DDEkit related calls/data + */ + +#include /* memset */ + +#include + +#include +#include + + +/*===========================================================================* + * Local declarations * + *===========================================================================*/ +/* + * In this file "struct ddekit_usb_dev" equals "hcd_device_state" + * */ +struct ddekit_usb_device_id; +struct ddekit_usb_urb; +struct ddekit_usb_dev; + +/*===========================================================================* + * Global definitions * + *===========================================================================*/ +ddekit_usb_completion_cb completion_cb = NULL; +ddekit_usb_connect_cb connect_cb = NULL; +ddekit_usb_disconnect_cb disconnect_cb = NULL; + + +/*===========================================================================* + * Implementation for usb_server.c * + *===========================================================================*/ + +/*===========================================================================* + * _ddekit_usb_get_manufacturer * + *===========================================================================*/ +char * +_ddekit_usb_get_manufacturer(struct ddekit_usb_dev * ddev) +{ + static const char mfg[] = "UNKNOWN"; + DEBUG_DUMP; + /* TODO: UNUSED won't work */ + ((void)ddev); + return (char *)mfg; +} + + +/*===========================================================================* + * _ddekit_usb_get_product * + *===========================================================================*/ +char * +_ddekit_usb_get_product(struct ddekit_usb_dev * ddev) +{ + static const char prod[] = "UNKNOWN"; + DEBUG_DUMP; + /* TODO: UNUSED won't work */ + ((void)ddev); + return (char *)prod; +} + + +/*===========================================================================* + * _ddekit_usb_get_serial * + *===========================================================================*/ +char * +_ddekit_usb_get_serial(struct ddekit_usb_dev * ddev) +{ + static const char serial[] = "UNKNOWN"; + DEBUG_DUMP; + /* TODO: UNUSED won't work */ + ((void)ddev); + return (char *)serial; +} + + +/*===========================================================================* + * _ddekit_usb_get_device_desc * + *===========================================================================*/ +struct usb_device_descriptor * +_ddekit_usb_get_device_desc(struct ddekit_usb_dev * ddev) +{ + hcd_device_state * dev; + + DEBUG_DUMP; + + dev = (hcd_device_state *)ddev; + + return (struct usb_device_descriptor *) + (&(dev->config_tree.descriptor)); +} + + +/*===========================================================================* + * _ddekit_usb_get_interface_desc * + *===========================================================================*/ +struct usb_interface_descriptor * +_ddekit_usb_get_interface_desc(struct ddekit_usb_dev * ddev, int inum) +{ + hcd_device_state * dev; + + DEBUG_DUMP; + + dev = (hcd_device_state *)ddev; + + return (struct usb_interface_descriptor *) + (&(dev->config_tree.interface[inum].descriptor)); +} + + +/*===========================================================================* + * Implementation for * + *===========================================================================*/ + +/*===========================================================================* + * ddekit_usb_dev_set_data * + *===========================================================================*/ +int +ddekit_usb_dev_set_data(struct ddekit_usb_dev * dev, void * data) +{ + hcd_device_state * hcd_dev; + + DEBUG_DUMP; + + hcd_dev = (hcd_device_state *)dev; + + hcd_dev->data = data; + + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * ddekit_usb_dev_get_data * + *===========================================================================*/ +void * +ddekit_usb_dev_get_data(struct ddekit_usb_dev * dev) +{ + hcd_device_state * hcd_dev; + + DEBUG_DUMP; + + hcd_dev = (hcd_device_state *)dev; + + return hcd_dev->data; +} + + +/* TODO: This was in header file but is not used anywhere */ +#if 0 +/*===========================================================================* + * ddekit_usb_get_device_id * + *===========================================================================*/ +void +ddekit_usb_get_device_id(struct ddekit_usb_dev * dev, + struct ddekit_usb_device_id * id) +{ + DEBUG_DUMP; + /* TODO: UNUSED won't work */ + ((void)dev); + ((void)id); + return; +} +#endif + + +/*===========================================================================* + * ddekit_usb_submit_urb * + *===========================================================================*/ +int +ddekit_usb_submit_urb(struct ddekit_usb_urb * d_urb) +{ + hcd_urb * urb; + hcd_device_state * dev; + + DEBUG_DUMP; + + urb = (hcd_urb *)d_urb; + dev = (hcd_device_state *)(urb->dev); + + /* TODO: queue URB's */ + /* Reassign and go to thread */ + dev->urb = urb; + hcd_device_continue(dev); + dev->urb = NULL; + + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * ddekit_usb_cancle_urb * + *===========================================================================*/ +int +ddekit_usb_cancle_urb(struct ddekit_usb_urb * d_urb) +{ + DEBUG_DUMP; + /* TODO: UNUSED won't work */ + ((void)d_urb); + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * ddekit_usb_init * + *===========================================================================*/ +int +ddekit_usb_init(struct ddekit_usb_driver * drv, + ddekit_usb_malloc_fn * _m, + ddekit_usb_free_fn * _f) +{ + DEBUG_DUMP; + + completion_cb = drv->completion; + connect_cb = drv->connect; + disconnect_cb = drv->disconnect; + + *_m = malloc; + *_f = free; + + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * hcd_connect_cb * + *===========================================================================*/ +void hcd_connect_cb(hcd_device_state * dev) +{ + unsigned int if_bitmask; + + DEBUG_DUMP; + + /* TODO: magic numbers like in ddekit/devman */ + /* Each bit starting from 0, represents valid interface */ + if_bitmask = 0xFFFFFFFF >> (32 - dev->config_tree.num_interfaces); + + USB_DBG("Interfaces %d, mask %08X", + dev->config_tree.num_interfaces, + if_bitmask); + + connect_cb((struct ddekit_usb_dev *)dev, (int)if_bitmask); +} + + +/*===========================================================================* + * hcd_disconnect_cb * + *===========================================================================*/ +void hcd_disconnect_cb(hcd_device_state * dev) +{ + DEBUG_DUMP; + + disconnect_cb((struct ddekit_usb_dev *)dev); +} + + +/*===========================================================================* + * hcd_completion_cb * + *===========================================================================*/ +void hcd_completion_cb(void * priv) +{ + DEBUG_DUMP; + + completion_cb(priv); +} + + +/* TODO: to be removed when this is linkable */ +/*===========================================================================* + * get_bp * + *===========================================================================*/ +unsigned int get_bp(void) +{ + DEBUG_DUMP; + + return EXIT_SUCCESS; +} diff --git a/drivers/usbd/hcd/musb/musb_am335x.c b/drivers/usbd/hcd/musb/musb_am335x.c new file mode 100755 index 000000000..7ea016d6d --- /dev/null +++ b/drivers/usbd/hcd/musb/musb_am335x.c @@ -0,0 +1,718 @@ +/* + * Implementation of whatever is hardware specific in AM335x MCU + */ + +#include /* memset */ +#include /* nanosleep */ + +#include +#include +#include +#include + +#include "musb_core.h" + +/* TODO: BeagleBone white uses USB0 for PC connection as peripheral */ +#undef AM335X_USE_USB0 + + +/*===========================================================================* + * AM335x base register defines * + *===========================================================================*/ +/* Where MUSB core space starts */ +#define AM335X_MUSB_CORE0_BASE_ADDR 0x47401400u +#define AM335X_MUSB_CORE1_BASE_ADDR 0x47401C00u +#define AM335X_MUSB_CORE_BASE_LEN 0x400u + + +/*===========================================================================* + * AM335x USB specific register defines * + *===========================================================================*/ +/* SS registers base address */ +#define AM335X_USBSS_BASE_ADDR 0x47400000u + +#define AM335X_REG_REVREG 0x000u +#define AM335X_REG_SYSCONFIG 0x010u +#define AM335X_REG_IRQSTATRAW 0x024u +#define AM335X_REG_IRQSTAT 0x028u +#define AM335X_REG_IRQENABLER 0x02Cu +#define AM335X_REG_IRQCLEARR 0x030u +#define AM335X_REG_IRQDMATHOLDTX00 0x100u +#define AM335X_REG_IRQDMATHOLDTX01 0x104u +#define AM335X_REG_IRQDMATHOLDTX02 0x108u +#define AM335X_REG_IRQDMATHOLDTX03 0x10Cu +#define AM335X_REG_IRQDMATHOLDRX00 0x110u +#define AM335X_REG_IRQDMATHOLDRX01 0x114u +#define AM335X_REG_IRQDMATHOLDRX02 0x118u +#define AM335X_REG_IRQDMATHOLDRX03 0x11Cu +#define AM335X_REG_IRQDMATHOLDTX10 0x120u +#define AM335X_REG_IRQDMATHOLDTX11 0x124u +#define AM335X_REG_IRQDMATHOLDTX12 0x128u +#define AM335X_REG_IRQDMATHOLDTX13 0x12Cu +#define AM335X_REG_IRQDMATHOLDRX10 0x130u +#define AM335X_REG_IRQDMATHOLDRX11 0x134u +#define AM335X_REG_IRQDMATHOLDRX12 0x138u +#define AM335X_REG_IRQDMATHOLDRX13 0x13Cu +#define AM335X_REG_IRQDMAENABLE0 0x140u +#define AM335X_REG_IRQDMAENABLE1 0x144u +#define AM335X_REG_IRQFRAMETHOLDTX00 0x200u +#define AM335X_REG_IRQFRAMETHOLDTX01 0x204u +#define AM335X_REG_IRQFRAMETHOLDTX02 0x208u +#define AM335X_REG_IRQFRAMETHOLDTX03 0x20Cu +#define AM335X_REG_IRQFRAMETHOLDRX00 0x210u +#define AM335X_REG_IRQFRAMETHOLDRX01 0x214u +#define AM335X_REG_IRQFRAMETHOLDRX02 0x218u +#define AM335X_REG_IRQFRAMETHOLDRX03 0x21Cu +#define AM335X_REG_IRQFRAMETHOLDTX10 0x220u +#define AM335X_REG_IRQFRAMETHOLDTX11 0x224u +#define AM335X_REG_IRQFRAMETHOLDTX12 0x228u +#define AM335X_REG_IRQFRAMETHOLDTX13 0x22Cu +#define AM335X_REG_IRQFRAMETHOLDRX10 0x230u +#define AM335X_REG_IRQFRAMETHOLDRX11 0x234u +#define AM335X_REG_IRQFRAMETHOLDRX12 0x238u +#define AM335X_REG_IRQFRAMETHOLDRX13 0x23Cu +#define AM335X_REG_IRQFRAMEENABLE0 0x240u +#define AM335X_REG_IRQFRAMEENABLE1 0x244u + +/* Length in bytes of SS registers */ +#define AM335X_USBSS_BASE_LEN AM335X_REG_IRQFRAMEENABLE1 + 4u + +/* USBx registers base addresses */ +#define AM335X_USB0_BASE_ADDR 0x47401000u +#define AM335X_USB1_BASE_ADDR 0x47401800u + +#define AM335X_REG_USBXREV 0x00u +#define AM335X_REG_USBXCTRL 0x14u +#define AM335X_REG_USBXSTAT 0x18u +#define AM335X_REG_USBXIRQMSTAT 0x20u +#define AM335X_REG_USBXIRQSTATRAW0 0x28u +#define AM335X_REG_USBXIRQSTATRAW1 0x2Cu +#define AM335X_REG_USBXIRQSTAT0 0x30u +#define AM335X_REG_USBXIRQSTAT1 0x34u +#define AM335X_REG_USBXIRQENABLESET0 0x38u +#define AM335X_REG_USBXIRQENABLESET1 0x3Cu +#define AM335X_REG_USBXIRQENABLECLR0 0x40u +#define AM335X_REG_USBXIRQENABLECLR1 0x44u +#define AM335X_REG_USBXTXMODE 0x70u +#define AM335X_REG_USBXRXMODE 0x74u +#define AM335X_REG_USBXGENRNDISEP1 0x80u +#define AM335X_REG_USBXGENRNDISEP2 0x84u +#define AM335X_REG_USBXGENRNDISEP3 0x88u +#define AM335X_REG_USBXGENRNDISEP4 0x8Cu +#define AM335X_REG_USBXGENRNDISEP5 0x90u +#define AM335X_REG_USBXGENRNDISEP6 0x94u +#define AM335X_REG_USBXGENRNDISEP7 0x98u +#define AM335X_REG_USBXGENRNDISEP8 0x9Cu +#define AM335X_REG_USBXGENRNDISEP9 0xA0u +#define AM335X_REG_USBXGENRNDISEP10 0xA4u +#define AM335X_REG_USBXGENRNDISEP11 0xA8u +#define AM335X_REG_USBXGENRNDISEP12 0xACu +#define AM335X_REG_USBXGENRNDISEP13 0xB0u +#define AM335X_REG_USBXGENRNDISEP14 0xB4u +#define AM335X_REG_USBXGENRNDISEP15 0xB8u +#define AM335X_REG_USBXAUTOREQ 0xD0u +#define AM335X_REG_USBXSRPFIXTIME 0xD4u +#define AM335X_REG_USBX_TDOWN 0xD8u +#define AM335X_REG_USBXUTMI 0xE0u +#define AM335X_REG_USBXMGCUTMILB 0xE4u +#define AM335X_REG_USBXMODE 0xE8u + +/* Values to be set */ +#define AM335X_VAL_USBXCTRL_SOFT_RESET HCD_BIT(0) +#define AM335X_VAL_USBXCTRL_UINT HCD_BIT(3) + +#define AM335X_VAL_USBXMODE_IDDIG_MUX HCD_BIT(7) +#define AM335X_VAL_USBXMODE_IDDIG HCD_BIT(8) + +#define AM335X_VAL_USBXIRQENABLEXXX0_EP0 HCD_BIT(0) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP1 HCD_BIT(1) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP2 HCD_BIT(2) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP3 HCD_BIT(3) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP4 HCD_BIT(4) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP5 HCD_BIT(5) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP6 HCD_BIT(6) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP7 HCD_BIT(7) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP8 HCD_BIT(8) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP9 HCD_BIT(9) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP10 HCD_BIT(10) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP11 HCD_BIT(11) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP12 HCD_BIT(12) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP13 HCD_BIT(13) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP14 HCD_BIT(14) +#define AM335X_VAL_USBXIRQENABLEXXX0_TX_EP15 HCD_BIT(15) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP1 HCD_BIT(17) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP2 HCD_BIT(18) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP3 HCD_BIT(19) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP4 HCD_BIT(20) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP5 HCD_BIT(21) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP6 HCD_BIT(22) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP7 HCD_BIT(23) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP8 HCD_BIT(24) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP9 HCD_BIT(25) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP10 HCD_BIT(26) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP11 HCD_BIT(27) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP12 HCD_BIT(28) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP13 HCD_BIT(29) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP14 HCD_BIT(30) +#define AM335X_VAL_USBXIRQENABLEXXX0_RX_EP15 HCD_BIT(31) + +#define AM335X_VAL_USBXIRQENABLEXXX1_SUSPEND HCD_BIT(0) +#define AM335X_VAL_USBXIRQENABLEXXX1_RESUME HCD_BIT(1) +#define AM335X_VAL_USBXIRQENABLEXXX1_RESET_BABBLE HCD_BIT(2) +#define AM335X_VAL_USBXIRQENABLEXXX1_SOF HCD_BIT(3) +#define AM335X_VAL_USBXIRQENABLEXXX1_CONNECTED HCD_BIT(4) +#define AM335X_VAL_USBXIRQENABLEXXX1_DISCONNECTED HCD_BIT(5) +#define AM335X_VAL_USBXIRQENABLEXXX1_SRP HCD_BIT(6) +#define AM335X_VAL_USBXIRQENABLEXXX1_VBUS HCD_BIT(7) +#define AM335X_VAL_USBXIRQENABLEXXX1_DRVVBUS HCD_BIT(8) +#define AM335X_VAL_USBXIRQENABLEXXX1_GENERIC HCD_BIT(9) + +#define AM335X_VAL_USBXIRQSTAT1_SUSPEND HCD_BIT(0) +#define AM335X_VAL_USBXIRQSTAT1_RESUME HCD_BIT(1) +#define AM335X_VAL_USBXIRQSTAT1_RESET_BABBLE HCD_BIT(2) +#define AM335X_VAL_USBXIRQSTAT1_SOF HCD_BIT(3) +#define AM335X_VAL_USBXIRQSTAT1_CONNECTED HCD_BIT(4) +#define AM335X_VAL_USBXIRQSTAT1_DISCONNECTED HCD_BIT(5) +#define AM335X_VAL_USBXIRQSTAT1_SRP HCD_BIT(6) +#define AM335X_VAL_USBXIRQSTAT1_VBUS HCD_BIT(7) +#define AM335X_VAL_USBXIRQSTAT1_DRVVBUS HCD_BIT(8) +#define AM335X_VAL_USBXIRQSTAT1_GENERIC HCD_BIT(9) + +/* Length in bytes of USBx registers */ +#define AM335X_USBX_BASE_LEN AM335X_REG_USBXMODE + 4u + +/* Helpers for interrupt clearing */ +#define CLEAR_IRQ0(irq0_bit) HCD_WR4(r, AM335X_REG_USBXIRQSTAT0, (irq0_bit)) +#define CLEAR_IRQ1(irq1_bit) HCD_WR4(r, AM335X_REG_USBXIRQSTAT1, (irq1_bit)) + + +/*===========================================================================* + * AM335x clocking register defines * + *===========================================================================*/ +/* Clock module registers offsets */ +#define AM335X_CM_PER_BASE_OFFSET 0x00u +#define AM335X_REG_CM_PER_USB0_CLKCTRL (AM335X_CM_PER_BASE_OFFSET + 28u) + +/* Possible values to be set */ +#define AM335X_VAL_CM_PER_USB0_CLKCTRL_MODULEMODE_ENABLE 0x2u +#define AM335X_CLKCONF_FULL_VAL 0xFFFFFFFFu + + +/*===========================================================================* + * AM335x USB configuration structures * + *===========================================================================*/ +#define AM335X_USBSS_IRQ 17 +#define AM335X_USB0_IRQ 18 +#define AM335X_USB1_IRQ 19 + +/* Hardware configuration values specific to AM335X USBSS (USB Subsystem) */ +typedef struct am335x_ss_config { + + void * regs; /* Points to beginning of memory mapped register space */ +} +am335x_ss_config; + +/* Hardware configuration values specific to AM335X USB(0,1) OTG */ +typedef struct am335x_usbX_config { + + void * regs; /* Points to beginning of memory mapped register space */ +} +am335x_usbX_config; + +/* Private data for AM335X's IRQ thread */ +typedef struct am335x_irq_private { + + int usb_num; /* Number of currently handled controller (0, 1) */ +} +am335x_irq_private; + +/* Single MUSB peripheral information */ +typedef struct am335x_controller { + + am335x_irq_private priv; + am335x_usbX_config usb; + musb_core_config core; + hcd_driver_state driver; +} +am335x_controller; + +#define AM335X_NUM_USB_CONTROLLERS 2 +#define AM335X_USB0 0 +#define AM335X_USB1 1 + +/* Configuration values specific to AM335X... */ +typedef struct am335x_config { + + am335x_ss_config ss; + am335x_controller ctrl[AM335X_NUM_USB_CONTROLLERS]; +} +am335x_config; + +/* ...and their current holder */ +static am335x_config am335x; + + +/*===========================================================================* + * Local declarations * + *===========================================================================*/ +/* Basic functionality */ +static int musb_am335x_internal_init(void); +static void musb_am335x_internal_deinit(void); + +/* Interrupt related */ +static void musb_am335x_irq_init(void *); /* TODO: required by DDEKit */ +static void musb_am335x_usbss_isr(void *); +static void musb_am335x_usbx_isr(void *); + +/* Configuration helpers */ +static void musb_am335x_usb_reset(int); +static void musb_am335x_otg_enable(int); + + +/*===========================================================================* + * musb_am335x_init * + *===========================================================================*/ +int +musb_am335x_init(void) +{ + am335x_controller * ctrl; + + DEBUG_DUMP; + + /* Initial cleanup */ + memset(&am335x, 0, sizeof(am335x)); + + /* These registers are specific to AM335X so they are mapped here */ + /* -------------------------------------------------------------- */ + /* USBSS -------------------------------------------------------- */ + /* -------------------------------------------------------------- */ + + /* Map memory for USBSS */ + am335x.ss.regs = hcd_os_regs_init(AM335X_USBSS_BASE_ADDR, + AM335X_USBSS_BASE_LEN); + + if (NULL == am335x.ss.regs) + return EXIT_FAILURE; + + /* Attach IRQ to number */ + if (EXIT_SUCCESS != hcd_os_interrupt_attach(AM335X_USBSS_IRQ, + musb_am335x_irq_init, + musb_am335x_usbss_isr, + NULL)) + return EXIT_FAILURE; + +#ifdef AM335X_USE_USB0 + /* -------------------------------------------------------------- */ + /* USB0 --------------------------------------------------------- */ + /* -------------------------------------------------------------- */ + { + ctrl = &(am335x.ctrl[AM335X_USB0]); + + /* IRQ thread private data */ + ctrl->priv.usb_num = AM335X_USB0; + + /* MUSB core addresses for later registering */ + ctrl->core.regs = hcd_os_regs_init(AM335X_MUSB_CORE0_BASE_ADDR, + AM335X_MUSB_CORE_BASE_LEN); + + if (NULL == ctrl->core.regs) + return EXIT_FAILURE; + + /* Map AM335X USB0 specific addresses */ + ctrl->usb.regs = hcd_os_regs_init(AM335X_USB0_BASE_ADDR, + AM335X_USBX_BASE_LEN); + + if (NULL == ctrl->usb.regs) + return EXIT_FAILURE; + + /* Attach IRQ to number */ + if (EXIT_SUCCESS != hcd_os_interrupt_attach(AM335X_USB0_IRQ, + musb_am335x_irq_init, + musb_am335x_usbx_isr, + &(ctrl->priv))) + return EXIT_FAILURE; + + /* Initialize HCD driver */ + ctrl->driver.private_data = &(ctrl->core); + ctrl->driver.setup_device = musb_setup_device; + ctrl->driver.reset_device = musb_reset_device; + ctrl->driver.setup_stage = musb_setup_stage; + ctrl->driver.in_data_stage = musb_in_data_stage; + ctrl->driver.out_data_stage = musb_out_data_stage; + ctrl->driver.in_status_stage = musb_in_status_stage; + ctrl->driver.out_status_stage = musb_out_status_stage; + ctrl->driver.read_data = musb_read_data; + ctrl->driver.check_error = musb_check_error; + } +#endif + + /* -------------------------------------------------------------- */ + /* USB1 --------------------------------------------------------- */ + /* -------------------------------------------------------------- */ + { + ctrl = &(am335x.ctrl[AM335X_USB1]); + + /* IRQ thread private data */ + ctrl->priv.usb_num = AM335X_USB1; + + /* MUSB core addresses for later registering */ + ctrl->core.regs = hcd_os_regs_init(AM335X_MUSB_CORE1_BASE_ADDR, + AM335X_MUSB_CORE_BASE_LEN); + + if (NULL == ctrl->core.regs) + return EXIT_FAILURE; + + /* Map AM335X USB1 specific addresses */ + ctrl->usb.regs = hcd_os_regs_init(AM335X_USB1_BASE_ADDR, + AM335X_USBX_BASE_LEN); + + if (NULL == ctrl->usb.regs) + return EXIT_FAILURE; + + /* Attach IRQ to number */ + if (EXIT_SUCCESS != hcd_os_interrupt_attach(AM335X_USB1_IRQ, + musb_am335x_irq_init, + musb_am335x_usbx_isr, + &(ctrl->priv))) + return EXIT_FAILURE; + + /* Initialize HCD driver */ + ctrl->driver.private_data = &(ctrl->core); + ctrl->driver.setup_device = musb_setup_device; + ctrl->driver.reset_device = musb_reset_device; + ctrl->driver.setup_stage = musb_setup_stage; + ctrl->driver.in_data_stage = musb_in_data_stage; + ctrl->driver.out_data_stage = musb_out_data_stage; + ctrl->driver.in_status_stage = musb_in_status_stage; + ctrl->driver.out_status_stage = musb_out_status_stage; + ctrl->driver.read_data = musb_read_data; + ctrl->driver.check_error = musb_check_error; + } + + return musb_am335x_internal_init(); +} + + +/*===========================================================================* + * musb_am335x_deinit * + *===========================================================================*/ +void +musb_am335x_deinit(void) +{ + DEBUG_DUMP; + + musb_am335x_internal_deinit(); + + /* Release maps if anything was assigned */ +#ifdef AM335X_USE_USB0 + if (NULL != am335x.ctrl[AM335X_USB0].usb.regs) + if (EXIT_SUCCESS != hcd_os_regs_deinit(AM335X_USB0_BASE_ADDR, + AM335X_USBX_BASE_LEN)) + USB_MSG("Failed to release USB0 OTG mapping"); + + if (NULL != am335x.ctrl[AM335X_USB0].core.regs) + if (EXIT_SUCCESS != hcd_os_regs_deinit( + AM335X_MUSB_CORE0_BASE_ADDR, + AM335X_MUSB_CORE_BASE_LEN)) + USB_MSG("Failed to release USB0 core mapping"); +#endif + if (NULL != am335x.ctrl[AM335X_USB1].usb.regs) + if (EXIT_SUCCESS != hcd_os_regs_deinit(AM335X_USB1_BASE_ADDR, + AM335X_USBX_BASE_LEN)) + USB_MSG("Failed to release USB1 OTG mapping"); + + if (NULL != am335x.ctrl[AM335X_USB1].core.regs) + if (EXIT_SUCCESS != hcd_os_regs_deinit( + AM335X_MUSB_CORE1_BASE_ADDR, + AM335X_MUSB_CORE_BASE_LEN)) + USB_MSG("Failed to release USB1 core mapping"); + + if (NULL != am335x.ss.regs) + if (EXIT_SUCCESS != hcd_os_regs_deinit(AM335X_USBSS_BASE_ADDR, + AM335X_USBSS_BASE_LEN)) + USB_MSG("Failed to release USBSS mapping"); +} + + +/*===========================================================================* + * musb_am335x_internal_init * + *===========================================================================*/ +static int +musb_am335x_internal_init(void) +{ + DEBUG_DUMP; + + /* Configure clocking */ + if (hcd_os_clkconf(AM335X_REG_CM_PER_USB0_CLKCTRL, + AM335X_VAL_CM_PER_USB0_CLKCTRL_MODULEMODE_ENABLE, + AM335X_CLKCONF_FULL_VAL)) + return EXIT_FAILURE; + + /* TODO: time to stabilize? */ + { + /* Sleep 25ms */ + struct timespec nanotm = {0, HCD_NANOSLEEP_MSEC(25)}; + nanosleep(&nanotm, NULL); + } + + /* Read and dump revision register */ + USB_MSG("Revision (REVREG): %08X", + (unsigned int)HCD_RD4(am335x.ss.regs, AM335X_REG_REVREG)); + + /* Allow OS to handle previously configured USBSS interrupts */ + hcd_os_interrupt_enable(AM335X_USBSS_IRQ); + +#ifdef AM335X_USE_USB0 + /* Reset controllers so we get default register values */ + musb_am335x_usb_reset(AM335X_USB0); + /* Allow OS to handle previously configured USB0 interrupts */ + hcd_os_interrupt_enable(AM335X_USB0_IRQ); + /* Enable whatever necessary for OTG part of controller */ + musb_am335x_otg_enable(AM335X_USB0); + /* Configure control endpoint EP0 */ + musb_ep0_config(&(am335x.ctrl[AM335X_USB0].core)); + /* Start actual MUSB core */ + musb_core_start(&(am335x.ctrl[AM335X_USB0].core)); +#endif + + /* Reset controllers so we get default register values */ + musb_am335x_usb_reset(AM335X_USB1); + /* Allow OS to handle previously configured USB1 interrupts */ + hcd_os_interrupt_enable(AM335X_USB1_IRQ); + /* Enable whatever necessary for OTG part of controller */ + musb_am335x_otg_enable(AM335X_USB1); + /* Configure control endpoint EP0 */ + musb_ep0_config(&(am335x.ctrl[AM335X_USB1].core)); + /* Start actual MUSB core */ + musb_core_start(&(am335x.ctrl[AM335X_USB1].core)); + + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * musb_am335x_internal_deinit * + *===========================================================================*/ +static void +musb_am335x_internal_deinit(void) +{ + DEBUG_DUMP; + + /* Disable all interrupts */ + hcd_os_interrupt_disable(AM335X_USBSS_IRQ); +#ifdef AM335X_USE_USB0 + hcd_os_interrupt_disable(AM335X_USB0_IRQ); +#endif + hcd_os_interrupt_disable(AM335X_USB1_IRQ); + + /* Stop core */ +#ifdef AM335X_USE_USB0 + musb_core_stop(&(am335x.ctrl[AM335X_USB0].core)); +#endif + musb_core_stop(&(am335x.ctrl[AM335X_USB1].core)); + + /* Every clkconf call should have corresponding release */ + hcd_os_clkconf_release(); +} + + +/*===========================================================================* + * musb_am335x_irq_init * + *===========================================================================*/ +static void +musb_am335x_irq_init(void * UNUSED(unused)) +{ + DEBUG_DUMP; +} + + +/*===========================================================================* + * musb_am335x_usbss_isr * + *===========================================================================*/ +static void +musb_am335x_usbss_isr(void * UNUSED(data)) +{ + void * r; + hcd_reg4 irqstat; + + DEBUG_DUMP; + + r = am335x.ss.regs; + + irqstat = HCD_RD4(r, AM335X_REG_IRQSTAT); + + USB_DBG("AM335X_REG_IRQSTAT = %X", (unsigned int)irqstat); + + /* Write to clear interrupt */ + HCD_WR4(r, AM335X_REG_IRQSTAT, irqstat); +} + + +/*===========================================================================* + * musb_am335x_usbx_isr * + *===========================================================================*/ +static void +musb_am335x_usbx_isr(void * data) +{ + void * r; + hcd_driver_state * driver; + hcd_reg4 irqstat0; + hcd_reg4 irqstat1; + int usb_num; + + DEBUG_DUMP; + + /* Prepare locals based on USB controller number for this interrupt */ + usb_num = ((am335x_irq_private*)data)->usb_num; + r = am335x.ctrl[usb_num].usb.regs; + driver = &(am335x.ctrl[usb_num].driver); + + /* Read, handle and clean interrupts */ + irqstat0 = HCD_RD4(r, AM335X_REG_USBXIRQSTAT0); + irqstat1 = HCD_RD4(r, AM335X_REG_USBXIRQSTAT1); + + /* TODO: priority of interrupts */ + if (irqstat1 & AM335X_VAL_USBXIRQSTAT1_DRVVBUS) { + USB_DBG("DRVVBUS level changed"); + CLEAR_IRQ1(AM335X_VAL_USBXIRQSTAT1_DRVVBUS); + return; + } + + if (irqstat1 & AM335X_VAL_USBXIRQENABLEXXX1_CONNECTED) { + USB_DBG("Device connected"); + CLEAR_IRQ1(AM335X_VAL_USBXIRQENABLEXXX1_CONNECTED); + driver->event = HCD_EVENT_CONNECTED; + driver->subevent = HCD_SUBEVENT_NONE; + hcd_handle_event(driver); + return; + } + + if (irqstat1 & AM335X_VAL_USBXIRQENABLEXXX1_DISCONNECTED) { + USB_DBG("Device disconnected"); + CLEAR_IRQ1(AM335X_VAL_USBXIRQENABLEXXX1_DISCONNECTED); + driver->event = HCD_EVENT_DISCONNECTED; + driver->subevent = HCD_SUBEVENT_NONE; + hcd_handle_event(driver); + return; + } + + if (irqstat0 & 0x01) { + USB_DBG("EP0 interrupt"); + CLEAR_IRQ0(0x01); + driver->event = HCD_EVENT_ENDPOINT; + driver->subevent = HCD_SUBEVENT_EP0; + hcd_handle_event(driver); + return; + } + + if (irqstat1 & AM335X_VAL_USBXIRQENABLEXXX1_SUSPEND) { + USB_DBG("Unhandled SUSPEND IRQ"); + CLEAR_IRQ1(AM335X_VAL_USBXIRQENABLEXXX1_SUSPEND); + return; + } + + if (irqstat1 & AM335X_VAL_USBXIRQENABLEXXX1_RESUME) { + USB_DBG("Unhandled RESUME IRQ"); + CLEAR_IRQ1(AM335X_VAL_USBXIRQENABLEXXX1_RESUME); + return; + } + + if (irqstat1 & AM335X_VAL_USBXIRQENABLEXXX1_RESET_BABBLE) { + USB_DBG("Unhandled RESET/BABBLE IRQ"); + CLEAR_IRQ1(AM335X_VAL_USBXIRQENABLEXXX1_RESET_BABBLE); + return; + } + + if (irqstat1 & AM335X_VAL_USBXIRQENABLEXXX1_SOF) { + USB_DBG("Unhandled SOF IRQ"); + CLEAR_IRQ1(AM335X_VAL_USBXIRQENABLEXXX1_SOF); + return; + } + + if (irqstat1 & AM335X_VAL_USBXIRQENABLEXXX1_SRP) { + USB_DBG("Unhandled SRP IRQ"); + CLEAR_IRQ1(AM335X_VAL_USBXIRQENABLEXXX1_SRP); + return; + } + + if (irqstat1 & AM335X_VAL_USBXIRQENABLEXXX1_VBUS) { + USB_DBG("Unhandled VBUS IRQ"); + CLEAR_IRQ1(AM335X_VAL_USBXIRQENABLEXXX1_VBUS); + return; + } + + if (irqstat1 & AM335X_VAL_USBXIRQENABLEXXX1_GENERIC) { + USB_DBG("Unhandled GENERIC IRQ"); + CLEAR_IRQ1(AM335X_VAL_USBXIRQENABLEXXX1_GENERIC); + return; + } + + /* When controller is correctly configured this should never happen: */ + USB_MSG("Illegal value of IRQxSTAT: 0=%X 1=%X", + (unsigned int)irqstat0, (unsigned int)irqstat1); + USB_ASSERT(0, "IRQxSTAT error"); +} + + +/*===========================================================================* + * musb_am335x_usb_reset * + *===========================================================================*/ +static void +musb_am335x_usb_reset(int usb_num) +{ + void * r; + hcd_reg4 ctrl; + + DEBUG_DUMP; + + r = am335x.ctrl[usb_num].usb.regs; + + /* Set SOFT_RESET bit and wait until it is off */ + ctrl = HCD_RD4(r, AM335X_REG_USBXCTRL); + HCD_SET(ctrl, AM335X_VAL_USBXCTRL_SOFT_RESET); + HCD_WR4(r, AM335X_REG_USBXCTRL, ctrl); + while (HCD_RD4(r, AM335X_REG_USBXCTRL) & + AM335X_VAL_USBXCTRL_SOFT_RESET); +} + + +/*===========================================================================* + * musb_am335x_otg_enable * + *===========================================================================*/ +static void +musb_am335x_otg_enable(int usb_num) +{ + void * r; + hcd_reg4 intreg; + hcd_reg4 mode; + + DEBUG_DUMP; + + r = am335x.ctrl[usb_num].usb.regs; + + /* Force host operation */ + mode = HCD_RD4(r, AM335X_REG_USBXMODE); + HCD_SET(mode, AM335X_VAL_USBXMODE_IDDIG_MUX); + HCD_CLR(mode, AM335X_VAL_USBXMODE_IDDIG); + HCD_WR4(r, AM335X_REG_USBXMODE, mode); + + /* Set all important interrupts to be handled */ + intreg = HCD_RD4(r, AM335X_REG_USBXIRQENABLESET1); + HCD_SET(intreg, AM335X_VAL_USBXIRQENABLEXXX1_SUSPEND | + AM335X_VAL_USBXIRQENABLEXXX1_RESUME | + AM335X_VAL_USBXIRQENABLEXXX1_RESET_BABBLE | + /* AM335X_VAL_USBXIRQENABLEXXX1_SOF | */ + AM335X_VAL_USBXIRQENABLEXXX1_CONNECTED | + AM335X_VAL_USBXIRQENABLEXXX1_DISCONNECTED | + AM335X_VAL_USBXIRQENABLEXXX1_SRP | + AM335X_VAL_USBXIRQENABLEXXX1_VBUS | + AM335X_VAL_USBXIRQENABLEXXX1_DRVVBUS | + AM335X_VAL_USBXIRQENABLEXXX1_GENERIC); + + HCD_WR4(r, AM335X_REG_USBXIRQENABLESET1, intreg); + + /* Set endpoint 0 interrupt to be enabled */ + intreg = HCD_RD4(r, AM335X_REG_USBXIRQENABLESET0); + HCD_SET(intreg, AM335X_VAL_USBXIRQENABLEXXX0_EP0); + HCD_WR4(r, AM335X_REG_USBXIRQENABLESET0, intreg); +} diff --git a/drivers/usbd/hcd/musb/musb_core.c b/drivers/usbd/hcd/musb/musb_core.c new file mode 100755 index 000000000..cf2435ad9 --- /dev/null +++ b/drivers/usbd/hcd/musb/musb_core.c @@ -0,0 +1,570 @@ +/* + * Implementation of low level MUSB core logic (variant independent) + */ + +#include /* memcpy */ +#include /* nanosleep */ + +#include +#include +#include + +#include "musb_core.h" +#include "musb_regs.h" + + +/*===========================================================================* + * Local defines * + *===========================================================================*/ +#define HCD_COPYBUF_BYTES 64 /* Stack allocated, must be multiple of 4 */ +#define HCD_COPYBUF_WORDS (HCD_COPYBUF_BYTES/4) + + +/*===========================================================================* + * Local prototypes * + *===========================================================================*/ +static void musb_set_state(musb_core_config *); +static int musb_check_rxpktrdy(void *); +static void musb_in_stage_cleanup(void *); +static void musb_clear_rxpktrdy(void *); +static void musb_clear_statuspkt(void *); +static int musb_get_count(void *); +static void musb_read_fifo(void *, hcd_reg1 *, int, int); + + +/*===========================================================================* + * * + * MUSB core implementation * + * * + *===========================================================================*/ + +/*===========================================================================* + * musb_set_state * + *===========================================================================*/ +static void +musb_set_state(musb_core_config * cfg) +{ + void * r; + hcd_reg1 idx; + + DEBUG_DUMP; + + r = cfg->regs; + + /* Set EP and address to be used in next MUSB command */ + + /* Set EP by selecting INDEX */ + idx = HCD_RD1(r, MUSB_REG_INDEX); + HCD_CLR(idx, 0x0F); + HCD_SET(idx, cfg->ep & 0x0F); + HCD_WR1(r, MUSB_REG_INDEX, idx); + + /* Use device with address 'cfg->addr' */ + HCD_WR2(r, MUSB_REG_RXFUNCADDR, cfg->addr); + HCD_WR2(r, MUSB_REG_TXFUNCADDR, cfg->addr); +} + + +/*===========================================================================* + * musb_check_rxpktrdy * + *===========================================================================*/ +static int +musb_check_rxpktrdy(void * cfg) +{ + void * r; + hcd_reg2 host_csr0; + + DEBUG_DUMP; + + r = ((musb_core_config *)cfg)->regs; + + /* Set EP and device address to be used in this command */ + musb_set_state((musb_core_config *)cfg); + + /* Get control status register for EP 0 */ + host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0); + + /* Check for RXPKTRDY */ + if (host_csr0 & MUSB_VAL_HOST_CSR0_RXPKTRDY) + return EXIT_SUCCESS; + + return EXIT_FAILURE; +} + + +/*===========================================================================* + * musb_in_stage_cleanup * + *===========================================================================*/ +static void +musb_in_stage_cleanup(void * cfg) +{ + DEBUG_DUMP; + + musb_clear_rxpktrdy(cfg); + musb_clear_statuspkt(cfg); +} + + +/*===========================================================================* + * musb_clear_rxpktrdy * + *===========================================================================*/ +static void +musb_clear_rxpktrdy(void * cfg) +{ + void * r; + hcd_reg2 host_csr0; + + DEBUG_DUMP; + + r = ((musb_core_config *)cfg)->regs; + + /* Set EP and device address to be used in this command */ + musb_set_state((musb_core_config *)cfg); + + /* Get control status register for EP 0 */ + host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0); + + /* Clear RXPKTRDY to signal receive completion */ + HCD_CLR(host_csr0, MUSB_VAL_HOST_CSR0_RXPKTRDY); + HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0); +} + + +/*===========================================================================* + * musb_clear_statuspkt * + *===========================================================================*/ +static void +musb_clear_statuspkt(void * cfg) +{ + void * r; + hcd_reg2 host_csr0; + + DEBUG_DUMP; + + r = ((musb_core_config *)cfg)->regs; + + /* Set EP and device address to be used in this command */ + musb_set_state((musb_core_config *)cfg); + + /* Get control status register for EP 0 */ + host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0); + + /* Clear STATUSPKT to signal status packet completion */ + HCD_CLR(host_csr0, MUSB_VAL_HOST_CSR0_STATUSPKT); + HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0); +} + + +/*===========================================================================* + * musb_get_count * + *===========================================================================*/ +static int +musb_get_count(void * cfg) +{ + void * r; + + DEBUG_DUMP; + + r = ((musb_core_config *)cfg)->regs; + + /* Set EP and device address to be used in this command */ + musb_set_state((musb_core_config *)cfg); + + /* Reserved part returns zero so no need to generalize + * this return for MUSB_REG_RXCOUNT */ + return (int)(HCD_RD2(r, MUSB_REG_COUNT0)); +} + + +/*===========================================================================* + * musb_read_fifo * + *===========================================================================*/ +static void +musb_read_fifo(void * cfg, hcd_reg1 * output, int size, int fifo_num) +{ + void * r; + hcd_reg4 * word; + hcd_reg4 copy_buf[HCD_COPYBUF_WORDS]; + hcd_addr fifo_addr; + int limit; + int idx; + + DEBUG_DUMP; + + USB_ASSERT((fifo_num >= 0) && (fifo_num <= 4), "Wrong FIFO number"); + + r = ((musb_core_config *)cfg)->regs; + fifo_addr = MUSB_REG_FIFO0 + (fifo_num * MUSB_REG_FIFO_LEN); + + /* Set EP and device address to be used in this command */ + musb_set_state((musb_core_config *)cfg); + + /* Read full words from MUSB FIFO */ + while (size > 0) { + /* Largest amount of bytes that can be copied at a time */ + limit = (size < HCD_COPYBUF_BYTES) ? size : HCD_COPYBUF_BYTES; + + /* Start copying into that */ + word = copy_buf; + + /* Read words from FIFO into copy_buf */ + for (idx = 0; idx < limit; idx += sizeof(*word)) + *word++ = HCD_RD4(r, fifo_addr); + + /* Copy and shift */ + memcpy(output, copy_buf, limit); + output += limit; + size -= limit; + } +} + + +/*===========================================================================* + * musb_core_start * + *===========================================================================*/ +void +musb_core_start(void * cfg) +{ + void * r; + hcd_reg1 devctl; + + DEBUG_DUMP; + + r = ((musb_core_config *)cfg)->regs; + + /* Enable all interrupts valid for host */ + HCD_WR1(r, MUSB_REG_INTRUSBE, + MUSB_VAL_INTRUSBE_SUSPEND | + MUSB_VAL_INTRUSBE_RESUME | + MUSB_VAL_INTRUSBE_RESET_BABBLE | + /* MUSB_VAL_INTRUSBE_SOF | */ + MUSB_VAL_INTRUSBE_CONN | + MUSB_VAL_INTRUSBE_DISCON | + MUSB_VAL_INTRUSBE_SESSREQ | + MUSB_VAL_INTRUSBE_VBUSERR); + + /* Start session */ + devctl = HCD_RD1(r, MUSB_REG_DEVCTL); + HCD_SET(devctl, MUSB_VAL_DEVCTL_SESSION); + HCD_WR1(r, MUSB_REG_DEVCTL, devctl); +} + + +/*===========================================================================* + * musb_core_stop * + *===========================================================================*/ +void +musb_core_stop(void * cfg) +{ + void * r; + hcd_reg1 devctl; + + DEBUG_DUMP; + + r = ((musb_core_config *)cfg)->regs; + + /* TODO: add hardware interrupt disable */ + + /* Stop session */ + devctl = HCD_RD1(r, MUSB_REG_DEVCTL); + HCD_CLR(devctl, MUSB_VAL_DEVCTL_SESSION); + HCD_WR1(r, MUSB_REG_DEVCTL, devctl); +} + + +/*===========================================================================* + * musb_ep0_config * + *===========================================================================*/ +void +musb_ep0_config(void * cfg) +{ + void * r; + hcd_reg1 host_type0; + hcd_reg2 intrtxe; + + DEBUG_DUMP; + + r = ((musb_core_config *)cfg)->regs; + + /* Set parameters temporarily */ + musb_setup_device((musb_core_config *)cfg, + HCD_DEFAULT_EP, + HCD_DEFAULT_ADDR); + + /* Set EP and device address to be used in this command */ + musb_set_state((musb_core_config *)cfg); + + /* Set high speed for EP0 */ + host_type0 = HCD_RD1(r, MUSB_REG_HOST_TYPE0); + HCD_CLR(host_type0, MUSB_VAL_HOST_TYPE0_MASK); + HCD_SET(host_type0, MUSB_VAL_HOST_TYPE0_HIGH_SPEED); + HCD_WR1(r, MUSB_REG_HOST_TYPE0, host_type0); + + /* Enable EP interrupt */ + intrtxe = HCD_RD2(r, MUSB_REG_INTRTXE); + HCD_SET(intrtxe, MUSB_VAL_INTRTXE_EP0); + HCD_WR2(r, MUSB_REG_INTRTXE, intrtxe); +} + + +/*===========================================================================* + * * + * HCD interface implementation * + * * + *===========================================================================*/ + +/*===========================================================================* + * musb_setup_device * + *===========================================================================*/ +void +musb_setup_device(void * cfg, hcd_reg1 ep, hcd_reg1 addr) +{ + DEBUG_DUMP; + + /* Assign */ + ((musb_core_config *)cfg)->ep = ep; + ((musb_core_config *)cfg)->addr = addr; +} + + +/*===========================================================================* + * musb_reset_device * + *===========================================================================*/ +void +musb_reset_device(void * cfg) +{ + void * r; + hcd_reg1 power; + + DEBUG_DUMP; + + r = ((musb_core_config *)cfg)->regs; + + /* Write reset bit and high speed negotiation wait for at least + * 20ms for reset, clear reset bit and wait for device */ + power = HCD_RD1(r, MUSB_REG_POWER); + HCD_SET(power, MUSB_VAL_POWER_RESET | MUSB_VAL_POWER_HSEN); + HCD_WR1(r, MUSB_REG_POWER, power); + + { + /* Sleep 25ms */ + struct timespec nanotm = {0, HCD_NANOSLEEP_MSEC(25)}; + nanosleep(&nanotm, NULL); + } + + power = HCD_RD1(r, MUSB_REG_POWER); + HCD_CLR(power, MUSB_VAL_POWER_RESET); + HCD_WR1(r, MUSB_REG_POWER, power); + + { + /* Sleep 25ms */ + struct timespec nanotm = {0, HCD_NANOSLEEP_MSEC(25)}; + nanosleep(&nanotm, NULL); + } +} + + +/*===========================================================================* + * musb_setup_stage * + *===========================================================================*/ +void +musb_setup_stage(void * cfg, hcd_ctrlrequest * setup) +{ + void * r; + char * setup_byte; + hcd_reg2 host_csr0; + + DEBUG_DUMP; + + r = ((musb_core_config *)cfg)->regs; + setup_byte = (char*)setup; + + /* Set EP and device address to be used in this command */ + musb_set_state((musb_core_config *)cfg); + + /* TODO: check for ongoing transmission */ + + /* Put USB setup data into corresponding FIFO */ + HCD_WR4(r, MUSB_REG_FIFO0, HCD_8TO32(&setup_byte[0])); + HCD_WR4(r, MUSB_REG_FIFO0, HCD_8TO32(&setup_byte[sizeof(hcd_reg4)])); + + /* Get control status register for EP 0 */ + host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0); + + /* Send actual packet from FIFO */ + HCD_SET(host_csr0, MUSB_VAL_HOST_CSR0_TXPKTRDY | + MUSB_VAL_HOST_CSR0_SETUPPKT); + + HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0); +} + + +/*===========================================================================* + * musb_in_data_stage * + *===========================================================================*/ +void +musb_in_data_stage(void * cfg) +{ + void * r; + hcd_reg2 host_csr0; + + DEBUG_DUMP; + + r = ((musb_core_config *)cfg)->regs; + + /* Set EP and device address to be used in this command */ + musb_set_state((musb_core_config *)cfg); + + /* Get control status register for EP 0 */ + host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0); + + /* Request IN DATA stage */ + HCD_SET(host_csr0, MUSB_VAL_HOST_CSR0_REQPKT); + HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0); +} + + +/*===========================================================================* + * musb_out_data_stage * + *===========================================================================*/ +void +musb_out_data_stage(void * cfg) +{ + DEBUG_DUMP; + + /* Set EP and device address to be used in this command */ + musb_set_state((musb_core_config *)cfg); + + /* TODO: not needed for enumeration but will be needed later */ + ((void)cfg); + USB_MSG("NOT IMPLEMENTED"); +} + + +/*===========================================================================* + * musb_in_status_stage * + *===========================================================================*/ +void +musb_in_status_stage(void * cfg) +{ + void * r; + hcd_reg2 host_csr0; + + DEBUG_DUMP; + + r = ((musb_core_config *)cfg)->regs; + + /* Set EP and device address to be used in this command */ + musb_set_state((musb_core_config *)cfg); + + /* Get control status register for EP 0 */ + host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0); + + /* Request IN STATUS stage */ + HCD_SET(host_csr0, MUSB_VAL_HOST_CSR0_REQPKT | + MUSB_VAL_HOST_CSR0_STATUSPKT); + + HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0); +} + + +/*===========================================================================* + * musb_out_status_stage * + *===========================================================================*/ +void +musb_out_status_stage(void * cfg) +{ + void * r; + hcd_reg2 host_csr0; + + DEBUG_DUMP; + + r = ((musb_core_config *)cfg)->regs; + + /* Set EP and device address to be used in this command */ + musb_set_state((musb_core_config *)cfg); + + /* Get control status register for EP 0 */ + host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0); + + /* Request OUT STATUS stage */ + HCD_SET(host_csr0, MUSB_VAL_HOST_CSR0_TXPKTRDY | + MUSB_VAL_HOST_CSR0_STATUSPKT); + + HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0); +} + + +/*===========================================================================* + * musb_read_data * + *===========================================================================*/ +int +musb_read_data(void * cfg, hcd_reg1 * buffer, int buffer_num) +{ + int count0; + + DEBUG_DUMP; + + /* Check if anything received at all */ + if (EXIT_SUCCESS != musb_check_rxpktrdy(cfg)) { + USB_MSG("RXPKTRDY not set when receiving"); + return HCD_READ_ERR; + } + + /* Number of bytes received at EP0 */ + count0 = musb_get_count(cfg); + + /* Read from given FIFO */ + if ((NULL != buffer) && (count0 > 0)) + musb_read_fifo(cfg, buffer, count0, buffer_num); + + /* Cleanup after reading */ + musb_in_stage_cleanup(cfg); + + return count0; +} + + +/*===========================================================================* + * musb_check_error * + *===========================================================================*/ +int +musb_check_error(void * cfg) +{ + void * r; + hcd_reg2 host_csr0; + + DEBUG_DUMP; + + r = ((musb_core_config *)cfg)->regs; + + /* Set EP and device address to be used in this command */ + musb_set_state((musb_core_config *)cfg); + + /* Get control status register for EP 0 */ + host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0); + + /* Check for common errors */ + if (host_csr0 & MUSB_VAL_HOST_CSR0_ERROR) { + USB_MSG("HOST_CSR0 ERROR: %04X", host_csr0); + HCD_CLR(host_csr0, MUSB_VAL_HOST_CSR0_ERROR); + HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0); + return EXIT_FAILURE; + } + + if (host_csr0 & MUSB_VAL_HOST_CSR0_RXSTALL) { + USB_MSG("HOST_CSR0 STALL: %04X", host_csr0); + HCD_CLR(host_csr0, MUSB_VAL_HOST_CSR0_RXSTALL); + HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0); + return EXIT_FAILURE; + } + + if (host_csr0 & MUSB_VAL_HOST_CSR0_NAK_TIMEOUT) { + USB_MSG("HOST_CSR0 NAK_TIMEOUT: %04X", host_csr0); + HCD_CLR(host_csr0, MUSB_VAL_HOST_CSR0_NAK_TIMEOUT); + HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/drivers/usbd/hcd/musb/musb_core.h b/drivers/usbd/hcd/musb/musb_core.h new file mode 100755 index 000000000..c85aa1848 --- /dev/null +++ b/drivers/usbd/hcd/musb/musb_core.h @@ -0,0 +1,47 @@ +/* + * Interface of low level MUSB core logic (variant independent) + */ + +#ifndef _MUSB_CORE_H_ +#define _MUSB_CORE_H_ + +#include + + +/*===========================================================================* + * Types and constants * + *===========================================================================*/ +/* Structure to hold Mentor USB core configuration + * May be more than one on a single chip + * Should be initialized by MUSB's variant specific code (like AM335x) */ +typedef struct { + + void * regs; /* Points to beginning of memory mapped registers */ + hcd_reg1 ep; /* Currently used endpoint */ + hcd_reg1 addr; /* Currently used address */ +} +musb_core_config; + + +/*===========================================================================* + * Function prototypes * + *===========================================================================*/ +/* Only to be used outside generic HCD code */ +void musb_core_start(void *); +void musb_core_stop(void *); +void musb_ep0_config(void *); + + +/* For HCD interface */ +void musb_setup_device(void *, hcd_reg1, hcd_reg1); +void musb_reset_device(void *); +void musb_setup_stage(void *, hcd_ctrlrequest *); +void musb_in_data_stage(void *); +void musb_out_data_stage(void *); +void musb_in_status_stage(void *); +void musb_out_status_stage(void *); +int musb_read_data(void *, hcd_reg1 *, int); +int musb_check_error(void *); + + +#endif /* !_MUSB_CORE_H_ */ diff --git a/drivers/usbd/hcd/musb/musb_regs.h b/drivers/usbd/hcd/musb/musb_regs.h new file mode 100755 index 000000000..15fd554d9 --- /dev/null +++ b/drivers/usbd/hcd/musb/musb_regs.h @@ -0,0 +1,126 @@ +/* + * Common MUSB core registers definitions + */ + +#ifndef _MUSB_REGS_H_ +#define _MUSB_REGS_H_ + +#include + + +/*===========================================================================* + * MUSB core register offsets * + *===========================================================================*/ +#define MUSB_REG_FADDR 0x00u +#define MUSB_REG_POWER 0x01u +#define MUSB_REG_INTRTX 0x02u +#define MUSB_REG_INTRRX 0x04u +#define MUSB_REG_INTRTXE 0x06u +#define MUSB_REG_INTRRXE 0x08u +#define MUSB_REG_INTRUSB 0x0Au +#define MUSB_REG_INTRUSBE 0x0Bu +#define MUSB_REG_FRAME 0x0Cu +#define MUSB_REG_INDEX 0x0Eu +#define MUSB_REG_TESTMODE 0x0Fu + +/* Proxy registers for endpoint configuration, + * that correspond to specific endpoint's register space + * selected with MUSB_REG_INDEX */ +#define MUSB_REG_TXMAXP 0x10u +#define MUSB_REG_PERI_CSR0 0x12u +#define MUSB_REG_HOST_CSR0 MUSB_REG_PERI_CSR0 +#define MUSB_REG_PERI_TXCSR MUSB_REG_PERI_CSR0 +#define MUSB_REG_HOST_TXCSR MUSB_REG_PERI_CSR0 +#define MUSB_REG_RXMAXP 0x14u +#define MUSB_REG_PERI_RXCSR 0x16u +#define MUSB_REG_HOST_RXCSR MUSB_PERI_RXCSR +#define MUSB_REG_COUNT0 0x18u +#define MUSB_REG_RXCOUNT MUSB_COUNT0 +#define MUSB_REG_HOST_TYPE0 0x1Au +#define MUSB_REG_HOST_TXTYPE MUSB_HOST_TYPE0 +#define MUSB_REG_HOST_NAKLIMIT0 0x1Bu +#define MUSB_REG_HOST_TXINTERVAL MUSB_HOST_NAKLIMIT0 +#define MUSB_REG_HOST_RXTYPE 0x1Cu +#define MUSB_REG_HOST_RXINTERVAL 0x1Du +#define MUSB_REG_CONFIGDATA 0x1Fu + +#define MUSB_REG_FIFO0 0x20u +#define MUSB_REG_FIFO1 0x24u +#define MUSB_REG_FIFO2 0x28u +#define MUSB_REG_FIFO3 0x2Cu +#define MUSB_REG_FIFO4 0x30u +#define MUSB_REG_FIFO_LEN 0x04u + +#define MUSB_REG_DEVCTL 0x60u +#define MUSB_REG_TXFIFOSZ 0x62u +#define MUSB_REG_RXFIFOSZ 0x63u +#define MUSB_REG_TXFIFOADDR 0x64u +#define MUSB_REG_RXFIFOADDR 0x66u + +#define MUSB_REG_TXFUNCADDR 0x80u +#define MUSB_REG_TXHUBADDR 0x82u +#define MUSB_REG_TXHUBPORT 0x83u +#define MUSB_REG_RXFUNCADDR 0x84u +#define MUSB_REG_RXHUBADDR 0x86u +#define MUSB_REG_RXHUBPORT 0x87u + + +/*===========================================================================* + * MUSB core register values * + *===========================================================================*/ +/* POWER */ +#define MUSB_VAL_POWER_ENSUSPM HCD_BIT(0) +#define MUSB_VAL_POWER_SUSPENDM HCD_BIT(1) +#define MUSB_VAL_POWER_RESUME HCD_BIT(2) +#define MUSB_VAL_POWER_RESET HCD_BIT(3) +#define MUSB_VAL_POWER_HSMODE HCD_BIT(4) +#define MUSB_VAL_POWER_HSEN HCD_BIT(5) +#define MUSB_VAL_POWER_SOFTCONN HCD_BIT(6) +#define MUSB_VAL_POWER_ISOUPDATE HCD_BIT(7) + +/* DEVCTL */ +#define MUSB_VAL_DEVCTL_SESSION HCD_BIT(0) +#define MUSB_VAL_DEVCTL_HOSTREQ HCD_BIT(1) +#define MUSB_VAL_DEVCTL_HOSTMODE HCD_BIT(2) +#define MUSB_VAL_DEVCTL_VBUS_1 HCD_BIT(3) +#define MUSB_VAL_DEVCTL_VBUS_2 HCD_BIT(4) +#define MUSB_VAL_DEVCTL_VBUS_3 (HCD_BIT(3) | HCD_BIT(4)) +#define MUSB_VAL_DEVCTL_LSDEV HCD_BIT(5) +#define MUSB_VAL_DEVCTL_FSDEV HCD_BIT(6) +#define MUSB_VAL_DEVCTL_BDEVICE HCD_BIT(7) + +/* INTRUSBE */ +#define MUSB_VAL_INTRUSBE_SUSPEND HCD_BIT(0) +#define MUSB_VAL_INTRUSBE_RESUME HCD_BIT(1) +#define MUSB_VAL_INTRUSBE_RESET_BABBLE HCD_BIT(2) +#define MUSB_VAL_INTRUSBE_SOF HCD_BIT(3) +#define MUSB_VAL_INTRUSBE_CONN HCD_BIT(4) +#define MUSB_VAL_INTRUSBE_DISCON HCD_BIT(5) +#define MUSB_VAL_INTRUSBE_SESSREQ HCD_BIT(6) +#define MUSB_VAL_INTRUSBE_VBUSERR HCD_BIT(7) + +/* HOST_TYPE0 */ +#define MUSB_VAL_HOST_TYPE0_MASK (HCD_BIT(6) | HCD_BIT(7)) +#define MUSB_VAL_HOST_TYPE0_HIGH_SPEED HCD_BIT(6) +#define MUSB_VAL_HOST_TYPE0_FULL_SPEED HCD_BIT(7) +#define MUSB_VAL_HOST_TYPE0_LOW_SPEED (HCD_BIT(6) | HCD_BIT(7)) + +/* INTRTXE */ +#define MUSB_VAL_INTRTXE_EP0 HCD_BIT(0) +#define MUSB_VAL_INTRTXE_EP1TX HCD_BIT(1) +#define MUSB_VAL_INTRTXE_EP2TX HCD_BIT(2) +#define MUSB_VAL_INTRTXE_EP3TX HCD_BIT(3) +#define MUSB_VAL_INTRTXE_EP4TX HCD_BIT(4) + +/* HOST_CSR0 */ +#define MUSB_VAL_HOST_CSR0_RXPKTRDY HCD_BIT(0) +#define MUSB_VAL_HOST_CSR0_TXPKTRDY HCD_BIT(1) +#define MUSB_VAL_HOST_CSR0_RXSTALL HCD_BIT(2) +#define MUSB_VAL_HOST_CSR0_SETUPPKT HCD_BIT(3) +#define MUSB_VAL_HOST_CSR0_ERROR HCD_BIT(4) +#define MUSB_VAL_HOST_CSR0_REQPKT HCD_BIT(5) +#define MUSB_VAL_HOST_CSR0_STATUSPKT HCD_BIT(6) +#define MUSB_VAL_HOST_CSR0_NAK_TIMEOUT HCD_BIT(7) +#define MUSB_VAL_HOST_CSR0_FLUSHFIFO HCD_BIT(8) + +#endif /* !_MUSB_REGS_H_ */ diff --git a/drivers/usbd/include/usb/hcd_common.h b/drivers/usbd/include/usb/hcd_common.h new file mode 100755 index 000000000..0bb029970 --- /dev/null +++ b/drivers/usbd/include/usb/hcd_common.h @@ -0,0 +1,214 @@ +/* + * Contains commonly used types and procedures, for HCD handling/initialization + * If possible, everything OS specific (IPC, virtual memory...) should be here + */ + +#ifndef _HCD_COMMON_H_ +#define _HCD_COMMON_H_ + +#include +#include +#include + +#include /* for setup structures */ +#include /* for descriptor structures */ + + +/*===========================================================================* + * USB register handling defines * + *===========================================================================*/ +/* Helper type used for register bitwise access */ +#define HCD_BIT(num) (0x01u << (num)) + +/* Unsigned type that can hold all possible addresses */ +typedef unsigned long hcd_addr; + +/* Register types */ +typedef unsigned long hcd_reg4; +typedef unsigned short hcd_reg2; +typedef unsigned char hcd_reg1; + +/* For register dereferencing */ +#define _HCD_REG4 hcd_reg4 * volatile +#define _HCD_REG2 hcd_reg2 * volatile +#define _HCD_REG1 hcd_reg1 * volatile + +/* Scalar address to dereference */ +#define _HCD_ADDR(base, off) (((hcd_addr)(base))+(off)) + +/* Defines for fixed size register access + * May cause unaligned memory access */ +#define HCD_WR4(base, off, val) (*((_HCD_REG4)_HCD_ADDR(base, off)) = (val)) +#define HCD_WR2(base, off, val) (*((_HCD_REG2)_HCD_ADDR(base, off)) = (val)) +#define HCD_WR1(base, off, val) (*((_HCD_REG1)_HCD_ADDR(base, off)) = (val)) +#define HCD_RD4(base, off) (*((_HCD_REG4)_HCD_ADDR(base, off))) +#define HCD_RD2(base, off) (*((_HCD_REG2)_HCD_ADDR(base, off))) +#define HCD_RD1(base, off) (*((_HCD_REG1)_HCD_ADDR(base, off))) + +/* Other useful defines */ +#define HCD_SET(val, bits) ((val)|=(bits)) +#define HCD_CLR(val, bits) ((val)&=~(bits)) + +/* Alignment safe conversion from 'bytes' array to a word */ +#define HCD_8TO32(bytes) (((bytes)[0]) | \ + (((bytes)[1])<<8) | \ + (((bytes)[2])<<16) | \ + (((bytes)[3])<<24)) + +/* Convert type's 'sizeof' to 4-byte words count */ +#define HCD_SIZEOF_TO_4(type) ((sizeof(type)+3)/4) + + +/*===========================================================================* + * USB descriptor types * + *===========================================================================*/ +typedef struct usb_ctrlrequest hcd_ctrlrequest; +typedef usb_descriptor_t hcd_descriptor; +typedef usb_device_descriptor_t hcd_device_descriptor; +typedef usb_config_descriptor_t hcd_config_descriptor; +typedef usb_interface_descriptor_t hcd_interface_descriptor; +typedef usb_endpoint_descriptor_t hcd_endpoint_descriptor; +typedef usb_string_descriptor_t hcd_string_descriptor; + + +/*===========================================================================* + * HCD descriptor tree types * + *===========================================================================*/ +typedef struct hcd_endpoint { + + hcd_endpoint_descriptor descriptor; +} +hcd_endpoint; + +typedef struct hcd_interface { + + hcd_interface_descriptor descriptor; + hcd_endpoint * endpoint; + int num_endpoints; +} +hcd_interface; + +typedef struct hcd_configuration { + + hcd_config_descriptor descriptor; + hcd_interface * interface; + int num_interfaces; +} +hcd_configuration; + + +/*===========================================================================* + * HCD device helper types * + *===========================================================================*/ +typedef void (*hcd_thread_function)(void *); +typedef ddekit_thread_t hcd_thread; +typedef ddekit_sem_t hcd_lock; +typedef struct hcd_driver_state hcd_driver_state; +typedef struct ddekit_usb_urb hcd_urb; + +typedef enum { + + HCD_STATE_DISCONNECTED = 0, /* default for initialization */ + HCD_STATE_CONNECTION_PENDING, + HCD_STATE_CONNECTED +} +hcd_state; + +/* Largest value that can be transfered by this driver at a time + * see MAXPAYLOAD in TXMAXP/RXMAXP */ +#define MAX_WTOTALLENGTH 1024 + +typedef struct hcd_device_state { + + hcd_driver_state * driver; + hcd_thread * thread; + hcd_lock * lock; + hcd_urb * urb; + void * data; + + hcd_device_descriptor device_desc; + hcd_configuration config_tree; + hcd_reg1 max_packet_size; + + hcd_state state; + + /* Number of bytes received/transmitted in last transfer */ + int data_len; + /* TODO: forcefully align buffer to make things clear? */ + /* Buffer for each device to hold transfered data */ + hcd_reg1 buffer[MAX_WTOTALLENGTH]; +} +hcd_device_state; + + +/*===========================================================================* + * Other definitions * + *===========================================================================*/ +#define HCD_NANOSLEEP_SEC(sec) ((sec) * 1000000000) +#define HCD_NANOSLEEP_MSEC(msec) ((msec) * 1000000) +#define HCD_NANOSLEEP_USEC(usec) ((usec) * 1000) + +/* Default USB communication parameters */ +#define HCD_DEFAULT_EP 0x00 +#define HCD_DEFAULT_ADDR 0x00 + +/* TODO: one device */ +#define HCD_ATTACHED_ADDR 0x01 + + +/*===========================================================================* + * Operating system specific * + *===========================================================================*/ +/* Generic method for registering interrupts */ +int hcd_os_interrupt_attach(int irq, void (*init)(void *), + void (*isr)(void *), void *priv); + +/* Generic method for unregistering interrupts */ +void hcd_os_interrupt_detach(int); + +/* Generic method for enabling interrupts */ +void hcd_os_interrupt_enable(int); + +/* Generic method for disabling interrupts */ +void hcd_os_interrupt_disable(int); + +/* Returns pointer to memory mapped for given arguments */ +void * hcd_os_regs_init(unsigned long, unsigned long); + +/* Unregisters mapped memory */ +int hcd_os_regs_deinit(unsigned long, unsigned long); + +/* Configure clocking */ +int hcd_os_clkconf(unsigned long, unsigned long, unsigned long); + +/* Release clocking */ +int hcd_os_clkconf_release(void); + + +/*===========================================================================* + * Device handling calls * + *===========================================================================*/ +/* Initializes device threading on connection */ +int hcd_connect_device(hcd_device_state *, hcd_thread_function); + +/* Cleans after device disconnection */ +void hcd_disconnect_device(hcd_device_state *); + +/* Locks device thread until 'hcd_device_continue' */ +void hcd_device_wait(hcd_device_state *); + +/* Unlocks device thread halted by 'hcd_device_wait' */ +void hcd_device_continue(hcd_device_state *); + + +/*===========================================================================* + * Descriptor tree calls * + *===========================================================================*/ +/* Creates descriptor tree based on given buffer */ +int hcd_buffer_to_tree(hcd_reg1 *, int, hcd_configuration *); + +/* Frees descriptor tree */ +void hcd_tree_cleanup(hcd_configuration *); + + +#endif /* !_HCD_COMMON_H_ */ diff --git a/drivers/usbd/include/usb/hcd_ddekit.h b/drivers/usbd/include/usb/hcd_ddekit.h new file mode 100755 index 000000000..67cb3254e --- /dev/null +++ b/drivers/usbd/include/usb/hcd_ddekit.h @@ -0,0 +1,18 @@ +/* + * Whatever must be known to DDEkit callers + */ + +#ifndef _HCD_DDEKIT_H_ +#define _HCD_DDEKIT_H_ + +#include + +/*===========================================================================* + * External declarations * + *===========================================================================*/ +void hcd_connect_cb(hcd_device_state *); +void hcd_disconnect_cb(hcd_device_state *); +void hcd_completion_cb(void *); + + +#endif /* !_HCD_DDEKIT_H_ */ diff --git a/drivers/usbd/include/usb/hcd_interface.h b/drivers/usbd/include/usb/hcd_interface.h new file mode 100755 index 000000000..20ee3babe --- /dev/null +++ b/drivers/usbd/include/usb/hcd_interface.h @@ -0,0 +1,73 @@ +/* + * Interface for HCD + * + * This file holds prototypes that must be implemented by HCD + * and event call that should be called when interrupt occurred + */ + +#ifndef _HCD_INTERFACE_H_ +#define _HCD_INTERFACE_H_ + +#include + + +/*===========================================================================* + * HCD event handling types * + *===========================================================================*/ +/* Possible HCD events */ +typedef enum { + + HCD_EVENT_CONNECTED, + HCD_EVENT_DISCONNECTED, + HCD_EVENT_ENDPOINT +} +hcd_event; + +/* Possible HCD sub-events */ +typedef enum { + + HCD_SUBEVENT_NONE, + HCD_SUBEVENT_EP0, +} +hcd_subevent; + + +/*===========================================================================* + * HCD additional defines * + *===========================================================================*/ +#define HCD_READ_ERR -1 + + +/*===========================================================================* + * HCD driver structure to be filled + *===========================================================================*/ +struct hcd_driver_state { + /* Standard USB controller procedures */ + void (*setup_device) (void *, hcd_reg1, hcd_reg1); + void (*reset_device) (void *); + void (*setup_stage) (void *, hcd_ctrlrequest *); + void (*in_data_stage) (void *); + void (*out_data_stage) (void *); + void (*in_status_stage) (void *); + void (*out_status_stage) (void *); + int (*read_data) (void *, hcd_reg1 *, int); + int (*check_error) (void *); + + /* Controller's private data (like mapped registers) */ + void * private_data; + + /* Current state to be handled by driver */ + hcd_event event; + hcd_subevent subevent; +}; + + +/*===========================================================================* + * HCD event handling routine * + *===========================================================================*/ +/* Handle asynchronous event + * This must be called in case of specific HCD interrupts listed above */ +void hcd_handle_event(hcd_driver_state *); + + +#endif /* !_HCD_INTERFACE_H_ */ diff --git a/drivers/usbd/include/usb/hcd_platforms.h b/drivers/usbd/include/usb/hcd_platforms.h new file mode 100755 index 000000000..38bc3c176 --- /dev/null +++ b/drivers/usbd/include/usb/hcd_platforms.h @@ -0,0 +1,20 @@ +/* + * Externally visible interface for possible USB controllers + */ + +#ifndef _HCD_PLATFORMS_H_ +#define _HCD_PLATFORMS_H_ + + +/*===========================================================================* + * MUSB * + *===========================================================================*/ +/* ----- AM335X ----- */ +int musb_am335x_init(void); +void musb_am335x_deinit(void); +/* ----- AM/DM37X ----- */ +int musb_dm37x_init(void); +void musb_dm37x_deinit(void); + + +#endif /* !_HCD_PLATFORMS_H_ */ diff --git a/drivers/usbd/include/usb/usb_common.h b/drivers/usbd/include/usb/usb_common.h new file mode 100755 index 000000000..f4f82f2c5 --- /dev/null +++ b/drivers/usbd/include/usb/usb_common.h @@ -0,0 +1,65 @@ +/* + * Whatever is commonly used throughout USB code + */ + +#ifndef _USB_COMMON_H_ +#define _USB_COMMON_H_ + +/* For commonly used: NULL, EXIT_*, and stuff like that */ +#include + +/* Current printf implementation for dumping important messages */ +#include + +#if 1 +/* TODO: should be elsewhere */ +#define DEBUG +#endif + + +/*===========================================================================* + * Standard output message * + *===========================================================================*/ +#define USB_MSG(fmt, ...) \ + do { \ + printf("USBD: "); \ + printf(fmt, ##__VA_ARGS__); \ + printf("\n"); \ + } while(0) + + +/*===========================================================================* + * Debug helpers * + *===========================================================================*/ +#ifdef DEBUG +#define DEBUG_DUMP \ + do { \ + printf("USBD (DEBUG %s)\n", __func__); \ + } while(0) + +#define USB_DBG(fmt, ...) \ + do { \ + printf("USBD (DEBUG %s): ", __func__); \ + printf(fmt, ##__VA_ARGS__); \ + printf("\n"); \ + } while(0) + +#else +#define DEBUG_DUMP +#define USB_DBG(fmt, ...) +#endif + + +/*===========================================================================* + * Assert for USB code * + *===========================================================================*/ +#define USB_ASSERT(cond, otherwise) \ + do { \ + if(!(cond)) { \ + USB_MSG("ERROR - "otherwise); \ + exit(EXIT_FAILURE); \ + } \ + } while(0) + + +#endif /* !_USB_COMMON_H_ */ diff --git a/drivers/usbd/include/usb/usbd_interface.h b/drivers/usbd/include/usb/usbd_interface.h new file mode 100755 index 000000000..fbf192cc7 --- /dev/null +++ b/drivers/usbd/include/usb/usbd_interface.h @@ -0,0 +1,24 @@ +/* + * Interface for USBD + * + * This file holds prototypes that must be implemented by platform + * specific USBD + * + * Must be implemented once per USBD but may be used for multiple + * controllers at a time when platform has more than one HCD + */ + +#ifndef _USBD_INTERFACE_H_ +#define _USBD_INTERFACE_H_ + +/*===========================================================================* + * Prototypes to be implemented * + *===========================================================================*/ +/* Must set up HCDs in general and interrupts to + * be handled by DDEkit in particular */ +int usbd_init_hcd(void); + +/* Should clean whatever usbd_init_hcd used */ +void usbd_deinit_hcd(void); + +#endif /* !_USBD_INTERFACE_H_ */ diff --git a/drivers/usbd/usbd.conf b/drivers/usbd/usbd.conf new file mode 100755 index 000000000..0ecb10f96 --- /dev/null +++ b/drivers/usbd/usbd.conf @@ -0,0 +1,14 @@ +service usbd +{ + system + PRIVCTL + UMAP + IRQCTL + ; + irq + 92 #BeagleXM + 17 #BeagleBoneWhite + 18 #BeagleBoneWhite + 19 #BeagleBoneWhite + ; +}; -- 2.44.0