From cccbcdee0de2c79e43d513ccc87d7b573bdfc02a Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Wed, 7 Feb 2018 16:43:26 -0600 Subject: [PATCH] Add USBPhy for the LPC17xx Move the LPC17xx USB driver files from mbed-os\features\unsupported\USBDevice\targets\TARGET_NXP and update them to match the new USBPhy API. --- .../usb/USBEndpoints_LPC17_LPC23.h | 97 +++ .../TARGET_LPC176X/usb/USBHAL_LPC17.cpp | 709 ++++++++++++++++++ .../TARGET_NXP/TARGET_LPC176X/usb/USBPhyHw.h | 66 ++ targets/targets.json | 2 +- 4 files changed, 873 insertions(+), 1 deletion(-) create mode 100644 targets/TARGET_NXP/TARGET_LPC176X/usb/USBEndpoints_LPC17_LPC23.h create mode 100644 targets/TARGET_NXP/TARGET_LPC176X/usb/USBHAL_LPC17.cpp create mode 100644 targets/TARGET_NXP/TARGET_LPC176X/usb/USBPhyHw.h diff --git a/targets/TARGET_NXP/TARGET_LPC176X/usb/USBEndpoints_LPC17_LPC23.h b/targets/TARGET_NXP/TARGET_LPC176X/usb/USBEndpoints_LPC17_LPC23.h new file mode 100644 index 0000000000..09ab66540d --- /dev/null +++ b/targets/TARGET_NXP/TARGET_LPC176X/usb/USBEndpoints_LPC17_LPC23.h @@ -0,0 +1,97 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define NUMBER_OF_LOGICAL_ENDPOINTS (16) +#define NUMBER_OF_PHYSICAL_ENDPOINTS (NUMBER_OF_LOGICAL_ENDPOINTS * 2) + +/* Define physical endpoint numbers */ + +/* Endpoint No. Type(s) MaxPacket DoubleBuffer */ +/* ---------------- ------------ ---------- --- */ +#define EP0OUT (0x00) /* Control 64 No */ +#define EP0IN (0x80) /* Control 64 No */ +#define EP1OUT (0x01) /* Interrupt 64 No */ +#define EP1IN (0x81) /* Interrupt 64 No */ +#define EP2OUT (0x02) /* Bulk 64 Yes */ +#define EP2IN (0x82) /* Bulk 64 Yes */ +#define EP3OUT (0x03) /* Isochronous 1023 Yes */ +#define EP3IN (0x83) /* Isochronous 1023 Yes */ +#define EP4OUT (0x04) /* Interrupt 64 No */ +#define EP4IN (0x84) /* Interrupt 64 No */ +#define EP5OUT (0x05) /* Bulk 64 Yes */ +#define EP5IN (0x85) /* Bulk 64 Yes */ +#define EP6OUT (0x06) /* Isochronous 1023 Yes */ +#define EP6IN (0x86) /* Isochronous 1023 Yes */ +#define EP7OUT (0x07) /* Interrupt 64 No */ +#define EP7IN (0x87) /* Interrupt 64 No */ +#define EP8OUT (0x08) /* Bulk 64 Yes */ +#define EP8IN (0x88) /* Bulk 64 Yes */ +#define EP9OUT (0x09) /* Isochronous 1023 Yes */ +#define EP9IN (0x89) /* Isochronous 1023 Yes */ +#define EP10OUT (0x0A) /* Interrupt 64 No */ +#define EP10IN (0x8A) /* Interrupt 64 No */ +#define EP11OUT (0x0B) /* Bulk 64 Yes */ +#define EP11IN (0x8B) /* Bulk 64 Yes */ +#define EP12OUT (0x0C) /* Isochronous 1023 Yes */ +#define EP12IN (0x8C) /* Isochronous 1023 Yes */ +#define EP13OUT (0x0D) /* Interrupt 64 No */ +#define EP13IN (0x8D) /* Interrupt 64 No */ +#define EP14OUT (0x0E) /* Bulk 64 Yes */ +#define EP14IN (0x8E) /* Bulk 64 Yes */ +#define EP15OUT (0x0F) /* Bulk 64 Yes */ +#define EP15IN (0x8F) /* Bulk 64 Yes */ + +/* Maximum Packet sizes */ + +#define MAX_PACKET_SIZE_EP0 (64) +#define MAX_PACKET_SIZE_EP1 (64) +#define MAX_PACKET_SIZE_EP2 (64) +#define MAX_PACKET_SIZE_EP3 (1023) +#define MAX_PACKET_SIZE_EP4 (64) +#define MAX_PACKET_SIZE_EP5 (64) +#define MAX_PACKET_SIZE_EP6 (1023) +#define MAX_PACKET_SIZE_EP7 (64) +#define MAX_PACKET_SIZE_EP8 (64) +#define MAX_PACKET_SIZE_EP9 (1023) +#define MAX_PACKET_SIZE_EP10 (64) +#define MAX_PACKET_SIZE_EP11 (64) +#define MAX_PACKET_SIZE_EP12 (1023) +#define MAX_PACKET_SIZE_EP13 (64) +#define MAX_PACKET_SIZE_EP14 (64) +#define MAX_PACKET_SIZE_EP15 (64) + +/* Generic endpoints - intended to be portable accross devices */ +/* and be suitable for simple USB devices. */ + +/* Bulk endpoints */ +#define EPBULK_OUT (EP2OUT) +#define EPBULK_IN (EP2IN) +#define EPBULK_OUT_callback EP2_OUT_callback +#define EPBULK_IN_callback EP2_IN_callback +/* Interrupt endpoints */ +#define EPINT_OUT (EP1OUT) +#define EPINT_IN (EP1IN) +#define EPINT_OUT_callback EP1_OUT_callback +#define EPINT_IN_callback EP1_IN_callback +/* Isochronous endpoints */ +#define EPISO_OUT (EP3OUT) +#define EPISO_IN (EP3IN) +#define EPISO_OUT_callback EP3_OUT_callback +#define EPISO_IN_callback EP3_IN_callback + +#define MAX_PACKET_SIZE_EPBULK (MAX_PACKET_SIZE_EP2) +#define MAX_PACKET_SIZE_EPINT (MAX_PACKET_SIZE_EP1) +#define MAX_PACKET_SIZE_EPISO (MAX_PACKET_SIZE_EP3) diff --git a/targets/TARGET_NXP/TARGET_LPC176X/usb/USBHAL_LPC17.cpp b/targets/TARGET_NXP/TARGET_LPC176X/usb/USBHAL_LPC17.cpp new file mode 100644 index 0000000000..23e8c1f87a --- /dev/null +++ b/targets/TARGET_NXP/TARGET_LPC176X/usb/USBHAL_LPC17.cpp @@ -0,0 +1,709 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined(DEVICE_USBDEVICE) && DEVICE_USBDEVICE && \ + (defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC2460)) + +#include "USBEndpoints_LPC17_LPC23.h" +#include "USBPhyHw.h" +#include "usb_phy_api.h" + + +// Get endpoint direction +#define IN_EP(endpoint) ((endpoint) & 1U ? true : false) +#define OUT_EP(endpoint) ((endpoint) & 1U ? false : true) + +// Convert physical endpoint number to register bit +#define EP(endpoint) (1UL<>1)|(((endpoint)&1)?0x80:0)) + +// Power Control for Peripherals register +#define PCUSB (1UL<<31) + +// USB Clock Control register +#define DEV_CLK_EN (1UL<<1) +#define AHB_CLK_EN (1UL<<4) + +// USB Clock Status register +#define DEV_CLK_ON (1UL<<1) +#define AHB_CLK_ON (1UL<<4) + +// USB Device Interupt registers +#define FRAME (1UL<<0) +#define EP_FAST (1UL<<1) +#define EP_SLOW (1UL<<2) +#define DEV_STAT (1UL<<3) +#define CCEMPTY (1UL<<4) +#define CDFULL (1UL<<5) +#define RxENDPKT (1UL<<6) +#define TxENDPKT (1UL<<7) +#define EP_RLZED (1UL<<8) +#define ERR_INT (1UL<<9) + +// USB Control register +#define RD_EN (1<<0) +#define WR_EN (1<<1) +#define LOG_ENDPOINT(endpoint) ((DESC_TO_PHY(endpoint)>>1)<<2) + +// USB Receive Packet Length register +#define DV (1UL<<10) +#define PKT_RDY (1UL<<11) +#define PKT_LNGTH_MASK (0x3ff) + +// Serial Interface Engine (SIE) +#define SIE_WRITE (0x01) +#define SIE_READ (0x02) +#define SIE_COMMAND (0x05) +#define SIE_CMD_CODE(phase, data) ((phase<<8)|(data<<16)) + +// SIE Command codes +#define SIE_CMD_SET_ADDRESS (0xD0) +#define SIE_CMD_CONFIGURE_DEVICE (0xD8) +#define SIE_CMD_SET_MODE (0xF3) +#define SIE_CMD_READ_FRAME_NUMBER (0xF5) +#define SIE_CMD_READ_TEST_REGISTER (0xFD) +#define SIE_CMD_SET_DEVICE_STATUS (0xFE) +#define SIE_CMD_GET_DEVICE_STATUS (0xFE) +#define SIE_CMD_GET_ERROR_CODE (0xFF) +#define SIE_CMD_READ_ERROR_STATUS (0xFB) + +#define SIE_CMD_SELECT_ENDPOINT(endpoint) (0x00+DESC_TO_PHY(endpoint)) +#define SIE_CMD_SELECT_ENDPOINT_CLEAR_INTERRUPT(endpoint) (0x40+DESC_TO_PHY(endpoint)) +#define SIE_CMD_SET_ENDPOINT_STATUS(endpoint) (0x40+DESC_TO_PHY(endpoint)) + +#define SIE_CMD_CLEAR_BUFFER (0xF2) +#define SIE_CMD_VALIDATE_BUFFER (0xFA) + +// SIE Device Status register +#define SIE_DS_CON (1<<0) +#define SIE_DS_CON_CH (1<<1) +#define SIE_DS_SUS (1<<2) +#define SIE_DS_SUS_CH (1<<3) +#define SIE_DS_RST (1<<4) + +// SIE Device Set Address register +#define SIE_DSA_DEV_EN (1<<7) + +// SIE Configue Device register +#define SIE_CONF_DEVICE (1<<0) + +// Select Endpoint register +#define SIE_SE_FE (1<<0) +#define SIE_SE_ST (1<<1) +#define SIE_SE_STP (1<<2) +#define SIE_SE_PO (1<<3) +#define SIE_SE_EPN (1<<4) +#define SIE_SE_B_1_FULL (1<<5) +#define SIE_SE_B_2_FULL (1<<6) + +// Set Endpoint Status command +#define SIE_SES_ST (1<<0) +#define SIE_SES_DA (1<<5) +#define SIE_SES_RF_MO (1<<6) +#define SIE_SES_CND_ST (1<<7) + + +static USBPhyHw *instance; + +static volatile int epComplete; + +static void SIECommand(uint32_t command) +{ + // The command phase of a SIE transaction + LPC_USB->USBDevIntClr = CCEMPTY; + LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_COMMAND, command); + while (!(LPC_USB->USBDevIntSt & CCEMPTY)); +} + +static void SIEWriteData(uint8_t data) +{ + // The data write phase of a SIE transaction + LPC_USB->USBDevIntClr = CCEMPTY; + LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_WRITE, data); + while (!(LPC_USB->USBDevIntSt & CCEMPTY)); +} + +static uint8_t SIEReadData(uint32_t command) +{ + // The data read phase of a SIE transaction + LPC_USB->USBDevIntClr = CDFULL; + LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_READ, command); + while (!(LPC_USB->USBDevIntSt & CDFULL)); + return (uint8_t)LPC_USB->USBCmdData; +} + +static void SIEsetDeviceStatus(uint8_t status) +{ + // Write SIE device status register + SIECommand(SIE_CMD_SET_DEVICE_STATUS); + SIEWriteData(status); +} + +static uint8_t SIEgetDeviceStatus(void) +{ + // Read SIE device status register + SIECommand(SIE_CMD_GET_DEVICE_STATUS); + return SIEReadData(SIE_CMD_GET_DEVICE_STATUS); +} + +void SIEsetAddress(uint8_t address) +{ + // Write SIE device address register + SIECommand(SIE_CMD_SET_ADDRESS); + SIEWriteData((address & 0x7f) | SIE_DSA_DEV_EN); +} + +static uint8_t SIEselectEndpoint(uint8_t endpoint) +{ + // SIE select endpoint command + SIECommand(SIE_CMD_SELECT_ENDPOINT(endpoint)); + return SIEReadData(SIE_CMD_SELECT_ENDPOINT(endpoint)); +} + +static uint8_t SIEclearBuffer(void) +{ + // SIE clear buffer command + SIECommand(SIE_CMD_CLEAR_BUFFER); + return SIEReadData(SIE_CMD_CLEAR_BUFFER); +} + +static void SIEvalidateBuffer(void) +{ + // SIE validate buffer command + SIECommand(SIE_CMD_VALIDATE_BUFFER); +} + +static void SIEsetEndpointStatus(uint8_t endpoint, uint8_t status) +{ + // SIE set endpoint status command + SIECommand(SIE_CMD_SET_ENDPOINT_STATUS(endpoint)); + SIEWriteData(status); +} + +static uint16_t SIEgetFrameNumber(void) __attribute__((unused)); +static uint16_t SIEgetFrameNumber(void) +{ + // Read current frame number + uint16_t lowByte; + uint16_t highByte; + + SIECommand(SIE_CMD_READ_FRAME_NUMBER); + lowByte = SIEReadData(SIE_CMD_READ_FRAME_NUMBER); + highByte = SIEReadData(SIE_CMD_READ_FRAME_NUMBER); + + return (highByte << 8) | lowByte; +} + +static void SIEconfigureDevice(void) +{ + // SIE Configure device command + SIECommand(SIE_CMD_CONFIGURE_DEVICE); + SIEWriteData(SIE_CONF_DEVICE); +} + +static void SIEunconfigureDevice(void) +{ + // SIE Configure device command + SIECommand(SIE_CMD_CONFIGURE_DEVICE); + SIEWriteData(0); +} + +static void SIEconnect(void) +{ + // Connect USB device + uint8_t status = SIEgetDeviceStatus(); + SIEsetDeviceStatus(status | SIE_DS_CON); +} + + +static void SIEdisconnect(void) +{ + // Disconnect USB device + uint8_t status = SIEgetDeviceStatus(); + SIEsetDeviceStatus(status & ~SIE_DS_CON); +} + + +static uint8_t selectEndpointClearInterrupt(uint8_t endpoint) +{ + // Implemented using using EP_INT_CLR. + LPC_USB->USBEpIntClr = EP(endpoint); + while (!(LPC_USB->USBDevIntSt & CDFULL)); + return (uint8_t)LPC_USB->USBCmdData; +} + + +static void enableEndpointEvent(uint8_t endpoint) +{ + // Enable an endpoint interrupt + LPC_USB->USBEpIntEn |= EP(endpoint); +} + +static void disableEndpointEvent(uint8_t endpoint) __attribute__((unused)); +static void disableEndpointEvent(uint8_t endpoint) +{ + // Disable an endpoint interrupt + LPC_USB->USBEpIntEn &= ~EP(endpoint); +} + + +static uint32_t endpointReadcore(uint8_t endpoint, uint8_t *buffer, uint32_t size) +{ + // Read from an OUT endpoint + uint32_t actual_size; + uint32_t i; + uint32_t data = 0; + uint8_t offset; + + LPC_USB->USBCtrl = LOG_ENDPOINT(endpoint) | RD_EN; + while (!(LPC_USB->USBRxPLen & PKT_RDY)); + + actual_size = LPC_USB->USBRxPLen & PKT_LNGTH_MASK; + + offset = 0; + + if (actual_size > 0) { + for (i = 0; i < actual_size; i++) { + if (offset == 0) { + // Fetch up to four bytes of data as a word + data = LPC_USB->USBRxData; + } + + // extract a byte + if (size) { + *buffer = (data >> offset) & 0xff; + buffer++; + size--; + } + + // move on to the next byte + offset = (offset + 8) % 32; + } + } else { + (void)LPC_USB->USBRxData; + } + + LPC_USB->USBCtrl = 0; + + return actual_size; +} + +static void endpointWritecore(uint8_t endpoint, uint8_t *buffer, uint32_t size) +{ + // Write to an IN endpoint + uint32_t temp, data; + uint8_t offset; + + LPC_USB->USBCtrl = LOG_ENDPOINT(endpoint) | WR_EN; + + LPC_USB->USBTxPLen = size; + offset = 0; + data = 0; + + if (size > 0) { + do { + // Fetch next data byte into a word-sized temporary variable + temp = *buffer++; + + // Add to current data word + temp = temp << offset; + data = data | temp; + + // move on to the next byte + offset = (offset + 8) % 32; + size--; + + if ((offset == 0) || (size == 0)) { + // Write the word to the endpoint + LPC_USB->USBTxData = data; + data = 0; + } + } while (size > 0); + } else { + LPC_USB->USBTxData = 0; + } + + // Clear WR_EN to cover zero length packet case + LPC_USB->USBCtrl = 0; + + SIEselectEndpoint(endpoint); + SIEvalidateBuffer(); +} + +USBPhy *get_usb_phy() +{ + static USBPhyHw usbphy; + return &usbphy; +} + +USBPhyHw::USBPhyHw(void) +{ + +} + +USBPhyHw::~USBPhyHw(void) +{ + +} + +void USBPhyHw::init(USBPhyEvents *events) +{ + this->events = events; + + // Disable IRQ + NVIC_DisableIRQ(USB_IRQn); + + // Enable power to USB device controller + LPC_SC->PCONP |= PCUSB; + + // Enable USB clocks + LPC_USB->USBClkCtrl |= DEV_CLK_EN | AHB_CLK_EN; + while (LPC_USB->USBClkSt != (DEV_CLK_ON | AHB_CLK_ON)); + + // Configure pins P0.29 and P0.30 to be USB D+ and USB D- + LPC_PINCON->PINSEL1 &= 0xc3ffffff; + LPC_PINCON->PINSEL1 |= 0x14000000; + + // Disconnect USB device + SIEdisconnect(); + + // Configure pin P2.9 to be Connect + LPC_PINCON->PINSEL4 &= 0xfffcffff; + LPC_PINCON->PINSEL4 |= 0x00040000; + + // Connect must be low for at least 2.5uS + wait(0.3); + + // Set the maximum packet size for the control endpoints + endpoint_add(EP0IN, MAX_PACKET_SIZE_EP0, USB_EP_TYPE_CTRL); + endpoint_add(EP0OUT, MAX_PACKET_SIZE_EP0, USB_EP_TYPE_CTRL); + + // Attach IRQ + instance = this; + NVIC_SetVector(USB_IRQn, (uint32_t)&_usbisr); + + // Enable interrupts for device events and EP0 + LPC_USB->USBDevIntEn = EP_SLOW | DEV_STAT | FRAME; + enableEndpointEvent(EP0IN); + enableEndpointEvent(EP0OUT); +} + +void USBPhyHw::deinit() +{ + // Ensure device disconnected + SIEdisconnect(); + // Disable USB interrupts + NVIC_DisableIRQ(USB_IRQn); + events = NULL; +} + +bool USBPhyHw::powered() +{ + return true; +} + +void USBPhyHw::connect(void) +{ + NVIC_EnableIRQ(USB_IRQn); + // Connect USB device + SIEconnect(); +} + +void USBPhyHw::disconnect(void) +{ + NVIC_DisableIRQ(USB_IRQn); + // Disconnect USB device + SIEdisconnect(); +} + +void USBPhyHw::configure(void) +{ + SIEconfigureDevice(); +} + +void USBPhyHw::unconfigure(void) +{ + SIEunconfigureDevice(); +} + +void USBPhyHw::sof_enable() +{ + //TODO +} + +void USBPhyHw::sof_disable() +{ + //TODO +} + +void USBPhyHw::set_address(uint8_t address) +{ + SIEsetAddress(address); +} + +uint32_t USBPhyHw::ep0_set_max_packet(uint32_t max_packet) +{ + return MAX_PACKET_SIZE_EP0; +} + +void USBPhyHw::ep0_setup_read_result(uint8_t *buffer, uint32_t size) +{ + endpointReadcore(EP0OUT, buffer, size); +} + +void USBPhyHw::ep0_read(void) +{ + endpoint_read(EP0OUT, MAX_PACKET_SIZE_EP0); +} + +uint32_t USBPhyHw::ep0_read_result(uint8_t *buffer, uint32_t size) +{ + return endpointReadcore(EP0OUT, buffer, size); +} + +void USBPhyHw::ep0_write(uint8_t *buffer, uint32_t size) +{ + endpointWritecore(EP0IN, buffer, size); +} + +void USBPhyHw::ep0_stall(void) +{ + // This will stall both control endpoints + endpoint_stall(EP0OUT); +} + +bool USBPhyHw::endpoint_read(usb_ep_t endpoint, uint32_t maximumSize) +{ + // Don't clear isochronous endpoints + if ((DESC_TO_PHY(endpoint) >> 1) % 3 || (DESC_TO_PHY(endpoint) >> 1) == 0) { + SIEselectEndpoint(endpoint); + SIEclearBuffer(); + } + return true; +} + +bool USBPhyHw::endpoint_read_result(usb_ep_t endpoint, uint8_t *buffer, uint32_t size, uint32_t *bytesRead) +{ + + //for isochronous endpoint, we don't wait an interrupt + if ((DESC_TO_PHY(endpoint) >> 1) % 3 || (DESC_TO_PHY(endpoint) >> 1) == 0) { + if (!(epComplete & EP(endpoint))) { + return false; + } + } + + *bytesRead = endpointReadcore(endpoint, buffer, size); + epComplete &= ~EP(endpoint); + return true; +} + +bool USBPhyHw::endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size) +{ + epComplete &= ~EP(endpoint); + + endpointWritecore(endpoint, data, size); + return true; +} + +void USBPhyHw::endpoint_abort(usb_ep_t endpoint) +{ + //TODO - needs to be implemented +} + +bool USBPhyHw::endpoint_add(usb_ep_t endpoint, uint32_t maxPacket, usb_ep_type_t type) +{ + // Realise an endpoint + LPC_USB->USBDevIntClr = EP_RLZED; + LPC_USB->USBReEp |= EP(endpoint); + LPC_USB->USBEpInd = DESC_TO_PHY(endpoint); + LPC_USB->USBMaxPSize = maxPacket; + + while (!(LPC_USB->USBDevIntSt & EP_RLZED)); + LPC_USB->USBDevIntClr = EP_RLZED; + + enableEndpointEvent(endpoint); + return true; +} + +void USBPhyHw::endpoint_remove(usb_ep_t endpoint) +{ + // Unrealise an endpoint + + disableEndpointEvent(endpoint); + + LPC_USB->USBDevIntClr = EP_RLZED; + LPC_USB->USBReEp &= ~EP(endpoint); + + while (!(LPC_USB->USBDevIntSt & EP_RLZED)); + LPC_USB->USBDevIntClr = EP_RLZED; +} + +void USBPhyHw::endpoint_stall(usb_ep_t endpoint) +{ + // Stall an endpoint + if ((endpoint == EP0IN) || (endpoint == EP0OUT)) { + // Conditionally stall both control endpoints + SIEsetEndpointStatus(EP0OUT, SIE_SES_CND_ST); + } else { + SIEsetEndpointStatus(endpoint, SIE_SES_ST); + } +} + +void USBPhyHw::endpoint_unstall(usb_ep_t endpoint) +{ + // Unstall an endpoint. The endpoint will also be reinitialised + SIEsetEndpointStatus(endpoint, 0); +} + +void USBPhyHw::remote_wakeup(void) +{ + // Remote wakeup + uint8_t status; + + // Enable USB clocks + LPC_USB->USBClkCtrl |= DEV_CLK_EN | AHB_CLK_EN; + while (LPC_USB->USBClkSt != (DEV_CLK_ON | AHB_CLK_ON)); + + status = SIEgetDeviceStatus(); + SIEsetDeviceStatus(status & ~SIE_DS_SUS); +} + +const usb_ep_table_t *USBPhyHw::endpoint_table() +{ + static const usb_ep_table_t lpc_table = { + 4096 - 32 * 4, // 32 words for endpoint buffers + // +3 based added to interrupt and isochronous to ensure enough + // space for 4 byte alignment + { + {USB_EP_ATTR_ALLOW_CTRL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0}, + {USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3}, + {USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0}, + {USB_EP_ATTR_ALLOW_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3}, + {USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3}, + {USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0}, + {USB_EP_ATTR_ALLOW_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3}, + {USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3}, + {USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0}, + {USB_EP_ATTR_ALLOW_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3}, + {USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3}, + {USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0}, + {USB_EP_ATTR_ALLOW_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3}, + {USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3}, + {USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0}, + {USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0} + } + }; + return &lpc_table; +} + +void USBPhyHw::_usbisr(void) +{ + NVIC_DisableIRQ(USB_IRQn); + instance->events->start_process(); +} + +void USBPhyHw::process(void) +{ + uint8_t devStat; + + if (LPC_USB->USBDevIntSt & FRAME) { + // Start of frame event + events->sof(SIEgetFrameNumber()); + // Clear interrupt status flag + LPC_USB->USBDevIntClr = FRAME; + } + + if (LPC_USB->USBDevIntSt & DEV_STAT) { + // Device Status interrupt + // Must clear the interrupt status flag before reading the device status from the SIE + LPC_USB->USBDevIntClr = DEV_STAT; + + // Read device status from SIE + devStat = SIEgetDeviceStatus(); + //printf("devStat: %d\r\n", devStat); + + if (devStat & SIE_DS_SUS_CH) { + // Suspend status changed + if ((devStat & SIE_DS_SUS) != 0) { + events->suspend(false); + } + } + + if (devStat & SIE_DS_RST) { + // Bus reset + if ((devStat & SIE_DS_SUS) == 0) { + events->suspend(true); + } + events->reset(); + } + } + + if (LPC_USB->USBDevIntSt & EP_SLOW) { + // (Slow) Endpoint Interrupt + + // Process IN packets before SETUP packets + // Note - order of OUT and SETUP does not matter as OUT packets + // are clobbered by SETUP packets and thus ignored. + // + // A SETUP packet can arrive at any time where as an IN packet is + // only sent after calling EP0write and an OUT packet after EP0read. + // The functions EP0write and EP0read are called only in response to + // a setup packet or IN/OUT packets sent in response to that + // setup packet. Therefore, if an IN or OUT packet is pending + // at the same time as a SETUP packet, the IN or OUT packet belongs + // to the previous control transfer and should either be processed + // before the SETUP packet (in the case of IN) or dropped (in the + // case of OUT as SETUP clobbers the OUT data). + if (LPC_USB->USBEpIntSt & EP(EP0IN)) { + selectEndpointClearInterrupt(EP0IN); + LPC_USB->USBDevIntClr = EP_SLOW; + events->ep0_in(); + } + + // Process each endpoint interrupt + if (LPC_USB->USBEpIntSt & EP(EP0OUT)) { + if (selectEndpointClearInterrupt(EP0OUT) & SIE_SE_STP) { + // this is a setup packet + events->ep0_setup(); + } else { + events->ep0_out(); + } + LPC_USB->USBDevIntClr = EP_SLOW; + } + + //TODO - should probably process in the reverse order + for (uint8_t num = 2; num < 16 * 2; num++) { + uint8_t endpoint = PHY_TO_DESC(num); + if (LPC_USB->USBEpIntSt & EP(endpoint)) { + selectEndpointClearInterrupt(endpoint); + epComplete |= EP(endpoint); + LPC_USB->USBDevIntClr = EP_SLOW; + if (endpoint & 0x80) {//TODO - use macro + events->in(endpoint); + } else { + events->out(endpoint); + } + } + } + } + + NVIC_ClearPendingIRQ(USB_IRQn); + NVIC_EnableIRQ(USB_IRQn); +} + +#endif diff --git a/targets/TARGET_NXP/TARGET_LPC176X/usb/USBPhyHw.h b/targets/TARGET_NXP/TARGET_LPC176X/usb/USBPhyHw.h new file mode 100644 index 0000000000..999991c6a6 --- /dev/null +++ b/targets/TARGET_NXP/TARGET_LPC176X/usb/USBPhyHw.h @@ -0,0 +1,66 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBPHYHW_H +#define USBPHYHW_H + +#include "mbed.h" +#include "USBPhy.h" + + +class USBPhyHw : public USBPhy { +public: + USBPhyHw(); + virtual ~USBPhyHw(); + virtual void init(USBPhyEvents *events); + virtual void deinit(); + virtual bool powered(); + virtual void connect(); + virtual void disconnect(); + virtual void configure(); + virtual void unconfigure(); + virtual void sof_enable(); + virtual void sof_disable(); + virtual void set_address(uint8_t address); + virtual void remote_wakeup(); + virtual const usb_ep_table_t *endpoint_table(); + + virtual uint32_t ep0_set_max_packet(uint32_t max_packet); + virtual void ep0_setup_read_result(uint8_t *buffer, uint32_t size); + virtual void ep0_read(void); + virtual uint32_t ep0_read_result(uint8_t *buffer, uint32_t size); + virtual void ep0_write(uint8_t *buffer, uint32_t size); + virtual void ep0_stall(void); + + virtual bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type); + virtual void endpoint_remove(usb_ep_t endpoint); + virtual void endpoint_stall(usb_ep_t endpoint); + virtual void endpoint_unstall(usb_ep_t endpoint); + + virtual bool endpoint_read(usb_ep_t endpoint, uint32_t maximumSize); + virtual bool endpoint_read_result(usb_ep_t endpoint, uint8_t *data, uint32_t size, uint32_t *bytesRead); + virtual bool endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size); + virtual void endpoint_abort(usb_ep_t endpoint); + + virtual void process(); + +private: + USBPhyEvents *events; + + static void _usbisr(void); +}; + +#endif diff --git a/targets/targets.json b/targets/targets.json index bcdda9bbb2..661fb2298f 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -232,7 +232,7 @@ "extra_labels": ["NXP", "LPC176X", "MBED_LPC1768"], "supported_toolchains": ["ARM", "uARM", "GCC_ARM", "GCC_CR", "IAR"], "detect_code": ["1010"], - "device_has": ["ANALOGIN", "ANALOGOUT", "CAN", "DEBUG_AWARENESS", "ETHERNET", "I2C", "I2CSLAVE", "INTERRUPTIN", "LOCALFILESYSTEM", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SEMIHOST", "SERIAL", "SERIAL_FC", "SLEEP", "SPI", "SPISLAVE", "STDIO_MESSAGES", "FLASH"], + "device_has": ["ANALOGIN", "ANALOGOUT", "CAN", "DEBUG_AWARENESS", "ETHERNET", "I2C", "I2CSLAVE", "INTERRUPTIN", "LOCALFILESYSTEM", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SEMIHOST", "SERIAL", "SERIAL_FC", "SLEEP", "SPI", "SPISLAVE", "STDIO_MESSAGES", "FLASH", "USBDEVICE"], "release_versions": ["2", "5"], "features": ["LWIP"], "device_name": "LPC1768",