diff options
author | huang lin <hl@rock-chips.com> | 2015-05-20 17:27:10 +0800 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2015-06-30 08:09:49 +0200 |
commit | 41e2499734b37d5f38a17416a3512c2c9042e79c (patch) | |
tree | f17f03fd74bd2a308559b3381f5620267016a7dd /payloads/libpayload/drivers | |
parent | 066cc85e229fb21700061a8d8545234ee690b504 (diff) | |
download | coreboot-41e2499734b37d5f38a17416a3512c2c9042e79c.tar.xz |
libpayload: add UDC driver for Designware controller
Found in rockchips rk3288 as used in google/veyron.
BUG=None
TEST=None
BRANCH=None
Change-Id: I2f2c36c5bea3986a8a37f84c75608b838a8782ae
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Commit-Id: 59a0bcd97e8d0f5ce5ac1301910e11b01e2d24b1
Original-Change-Id: Ic89ed54c48d6f9ce125a93caf96471abc6e8cd9d
Original-Signed-off-by: Yunzhi Li <lyz@rock-chips.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/272108
Original-Reviewed-by: Julius Werner <jwerner@chromium.org>
Original-Commit-Queue: Lin Huang <hl@rock-chips.com>
Original-Tested-by: Lin Huang <hl@rock-chips.com>
Reviewed-on: http://review.coreboot.org/10689
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Diffstat (limited to 'payloads/libpayload/drivers')
-rw-r--r-- | payloads/libpayload/drivers/Makefile.inc | 1 | ||||
-rw-r--r-- | payloads/libpayload/drivers/udc/dwc2.c | 937 | ||||
-rw-r--r-- | payloads/libpayload/drivers/udc/dwc2_priv.h | 65 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/dwc2_private.h | 616 |
4 files changed, 1004 insertions, 615 deletions
diff --git a/payloads/libpayload/drivers/Makefile.inc b/payloads/libpayload/drivers/Makefile.inc index 57a82ac0a9..25ff4fea20 100644 --- a/payloads/libpayload/drivers/Makefile.inc +++ b/payloads/libpayload/drivers/Makefile.inc @@ -108,6 +108,7 @@ libc-$(CONFIG_LP_USB_DWC2) += usb/dwc2_rh.c # USB device stack libc-$(CONFIG_LP_UDC) += udc/udc.c libc-$(CONFIG_LP_UDC_CI) += udc/chipidea.c +libc-$(CONFIG_LP_UDC_DWC2) += udc/dwc2.c # used by both USB HID and keyboard libc-y += hid.c diff --git a/payloads/libpayload/drivers/udc/dwc2.c b/payloads/libpayload/drivers/udc/dwc2.c new file mode 100644 index 0000000000..99ee0b323b --- /dev/null +++ b/payloads/libpayload/drivers/udc/dwc2.c @@ -0,0 +1,937 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2015 Rockchip Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <libpayload.h> +#include <assert.h> +#include <limits.h> + +#include <udc/dwc2_udc.h> +#include "dwc2_priv.h" + +static int get_mps(dwc2_ep_t *ep) +{ + dwc2_ep_reg_t *ep_reg = ep->ep_regs; + depctl_t depctl; + uint16_t mps = 0; + + depctl.d32 = readl(&ep_reg->depctl); + if (ep->ep_num == 0) { + switch (depctl.mps) { + case D0EPCTL_MPS_64: + mps = 64; + break; + case D0EPCTL_MPS_32: + mps = 32; + break; + case D0EPCTL_MPS_16: + mps = 16; + break; + case D0EPCTL_MPS_8: + mps = 8; + break; + default: + usb_debug("get mps error\n"); + } + } else { + mps = depctl.mps; + } + + return mps; +} + +static void dwc2_process_ep(dwc2_ep_t *ep, int len, void *buf) +{ + depctl_t depctl; + depsiz_t depsiz; + uint16_t pkt_cnt; + uint16_t mps; + int max_transfer_size; + dwc2_ep_reg_t *ep_reg = ep->ep_regs; + + if (ep->ep_num == 0) + max_transfer_size = EP0_MAXLEN; + else + max_transfer_size = EP_MAXLEN; + assert(len <= max_transfer_size); + + mps = get_mps(ep); + + pkt_cnt = ALIGN_UP(len, mps) / mps; + if (pkt_cnt == 0) + pkt_cnt = 1; + + depsiz.pktcnt = pkt_cnt; + depsiz.xfersize = len; + writel(depsiz.d32, &ep_reg->deptsiz); + + writel((uint32_t)buf, &ep_reg->depdma); + + depctl.d32 = readl(&ep_reg->depctl); + + if (ep->ep_num != 0) { + if (depctl.dpid == 0) + depctl.setd0pid = 1; + else + depctl.setd1pid = 1; + } + depctl.cnak = 1; + depctl.epena = 1; + writel(depctl.d32, &ep_reg->depctl); + +} + +static void dwc2_write_ep(dwc2_ep_t *ep, int len, void *buf) +{ + dwc2_process_ep(ep, len, buf); +} + +static void dwc2_read_ep(dwc2_ep_t *ep, int len, void *buf) +{ + dwc2_process_ep(ep, len, buf); +} + +static void dwc2_connect(struct usbdev_ctrl *this, int connect) +{ + /* Turn on the USB connection by enabling the pullup resistor */ + dwc2_pdata_t *p = DWC2_PDATA(this); + dctl_t dctl; + + usb_debug("DwcUdcConnect\n"); + + dctl.d32 = readl(&p->regs->device.dctl); + + if (connect) + /* Connect */ + dctl.sftdiscon = 0; + else + /* Disconnect */ + dctl.sftdiscon = 1; + + writel(dctl.d32, &p->regs->device.dctl); +} + +static void dwc2_bus_reset(struct usbdev_ctrl *this) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + dcfg_t dcfg; + dctl_t dctl; + + if (this->initialized) + this->initialized = 0; + + /* Reset device addr */ + dcfg.d32 = readl(&p->regs->device.dcfg); + dcfg.devaddr = 0; + writel(dcfg.d32, &p->regs->device.dcfg); + + dctl.d32 = readl(&p->regs->device.dctl); + dctl.rmtwkupsig = 0; + writel(dctl.d32, &p->regs->device.dctl); +} + +static void dwc2_enum_done(struct usbdev_ctrl *this) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + + dctl_t dctl; + dsts_t dsts; + + usb_debug("dwc2_enum_done\n"); + + dsts.d32 = readl(&p->regs->device.dsts); + + switch (dsts.enumspd) { + case 0: + this->ep_mps[0][0] = 64; + this->ep_mps[0][1] = 64; + usb_debug("HighSpeed Enum Done\n"); + break; + default: + usb_debug("EnumSpeed Error\n"); + return; + } + + /* Clear global IN Nak */ + dctl.d32 = readl(&p->regs->device.dctl); + dctl.cgnpinnak = 1; + writel(dctl.d32, &p->regs->device.dctl); +} + +static void dwc2_tx_fifo_flush(struct usbdev_ctrl *this, unsigned int idx) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + grstctl_t grstctl; + int timeout = 100; + + grstctl.d32 = readl(&p->regs->core.grstctl); + grstctl.txfflsh = 1; + grstctl.txfnum = idx; + writel(grstctl.d32, &p->regs->core.grstctl); + + /* wait until the fifo is flushed */ + do { + udelay(1); + grstctl.d32 = readl(&p->regs->core.grstctl); + + if (--timeout < 0) { + usb_debug("timeout flushing Tx fifo %x\n", idx); + break; + } + } while (grstctl.txfflsh); +} + +static void dwc2_rx_fifo_flush(struct usbdev_ctrl *this, unsigned int idx) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + grstctl_t grstctl; + int timeout = 100; + + grstctl.d32 = readl(&p->regs->core.grstctl); + grstctl.rxfflsh = 1; + writel(grstctl.d32, &p->regs->core.grstctl); + + /* wait until the fifo is flushed */ + do { + udelay(1); + grstctl.d32 = readl(&p->regs->core.grstctl); + + if (--timeout < 0) { + usb_debug("timeout flushing Rx fifo %x\n", idx); + break; + } + } while (grstctl.rxfflsh); +} + +static void dwc2_disable_ep(dwc2_ep_reg_t *ep_reg) +{ + depctl_t depctl; + depint_t depint; + + /* Disable the required IN/OUT endpoint */ + depctl.d32 = readl(&ep_reg->depctl); + + /* Already disabled */ + if (depctl.epena == 0) + return; + depctl.epdis = 1; + depctl.snak = 1; + writel(depctl.d32, &ep_reg->depctl); + + /* Wait for the DEPINTn.EPDisabled interrupt */ + do { + depint.d32 = readl(&ep_reg->depint); + } while (!depint.epdisbld); + + /* Clear DEPINTn.EPDisabled */ + writel(depint.d32, &ep_reg->depint); + + depctl.d32 = readl(&ep_reg->depctl); + depctl.epena = 0; + depctl.epdis = 0; + writel(depctl.d32, &ep_reg->depctl); +} + +static void dwc2_halt_ep(struct usbdev_ctrl *this, int ep, int in_dir) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + dwc2_ep_reg_t *ep_reg = p->eps[ep][in_dir].ep_regs; + depctl_t depctl; + dctl_t dctl; + gintsts_t gintsts; + + usb_debug("dwc2_halt_ep ep %d-%d\n", ep, in_dir); + depctl.d32 = readl(&ep_reg->depctl); + /*Alread disabled*/ + if (!depctl.epena) + return; + /* First step: disable EP */ + if (in_dir) { + /* Only support Non-Periodic IN Endpoints */ + dctl.d32 = readl(&p->regs->device.dctl); + dctl.sgnpinnak = 1; + writel(dctl.d32, &p->regs->device.dctl); + + /* Wait for the GINTSTS.Global IN NP NAK Effective interrupt */ + do { + gintsts.d32 = readl(&p->regs->core.gintsts); + } while (!gintsts.ginnakeff); + + /* Clear GINTSTS.Global IN NP NAK Effective interrupt */ + writel(gintsts.d32, &p->regs->core.gintsts); + dwc2_disable_ep(ep_reg); + + /* Flush Tx Fifo */ + dwc2_tx_fifo_flush(this, p->eps[ep][in_dir].txfifo); + + } else { + /* Enable Global OUT NAK mode */ + dctl.d32 = readl(&p->regs->device.dctl); + dctl.sgoutnak = 1; + writel(dctl.d32, &p->regs->device.dctl); + + /* Wait for the GINTSTS.GOUTNakEff interrupt */ + do { + gintsts.d32 = readl(&p->regs->core.gintsts); + } while (!gintsts.goutnakeff); + + /* Clear GINTSTS.GOUTNakEff */ + writel(gintsts.d32, &p->regs->core.gintsts); + + dwc2_disable_ep(ep_reg); + + dctl.d32 = readl(&p->regs->device.dctl); + dctl.cgoutnak = 1; + dctl.sgoutnak = 0; + writel(dctl.d32, &p->regs->device.dctl); + } + + /* Second step: clear job queue */ + while (!SIMPLEQ_EMPTY(&p->eps[ep][in_dir].job_queue)) { + struct job *job = SIMPLEQ_FIRST(&p->eps[ep][in_dir].job_queue); + + if (job->autofree) + free(job->data); + + SIMPLEQ_REMOVE_HEAD(&p->eps[ep][in_dir].job_queue, queue); + } +} + +static int find_tx_fifo(struct usbdev_ctrl *this, uint32_t mps) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + uint32_t fifo_index = 0; + uint32_t fifo_size = UINT_MAX; + gnptxfsiz_t gnptxfsiz; + int i, val; + + for (i = 1; i < MAX_EPS_CHANNELS - 1; i++) { + if (p->fifo_map & (1<<i)) + continue; + gnptxfsiz.d32 = readl(&p->regs->core.dptxfsiz_dieptxf[i]); + val = gnptxfsiz.nptxfdep * 4; + + if (val < mps) + continue; + /* Search for smallest acceptable fifo */ + if (val < fifo_size) { + fifo_size = val; + fifo_index = i; + } + } + + if (!fifo_index) + fatal("find_tx_fifo no suitable fifo found\n"); + + p->fifo_map |= 1 << fifo_index; + + return fifo_index; + +} + +static void dwc2_start_ep0(struct usbdev_ctrl *this) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + depctl_t depctl = { .d32 = 0 }; + depint_t depint = { .d32 = 0xff }; + + usb_debug("dwc2_start_ep0\n"); + + /* Enable endpoint, reset data toggle */ + depctl.mps = 0; + depctl.usbactep = 1; + depctl.snak = 1; + depctl.epdis = 1; + + writel(depctl.d32, &p->regs->device.inep[0].depctl); + writel(depint.d32, &p->regs->device.inep[0].depint); + writel(depctl.d32, &p->regs->device.outep[0].depctl); + writel(depint.d32, &p->regs->device.outep[0].depint); + + p->eps[0][0].busy = 0; + p->eps[0][1].busy = 0; + this->ep_mps[0][0] = 64; + this->ep_mps[0][1] = 64; +} + +static void dwc2_start_ep(struct usbdev_ctrl *this, + int ep, int in_dir, int ep_type, int mps) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + dwc2_ep_reg_t *ep_reg = p->eps[ep][in_dir].ep_regs; + depctl_t depctl = { .d32 = 0 }; + + assert((ep < 16) && (ep > 0)); + usb_debug("dwc2_start_ep %d-%d (type %d)\n", ep, in_dir, ep_type); + + in_dir = in_dir ? 1 : 0; + + /* Enable endpoint, reset data toggle */ + depctl.setd0pid = 1; + depctl.mps = mps & 0x3ff; + depctl.usbactep = 1; + + /* ep type 0:ctrl 1:isoc 2:bulk 3:intr */ + depctl.eptype = ep_type; + depctl.snak = 1; + if (in_dir) { + /* Allocate Tx FIFO */ + p->eps[ep][in_dir].txfifo = find_tx_fifo(this, mps); + } + writel(depctl.d32, &ep_reg->depctl); + + p->eps[ep][in_dir].busy = 0; + this->ep_mps[ep][in_dir] = mps; +} + +static void continue_ep_transfer(dwc2_pdata_t *p, + int endpoint, int in_dir) +{ + int max_transfer_size = (endpoint == 0) ? EP0_MAXLEN : EP_MAXLEN; + int mps; + uint32_t remind_length; + void *data_buf; + + if (SIMPLEQ_EMPTY(&p->eps[endpoint][in_dir].job_queue)) + return; + + struct job *job = SIMPLEQ_FIRST(&p->eps[endpoint][in_dir].job_queue); + + remind_length = job->length - job->xfered_length; + + job->xfer_length = (remind_length > max_transfer_size) ? + max_transfer_size : remind_length; + data_buf = job->data + job->xfered_length; + + if ((((uint32_t)data_buf & 3) != 0) && (job->xfer_length > 0)) + usb_debug("Un-aligned buffer address\n"); + + if (in_dir) { + dwc2_write_ep(&p->eps[endpoint][in_dir], + job->xfer_length, data_buf); + } else { + mps = get_mps(&p->eps[endpoint][in_dir]); + job->xfer_length = ALIGN_UP(job->xfer_length, mps); + dwc2_read_ep(&p->eps[endpoint][0], job->xfer_length, data_buf); + } +} + +static void start_ep_transfer(dwc2_pdata_t *p, + int endpoint, int in_dir) +{ + int max_transfer_size = (endpoint == 0) ? EP0_MAXLEN : EP_MAXLEN; + int mps; + + if (p->eps[endpoint][in_dir].busy) { + usb_debug("ep %d-%d busy\n", endpoint, in_dir); + return; + } + + if (SIMPLEQ_EMPTY(&p->eps[endpoint][in_dir].job_queue)) { + usb_debug("ep %d-%d empty\n", endpoint, in_dir); + return; + } + + struct job *job = SIMPLEQ_FIRST(&p->eps[endpoint][in_dir].job_queue); + + job->xfer_length = (job->length > max_transfer_size) ? + max_transfer_size : job->length; + + if (in_dir) { + dwc2_write_ep(&p->eps[endpoint][1], job->xfer_length, job->data); + } else { + mps = get_mps(&p->eps[endpoint][0]); + job->xfer_length = ALIGN_UP(job->xfer_length, mps); + /* BUG */ + if ((endpoint == 0) && (job->length == 0)) + job->data = p->setup_buf; + dwc2_read_ep(&p->eps[endpoint][0], job->xfer_length, job->data); + } + + usb_debug("start EP %d-%d with %zx bytes starting at %p\n", endpoint, + in_dir, job->length, job->data); + + p->eps[endpoint][in_dir].busy = 1; +} + +static void dwc2_enqueue_packet(struct usbdev_ctrl *this, int endpoint, + int in_dir, void *data, int len, int zlp, int autofree) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + struct job *job = xzalloc(sizeof(*job)); + + job->data = data; + job->length = len; + job->zlp = zlp; + job->autofree = autofree; + + usb_debug("adding job %d bytes to EP %d-%d\n", len, endpoint, in_dir); + SIMPLEQ_INSERT_TAIL(&p->eps[endpoint][in_dir].job_queue, job, queue); + + if ((endpoint == 0) || (this->initialized)) + start_ep_transfer(p, endpoint, in_dir); +} + +static void complete_ep_transfer(struct usbdev_ctrl *this, int endpoint, + int in_dir, int xfer_result) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + struct job *job = SIMPLEQ_FIRST(&p->eps[endpoint][in_dir].job_queue); + int mps = this->ep_mps[endpoint][in_dir]; + + if (in_dir) { + job->xfered_length += job->xfer_length - xfer_result; + if (job->xfered_length < job->length || + (job->xfered_length == job->length && + job->xfered_length % mps == 0 && job->xfer_length)) { + continue_ep_transfer(p, endpoint, in_dir); + return; + } + } else { + job->xfered_length += job->xfer_length - xfer_result; + } + SIMPLEQ_REMOVE_HEAD(&p->eps[endpoint][in_dir].job_queue, queue); + + usb_debug("%d-%d: scheduled %zd, now %d bytes\n", endpoint, in_dir, + job->length, job->xfered_length); + + if (this->current_config && + this->current_config->interfaces[0].handle_packet) + this->current_config->interfaces[0].handle_packet(this, + endpoint, in_dir, job->data, job->xfered_length); + + if (job->autofree) + free(job->data); + free(job); + + p->eps[endpoint][in_dir].busy = 0; + + if (endpoint == 0 && job->xfered_length == 0) + dwc2_enqueue_packet(this, 0, 0, p->setup_buf, 8, 0, 0); + + start_ep_transfer(p, endpoint, in_dir); +} + +static void dwc2_outep_intr(struct usbdev_ctrl *this, dwc2_ep_t *ep) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + depint_t depint; + depsiz_t depsiz; + + depint.d32 = readl(&ep->ep_regs->depint) & + readl(&p->regs->device.doepmsk); + + /* Don't process XferCompl interrupt if it is a setup packet */ + if ((ep->ep_num == 0) && (depint.setup || depint.stuppktrcvd)) + depint.xfercompl = 0; + + /* Transfer completed */ + if (depint.xfercompl) { + usb_debug("DOEPINT_XFERCOMPL\n"); + writel(DXEPINT_XFERCOMPL, &ep->ep_regs->depint); + depsiz.d32 = readl(&ep->ep_regs->deptsiz); + + if (ep->ep_num == 0) + depsiz.xfersize &= 0x7f; + + complete_ep_transfer(this, ep->ep_num, 0, depsiz.xfersize); + } + /* Endpoint disable */ + if (depint.epdisbld) { + usb_debug("DEPINT_EPDISBLD\n"); + writel(DXEPINT_EPDISBLD, &ep->ep_regs->depint); + } + /* AHB Error */ + if (depint.ahberr) { + usb_debug("DEPINT_AHBERR\n"); + writel(DXEPINT_AHBERR, &ep->ep_regs->depint); + } + + /* Handle Setup Phase Done (Contorl Ep) */ + if (depint.setup) { + usb_debug("DEPINT_SETUP\n"); + writel(DXEPINT_SETUP, &ep->ep_regs->depint); +#ifdef USB_DEBUG + hexdump((unsigned int)p->setup_buf, sizeof(dev_req_t)); +#endif + SIMPLEQ_REMOVE_HEAD(&p->eps[0][0].job_queue, queue); + p->eps[0][0].busy = 0; + + udc_handle_setup(this, ep->ep_num, (dev_req_t *)p->setup_buf); + } +} + +static void dwc2_inep_intr(struct usbdev_ctrl *this, dwc2_ep_t *ep) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + depint_t depint; + depsiz_t depsiz; + + depint.d32 = readl(&ep->ep_regs->depint) & + readl(&p->regs->device.doepmsk); + + /* Don't process XferCompl interrupt if it is a setup packet */ + if ((ep->ep_num == 0) && (depint.setup)) { + usb_debug("IN ep timeout\n"); + writel(DXEPINT_TIMEOUT, &ep->ep_regs->depint); + } + + /* Transfer completed */ + if (depint.xfercompl) { + usb_debug("DIEPINT_XFERCOMPL\n"); + writel(DXEPINT_XFERCOMPL, &ep->ep_regs->depint); + depsiz.d32 = readl(&ep->ep_regs->deptsiz); + + if (ep->ep_num == 0) + depsiz.xfersize &= 0x7f; + + complete_ep_transfer(this, ep->ep_num, 1, depsiz.xfersize); + } + /* Endpoint disable */ + if (depint.epdisbld) { + usb_debug("DEPINT_EPDISBLD\n"); + writel(DXEPINT_EPDISBLD, &ep->ep_regs->depint); + } + /* AHB Error */ + if (depint.ahberr) { + usb_debug("DEPINT_AHBERR\n"); + writel(DXEPINT_AHBERR, &ep->ep_regs->depint); + } +} + +static int dwc2_check_irq(struct usbdev_ctrl *this) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + gintsts_t gintsts; + uint32_t daint, daint_out, daint_in, ep; + + gintsts.d32 = readl(&p->regs->core.gintsts) & + readl(&p->regs->core.gintmsk); + + if (gintsts.d32 == 0) + return 1; + + /* EP INTR */ + if (gintsts.oepint || gintsts.iepint) { + + daint = readl(&p->regs->device.daint) & + readl(&p->regs->device.daintmsk); + + daint_out = daint >> DAINT_OUTEP_SHIFT; + daint_in = daint & ~(daint_out << DAINT_OUTEP_SHIFT); + + for (ep = 0; ep < MAX_EPS_CHANNELS; ep++, daint_in >>= 1) { + if (daint_in & 1) + dwc2_inep_intr(this, &p->eps[ep][1]); + } + + for (ep = 0; ep < MAX_EPS_CHANNELS; ep++, daint_out >>= 1) { + if (daint_out & 1) + dwc2_outep_intr(this, &p->eps[ep][0]); + } + } + + /* USB Bus Suspend */ + if (gintsts.usbsusp) { + usb_debug("GINTSTS_ERLYSUSP\n"); + writel(GINTSTS_USBSUSP, &p->regs->core.gintsts); + } + /* USB Bus Reset */ + if (gintsts.usbrst) { + usb_debug("GINTSTS_USBRST\n"); + dwc2_bus_reset(this); + writel(GINTSTS_USBRST, &p->regs->core.gintsts); + } + /* Enumeration done */ + if (gintsts.enumdone) { + usb_debug("GINTSTS_ENUMDONE\n"); + dwc2_enum_done(this); + writel(GINTSTS_ENUMDONE, &p->regs->core.gintsts); + } + if (gintsts.sessreqint) { + usb_debug("GINTSTS_SESSREQINT\n"); + writel(GINTSTS_SESSREQINT, &p->regs->core.gintsts); + } + if (gintsts.wkupint) { + usb_debug("GINTSTS_WKUPINT\n"); + writel(GINTSTS_WKUPINT, &p->regs->core.gintsts); + } + + return 1; +} + +static void dwc2_shutdown(struct usbdev_ctrl *this) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + int i, j; + int is_empty = 0; + gusbcfg_t gusbcfg; + + while (!is_empty) { + is_empty = 1; + this->poll(this); + for (i = 0; i < 16; i++) + for (j = 0; j < 2; j++) + if (!SIMPLEQ_EMPTY(&p->eps[i][j].job_queue)) + is_empty = 0; + } + + /* Disconnect */ + dwc2_connect(this, 0); + + /* Back to normal otg mode */ + gusbcfg.d32 = readl(&p->regs->core.gusbcfg); + gusbcfg.forcehstmode = 0; + gusbcfg.forcedevmode = 0; + writel(gusbcfg.d32, &p->regs->core.gusbcfg); + + free(p); + free(this); +} + +static void dwc2_set_address(struct usbdev_ctrl *this, int address) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + dcfg_t dcfg; + + dcfg.d32 = readl(&p->regs->device.dcfg); + dcfg.devaddr = address; + writel(dcfg.d32, &p->regs->device.dcfg); +} + +static void dwc2_stall(struct usbdev_ctrl *this, + uint8_t ep, int in_dir, int set) +{ + dwc2_pdata_t *p = DWC2_PDATA(this); + dwc2_ep_reg_t *ep_reg = p->eps[ep][in_dir].ep_regs; + depctl_t depctl; + + usb_debug("dwc2_stall\n"); + depctl.d32 = readl(&ep_reg->depctl); + + in_dir = in_dir ? 1 : 0; + + if (set) { + depctl.stall = 1; + depctl.setd0pid = 1; + writel(depctl.d32, &ep_reg->depctl); + } else { + /* STALL bit will be clear by core */ + } + this->ep_halted[ep][in_dir] = set; +} + +static void *dwc2_malloc(size_t size) +{ + return dma_memalign(4096, size); +} + +static void dwc2_free(void *ptr) +{ + free(ptr); +} + +static int dwc2_reinit_udc(struct usbdev_ctrl *this, void *_opreg, + const device_descriptor_t *dd) +{ + grstctl_t grstctl = { .d32 = 0 }; + gintmsk_t gintmsk = { .d32 = 0 }; + gahbcfg_t gahbcfg = { .d32 = 0 }; + gusbcfg_t gusbcfg = { .d32 = 0 }; + grxfsiz_t grxfsiz = { .d32 = 0 }; + dtxfsiz_t dtxfsiz0 = { .d32 = 0 }; + dtxfsiz_t dtxfsiz1 = { .d32 = 0 }; + dtxfsiz_t dtxfsiz2 = { .d32 = 0 }; + depint_t depint_msk = { .d32 = 0 }; + dcfg_t dcfg = { .d32 = 0 }; + dwc2_reg_t *regs = (dwc2_reg_t *)_opreg; + dwc2_pdata_t *p = DWC2_PDATA(this); + const int timeout = 10000; + int i; + + p->regs = phys_to_virt(regs); + p->fifo_map = 0; + p->setup_buf = dma_memalign(4, 64); + + for (i = 0; i < MAX_EPS_CHANNELS; i++) { + /* Init OUT EPs */ + p->eps[i][0].ep_num = i; + p->eps[i][0].ep_regs = ®s->device.outep[i]; + SIMPLEQ_INIT(&p->eps[i][0].job_queue); + + /* Init IN EPs */ + p->eps[i][1].ep_num = i; + p->eps[i][1].ep_regs = ®s->device.inep[i]; + SIMPLEQ_INIT(&p->eps[i][1].job_queue); + } + + usb_debug("dwc2_hw_init\n"); + + /* Wait for AHB idle */ + for (i = 0; i < timeout; i++) { + udelay(1); + grstctl.d32 = readl(®s->core.grstctl); + if (grstctl.ahbidle) + break; + } + if (i == timeout) { + usb_debug("DWC2 Init error AHB Idle\n"); + return 0; + } + + /* Restart the Phy Clock */ + /* Core soft reset */ + grstctl.csftrst = 1; + writel(grstctl.d32, ®s->core.grstctl); + for (i = 0; i <= timeout; i++) { + udelay(1); + grstctl.d32 = readl(®s->core.grstctl); + if (!grstctl.csftrst) + break; + + if (i == timeout) { + usb_debug("DWC2 Init error reset fail\n"); + return 0; + } + } + + /* Restart the Phy Clock */ + writel(0x0, ®s->pcgr.pcgcctl); + + /* Set 16bit PHY if & Force host mode */ + gusbcfg.d32 = readl(®s->core.gusbcfg); + gusbcfg.phyif = 1; + gusbcfg.forcehstmode = 0; + gusbcfg.forcedevmode = 1; + writel(gusbcfg.d32, ®s->core.gusbcfg); + + dcfg.d32 = readl(®s->device.dcfg); + /* reset device addr */ + dcfg.devaddr = 0; + /* enable HS */ + dcfg.devspd = 0; + writel(dcfg.d32, ®s->device.dcfg); + + dwc2_tx_fifo_flush(this, 0x10); + dwc2_rx_fifo_flush(this, 0); + + grxfsiz.rxfdep = RX_FIFO_SIZE; + writel(grxfsiz.d32, ®s->core.grxfsiz); + + dtxfsiz0.dtxfdep = DTX_FIFO_SIZE_0; + dtxfsiz0.dtxfstaddr = DTX_FIFO_SIZE_0_OFFSET; + writel(dtxfsiz0.d32, ®s->core.gnptxfsiz); + + dtxfsiz1.dtxfdep = DTX_FIFO_SIZE_1; + dtxfsiz1.dtxfstaddr = DTX_FIFO_SIZE_1_OFFSET; + writel(dtxfsiz1.d32, ®s->core.dptxfsiz_dieptxf[0]); + + dtxfsiz2.dtxfdep = DTX_FIFO_SIZE_2; + dtxfsiz2.dtxfstaddr = DTX_FIFO_SIZE_2_OFFSET; + writel(dtxfsiz2.d32, ®s->core.dptxfsiz_dieptxf[1]); + + /* Config Ep0 */ + dwc2_start_ep0(this); + + dwc2_enqueue_packet(this, 0, 0, p->setup_buf, 8, 0, 0); + + depint_msk.xfercompl = 1; + depint_msk.epdisbld = 1; + depint_msk.ahberr = 1; + depint_msk.setup = 1; + + /* device IN interrupt mask */ + writel(depint_msk.d32, ®s->device.diepmsk); + /* device OUT interrupt mask */ + writel(depint_msk.d32, ®s->device.doepmsk); + + /* Clear all pending interrupt */ + writel(0xffffffff, ®s->device.daint); + + /* Config core interface regs */ + writel(0xffffffff, ®s->core.gintsts); + writel(0xffffffff, ®s->core.gotgint); + + /* Enable device endpoint interrupt */ + writel(0xffffffff, ®s->device.daintmsk); + + gintmsk.usbsusp = 1; + gintmsk.usbrst = 1; + gintmsk.enumdone = 1; + gintmsk.sessreqint = 1; + gintmsk.iepint = 1; + gintmsk.oepint = 1; + writel(gintmsk.d32, ®s->core.gintmsk); + + gahbcfg.d32 = readl(®s->core.gahbcfg); + gahbcfg.dmaen = 1; + gahbcfg.glblintrmsk = 1; + gahbcfg.hbstlen = DMA_BURST_INCR16; + writel(gahbcfg.d32, ®s->core.gahbcfg); + + dwc2_connect(this, 1); + + return 1; + +} + +struct usbdev_ctrl *dwc2_udc_init(device_descriptor_t *dd) +{ + struct usbdev_ctrl *ctrl = calloc(1, sizeof(*ctrl)); + int i; + + usb_debug("dwc2_udc_init\n"); + if (ctrl == NULL) + return NULL; + + ctrl->pdata = calloc(1, sizeof(dwc2_pdata_t)); + if (ctrl->pdata == NULL) { + free(ctrl); + return NULL; + } + memcpy(&ctrl->device_descriptor, dd, sizeof(*dd)); + SLIST_INIT(&ctrl->configs); + + ctrl->poll = dwc2_check_irq; + ctrl->add_gadget = udc_add_gadget; + ctrl->enqueue_packet = dwc2_enqueue_packet; + ctrl->shutdown = dwc2_shutdown; + ctrl->set_address = dwc2_set_address; + ctrl->stall = dwc2_stall; + ctrl->halt_ep = dwc2_halt_ep; + ctrl->start_ep = dwc2_start_ep; + ctrl->alloc_data = dwc2_malloc; + ctrl->free_data = dwc2_free; + ctrl->initialized = 0; + + ctrl->ep_mps[0][0] = 64; + ctrl->ep_mps[0][1] = 64; + for (i = 1; i < 16; i++) { + ctrl->ep_mps[i][0] = 512; + ctrl->ep_mps[i][1] = 512; + } + + if (!dwc2_reinit_udc(ctrl, (void *)0xff580000, dd)) { + free(ctrl->pdata); + free(ctrl); + return NULL; + } + + return ctrl; +} + diff --git a/payloads/libpayload/drivers/udc/dwc2_priv.h b/payloads/libpayload/drivers/udc/dwc2_priv.h new file mode 100644 index 0000000000..8ea4c3a747 --- /dev/null +++ b/payloads/libpayload/drivers/udc/dwc2_priv.h @@ -0,0 +1,65 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2015 Rockchip Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __DWC2_PRIV_H__ +#define __DWC2_PRIV_H__ +#include <usb/dwc2_registers.h> + +#define EP_MAXLEN (64 * 1024) +#define EP0_MAXLEN 64 + +#define RX_FIFO_SIZE 0x210 +#define DTX_FIFO_SIZE_0_OFFSET RX_FIFO_SIZE +#define DTX_FIFO_SIZE_0 0x10 +#define DTX_FIFO_SIZE_1_OFFSET (DTX_FIFO_SIZE_0_OFFSET +\ + DTX_FIFO_SIZE_0) +#define DTX_FIFO_SIZE_1 0x100 +#define DTX_FIFO_SIZE_2_OFFSET (DTX_FIFO_SIZE_1_OFFSET +\ + DTX_FIFO_SIZE_1) +#define DTX_FIFO_SIZE_2 0x10 + +struct job { + SIMPLEQ_ENTRY(job) queue; // linkage + void *data; + size_t length; + size_t xfered_length; + size_t xfer_length; + int zlp; // append zero length packet? + int autofree; // free after processing? +}; +SIMPLEQ_HEAD(job_queue, job); + +typedef struct dwc2_ep { + dwc2_ep_reg_t *ep_regs; + struct job_queue job_queue; + unsigned txfifo:5; + unsigned busy:1; + unsigned ep_num:8; +} dwc2_ep_t; + +typedef struct dwc2_pdata { + dwc2_reg_t *regs; + dwc2_ep_t eps[MAX_EPS_CHANNELS][2]; + uint32_t fifo_map; + void *setup_buf; +} dwc2_pdata_t; + +#define DWC2_PDATA(ctrl) ((dwc2_pdata_t *)((ctrl)->pdata)) + +#endif diff --git a/payloads/libpayload/drivers/usb/dwc2_private.h b/payloads/libpayload/drivers/usb/dwc2_private.h index fa65f8b2a2..7e6621dce8 100644 --- a/payloads/libpayload/drivers/usb/dwc2_private.h +++ b/payloads/libpayload/drivers/usb/dwc2_private.h @@ -19,621 +19,7 @@ #ifndef __DWC2_REGS_H__ #define __DWC2_REGS_H__ -#define MAX_EPS_CHANNELS 16 - -typedef struct core_reg { - uint32_t gotgctl; - uint32_t gotgint; - uint32_t gahbcfg; - uint32_t gusbcfg; - uint32_t grstctl; - uint32_t gintsts; - uint32_t gintmsk; - uint32_t grxstsr; - uint32_t grxstsp; - uint32_t grxfsiz; - uint32_t gnptxfsiz; - uint32_t gnptxsts; - uint32_t gi2cctl; - uint32_t gpvndctl; - uint32_t ggpio; - uint32_t guid; - uint32_t gsnpsid; - uint32_t ghwcfg1; - uint32_t ghwcfg2; - uint32_t ghwcfg3; - uint32_t ghwcfg4; - uint32_t reserved1[(0x100 - 0x54) / 4]; - uint32_t hptxfsiz; - uint32_t dptxfsiz_dieptxf[15]; - uint32_t reserved2[(0x400 - 0x140) / 4]; -} core_reg_t; - -typedef struct hc_reg { - uint32_t hccharn; - uint32_t hcspltn; - uint32_t hcintn; - uint32_t hcintmaskn; - uint32_t hctsizn; - uint32_t hcdman; - uint32_t reserved[2]; -} hc_reg_t; - -/* Host Mode Register Structures */ -typedef struct host_reg { - uint32_t hcfg; - uint32_t hfir; - uint32_t hfnum; - uint32_t reserved0; - uint32_t hptxsts; - uint32_t haint; - uint32_t haintmsk; - uint32_t reserved1[(0x440 - 0x41c) / 4]; - uint32_t hprt; - uint32_t reserved2[(0x500 - 0x444) / 4]; - hc_reg_t hchn[MAX_EPS_CHANNELS]; - uint32_t reserved3[(0x800 - 0x700) / 4]; -} host_reg_t; - -/* Device IN ep reg */ -typedef struct in_ep_reg { - uint32_t diepctl; - uint32_t reserved04; - uint32_t diepint; - uint32_t reserved0c; - uint32_t dieptsiz; - uint32_t diepdma; - uint32_t dtxfsts; - uint32_t diepdmab; -} in_ep_reg_t; - -typedef struct out_ep_reg { - uint32_t doepctl; - uint32_t reserved04; - uint32_t doepint; - uint32_t reserved0c; - uint32_t doeptsiz; - uint32_t doepdma; - uint32_t reserved18; - uint32_t doepdmab; -} out_ep_reg_t; - -/* Device Mode Registers Structures */ -typedef struct device_reg { - uint32_t dcfg; - uint32_t dctl; - uint32_t dsts; - uint32_t unused; - uint32_t diepmsk; - uint32_t doepmsk; - uint32_t daint; - uint32_t daintmsk; - uint32_t dtknqr1; - uint32_t dtknqr2; - uint32_t dvbusdis; - uint32_t dvbuspulse; - uint32_t dtknqr3_dthrctl; - uint32_t dtknqr4_fifoemptymsk; - uint32_t reserved1[(0x900 - 0x838) / 4]; - - in_ep_reg_t inep[MAX_EPS_CHANNELS]; - out_ep_reg_t outep[MAX_EPS_CHANNELS]; - uint32_t reserved8[(0xe00 - 0xd00) / 4]; -} device_reg_t; - -typedef struct pwr_clk_ctrl_reg { - uint32_t pcgcctl; - uint32_t reserved[(0x1000 - 0xe04) / 4]; -} pwr_clk_ctrl_reg_t; - -typedef struct data_fifo { - uint32_t dataport; - uint32_t reserved[(0x1000 - 0x004) / 4]; -} data_fifo_t; - -typedef struct dwc2_otg_reg { - core_reg_t core; - host_reg_t host; - device_reg_t device; - pwr_clk_ctrl_reg_t pcgr; - data_fifo_t dfifo[MAX_EPS_CHANNELS]; - uint32_t reserved[(0x40000 - 0x11000) / 4]; -} dwc2_reg_t; - -/** - * This union represents the bit fields of the Core AHB Configuration - * Register (GAHBCFG). - */ -typedef union { - /* raw register data */ - uint32_t d32; - /* register bits */ - struct { - unsigned glblintrmsk:1; -#define GLBINT_ENABLE 1 - - unsigned hbstlen:4; -#define DMA_BURST_SINGLE 0 -#define DMA_BURST_INCR 1 -#define DMA_BURST_INCR4 3 -#define DMA_BURST_INCR8 5 -#define DMA_BURST_INCR16 7 - - unsigned dmaen:1; - unsigned reserved:1; - unsigned nptxfemplvl:1; - unsigned ptxfemplvl:1; - unsigned reserved9_31:23; - }; -} gahbcfg_t; - -/** - * This union represents the bit fields of the Core USB Configuration - * Register (GUSBCFG). - */ -typedef union { - /* raw register data */ - uint32_t d32; - /* register bits */ - struct { - unsigned toutcal:3; - unsigned phyif:1; - unsigned ulpiutmisel:1; - unsigned fsintf:1; - unsigned physel:1; - unsigned ddrsel:1; - unsigned srpcap:1; - unsigned hnpcap:1; - unsigned usbtrdtim:4; - unsigned reserved14:1; - unsigned phylpwrclksel:1; - unsigned otgi2csel:1; - unsigned ulpifsls:1; - unsigned ulpiautores:1; - unsigned ulpiclksusm:1; - unsigned ulpiextvbusdrv:1; - unsigned ulpiextvbusindicator:1; - unsigned termseldlpulse:1; - unsigned reserved23_28:6; - unsigned forcehstmode:1; - unsigned forcedevmode:1; - unsigned cortxpkt:1; - }; -} gusbcfg_t; - -/** - * This union represents the bit fields of the Core Reset Register - * (GRSTCTL). - */ -typedef union { - /* raw register data */ - uint32_t d32; - /* register bits */ - struct { - /** Core Soft Reset (CSftRst) (Device and Host) - * - * The application can flush the control logic in the - * entire core using this bit. This bit resets the - * pipelines in the AHB Clock domain as well as the - * PHY Clock domain. - * - * The state machines are reset to an IDLE state, the - * control bits in the CSRs are cleared, all the - * transmit FIFOs and the receive FIFO are flushed. - * - * The status mask bits that control the generation of - * the interrupt, are cleared, to clear the - * interrupt. The interrupt status bits are not - * cleared, so the application can get the status of - * any events that occurred in the core after it has - * set this bit. - * - * Any transactions on the AHB are terminated as soon - * as possible following the protocol. Any - * transactions on the USB are terminated immediately. - * - * The configuration settings in the CSRs are - * unchanged, so the software doesn't have to - * reprogram these registers (Device - * Configuration/Host Configuration/Core System - * Configuration/Core PHY Configuration). - * - * The application can write to this bit, any time it - * wants to reset the core. This is a self clearing - * bit and the core clears this bit after all the - * necessary logic is reset in the core, which may - * take several clocks, depending on the current state - * of the core. - */ - unsigned csftrst:1; - /** Hclk Soft Reset - * - * The application uses this bit to reset the control logic in - * the AHB clock domain. Only AHB clock domain pipelines are - * reset. - */ - unsigned hsftrst:1; - /** Host Frame Counter Reset (Host Only)<br> - * - * The application can reset the (micro)frame number - * counter inside the core, using this bit. When the - * (micro)frame counter is reset, the subsequent SOF - * sent out by the core, will have a (micro)frame - * number of 0. - */ - unsigned frmcntrrst:1; - /** In Token Sequence Learning Queue Flush - * (INTknQFlsh) (Device Only) - */ - unsigned intknqflsh:1; - /** RxFIFO Flush (RxFFlsh) (Device and Host) - * - * The application can flush the entire Receive FIFO - * using this bit. <p>The application must first - * ensure that the core is not in the middle of a - * transaction. <p>The application should write into - * this bit, only after making sure that neither the - * DMA engine is reading from the RxFIFO nor the MAC - * is writing the data in to the FIFO. <p>The - * application should wait until the bit is cleared - * before performing any other operations. This bit - * will takes 8 clocks (slowest of PHY or AHB clock) - * to clear. - */ - unsigned rxfflsh:1; - /** TxFIFO Flush (TxFFlsh) (Device and Host). - * - * This bit is used to selectively flush a single or - * all transmit FIFOs. The application must first - * ensure that the core is not in the middle of a - * transaction. <p>The application should write into - * this bit, only after making sure that neither the - * DMA engine is writing into the TxFIFO nor the MAC - * is reading the data out of the FIFO. <p>The - * application should wait until the core clears this - * bit, before performing any operations. This bit - * will takes 8 clocks (slowest of PHY or AHB clock) - * to clear. - */ - unsigned txfflsh:1; - - /** TxFIFO Number (TxFNum) (Device and Host). - * - * This is the FIFO number which needs to be flushed, - * using the TxFIFO Flush bit. This field should not - * be changed until the TxFIFO Flush bit is cleared by - * the core. - * - 0x0 : Non Periodic TxFIFO Flush - * - 0x1 : Periodic TxFIFO #1 Flush in device mode - * or Periodic TxFIFO in host mode - * - 0x2 : Periodic TxFIFO #2 Flush in device mode. - * - ... - * - 0xF : Periodic TxFIFO #15 Flush in device mode - * - 0x10: Flush all the Transmit NonPeriodic and - * Transmit Periodic FIFOs in the core - */ - unsigned txfnum:5; - /** Reserved */ - unsigned reserved11_29:19; - /** DMA Request Signal. Indicated DMA request is in - * probress. Used for debug purpose. */ - unsigned dmareq:1; - /** AHB Master Idle. Indicates the AHB Master State - * Machine is in IDLE condition. */ - unsigned ahbidle:1; - } ; -} grstctl_t; - -/** - * This union represents the bit fields of the Core Interrupt Mask - * Register (GINTMSK). - */ -typedef union { - /* raw register data */ - uint32_t d32; - /* register bits */ - struct { - unsigned curmod:1; - unsigned modemis:1; - unsigned otgint:1; - unsigned sof:1; - unsigned rxflvl:1; - unsigned nptxfemp:1; - unsigned ginnakeff:1; - unsigned goutnakeff:1; - unsigned reserved8:1; - unsigned i2cint:1; - unsigned erlysusp:1; - unsigned usbsusp:1; - unsigned usbrst:1; - unsigned enumdone:1; - unsigned isooutdrop:1; - unsigned eopf:1; - unsigned reserved16_20:5; - unsigned incompip:1; - unsigned reserved22_23:2; - unsigned prtint:1; - unsigned hchint:1; - unsigned ptxfemp:1; - unsigned reserved27:1; - unsigned conidstschng:1; - unsigned disconnint:1; - unsigned sessreqint:1; - unsigned wkupint:1; - } ; -} gintmsk_t; - -/** -* This union represents the bit fields of the Core Non-Periodic -* Transmit FIFO Size Register(GNPTXFSIZ). -*/ -typedef union { - /* raw register data */ - uint32_t d32; - /* register bits */ - struct { - unsigned nptxfstaddr:16; - unsigned nptxfdep:16; - }; -} gnptxfsiz_t; - -/** - * This union represents the bit fields of the Core Receive FIFO Size - * Register(GRXFSIZ). - */ -typedef union { - /* raw register data */ - uint32_t d32; - /* register bits */ - /*The value in this fieles is in terms of 32-bit words size. - */ - struct { - unsigned rxfdep:16; - unsigned reserved:16; - }; -} grxfsiz_t; - -/** - * This union represents the bit fields of the Core Interrupt Register - * (GINTSTS). - */ -typedef union { - /* raw register data */ - uint32_t d32; -#define SOF_INTR_MASK 0x0008 - /* register bits */ - struct { - unsigned curmod:1; -#define HOST_MODE 1 -#define DEVICE_MODE 0 - unsigned modemis:1; - unsigned otgint:1; - unsigned sof:1; - unsigned rxflvl:1; - unsigned nptxfemp:1; - unsigned ginnakeff:1; - unsigned goutnakeff:1; - unsigned reserved8:1; - unsigned i2cint:1; - unsigned erlysusp:1; - unsigned usbsusp:1; - unsigned usbrst:1; - unsigned enumdone:1; - unsigned isooutdrop:1; - unsigned eopf:1; - unsigned reserved16_20:5; - unsigned incompip:1; - unsigned reserved22_23:2; - unsigned prtint:1; - unsigned hchint:1; - unsigned ptxfemp:1; - unsigned reserved27:1; - unsigned conidstschng:1; - unsigned disconnint:1; - unsigned sessreqint:1; - unsigned wkupint:1; - }; -} gintsts_t; - -/** - * This union represents the bit fields of the User HW Config3 Register - * (GHWCFG3). - */ -typedef union { - /* raw register data */ - uint32_t d32; - /* register bits */ - struct { - unsigned reserved:16; - unsigned dfifodepth:16; - }; -} ghwcfg3_t; - -/** - * This union represents the bit fields in the Host Configuration Register. - */ -typedef union { - /* raw register data */ - uint32_t d32; - - /* register bits */ - struct { - /** FS/LS Phy Clock Select */ - unsigned fslspclksel:2; -#define PHYCLK_30_60_MHZ 0 -#define PHYCLK_48_MHZ 1 -#define PHYCLK_6_MHZ 2 - - /** FS/LS Only Support */ - unsigned fslssupp:1; - }; -} hcfg_t; - -/** - * This union represents the bit fields in the Host Port Control and status - * Register. - */ -typedef union { - /* raw register data */ - uint32_t d32; - /* register bits */ - struct { - unsigned prtconnsts:1; - unsigned prtconndet:1; - unsigned prtena:1; - unsigned prtenchng:1; - unsigned prtovrcurract:1; - unsigned prtovrcurrchng:1; - unsigned prtres:1; - unsigned prtsusp:1; - unsigned prtrst:1; - unsigned reserved9:1; - unsigned prtlnsts:2; - unsigned prtpwr:1; - unsigned prttstctl:4; - unsigned prtspd:2; -#define PRTSPD_HIGH 0 -#define PRTSPD_FULL 1 -#define PRTSPD_LOW 2 - unsigned reserved19_31:13; - }; -} hprt_t; -/* Mask W1C bits */ -#define HPRT_W1C_MASK (~((1 << 1) | (1 << 2) | (1 << 3) | (1 << 5))) - -/** - * This union represents the bit fields in the Host Channel Characteristics - * Register. - */ -typedef union { - /* raw register data */ - uint32_t d32; - - /* register bits */ - struct { - /** Maximum packet size in bytes */ - unsigned mps:11; - /** Endpoint number */ - unsigned epnum:4; - /** 0: OUT, 1: IN */ - unsigned epdir:1; - unsigned reserved:1; - /** 0: Full/high speed device, 1: Low speed device */ - unsigned lspddev:1; - /** 0: Control, 1: Isoc, 2: Bulk, 3: Intr */ - unsigned eptype:2; - /** Packets per frame for periodic transfers. 0 is reserved. */ - unsigned multicnt:2; - /** Device address */ - unsigned devaddr:7; - /** - * Frame to transmit periodic transaction. - * 0: even, 1: odd - */ - unsigned oddfrm:1; - /** Channel disable */ - unsigned chdis:1; - /** Channel enable */ - unsigned chen:1; - }; -} hcchar_t; - -typedef enum { - EPDIR_OUT = 0, - EPDIR_IN, -} ep_dir_t; - -/** - * This union represents the bit fields in the Host All Interrupt - * Register. - */ -typedef union { - /* raw register data */ - uint32_t d32; - /* register bits */ - struct { - /** Transfer Complete */ - unsigned xfercomp:1; - /** Channel Halted */ - unsigned chhltd:1; - /** AHB Error */ - unsigned ahberr:1; - /** STALL Response Received */ - unsigned stall:1; - /** NAK Response Received */ - unsigned nak:1; - /** ACK Response Received */ - unsigned ack:1; - /** NYET Response Received */ - unsigned nyet:1; - /** Transaction Err */ - unsigned xacterr:1; - /** Babble Error */ - unsigned bblerr:1; - /** Frame Overrun */ - unsigned frmovrun:1; - /** Data Toggle Error */ - unsigned datatglerr:1; - /** Reserved */ - unsigned reserved:21; - }; -} hcint_t; - -/** - * This union represents the bit fields in the Host Channel Transfer Size - * Register. - */ -typedef union { - /* raw register data */ - uint32_t d32; - - /* register bits */ - struct { - /* Total transfer size in bytes */ - unsigned xfersize:19; - /** Data packets to transfer */ - unsigned pktcnt:10; - /** - * Packet ID for next data packet - * 0: DATA0 - * 1: DATA2 - * 2: DATA1 - * 3: MDATA (non-Control), SETUP (Control) - */ - unsigned pid:2; -#define PID_DATA0 0 -#define PID_DATA1 2 -#define PID_DATA2 1 -#define PID_MDATA 3 -#define PID_SETUP 3 - /* Do PING protocol when 1 */ - unsigned dopng:1; - }; -} hctsiz_t; - -/** - * This union represents the bit fields in the Host Channel Interrupt Mask - * Register. - */ -typedef union { - /* raw register data */ - uint32_t d32; - /* register bits */ - struct { - unsigned xfercomp:1; - unsigned chhltd:1; - unsigned ahberr:1; - unsigned stall:1; - unsigned nak:1; - unsigned ack:1; - unsigned nyet:1; - unsigned xacterr:1; - unsigned bblerr:1; - unsigned frmovrun:1; - unsigned datatglerr:1; - unsigned reserved:21; - }; -} hcintmsk_t; +#include <usb/dwc2_registers.h> typedef struct dwc_ctrl { #define DMA_SIZE (64 * 1024) |