diff --git a/arch/arm/src/samd5e5/chip/sam_fuses.h b/arch/arm/src/samd5e5/chip/sam_fuses.h new file mode 100644 index 00000000000..eb3843e7f82 --- /dev/null +++ b/arch/arm/src/samd5e5/chip/sam_fuses.h @@ -0,0 +1,276 @@ +/******************************************************************************************** + * arch/arm/src/samd5e5/chip/sam_fuses.h + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_SAMD5E5_CHIP_SAM_FUSES_H +#define __ARCH_ARM_SRC_SAMD5E5_CHIP_SAM_FUSES_H + +/******************************************************************************************** + * Included Files + ********************************************************************************************/ + +#include + +#include "chip.h" + +/******************************************************************************************** + * Pre-processor Definitions + ********************************************************************************************/ +/* NVM Fuse addresses **********************************************************************/ + +/* NVM user row bits. The first eight 32-bit words contains calibration information that + * are automatically read at device power-on. The remaining 480 bytes can be used for + * storing custom parameters. + */ + +#define SAM_NVMUSER_ROW0 (SAM_NVM_USERPAGE + 0x0000) /* Bits 0-31 */ +#define SAM_NVMUSER_ROW1 (SAM_NVM_USERPAGE + 0x0004) /* Bits 32-63 */ +#define SAM_NVMUSER_ROW2 (SAM_NVM_USERPAGE + 0x0008) /* Bits 64-95 */ +#define SAM_NVMUSER_ROW3 (SAM_NVM_USERPAGE + 0x000c) /* Bits 96-127 */ +#define SAM_NVMUSER_ROW4 (SAM_NVM_USERPAGE + 0x0010) /* Bits 128-159 */ +#define SAM_NVMUSER_ROW5 (SAM_NVM_USERPAGE + 0x0014) /* Bits 160-191 */ +#define SAM_NVMUSER_ROW6 (SAM_NVM_USERPAGE + 0x0018) /* Bits 192-223 */ +#define SAM_NVMUSER_ROW7 (SAM_NVM_USERPAGE + 0x001c) /* Bits 224-255 */ + +/* NVM Software Calibration Area */ + +#define SAM_NVM_CALIBAREA0 (SAM_NVM_CALIBAREA + 0x0000) /* Bits 0-31 */ +#define SAM_NVM_CALIBAREA1 (SAM_NVM_CALIBAREA + 0x0004) /* Bits 32-63 */ + +/* NVM Software Calibration Area for Temperature (read-only) */ + +#define SAM_NVM_CALIBTEMP0 (SAM_NVM_CALIBAREA + 0x0080) /* Bits 0-31 */ +#define SAM_NVM_CALIBTEMP1 (SAM_NVM_CALIBAREA + 0x0080) /* Bits 32-63 */ +#define SAM_NVM_CALIBTEMP2 (SAM_NVM_CALIBAREA + 0x0080) /* Bits 64-95 */ +#define SAM_NVM_CALIBTEMP3 (SAM_NVM_CALIBAREA + 0x0080) /* Bits 96-127 */ + +/* Fuse bit-field definitions **************************************************************/ +/* NVM user pages */ + +#define SAM_FUSES_BOD33_DIS_ADDR SAM_NVMUSER_ROW0 +#define SAM_FUSES_BOD33_DIS_SHIFT (0) /* Bit 0: BOD33 Disable */ +#define SAM_FUSES_BOD33_DIS_MASK (1 << SAM_FUSES_BOD33_DIS_SHIFT) + +#define SAM_FUSES_BOD33LEVEL_ADDR SAM_NVMUSER_ROW0 +#define SAM_FUSES_BOD33LEVEL_SHIFT (1) /* Bits 1-8: BOD33 Level */ +#define SAM_FUSES_BOD33LEVEL_MASK (0xff << SAM_FUSES_BOD33LEVEL_SHIFT) +# define SAM_FUSES_BOD33LEVEL(n) (((uint32_t)n) << SAM_FUSES_BOD33LEVEL_SHIFT) + +#define SAM_FUSES_BOD33_ACTION_ADDR SAM_NVMUSER_ROW0 +#define SAM_FUSES_BOD33_ACTION_SHIFT (9) /* Bits 9-10: BOD33 Action */ +#define SAM_FUSES_BOD33_ACTION_MASK (3 << SAM_FUSES_BOD33_ACTION_SHIFT) +# define SAM_FUSES_BOD33_ACTION(n) (((uint32_t)n) << SAM_FUSES_BOD33_ACTION_SHIFT) + +#define SAM_FUSES_BOD33_HYST_ADDR SAM_NVMUSER_ROW0 +#define SAM_FUSES_BOD33_HYST_SHIFT (11) /* Bits 11-14: BOD33 Hysteresis */ +#define SAM_FUSES_BOD33_HYST_MASK (3 << SAM_FUSES_BOD33_HYST_SHIFT) +# define SAM_FUSES_BOD33_HYST(n) (((uint32_t)n) << SAM_FUSES_BOD33_HYST_SHIFT) + + /* Bits 15-25: Reserved */ + +#define SAM_FUSES_NVM_BOOT_ADDR SAM_NVMUSER_ROW0 +#define SAM_FUSES_NVM_BOOT_SHIFT (26) /* Bits 26-29: NVM Bootloader Size */ +#define SAM_FUSES_NVM_BOOT_MASK (15 << SAM_FUSES_NVM_BOOT_SHIFT) +# define SAM_FUSES_NVM_BOOT(n) (((uint32_t)n) << SAM_FUSES_NVM_BOOT_SHIFT) + + /* Bits 30-31: Factor settings */ + +#define SAM_FUSES_SEESBLK_ADDR SAM_NVMUSER_ROW1 +#define SAM_FUSES_SEESBLK_SHIFT (0) /* Bits 32-35: NVM Bootloader Size */ +#define SAM_FUSES_SEESBLK_MASK (15 << SAM_FUSES_SEESBLK_SHIFT) +# define SAM_FUSES_SEESBLK(n) (((uint32_t)n) << SAM_FUSES_SEESBLK_SHIFT) + +#define SAM_FUSES_SEEPSZ_ADDR SAM_NVMUSER_ROW1 +#define SAM_FUSES_SEEPSZ_SHIFT (4) /* Bits 36-38: SmartEEPROM Page Size */ +#define SAM_FUSES_SEEPSZ_MASK (7 << SAM_FUSES_SEEPSZ_SHIFT) +# define SAM_FUSES_SEEPSZ(n) (((uint32_t)n) << SAM_FUSES_SEEPSZ_SHIFT) + +#define SAM_FUSES_RAM_ECCDIS_ADDR SAM_NVMUSER_ROW1 +#define SAM_FUSES_RAM_ECCDIS_SHIFT (7) /* Bit 39: SmartEEPROM Page Size */ +#define SAM_FUSES_RAM_ECCDIS_MASK (1 << SAM_FUSES_RAM_ECCDIS_SHIFT) + + /* Bits 40-47: Factor settings */ + +#define SAM_FUSES_WDT_ENA_ADDR SAM_NVMUSER_ROW1 +#define SAM_FUSES_WDT_ENA_SHIFT (16) /* Bit 48: WDT Enable */ +#define SAM_FUSES_WDT_ENA_MASK (1 << SAM_FUSES_WDT_ENA_SHIFT) + +#define SAM_FUSES_WDT_ALWAYSON_ADDR SAM_NVMUSER_ROW1 +#define SAM_FUSES_WDT_ALWAYSON_SHIFT (17) /* Bit 49: WDT Always On */ +#define SAM_FUSES_WDT_ALWAYSON_MASK (1 << SAM_FUSES_WDT_ALWAYSON_SHIFT) + +#define SAM_FUSES_WDT_PER_ADDR SAM_NVMUSER_ROW0 +#define SAM_FUSES_WDT_PER_SHIFT (18) /* Bits 50-53: WDT Period */ +#define SAM_FUSES_WDT_PER_MASK (15 << SAM_FUSES_WDT_PER_SHIFT) +# define SAM_FUSES_WDT_PER(n) ((uint32_t)(n) << SAM_FUSES_WDT_PER_SHIFT) + +#define SAM_FUSES_WDT_WINDOW_ADDR SAM_NVMUSER_ROW1 +#define SAM_FUSES_WDT_WINDOW_SHIFT (22) /* Bits 54-57: WDT Window */ +#define SAM_FUSES_WDT_WINDOW_MASK (15 << SAM_FUSES_WDT_WINDOW_SHIFT) + +#define SAM_FUSES_WDT_EWOFFSET_ADDR SAM_NVMUSER_ROW1 +#define SAM_FUSES_WDT_EWOFFSET_SHIFT (26) /* Bits 58-61: WDT Early Warning Offset */ +#define SAM_FUSES_WDT_EWOFFSET_MASK (15 << SAM_FUSES_WDT_EWOFFSET_SHIFT) +# define SAM_FUSES_WDT_EWOFFSET(n) ((uint32_t)(n) << SAM_FUSES_WDT_EWOFFSET_SHIFT) + +#define SAM_FUSES_WDT_WEN_ADDR SAM_NVMUSER_ROW1 +#define SAM_FUSES_WDT_WEN_SHIFT (30) /* Bit 62: WDT Window Mode Enable*/ +#define SAM_FUSES_WDT_WEN_MASK (1 << SAM_FUSES_WDT_WEN_SHIFT) + + /* Bit 63: Reserved */ + +#define SAM_FUSES_LOCK_ADDR SAM_NVMUSER_ROW2 +#define SAM_FUSES_LOCK_SHIFT (0) /* Bits 64-95: NVM Region Lock bits */ +#define SAM_FUSES_LOCK_MASK (0xffffffff << SAM_FUSES_LOCK_SHIFT) +# define SAM_FUSES_LOCK(n) ((uint32_t)(n) << SAM_FUSES_LOCK_SHIFT) + + /* Bits 96-127: Usage page */ + /* Bits 128-159: Reserved */ + /* Bits 160-255: User pages */ + +/* NVM Calibration Area */ + +#define SAM_FUSES_AC_BIAS_ADDR SAM_NVM_CALIBAREA0 +#define SAM_FUSES_AC_BIAS_SHIFT (0) /* Bits 0-1: AC Comparator 0/1 Bias Scaling */ +#define SAM_FUSES_AC_BIAS_MASK (3 << SAM_FUSES_AC_BIAS_SHIFT) +# define SAM_FUSES_AC_BIAS(n) ((uint32_t)(n) << SAM_FUSES_AC_BIAS_SHIFT) + +#define SAM_FUSES_ADC0_BIASCOMP_ADDR SAM_NVM_CALIBAREA0 +#define SAM_FUSES_ADC0_BIASCOMP_SHIFT (2) /* Bits 2-4: ADC0 Bias comparator scaling */ +#define SAM_FUSES_ADC0_BIASCOMP_MASK (7 << SAM_FUSES_ADC0_BIASCOMP_SHIFT) +# define SAM_FUSES_ADC0_BIASCOMP(n) ((uint32_t)(n) << SAM_FUSES_ADC0_BIASCOMP_SHIFT) + +#define SAM_FUSES_ADC0_BIASREFBUF_ADDR SAM_NVM_CALIBAREA0 +#define SAM_FUSES_ADC0_BIASREFBUF_SHIFT (5) /* Bits 5-7: ADC0 Bias comparator scaling */ +#define SAM_FUSES_ADC0_BIASREFBUF_MASK (7 << SAM_FUSES_ADC0_BIASREFBUF_SHIFT) +# define SAM_FUSES_ADC0_BIASREFBUF(n) ((uint32_t)(n) << SAM_FUSES_ADC0_BIASREFBUF_SHIFT) + +#define SAM_FUSES_ADC0_BIASR2R_ADDR SAM_NVM_CALIBAREA0 +#define SAM_FUSES_ADC0_BIASR2R_SHIFT (8) /* Bits 8-10: ADC0 Bias comparator scaling */ +#define SAM_FUSES_ADC0_BIASR2R_MASK (7 << SAM_FUSES_ADC0_BIASR2R_SHIFT) +# define SAM_FUSES_ADC0_BIASR2R(n) ((uint32_t)(n) << SAM_FUSES_ADC0_BIASR2R_SHIFT) + + /* Bits 11-15: Reserved */ + +#define SAM_FUSES_ADC1_BIASCOMP_ADDR SAM_NVM_CALIBAREA0 +#define SAM_FUSES_ADC1_BIASCOMP_SHIFT (16) /* Bits 16-18: ADC1 Bias comparator scaling */ +#define SAM_FUSES_ADC1_BIASCOMP_MASK (7 << SAM_FUSES_ADC1_BIASCOMP_SHIFT) +# define SAM_FUSES_ADC1_BIASCOMP(n) ((uint32_t)(n) << SAM_FUSES_ADC1_BIASCOMP_SHIFT) + +#define SAM_FUSES_ADC1_BIASREFBUF_ADDR SAM_NVM_CALIBAREA0 +#define SAM_FUSES_ADC1_BIASREFBUF_SHIFT (19) /* Bits 19-21: ADC1 Bias comparator scaling */ +#define SAM_FUSES_ADC1_BIASREFBUF_MASK (7 << SAM_FUSES_ADC1_BIASREFBUF_SHIFT) +# define SAM_FUSES_ADC1_BIASREFBUF(n) ((uint32_t)(n) << SAM_FUSES_ADC1_BIASREFBUF_SHIFT) + +#define SAM_FUSES_ADC1_BIASR2R_ADDR SAM_NVM_CALIBAREA0 +#define SAM_FUSES_ADC1_BIASR2R_SHIFT (22) /* Bits 22-24: ADC1 Bias comparator scaling */ +#define SAM_FUSES_ADC1_BIASR2R_MASK (7 << SAM_FUSES_ADC1_BIASR2R_SHIFT) +# define SAM_FUSES_ADC1_BIASR2R(n) ((uint32_t)(n) << SAM_FUSES_ADC1_BIASR2R_SHIFT) + + /* Bits 25-35: Reserved */ + +#define SAM_FUSES_USBTRANSN_ADDR SAM_NVM_CALIBAREA1 +#define SAM_FUSES_USBTRANSN_SHIFT (0) /* Bits 32-36: USB TRNSN Calibration */ +#define SAM_FUSES_USBTRANSN_MASK (31 << SAM_FUSES_USBTRANSN_SHIFT) +# define SAM_FUSES_USBTRANSN(n) ((uint32_t)(n) << SAM_FUSES_USBTRANSN_SHIFT) + +#define SAM_FUSES_USBTRANSP_ADDR SAM_NVM_CALIBAREA1 +#define SAM_FUSES_USBTRANSP_SHIFT (5) /* Bits 37-41: USB TRNSP Calibration */ +#define SAM_FUSES_USBTRANSP_MASK (31 << SAM_FUSES_USBTRANSP_SHIFT) +# define SAM_FUSES_USBTRANSP(n) ((uint32_t)(n) << SAM_FUSES_USBTRANSP_SHIFT) + +#define SAM_FUSES_USBTRIM_ADDR SAM_NVM_CALIBAREA1 +#define SAM_FUSES_USBTRIM_SHIFT (11) /* Bits 43-44: USB TRIM Calibration */ +#define SAM_FUSES_USBTRIM_MASK (7 << SAM_FUSES_USBTRIM_SHIFT) +# define SAM_FUSES_USBTRIM(n) ((uint32_t)(n) << SAM_FUSES_USBTRIM_SHIFT) + +/* NVM Software Callibration Area -- Temperature Calibration Parameters */ + +#define SAM_FUSES_TLI_ADDR SAM_NVM_CALIBTEMP0 +#define SAM_FUSES_TLI_SHIFT (0) /* Bits 0-7: Integer part temperature TL */ +#define SAM_FUSES_TLI_MASK (0xff << SAM_FUSES_TLI_SHIFT) +# define SAM_FUSES_TLI(n) ((uint32_t)(n) << SAM_FUSES_TLI_SHIFT) + +#define SAM_FUSES_TLD_ADDR SAM_NVM_CALIBTEMP0 +#define SAM_FUSES_TLD_SHIFT (8) /* Bits 8-11: Fractional part temerature TL */ +#define SAM_FUSES_TLD_MASK (15 << SAM_FUSES_TLD_SHIFT) +# define SAM_FUSES_TLD(n) ((uint32_t)(n) << SAM_FUSES_TLD_SHIFT) + +#define SAM_FUSES_THI_ADDR SAM_NVM_CALIBTEMP0 +#define SAM_FUSES_THI_SHIFT (12) /* Bits 12-19: Integer part temperature TH */ +#define SAM_FUSES_THI_MASK (0xff << SAM_FUSES_THI_SHIFT) +# define SAM_FUSES_THI(n) ((uint32_t)(n) << SAM_FUSES_THI_SHIFT) + +#define SAM_FUSES_THD_ADDR SAM_NVM_CALIBTEMP0 +#define SAM_FUSES_THD_SHIFT (20) /* Bits 20-23: Fractional part temperature TH */ +#define SAM_FUSES_THD_MASK (15 << SAM_FUSES_THD_SHIFT) +# define SAM_FUSES_THD(n) ((uint32_t)(n) << SAM_FUSES_THD_SHIFT) + + /* 24-39: Reserved */ + +#define SAM_FUSES_VPL_ADDR SAM_NVM_CALIBTEMP1 +#define SAM_FUSES_VPL_SHIFT (8) /* Bits 40-51:Temperature calibration parameter */ +#define SAM_FUSES_VPL_MASK (0xfff << SAM_FUSES_VPL_SHIFT) +# define SAM_FUSES_VPL(n) ((uint32_t)(n) << SAM_FUSES_VPL_SHIFT) + +#define SAM_FUSES_VPH_ADDR SAM_NVM_CALIBTEMP1 +#define SAM_FUSES_VPH_SHIFT (20) /* Bits 52-63: Temperature calibration parameter */ +#define SAM_FUSES_VPH_MASK (0xfff << SAM_FUSES_VPH_SHIFT) +# define SAM_FUSES_VPH(n) ((uint32_t)(n) << SAM_FUSES_VPH_SHIFT) + +#define SAM_FUSES_VCL_ADDR SAM_NVM_CALIBTEMP2 +#define SAM_FUSES_VCL_SHIFT (20) /* Bits 64-75: Temperature calibration parameter */ +#define SAM_FUSES_VCL_MASK (0xfff << SAM_FUSES_VCL_SHIFT) +# define SAM_FUSES_VCL(n) ((uint32_t)(n) << SAM_FUSES_VCL_SHIFT) + +#define SAM_FUSES_VCH_ADDR SAM_NVM_CALIBTEMP2 +#define SAM_FUSES_VCH_SHIFT (11) /* Bits 76-87: Temperature calibration parameter */ +#define SAM_FUSES_VCH_MASK (0xfff << SAM_FUSES_VCH_SHIFT) +# define SAM_FUSES_VCH(n) ((uint32_t)(n) << SAM_FUSES_VCH_SHIFT) + + /* 88-127: Reserved */ + +/******************************************************************************************** + * Public Types + ********************************************************************************************/ + +/******************************************************************************************** + * Public Data + ********************************************************************************************/ + +/******************************************************************************************** + * Public Functions + ********************************************************************************************/ + +#endif /* __ARCH_ARM_SRC_SAMD5E5_CHIP_SAM_FUSES_H */ diff --git a/arch/arm/src/samd5e5/chip/sam_usb.h b/arch/arm/src/samd5e5/chip/sam_usb.h new file mode 100644 index 00000000000..a64a1ff1f89 --- /dev/null +++ b/arch/arm/src/samd5e5/chip/sam_usb.h @@ -0,0 +1,633 @@ +/******************************************************************************************** + * arch/arm/src/samd5e5/chip/sam_usb.h + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ********************************************************************************************/ + + +#ifndef __ARCH_ARM_SRC_SAMD5E5_CHIP_SAM_USB_H +#define __ARCH_ARM_SRC_SAMD5E5_CHIP_SAM_USB_H + +/******************************************************************************************** + * Included Files + ********************************************************************************************/ + +#include + +#include "chip.h" + +/******************************************************************************************** + * Pre-processor Definitions + ********************************************************************************************/ + +/* Capabilities and characteristics of endpoints ********************************************/ + +/* EP EP BANKS EP SIZE EP TYPE + * --- --------- --------- --------- + * 0 2 64/1023 Control/Bulk/Iso/Interrupt + * 1 2 64/1023 Control/Bulk/Iso/Interrupt + * 2 2 64/1023 Control/Bulk/Iso/Interrupt + * 3 2 64/1023 Control/Bulk/Iso/Interrupt + * 4 2 64/1023 Control/Bulk/Iso/Interrupt + * 5 2 64/1023 Control/Bulk/Iso/Interrupt + * 6 2 64/1023 Control/Bulk/Iso/Interrupt + * 7 2 64/1023 Control/Bulk/Iso/Interrupt + */ + +#define SAM_USB_NENDPOINTS (8) /* 8 IN and 8 OUT endpoints */ +#define SAM_USB_MAXPACKETSIZE(ep) (64) +#define SAM_USB_NBANKS(ep) (2) +#define SAM_USB_CONTROL(ep) (true) +#define SAM_USB_BULK(ep) (true) +#define SAM_USB_ISOCHRONOUS(ep) (true) +#define SAM_USB_INTERRUPT(ep) (true) + +/* USB register offsets ********************************************************************/ + +/* Common USB Device/Host Register Offsets */ + +#define SAM_USB_CTRLA_OFFSET 0x0000 /* Control A Register */ +#define SAM_USB_SYNCBUSY_OFFSET 0x0002 /* Synchronization Busy Register */ +#define SAM_USB_QOSCTRL_OFFSET 0x0003 /* QOS Control Register */ +#define SAM_USB_FSMSTATUS_OFFSET 0x000d /* Finite State Machine Register */ +#define SAM_USB_DESCADD_OFFSET 0x0024 /* Descriptor Address Register */ +#define SAM_USB_PADCAL_OFFSET 0x0028 /* Pad Calibration Register */ + +/* USB Device Register Offsets */ + +#define SAM_USBDEV_CTRLB_OFFSET 0x0008 /* Control B Register */ +#define SAM_USBDEV_DADD_OFFSET 0x000a /* Device Address Register */ +#define SAM_USBDEV_STATUS_OFFSET 0x000c /* Status Register */ +#define SAM_USBDEV_FNUM_OFFSET 0x0010 /* Device Frame Number Register */ +#define SAM_USBDEV_INTENCLR_OFFSET 0x0014 /* Device Ineterrupt Enable Clear Register */ +#define SAM_USBDEV_INTENSET_OFFSET 0x0018 /* Device interrupt Enable Set Register */ +#define SAM_USBDEV_INTFLAG_OFFSET 0x001c /* Device Interrupt Flag Register */ +#define SAM_USBDEV_EPINTSMRY_OFFSET 0x0020 /* Endpoint Interrupt Summary Register */ + +/* Endpoint Register n offset */ + +#define SAM_USBDEV_EP_OFFSET(n) (0x0100 + ((n) << 5)) + +/* The following are then relative to the endpoint register n offset */ + +#define SAM_USBDEV_EPCFG_OFFSET 0x0000 /* Device Endpoint Configuration Register */ +#define SAM_USBDEV_EPSTATUSCLR_OFFSET 0x0004 /* Endpoint Status Clear Register */ +#define SAM_USBDEV_EPSTATUSSET_OFFSET 0x0005 /* Endpoint Status Set Register */ +#define SAM_USBDEV_EPSTATUS_OFFSET 0x0006 /* Endpoint Status Register */ +#define SAM_USBDEV_EPINTFLAG_OFFSET 0x0007 /* Device Endpoint Interrupt Flag Register */ +#define SAM_USBDEV_EPINTENCLR_OFFSET 0x0008 /* Device Endpoint Interrupt Enable Register */ +#define SAM_USBDEV_EPINTENSET_OFFSET 0x0009 /* Device Endpoint Interrupt Set Register */ + +#define SAM_USBDEV_EPnCFG_OFFSET(n) (SAM_USBDEV_EP_OFFSET(n) + SAM_USBDEV_EPCFG_OFFSET) +#define SAM_USBDEV_EPnSTATUSCLR_OFFSET(n) (SAM_USBDEV_EP_OFFSET(n) + SAM_USBDEV_EPSTATUSCLR_OFFSET) +#define SAM_USBDEV_EPnSTATUSSET_OFFSET(n) (SAM_USBDEV_EP_OFFSET(n) + SAM_USBDEV_EPSTATUSSET_OFFSET) +#define SAM_USBDEV_EPnSTATUS_OFFSET(n) (SAM_USBDEV_EP_OFFSET(n) + SAM_USBDEV_EPSTATUS_OFFSET) +#define SAM_USBDEV_EPnINTFLAG_OFFSET(n) (SAM_USBDEV_EP_OFFSET(n) + SAM_USBDEV_EPINTFLAG_OFFSET) +#define SAM_USBDEV_EPnINTENCLR_OFFSET(n) (SAM_USBDEV_EP_OFFSET(n) + SAM_USBDEV_EPINTENCLR_OFFSET) +#define SAM_USBDEV_EPnINTENSET_OFFSET(n) (SAM_USBDEV_EP_OFFSET(n) + SAM_USBDEV_EPINTENSET_OFFSET) + +/* Device Endpoint Descriptor */ + +#define SAM_USBDEV_ADDR_OFFSET 0x0000 /* Address of Data buffer (Both banks) */ +#define SAM_USBDEV_PKTSIZE_OFFSET 0x0004 /* Packet Size (Both banks) */ +#define SAM_USBDEV_EXTREG_OFFSET 0x0008 /* Extended Register (Bank 0 only) */ +#define SAM_USBDEV_STATUSBK_OFFSET 0x000a /* Device Status Bank (Both banks) */ + +/* USB Host Register Offsets */ + +#define SAM_USBHOST_CTRLB_OFFSET 0x0008 /* Control B Register */ +#define SAM_USBHOST_HSOFC_OFFSET 0x000a /* Host Start-of-Frame Control Register */ +#define SAM_USBHOST_STATUS_OFFSET 0x000c /* Status Register */ +#define SAM_USBHOST_FNUM_OFFSET 0x0010 /* Host Frame Number Register */ +#define SAM_USBHOST_FLENHIGH_OFFSET 0x0012 /* Host Frame Length Register */ +#define SAM_USBHOST_INTENCLR_OFFSET 0x0014 /* Host Interrupt Enable Clear Register */ +#define SAM_USBHOST_INTENSET_OFFSET 0x0018 /* Host Interrupt Enable Set Register */ +#define SAM_USBHOST_INTFLAG_OFFSET 0x001c /* Host Interrupt Flag Status and Clear Register */ +#define SAM_USBHOST_PINTSMRY_OFFSET 0x0020 /* Pipe Interrupt Summary Register */ + +/* Pipe Register n Register */ + +#define SAM_USBHOST_PIPE_OFFSET(n) (0x0100 + ((n) << 4)) + +/* The following are then relative to the pipe register n offset */ + +#define SAM_USBHOST_PCFG_OFFSET 0x0000 /* Host Pipe Configuration Register */ +#define SAM_USBHOST_BINTERVAL_OFFSET 0x0003 /* Interval for Bulk-OUT/Ping Transaction Register */ +#define SAM_USBHOST_PSTATUSCLR_OFFSET 0x0004 /* Pipe Status Clear Register */ +#define SAM_USBHOST_PSTATUSET_OFFSET 0x0005 /* Pipe Status Set Register */ +#define SAM_USBHOST_PSTATUS_OFFSET 0x0006 /* Pipe Status Register */ +#define SAM_USBHOST_PINTFLAG_OFFSET 0x0007 /* Host Pipe Interrupt Flag Register */ +#define SAM_USBHOST_PINTENCLR_OFFSET 0x0008 /* Host Pipe Interrupt Clear Register */ +#define SAM_USBHOST_PINTENSET_OFFSET 0x0009 /* Host Pipe Interrupt Set Register */ + +#define SAM_USBHOST_PnCFG_OFFSET(n) (SAM_USBHOST_PIPE_OFFSET(n) + SAM_USBHOST_PCFG_OFFSET) +#define SAM_USBHOST_BINTERVALn_OFFSET(n) (SAM_USBHOST_PIPE_OFFSET(n) + SAM_USBHOST_BINTERVAL_OFFSET) +#define SAM_USBHOST_PnSTATUSCLR_OFFSET(n) (SAM_USBHOST_PIPE_OFFSET(n) + SAM_USBHOST_PSTATUSCLR_OFFSET) +#define SAM_USBHOST_PnSTATUSET_OFFSET(n) (SAM_USBHOST_PIPE_OFFSET(n) + SAM_USBHOST_PSTATUSET_OFFSET) +#define SAM_USBHOST_PnSTATUS_OFFSET(n) (SAM_USBHOST_PIPE_OFFSET(n) + SAM_USBHOST_PSTATUS_OFFSET) +#define SAM_USBHOST_PnINTFLAG_OFFSET(n) (SAM_USBHOST_PIPE_OFFSET(n) + SAM_USBHOST_PINTFLAG_OFFSET) +#define SAM_USBHOST_PnINTENCLR_OFFSET(n) (SAM_USBHOST_PIPE_OFFSET(n) + SAM_USBHOST_PINTENCLR_OFFSET) +#define SAM_USBHOST_PnINTENSET_OFFSET(n) (SAM_USBHOST_PIPE_OFFSET(n) + SAM_USBHOST_PINTENSET_OFFSET) + +/* Host Pipe Descriptor */ + +#define SAM_USBHOST_ADDR_OFFSET 0x0000 /* Data buffer address (Both banks) */ +#define SAM_USBHOST_PKTSIZE_OFFSET 0x0004 /* Packet size (Both banks) */ +#define SAM_USBHOST_EXTREG_OFFSET 0x0008 /* Extended register (Bank 0 only) */ +#define SAM_USBHOST_STATUSBK_OFFSET 0x000a /* host status bank (Both banks) */ +#define SAM_USBHOST_CTRLPIPE_OFFSET 0x000c /* Host control pipe (Bank 0 only) */ +#define SAM_USBHOST_STATUSPIPE_OFFSET 0x000e /* Host status pipe (Both banks) */ + +/* USB register addresses ******************************************************************/ + +/* Common USB Device/Host Register Addresses */ + +#define SAM_USB_CTRLA (SAM_USB_BASE + SAM_USB_CTRLA_OFFSET) +#define SAM_USB_SYNCBUSY (SAM_USB_BASE + SAM_USB_SYNCBUSY_OFFSET) +#define SAM_USB_QOSCTRL (SAM_USB_BASE + SAM_USB_QOSCTRL_OFFSET) +#define SAM_USB_FSMSTATUS (SAM_USB_BASE + SAM_USB_FSMSTATUS_OFFSET) +#define SAM_USB_DESCADD (SAM_USB_BASE + SAM_USB_DESCADD_OFFSET) +#define SAM_USB_PADCAL (SAM_USB_BASE + SAM_USB_PADCAL_OFFSET) + +/* USB Device Register Addresses */ + +#define SAM_USBDEV_CTRLB (SAM_USB_BASE + SAM_USBDEV_CTRLB_OFFSET) +#define SAM_USBDEV_DADD (SAM_USB_BASE + SAM_USBDEV_DADD_OFFSET) +#define SAM_USBDEV_STATUS (SAM_USB_BASE + SAM_USBDEV_STATUS_OFFSET) +#define SAM_USBDEV_FNUM (SAM_USB_BASE + SAM_USBDEV_FNUM_OFFSET) +#define SAM_USBDEV_INTENCLR (SAM_USB_BASE + SAM_USBDEV_INTENCLR_OFFSET) +#define SAM_USBDEV_INTENSET (SAM_USB_BASE + SAM_USBDEV_INTENSET_OFFSET) +#define SAM_USBDEV_INTFLAG (SAM_USB_BASE + SAM_USBDEV_INTFLAG_OFFSET) +#define SAM_USBDEV_EPINTSMRY (SAM_USB_BASE + SAM_USBDEV_EPINTSMRY_OFFSET) + +/* Endpoint Register n Base Address */ + +#define SAM_USBDEV_EP_BASE(n) (SAM_USB_BASE + SAM_USBDEV_EP_OFFSET(n)) + +/* Endpoint n Register Addresses */ + +#define SAM_USBDEV_EPCFG(n) (SAM_USBDEV_EP_BASE(n) + SAM_USBDEV_EPCFG_OFFSET) +#define SAM_USBDEV_EPSTATUSCLR(n) (SAM_USBDEV_EP_BASE(n) + SAM_USBDEV_EPSTATUSCLR_OFFSET) +#define SAM_USBDEV_EPSTATUSSET(n) (SAM_USBDEV_EP_BASE(n) + SAM_USBDEV_EPSTATUSSET_OFFSET) +#define SAM_USBDEV_EPSTATUS(n) (SAM_USBDEV_EP_BASE(n) + SAM_USBDEV_EPSTATUS_OFFSET) +#define SAM_USBDEV_EPINTFLAG(n) (SAM_USBDEV_EP_BASE(n) + SAM_USBDEV_EPINTFLAG_OFFSET) +#define SAM_USBDEV_EPINTENCLR(n) (SAM_USBDEV_EP_BASE(n) + SAM_USBDEV_EPINTENCLR_OFFSET) +#define SAM_USBDEV_EPINTENSET(n) (SAM_USBDEV_EP_BASE(n) + SAM_USBDEV_EPINTENSET_OFFSET) + +/* USB Host Register Addresses */ + +#define SAM_USBHOST_CTRLB (SAM_USB_BASE + SAM_USBHOST_CTRLB_OFFSET) +#define SAM_USBHOST_HSOFC (SAM_USB_BASE + SAM_USBHOST_HSOFC_OFFSET) +#define SAM_USBHOST_STATUS (SAM_USB_BASE + SAM_USBHOST_STATUS_OFFSET) +#define SAM_USBHOST_FNUM (SAM_USB_BASE + SAM_USBHOST_FNUM_OFFSET) +#define SAM_USBHOST_FLENHIGH (SAM_USB_BASE + SAM_USBHOST_FLENHIGH_OFFSET) +#define SAM_USBHOST_INTENCLR (SAM_USB_BASE + SAM_USBHOST_INTENCLR_OFFSET) +#define SAM_USBHOST_INTENSET (SAM_USB_BASE + SAM_USBHOST_INTENSET_OFFSET) +#define SAM_USBHOST_INTFLAG (SAM_USB_BASE + SAM_USBHOST_INTFLAG_OFFSET) +#define SAM_USBHOST_PINTSMRY (SAM_USB_BASE + SAM_USBHOST_PINTSMRY_OFFSET) + +/* Pipe Register n Base Address */ + +#define SAM_USBHOST_PIPE_BASE(n) (SAM_USB_BASE + SAM_USBHOST_PIPE_OFFSET(n)) + +/* Pipe n Register Addresses */ + +#define SAM_USBHOST_PCFG(n) (SAM_USBHOST_PIPE_BASE(n) + SAM_USBHOST_PCFG_OFFSET) +#define SAM_USBHOST_BINTERVAL(n) (SAM_USBHOST_PIPE_BASE(n) + SAM_USBHOST_BINTERVAL_OFFSET) +#define SAM_USBHOST_PSTATUSCLR(n) (SAM_USBHOST_PIPE_BASE(n) + SAM_USBHOST_PSTATUSCLR_OFFSET) +#define SAM_USBHOST_PSTATUSET(n) (SAM_USBHOST_PIPE_BASE(n) + SAM_USBHOST_PSTATUSET_OFFSET) +#define SAM_USBHOST_PSTATUS(n) (SAM_USBHOST_PIPE_BASE(n) + SAM_USBHOST_PSTATUS_OFFSET) +#define SAM_USBHOST_PINTFLAG(n) (SAM_USBHOST_PIPE_BASE(n) + SAM_USBHOST_PINTFLAG_OFFSET) +#define SAM_USBHOST_PINTENCLR(n) (SAM_USBHOST_PIPE_BASE(n) + SAM_USBHOST_PINTENCLR_OFFSET) +#define SAM_USBHOST_PINTENSET(n) (SAM_USBHOST_PIPE_BASE(n) + SAM_USBHOST_PINTENSET_OFFSET) + +/* USB register bit definitions ************************************************************/ + +/* Common USB Device/Host Register Offsets */ + +/* Control A Register */ + +#define USB_CTRLA_SWRST (1 << 0) /* Bit 0: Software reset */ +#define USB_CTRLA_ENABLE (1 << 1) /* Bit 1: Enable */ +#define USB_CTRLA_RUNSTBY (1 << 2) /* Bit 2: Run in standby */ +#define USB_CTRLA_MODE (1 << 7) /* Bit 7: Operating mode */ +# define USB_CTRLA_MODE_DEVICE (0) /* 0 = USB device mode */ +# define USB_CTRLA_MODE_HOST (1 << 7) /* 1 = USB host mode */ + +/* Synchronization Busy Register */ + +#define USB_SYNCBUSY_SWRST (1 << 0) /* Bit 0: Software reset status */ +#define USB_SYNCBUSY_ENABLE (1 << 1) /* Bit 1: Enable status */ + +/* QOS Control Register */ + +#define USB_QOSCTRL_CQOS_SHIFT (0) /* Bit 0-1: Data quality of service */ +#define USB_QOSCTRL_CQOS_MASK (3 << USB_QOSCTRL_CQOS_SHIFT) +# define USB_QOSCTRL_CQOS(n) ((uint8_t)(n) << USB_QOSCTRL_CQOS_SHIFT) +#define USB_QOSCTRL_DQOS_SHIFT (2) /* Bit 2-3: Configuration qualilty of service */ +#define USB_QOSCTRL_DQOS_MASK (3 << USB_QOSCTRL_DQOS_SHIFT) +# define USB_QOSCTRL_DQOS(n) ((uint8_t)(n) << USB_QOSCTRL_DQOS_SHIFT) + +/* Finite State Machine Register */ + +#define USB_FSMSTATUS_SHIFT (0) /* Bits 0-6: Finite state machine status */ +#define USB_FSMSTATUS_MASK (0x7f << USB_FSMSTATUS_SHIFT) +# define USB_FSMSTATUS_OFF (0x01 << USB_FSMSTATUS_SHIFT) /* Powered-off, disconnect, disabled state */ +# define USB_FSMSTATUS_ON (0x02 << USB_FSMSTATUS_SHIFT) /* Idle and Active states */ +# define USB_FSMSTATUS_SUSPEND (0x04 << USB_FSMSTATUS_SHIFT) +# define USB_FSMSTATUS_SLEEP (0x08 << USB_FSMSTATUS_SHIFT) +# define USB_FSMSTATUS_DNRESUME (0x10 << USB_FSMSTATUS_SHIFT) /* Down stream resume */ +# define USB_FSMSTATUS_UPRESUME (0x20 << USB_FSMSTATUS_SHIFT) /* Up stream resume */ +# define USB_FSMSTATUS_RESET (0x40 << USB_FSMSTATUS_SHIFT) /* USB lines reset */ + +/* Descriptor Address Register (32-bit address) */ + +/* Pad Calibration Register */ + +#define USB_PADCAL_TRANSP_SHIFT (0) /* Bit 0-4: Trimmable output driver impedance P */ +#define USB_PADCAL_TRANSP_MASK (31 << USB_PADCAL_TRANSP_SHIFT) +# define USB_PADCAL_TRANSP(n) ((uint16_t)(n) << USB_PADCAL_TRANSP_SHIFT) +#define USB_PADCAL_TRANSN_SHIFT (6) /* Bit 6-10: Trimmable output driver impedance N */ +#define USB_PADCAL_TRANSN_MASK (31 << USB_PADCAL_TRANSN_SHIFT) +# define USB_PADCAL_TRANSN(n) ((uint16_t)(n) << USB_PADCAL_TRANSN_SHIFT) +#define USB_PADCAL_TRIM_SHIFT (12) /* Bit 12-14: Trim bits for DP/DM */ +#define USB_PADCAL_TRIM_MASK (7 << USB_PADCAL_TRIM_SHIFT) +# define USB_PADCAL_TRIM(n) ((uint16_t)(n) << USB_PADCAL_TRIM_SHIFT) + +/* USB Device Register Offsets */ + +/* Control B Register */ + +#define USBDEV_CTRLB_DETACH (1 << 0) /* Bit 0: Detach */ +#define USBDEV_CTRLB_UPRSM (1 << 1) /* Bit 1: Upstream resume */ +#define USBDEV_CTRLB_SPDCONF_SHIFT (2) /* Bits 2-3: Speed configuration */ +#define USBDEV_CTRLB_SPDCONF_MASK (3 << USBDEV_CTRLB_SPDCONF_SHIFT) +# define USBDEV_CTRLB_SPDCONF_FULL (0 << USBDEV_CTRLB_SPDCONF_SHIFT) /* Full speed */ +# define USBDEV_CTRLB_SPDCONF_LOW (1 << USBDEV_CTRLB_SPDCONF_SHIFT) /* Low speed */ +# define USBDEV_CTRLB_SPDCONF_HIGH (2 << USBDEV_CTRLB_SPDCONF_SHIFT) /* High speed capable */ +# define USBDEV_CTRLB_SPDCONF_HIGH_TM (3 << USBDEV_CTRLB_SPDCONF_SHIFT) /* High speed Test Mode */ +#define USBDEV_CTRLB_NREPLY (1 << 4) /* Bit 4: No reply except SETUP token */ +#define USBDEV_CTRLB_GNAK (1 << 9) /* Bit 9: Global NAK */ +#define USBDEV_CTRLB_LPMHDSK_SHIFT (10) /* Bits 10-11: Link power management handshake */ +#define USBDEV_CTRLB_LPMHDSK_MASK (3 << USBDEV_CTRLB_LPMHDSK_SHIFT) +# define USBDEV_CTRLB_LPMHDSK_NONE (0 << USBDEV_CTRLB_LPMHDSK_SHIFT) /* No handshake, LPM not supported */ +# define USBDEV_CTRLB_LPMHDSK_ACK (1 << USBDEV_CTRLB_LPMHDSK_SHIFT) +# define USBDEV_CTRLB_LPMHDSK_NYET (2 << USBDEV_CTRLB_LPMHDSK_SHIFT) + +/* Device Address Register */ + +#define USBDEV_DADD_SHIFT (0) /* Bits 0-6: Device address */ +#define USBDEV_DADD_MASK (0x7f << USBDEV_DADD_SHIFT) +# define USBDEV_DADD(n) ((uint8_t)(n) << USBDEV_DADD_SHIFT) +#define USBDEV_DADD_ADDEN (1 << 7) /* Bit 7: Device address enable */ + +/* Status Register */ + +#define USBDEV_STATUS_SPEED_SHIFT (2) /* Bits 2-3: Speed status */ +#define USBDEV_STATUS_SPEED_MASK (3 << USBDEV_STATUS_SPEED_SHIFT) +# define USBDEV_STATUS_SPEED_LOW (0 << USBDEV_STATUS_SPEED_SHIFT) /* Low speed */ +# define USBDEV_STATUS_SPEED_FULL (1 << USBDEV_STATUS_SPEED_SHIFT) /* Full speed */ +#define USBDEV_STATUS_LNSTATE_SHIFT (6) /* Bits 6-7: USB line status */ +#define USBDEV_STATUS_LNSTATE_MASK (3 << USBDEV_STATUS_LNSTATE_SHIFT) +# define USBDEV_STATUS_LNSTATE_SE0 (0 << USBDEV_STATUS_LNSTATE_SHIFT) /* SE0/RESET */ +# define USBDEV_STATUS_LNSTATE_LJFK (1 << USBDEV_STATUS_LNSTATE_SHIFT) /* LS-J or FS-K */ +# define USBDEV_STATUS_LNSTATE_LKFJ (2 << USBDEV_STATUS_LNSTATE_SHIFT) /* LS-K or FS-L */ + +/* Device Frame Number Register */ + +#define USBDEV_MFNUM_SHIFT (0) /* Bits 0-2: Micro-frame number */ +#define USBDEV_MFNUM_MASK (7 << USBDEV_MFNUM_SHIFT) +#define USBDEV_FNUM_SHIFT (3) /* Bits 3-13: Frame number */ +#define USBDEV_FNUM_MASK (0x7ff << USBDEV_FNUM_SHIFT) +#define USBDEV_FNUM_FNCERR (1 << 15) /* Bit 15: Frame number CRC error */ + +/* Common definitions for Device Interrupt Enable Clear Register, Device interrupt + * Enable Set Register, and Device Interrupt Flag Status and Clear Register. + */ + +#define USBDEV_INT_SUSPEND (1 << 0) /* Bit 0: Suspend interrupt */ +#define USBDEV_INT_SOF (1 << 2) /* Bit 2: Start of frame interrupt */ +#define USBDEV_INT_EORST (1 << 3) /* Bit 3: End of reset interrupt */ +#define USBDEV_INT_WAKEUP (1 << 4) /* Bit 4: Wake-up interrupt */ +#define USBDEV_INT_EORSM (1 << 5) /* Bit 5: End of resume interrupt */ +#define USBDEV_INT_UPRSM (1 << 6) /* Bit 6: Upstream resume interrupt */ +#define USBDEV_INT_RAMACER (1 << 7) /* Bit 7: RAM access interrupt */ +#define USBDEV_INT_LPMNYET (1 << 8) /* Bit 8: Link power NotYet interrupt */ +#define USBDEV_INT_LPMSUSP (1 << 9) /* Bit 9: Link power suspend interrupt */ + +/* Endpoint Interrupt Summary Register */ + +#define USBDEV_EPINTSMRY_EPINT(n) (1 << (n)) + +/* Device Endpoint Configuration Register */ + +#define USBDEV_EPCFG_EPTYPE0_SHIFT (0) /* Bits 0-2: Endpoint type for OUT direction */ +#define USBDEV_EPCFG_EPTYPE0_MASK (7 << USBDEV_EPCFG_EPTYPE0_SHIFT) +# define USBDEV_EPCCFG_EPTYPE0_DISABLED (0 << USBDEV_EPCFG_EPTYPE0_SHIFT) /* Bank 0 disabled */ +# define USBDEV_EPCCFG_EPTYPE0_CTRLOUT (1 << USBDEV_EPCFG_EPTYPE0_SHIFT) /* Bank 0 control SETUP/OUT */ +# define USBDEV_EPCCFG_EPTYPE0_ISOCOUT (2 << USBDEV_EPCFG_EPTYPE0_SHIFT) /* Bank 0 isochronous OUT */ +# define USBDEV_EPCCFG_EPTYPE0_BULKOUT (3 << USBDEV_EPCFG_EPTYPE0_SHIFT) /* Bank 0 bulk OUT */ +# define USBDEV_EPCCFG_EPTYPE0_INTOUT (4 << USBDEV_EPCFG_EPTYPE0_SHIFT) /* Bank 0 interrupt OUT */ +# define USBDEV_EPCCFG_EPTYPE0_DBIN (5 << USBDEV_EPCFG_EPTYPE0_SHIFT) /* Bank 0 dual bank IN */ +#define USBDEV_EPCFG_EPTYPE1_SHIFT (4) /* Bits 4-6: Endpoint type for IN direction */ +#define USBDEV_EPCFG_EPTYPE1_MASK (7 << USBDEV_EPCFG_EPTYPE1_SHIFT) +# define USBDEV_EPCCFG_EPTYPE1_DISABLED (0 << USBDEV_EPCFG_EPTYPE1_SHIFT) /* Bank 1 disabled */ +# define USBDEV_EPCCFG_EPTYPE1_CTRLIN (1 << USBDEV_EPCFG_EPTYPE1_SHIFT) /* Bank 1 control IN */ +# define USBDEV_EPCCFG_EPTYPE1_ISOCIN (2 << USBDEV_EPCFG_EPTYPE1_SHIFT) /* Bank 1 isochronous IN */ +# define USBDEV_EPCCFG_EPTYPE1_BULKIN (3 << USBDEV_EPCFG_EPTYPE1_SHIFT) /* Bank 1 bulk IN */ +# define USBDEV_EPCCFG_EPTYPE1_INTIN (4 << USBDEV_EPCFG_EPTYPE1_SHIFT) /* Bank 1 interrupt IN */ +# define USBDEV_EPCCFG_EPTYPE1_DBOUT (5 << USBDEV_EPCFG_EPTYPE1_SHIFT) /* Bank 1 dual bank OUT */ + +/* Common definitions for Endpoint Status Clear Register, Endpoint Status Set + * Register, and Endpoint Status Set Register + */ + +#define USBDEV_EPSTATUS_DTGLOUT (1 << 0) /* Bit 0: Data toggle OUT */ +#define USBDEV_EPSTATUS_DTGLIN (1 << 1) /* Bit 1: Data toggle IN */ +#define USBDEV_EPSTATUS_CURBK (1 << 2) /* Bit 2: Current bank */ +#define USBDEV_EPSTATUS_STALLRQ0 (1 << 4) /* Bit 4: Stall bank 0 request */ +#define USBDEV_EPSTATUS_STALLRQ1 (1 << 5) /* Bit 5: Stall bank 1 request */ +#define USBDEV_EPSTATUS_BK0RDY (1 << 6) /* Bit 6: Bank 0 ready */ +#define USBDEV_EPSTATUS_BK1RDY (1 << 7) /* Bit 7: Bank 1 ready */ + +/* Common definitions for Device Endpoint Interrupt Flag Register, Device Endpoint + * Interrupt Enable Register, and Device Endpoint Interrupt Set Register + * + * REVISIT: Datasheet shows only one bit for TRCPT, TRFAIL, and STALL. But text + * (and code) use two bits, one for IN and one for OUT. Both can't be true. + */ + +#define USBDEV_EPINT_TRCPT0 (1 << 0) /* Bit 0: Transmit complete 0 interrupt */ +#define USBDEV_EPINT_TRCPT1 (1 << 1) /* Bit 1: Transmit complete 1 interrupt */ +#define USBDEV_EPINT_TRFAIL0 (1 << 2) /* Bit 2: Transmit fail 0 interrupt */ +#define USBDEV_EPINT_TRFAIL1 (1 << 3) /* Bit 3: Transmit fail 1 interrupt */ +#define USBDEV_EPINT_RXSTP (1 << 4) /* Bit 4: Received SETUP interrupt */ +#define USBDEV_EPINT_STALL0 (1 << 5) /* Bit 5: Transmit stall 0 interrupt */ +#define USBDEV_EPINT_STALL1 (1 << 6) /* Bit 6: Transmit stall 1 interrupt */ + +/* Device Endpoint Descriptor */ + +/* Address of Data buffer (Both banks, 32-bit address) */ + +/* Packet Size (Both banks) */ + +#define USBDEV_PKTSIZE_BCNT_SHIFT (0) /* Bits 0-13: Byte count */ +#define USBDEV_PKTSIZE_BCNT_MASK (0x3fff << USBDEV_PKTSIZE_BCNT_SHIFT) +# define USBDEV_PKTSIZE_BCNT(n) ((uint32_t)(n) << USBDEV_PKTSIZE_BCNT_SHIFT) +#define USBDEV_PKTSIZE_MPKTSIZE_SHIFT (14) /* Bits 14-27: Multi-packet size */ +#define USBDEV_PKTSIZE_MPKTSIZE_MASK (0x3fff << USBDEV_PKTSIZE_MPKTSIZE_SHIFT) +# define USBDEV_PKTSIZE_MPKTSIZE(n) ((uint32_t)(n) << USBDEV_PKTSIZE_MPKTSIZE_SHIFT) +#define USBDEV_PKTSIZE_SIZE_SHIFT (28) /* Bits 28-30: Endpoint size */ +#define USBDEV_PKTSIZE_SIZE_MASK (7 << USBDEV_PKTSIZE_SIZE_SHIFT) +# define USBDEV_PKTSIZE_SIZE_8B (0 << USBDEV_PKTSIZE_SIZE_SHIFT) /* 8 bytes */ +# define USBDEV_PKTSIZE_SIZE_16B (1 << USBDEV_PKTSIZE_SIZE_SHIFT) /* 16 bytes */ +# define USBDEV_PKTSIZE_SIZE_32B (2 << USBDEV_PKTSIZE_SIZE_SHIFT) /* 32 bytes */ +# define USBDEV_PKTSIZE_SIZE_64B (3 << USBDEV_PKTSIZE_SIZE_SHIFT) /* 64 bytes */ +# define USBDEV_PKTSIZE_SIZE_128B (4 << USBDEV_PKTSIZE_SIZE_SHIFT) /* 128 bytes (isoc only) */ +# define USBDEV_PKTSIZE_SIZE_256B (5 << USBDEV_PKTSIZE_SIZE_SHIFT) /* 256 bytes (isoc only) */ +# define USBDEV_PKTSIZE_SIZE_512B (6 << USBDEV_PKTSIZE_SIZE_SHIFT) /* 512 bytes (isoc only) */ +# define USBDEV_PKTSIZE_SIZE_1023B (7 << USBDEV_PKTSIZE_SIZE_SHIFT) /* 1023 bytes (isoc only) */ +#define USBDEV_PKTSIZE_AUTOZLP (1 << 31) /* Bit 31: Automatic zero length packet */ + +/* Extended Register (Bank 0 only) */ + +#define USBDEV_EXTREG_SUBPID_SHIFT (0) /* Bits 0-3: Sub PID */ +#define USBDEV_EXTREG_SUBPID_MASK (15 << USBDEV_EXTREG_SUBPID_SHIFT) +# define USBDEV_EXTREG_SUBPID(n) ((uint16_t)(n) << USBDEV_EXTREG_SUBPID_SHIFT) +#define USBDEV_EXTREG_VARIABLE_SHIFT (4) /* Bits 4-14: Variable */ +#define USBDEV_EXTREG_VARIABLE_MASK (0x7ff << USBDEV_EXTREG_VARIABLE_SHIFT) +# define USBDEV_EXTREG_VARIABLE(n) ((uint16_t)(n) << USBDEV_EXTREG_VARIABLE_SHIFT) + +/* Device Status Bank (Both banks) */ + +#define USBDEV_STATUSBK_CRCERR (1 << 0) /* Bit 0: CRC error */ +#define USBDEV_STATUSBK_ERRORFLOW (1 << 1) /* Bit 1: Error flow status */ + +/* USB Host Register Offsets */ + +/* Control B Register */ + +#define USBHOST_CTRLB_RESUME (1 << 1) /* Bit 1: Send USB resume */ +#define USBHOST_CTRLB_SPDCONF_SHIFT (2) /* Bits 2-3: Host speed configuration */ +#define USBHOST_CTRLB_SPDCONF_MASK (3 << USBHOST_CTRLB_SPDCONF_SHIFT) +# define USBHOST_CTRLB_SPDCONF_LF (0 << USBHOST_CTRLB_SPDCONF_SHIFT) /* Low and full capable */ +#define USBHOST_CTRLB_SOFE (1 << 8) /* Bit 8: Start of frame generation enable */ +#define USBHOST_CTRLB_BUSRESET (1 << 9) /* Bit 9: Send USB reset */ +#define USBHOST_CTRLB_VBUSOK (1 << 10) /* Bit 10: VBUS is OK */ +#define USBHOST_CTRLB_L1RESUME (1 << 11) /* Bit 11: Send USB L1 resume */ + +/* Host Start-of-Frame Control Register */ + +#define USBHOST_HSOFC_FLENC_SHIFT (0) /* Bits 0-3: Frame length control */ +#define USBHOST_HSOFC_FLENC_MASK (15 << USBHOST_HSOFC_FLENC_SHIFT) +# define USBHOST_HSOFC_FLENC(n) ((uint8_t)(n) << USBHOST_HSOFC_FLENC_SHIFT) +#define USBHOST_HSOFC_FLENCE (1 << 7) /* Bit 7: Frame length control enable */ + +/* Status Register */ + +#define USBHOST_STATUS_SPEED_SHIFT (2) /* Bits 2-3: Speed status */ +#define USBHOST_STATUS_SPEED_MASK (3 << USBHOST_STATUS_SPEED_SHIFT) +# define USBHOST_STATUS_SPEED_LOW (0 << USBHOST_STATUS_SPEED_SHIFT) /* Full speed mode */ +# define USBHOST_STATUS_SPEED_FULL (2 << USBHOST_STATUS_SPEED_SHIFT) /* Low speed mode */ +#define USBHOST_STATUS_LNSTATE_SHIFT (6) /* Bits 6-7: USB line status */ +#define USBHOST_STATUS_LNSTATE_MASK (3 << USBHOST_STATUS_LNSTATE_SHIFT) +# define USBHOST_STATUS_LNSTATE_SE0 (0 << USBHOST_STATUS_LNSTATE_SHIFT) /* SE0/RESET */ +# define USBHOST_STATUS_LNSTATE_FJLK (1 << USBHOST_STATUS_LNSTATE_SHIFT) /* FS-J or LS-K */ +# define USBHOST_STATUS_LNSTATE_FKLJ (2 << USBHOST_STATUS_LNSTATE_SHIFT) /* FS-K or :S-L */ + +/* Host Frame Number Register */ + +#define USBHOST_FNUM_SHIFT (3) /* Bits 3-13: Frame number */ +#define USBHOST_FNUM_MASK (0x07ff << USBHOST_FNUM_SHIFT) + +/* Host Frame Length Register (8-bit data) */ + +/* Common definitions for Host Interrupt Enable Clear Register, Host Interrupt Enable + * Set Register, and Host Interrupt Flag Status and Clear Register + */ + +#define USBHOST_INT_HSOF (1 << 2) /* Bit 2: Host start of frame interrupt */ +#define USBHOST_INT_RST (1 << 3) /* Bit 3: Bus reset interrupt */ +#define USBHOST_INT_WAKEUP (1 << 4) /* Bit 4: Wake up interrupt */ +#define USBHOST_INT_DNRSM (1 << 5) /* Bit 5: Downstream resume interrupt */ +#define USBHOST_INT_UPRSM (1 << 6) /* Bit 6: Upstream resume from device interrupt */ +#define USBHOST_INT_RAMACER (1 << 7) /* Bit 7: FAM access interrupt */ +#define USBHOST_INT_DCONN (1 << 8) /* Bit 8: Device connection interrupt */ +#define USBHOST_INT_DDISC (1 << 9) /* Bit 9: Device disconnection interrupt */ + +/* Pipe Interrupt Summary Register */ + +#define USBHOST_PINTSMRY_PIPEINT(n) (1 << (n)) + +/* Host Pipe Configuration Register */ + +#define USBHOST_PCFG_PTOKEN_SHIFT (0) /* Bits 0-1: Pipe token */ +#define USBHOST_PCFG_PTOKEN_MASK (3 << USBHOST_PCFG_PTOKEN_SHIFT) +# define USBHOST_PCFG_PTOKEN_SETUP (0 << USBHOST_PCFG_PTOKEN_SHIFT) +# define USBHOST_PCFG_PTOKEN_IN (1 << USBHOST_PCFG_PTOKEN_SHIFT) +# define USBHOST_PCFG_PTOKEN_OUT (2 << USBHOST_PCFG_PTOKEN_SHIFT) +#define USBHOST_PCFG_BK (1 << 2) /* Bit 2: Pipe bank */ + #define USBHOST_PCFG_BK_SINGLE (0) /* 0=Single bank endpoint */ + #define USBHOST_PCFG_BK_DUAL (1 << 2) /* 1=Dual bank endpoint */ +#define USBHOST_PCFG_PTYPE_SHIFT (3) /* Bits 3-5: Type of pipe */ +#define USBHOST_PCFG_PTYPE_MASK (7 << USBHOST_PCFG_PTYPE_SHIFT) +# define USBHOST_PCFG_PTYPE_DISABLED (0 << USBHOST_PCFG_PTYPE_SHIFT) /* Disabled */ +# define USBHOST_PCFG_PTYPE_CTRL (1 << USBHOST_PCFG_PTYPE_SHIFT) /* Control pipe */ +# define USBHOST_PCFG_PTYPE_ISOC (2 << USBHOST_PCFG_PTYPE_SHIFT) /* Isochronous pipe */ +# define USBHOST_PCFG_PTYPE_BULK (3 << USBHOST_PCFG_PTYPE_SHIFT) /* Bulk pipe */ +# define USBHOST_PCFG_PTYPE_INT (4 << USBHOST_PCFG_PTYPE_SHIFT) /* Interrupt pipe */ +# define USBHOST_PCFG_PTYPE_EXTENDED (5 << USBHOST_PCFG_PTYPE_SHIFT) /* Extended pipe */ + +/* Interval for Bulk-OUT/Ping Transaction Register (8-bit data) */ + +/* Common definitions for Pipe Status Clear Register, Pipe Status Set Register, and + * Pipe Status Register + */ + +#define USBHOST_PSTATUS_DTGL (1 << 0) /* Bit 0: Data toggle sequence */ +#define USBHOST_PSTATUS_CURBK (1 << 2) /* Bit 2: Current bank */ +#define USBHOST_PSTATUS_PFREEZE (1 << 4) /* Bit 4: Pipe freeze */ +#define USBHOST_PSTATUS_BK0RDY (1 << 6) /* Bit 6: Bank 0 ready */ +#define USBHOST_PSTATUS_BK1RDY (1 << 7) /* Bit 7: Bank 1 ready */ + +/* Common definitions for Host Pipe Interrupt Flag Register, Host Pipe Interrupt Clear + * Register, and Host Pipe Interrupt Set Register + * + * REVISIT: Datasheet shows only one bit for TRCPT. But text (and code) use two + * bits, one for IN and one for OUT. Both can't be true. + */ + +#define USBHOST_PINTFLAG_TRCPT0 (1 << 0) /* Bit 0: Transfer complete interrupt 0 */ +#define USBHOST_PINTFLAG_TRCPT1 (1 << 1) /* Bit 1: Transfer complete interrupt 1 */ +#define USBHOST_PINTFLAG_TRFAIL (1 << 2) /* Bit 2: Transfer fail interrupt */ +#define USBHOST_PINTFLAG_PERR (1 << 3) /* Bit 3: Pipe error interrupt */ +#define USBHOST_PINTFLAG_TXSTP (1 << 4) /* Bit 4: Transmitted SETUP interrupt */ +#define USBHOST_PINTFLAG_STALL (1 << 5) /* Bit 5: STALL received interrupt */ + +/* Host Pipe Descriptor */ + +/* Data buffer address (Both banks, 32-bit address) */ + +/* Packet size (Both banks) */ + +#define USBHOST_PKTSIZE_BCNT_SHIFT (8) /* Bits 8-13: Byte count */ +#define USBHOST_PKTSIZE_BCNT_MASK (0x3f << USBHOST_PKTSIZE_BCNT_SHIFT) +# define USBHOST_PKTSIZE_BCNT(n) ((uint32_t)(n) << USBHOST_PKTSIZE_BCNT_SHIFT) +#define USBHOST_PKTSIZE_MPKTSIZE_SHIFT (14) /* Bits 14-27: Multi-packet IN/OUT size */ +#define USBHOST_PKTSIZE_MPKTSIZE_MASK (0x3fff << USBHOST_PKTSIZE_MPKTSIZE_SHIFT) +# define USBHOST_PKTSIZE_MPKTSIZE(n) ((uint32_t)(n) << USBHOST_PKTSIZE_MPKTSIZE_SHIFT) +#define USBHOST_PKTSIZE_SIZE_SHIFT (28) /* Bits 28-30: Pipe size */ +#define USBHOST_PKTSIZE_SIZE_MASK (7 << USBHOST_PKTSIZE_SIZE_SHIFT) +# define USBHOST_PKTSIZE_SIZE_8B (0 << USBHOST_PKTSIZE_SIZE_SHIFT) /* 8 bytes */ +# define USBHOST_PKTSIZE_SIZE_16B (1 << USBHOST_PKTSIZE_SIZE_SHIFT) /* 16 bytes */ +# define USBHOST_PKTSIZE_SIZE_32B (2 << USBHOST_PKTSIZE_SIZE_SHIFT) /* 32 bytes */ +# define USBHOST_PKTSIZE_SIZE_64B (3 << USBHOST_PKTSIZE_SIZE_SHIFT) /* 64 bytes */ +# define USBHOST_PKTSIZE_SIZE_128B (4 << USBHOST_PKTSIZE_SIZE_SHIFT) /* 128 bytes (isoc only) */ +# define USBHOST_PKTSIZE_SIZE_256B (5 << USBHOST_PKTSIZE_SIZE_SHIFT) /* 256 bytes (isoc only) */ +# define USBHOST_PKTSIZE_SIZE_512B (6 << USBHOST_PKTSIZE_SIZE_SHIFT) /* 512 bytes (isoc only) */ +# define USBHOST_PKTSIZE_SIZE_1023B (7 << USBHOST_PKTSIZE_SIZE_SHIFT) /* 1023 bytes (isoc only) */ +#define USBHOST_PKTSIZE_AUTOZLP (1 << 31) /* Bit 31: Automatic zero length packet */ + +/* Extended register (Bank 0 only) */ + +#define USBHOST_EXTREG_SUBPID_SHIFT (0) /* Bits 0-3: SUBPID */ +#define USBHOST_EXTREG_SUBPID_MASK (15 << USBHOST_EXTREG_SUBPID_SHIFT) +# define USBHOST_EXTREG_SUBPID(n) ((uint16_t)(n) << USBHOST_EXTREG_SUBPID_SHIFT) +#define USBHOST_EXTREG_VARIABLE_SHIFT (4) /* Bits 4-14: VARIABLE */ +#define USBHOST_EXTREG_VARIABLE_MASK (0x7ff << USBHOST_EXTREG_VARIABLE_SHIFT) +# define USBHOST_EXTREG_VARIABLE(n) ((uint16_t)(n) << USBHOST_EXTREG_VARIABLE_SHIFT) + +/* host status bank (Both banks) */ + +#define USBHOST_STATUSBK_CRCERR (1 << 0) /* Bit 0: CRC error */ +#define USBHOST_STATUSBK_ERRORFLOW (1 << 1) /* Bit 1: Error flow status */ + +/* Host control pipe (Bank 0 only) */ + +#define USBHOST_CTRLPIPE_PDADDR_SHIFT (0) /* Bits 0-6: Pipe device address */ +#define USBHOST_CTRLPIPE_PDADDR_MASK (0x7f << USBHOST_CTRLPIPE_PDADDR_SHIFT) +# define USBHOST_CTRLPIPE_PDADDR(n) ((uint16_t)(n) << USBHOST_CTRLPIPE_PDADDR_SHIFT) +#define USBHOST_CTRLPIPE_PEPNUM_SHIFT (8) /* Bits 8-11: Pipe endpoint number */ +#define USBHOST_CTRLPIPE_PEPNUM_MASK (15 << USBHOST_CTRLPIPE_PEPNUM_SHIFTxx) +# define USBHOST_CTRLPIPE_PEPNUM(n) ((uint16_t)(n) << USBHOST_CTRLPIPE_PEPNUM_SHIFTxx) +#define USBHOST_CTRLPIPE_PEPMAX_SHIFT (12) /* Bitx 12-15: Pipe error max number */ +#define USBHOST_CTRLPIPE_PEPMAX_MASK (15 << USBHOST_CTRLPIPE_PEPMAX_SHIFT) +# define USBHOST_CTRLPIPE_PEPMAX(n) ((uint16_t)(n) << USBHOST_CTRLPIPE_PEPMAX_SHIFT) + +/* Host status pipe (Both banks) */ + +#define USBHOST_STATUSPIPE_DTGLER (1 << 0) /* Bit 0: Data toggle error */ +#define USBHOST_STATUSPIPE_DAPIDER (1 << 1) /* Bit 1: Data PID error */ +#define USBHOST_STATUSPIPE_PIDER (1 << 2) /* Bit 2: PID error */ +#define USBHOST_STATUSPIPE_TOUTER (1 << 3) /* Bit 3: Timeout error */ +#define USBHOST_STATUSPIPE_CRC16ER (1 << 4) /* Bit 4: CRC16 error */ +#define USBHOST_STATUSPIPE_ERCNT_SHIFT (5) /* Bits 5-7: Pipe error counter */ +#define USBHOST_STATUSPIPE_ERCNT_MASK (7 << USBHOST_STATUSPIPE_ERCNT_SHIFT) +# define USBHOST_STATUSPIPE_ERCNT(n) ((uint16_t)(n) << USBHOST_STATUSPIPE_ERCNT_SHIFT) + +/******************************************************************************************** + * Public Types + ********************************************************************************************/ + +/* Device Endpoint Descriptor. See USBDEV_* bit definitions above. */ + +struct usbdev_epdesc_s +{ + uint32_t addr; /* 0x0000-0x0003: Address of Data buffer (Both banks) */ + uint32_t pktsize; /* 0x0004-0x0007: Packet Size (Both banks) */ + uint16_t extreg; /* 0x0008-0x0009: Extended Register (Bank 0 only) */ + uint8_t stausbk; /* 0x000a-0x000a: Device Status Bank (Both banks) */ + uint8_t reserved[5]; /* 0x000b-0x000f: Both banks */ +}; + +/* Host Pipe Descriptor. See USBHOST_* bit definitions above */ + +struct usbhost_pipedesc_s +{ + uint32_t addr; /* 0x0000-0x0003: Address of Data buffer (Both banks) */ + uint32_t pktsize; /* 0x0004-0x0007: Packet Size (Both banks) */ + uint16_t extreg; /* 0x0008-0x0009: Extended Register (Bank 0 only) */ + uint8_t stausbk; /* 0x000a-0x000a: Device Status Bank (Both banks) */ + uint8_t reserved; /* 0x000b-0x000b: Both banks */ + uint16_t ctrlpipe; /* 0x000c-0x000d: Host control pipe (Bank 0 only) */ + uint16_t statuspipe; /* 0x000e-0x000f: Host status pipe (Both banks) */ +}; + +/******************************************************************************************** + * Public Data + ********************************************************************************************/ + +/******************************************************************************************** + * Public Functions + ********************************************************************************************/ + +#endif /* __ARCH_ARM_SRC_SAMD5E5_CHIP_SAM_USB_H */ diff --git a/arch/arm/src/samd5e5/chip/samd5e5_memorymap.h b/arch/arm/src/samd5e5/chip/samd5e5_memorymap.h index de822f5a548..6921e2e6311 100644 --- a/arch/arm/src/samd5e5/chip/samd5e5_memorymap.h +++ b/arch/arm/src/samd5e5/chip/samd5e5_memorymap.h @@ -66,6 +66,15 @@ # define SAM_SCS_BASE 0xe000e000 /* SCSS */ # define SAM_ROMTAB_BASE 0xe000ff00 /* ROM table */ +/* NVM area */ + +#define SAM_NVM_CALIBAREA 0x00800080 /* NVM software calibration area */ +#define SAM_NVM_USERPAGE 0x00804000 /* NVM user page (512b) */ +#define SAM_NVM_SNWORD0 0x008061fc /* Serial number word 0 */ +#define SAM_NVM_SNWORD1 0x00806010 /* Serial number word 1 */ +#define SAM_NVM_SNWORD2 0x00806014 /* Serial number word 2 */ +#define SAM_NVM_SNWORD3 0x00806018 /* Serial number word 3 */ + /* AHB-APB Bridge A */ #define SAM_PAC_BASE 0x40000000 /* Peripheral Access Controller 0 */ diff --git a/arch/arm/src/samd5e5/sam_usb.c b/arch/arm/src/samd5e5/sam_usb.c new file mode 100644 index 00000000000..983feb761f4 --- /dev/null +++ b/arch/arm/src/samd5e5/sam_usb.c @@ -0,0 +1,4067 @@ +/***************************************************************************** + * arch/arm/src/samd5e5/sam_usb.h + * + * Copyright (C) 2015 Filament - www.filament.com + * Copyright (C) 2015 Offcode Ltd. All rights reserved. + * Author: Janne Rosberg + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt (SAMD5E5 port) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * USB core features: + * + * - Compatible with the USB 2.1 specification + * - USB Embedded Host and Device mode + * - Supports full (12Mbit/s) and low (1.5Mbit/s) speed communication + * - Supports Link Power Management (LPM-L1) protocol + * - On-chip transceivers with built-in pull-ups and pull-downs + * - On-Chip USB serial resistors + * - 1kHz SOF clock available on external pin + * + * Device mode + * - Supports 8 IN endpoints and 8 OUT endpoints + * – No endpoint size limitations + * – Built-in DMA with multi-packet and dual bank for all endpoints + * – Supports feedback endpoint + * – Supports crystal less clock + * + * Host mode + * - Supports 8 physical pipes + * – No pipe size limitations + * – Supports multiplexed virtual pipe on one physical pipe to allow an + * unlimited USB tree + * – Built-in DMA with multi-packet support and dual bank for all pipes + * – Supports feedback endpoint + * – Supports the USB 2.0 Phase-locked SOFs feature + * + ****************************************************************************/ + +/**************************************************************************** + * WIP NOTES: + * + * DS 38.1 (859) + * To maximize throughput, an endpoint can be configured for ping-pong + * operation. + * When this is done the input and output endpoint with the same address are + * used in the same direction. The CPU or DMA Controller can then read/write + * one data buffer while the USB module writes/reads from the other buffer. + * This gives double buffered communication. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "up_arch.h" +#include "up_internal.h" + +#include "chip.h" +#include "chip/sam_pinmap.h" +#include "chip/sam_fuses.h" +#include "sam_gclk.h" +#include "sam_port.h" +#include "sam_periphclks.h" +#include "sam_usb.h" + +#if defined(CONFIG_USBHOST) && defined(CONFIG_SAMD5E5_USB) +# error USBHOST mode not yet implemented! +#endif + +#if defined(CONFIG_USBDEV) && defined(CONFIG_SAMD5E5_USB) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifndef CONFIG_USBDEV_EP0_MAXSIZE +# define CONFIG_USBDEV_EP0_MAXSIZE 64 +#endif + +/* Extremely detailed register debug that you would normally never want + * enabled. + */ + +#ifndef CONFIG_DEBUG_USB +# undef CONFIG_SAMD5E5_USB_REGDEBUG +#endif + +/* Driver Definitions *******************************************************/ + +#define EP0 (0) +#define SAM_EPSET_ALL (0xff) /* All endpoints */ +#define SAM_EPSET_NOTEP0 (0xfe) /* All endpoints except EP0 */ +#define SAM_EP_BIT(ep) (1 << (ep)) +#define SAM_EP0_MAXPACKET (CONFIG_USBDEV_EP0_MAXSIZE) /* EP0 Max. packet size */ +#define SAM_MAX_MULTIPACKET_SIZE (0x3fff) + +/* Request queue operations *************************************************/ + +#define sam_rqempty(q) ((q)->head == NULL) +#define sam_rqpeek(q) ((q)->head) + +/* USB trace ****************************************************************/ +/* Trace error codes */ + +#define SAM_TRACEERR_ALLOCFAIL 0x0001 +#define SAM_TRACEERR_BADCLEARFEATURE 0x0002 +#define SAM_TRACEERR_BADDEVGETSTATUS 0x0003 +#define SAM_TRACEERR_BADEPGETSTATUS 0x0004 +#define SAM_TRACEERR_BADEOBSTATE 0x0005 +#define SAM_TRACEERR_BADEPNO 0x0006 +#define SAM_TRACEERR_BADEPTYPE 0x0007 +#define SAM_TRACEERR_BADGETCONFIG 0x0008 +#define SAM_TRACEERR_BADGETSETDESC 0x0009 +#define SAM_TRACEERR_BADGETSTATUS 0x000a +#define SAM_TRACEERR_BADSETADDRESS 0x000b +#define SAM_TRACEERR_BADSETCONFIG 0x000c +#define SAM_TRACEERR_BADSETFEATURE 0x000d +#define SAM_TRACEERR_BINDFAILED 0x000e +#define SAM_TRACEERR_DISPATCHSTALL 0x000f +#define SAM_TRACEERR_DRIVER 0x0010 +#define SAM_TRACEERR_DRIVERREGISTERED 0x0011 +#define SAM_TRACEERR_EP0SETUPOUTSIZE 0x0012 +#define SAM_TRACEERR_EP0SETUPSTALLED 0x0013 +#define SAM_TRACEERR_EPOUTNULLPACKET 0x0014 +#define SAM_TRACEERR_EPRESERVE 0x0015 +#define SAM_TRACEERR_INVALIDCTRLREQ 0x0016 +#define SAM_TRACEERR_INVALIDPARMS 0x0017 +#define SAM_TRACEERR_IRQREGISTRATION 0x0018 +#define SAM_TRACEERR_NOTCONFIGURED 0x0019 +#define SAM_TRACEERR_REQABORTED 0x001a +#define SAM_TRACEERR_RXDATABKERR 0x001b +#define SAM_TRACEERR_TXCOMPERR 0x001c +#define SAM_TRACEERR_UNSUPPEPTYPE 0x001d + +/* Trace interrupt codes */ + +#define SAM_TRACEINTID_INTERRUPT 0x0001 +#define SAM_TRACEINTID_PENDING 0x0002 +#define SAM_TRACEINTID_PENDING_EP 0x0003 +#define SAM_TRACEINTID_SUSPEND 0x0004 +#define SAM_TRACEINTID_SOF 0x0005 +#define SAM_TRACEINTID_EORST 0x0006 +#define SAM_TRACEINTID_WAKEUP 0x0007 +#define SAM_TRACEINTID_EORSM 0x0008 +#define SAM_TRACEINTID_UPRSM 0x0009 +#define SAM_TRACEINTID_RAMACER 0x000a +#define SAM_TRACEINTID_LPMNYET 0x000b +#define SAM_TRACEINTID_LPMSUSP 0x000c +#define SAM_TRACEINTID_EPNO 0x000d +#define SAM_TRACEINTID_EPINTFLAGS 0x000e +#define SAM_TRACEINTID_EPTRCPT0 0x000f +#define SAM_TRACEINTID_EPTRCPT1 0x0010 +#define SAM_TRACEINTID_EPTRFAIL0 0x0011 +#define SAM_TRACEINTID_EPTRFAIL1 0x0012 +#define SAM_TRACEINTID_EPRXSTP 0x0013 +#define SAM_TRACEINTID_EPSTALL0 0x0014 +#define SAM_TRACEINTID_EPSTALL1 0x0015 +#define SAM_TRACEINTID_EPINQEMPTY 0x0016 +#define SAM_TRACEINTID_EPOUTQEMPTY 0x0017 +#define SAM_TRACEINTID_EP0SETUPOUT 0x0018 +#define SAM_TRACEINTID_EP0SETUPIN 0x0019 +#define SAM_TRACEINTID_EP0SETUPSETADDRESS 0x001a +#define SAM_TRACEINTID_NOSTDREQ 0x001b +#define SAM_TRACEINTID_GETSTATUS 0x001c +#define SAM_TRACEINTID_DEVGETSTATUS 0x001d +#define SAM_TRACEINTID_IFGETSTATUS 0x001e +#define SAM_TRACEINTID_CLEARFEATURE 0x001f +#define SAM_TRACEINTID_SETFEATURE 0x0020 +#define SAM_TRACEINTID_GETSETDESC 0x0021 +#define SAM_TRACEINTID_GETCONFIG 0x0022 +#define SAM_TRACEINTID_SETCONFIG 0x0023 +#define SAM_TRACEINTID_GETSETIF 0x0024 +#define SAM_TRACEINTID_SYNCHFRAME 0x0025 +#define SAM_TRACEINTID_DISPATCH 0x0026 +#define SAM_TRACEINTID_ADDRESSED 0x0027 +#define SAM_TRACEINTID_EPCONF 0x0028 +#define SAM_TRACEINTID_EPINTEN 0x0029 +#define SAM_TRACEINTID_EP0WRSTATUS 0x002a +#define SAM_TRACEINTID_EPTRCPT0_LEN 0x002b + +/* Ever-present MIN and MAX macros */ + +#ifndef MIN +# define MIN(a,b) (a < b ? a : b) +#endif + +#ifndef MAX +# define MAX(a,b) (a > b ? a : b) +#endif + +/* Byte ordering in host-based values */ + +#ifdef CONFIG_ENDIAN_BIG +# define LSB 1 +# define MSB 0 +#else +# define LSB 0 +# define MSB 1 +#endif + +/**************************************************************************** + * Private Type Definitions + ****************************************************************************/ + +/* State of an endpoint */ + +enum sam_epstate_e +{ + /* --- All Endpoints --- */ + USB_EPSTATE_DISABLED = 0, /* Endpoint is disabled */ + USB_EPSTATE_STALLED, /* Endpoint is stalled */ + USB_EPSTATE_IDLE, /* Endpoint is idle (i.e. ready for transmission) */ + USB_EPSTATE_SENDING, /* Endpoint is sending data */ + USB_EPSTATE_RXSTOPPED, /* OUT endpoint is stopped waiting for a read request */ + /* --- Endpoint 0 Only --- */ + USB_EPSTATE_EP0DATAOUT, /* Endpoint 0 is receiving SETUP OUT data */ + USB_EPSTATE_EP0STATUSIN, /* Endpoint 0 is sending SETUP status */ + USB_EPSTATE_EP0ADDRESS /* Address change is pending completion of status */ +}; + +/* The overall state of the device */ + +enum sam_devstate_e +{ + USB_DEVSTATE_SUSPENDED = 0, /* The device is currently suspended */ + USB_DEVSTATE_POWERED, /* Host is providing +5V through the USB cable */ + USB_DEVSTATE_DEFAULT, /* Device has been reset */ + USB_DEVSTATE_ADDRESSED, /* The device has been given an address on the bus */ + USB_DEVSTATE_CONFIGURED /* A valid configuration has been selected. */ +}; + +/* The result of EP0 SETUP processing */ + +enum sam_ep0setup_e +{ + USB_EP0SETUP_SUCCESS = 0, /* The SETUP was handle without incident */ + USB_EP0SETUP_DISPATCHED, /* The SETUP was forwarded to the class driver */ + USB_EP0SETUP_ADDRESS, /* A new device address is pending */ + USB_EP0SETUP_STALL /* An error occurred */ +}; + +union wb_u +{ + uint16_t w; + uint8_t b[2]; +}; + +/* A container for a request so that the request may be retained in a list */ + +struct sam_req_s +{ + struct usbdev_req_s req; /* Standard USB request */ + struct sam_req_s *flink; /* Supports a singly linked list */ + uint16_t inflight; /* Number of TX bytes tansmitting or + * number of RX bytes we are waiting */ +}; + +/* The head of a queue of requests */ + +struct sam_rqhead_s +{ + struct sam_req_s *head; /* Requests are added to the head of the list */ + struct sam_req_s *tail; /* Requests are removed from the tail of the list */ +}; + +/* This is the internal representation of an endpoint */ + +struct sam_ep_s +{ + /* Common endpoint fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbdev_ep_s + * to struct sam_ep_s. + */ + + struct usbdev_ep_s ep; /* Standard endpoint structure */ + + /* SAMD5E5-specific fields */ + + struct sam_usbdev_s *dev; /* Reference to private driver data */ + struct sam_rqhead_s reqq; /* Read/write request queue */ + struct sam_rqhead_s pendq; /* Write requests pending stall sent */ + struct usbdev_epdesc_s *descb[2]; /* Pointers to this endpoints descriptors */ + volatile uint8_t epstate; /* State of the endpoint (see enum sam_epstate_e) */ + uint8_t stalled:1; /* true: Endpoint is stalled */ + uint8_t pending:1; /* true: IN Endpoint stall is pending */ + uint8_t halted:1; /* true: Endpoint feature halted */ + uint8_t zlpsent:1; /* Zero length packet has been sent */ + uint8_t txbusy:1; /* Write request queue is busy (recursion avoidance kludge) */ + uint8_t rxactive:1; /* read request is active (for top of queue) */ +}; + +struct sam_usbdev_s +{ + /* Common device fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbdev_s + * to struct sam_usbdev_s. + */ + + struct usbdev_s usbdev; + + /* The bound device class driver */ + + struct usbdevclass_driver_s *driver; + + /* USB-specific fields */ + + struct usb_ctrlreq_s ctrl; /* Last EP0 request */ + uint8_t devstate; /* State of the device (see enum sam_devstate_e) */ + uint8_t prevstate; /* Previous state of the device before SUSPEND */ + uint8_t devaddr; /* Assigned device address */ + uint8_t selfpowered:1; /* 1: Device is self powered */ + uint16_t epavail; /* Bitset of available endpoints */ + + /* The endpoint list */ + + struct sam_ep_s eplist[SAM_USB_NENDPOINTS]; + + /* Endpoint descriptors 2 banks for each endpoint */ + + struct usbdev_epdesc_s ep_descriptors[SAM_USB_NENDPOINTS * 2]; + + /* EP0 data buffer. For data that is included in an EP0 SETUP OUT + * transaction. In this case, no request is in place from the class + * driver and the incoming data is caught in this buffer. The size + * of valid data in the buffer is given by ctrlreg.len[]. For the + * case of EP0 SETUP IN transaction, the normal request mechanism is + * used and the class driver provides the buffering. + */ + + uint8_t ep0out[SAM_EP0_MAXPACKET]; +}; + + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register operations ******************************************************/ + +#ifdef CONFIG_SAMD5E5_USB_REGDEBUG +static void sam_printreg(uintptr_t regaddr, uint32_t regval, bool iswrite); +static void sam_checkreg(uintptr_t regaddr, uint32_t regval, bool iswrite); +static uint32_t sam_getreg32(uintptr_t regaddr); +static void sam_putreg32(uint32_t regval, uintptr_t regaddr); +static uint32_t sam_getreg16(uintptr_t regaddr); +static void sam_putreg16(uint16_t regval, uintptr_t regaddr); +static uint32_t sam_getreg8(uintptr_t regaddr); +static void sam_putreg8(uint8_t regval, uintptr_t regaddr); +static void sam_dumpep(struct sam_usbdev_s *priv, uint8_t epno); +#else +static inline uint32_t sam_getreg32(uintptr_t regaddr); +static inline void sam_putreg32(uint32_t regval, uintptr_t regaddr); +static inline uint32_t sam_getreg16(uintptr_t regaddr); +static inline void sam_putreg16(uint16_t regval, uintptr_t regaddr); +static inline uint32_t sam_getreg8(uintptr_t regaddr); +static inline void sam_putreg8(uint8_t regval, uintptr_t regaddr); +# define sam_dumpep(priv,epno) +#endif + +/* Suspend/Resume Helpers ***************************************************/ + +static void sam_suspend(struct sam_usbdev_s *priv); +static void sam_resume(struct sam_usbdev_s *priv); + +/* Request Helpers **********************************************************/ + +static struct sam_req_s * + sam_req_dequeue(struct sam_rqhead_s *queue); +static void sam_req_enqueue(struct sam_rqhead_s *queue, + struct sam_req_s *req); +static void sam_req_complete(struct sam_ep_s *privep, int16_t result); +static void sam_req_wrsetup(struct sam_usbdev_s *priv, + struct sam_ep_s *privep, struct sam_req_s *privreq); +static int sam_req_write(struct sam_usbdev_s *priv, + struct sam_ep_s *privep); +static int sam_req_read(struct sam_usbdev_s *priv, + struct sam_ep_s *privep, uint16_t recvsize); +static void sam_req_cancel(struct sam_ep_s *privep, int16_t status); + + +/* Interrupt level processing ***********************************************/ + +static void sam_ep0_ctrlread(struct sam_usbdev_s *priv); +static void sam_ep0_wrstatus(struct sam_usbdev_s *priv, + const uint8_t *buffer, size_t buflen); +static void sam_ep0_dispatch(struct sam_usbdev_s *priv); +static void sam_setdevaddr(struct sam_usbdev_s *priv, uint8_t value); +static void sam_ep0_setup(struct sam_usbdev_s *priv); +static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno); +static int sam_usb_interrupt(int irq, void *context, void *arg); + +/* Endpoint helpers *********************************************************/ + +static void sam_ep_reset(struct sam_usbdev_s *priv, uint8_t epno); +static void sam_epset_reset(struct sam_usbdev_s *priv, uint16_t epset); +static int sam_ep_stall(struct sam_ep_s *privep); +static int sam_ep_resume(struct sam_ep_s *privep); +static inline struct sam_ep_s * + sam_ep_reserve(struct sam_usbdev_s *priv, uint8_t epset); +static inline void + sam_ep_unreserve(struct sam_usbdev_s *priv, + struct sam_ep_s *privep); +static inline bool + sam_ep_reserved(struct sam_usbdev_s *priv, int epno); +static int sam_ep_configure_internal(struct sam_ep_s *privep, + const struct usb_epdesc_s *desc); + +/* Endpoint operations ******************************************************/ + +static int sam_ep_configure(struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, bool last); +static int sam_ep_disable(struct usbdev_ep_s *ep); +static struct usbdev_req_s * + sam_ep_allocreq(struct usbdev_ep_s *ep); +#ifdef CONFIG_USBDEV_DMA +static void *sam_ep_allocbuffer(struct usbdev_ep_s *ep, uint16_t nbytes); +static void sam_ep_freebuffer(struct usbdev_ep_s *ep, void *buf); +#endif +static void sam_ep_freereq(struct usbdev_ep_s *ep, + struct usbdev_req_s *); +static int sam_ep_submit(struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int sam_ep_cancel(struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int sam_ep_stallresume(struct usbdev_ep_s *ep, bool resume); + +/* USB device controller operations *****************************************/ + +static struct usbdev_ep_s * + sam_allocep(struct usbdev_s *dev, uint8_t epno, bool in, + uint8_t eptype); +static void sam_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep); +static int sam_getframe(struct usbdev_s *dev); +static int sam_wakeup(struct usbdev_s *dev); +static int sam_selfpowered(struct usbdev_s *dev, bool selfpowered); +static int sam_pullup(struct usbdev_s *dev, bool enable); + +/* Initialization/Reset *****************************************************/ + +static void sam_reset(struct sam_usbdev_s *priv); +static void sam_enableclks(void); +static void sam_disableclks(void); +static void sam_hw_setup(struct sam_usbdev_s *priv); +static void sam_sw_setup(struct sam_usbdev_s *priv); +static void sam_hw_shutdown(struct sam_usbdev_s *priv); +static void sam_sw_shutdown(struct sam_usbdev_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Since there is only a single USB interface, all status information can be + * be simply retained in a single global instance. + */ + +static struct sam_usbdev_s g_usbd; + +static const struct usbdev_epops_s g_epops = +{ + .configure = sam_ep_configure, + .disable = sam_ep_disable, + .allocreq = sam_ep_allocreq, + .freereq = sam_ep_freereq, +#ifdef CONFIG_USBDEV_DMA + .allocbuffer = sam_ep_allocbuffer, + .freebuffer = sam_ep_freebuffer, +#endif + .submit = sam_ep_submit, + .cancel = sam_ep_cancel, + .stall = sam_ep_stallresume, +}; + +static const struct usbdev_ops_s g_devops = +{ + .allocep = sam_allocep, + .freeep = sam_freeep, + .getframe = sam_getframe, + .wakeup = sam_wakeup, + .selfpowered = sam_selfpowered, + .pullup = sam_pullup, +}; + +/* This describes endpoint 0 */ + +static const struct usb_epdesc_s g_ep0desc = +{ + .len = USB_SIZEOF_EPDESC, + .type = USB_DESC_TYPE_ENDPOINT, + .addr = EP0, + .attr = USB_EP_ATTR_XFER_CONTROL, + .mxpacketsize = {64, 0}, + .interval = 0 +}; + +/* Device error strings that may be enabled for more desciptive USB trace + * output. + */ + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_deverror[] = +{ + TRACE_STR(SAM_TRACEERR_ALLOCFAIL), + TRACE_STR(SAM_TRACEERR_BADCLEARFEATURE), + TRACE_STR(SAM_TRACEERR_BADDEVGETSTATUS), + TRACE_STR(SAM_TRACEERR_BADEPGETSTATUS), + TRACE_STR(SAM_TRACEERR_BADEOBSTATE), + TRACE_STR(SAM_TRACEERR_BADEPNO), + TRACE_STR(SAM_TRACEERR_BADEPTYPE), + TRACE_STR(SAM_TRACEERR_BADGETCONFIG), + TRACE_STR(SAM_TRACEERR_BADGETSETDESC), + TRACE_STR(SAM_TRACEERR_BADGETSTATUS), + TRACE_STR(SAM_TRACEERR_BADSETADDRESS), + TRACE_STR(SAM_TRACEERR_BADSETCONFIG), + TRACE_STR(SAM_TRACEERR_BADSETFEATURE), + TRACE_STR(SAM_TRACEERR_BINDFAILED), + TRACE_STR(SAM_TRACEERR_DISPATCHSTALL), + TRACE_STR(SAM_TRACEERR_DRIVER), + TRACE_STR(SAM_TRACEERR_DRIVERREGISTERED), + TRACE_STR(SAM_TRACEERR_EP0SETUPOUTSIZE), + TRACE_STR(SAM_TRACEERR_EP0SETUPSTALLED), + TRACE_STR(SAM_TRACEERR_EPOUTNULLPACKET), + TRACE_STR(SAM_TRACEERR_EPRESERVE), + TRACE_STR(SAM_TRACEERR_INVALIDCTRLREQ), + TRACE_STR(SAM_TRACEERR_INVALIDPARMS), + TRACE_STR(SAM_TRACEERR_IRQREGISTRATION), + TRACE_STR(SAM_TRACEERR_NOTCONFIGURED), + TRACE_STR(SAM_TRACEERR_REQABORTED), + TRACE_STR(SAM_TRACEERR_RXDATABKERR), + TRACE_STR(SAM_TRACEERR_TXCOMPERR), + TRACE_STR(SAM_TRACEERR_UNSUPPEPTYPE), + TRACE_STR_END +}; +#endif + +/* Interrupt event strings that may be enabled for more desciptive USB trace + * output. + */ + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_intdecode[] = +{ + TRACE_STR(SAM_TRACEINTID_INTERRUPT), + TRACE_STR(SAM_TRACEINTID_PENDING), + TRACE_STR(SAM_TRACEINTID_PENDING_EP), + TRACE_STR(SAM_TRACEINTID_SUSPEND), + TRACE_STR(SAM_TRACEINTID_SOF), + TRACE_STR(SAM_TRACEINTID_EORST), + TRACE_STR(SAM_TRACEINTID_WAKEUP), + TRACE_STR(SAM_TRACEINTID_EORSM), + TRACE_STR(SAM_TRACEINTID_UPRSM), + TRACE_STR(SAM_TRACEINTID_RAMACER), + TRACE_STR(SAM_TRACEINTID_LPMNYET), + TRACE_STR(SAM_TRACEINTID_LPMSUSP), + + TRACE_STR(SAM_TRACEINTID_EPNO), + TRACE_STR(SAM_TRACEINTID_EPINTFLAGS), + TRACE_STR(SAM_TRACEINTID_EPTRCPT0), + TRACE_STR(SAM_TRACEINTID_EPTRCPT1), + TRACE_STR(SAM_TRACEINTID_EPTRFAIL0), + TRACE_STR(SAM_TRACEINTID_EPTRFAIL1), + TRACE_STR(SAM_TRACEINTID_EPRXSTP), + TRACE_STR(SAM_TRACEINTID_EPSTALL0), + TRACE_STR(SAM_TRACEINTID_EPSTALL1), + + TRACE_STR(SAM_TRACEINTID_EPINQEMPTY), + TRACE_STR(SAM_TRACEINTID_EPOUTQEMPTY), + TRACE_STR(SAM_TRACEINTID_EP0SETUPOUT), + TRACE_STR(SAM_TRACEINTID_EP0SETUPIN), + TRACE_STR(SAM_TRACEINTID_EP0SETUPSETADDRESS), + TRACE_STR(SAM_TRACEINTID_NOSTDREQ), + TRACE_STR(SAM_TRACEINTID_GETSTATUS), + TRACE_STR(SAM_TRACEINTID_DEVGETSTATUS), + TRACE_STR(SAM_TRACEINTID_IFGETSTATUS), + TRACE_STR(SAM_TRACEINTID_CLEARFEATURE), + TRACE_STR(SAM_TRACEINTID_SETFEATURE), + TRACE_STR(SAM_TRACEINTID_GETSETDESC), + TRACE_STR(SAM_TRACEINTID_GETCONFIG), + TRACE_STR(SAM_TRACEINTID_SETCONFIG), + TRACE_STR(SAM_TRACEINTID_GETSETIF), + TRACE_STR(SAM_TRACEINTID_SYNCHFRAME), + + TRACE_STR(SAM_TRACEINTID_DISPATCH), + TRACE_STR(SAM_TRACEINTID_ADDRESSED), + TRACE_STR(SAM_TRACEINTID_EPCONF), + TRACE_STR(SAM_TRACEINTID_EPINTEN), + TRACE_STR(SAM_TRACEINTID_EP0WRSTATUS), + TRACE_STR(SAM_TRACEINTID_EPTRCPT0_LEN), + + TRACE_STR_END +}; +#endif + +/**************************************************************************** + * Private Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Register Operations + ****************************************************************************/ + +/******************************************************************************* + * Name: sam_printreg + * + * Description: + * Print the SAMD5E5 USB register access + * + *******************************************************************************/ + +#ifdef CONFIG_SAMD5E5_USB_REGDEBUG +static void sam_printreg(uintptr_t regaddr, uint32_t regval, bool iswrite) +{ + uinfo("%p%s%08x\n", regaddr, iswrite ? "<-" : "->", regval); +} +#endif + +/******************************************************************************* + * Name: sam_checkreg + * + * Description: + * Check if it is time to output debug information for accesses to a SAMD5E5 + * USB registers + * + *******************************************************************************/ + +#ifdef CONFIG_SAMD5E5_USB_REGDEBUG +static void sam_checkreg(uintptr_t regaddr, uint32_t regval, bool iswrite) +{ + static uintptr_t prevaddr = 0; + static uint32_t preval = 0; + static uint32_t count = 0; + static bool prevwrite = false; + + /* Is this the same value that we read from/wrote to the same register last time? + * Are we polling the register? If so, suppress the output. + */ + + if (regaddr == prevaddr && regval == preval && prevwrite == iswrite) + { + /* Yes.. Just increment the count */ + + count++; + } + else + { + /* No this is a new address or value or operation. Were there any + * duplicate accesses before this one? + */ + + if (count > 0) + { + /* Yes.. Just one? */ + + if (count == 1) + { + /* Yes.. Just one */ + + sam_printreg(prevaddr, preval, prevwrite); + } + else + { + /* No.. More than one. */ + + uinfo("[repeats %d more times]\n", count); + } + } + + /* Save the new address, value, count, and operation for next time */ + + prevaddr = regaddr; + preval = regval; + count = 0; + prevwrite = iswrite; + + /* Show the new register access */ + + sam_printreg(regaddr, regval, iswrite); + } +} +#endif + +/**************************************************************************** + * Name: sam_getreg32 + * + * Description: + * Get the contents of an 32-bit SAMD5E5 USB register + * + ****************************************************************************/ + +#ifdef CONFIG_SAMD5E5_USB_REGDEBUG +static uint32_t sam_getreg32(uintptr_t regaddr) +{ + /* Read the value from the register */ + + uint32_t regval = getreg32(regaddr); + + /* Check if we need to print this value */ + + sam_checkreg(regaddr, regval, false); + return regval; +} +#else +static inline uint32_t sam_getreg32(uintptr_t regaddr) +{ + return getreg32(regaddr); +} +#endif + +/**************************************************************************** + * Name: sam_putreg32 + * + * Description: + * Set the contents of an 32-bit SAMD5E5 USB register to a value + * + ****************************************************************************/ + +#ifdef CONFIG_SAMD5E5_USB_REGDEBUG +static void sam_putreg32(uint32_t regval, uintptr_t regaddr) +{ + /* Check if we need to print this value */ + + sam_checkreg(regaddr, regval, true); + + /* Write the value */ + + putreg32(regval, regaddr); +} +#else +static inline void sam_putreg32(uint32_t regval, uint32_t regaddr) +{ + putreg32(regval, regaddr); +} +#endif + +/**************************************************************************** + * Name: sam_getreg16 + * + * Description: + * Get the contents of an 16-bit SAMD5E5 USB register + * + ****************************************************************************/ + +#ifdef CONFIG_SAMD5E5_USB_REGDEBUG +static uint32_t sam_getreg16(uintptr_t regaddr) +{ + /* Read the value from the register */ + + uint32_t regval = getreg16(regaddr); + + /* Check if we need to print this value */ + + sam_checkreg(regaddr, regval, false); + return regval; +} +#else +static inline uint32_t sam_getreg16(uintptr_t regaddr) +{ + return getreg16(regaddr); +} +#endif + +/**************************************************************************** + * Name: sam_putreg16 + * + * Description: + * Set the contents of an 16-bit SAMD5E5 USB register to a value + * + ****************************************************************************/ + +#ifdef CONFIG_SAMD5E5_USB_REGDEBUG +static void sam_putreg16(uint16_t regval, uintptr_t regaddr) +{ + /* Check if we need to print this value */ + + sam_checkreg(regaddr, regval, true); + + /* Write the value */ + + putreg16(regval, regaddr); +} +#else +static inline void sam_putreg16(uint16_t regval, uint32_t regaddr) +{ + putreg16(regval, regaddr); +} +#endif + +/**************************************************************************** + * Name: sam_getreg8 + * + * Description: + * Get the contents of an 8-bit SAMD5E5 USB register + * + ****************************************************************************/ + +#ifdef CONFIG_SAMD5E5_USB_REGDEBUG +static uint32_t sam_getreg8(uintptr_t regaddr) +{ + /* Read the value from the register */ + + uint32_t regval = getreg8(regaddr); + + /* Check if we need to print this value */ + + sam_checkreg(regaddr, regval, false); + return regval; +} +#else +static inline uint32_t sam_getreg8(uintptr_t regaddr) +{ + return getreg8(regaddr); +} +#endif + +/**************************************************************************** + * Name: sam_putreg8 + * + * Description: + * Set the contents of an 8-bit SAMD5E5 USB register to a value + * + ****************************************************************************/ + +#ifdef CONFIG_SAMD5E5_USB_REGDEBUG +static void sam_putreg8(uint8_t regval, uintptr_t regaddr) +{ + /* Check if we need to print this value */ + + sam_checkreg(regaddr, regval, true); + + /* Write the value */ + + putreg8(regval, regaddr); +} +#else +static inline void sam_putreg8(uint8_t regval, uint32_t regaddr) +{ + putreg8(regval, regaddr); +} +#endif + + +/**************************************************************************** + * Name: sam_dumpep + ****************************************************************************/ + +#if defined(CONFIG_SAMD5E5_USB_REGDEBUG) && defined(CONFIG_DEBUG_USB) +static void sam_dumpep(struct sam_usbdev_s *priv, uint8_t epno) +{ + /* Global Registers */ + + uinfo("Global Registers:\n"); + uinfo(" CTRLB: %04x\n", sam_getreg16(SAM_USBDEV_CTRLB)); + uinfo(" FNUM: %04x\n", sam_getreg16(SAM_USBDEV_FNUM)); + uinfo(" DADD: %02x\n", sam_getreg8(SAM_USBDEV_DADD)); + uinfo(" INTEN: %04x\n", sam_getreg16(SAM_USBDEV_INTENSET)); + uinfo(" STATUS: %02x\n", sam_getreg8(SAM_USBDEV_STATUS)); + uinfo(" INTFLAG: %04x\n", sam_getreg16(SAM_USBDEV_INTFLAG)); + uinfo(" EPCFG[%d]: %02x\n", epno, sam_getreg8(SAM_USBDEV_EPCFG(epno))); + uinfo("EPSTATUS[%d]: %02x\n", epno, sam_getreg8(SAM_USBDEV_EPSTATUS(epno))); +} +#endif + +/**************************************************************************** + * Request Helpers + ****************************************************************************/ +/**************************************************************************** + * Name: sam_req_dequeue + ****************************************************************************/ + +static struct sam_req_s *sam_req_dequeue(struct sam_rqhead_s *queue) +{ + struct sam_req_s *ret = queue->head; + + if (ret) + { + queue->head = ret->flink; + if (!queue->head) + { + queue->tail = NULL; + } + + ret->flink = NULL; + } + + return ret; +} + +/**************************************************************************** + * Name: sam_req_enqueue + ****************************************************************************/ + +static void sam_req_enqueue(struct sam_rqhead_s *queue, struct sam_req_s *req) +{ + req->flink = NULL; + if (!queue->head) + { + queue->head = req; + queue->tail = req; + } + else + { + queue->tail->flink = req; + queue->tail = req; + } +} + +/**************************************************************************** + * Name: sam_req_complete + ****************************************************************************/ + +static void sam_req_complete(struct sam_ep_s *privep, int16_t result) +{ + struct sam_req_s *privreq; + irqstate_t flags; + + /* Remove the completed request at the head of the endpoint request list */ + + flags = enter_critical_section(); + privreq = sam_req_dequeue(&privep->reqq); + leave_critical_section(flags); + + if (privreq) + { + /* Save the result in the request structure */ + + privreq->req.result = result; + + /* Callback to the request completion handler */ + + privreq->flink = NULL; + privreq->req.callback(&privep->ep, &privreq->req); + + /* mark endpoint ready to next transmission */ + + privep->epstate = USB_EPSTATE_IDLE; + privep->zlpsent = false; + } +} + +/**************************************************************************** + * Name: sam_req_wrsetup + * + * Description: + * Process the next queued write request. + * + ****************************************************************************/ + +static void sam_req_wrsetup(struct sam_usbdev_s *priv, + struct sam_ep_s *privep, + struct sam_req_s *privreq) +{ + const uint8_t *buf; + uint8_t epno; + int nbytes; + uint32_t packetsize; + + /* Get the unadorned endpoint number */ + + epno = USB_EPNO(privep->ep.eplog); + + /* Get the number of bytes remaining to be sent. */ + + DEBUGASSERT(privreq->req.xfrd < privreq->req.len); + nbytes = privreq->req.len - privreq->req.xfrd; + + /* Either send the maxpacketsize(multi) or all of the remaining data in + * the request. + */ + + if (nbytes >= SAM_MAX_MULTIPACKET_SIZE) + { + nbytes = SAM_MAX_MULTIPACKET_SIZE; + } + + /* This is the new number of bytes "in-flight" */ + + privreq->inflight = nbytes; + usbtrace(TRACE_WRITE(USB_EPNO(privep->ep.eplog)), nbytes); + + /* The new buffer pointer is the start of the buffer plus the number of + * bytes successfully transferred plus the number of bytes previously + * "in-flight". + */ + + buf = privreq->req.buf + privreq->req.xfrd; + + /* setup TX transfer using ep configured maxpacket size */ + + priv->eplist[epno].descb[1]->addr = (uint32_t) buf; + packetsize = priv->eplist[epno].descb[1]->pktsize; + packetsize &= ~USBDEV_PKTSIZE_BCNT_MASK; + packetsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK; + packetsize |= USBDEV_PKTSIZE_BCNT(nbytes); + + /* also set automatic ZLP sending if requested on req */ + + if (privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) + { + packetsize |= USBDEV_PKTSIZE_AUTOZLP; + } + + priv->eplist[epno].descb[1]->pktsize = packetsize; + + /* Indicate that we are in the sending state + * This indication will be need in interrupt processing (TRCPT1) + * in order to properly terminate the request. + */ + + privep->epstate = USB_EPSTATE_SENDING; + + /* Set BK1RDY to notify the USB hardware that TX data is ready on + * descriptor bank1. We will be notified that the descriptor has been + * transmitted by the USB device when TRCPT1 in the endpoint's EPINTFLAG + * register has been set. + */ + + sam_putreg8(USBDEV_EPSTATUS_BK1RDY, SAM_USBDEV_EPSTATUSSET(epno)); +} + +/**************************************************************************** + * Name: sam_req_write + * + * Description: + * Process the next queued write request. This function is called in one + * of three contexts: + * (1) When the endpoint is IDLE and a new write request is submitted + * (with interrupts disabled), + * (2) from TRCPT1 interrupt handling when the current Tx transfer + * completes + * (3) when resuming a stalled IN or control endpoint. + * + * Calling rules: + * + * The transfer state must IDLE + * + * When a request is queued, the request 'len' is the number of bytes + * to transfer and 'xfrd' and 'inflight' must be zero. + * + * When this function starts a transfer it will update the request + * 'inflight' field to indicate the size of the transfer. + * + * When the transfer completes, the 'inflight' field must hold the + * number of bytes that have completed the transfer. This function will + * update 'xfrd' with the new size of the transfer. + * + ****************************************************************************/ + +static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep) +{ + struct sam_req_s *privreq; + uint8_t epno; + int bytesleft; + + /* Get the unadorned endpoint number */ + + epno = USB_EPNO(privep->ep.eplog); + + /* We get here when an IN endpoint interrupt occurs. So now we know that + * there is no TX transfer in progress (epstate should be IDLE). + */ + + DEBUGASSERT(privep->epstate == USB_EPSTATE_IDLE); + while (privep->epstate == USB_EPSTATE_IDLE) + { + /* Check the request from the head of the endpoint request queue */ + + privreq = sam_rqpeek(&privep->reqq); + if (!privreq) + { + /* There is no TX transfer in progress and no new pending TX + * requests to send. + */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINQEMPTY), epno); + + /* Was there a pending endpoint stall? */ + + if (privep->pending) + { + /* Yes... stall the endpoint now */ + + (void)sam_ep_stall(privep); + } + + return -ENOENT; + } + + uinfo("epno=%d req=%p: len=%d xfrd=%d inflight=%d\n", + epno, privreq, privreq->req.len, privreq->req.xfrd, + privreq->inflight); + + /* Handle any bytes in flight. */ + + privreq->req.xfrd += privreq->inflight; + privreq->inflight = 0; + + /* Get the number of bytes left to be sent in the packet */ + + bytesleft = privreq->req.len - privreq->req.xfrd; + if (bytesleft > 0) + { + /* Perform the write operation. epstate will become SENDING. */ + + sam_req_wrsetup(priv, privep, privreq); + } + + /* No data to send... + * This can happen on one of two ways: + * (1) The last packet sent was the final packet of a transfer. + * Or + * (2) called with a request packet that has len == 0 + * + * len == 0 means that it is requested to send a zero length packet + * required by protocoll + */ + + else if ((privreq->req.len == 0) && !privep->zlpsent) + { + /* If we get here, we requested to send the zero length packet now. + */ + + privep->epstate = USB_EPSTATE_SENDING; + privep->zlpsent = true; + privreq->inflight = 0; + + usbtrace(TRACE_WRITE(epno), 0); + + /* setup 0 length TX transfer */ + + priv->eplist[0].descb[1]->addr = (uint32_t) &priv->ep0out[0]; + priv->eplist[0].descb[1]->pktsize &= ~USBDEV_PKTSIZE_BCNT_MASK; + priv->eplist[0].descb[1]->pktsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK; + priv->eplist[0].descb[1]->pktsize |= USBDEV_PKTSIZE_BCNT(0); + sam_putreg8(USBDEV_EPSTATUS_BK1RDY, SAM_USBDEV_EPSTATUSSET(epno)); + } + + /* If all of the bytes were sent (including any final zero length + * packet) then we are finished with the request buffer and we can + * return the request buffer to the class driver. The state will + * remain IDLE only if nothing else was put in flight. + * + * Note that we will then loop to check to check the next queued + * write request. + */ + + if (privep->epstate == USB_EPSTATE_IDLE) + { + /* Return the write request to the class driver. Set the txbusy + * bit to prevent being called recursively from any new submission + * generated by returning the write request. + */ + + usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd); + DEBUGASSERT(privreq->req.len == privreq->req.xfrd); + + privep->txbusy = true; + sam_req_complete(privep, OK); + privep->txbusy = false; + } + } + + return OK; +} + +/**************************************************************************** + * Name: sam_req_read + * + * Description: + * Complete the last read request. full or partial. + * The USB core has transferred the data to user request buffer. + * return the completed read request to the class + * implementation, and try to start the next queued read request. + * + * REVISIT: + * This function is called in one of two contexts: + * The normal case is + * (1) When the endpoint is IDLE and a new read request is submitted + * (with interrupts disabled), + * (2) from interrupt handling when the current RX transfer completes. + * But there is also a special case + * (3) when the OUT endpoint is stopped because there are no + * available read requests. + * + * Calling rules: + * + * The transfer state must IDLE + * + * When a request is queued, the request 'len' is size of the request + * buffer. Any OUT request can be received that will fit in this + * buffer. 'xfrd' and 'inflight' in the request must be zero + * If sam_req_read() is called to start a new transfer, the recvsize + * parameter must be zero. + * + * When the transfer completes, the 'recvsize' is the number of bytes + * ready on req->buffer. + * + ****************************************************************************/ + +static int sam_req_read(struct sam_usbdev_s *priv, struct sam_ep_s *privep, + uint16_t recvsize) +{ + struct sam_req_s *privreq; + uint32_t packetsize; + int epno; + + DEBUGASSERT(priv && privep && privep->epstate == USB_EPSTATE_IDLE); + + /* Check the request from the head of the endpoint request queue */ + + epno = USB_EPNO(privep->ep.eplog); + do + { + /* Peek at the next read request in the request queue */ + + privreq = sam_rqpeek(&privep->reqq); + if (!privreq) + { + /* No read request to receive data */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPOUTQEMPTY), epno); + + /* When no read requests are pending no EP descriptors are set to + * ready. HW sends NAK to host if it tries to send something. + */ + + privep->epstate = USB_EPSTATE_RXSTOPPED; + return -ENOENT; + } + + uinfo("EP%d: req.len=%d xfrd=%d recvsize=%d\n", + epno, privreq->req.len, privreq->req.xfrd, recvsize); + + /* Ignore any attempt to receive a zero length packet */ + + if (privreq->req.len == 0) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EPOUTNULLPACKET), 0); + sam_req_complete(privep, OK); + privreq = NULL; + } + + /* complete read request with available data */ + + if ((privreq->inflight) && (recvsize != 0)) + { + usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), recvsize); + + /* Update the total number of bytes transferred */ + + privreq->req.xfrd += recvsize; + privreq->inflight = 0; + usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd); + sam_req_complete(privep, OK); + + /* need to set recvsize to zero. When calling sam_req_complete() + * class driver could call submit() again and we have new request + * ready on next while() loop. + */ + + privep->rxactive = false; + recvsize = 0; + privreq = NULL; + } + } + while (privreq == NULL); + + DEBUGASSERT(recvsize == 0); + + /* activate new read request from queue */ + + privep->rxactive = true; + privreq->req.xfrd = 0; + privreq->inflight = privreq->req.len; + priv->eplist[epno].descb[0]->addr = (uint32_t) privreq->req.buf; + packetsize = priv->eplist[epno].descb[0]->pktsize; + packetsize &= ~USBDEV_PKTSIZE_BCNT_MASK; + packetsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK; + packetsize |= USBDEV_PKTSIZE_MPKTSIZE(privreq->inflight); + sam_putreg8(USBDEV_EPSTATUS_BK0RDY, SAM_USBDEV_EPSTATUSCLR(epno)); + + return OK; +} + +/**************************************************************************** + * Name: sam_req_cancel + ****************************************************************************/ + +static void sam_req_cancel(struct sam_ep_s *privep, int16_t result) +{ + /* Complete every queued request with the specified status */ + + while (!sam_rqempty(&privep->reqq)) + { + usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), + (sam_rqpeek(&privep->reqq))->req.xfrd); + sam_req_complete(privep, result); + } +} + +/**************************************************************************** + * Name: sam_ep_configure_internal + * + * Description: + * This is the internal implementation of the endpoint configuration logic + * and implements the endpoint configuration method of the usbdev_ep_s + * interface. As an internal interface, it will be used to configure + * endpoint 0 which is not available to the class implementation. + * + ****************************************************************************/ + +static int sam_ep_configure_internal(struct sam_ep_s *privep, + const struct usb_epdesc_s *desc) +{ + uint8_t epconf; + uint16_t maxpacket; + uint8_t epno; + uint8_t eptype; + uint8_t intflags; + uint32_t ephwsize; + bool dirin; + + DEBUGASSERT(privep && privep->dev && desc); + + uinfo("len: %02x type: %02x addr: %02x attr: %02x " + "maxpacketsize: %02x %02x interval: %02x\n", + desc->len, desc->type, desc->addr, desc->attr, + desc->mxpacketsize[0], desc->mxpacketsize[1], + desc->interval); + + /* Decode the endpoint descriptor */ + + epno = USB_EPNO(desc->addr); + dirin = (desc->addr & USB_DIR_MASK) == USB_REQ_DIR_IN; + eptype = (desc->attr & USB_EP_ATTR_XFERTYPE_MASK); + maxpacket = GETUINT16(desc->mxpacketsize); + + if (maxpacket <= 8) + { + ephwsize = USBDEV_PKTSIZE_SIZE_8B; + } + else if (maxpacket <= 16) + { + ephwsize = USBDEV_PKTSIZE_SIZE_16B; + } + else if (maxpacket <= 32) + { + ephwsize = USBDEV_PKTSIZE_SIZE_32B; + } + else if (maxpacket <= 64) + { + ephwsize = USBDEV_PKTSIZE_SIZE_64B; + } + else if ((maxpacket <= 128) && (eptype == USB_EP_ATTR_XFER_ISOC)) + { + ephwsize = USBDEV_PKTSIZE_SIZE_128B; + } + else if ((maxpacket <= 256) && (eptype == USB_EP_ATTR_XFER_ISOC)) + { + ephwsize = USBDEV_PKTSIZE_SIZE_256B; + } + else if ((maxpacket <= 512) && (eptype == USB_EP_ATTR_XFER_ISOC)) + { + ephwsize = USBDEV_PKTSIZE_SIZE_512B; + } + else if ((maxpacket <= 1023) && (eptype == USB_EP_ATTR_XFER_ISOC)) + { + ephwsize = USBDEV_PKTSIZE_SIZE_1023B; + } + else + { + return -EINVAL; + } + + /* update endpoint descriptors to correct size */ + + privep->descb[0]->pktsize = ephwsize; + privep->descb[1]->pktsize = ephwsize; + + /* Initialize the endpoint structure */ + + privep->ep.eplog = desc->addr; /* Includes direction */ + privep->ep.maxpacket = maxpacket; + privep->epstate = USB_EPSTATE_IDLE; + + /* get current config IN and OUT */ + + epconf = 0x00; + sam_putreg8(0x00, SAM_USBDEV_EPCFG(epno)); + + if (dirin) + { + /* Disable bank1 (IN) */ + + intflags = USBDEV_EPINT_TRCPT1 | USBDEV_EPINT_STALL1; + } + else + { + /* Disable bank0 (OUT) */ + + intflags = USBDEV_EPINT_TRCPT0 | USBDEV_EPINT_STALL0; + } + + /* write back disabled config */ + + sam_putreg8(0x7e, SAM_USBDEV_EPINTENCLR(epno)); + sam_putreg8(0x7e, SAM_USBDEV_EPINTFLAG(epno)); + + /* Re-configure and enable the endpoint */ + + switch (eptype) + { + case USB_EP_ATTR_XFER_CONTROL: + { + epconf = USBDEV_EPCCFG_EPTYPE0_CTRLOUT | USBDEV_EPCCFG_EPTYPE1_CTRLIN; + + /* Also enable IN interrupts */ + + intflags = USBDEV_EPINT_TRCPT0 | USBDEV_EPINT_STALL0; + intflags |= USBDEV_EPINT_TRCPT1 | USBDEV_EPINT_STALL1; + intflags |= USBDEV_EPINT_RXSTP; + sam_putreg8(USBDEV_EPSTATUS_BK0RDY, SAM_USBDEV_EPSTATUSSET(0)); + sam_putreg8(USBDEV_EPSTATUS_BK1RDY, SAM_USBDEV_EPSTATUSCLR(0)); + } + break; + +#ifdef CONFIG_USBDEV_ISOCHRONOUS + case USB_EP_ATTR_XFER_ISOC: + if (dirin) + { + epconf |= USBDEV_EPCCFG_EPTYPE1_ISOCIN; + } + else + { + epconf |= USBDEV_EPCCFG_EPTYPE0_ISOCOUT; + } + break; +#endif + + case USB_EP_ATTR_XFER_BULK: + if (dirin) + { + epconf |= USBDEV_EPCCFG_EPTYPE1_BULKIN; + } + else + { + epconf |= USBDEV_EPCCFG_EPTYPE0_BULKOUT; + } + break; + + case USB_EP_ATTR_XFER_INT: + if (dirin) + { + epconf |= USBDEV_EPCCFG_EPTYPE1_INTIN; + } + else + { + epconf |= USBDEV_EPCCFG_EPTYPE0_INTOUT; + } + break; + + default: + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADEPTYPE), + eptype >> USB_EP_ATTR_XFERTYPE_SHIFT); + return -EINVAL; + } + + + sam_putreg8(epconf, SAM_USBDEV_EPCFG(epno)); + + /* Enable endpoint interrupts */ + + sam_putreg8(intflags, SAM_USBDEV_EPINTENSET(epno)); + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPCONF), epno << 8 | epconf); + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINTEN), epno << 8 | intflags); + + sam_dumpep(privep->dev, epno); + return OK; +} + +/**************************************************************************** + * Name: sam_ep_reserve + * + * Description: + * Find and un-reserved endpoint number and reserve it for the caller. + * + ****************************************************************************/ + +static inline struct sam_ep_s * +sam_ep_reserve(struct sam_usbdev_s *priv, uint8_t epset) +{ + struct sam_ep_s *privep = NULL; + irqstate_t flags; + int epndx = 0; + + flags = enter_critical_section(); + epset &= priv->epavail; + if (epset) + { + /* Select the lowest bit in the set of matching, available endpoints + * (skipping EP0) + */ + + for (epndx = 1; epndx < SAM_USB_NENDPOINTS; epndx++) + { + uint8_t bit = SAM_EP_BIT(epndx); + if ((epset & bit) != 0) + { + /* Mark the endpoint no longer available */ + + priv->epavail &= ~bit; + + /* And return the pointer to the standard endpoint structure */ + + privep = &priv->eplist[epndx]; + break; + } + } + } + + leave_critical_section(flags); + return privep; +} + +/**************************************************************************** + * Name: sam_ep_unreserve + * + * Description: + * The endpoint is no long in-used. It will be unreserved and can be + * re-used if needed. + * + ****************************************************************************/ + +static inline void +sam_ep_unreserve(struct sam_usbdev_s *priv, struct sam_ep_s *privep) +{ + irqstate_t flags = enter_critical_section(); + priv->epavail |= SAM_EP_BIT(USB_EPNO(privep->ep.eplog)); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: sam_ep_reserved + * + * Description: + * Check if the endpoint has already been allocated. + * + ****************************************************************************/ + +static inline bool +sam_ep_reserved(struct sam_usbdev_s *priv, int epno) +{ + return ((priv->epavail & SAM_EP_BIT(epno)) == 0); +} + +/**************************************************************************** + * Endpoint operations + ****************************************************************************/ +/**************************************************************************** + * Name: sam_ep_configure + * + * Description: + * This is the endpoint configuration method of the usbdev_ep_s interface. + * + ****************************************************************************/ + +static int sam_ep_configure(struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, + bool last) +{ + struct sam_ep_s *privep = (struct sam_ep_s *)ep; + int ret; + + /* Verify parameters. Endpoint 0 is not available at this interface */ + +#if defined(CONFIG_DEBUG_USB) || defined(CONFIG_USBDEV_TRACE) + uint8_t epno = USB_EPNO(desc->addr); + usbtrace(TRACE_EPCONFIGURE, (uint16_t)epno); + + DEBUGASSERT(ep && desc && epno > 0 && epno < SAM_USB_NENDPOINTS); + DEBUGASSERT(epno == USB_EPNO(ep->eplog)); +#endif + + /* This logic is implemented in sam_ep_configure_internal */ + + ret = sam_ep_configure_internal(privep, desc); + + /* If this was the last endpoint, then the class driver is fully + * configured. + */ + + if (ret == OK && last) + { + struct sam_usbdev_s *priv = privep->dev; + + /* Go to the configured state (we should have been in the addressed + * state) + */ + + DEBUGASSERT(priv && priv->devstate == USB_DEVSTATE_ADDRESSED); + priv->devstate = USB_DEVSTATE_CONFIGURED; + } + + return ret; +} + +/**************************************************************************** + * Name: sam_ep_disable + * + * Description: + * This is the disable() method of the USB device endpoint structure. + * + ****************************************************************************/ + +static int sam_ep_disable(struct usbdev_ep_s *ep) +{ + struct sam_ep_s *privep = (struct sam_ep_s *)ep; + struct sam_usbdev_s *priv; + irqstate_t flags; + uint8_t epno; + +#ifdef CONFIG_DEBUG_USB + if (!ep) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + uerr("ERROR: ep=%p\n", ep); + return -EINVAL; + } +#endif + + epno = USB_EPNO(ep->eplog); + usbtrace(TRACE_EPDISABLE, epno); + + /* Reset the endpoint and cancel any ongoing activity */ + + flags = enter_critical_section(); + priv = privep->dev; + sam_ep_reset(priv, epno); + + /* Revert to the addressed-but-not-configured state */ + + sam_setdevaddr(priv, priv->devaddr); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: sam_ep_allocreq + * + * Description: + * This is the allocreq() method of the USB device endpoint structure. + * + ****************************************************************************/ + +static struct usbdev_req_s *sam_ep_allocreq(struct usbdev_ep_s *ep) +{ + struct sam_req_s *privreq; + +#ifdef CONFIG_DEBUG_USB + if (!ep) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return NULL; + } +#endif + + usbtrace(TRACE_EPALLOCREQ, USB_EPNO(ep->eplog)); + + privreq = (struct sam_req_s *)kmm_malloc(sizeof(struct sam_req_s)); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_ALLOCFAIL), 0); + return NULL; + } + + memset(privreq, 0, sizeof(struct sam_req_s)); + return &privreq->req; +} + +/**************************************************************************** + * Name: sam_ep_freereq + * + * Description: + * This is the freereq() method of the USB device endpoint structure. + * + ****************************************************************************/ + +static void sam_ep_freereq(struct usbdev_ep_s *ep, struct usbdev_req_s *req) +{ + struct sam_req_s *privreq = (struct sam_req_s*)req; + +#ifdef CONFIG_DEBUG_USB + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return; + } +#endif + usbtrace(TRACE_EPFREEREQ, USB_EPNO(ep->eplog)); + + kmm_free(privreq); +} + +/**************************************************************************** + * Name: sam_ep_allocbuffer + * + * Description: + * This is the allocbuffer() method of the USB device endpoint structure. + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void *sam_ep_allocbuffer(struct usbdev_ep_s *ep, uint16_t nbytes) +{ + /* There is not special buffer allocation requirement */ + + return kumm_malloc(nbytes); +} +#endif + +/**************************************************************************** + * Name: sam_ep_freebuffer + * + * Description: + * This is the freebuffer() method of the USB device endpoint structure. + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void sam_ep_freebuffer(struct usbdev_ep_s *ep, void *buf) +{ + /* There is not special buffer allocation requirement */ + + kumm_free(buf); +} +#endif + +/**************************************************************************** + * Name: sam_ep_submit + * + * Description: + * This is the submit() method of the USB device endpoint structure. + * + ****************************************************************************/ + +static int sam_ep_submit(struct usbdev_ep_s *ep, struct usbdev_req_s *req) +{ + struct sam_req_s *privreq = (struct sam_req_s *)req; + struct sam_ep_s *privep = (struct sam_ep_s *)ep; + struct sam_usbdev_s *priv; + irqstate_t flags; + uint8_t epno; + int ret = OK; + +#ifdef CONFIG_DEBUG_USB + if (!req || !req->callback || !req->buf || !ep) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + uerr("ERROR: req=%p callback=%p buf=%p ep=%p\n", req, req->callback, + req->buf, ep); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPSUBMIT, USB_EPNO(ep->eplog)); + priv = privep->dev; + +#ifdef CONFIG_DEBUG_USB + if (!priv->driver) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_NOTCONFIGURED), priv->usbdev.speed); + uerr("ERROR: driver=%p\n", priv->driver); + return -ESHUTDOWN; + } +#endif + + /* Handle the request from the class driver */ + + epno = USB_EPNO(ep->eplog); + req->result = -EINPROGRESS; + req->xfrd = 0; + privreq->inflight = 0; + flags = enter_critical_section(); + + /* Handle IN (device-to-host) requests. NOTE: If the class device is + * using the bi-directional EP0, then we assume that they intend the EP0 + * IN functionality (EP0 SETUP OUT data receipt does not use requests). + */ + + if (USB_ISEPIN(ep->eplog) || epno == EP0) + { + /* Check if the endpoint is stalled (or there is a stall pending) */ + + if (privep->stalled || privep->pending) + { + /* Yes.. in this case, save the new they will get in a special + * "pending" they will get queue until the stall is cleared. + */ + + uinfo("Pending stall clear\n"); + sam_req_enqueue(&privep->pendq, privreq); + usbtrace(TRACE_INREQQUEUED(epno), req->len); + ret = OK; + } + + else + { + /* Add the new request to the request queue for the IN endpoint */ + + sam_req_enqueue(&privep->reqq, privreq); + usbtrace(TRACE_INREQQUEUED(epno), req->len); + + /* If the IN endpoint is IDLE and there is not write queue + * processing in progress, then transfer the data now. + */ + + if (privep->epstate == USB_EPSTATE_IDLE && !privep->txbusy) + { + ret = sam_req_write(priv, privep); + } + } + } + + /* Handle OUT (host-to-device) requests */ + + else + { + /* Add the new request to the request queue for the OUT endpoint */ + + sam_req_enqueue(&privep->reqq, privreq); + usbtrace(TRACE_OUTREQQUEUED(epno), req->len); + + /* Check if we have stopped RX receipt due to lack of read + * requests. In that case we are not receiving anything from host. + * and HW sends NAK to host. see sam_req_read() + * so this "state" is actually not required (at least yet) + */ + + if (privep->epstate == USB_EPSTATE_RXSTOPPED) + { + privep->epstate = USB_EPSTATE_IDLE; + } + + /* start new read if no active yet */ + + if (!privep->rxactive) + { + ret = sam_req_read(priv, privep, 0); + } + + } + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: sam_ep_cancel + ****************************************************************************/ + +static int sam_ep_cancel(struct usbdev_ep_s *ep, struct usbdev_req_s *req) +{ + struct sam_ep_s *privep = (struct sam_ep_s *)ep; + irqstate_t flags; + +#ifdef CONFIG_DEBUG_USB + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + usbtrace(TRACE_EPCANCEL, USB_EPNO(ep->eplog)); + + flags = enter_critical_section(); + sam_req_cancel(privep, -EAGAIN); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: sam_ep_stallresume + ****************************************************************************/ + +static int sam_ep_stallresume(struct usbdev_ep_s *ep, bool resume) +{ + struct sam_ep_s *privep; + uint8_t epno; + irqstate_t flags; + int ret; + +#ifdef CONFIG_DEBUG_USB + if (!ep) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Handle the resume condition */ + + privep = (struct sam_ep_s *)ep; + if (resume) + { + ret = sam_ep_resume(privep); + } + + /* Handle the stall condition */ + + else + { + /* If this is an IN endpoint (and not EP0) and if there are queued + * write requests, then we cannot stall now. Perhaps this is a + * protocol stall. In that case, we will need to drain the write + * requests before sending the stall. + */ + + flags = enter_critical_section(); + epno = USB_EPNO(ep->eplog); + if (epno != 0 && USB_ISEPIN(ep->eplog)) + { + /* Are there any unfinished write requests in the request queue? */ + + if (!sam_rqempty(&privep->reqq)) + { + /* Just set a flag to indicate that the endpoint must be + * stalled on the next TRCPTx interrupt when the request + * queue becomes empty. + */ + + privep->pending = true; + leave_critical_section(flags); + return OK; + } + } + + /* Not an IN endpoint, endpoint 0, or no pending write requests. + * Stall the endpoint now. + */ + + ret = sam_ep_stall(privep); + leave_critical_section(flags); + } + + return ret; +} + +/**************************************************************************** + * Device Controller Operations + ****************************************************************************/ +/**************************************************************************** + * Name: sam_allocep + * + * Description: + * This is the allocep() method of the USB device driver interface + * + ****************************************************************************/ + +static struct usbdev_ep_s *sam_allocep(struct usbdev_s *dev, uint8_t epno, + bool in, uint8_t eptype) +{ + struct sam_usbdev_s *priv = (struct sam_usbdev_s *)dev; + struct sam_ep_s *privep = NULL; + uint16_t epset = SAM_EPSET_NOTEP0; + + usbtrace(TRACE_DEVALLOCEP, (uint16_t)epno); +#ifdef CONFIG_DEBUG_USB + if (!dev) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return NULL; + } +#endif + + /* Ignore any direction bits in the logical address */ + + epno = USB_EPNO(epno); + + /* A logical address of 0 means that any endpoint will do */ + + if (epno > 0) + { + /* Otherwise, we will return the endpoint structure only for the requested + * 'logical' endpoint. All of the other checks will still be performed. + * + * First, verify that the logical endpoint is in the range supported by + * by the hardware. + */ + + if (epno >= SAM_USB_NENDPOINTS) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADEPNO), (uint16_t)epno); + return NULL; + } + + /* Convert the logical address to a physical OUT endpoint address and + * remove all of the candidate endpoints from the bitset except for the + * the IN/OUT pair for this logical address. + */ + + epset = SAM_EP_BIT(epno); + } + + /* Check if the selected endpoint number is available */ + + privep = sam_ep_reserve(priv, epset); + if (!privep) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EPRESERVE), (uint16_t)epset); + return NULL; + } + + return &privep->ep; +} + +/**************************************************************************** + * Name: sam_freeep + * + * Description: + * This is the freeep() method of the USB device driver interface + * + ****************************************************************************/ + +static void sam_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep) +{ + struct sam_usbdev_s *priv; + struct sam_ep_s *privep; + +#ifdef CONFIG_DEBUG_USB + if (!dev || !ep) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return; + } +#endif + priv = (struct sam_usbdev_s *)dev; + privep = (struct sam_ep_s *)ep; + usbtrace(TRACE_DEVFREEEP, (uint16_t)USB_EPNO(ep->eplog)); + + if (priv && privep) + { + /* Mark the endpoint as available */ + + sam_ep_unreserve(priv, privep); + } +} + +/**************************************************************************** + * Name: sam_getframe + * + * Description: + * This is the getframe() method of the USB device driver interface + * + ****************************************************************************/ + +static int sam_getframe(struct usbdev_s *dev) +{ + uint32_t regval; + uint16_t frameno; + +#ifdef CONFIG_DEBUG_USB + if (!dev) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Return the last frame number detected by the hardware */ + + regval = sam_getreg16(SAM_USBDEV_FNUM); + frameno = (regval & USBDEV_FNUM_MASK) >> USBDEV_FNUM_SHIFT; + + usbtrace(TRACE_DEVGETFRAME, frameno); + return frameno; +} + +/**************************************************************************** + * Name: sam_wakeup + * + * Description: + * This is the wakeup() method of the USB device driver interface + * + ****************************************************************************/ + +static int sam_wakeup(struct usbdev_s *dev) +{ + struct sam_usbdev_s *priv = (struct sam_usbdev_s *)dev; + irqstate_t flags; + uint16_t regval; + + usbtrace(TRACE_DEVWAKEUP, 0); +#ifdef CONFIG_DEBUG_USB + if (!dev) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Resume normal operation */ + + flags = enter_critical_section(); + sam_resume(priv); + + /* Activate a remote wakeup. (aka upstream resume) + * Setting the Remote Wakeup bit in CTRLB.UPRSM starts the + * Remote Wake Up procedure. + * + * This will automatically be done by the controller after 5 ms of + * inactivity on the USB bus. + * + * When the controller sends the Upstream Resume INTFLAG.WAKEUP is set + * and INTFLAG.SUSPEND is cleared. + * The CTRLB.UPRSM is cleared at the end of the transmitting Upstream Resume. + */ + + regval = sam_getreg16(SAM_USBDEV_CTRLB); + regval |= USBDEV_CTRLB_UPRSM; + sam_putreg16(regval, SAM_USBDEV_CTRLB); + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: sam_selfpowered + * + * Description: + * This is the selfpowered() method of the USB device driver interface + * + ****************************************************************************/ + +static int sam_selfpowered(struct usbdev_s *dev, bool selfpowered) +{ + struct sam_usbdev_s *priv = (struct sam_usbdev_s *)dev; + + usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered); + +#ifdef CONFIG_DEBUG_USB + if (!dev) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return -ENODEV; + } +#endif + + priv->selfpowered = selfpowered; + return OK; +} + +/**************************************************************************** + * Suspend/Resume Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_suspend + ****************************************************************************/ + +static void sam_suspend(struct sam_usbdev_s *priv) +{ + /* Don't do anything if the device is already suspended */ + + if (priv->devstate != USB_DEVSTATE_SUSPENDED) + { + /* Notify the class driver of the suspend event */ + + if (priv->driver) + { + CLASS_SUSPEND(priv->driver, &priv->usbdev); + } + + /* Switch to the Suspended state */ + + priv->prevstate = priv->devstate; + priv->devstate = USB_DEVSTATE_SUSPENDED; + + /* Disable clocking to the USB peripheral */ + + sam_disableclks(); + + /* Let the board-specific logic know that we have entered the + * suspend state. This may trigger additional reduced power + * consumption measures. + */ + + sam_usb_suspend((struct usbdev_s *)priv, false); + } +} + +/**************************************************************************** + * Name: sam_resume + ****************************************************************************/ + +static void sam_resume(struct sam_usbdev_s *priv) +{ + /* This function is called when either (1) a WAKEUP interrupt is received + * from the host PC, or (2) the class device implementation calls the + * wakeup() method. + */ + + /* Don't do anything if the device was not suspended */ + + if (priv->devstate == USB_DEVSTATE_SUSPENDED) + { + /* Revert to the previous state */ + + priv->devstate = priv->prevstate; + + /* Restore clocking to the USB peripheral */ + + sam_enableclks(); + + /* Restore full power -- whatever that means for this particular board */ + + sam_usb_suspend((struct usbdev_s *)priv, true); + + /* Notify the class driver of the resume event */ + + if (priv->driver) + { + CLASS_RESUME(priv->driver, &priv->usbdev); + } + } +} + +/**************************************************************************** + * Initialization/Reset + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_reset + ****************************************************************************/ + +static void sam_reset(struct sam_usbdev_s *priv) +{ + uint16_t regval; + uint8_t epno; + + /* Make sure that clocking is enabled to the USB peripheral. */ + + sam_enableclks(); + + /* Tell the class driver that we are disconnected. The class driver + * should then accept any new configurations. + */ + + CLASS_DISCONNECT(priv->driver, &priv->usbdev); + + /* The device enters the Default state (un-addressed and un-configured) */ + + priv->devaddr = 0; + sam_setdevaddr(priv, 0); + + priv->devstate = USB_DEVSTATE_DEFAULT; + + /* Reset and disable all endpoints. Then re-configure EP0 */ + + sam_epset_reset(priv, SAM_EPSET_ALL); + sam_ep_configure_internal(&priv->eplist[EP0], &g_ep0desc); + + /* set EP0 waiting for SETUP */ + + sam_ep0_ctrlread(priv); + + /* Reset endpoint data structures */ + + for (epno = 0; epno < SAM_USB_NENDPOINTS; epno++) + { + struct sam_ep_s *privep = &priv->eplist[epno]; + + /* Cancel any queued requests. Since they are cancelled + * with status -ESHUTDOWN, then will not be requeued + * until the configuration is reset. NOTE: This should + * not be necessary... the CLASS_DISCONNECT above should + * result in the class implementation calling sam_ep_disable + * for each of its configured endpoints. + */ + + sam_req_cancel(privep, -ESHUTDOWN); + + /* Reset endpoint status */ + + privep->stalled = false; + privep->pending = false; + privep->halted = false; + privep->zlpsent = false; + privep->txbusy = false; + privep->rxactive = false; + } + + /* Re-configure the USB controller in its initial, unconnected state */ + + priv->usbdev.speed = USB_SPEED_FULL; + + /* Clear all pending interrupt status */ + + regval = USBDEV_INT_SUSPEND | USBDEV_INT_SOF | USBDEV_INT_EORST | + USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | USBDEV_INT_UPRSM | + USBDEV_INT_RAMACER | USBDEV_INT_LPMNYET | USBDEV_INT_LPMSUSP; + + sam_putreg16(regval, SAM_USBDEV_INTFLAG); + + /* Enable normal operational interrupts + * endpoint 0 is enabled on sam_ep_configure_internal() + */ + + regval = USBDEV_INT_SOF | USBDEV_INT_WAKEUP | USBDEV_INT_SUSPEND; + sam_putreg16(regval, SAM_USBDEV_INTENSET); + + sam_dumpep(priv, EP0); +} + +/**************************************************************************** + * Interrupt Level Processing + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_ep0_wrstatus + * + * Description: + * write ep0 status reply back to host + * + ****************************************************************************/ + +static void sam_ep0_wrstatus(struct sam_usbdev_s *priv, + const uint8_t *buffer, size_t buflen) +{ + uint32_t packetsize; + + /* we need to make copy of data as source is in stack + * reusing the static ep0 setup buffer + */ + + DEBUGASSERT(buflen < SAM_EP0_MAXPACKET); + memcpy(&priv->ep0out[0], buffer, buflen); + + /* set read for next setup OUT */ + + sam_ep0_ctrlread(priv); + + /* setup TX transfer */ + + priv->eplist[0].descb[1]->addr = (uint32_t) &priv->ep0out[0]; + packetsize = priv->eplist[0].descb[1]->pktsize; + packetsize &= ~USBDEV_PKTSIZE_BCNT_MASK; + packetsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK; + packetsize |= USBDEV_PKTSIZE_BCNT(buflen); + priv->eplist[0].descb[1]->pktsize = packetsize; + + /* Set BK1RDY to notify the USB hardware that TX data is ready on + * descriptor bank1. We will be notified that the descriptor has been + * transmitted by the USB device when TRCPT1 in the endpoint's EPINTFLAG + * register has been set. + */ + + sam_putreg8(USBDEV_EPSTATUS_BK1RDY, SAM_USBDEV_EPSTATUSSET(0)); + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0WRSTATUS), buflen); +} + +/**************************************************************************** + * Name: sam_ep0_dispatch + ****************************************************************************/ + +static void sam_ep0_dispatch(struct sam_usbdev_s *priv) +{ + uint8_t *dataout; + size_t outlen; + int ret; + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_DISPATCH), 0); + if (priv && priv->driver) + { + /* Assume IN SETUP (or OUT SETUP with no data) */ + + dataout = NULL; + outlen = 0; + + /* Was this an OUT SETUP command? */ + + if (USB_REQ_ISOUT(priv->ctrl.type)) + { + uint16_t tmplen = GETUINT16(priv->ctrl.len); + if (tmplen > 0) + { + dataout = priv->ep0out; + outlen = tmplen; + } + } + + /* Forward to the control request to the class driver implementation */ + + ret = CLASS_SETUP(priv->driver, &priv->usbdev, &priv->ctrl, + dataout, outlen); + if (ret < 0) + { + /* Stall on failure */ + + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_DISPATCHSTALL), 0); + (void)sam_ep_stall(&priv->eplist[EP0]); + } + } +} + +/**************************************************************************** + * Name: sam_setdevaddr + * + * Description: + * This function is called after the completion of the STATUS phase to + * instantiate the device address that was received during the SETUP + * phase. This enters the ADDRESSED state from either the DEFAULT or the + * CONFIGURED states. + * + * If called with address == 0, then function will revert to the DEFAULT, + * un-configured and un-addressed state. + * + ****************************************************************************/ + +static void sam_setdevaddr(struct sam_usbdev_s *priv, uint8_t address) +{ + DEBUGASSERT(address <= 0x7f); + if (address) + { + /* Enable the address */ + + address |= USBDEV_DADD_ADDEN; + sam_putreg8(address, SAM_USBDEV_DADD); + + /* Go to the addressed but not configured state */ + + priv->devstate = USB_DEVSTATE_ADDRESSED; + } + else + { + /* Set address to zero. clear ADDEN bit */ + + sam_putreg8(0x00, SAM_USBDEV_DADD); + + /* Revert to the un-addressed, default state */ + + priv->devstate = USB_DEVSTATE_DEFAULT; + } +} + +/**************************************************************************** + * Name: sam_ep0_setup + * + * Description: + * This function is called after the receiving of the SETUP packet + * data is ready on usb_ctrlreq_s struct + * + ****************************************************************************/ + +static void sam_ep0_setup(struct sam_usbdev_s *priv) +{ + struct sam_ep_s *ep0 = &priv->eplist[EP0]; + struct sam_ep_s *privep; + union wb_u value; + union wb_u index; + union wb_u len; + union wb_u response; + enum sam_ep0setup_e ep0result; + uint8_t epno; + int nbytes = 0; /* Assume zero-length packet */ + int ret; + + /* Terminate any pending requests */ + + sam_req_cancel(ep0, -EPROTO); + + /* Assume NOT stalled; no TX in progress */ + + ep0->stalled = false; + ep0->pending = false; + ep0->epstate = USB_EPSTATE_IDLE; + + /* And extract the little-endian 16-bit values to host order */ + + value.w = GETUINT16(priv->ctrl.value); + index.w = GETUINT16(priv->ctrl.index); + len.w = GETUINT16(priv->ctrl.len); + + uinfo("SETUP: type=%02x req=%02x value=%04x index=%04x len=%04x\n", + priv->ctrl.type, priv->ctrl.req, value.w, index.w, len.w); + + /* Dispatch any non-standard requests */ + + if ((priv->ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_NOSTDREQ), priv->ctrl.type); + + /* Let the class implementation handle all non-standard requests */ + + sam_ep0_dispatch(priv); + return; + } + + /* Handle standard request. Pick off the things of interest to the + * USB device controller driver; pass what is left to the class driver + */ + + ep0result = USB_EP0SETUP_SUCCESS; + switch (priv->ctrl.req) + { + case USB_REQ_GETSTATUS: + { + /* type: device-to-host; recipient = device, interface, endpoint + * value: 0 + * index: zero interface endpoint + * len: 2; data = status + */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_GETSTATUS), priv->ctrl.type); + if (len.w != 2 || (priv->ctrl.type & USB_REQ_DIR_IN) == 0 || + index.b[MSB] != 0 || value.w != 0) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADEPGETSTATUS), 0); + ep0result = USB_EP0SETUP_STALL; + } + else + { + switch (priv->ctrl.type & USB_REQ_RECIPIENT_MASK) + { + case USB_REQ_RECIPIENT_ENDPOINT: + { + epno = USB_EPNO(index.b[LSB]); + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_GETSTATUS), epno); + if (epno >= SAM_USB_NENDPOINTS) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADEPGETSTATUS), epno); + ep0result = USB_EP0SETUP_STALL; + } + else + { + privep = &priv->eplist[epno]; + response.w = 0; /* Not stalled */ + nbytes = 2; /* Response size: 2 bytes */ + + if (privep->stalled) + { + /* Endpoint stalled */ + + response.b[LSB] = 1; /* Stalled */ + } + } + } + break; + + case USB_REQ_RECIPIENT_DEVICE: + { + if (index.w == 0) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_DEVGETSTATUS), 0); + + /* Features: Remote Wakeup=YES; selfpowered=? */ + + response.w = 0; + response.b[LSB] = (priv->selfpowered << USB_FEATURE_SELFPOWERED) | + (1 << USB_FEATURE_REMOTEWAKEUP); + nbytes = 2; /* Response size: 2 bytes */ + } + else + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADDEVGETSTATUS), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + break; + + case USB_REQ_RECIPIENT_INTERFACE: + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_IFGETSTATUS), 0); + response.w = 0; + nbytes = 2; /* Response size: 2 bytes */ + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADGETSTATUS), 0); + ep0result = USB_EP0SETUP_STALL; + } + break; + } + } + } + break; + + case USB_REQ_CLEARFEATURE: + { + /* type: host-to-device; recipient = device, interface or endpoint + * value: feature selector + * index: zero interface endpoint; + * len: zero, data = none + */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_CLEARFEATURE), priv->ctrl.type); + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT) + { + /* Let the class implementation handle all recipients (except for the + * endpoint recipient) + */ + + sam_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + /* Endpoint recipient */ + + epno = USB_EPNO(index.b[LSB]); + if (epno < SAM_USB_NENDPOINTS && index.b[MSB] == 0 && + value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0) + { + privep = &priv->eplist[epno]; + privep->halted = false; + + ret = sam_ep_resume(privep); + if (ret < 0) + { + ep0result = USB_EP0SETUP_STALL; + } + } + else + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADCLEARFEATURE), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + } + break; + + case USB_REQ_SETFEATURE: + { + /* type: host-to-device; recipient = device, interface, endpoint + * value: feature selector + * index: zero interface endpoint; + * len: 0; data = none + */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SETFEATURE), priv->ctrl.type); + if (((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) && + value.w == USB_FEATURE_TESTMODE) + { + /* Special case recipient=device test mode */ + + uinfo("test mode: %d\n", index.w); + } + else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT) + { + /* The class driver handles all recipients except recipient=endpoint */ + + sam_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + /* Handler recipient=endpoint */ + + epno = USB_EPNO(index.b[LSB]); + if (epno < SAM_USB_NENDPOINTS && index.b[MSB] == 0 && + value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0) + { + privep = &priv->eplist[epno]; + privep->halted = true; + + ret = sam_ep_stall(privep); + if (ret < 0) + { + ep0result = USB_EP0SETUP_STALL; + } + } + else + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADSETFEATURE), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + } + break; + + case USB_REQ_SETADDRESS: + { + /* type: host-to-device; recipient = device + * value: device address + * index: 0 + * len: 0; data = none + */ + + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_DEVICE || + index.w != 0 || len.w != 0 || value.w > 127) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADSETADDRESS), 0); + ep0result = USB_EP0SETUP_STALL; + } + else + { + /* Note that setting of the device address will be deferred. + * A zero-length packet will be sent and the device address will + * be set when the zero-length packet transfer completes. + */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPSETADDRESS), value.w); + priv->devaddr = value.w; + ep0result = USB_EP0SETUP_ADDRESS; + } + } + break; + + case USB_REQ_GETDESCRIPTOR: + /* type: device-to-host; recipient = device + * value: descriptor type and index + * index: 0 or language ID; + * len: descriptor len; data = descriptor + */ + case USB_REQ_SETDESCRIPTOR: + /* type: host-to-device; recipient = device + * value: descriptor type and index + * index: 0 or language ID; + * len: descriptor len; data = descriptor + */ + + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_GETSETDESC), priv->ctrl.type); + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) + { + /* The request seems valid... let the class implementation handle it */ + + sam_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADGETSETDESC), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + break; + + case USB_REQ_GETCONFIGURATION: + /* type: device-to-host; recipient = device + * value: 0; + * index: 0; + * len: 1; data = configuration value + */ + + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_GETCONFIG), priv->ctrl.type); + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + value.w == 0 && index.w == 0 && len.w == 1) + { + /* The request seems valid... let the class implementation handle it */ + + sam_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADGETCONFIG), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + break; + + case USB_REQ_SETCONFIGURATION: + /* type: host-to-device; recipient = device + * value: configuration value + * index: 0; + * len: 0; data = none + */ + + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SETCONFIG), priv->ctrl.type); + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + index.w == 0 && len.w == 0) + { + /* The request seems valid... let the class implementation handle it. + * If the class implementation accepts it new configuration, it will + * call sam_ep_configure() to configure the endpoints. + */ + + sam_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADSETCONFIG), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + break; + + case USB_REQ_GETINTERFACE: + /* type: device-to-host; recipient = interface + * value: 0 + * index: interface; + * len: 1; data = alt interface + */ + case USB_REQ_SETINTERFACE: + /* type: host-to-device; recipient = interface + * value: alternate setting + * index: interface; + * len: 0; data = none + */ + + { + /* Let the class implementation handle the request */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_GETSETIF), priv->ctrl.type); + sam_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + break; + + case USB_REQ_SYNCHFRAME: + /* type: device-to-host; recipient = endpoint + * value: 0 + * index: endpoint; + * len: 2; data = frame number + */ + + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SYNCHFRAME), 0); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDCTRLREQ), priv->ctrl.req); + ep0result = USB_EP0SETUP_STALL; + } + break; + } + + /* Restrict the data length to the length requested in the setup packet */ + + if (nbytes > len.w) + { + nbytes = len.w; + } + + /* At this point, the request has been handled and there are three + * (or four) possible outcomes: + * + * 1a. ep0result == USB_EP0SETUP_SUCCESS + * + * The setup request was successfully handled above and a response + * packet must be sent (may be a zero length packet). + * + * 1b. ep0result == USB_EP0SETUP_ADDRESS + * + * A special case is the case where epstate=USB_EPSTATE_EP0ADDRESS. + * This means that the above processing generated an additional state + * where we need to wait until we complete the status phase before + * applying the new device address. + * + * 2. ep0result == USB_EP0SETUP_DISPATCHED; + * + * The request was forwarded to the class implementation. In case, + * EP0 IN data may have already been sent and the EP0 IN response + * has already been queued? Or perhaps the endpoint has already + * been stalled? This is all under the control of the class driver. + * + * NOTE that for the case of non-standard SETUP requested, those + * requests were forwarded to the class driver and we don't even get + * to this logic. + * + * 3. ep0result == USB_EP0SETUP_STALL; + * + * An error was detected in either the above logic or by the class + * implementation logic. + */ + + switch (ep0result) + { + case USB_EP0SETUP_SUCCESS: + { + /* Send the response (might be a zero-length packet) */ + + ep0->epstate = USB_EPSTATE_EP0STATUSIN; + sam_ep0_wrstatus(priv, response.b, nbytes); + } + break; + + case USB_EP0SETUP_ADDRESS: + { + /* Send the response (might be a zero-length packet) */ + + ep0->epstate = USB_EPSTATE_EP0ADDRESS; + sam_ep0_wrstatus(priv, response.b, nbytes); + } + break; + + case USB_EP0SETUP_STALL: + { + /* Stall EP0 */ + + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EP0SETUPSTALLED), + priv->ctrl.req); + + (void)sam_ep_stall(&priv->eplist[EP0]); + } + break; + + case USB_EP0SETUP_DISPATCHED: + default: + break; + } +} + +/**************************************************************************** + * Name: sam_ctrla_write + * + * Description: + * writes value to CTRLA register some bits needs write-synchronisation + * + ****************************************************************************/ + +static void sam_ctrla_write(uint8_t value) +{ + sam_putreg8(value, SAM_USB_CTRLA); + + if (value & USB_CTRLA_SWRST) + { + /* Due to synchronization there is a delay from writing CTRLA.SWRST + * until the reset is complete. CTRLA.SWRST and SYNCBUSY.SWRST will + * both be cleared when the reset is complete. + */ + + while ((sam_getreg8(SAM_USB_CTRLA) & USB_CTRLA_SWRST) && + (sam_getreg8(SAM_USB_SYNCBUSY) & USB_SYNCBUSY_SWRST)) + ; + + return; + } + + if (value & USB_CTRLA_ENABLE) + { + /* Due to synchronization there is delay from writing CTRLA.ENABLE + * until the peripheral is enabled/disabled. + * SYNCBUSY.ENABLE will be cleared when the operation is complete. + */ + + while ((sam_getreg8(SAM_USB_SYNCBUSY) & USB_SYNCBUSY_ENABLE)) + ; + } +} + +/**************************************************************************** + * Name: sam_ep_trcpt_interrupt + * + * Description: + * Transmit completed on Bank 0/1 + * Normal: + * OUT data transmit has been completed bank=0 + * Ping-Pong: + * TODO: + * + ****************************************************************************/ +static void sam_ep_trcpt_interrupt(struct sam_usbdev_s *priv, + struct sam_ep_s *privep, + uint32_t flags, int bank) +{ + uint32_t eptype; + uint16_t pktsize; + uint8_t epno; + + /* Get the endpoint type */ + + epno = USB_EPNO(privep->ep.eplog); + eptype = sam_getreg8(SAM_USBDEV_EPCFG(epno)) & USBDEV_EPCFG_EPTYPE0_MASK; + + /* Are we receiving data for a read request? EP0 does not receive data + * using read requests. + */ + + /* Get the size of the packet that we just received */ + + pktsize = privep->descb[bank]->pktsize & USBDEV_PKTSIZE_BCNT_MASK; + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRCPT0_LEN), (uint16_t)pktsize); + + if (privep->epstate == USB_EPSTATE_IDLE && epno != 0) + { + /* continue processing the read request. */ + + (void)sam_req_read(priv, privep, pktsize); + } + + /* Did we just receive the data associated with an OUT SETUP command? */ + + else if (privep->epstate == USB_EPSTATE_EP0DATAOUT) + { + uint16_t len; + + DEBUGASSERT(epno == EP0 && bank == 0); + + /* Yes.. back to the IDLE state */ + + privep->epstate = USB_EPSTATE_IDLE; + + /* Get the size that we expected to receive */ + + len = GETUINT16(priv->ctrl.len); + if (len == pktsize) + { + /* And handle the EP0 SETUP now. */ + + sam_ep0_setup(priv); + } + else + { + /* Then stall. */ + + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EP0SETUPOUTSIZE), pktsize); + + (void)sam_ep_stall(privep); + } + } + + /* Check for a EP0 STATUS packet returned by the host at the end of a + * SETUP status phase + */ + + else if ((eptype == USBDEV_EPCCFG_EPTYPE0_CTRLOUT) && pktsize == 0) + { + DEBUGASSERT(epno == EP0 && bank == 0); + } + + /* Otherwise there is a problem. Complain an clear the interrupt */ + + else + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_RXDATABKERR), privep->epstate); + } +} + +/**************************************************************************** + * Name: sam_ep0_ctrlread + * + * Description: + * setup 8-byte read req for ep0-ctrl setup phase. + * data is received on priv->ep0out buffer. This is notified by + * endpoint TRCPT0 interrupt + * + ****************************************************************************/ + +static void sam_ep0_ctrlread(struct sam_usbdev_s *priv) +{ + priv->eplist[0].descb[0]->addr = (uint32_t) &priv->ep0out[0]; + priv->eplist[0].descb[0]->pktsize = USBDEV_PKTSIZE_MPKTSIZE(8) | USBDEV_PKTSIZE_SIZE_64B; + sam_putreg8(USBDEV_EPSTATUS_BK0RDY, SAM_USBDEV_EPSTATUSCLR(0)); +} + +/**************************************************************************** + * Name: sam_ep_interrupt + * + * Description: + * Handle the USB endpoint interrupt + * + ****************************************************************************/ + +static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno) +{ + struct sam_ep_s *privep; + uint16_t flags; + + DEBUGASSERT((unsigned)epno < SAM_USB_NENDPOINTS); + + /* Get the endpoint structure */ + + privep = &priv->eplist[epno]; + + /* Get the endpoint irq */ + + flags = sam_getreg8(SAM_USBDEV_EPINTFLAG(epno)); + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINTFLAGS), flags); + + /* TRCPT1: IN packet sent and acknowledged by the host */ + + if ((flags & USBDEV_EPINT_TRCPT1) != 0) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRCPT1), flags); + + /* Clear the TRCPT1 interrupt */ + + sam_putreg8(USBDEV_EPINT_TRCPT1, SAM_USBDEV_EPINTFLAG(epno)); + + /* Sending state. This is the completion of a "normal" write request + * transfer. In this case, we need to resume request processing in + * order to send the next outgoing packet. + */ + + if (privep->epstate == USB_EPSTATE_SENDING || + privep->epstate == USB_EPSTATE_EP0STATUSIN) + { + /* Continue/resume processing the write requests */ + + privep->epstate = USB_EPSTATE_IDLE; + (void)sam_req_write(priv, privep); + } + + /* Setting of the device address is a special case. The address was + * obtained when a preceding SETADDRESS SETUP command was processed. + * But the address is not set until the final SETUP status phase + * completes. This interrupt indicates the completion of that status + * phase and now we set the address. + */ + + else if (privep->epstate == USB_EPSTATE_EP0ADDRESS) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_ADDRESSED), priv->devaddr); + DEBUGASSERT(epno == EP0); + + /* Set the device address */ + + privep->epstate = USB_EPSTATE_IDLE; + sam_setdevaddr(priv, priv->devaddr); + } + else + { + /* Unexpected TRCPT1 interrupt */ + + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_TXCOMPERR), privep->epstate); + } + } + + /* OUT packet received */ + + if ((flags & USBDEV_EPINT_TRCPT0) != 0) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRCPT0), flags); + + /* Clear the TRCPT0 interrupt. */ + + sam_putreg8(USBDEV_EPINT_TRCPT0, SAM_USBDEV_EPINTFLAG(epno)); + + sam_ep_trcpt_interrupt(priv, privep, flags, 0); + } + + /* Endpoint stall */ + + if ((flags & USBDEV_EPINT_STALL0) != 0) + { + sam_putreg8(USBDEV_EPINT_STALL0, SAM_USBDEV_EPINTFLAG(epno)); + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPSTALL0), flags); + + /* If EP is not halted, clear STALL */ + + if (privep->epstate != USB_EPSTATE_STALLED) + { + sam_putreg8(USBDEV_EPSTATUS_STALLRQ0, SAM_USBDEV_EPSTATUSCLR(epno)); + } + } + + if ((flags & USBDEV_EPINT_STALL1) != 0) + { + sam_putreg8(USBDEV_EPINT_STALL1, SAM_USBDEV_EPINTFLAG(epno)); + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPSTALL1), flags); + + /* If EP is not halted, clear STALL */ + + if (privep->epstate != USB_EPSTATE_STALLED) + { + sam_putreg8(USBDEV_EPSTATUS_STALLRQ1, SAM_USBDEV_EPSTATUSCLR(epno)); + } + } + + /* Transmit FAIL! */ + + if ((flags & USBDEV_EPINT_TRFAIL0) != 0) + { + sam_putreg8(USBDEV_EPINT_TRFAIL0, SAM_USBDEV_EPINTFLAG(epno)); + privep->descb[0]->stausbk &= ~USBDEV_STATUSBK_ERRORFLOW; + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRFAIL0), flags); + } + if ((flags & USBDEV_EPINT_TRFAIL1) != 0) + { + sam_putreg8(USBDEV_EPINT_TRFAIL1, SAM_USBDEV_EPINTFLAG(epno)); + privep->descb[1]->stausbk &= ~USBDEV_STATUSBK_ERRORFLOW; + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRFAIL1), flags); + } + + /* SETUP packet received */ + + if ((flags & USBDEV_EPINT_RXSTP) != 0) + { + uint16_t len; + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPRXSTP), flags); + + /* If a write request transfer was pending, complete it. */ + + if (privep->epstate == USB_EPSTATE_SENDING) + { + sam_req_complete(privep, -EPROTO); + } + + /* SETUP data is ready in the ep0out buffer. */ + + memcpy((uint8_t *)&priv->ctrl, (uint8_t *)&priv->ep0out[0], + USB_SIZEOF_CTRLREQ); + + /* Check for a SETUP IN transaction with data. */ + + len = GETUINT16(priv->ctrl.len); + if (USB_REQ_ISOUT(priv->ctrl.type) && len > 0) + { + /* Yes.. then we have to wait for the OUT data phase to complete + * before processing the SETUP command. + */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPOUT), priv->ctrl.req); + privep->epstate = USB_EPSTATE_EP0DATAOUT; + + /* Clear the RXSTP indication. */ + + sam_putreg8(USBDEV_EPINT_RXSTP, SAM_USBDEV_EPINTFLAG(epno)); + } + else + { + /* This is an SETUP IN command (or a SETUP IN with no data). */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPIN), len); + privep->epstate = USB_EPSTATE_IDLE; + + /* Clear the RXSTP indication. */ + + sam_putreg8(USBDEV_EPINT_RXSTP, SAM_USBDEV_EPINTFLAG(epno)); + + /* Handle the SETUP OUT command now */ + + sam_ep0_setup(priv); + } + + /* ready for next setup data */ + + sam_ep0_ctrlread(priv); + + } +} + +/**************************************************************************** + * Name: sam_usb_interrupt + * + * Description: + * Handle the USB interrupt. + * Device Mode only TODO: Host + * + ****************************************************************************/ + +static int sam_usb_interrupt(int irq, void *context, void *arg) +{ + struct sam_usbdev_s *priv = (struct sam_usbdev_s *)arg; + uint16_t isr; + uint16_t pending; + uint16_t regval; + uint16_t pendingep; + int i; + + /* Get the set of pending device interrupts */ + + isr = sam_getreg16(SAM_USBDEV_INTFLAG); + regval = sam_getreg16(SAM_USBDEV_INTENSET); + pending = isr & regval; + + /* Get the set of pending enpoint interrupts */ + + pendingep = sam_getreg16(SAM_USBDEV_EPINTSMRY); + + /* Handle all pending USB interrupts */ + /* Serve Endpoint Interrupts first */ + + if (pendingep) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_PENDING_EP), pendingep); + + for (i = 0; i < SAM_USB_NENDPOINTS; i++) + { + if ((pendingep & USBDEV_EPINTSMRY_EPINT(i))) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPNO), (uint16_t)i); + sam_ep_interrupt(priv, i); + } + } + } + + /* Suspend, treated last */ + + if (pending == USBDEV_INT_SUSPEND) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SUSPEND), pending); + + /* Enable wakeup interrupts */ + + sam_putreg16(USBDEV_INT_SUSPEND, SAM_USBDEV_INTENCLR); + sam_putreg16(USBDEV_INT_UPRSM | USBDEV_INT_WAKEUP | USBDEV_INT_EORSM, + SAM_USBDEV_INTENSET); + + /* Clear the pending suspend (and any wakeup) interrupts */ + + sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_WAKEUP, + SAM_USBDEV_INTFLAG); + + /* Perform board-specific suspend operations. + * + * The USB device peripheral clocks can be switched off. + * Resume event is asynchronously detected. MCK and USBCK can be + * switched off in the Power Management controller and + * Other board-specific operations could also be performed. + */ + } + + /* SOF interrupt*/ + + else if ((pending & USBDEV_INT_SOF) != 0) + { + /* Clear the pending SOF interrupt */ + + sam_putreg16(SAM_TRACEINTID_SOF, SAM_USBDEV_INTFLAG); + + /* TODO: do we need check frame errors FNUM.FNCERR */ + } + + /* Resume or wakeup. REVISIT: Treat the same? */ + + else if ((pending & (USBDEV_INT_WAKEUP | USBDEV_INT_EORSM)) != 0) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_WAKEUP), (uint16_t)pending); + sam_resume(priv); + + /* Clear the pending wakeup, resume, (and any suspend) interrupts */ + + sam_putreg16(USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | + USBDEV_INT_SUSPEND, SAM_USBDEV_INTFLAG); + + /* Disable wakup and endofresume Enable suspend interrupt */ + + sam_putreg16(USBDEV_INT_WAKEUP | USBDEV_INT_EORSM, SAM_USBDEV_INTENCLR); + sam_putreg16(USBDEV_INT_SUSPEND, SAM_USBDEV_INTENSET); + } + + /* End of Reset. Set by hardware when an End Of Reset has been + * detected by the USB controller. + */ + + if ((pending & USBDEV_INT_EORST) != 0) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EORST), pending); + + /* Clear the end-of-reset interrupt */ + + sam_putreg16(USBDEV_INT_EORST, SAM_USBDEV_INTFLAG); + + /* Handle the reset */ + + sam_reset(priv); + + /* REVISIT: Set the device speed Why here ?? */ + + priv->usbdev.speed = USB_SPEED_FULL; + } + +#if 0 + /* for DEBUG help: check for pending unhandled irq's */ + + isr = sam_getreg16(SAM_USBDEV_INTFLAG); + if (isr) + { + uwarn("WARNING: Unhandled:0x%X\n", isr); + } + + pendingep = sam_getreg16(SAM_USBDEV_EPINTSMRY); + if (pendingep) + { + uwarn("WARNING: Unhandled_EP:0x%X\n", pendingep); + } +#endif + + return OK; +} + +void up_usbuninitialize(void) +{ + uinfo("up_usbuninitialize()\n"); +} + +void up_usbinitialize(void) +{ + /* For now there is only one USB controller, but we will always refer to + * it using a pointer to make any future ports to multiple USB controllers + * easier. + */ + + struct sam_usbdev_s *priv = &g_usbd; + + usbtrace(TRACE_DEVINIT, 0); + + /* Software initialization */ + + sam_sw_setup(priv); + + /* Power up and initialize USB controller. Interrupt from the USB + * controller is initialized here, but will not be enabled at the AIC + * until the class driver is installed. + */ + + sam_hw_setup(priv); + + /* Attach USB controller interrupt handlers. The hardware will not be + * initialized and interrupts will not be enabled until the class device + * driver is bound. Getting the IRQs here only makes sure that we have + * them when we need them later. + */ + + if (irq_attach(SAM_IRQ_USB, sam_usb_interrupt, priv) != 0) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_IRQREGISTRATION), + (uint16_t)SAM_IRQ_USB); + goto errout; + } + + uinfo("OK\n"); + return; + +errout: + up_usbuninitialize(); +} + +/**************************************************************************** + * Endpoint Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_ep_reset + * + * Description: + * Reset and disable one endpoints. + * + ****************************************************************************/ + +static void sam_ep_reset(struct sam_usbdev_s *priv, uint8_t epno) +{ + struct sam_ep_s *privep = &priv->eplist[epno]; + + /* Disable endpoint interrupts */ + + sam_putreg8(0x7e, SAM_USBDEV_EPINTENCLR(epno)); + sam_putreg8(0x7e, SAM_USBDEV_EPINTFLAG(epno)); + + /* Cancel any queued requests. Since they are cancelled with status + * -ESHUTDOWN, then will not be requeued until the configuration is reset. + * NOTE: This should not be necessary... the CLASS_DISCONNECT above + * should result in the class implementation calling sam_ep_disable + * for each of its configured endpoints. + */ + + sam_req_cancel(privep, -ESHUTDOWN); + + /* Reset endpoint status */ + + privep->epstate = USB_EPSTATE_DISABLED; + privep->stalled = false; + privep->pending = false; + privep->halted = false; + privep->zlpsent = false; + privep->txbusy = false; + privep->rxactive = false; +} + +/**************************************************************************** + * Name: sam_epset_reset + * + * Description: + * Reset and disable a set of endpoints. + * + ****************************************************************************/ + +static void sam_epset_reset(struct sam_usbdev_s *priv, uint16_t epset) +{ + uint32_t bit; + int epno; + + /* Reset each endpoint in the set */ + + for (epno = 0, bit = 1, epset &= SAM_EPSET_ALL; + epno < SAM_USB_NENDPOINTS && epset != 0; + epno++, bit <<= 1) + { + /* Is this endpoint in the set? */ + + if ((epset & bit) != 0) + { + /* Yes.. reset and disable it */ + + sam_ep_reset(priv, epno); + epset &= ~bit; + } + } +} + +/**************************************************************************** + * Name: sam_ep_stall + ****************************************************************************/ + +static int sam_ep_stall(struct sam_ep_s *privep) +{ + irqstate_t flags; + uint8_t epno; + + /* Check that endpoint is in Idle state */ + + DEBUGASSERT(/* privep->epstate == UDP_EPSTATE_IDLE && */ privep->dev); + + /* Check that endpoint is enabled and not already in Halt state */ + + flags = enter_critical_section(); + if ((privep->epstate != USB_EPSTATE_DISABLED) && + (privep->epstate != USB_EPSTATE_STALLED)) + { + epno = USB_EPNO(privep->ep.eplog); + usbtrace(TRACE_EPSTALL, epno); + + /* If this is an IN endpoint (or endpoint 0), then cancel any + * write requests in progress. + */ + + if (epno == 0 || USB_ISEPIN(privep->ep.eplog)) + { + sam_req_cancel(privep, -EPERM); + } + + /* Put endpoint into stalled state */ + + privep->epstate = USB_EPSTATE_STALLED; + privep->stalled = true; + privep->pending = false; + + sam_putreg8(USBDEV_EPSTATUS_STALLRQ0 | USBDEV_EPSTATUS_STALLRQ1, + SAM_USBDEV_EPSTATUSSET(epno)); + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: sam_ep_resume + ****************************************************************************/ + +static int sam_ep_resume(struct sam_ep_s *privep) +{ + struct sam_usbdev_s *priv; + struct sam_req_s *req; + irqstate_t flags; + uint8_t epno; + + /* Check that endpoint is in Idle state */ + + DEBUGASSERT(/* privep->epstate == UDP_EPSTATE_IDLE && */ privep->dev); + + flags = enter_critical_section(); + + /* Check if the endpoint is stalled */ + + if (privep->epstate == USB_EPSTATE_STALLED) + { + epno = USB_EPNO(privep->ep.eplog); + usbtrace(TRACE_EPRESUME, epno); + + priv = (struct sam_usbdev_s *)privep->dev; + + /* Return endpoint to Idle state */ + + privep->stalled = false; + privep->pending = false; + privep->epstate = USB_EPSTATE_IDLE; + + /* Clear STALLRQx request and reset data toggle */ + + if (USB_ISEPIN(privep->ep.eplog)) + { + sam_putreg8(USBDEV_EPSTATUS_STALLRQ1, SAM_USBDEV_EPSTATUSCLR(epno)); + sam_putreg8(USBDEV_EPSTATUS_DTGLIN, SAM_USBDEV_EPSTATUSCLR(epno)); + sam_putreg8(USBDEV_EPINT_STALL1, SAM_USBDEV_EPINTFLAG(epno)); + } + else + { + sam_putreg8(USBDEV_EPSTATUS_STALLRQ0, SAM_USBDEV_EPSTATUSCLR(epno)); + sam_putreg8(USBDEV_EPSTATUS_DTGLOUT, SAM_USBDEV_EPSTATUSCLR(epno)); + sam_putreg8(USBDEV_EPINT_STALL0, SAM_USBDEV_EPINTFLAG(epno)); + } + + /* Copy any requests in the pending request queue to the working + * request queue. + */ + + while ((req = sam_req_dequeue(&privep->pendq)) != NULL) + { + sam_req_enqueue(&privep->reqq, req); + } + + /* Resuming any blocked data transfers on the endpoint */ + + if (epno == 0 || USB_ISEPIN(privep->ep.eplog)) + { + /* IN endpoint (or EP0). Restart any queued write requests */ + + (void)sam_req_write(priv, privep); + } + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: sam_pullup + * + * Description: + * This is the pullup() method of the USB device driver interface + * + ****************************************************************************/ + +static int sam_pullup(struct usbdev_s *dev, bool enable) +{ + struct sam_usbdev_s *priv = (struct sam_usbdev_s *)dev; + uint32_t regval; + + usbtrace(TRACE_DEVPULLUP, (uint16_t)enable); + + /* Enable/disable the USB pull-up resistor */ + + regval = sam_getreg8(SAM_USBDEV_CTRLB); + + if (enable) + { + /* Connect the 1.5 KOhm integrated pull-up on USB */ + + regval &= ~USBDEV_CTRLB_DETACH; + } + else + { + /* Disconnect the 1.5 KOhm integrated pull-up on USB */ + + regval |= USBDEV_CTRLB_DETACH; + + /* Device returns to the Powered state */ + + if (priv->devstate > USB_DEVSTATE_POWERED) + { + priv->devstate = USB_DEVSTATE_POWERED; + } + } + + sam_putreg8((uint8_t)regval, SAM_USBDEV_CTRLB); + + return OK; +} + +/**************************************************************************** + * Initialization/Reset + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_enableclks + ****************************************************************************/ + +static void sam_enableclks(void) +{ + sam_ahb_usb_enableperiph(); + sam_apb_usb_enableperiph(); + sam_gclk_chan_enable(GCLK_CHAN_USB, BOARD_USB_GCLKGEN, false); +} + +/**************************************************************************** + * Name: sam_disableclks + ****************************************************************************/ + +static void sam_disableclks(void) +{ + sam_gclk_chan_disable(GCLK_CHAN_USB); + sam_apb_usb_disableperiph(); +} + +/**************************************************************************** + * Name: sam_hw_setup + ****************************************************************************/ + +static void sam_hw_setup(struct sam_usbdev_s *priv) +{ + int i; + uint16_t regval; + uint32_t padcalib; + + uint8_t calib_transn; + uint8_t calib_transp; + uint8_t calib_trim; + + /* To use the USB, the programmer must first configure the USB clock + * input, + */ + + sam_enableclks(); + + /* full reset USB */ + + sam_ctrla_write(USB_CTRLA_SWRST); + + /* Load USB factory calibration values from NVRAM */ + + calib_transn = (getreg32(SAM_FUSES_USBTRANSN_ADDR) & + SAM_FUSES_USBTRANSN_MASK) >> SAM_FUSES_USBTRANSN_SHIFT; + + calib_transp = (getreg32(SAM_FUSES_USBTRANSP_ADDR) & + SAM_FUSES_USBTRANSP_ADDR) >> SAM_FUSES_USBTRANSP_SHIFT; + + calib_trim = (getreg32(SAM_FUSES_USBTRIM_ADDR) & + SAM_FUSES_USBTRIM_MASK) >> SAM_FUSES_USBTRIM_SHIFT; + + padcalib = USB_PADCAL_TRANSP(calib_transp) | + USB_PADCAL_TRANSN(calib_transn) | + USB_PADCAL_TRIM(calib_trim); + + sam_putreg32(padcalib, SAM_USB_PADCAL); + + /* set config + * NREPLY = Any transaction to endpoint 0 will be ignored except SETUP + * cleared by hardware when receiving a SETUP packet. + * DETACH = The internal device pull-ups are disabled + */ + + regval = USBDEV_CTRLB_NREPLY | USBDEV_CTRLB_DETACH; + + /* do we need config to set LOW_SPEED mode? */ + + regval |= USBDEV_CTRLB_SPDCONF_FULL; + + sam_putreg16(regval, SAM_USBDEV_CTRLB); + + /* Enable USB core */ + +#ifdef CONFIG_USBDEV + sam_ctrla_write(USB_CTRLA_ENABLE | USB_CTRLA_MODE_DEVICE); +#endif +#ifdef CONFIG_USBHOST + sam_ctrla_write(USB_CTRLA_ENABLE | USB_CTRLA_MODE_HOST); +#endif + + /* Set up the USB DP/DM pins */ + + sam_portconfig(PORT_USB_DP); + sam_portconfig(PORT_USB_DM); + + /* Reset and disable endpoints */ + + sam_epset_reset(priv, SAM_EPSET_ALL); + + /* Initialize Endpoints */ + + for (i = 0; i < SAM_USB_NENDPOINTS; i++) + { + /* Reset endpoint configuration */ + + sam_putreg8(0, SAM_USBDEV_EPCFG(i)); + } + + /* Init descriptor base address */ + + sam_putreg32((uint32_t)&priv->ep_descriptors, SAM_USB_DESCADD); + + /* clear all previous descriptor data so no accidental + * DMA transfers could happen */ + + memset((uint8_t *)(&priv->ep_descriptors[0]), 0, + sizeof(priv->ep_descriptors)); + + /* Disable all interrupts */ + + sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_SOF | USBDEV_INT_EORST | + USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | USBDEV_INT_UPRSM | + USBDEV_INT_RAMACER | USBDEV_INT_LPMNYET | USBDEV_INT_LPMSUSP, + SAM_USBDEV_INTENCLR); + + sam_pullup(&priv->usbdev, false); +} + +/**************************************************************************** + * Name: sam_hw_shutdown + ****************************************************************************/ + +static void sam_hw_shutdown(struct sam_usbdev_s *priv) +{ + priv->usbdev.speed = USB_SPEED_UNKNOWN; + + /* Disable all interrupts */ + + sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_SOF | USBDEV_INT_EORST | + USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | USBDEV_INT_UPRSM | + USBDEV_INT_RAMACER | USBDEV_INT_LPMNYET | USBDEV_INT_LPMSUSP, + SAM_USBDEV_INTENCLR); + + /* Clear all pending interrupt status */ + + sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_SOF | USBDEV_INT_EORST | + USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | USBDEV_INT_UPRSM | + USBDEV_INT_RAMACER | USBDEV_INT_LPMNYET | USBDEV_INT_LPMSUSP, + SAM_USBDEV_INTFLAG); + + /* Disconnect the device / disable the pull-up */ + + sam_pullup(&priv->usbdev, false); + + /* Disable clocking to the UDP peripheral */ + + sam_disableclks(); +} + +/**************************************************************************** + * Name: sam_sw_setup + ****************************************************************************/ + +static void sam_sw_setup(struct sam_usbdev_s *priv) +{ + int epno; + + /* Initialize the device state structure. NOTE: many fields + * have the initial value of zero and, hence, are not explicitly + * initialized here. + */ + + memset(priv, 0, sizeof(struct sam_usbdev_s)); + priv->usbdev.ops = &g_devops; + priv->usbdev.ep0 = &priv->eplist[EP0].ep; + priv->epavail = SAM_EPSET_ALL & ~SAM_EP_BIT(EP0); + priv->devstate = USB_DEVSTATE_SUSPENDED; + priv->prevstate = USB_DEVSTATE_POWERED; + + /* Initialize the endpoint list */ + + for (epno = 0; epno < SAM_USB_NENDPOINTS; epno++) + { + /* Set endpoint operations, reference to driver structure (not + * really necessary because there is only one controller), and + * the (physical) endpoint number which is just the index to the + * endpoint. + */ + + priv->eplist[epno].ep.ops = &g_epops; + priv->eplist[epno].dev = priv; + priv->eplist[epno].ep.eplog = epno; + + /* We will use a maxpacket size for supported for each endpoint */ + + priv->eplist[epno].ep.maxpacket = SAM_USB_MAXPACKETSIZE(epno); + + /* set descriptor addresses */ + + priv->eplist[epno].descb[0] = &priv->ep_descriptors[(epno << 1)]; + priv->eplist[epno].descb[1] = &priv->ep_descriptors[(epno << 1) + 1]; + } + + /* Select a smaller endpoint size for EP0 */ + +#if SAM_EP0_MAXPACKET < 64 + priv->eplist[EP0].ep.maxpacket = SAM_EP0_MAXPACKET; +#endif +} + +/**************************************************************************** + * Name: sam_sw_shutdown + ****************************************************************************/ + +static void sam_sw_shutdown(struct sam_usbdev_s *priv) +{ + (void)priv; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: usbdev_register + * + * Description: + * Register a USB device class driver. The class driver's bind() method + * will be called to bind it to a USB device driver. + * + ****************************************************************************/ + +int usbdev_register(struct usbdevclass_driver_s *driver) +{ + /* For now there is only one USB controller, but we will always refer to + * it using a pointer to make any future ports to multiple USB controllers + * easier. + */ + + struct sam_usbdev_s *priv = &g_usbd; + int ret; + + usbtrace(TRACE_DEVREGISTER, 0); + +#ifdef CONFIG_DEBUG_USB + if (!driver || !driver->ops->bind || !driver->ops->unbind || + !driver->ops->disconnect || !driver->ops->setup) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } + + if (priv->driver) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_DRIVER), 0); + return -EBUSY; + } +#endif + + /* First hook up the driver */ + + priv->driver = driver; + + /* Then bind the class driver */ + + ret = CLASS_BIND(driver, &priv->usbdev); + if (ret) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BINDFAILED), (uint16_t)-ret); + priv->driver = NULL; + } + else + { + /* Enable USB controller interrupts at the NVIC. */ + + sam_hw_setup(priv); + up_enable_irq(SAM_IRQ_USB); + + /* Enable EORST irq */ + + sam_putreg16(USBDEV_INT_EORST, SAM_USBDEV_INTENSET); + + /* Enable pull-up to connect the device. The host should enumerate us + * some time after this. The next thing we expect is the EORST + * interrupt. + */ + + sam_pullup(&priv->usbdev, true); + priv->usbdev.speed = USB_SPEED_FULL; + } + + return ret; +} + +/**************************************************************************** + * Name: usbdev_unregister + * + * Description: + * Un-register usbdev class driver. If the USB device is connected to a + * USB host, it will first disconnect(). The driver is also requested to + * unbind() and clean up any device state, before this procedure finally + * returns. + * + ****************************************************************************/ + +int usbdev_unregister(struct usbdevclass_driver_s *driver) +{ + /* For now there is only one USB controller, but we will always refer to + * it using a pointer to make any future ports to multiple USB controllers + * easier. + */ + + struct sam_usbdev_s *priv = &g_usbd; + irqstate_t flags; + + usbtrace(TRACE_DEVUNREGISTER, 0); + +#ifdef CONFIG_DEBUG_USB + if (driver != priv->driver) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Reset the hardware and cancel all requests. All requests must be + * canceled while the class driver is still bound. + */ + + flags = enter_critical_section(); + + /* Unbind the class driver */ + + CLASS_UNBIND(driver, &priv->usbdev); + + /* Disable USB controller interrupts (but keep them attached) */ + + up_disable_irq(SAM_IRQ_USB); + + /* Put the hardware in an inactive state. Then bring the hardware back up + * in the initial state. This is essentially the same state as we were + * in when up_usbinitialize() was first called. + */ + + sam_hw_shutdown(priv); + sam_sw_shutdown(priv); + + sam_sw_setup(priv); + sam_hw_setup(priv); + + /* Unhook the driver */ + + priv->driver = NULL; + leave_critical_section(flags); + return OK; +} + +#endif /* CONFIG_USBDEV && CONFIG_SAMD5E5_USB */ diff --git a/arch/arm/src/samd5e5/sam_usb.h b/arch/arm/src/samd5e5/sam_usb.h new file mode 100644 index 00000000000..50e6fb357fd --- /dev/null +++ b/arch/arm/src/samd5e5/sam_usb.h @@ -0,0 +1,98 @@ +/****************************************************************************** + * arch/arm/src/samd5e5/sam_usb.h + * + * Copyright (C) 2015 Filament - www.filament.com + * Copyright (C) 2015 Offcode Ltd. All rights reserved. + * Author: Janne Rosberg + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt (port to SAMD5E5) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __ARCH_ARM_SRCSAMD5E5_SAM_USB_H +#define __ARCH_ARM_SRCSAMD5E5_SAM_USB_H + +/***************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "chip.h" +#include "chip/sam_usb.h" + +/***************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/***************************************************************************** + * Name: sam_usb_suspend + * + * Description: + * Board logic must provide the sam_usb_suspend logic if the USB driver is + * used. This function is called whenever the USB enters or leaves + * suspend mode. + * + * When 'resume' is false, this function call provides an opportunity to + * perform board-specific power-saving actions so that less power is consumed + * while the USB is suspended. + * + * NOTE: + * Certain power-saving operations are performed by the UDP driver when it + * enters suspend mode: The USB device peripheral clocks are be switched off. + * MCK and UDPCK are switched off and the USB transceiver is disabled. + * + * When 'resume' is true, normal clocking and operations must all be restored. + * + *****************************************************************************/ + +void sam_usb_suspend(FAR struct usbdev_s *dev, bool resume); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRCSAMD5E5_SAM_USB_H */ diff --git a/configs/metro-m4/include/board.h b/configs/metro-m4/include/board.h index 1f0e54ec97a..6f536184590 100644 --- a/configs/metro-m4/include/board.h +++ b/configs/metro-m4/include/board.h @@ -446,4 +446,8 @@ #define BOARD_SERCOM3_SLOWGEN 3 /* 48MHz */ #define BOARD_SERCOM3_FREQUENCY BOARD_GCLK1_FREQUENCY +/* USB */ + +#define BOARD_USB_GCLKGEN 1 /* GCLK1, 48MHz */ + #endif /* __CONFIG_METRO_M4_INCLUDE_BOARD_H */