summaryrefslogtreecommitdiff
path: root/chip/g/usb_console.c
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2014-11-19 14:37:10 -0800
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-04-02 20:27:46 +0000
commit4d0aad889476e3012f970fffa5305d5c9386cc47 (patch)
treeb9cf28b3a7b5ca1695c07d5451b4f170626e6136 /chip/g/usb_console.c
parentde24d511621fcc98b36152a7c373eeaa566fea74 (diff)
downloadchrome-ec-4d0aad889476e3012f970fffa5305d5c9386cc47.tar.gz
cr50: add USB support
Add a USB device driver for the Synopsys DWC USB device controller. The common USB protocol stack code still need to be de-duplicated with the STM32 implementation. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BRANCH=none BUG=chrome-os-partner:33919 TEST=plug Cr50 to a Linux workstation and see USB descriptors using "lsusb -v -d 18d1:5014" Change-Id: I4a367241053de2c2d94aa06f82ea4bee51f9f89a Reviewed-on: https://chromium-review.googlesource.com/231160 Commit-Queue: Vincent Palatin <vpalatin@chromium.org> Trybot-Ready: Vincent Palatin <vpalatin@chromium.org> Tested-by: Vincent Palatin <vpalatin@chromium.org> Reviewed-by: Bill Richardson <wfrichar@chromium.org>
Diffstat (limited to 'chip/g/usb_console.c')
-rw-r--r--chip/g/usb_console.c266
1 files changed, 266 insertions, 0 deletions
diff --git a/chip/g/usb_console.c b/chip/g/usb_console.c
new file mode 100644
index 0000000000..6f84001007
--- /dev/null
+++ b/chip/g/usb_console.c
@@ -0,0 +1,266 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "common.h"
+#include "config.h"
+#include "console.h"
+#include "link_defs.h"
+#include "printf.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+#include "usb.h"
+
+/* Console output macro */
+#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args)
+
+#define USB_CONSOLE_TIMEOUT_US (30 * MSEC)
+#define USB_CONSOLE_RX_BUF_SIZE 16
+#define RX_BUF_NEXT(i) (((i) + 1) & (USB_CONSOLE_RX_BUF_SIZE - 1))
+
+static volatile char rx_buf[USB_CONSOLE_RX_BUF_SIZE];
+static volatile int rx_buf_head;
+static volatile int rx_buf_tail;
+
+static int last_tx_ok = 1;
+
+static int is_reset;
+static int is_enabled = 1;
+
+/* USB-Serial descriptors */
+const struct usb_interface_descriptor USB_IFACE_DESC(USB_IFACE_CONSOLE) =
+{
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = USB_IFACE_CONSOLE,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = USB_SUBCLASS_GOOGLE_SERIAL,
+ .bInterfaceProtocol = USB_PROTOCOL_GOOGLE_SERIAL,
+ .iInterface = USB_STR_CONSOLE_NAME,
+};
+const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_CONSOLE, 0) =
+{
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x80 | USB_EP_CONSOLE,
+ .bmAttributes = 0x02 /* Bulk IN */,
+ .wMaxPacketSize = USB_MAX_PACKET_SIZE,
+ .bInterval = 10
+};
+const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_CONSOLE, 1) =
+{
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_EP_CONSOLE,
+ .bmAttributes = 0x02 /* Bulk OUT */,
+ .wMaxPacketSize = USB_MAX_PACKET_SIZE,
+ .bInterval = 0
+};
+
+static usb_uint ep_buf_tx[USB_MAX_PACKET_SIZE / 2] /*__usb_ram*/;
+static usb_uint ep_buf_rx[USB_MAX_PACKET_SIZE / 2] /*__usb_ram*/;
+static struct g_usb_desc ep_out_desc;
+static struct g_usb_desc ep_in_desc;
+
+static void con_ep_tx(void)
+{
+ /* clear IT */
+ GR_USB_DIEPINT(USB_EP_CONSOLE) = 0xffffffff;
+}
+
+static void con_ep_rx(void)
+{
+ int i;
+ int rx_size = USB_MAX_PACKET_SIZE
+ - (ep_out_desc.flags & DOEPDMA_RXBYTES_MASK);
+
+ for (i = 0; i < rx_size; i++) {
+ int rx_buf_next = RX_BUF_NEXT(rx_buf_head);
+ if (rx_buf_next != rx_buf_tail) {
+ rx_buf[rx_buf_head] = ((i & 1) ?
+ (ep_buf_rx[i >> 1] >> 8) :
+ (ep_buf_rx[i >> 1] & 0xff));
+ rx_buf_head = rx_buf_next;
+ }
+ }
+
+ ep_out_desc.flags = DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) |
+ DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC;
+ GR_USB_DOEPCTL(USB_EP_CONSOLE) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+ /* clear IT */
+ GR_USB_DOEPINT(USB_EP_CONSOLE) = 0xffffffff;
+
+ /* wake-up the console task */
+ console_has_input();
+}
+
+static void ep_reset(void)
+{
+ ep_out_desc.flags = DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) |
+ DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC;
+ ep_out_desc.addr = ep_buf_rx;
+ GR_USB_DOEPDMA(USB_EP_CONSOLE) = (uint32_t)&ep_out_desc;
+ ep_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_BSY | DIEPDMA_IOC;
+ ep_in_desc.addr = ep_buf_tx;
+ GR_USB_DIEPDMA(USB_EP_CONSOLE) = (uint32_t)&ep_in_desc;
+ GR_USB_DOEPCTL(USB_EP_CONSOLE) = DXEPCTL_MPS(64) | DXEPCTL_USBACTEP |
+ DXEPCTL_EPTYPE_BULK |
+ DXEPCTL_CNAK | DXEPCTL_EPENA;
+ GR_USB_DIEPCTL(USB_EP_CONSOLE) = DXEPCTL_MPS(64) | DXEPCTL_USBACTEP |
+ DXEPCTL_EPTYPE_BULK |
+ DXEPCTL_TXFNUM(USB_EP_CONSOLE);
+ GR_USB_DAINTMSK |= (1<<USB_EP_CONSOLE) | (1 << (USB_EP_CONSOLE+16));
+
+ is_reset = 1;
+}
+
+USB_DECLARE_EP(USB_EP_CONSOLE, con_ep_tx, con_ep_rx, ep_reset);
+
+static int __tx_char(void *context, int c)
+{
+ usb_uint *buf = (usb_uint *)ep_buf_tx;
+ int *tx_idx = context;
+
+ /* Do newline to CRLF translation */
+ if (c == '\n' && __tx_char(context, '\r'))
+ return 1;
+
+ if (*tx_idx > 63)
+ return 1;
+ if (!(*tx_idx & 1))
+ buf[*tx_idx/2] = c;
+ else
+ buf[*tx_idx/2] |= c << 8;
+ (*tx_idx)++;
+
+ return 0;
+}
+
+static void usb_enable_tx(int len)
+{
+ if (!is_enabled)
+ return;
+
+ ep_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_RDY | DIEPDMA_IOC |
+ DIEPDMA_TXBYTES(len);
+ GR_USB_DIEPCTL(USB_EP_CONSOLE) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+}
+
+static inline int usb_console_tx_valid(void)
+{
+ return (ep_in_desc.flags & DIEPDMA_BS_MASK) == DIEPDMA_BS_DMA_DONE;
+}
+
+static int usb_wait_console(void)
+{
+ timestamp_t deadline = get_time();
+ int wait_time_us = 1;
+
+ deadline.val += USB_CONSOLE_TIMEOUT_US;
+
+ /*
+ * If the USB console is not used, Tx buffer would never free up.
+ * In this case, let's drop characters immediately instead of sitting
+ * for some time just to time out. On the other hand, if the last
+ * Tx is good, it's likely the host is there to receive data, and
+ * we should wait so that we don't clobber the buffer.
+ */
+ if (last_tx_ok) {
+ while (usb_console_tx_valid() || !is_reset) {
+ if (timestamp_expired(deadline, NULL) ||
+ in_interrupt_context()) {
+ last_tx_ok = 0;
+ return EC_ERROR_TIMEOUT;
+ }
+ if (wait_time_us < MSEC)
+ udelay(wait_time_us);
+ else
+ usleep(wait_time_us);
+ wait_time_us *= 2;
+ }
+
+ return EC_SUCCESS;
+ } else {
+ last_tx_ok = !usb_console_tx_valid();
+ return EC_SUCCESS;
+ }
+}
+
+/*
+ * Public USB console implementation below.
+ */
+int usb_getc(void)
+{
+ int c;
+
+ if (rx_buf_tail == rx_buf_head)
+ return -1;
+
+ if (!is_enabled)
+ return -1;
+
+ c = rx_buf[rx_buf_tail];
+ rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
+ return c;
+}
+
+int usb_putc(int c)
+{
+ int ret;
+ int tx_idx = 0;
+
+ ret = usb_wait_console();
+ if (ret)
+ return ret;
+
+ ret = __tx_char(&tx_idx, c);
+ usb_enable_tx(tx_idx);
+
+ return ret;
+}
+
+int usb_puts(const char *outstr)
+{
+ int ret;
+ int tx_idx = 0;
+
+ ret = usb_wait_console();
+ if (ret)
+ return ret;
+
+ /* Put all characters in the output buffer */
+ while (*outstr) {
+ if (__tx_char(&tx_idx, *outstr++) != 0)
+ break;
+ }
+
+ usb_enable_tx(tx_idx);
+
+ /* Successful if we consumed all output */
+ return *outstr ? EC_ERROR_OVERFLOW : EC_SUCCESS;
+}
+
+int usb_vprintf(const char *format, va_list args)
+{
+ int ret;
+ int tx_idx = 0;
+
+ ret = usb_wait_console();
+ if (ret)
+ return ret;
+
+ ret = vfnprintf(__tx_char, &tx_idx, format, args);
+
+ usb_enable_tx(tx_idx);
+ return ret;
+}
+
+void usb_console_enable(int enabled)
+{
+ is_enabled = enabled;
+}