sam0_common: Add USB peripheral driver#10915
Conversation
dylad
left a comment
There was a problem hiding this comment.
some comments for the first round. I'll test on arduino-zero like board soon.
cpu/sam0_common/include/sam_usb.h
Outdated
| @@ -0,0 +1,64 @@ | |||
| /* | |||
| * Copyright (C) 2018 Freie Universität Berlin | |||
| * Copyright (C) 2018 Inria | |||
There was a problem hiding this comment.
You should probably update these copyrights.
cpu/sam0_common/include/sam_usb.h
Outdated
| * @{ | ||
| * | ||
| * @file | ||
| * @brief [RFC 4122](https://tools.ietf.org/html/rfc4122) UUID functions |
cpu/sam0_common/include/sam_usb.h
Outdated
| #define SAM_USB_NUM_EP USBDEV_NUM_ENDPOINTS | ||
|
|
||
| typedef struct { | ||
| usbdev_t usbdev; |
cpu/sam0_common/usb/usb.c
Outdated
| return 2 * num + (dir == USB_EP_DIR_OUT ? 0 : 1); | ||
| } | ||
|
|
||
| static inline unsigned _get_ep_num2(usbdev_ep_t *ep) |
There was a problem hiding this comment.
Why is there two get_ep_num functions ?
There was a problem hiding this comment.
Probably just bad naming, but will check.
There was a problem hiding this comment.
Refactored these a bit to make more sense
cpu/sam0_common/usb/usb.c
Outdated
| val = 0x7; | ||
| break; | ||
| default: | ||
| return; |
There was a problem hiding this comment.
What about a single line to directly compute this value with a nice comment ? :)
There was a problem hiding this comment.
Should be possible, but have to think about it a bit, the last case (1023) makes it a bit annoying.
There was a problem hiding this comment.
I've been thinking a bit about this. I'd rather keep it this way as signalling error back in case of invalid size is easier to do with the switch case statement
cpu/sam0_common/usb/usb.c
Outdated
| int res = -ENOTSUP; | ||
| assert(ep != NULL); | ||
| switch (opt) { | ||
| case USBOPT_EP_ENABLE: |
There was a problem hiding this comment.
removed as they are covered by the default option
cpu/sam0_common/usb/usb.c
Outdated
| /* Reset peripheral */ | ||
| USB->DEVICE.CTRLA.reg |= USB_CTRLA_SWRST; | ||
| while (usb_swrst_syncing()) {} | ||
| while (USB->DEVICE.CTRLA.bit.SWRST) {} |
There was a problem hiding this comment.
You can remove this line. The datasheet states that both bit will be clearer once reset is done, so the line above should be enough.
There was a problem hiding this comment.
Duplicate something something...
cpu/sam0_common/usb/usb.c
Outdated
| USB_PADCAL_TRIM((*(uint32_t *)USB_FUSES_TRIM_ADDR >> | ||
| USB_FUSES_TRIM_Pos)); | ||
|
|
||
| USB->DEVICE.CTRLB.bit.SPDCONF = 0x0; |
There was a problem hiding this comment.
Replaces 0 by USB_DEVICE_CTRLB_SPDCONF_FS
cpu/sam0_common/usb/usb.c
Outdated
| ep_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1; | ||
| } | ||
| else { | ||
| ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1; |
There was a problem hiding this comment.
| ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1; | |
| ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1; |
cpu/sam0_common/usb/usb.c
Outdated
| ep_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0; | ||
| } | ||
| else { | ||
| ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0; |
There was a problem hiding this comment.
| ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0; | |
| ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0; |
cpu/sam0_common/usb/usb.c
Outdated
| } | ||
| res = sizeof(usbopt_enable_t); | ||
| break; | ||
| default: |
cpu/sam0_common/usb/usb.c
Outdated
| UsbDeviceEndpoint *ep_reg = &USB->DEVICE.DeviceEndpoint[ep->num]; | ||
|
|
||
| if (ep->dir == USB_EP_DIR_IN) { | ||
| ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_BK1RDY; |
There was a problem hiding this comment.
| ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_BK1RDY; | |
| ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY; |
cpu/sam0_common/usb/usb.c
Outdated
| ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_BK1RDY; | ||
| } | ||
| else { | ||
| ep_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSCLR_BK0RDY; |
There was a problem hiding this comment.
| ep_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSCLR_BK0RDY; | |
| ep_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY; |
cpu/sam0_common/usb/usb.c
Outdated
|
|
||
| /* Enable USB device */ | ||
| USB->DEVICE.DESCADD.reg = (uint32_t)banks; | ||
| USB->DEVICE.CTRLA.reg |= USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY; |
There was a problem hiding this comment.
I think you can remove USB_CTRLA_RUNSTDBY for now or make it user defined in periph_conf.h
drivers/include/usb/usbdev.h
Outdated
| struct usbdev { | ||
| const struct usbdev_driver *driver; /**< usbdev driver struct */ | ||
| usbdev_event_cb_t cb; /**< Event callback supplied by | ||
| * upper layer */ |
There was a problem hiding this comment.
What about adding UsbDevice struct from atmel vendor files ? In the current state, this driver works fine with one USB device instance but maybe one day we will have several USB within a MCU.
This way we can replace all USB->DEVICE from the driver by a more generic call.
There was a problem hiding this comment.
I assume you want to add that to the sam0_common_usb_t and not to the generic usbdev struct.
I think your point is valid, but I have to check if the current API allows this. If it doesn't, I would assume this to be a shortcomming of the API.
There was a problem hiding this comment.
I assume you want to add that to the
sam0_common_usb_tand not to the genericusbdevstruct.
Yes
There was a problem hiding this comment.
Done, I'm not fully content with the current implementation for retrieving the context in the isr, but this can be reworked as soon as we actually have a device with multiple usb peripherals
a843254 to
12044ec
Compare
|
Removed WIP status btw, ready for review (I hope) |
12044ec to
2483ab5
Compare
|
rebased |
2483ab5 to
4677289
Compare
|
@bergzand i'll give it another round this weekend. Could you rebase please ? |
|
Reworked and rebased! |
cpu/sam0_common/include/sam_usb.h
Outdated
| /** | ||
| * USB endpoint buffer space | ||
| * | ||
| * @todo global configurable? |
There was a problem hiding this comment.
Nope, actually is globally configurable now (for a while)
| case 512: | ||
| val = 0x6; | ||
| break; | ||
| case 1023: |
There was a problem hiding this comment.
@bergzand I think I've read somewhere that some endpoint size can be 1023 or 1024 in specific case. Am I wrong ?
There was a problem hiding this comment.
1023 bytes is the max size for USB 2.0 full speed isochronous transfers, USB 2.0 high speed is allowed to have transfers of 1024 bytes with isochronous endpoint types.
There was a problem hiding this comment.
Alright then we're all good here :)
| GCLK_CLKCTRL_GEN_GCLK0 | | ||
| (GCLK_CLKCTRL_ID(USB_GCLK_ID))); | ||
| #elif defined(CPU_FAM_SAML21) | ||
| MCLK->AHBMASK.reg |= MCLK_AHBMASK_USB; |
There was a problem hiding this comment.
You missed MCLK->APBBMASK for SAML21 family
| GCLK->CLKCTRL.reg = (uint32_t)(GCLK_CLKCTRL_CLKEN | | ||
| GCLK_CLKCTRL_GEN_GCLK0 | | ||
| (GCLK_CLKCTRL_ID(USB_GCLK_ID))); | ||
| #elif defined(CPU_FAM_SAML21) |
There was a problem hiding this comment.
#elif defined(CPU_FAM_SAML21) || defined(CPU_FAM_SAMR30)
cpu/sam0_common/periph/usbdev.c
Outdated
| gpio_init(GPIO_PIN(PA, 24), GPIO_IN); | ||
| gpio_init(GPIO_PIN(PA, 25), GPIO_IN); | ||
| gpio_init_mux(GPIO_PIN(PA, 24), GPIO_MUX_G); | ||
| gpio_init_mux(GPIO_PIN(PA, 25), GPIO_MUX_G); |
There was a problem hiding this comment.
You should reused your config struct defined in periph_conf.h here.
There was a problem hiding this comment.
So much effort spent on the config struct, only to forget to use it in the implementation…
cpu/sam0_common/periph/usbdev.c
Outdated
| usbdev->config->device->CTRLB.bit.SPDCONF = USB_DEVICE_CTRLB_SPDCONF_FS; | ||
| _enable_irq(usbdev); | ||
|
|
||
| gpio_init_int(GPIO_PIN(PA, 7), GPIO_IN_PD, GPIO_BOTH, |
cpu/sam0_common/periph/usbdev.c
Outdated
| sam0_common_usb_t *usbdev = (sam0_common_usb_t*)dev; | ||
| if (usbdev->connect_change) { | ||
| usbdev->connect_change = false; | ||
| if (gpio_read(GPIO_PIN(PA, 7))) { |
|
@dylad fixed your remarks, also uncrustified the file (but should have done that in a separate fixup commit :'( ) |
I don't mind, I'll test it on hardware asap so we can move forward. |
Thanks, appreciating your reviews! |
|
I messed up the GPIO things :( |
fixed |
|
@dylad I've reworked the driver with the ideas as discussed out-of-band today. Sorry for the force-push, messed up my commit order a bit.
|
dylad
left a comment
There was a problem hiding this comment.
Seems like we're close to an end.
| return res; | ||
| } | ||
|
|
||
| static int _ep_unready(usbdev_ep_t *ep) |
There was a problem hiding this comment.
nitpick: why _ep_unready and _usbdev_ep_ready ?
There was a problem hiding this comment.
Arbitrary, nothing specific, position of the moon etc.
Will fix to _ep_ready
There was a problem hiding this comment.
Never mind the above, I remember now, all the static _usbdev_$SOMETHING calls are used in the instantiated usbdev_driver_t struct
| static void _usbdev_ep_esr(usbdev_ep_t *ep) | ||
| { | ||
| UsbDeviceEndpoint *ep_reg = _ep_reg_from_ep(ep); | ||
| signed event = -1; |
There was a problem hiding this comment.
int32 or int16 ? I'm sure this one will be troublesome outside of ARM.
There was a problem hiding this comment.
But this driver is closely tied to an ARM based core…
I can change it to int32_t if you like
There was a problem hiding this comment.
But same as this comment, there is not really a need for a specific variable size here, so I'd rather give the compiler more freedom for possible optimizations.
| /** | ||
| * @brief USB suspend condition no longer active | ||
| */ | ||
| USBDEV_EVENT_RESUME, |
There was a problem hiding this comment.
dump question here:
Is this suppose to be random or alphabetical order ?
There was a problem hiding this comment.
Good question. The only "order" I had in mind was something that appeared logical to me. Which at the moment is to group the similar events together as much as possible.
There was a problem hiding this comment.
Alright, I don't think the coding guideline force us to change something here. Let's keep it this way.
|
rebased to current master |
dylad
left a comment
There was a problem hiding this comment.
This is my last round. We're are almost good here !
cpu/sam0_common/periph/usbdev.c
Outdated
| GCLK->CLKCTRL.reg = (uint32_t)(GCLK_CLKCTRL_CLKEN | | ||
| GCLK_CLKCTRL_GEN_GCLK0 | | ||
| (GCLK_CLKCTRL_ID(USB_GCLK_ID))); | ||
| #elif defined(CPU_FAM_SAML21) || defined(CPU_FAM_SAMR30) |
There was a problem hiding this comment.
I would change this line to
| #elif defined(CPU_FAM_SAML21) || defined(CPU_FAM_SAMR30) | |
| #elif defined(CPU_SAML21) |
This will prevent us to add new sub-family like the upcoming SAMR34
There was a problem hiding this comment.
Huh? Are you sure? I changed this to the current situation based on your comment. I don't mind changing it back to what you are suggesting, just want to make sure that we get this right.
There was a problem hiding this comment.
You can go ahead, this is the way to go.
cpu/sam0_common/periph/usbdev.c
Outdated
| case USB_EP_TYPE_INTERRUPT: | ||
| type = 0x04; | ||
| break; | ||
| case USB_EP_TYPE_NONE: |
There was a problem hiding this comment.
add default statement at the same time here ?
There was a problem hiding this comment.
How about I add an
default:
assert(false);
here. Anything else than the types already handled in the switch is an error.
There was a problem hiding this comment.
Never mind, I can just change the case USB_EP_TYPE_NONE: to default:
|
ACK. |
|
@bergzand |
|
@dylad Squashed, all green here! 🎉 |
|
Here we go ! |
|
@dylad Thanks for the review and testing! |
Contribution description
This PR adds a USB peripheral driver for the sam0 device class. The driver is much WIP as some details still have to be thought out.
Testing procedure
Nothing yet, a follow-up is required to actually initialize the usb device.
Issues/PRs references
depends on #9830