diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/SLOF/lib/libusb | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/SLOF/lib/libusb')
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/Makefile | 52 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/tools.h | 77 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-core.c | 590 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-core.h | 279 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-ehci.c | 609 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-ehci.h | 155 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-hid.c | 453 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-hub.c | 183 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-key.c | 446 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-key.h | 42 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-ohci.c | 1055 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-ohci.h | 217 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-slof.c | 61 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-xhci.c | 1316 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-xhci.h | 386 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb.code | 162 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb.h | 77 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb.in | 29 |
18 files changed, 6189 insertions, 0 deletions
diff --git a/qemu/roms/SLOF/lib/libusb/Makefile b/qemu/roms/SLOF/lib/libusb/Makefile new file mode 100644 index 000000000..0780489ea --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/Makefile @@ -0,0 +1,52 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +include $(TOPCMNDIR)/make.rules + +ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames +CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \ + -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) -I$(SLOFCMNDIR) +LDFLAGS = -nostdlib + +TARGET = ../libusb.a + + +all: $(TARGET) + +SRCS = usb-core.c usb-ohci.c usb-ehci.c usb-slof.c usb-key.c usb-hid.c \ + usb-hub.c usb-xhci.c + +OBJS = $(SRCS:%.c=%.o) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep + +# Include dependency file if available: +-include Makefile.dep diff --git a/qemu/roms/SLOF/lib/libusb/tools.h b/qemu/roms/SLOF/lib/libusb/tools.h new file mode 100644 index 000000000..f531175c1 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/tools.h @@ -0,0 +1,77 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef __TOOLS_H +#define __TOOLS_H + +#include <stdint.h> +#include <byteorder.h> +#include <cache.h> + +#define PTR_U32(x) ((uint32_t) (uint64_t) (x)) + +static inline uint32_t read_reg32(uint32_t *reg) +{ + return bswap_32(ci_read_32(reg)); +} + +static inline void write_reg32(uint32_t *reg, uint32_t value) +{ + mb(); + ci_write_32(reg, bswap_32(value)); +} + +static inline uint8_t read_reg8(uint8_t *reg) +{ + return ci_read_8(reg); +} + +static inline void write_reg8(uint8_t *reg, uint8_t value) +{ + mb(); + ci_write_8(reg, value); +} + +static inline uint16_t read_reg16(uint16_t *reg) +{ + return bswap_16(ci_read_16(reg)); +} + +static inline void write_reg16(uint16_t *reg, uint16_t value) +{ + mb(); + ci_write_16(reg, bswap_16(value)); +} + +static inline uint64_t read_reg64(uint64_t *reg) +{ + return bswap_64(ci_read_64(reg)); +} + +static inline void write_reg64(uint64_t *reg, uint64_t value) +{ + mb(); + ci_write_64(reg, bswap_64(value)); +} + +static inline uint32_t ci_read_reg(uint32_t *reg) +{ + return bswap_32(ci_read_32(reg)); +} + +static inline void ci_write_reg(uint32_t *reg, uint32_t value) +{ + mb(); + ci_write_32(reg, bswap_32(value)); +} + +#endif diff --git a/qemu/roms/SLOF/lib/libusb/usb-core.c b/qemu/roms/SLOF/lib/libusb/usb-core.c new file mode 100644 index 000000000..6719c5726 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-core.c @@ -0,0 +1,590 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include "usb-core.h" + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define dprintf(_x ...) do { printf(_x); } while(0) +#else +#define dprintf(_x ...) +#endif + +#define __unused __attribute__((unused)) + +struct usb_hcd_ops *head; +struct usb_dev *devpool; +#define USB_DEVPOOL_SIZE 4096 + +static struct usb_dev *usb_alloc_devpool(void) +{ + struct usb_dev *head, *curr, *prev; + unsigned int dev_count = 0, i; + + head = SLOF_alloc_mem(USB_DEVPOOL_SIZE); + if (!head) + return NULL; + + dev_count = USB_DEVPOOL_SIZE/sizeof(struct usb_dev); + dprintf("%s: %d number of devices\n", __func__, dev_count); + /* Although an array, link them*/ + for (i = 0, curr = head, prev = NULL; i < dev_count; i++, curr++) { + if (prev) + prev->next = curr; + curr->next = NULL; + prev = curr; + } + +#ifdef DEBUG + for (i = 0, curr = head; curr; curr = curr->next) + printf("%s: %d dev %p\n", __func__, i++, curr); +#endif + + return head; +} + +struct usb_dev *usb_devpool_get(void) +{ + struct usb_dev *new; + + if (!devpool) { + devpool = usb_alloc_devpool(); + if (!devpool) + return NULL; + } + + new = devpool; + devpool = devpool->next; + memset(new, 0, sizeof(*new)); + new->next = NULL; + return new; +} + +void usb_devpool_put(struct usb_dev *dev) +{ + struct usb_dev *curr; + if (!dev && !devpool) + return; + + curr = devpool; + while (curr->next) + curr = curr->next; + curr->next = dev; + dev->next = NULL; +} + +#ifndef DEBUG +#define validate_hcd_ops(dev) (dev && dev->hcidev && dev->hcidev->ops) +#else +int validate_hcd_ops(struct usb_dev *dev) +{ + int ret = true; + + if (!dev) { + printf("dev is NULL\n"); + ret = false; + } else if (!dev->hcidev) { + printf("hcidev is NULL\n"); + ret = false; + } else if (!dev->hcidev->ops) { + printf("ops is NULL\n"); + ret = false; + } + return ret; +} +#endif + +struct usb_pipe *usb_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, + char *buf, size_t len) +{ + if (validate_hcd_ops(dev) && dev->hcidev->ops->get_pipe) + return dev->hcidev->ops->get_pipe(dev, ep, buf, len); + else { + printf("%s: Failed\n", __func__); + return NULL; + } +} + +void usb_put_pipe(struct usb_pipe *pipe) +{ + struct usb_dev *dev = NULL; + if (pipe && pipe->dev) { + dev = pipe->dev; + if (validate_hcd_ops(dev) && dev->hcidev->ops->put_pipe) + dev->hcidev->ops->put_pipe(pipe); + } +} + +int usb_poll_intr(struct usb_pipe *pipe, uint8_t *buf) +{ + struct usb_dev *dev = NULL; + if (pipe && pipe->dev) { + dev = pipe->dev; + if (validate_hcd_ops(dev) && dev->hcidev->ops->poll_intr) + return dev->hcidev->ops->poll_intr(pipe, buf); + } + return 0; +} + +void usb_hcd_register(struct usb_hcd_ops *ops) +{ + struct usb_hcd_ops *list; + + if (!ops) + printf("Error"); + dprintf("Registering %s %d\n", ops->name, ops->usb_type); + + if (head) { + list = head; + while (list->next) + list = list->next; + list->next = ops; + } else + head = ops; +} + +void usb_hcd_init(void *hcidev) +{ + struct usb_hcd_dev *dev = hcidev; + struct usb_hcd_ops *list = head; + + if (!dev) { + printf("Device Error"); + return; + } + + while (list) { + if (list->usb_type == dev->type) { + dprintf("usb_ops(%p) for the controller found\n", list); + dev->ops = list; + dev->ops->init(dev); + return; + } + list = list->next; + } + + dprintf("usb_ops for the controller not found\n"); +} + +void usb_hcd_exit(void *_hcidev) +{ + struct usb_hcd_dev *hcidev = _hcidev; + + dprintf("%s: enter \n", __func__); + if (!hcidev) { + printf("Device Error"); + return; + } + + if (hcidev->ops->exit) + hcidev->ops->exit(hcidev); +} + +int usb_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data) +{ + struct usb_dev *dev = NULL; + if (!pipe) + return false; + dev = pipe->dev; + if (validate_hcd_ops(dev) && dev->hcidev->ops->send_ctrl) + return dev->hcidev->ops->send_ctrl(pipe, req, data); + else { + printf("%s: Failed\n", __func__); + return false; + } +} + +int usb_transfer_ctrl(void *dev, void *req, void *data) +{ + struct usb_pipe *pipe = NULL; + struct usb_dev *usbdev; + + if (!dev) + return false; + usbdev = (struct usb_dev *)dev; + pipe = usbdev->control; + return usb_send_ctrl(pipe, req, data); +} + +int usb_transfer_bulk(void *dev, int dir, void *td, void *td_phys, void *data, int size) +{ + struct usb_pipe *pipe = NULL; + struct usb_dev *usbdev; + + if (!dev) + return false; + usbdev = (struct usb_dev *)dev; + pipe = (dir == USB_PIPE_OUT) ? usbdev->bulk_out : usbdev->bulk_in; + if (!pipe) + return false; + if (validate_hcd_ops(usbdev) && usbdev->hcidev->ops->transfer_bulk) + return usbdev->hcidev->ops->transfer_bulk(pipe, td, td_phys, data, size); + else { + printf("%s: Failed\n", __func__); + return false; + } +} + +/* + * USB Specification 1.1 + * 9.3 USB Device Requests + * 9.4 Standard Device Requests + */ +static int usb_set_address(struct usb_dev *dev, uint32_t port) +{ + struct usb_dev_req req; + struct usb_hcd_dev *hcidev; + + if (!dev) + return false; + + hcidev = dev->hcidev; + req.bmRequestType = 0; + req.bRequest = REQ_SET_ADDRESS; + req.wIndex = 0; + req.wLength = 0; + req.wValue = cpu_to_le16((uint16_t)(hcidev->nextaddr)); + if (usb_send_ctrl(dev->control, &req, NULL)) { + dev->addr = hcidev->nextaddr++; + return true; + } else + return false; +} + +static int usb_get_device_descr(struct usb_dev *dev, void *data, size_t size) +{ + struct usb_dev_req req; + + if (!dev) + return false; + + req.bmRequestType = 0x80; + req.bRequest = REQ_GET_DESCRIPTOR; + req.wIndex = 0; + req.wLength = cpu_to_le16((uint16_t) size); + req.wValue = cpu_to_le16(DESCR_TYPE_DEVICE << 8); + return usb_send_ctrl(dev->control, &req, data); +} + +static int usb_get_config_descr(struct usb_dev *dev, void *data, size_t size) +{ + struct usb_dev_req req; + + if (!dev) + return false; + + req.bmRequestType = 0x80; + req.bRequest = REQ_GET_DESCRIPTOR; + req.wIndex = 0; + req.wLength = cpu_to_le16((uint16_t) size); + req.wValue = cpu_to_le16(DESCR_TYPE_CONFIGURATION << 8); + return usb_send_ctrl(dev->control, &req, data); + +} + +static int usb_set_config(struct usb_dev *dev, uint8_t cfg_value) +{ + struct usb_dev_req req; + + if (!dev) + return false; + + req.bmRequestType = 0x00; + req.bRequest = REQ_SET_CONFIGURATION; + req.wIndex = 0; + req.wLength = 0; + req.wValue = cpu_to_le16(0x00FF & cfg_value); + return usb_send_ctrl(dev->control, &req, NULL); +} + +static int usb_clear_halt(struct usb_pipe *pipe) +{ + struct usb_dev_req req; + struct usb_dev *dev; + + if (pipe && pipe->dev) { + dev = pipe->dev; + dprintf("Clearing port %d dir %d type %d\n", + pipe->epno, pipe->dir, pipe->type); + req.bmRequestType = REQT_DIR_OUT | REQT_REC_EP; + req.bRequest = REQ_CLEAR_FEATURE; + req.wValue = FEATURE_ENDPOINT_HALT; + req.wIndex = cpu_to_le16(pipe->epno | pipe->dir); + req.wLength = 0; + return usb_send_ctrl(dev->control, &req, NULL); + } + return false; +} + +int usb_dev_populate_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, + void *buf, size_t len) +{ + uint8_t dir, type; + + dir = (ep->bEndpointAddress & 0x80) >> 7; + type = ep->bmAttributes & USB_EP_TYPE_MASK; + + dprintf("EP: %s: %d size %d type %d\n", dir ? "IN " : "OUT", + ep->bEndpointAddress & 0xF, le16_to_cpu(ep->wMaxPacketSize), + type); + if (type == USB_EP_TYPE_BULK) { + if (dir) + dev->bulk_in = usb_get_pipe(dev, ep, buf, len); + else + dev->bulk_out = usb_get_pipe(dev, ep, buf, len); + } else if (type == USB_EP_TYPE_INTR) + dev->intr = usb_get_pipe(dev, ep, buf, len); + + return true; +} + +static void usb_dev_copy_epdesc(struct usb_dev *dev, struct usb_ep_descr *ep) +{ + uint32_t ep_cnt; + + ep_cnt = dev->ep_cnt; + if (ep_cnt < USB_DEV_EP_MAX) + memcpy((void *)&dev->ep[ep_cnt], ep, sizeof(*ep)); + else + dprintf("usb-core: only %d EPs supported\n", USB_DEV_EP_MAX); + dev->ep_cnt++; +} + +int usb_hid_init(void *vdev) +{ + struct usb_dev *dev; + dev = (struct usb_dev *) vdev; + if (!dev) + return false; + if (dev->class == DEV_HID_KEYB) + usb_hid_kbd_init(dev); + return true; +} + +int usb_hid_exit(void *vdev) +{ + struct usb_dev *dev; + dev = (struct usb_dev *) vdev; + if (!dev) + return false; + if (dev->class == DEV_HID_KEYB) + usb_hid_kbd_exit(dev); + return true; +} + +#define usb_get_intf_class(x) ((x & 0x00FF0000) >> 16) + +int usb_msc_init(void *vdev) +{ + struct usb_dev *dev; + int i; + + dev = (struct usb_dev *) vdev; + dprintf("%s: enter %x\n", __func__, dev->class); + if (!dev) + return false; + if (usb_get_intf_class(dev->class) == 8) { + for (i = 0; i < dev->ep_cnt; i++) { + if ((dev->ep[i].bmAttributes & USB_EP_TYPE_MASK) + == USB_EP_TYPE_BULK) + usb_dev_populate_pipe(dev, &dev->ep[i], NULL, 0); + } + } + return true; +} + +int usb_msc_exit(void *vdev) +{ + struct usb_dev *dev; + dev = (struct usb_dev *) vdev; + dprintf("%s: enter %x\n", __func__, dev->class); + if (!dev) + return false; + if (usb_get_intf_class(dev->class) == 8) { + if (dev->bulk_in) + usb_put_pipe(dev->bulk_in); + if (dev->bulk_out) + usb_put_pipe(dev->bulk_out); + } + return true; +} + +static int usb_msc_reset(struct usb_dev *dev) +{ + struct usb_dev_req req; + + if (!dev) + return false; + + req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_OUT; + req.bRequest = 0xFF; + req.wLength = 0; + req.wValue = 0; + req.wIndex = cpu_to_le16(dev->intf_num); + return usb_send_ctrl(dev->control, &req, NULL); +} + +void usb_msc_resetrecovery(struct usb_dev *dev) +{ + // usb_msc_reset(dev); + usb_clear_halt(dev->bulk_in); + usb_clear_halt(dev->bulk_out); + SLOF_msleep(2); +} + +static int usb_handle_device(struct usb_dev *dev, struct usb_dev_config_descr *cfg, + uint8_t *ptr, uint16_t len) +{ + struct usb_dev_intf_descr *intf = NULL; + struct usb_ep_descr *ep = NULL; + struct usb_dev_hid_descr *hid __unused = NULL; + uint8_t desc_len, desc_type; + + len -= sizeof(struct usb_dev_config_descr); + ptr = (uint8_t *)(ptr + sizeof(struct usb_dev_config_descr)); + + while (len > 0) { + desc_len = *ptr; + desc_type = *(ptr + 1); + switch (desc_type) { + case DESCR_TYPE_INTERFACE: + intf = (struct usb_dev_intf_descr *)ptr; + dev->class = intf->bInterfaceClass << 16 | + intf->bInterfaceSubClass << 8 | + intf->bInterfaceProtocol; + break; + case DESCR_TYPE_ENDPOINT: + ep = (struct usb_ep_descr *)ptr; + dev->intf_num = intf->bInterfaceNumber; + usb_dev_copy_epdesc(dev, ep); + break; + case DESCR_TYPE_HID: + hid = (struct usb_dev_hid_descr *)ptr; + dprintf("hid-report %d size %d\n", + hid->bReportType, le16_to_cpu(hid->wReportLength)); + break; + case DESCR_TYPE_HUB: + break; + default: + printf("ptr %p desc_type %d\n", ptr, desc_type); + } + ptr += desc_len; + len -= desc_len; + } + return true; +} + +int setup_new_device(struct usb_dev *dev, unsigned int port) +{ + struct usb_dev_descr descr; + struct usb_dev_config_descr cfg; + struct usb_ep_descr ep; + uint16_t len; + void *data = NULL; + + dprintf("usb: %s - port %d\n", __func__, port); + + dev->addr = 0; + dev->port = port; + ep.bEndpointAddress = 0; + ep.bmAttributes = USB_EP_TYPE_CONTROL; + ep.wMaxPacketSize = cpu_to_le16(8); + dev->control = usb_get_pipe(dev, &ep, NULL, 0); + + if (!usb_get_device_descr(dev, &descr, 8)) + goto fail; + dev->control->mps = descr.bMaxPacketSize0; + + /* + * For USB3.0 ADDRESS-SLOT command takes care of setting + * address, skip this during generic device setup for USB3.0 + * devices + */ + if (dev->speed != USB_SUPER_SPEED) { + /* + * Qemu starts the port number from 1 which was + * revealed in bootindex and resulted in mismatch for + * storage devices names. Adjusting this here for + * compatibility. + */ + dev->port = port + 1; + if(!usb_set_address(dev, dev->port)) + goto fail; + } + mb(); + SLOF_msleep(100); + + if (!usb_get_device_descr(dev, &descr, sizeof(struct usb_dev_descr))) + goto fail; + + if (!usb_get_config_descr(dev, &cfg, sizeof(struct usb_dev_config_descr))) + goto fail; + + len = le16_to_cpu(cfg.wTotalLength); + /* No device config descriptor present */ + if (len == sizeof(struct usb_dev_config_descr)) + goto fail; + + data = SLOF_dma_alloc(len); + if (!data) { + printf("%s: alloc failed %d\n", __func__, port); + goto fail; + } + + if (!usb_get_config_descr(dev, data, len)) + goto fail_mem_free; + if (!usb_set_config(dev, cfg.bConfigurationValue)) + goto fail_mem_free; + mb(); + SLOF_msleep(100); + + if (!usb_handle_device(dev, &cfg, data, len)) + goto fail_mem_free; + + switch (usb_get_intf_class(dev->class)) { + case 3: + dprintf("HID found %06X\n", dev->class); + slof_usb_handle(dev); + break; + case 8: + dprintf("MASS STORAGE found %d %06X\n", dev->intf_num, + dev->class); + if ((dev->class & 0x50) != 0x50) { /* Bulk-only supported */ + printf("Device not supported %06X\n", dev->class); + goto fail_mem_free; + } + + if (!usb_msc_reset(dev)) { + printf("%s: bulk reset failed\n", __func__); + goto fail_mem_free; + } + SLOF_msleep(100); + slof_usb_handle(dev); + break; + case 9: + dprintf("HUB found\n"); + slof_usb_handle(dev); + break; + default: + printf("USB Interface class -%x- Not supported\n", dev->class); + break; + } + + SLOF_dma_free(data, len); + return true; +fail_mem_free: + SLOF_dma_free(data, len); +fail: + return false; +} diff --git a/qemu/roms/SLOF/lib/libusb/usb-core.h b/qemu/roms/SLOF/lib/libusb/usb-core.h new file mode 100644 index 000000000..7441979e9 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-core.h @@ -0,0 +1,279 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef __USB_CORE_H +#define __USB_CORE_H + +#include <stdio.h> +#include <stdbool.h> +#include "helpers.h" +#include "usb.h" +#include "tools.h" + +enum usb_hcd_type { + USB_OHCI = 1, + USB_EHCI = 2, + USB_XHCI = 3, +}; + +struct usb_hcd_dev; + +struct usb_hcd_dev { + void *base; + long type; + long num; + struct usb_hcd_ops *ops; + void *priv; /* hcd owned structure */ + long nextaddr; /* address for devices */ +}; + +struct usb_pipe; + +/*******************************************/ +/* Standard Endpoint Descriptor */ +/*******************************************/ +/* bmAttributes */ +#define USB_EP_TYPE_MASK 0x03 +#define USB_EP_TYPE_CONTROL 0 +#define USB_EP_TYPE_ISOC 1 +#define USB_EP_TYPE_BULK 2 +#define USB_EP_TYPE_INTR 3 + +struct usb_ep_descr { + uint8_t bLength; /* size of descriptor */ + uint8_t bDescriptorType; /* Type = 5 */ + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; +} __attribute__((packed)); + +#define DEV_HID_KEYB 0x030101 /* class=HIB, protocol=Keyboard */ +#define DEV_HID_MOUSE 0x030102 /* class=HIB, protocol=Mouse */ +#define DEV_HUB 0x090000 /* class=HUB, subclass, protocol */ +#define DEV_MASS_RBC 0x080150 /* MassStorage, RBC, Bulk */ +#define DEV_CDROM_ATAPI 0x080250 /* MassStorage, SFF-8020i , Bulk */ +#define DEV_MASS_FLOPPY 0x080450 /* MassStorage, UFI, Bulk */ +#define DEV_MASS_ATAPI 0x080550 /* MassStorage, SFF-8070i , Bulk */ +#define DEV_MASS_SCSI 0x080650 /* MassStorage, SCSI, Bulk */ + +enum USB_SPEED_TYPE { + USB_LOW_SPEED = 0, + USB_FULL_SPEED = 1, + USB_HIGH_SPEED = 2, + USB_SUPER_SPEED = 3, +}; + +/* Max number of endpoints supported in a device */ +#define USB_DEV_EP_MAX 4 +#define USB_TIMEOUT 5000 /* 5 sec usb timeout */ + +struct usb_dev { + struct usb_dev *next; + struct usb_hcd_dev *hcidev; + struct usb_pipe *intr; + struct usb_pipe *control; + struct usb_pipe *bulk_in; + struct usb_pipe *bulk_out; + struct usb_ep_descr ep[USB_DEV_EP_MAX]; + void *priv; + uint32_t ep_cnt; + uint32_t class; + uint32_t speed; + uint32_t addr; + uint32_t mps0; + uint32_t port; + uint16_t intf_num; +}; + +#define DEVICE_KEYBOARD 1 +#define DEVICE_MOUSE 2 +#define DEVICE_DISK 3 +#define DEVICE_HUB 4 + +/* Structure in sync with FORTH code */ +struct slof_usb_dev { + void *udev; + uint32_t port; + uint32_t addr; + uint32_t hcitype; + uint32_t num; + uint32_t devtype; +} __attribute__((packed)); + +enum USB_PIPE_DIR { + USB_PIPE_OUT = 0, + USB_PIPE_IN, +}; + +struct usb_pipe { + struct usb_dev *dev; + struct usb_pipe *next; + uint32_t type; + uint32_t speed; + uint32_t dir; + uint16_t epno; + uint16_t mps; +} __attribute__((packed)); + +#define REQ_GET_STATUS 0 /* see Table 9-4 */ +#define REQ_CLEAR_FEATURE 1 +#define REQ_GET_STATE 2 /* HUB specific */ +#define REQ_SET_FEATURE 3 +#define REQ_SET_ADDRESS 5 +#define REQ_GET_DESCRIPTOR 6 +#define REQ_SET_DESCRIPTOR 7 +#define REQ_GET_CONFIGURATION 8 +#define REQ_SET_CONFIGURATION 9 +#define REQ_GET_INTERFACE 10 +#define REQ_SET_INTERFACE 11 +#define REQ_SYNCH_FRAME 12 + +#define FEATURE_DEVICE_REMOTE_WAKEUP 1 +#define FEATURE_ENDPOINT_HALT 0 + +#define REQT_REC_DEVICE 0 +#define REQT_REC_INTERFACE 1 +#define REQT_REC_EP 2 +#define REQT_REC_OTHER 3 +#define REQT_TYPE_STANDARD (0 << 5) +#define REQT_TYPE_CLASS (1 << 5) +#define REQT_TYPE_VENDOR (2 << 5) +#define REQT_TYPE_RSRVD (3 << 5) +#define REQT_DIR_OUT (0 << 7) /* host -> device */ +#define REQT_DIR_IN (1 << 7) /* device -> host */ + +#define DESCR_TYPE_DEVICE 1 /* see Table 9-5 */ +#define DESCR_TYPE_CONFIGURATION 2 +#define DESCR_TYPE_STRING 3 +#define DESCR_TYPE_INTERFACE 4 +#define DESCR_TYPE_ENDPOINT 5 +#define DESCR_TYPE_HUB 0x29 /* Class Descriptor HUB */ +#define DESCR_TYPE_HID 0x21 /* Class Descriptor HID */ +#define DESCR_TYPE_REPORT 0x22 /* Class Descriptor HID */ +#define DESCR_TYPE_PHYSICAL 0x23 /* Class Descriptor HID */ + +struct usb_dev_req { + uint8_t bmRequestType; /* direction, recipient */ + uint8_t bRequest; /* see spec: Table 9-3 */ + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; /* number of bytes to transfer */ +} __attribute__((packed)); + +/* Standard Device Descriptor (18 Bytes) */ +/*******************************************/ +struct usb_dev_descr { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} __attribute__((packed)); + +/*******************************************/ +/* Standard Configuration Descriptor */ +/*******************************************/ +struct usb_dev_config_descr { + uint8_t bLength; /* size of descriptor */ + uint8_t bDescriptorType; /* Type = 2 */ + uint16_t wTotalLength; /* total returned data */ + uint8_t bNumInterfaces; /* interfaces supported by this config */ + uint8_t bConfigurationValue; /* Configuration-ID for SetConfiguration */ + uint8_t iConfiguration; /* index of string descriptor */ + uint8_t bmAttributes; /* configuration characteristics */ + uint8_t bMaxPower; /* in 2mA units */ +} __attribute__((packed)); + +/*******************************************/ +/* Standard Interface Descriptor */ +/*******************************************/ +struct usb_dev_intf_descr { + uint8_t bLength; /* size of descriptor */ + uint8_t bDescriptorType; /* Type = 4 */ + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; /* protocol code */ + uint8_t iInterface; /* index to string descriptor */ +} __attribute__((packed)); + +/*******************************************/ +/* HUB-Class Descriptor */ +/*******************************************/ +struct usb_dev_hub_descr { + uint8_t bLength; /* size of complete descriptor */ + uint8_t bDescriptorType; /* type = 0x29 for HUB */ + uint8_t bNbrPorts; /* number of downstream ports */ + uint8_t wHubCharacteristics; /* mode bits 7..0 */ + uint8_t reserved; /* mode bits 15..8 */ + uint8_t bPwrOn2PwrGood; /* in 2ms units */ + uint8_t bHubContrCurrent; /* current requirement in mA */ + uint8_t DeviceTable; /* length depends on number of ports */ +} __attribute__((packed)); + +/*******************************************/ +/* HID-Class Descriptor */ +/*******************************************/ +struct usb_dev_hid_descr { + uint8_t bLength; /* size of this descriptor */ + uint8_t bDescriptorType; /* type = 0x21 for HID */ + uint16_t bcdHID; /* Sample: 0x0102 for 2.01 */ + uint8_t bCountryCode; /* Hardware target country */ + uint8_t bNumDescriptors; /* Number of HID class descr. */ + uint8_t bReportType; /* Report Descriptor Type */ + uint16_t wReportLength; /* Total Length of Report Descr. */ +} __attribute__((packed)); + +struct usb_hcd_ops { + const char *name; + void (*init)(struct usb_hcd_dev *); + void (*exit)(struct usb_hcd_dev *); + void (*detect)(void); + void (*disconnect)(void); + int (*send_ctrl)(struct usb_pipe *pipe, struct usb_dev_req *req, void *data); + struct usb_pipe* (*get_pipe)(struct usb_dev *dev, struct usb_ep_descr *ep, + char *buf, size_t len); + int (*transfer_bulk)(struct usb_pipe *pipe, void *td, void *td_phys, void *data, int size); + void (*put_pipe)(struct usb_pipe *); + int (*poll_intr)(struct usb_pipe *, uint8_t *); + struct usb_hcd_ops *next; + unsigned int usb_type; +}; + +extern void usb_hcd_register(struct usb_hcd_ops *ops); +extern struct usb_pipe *usb_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, + char *buf, size_t len); +extern void usb_put_pipe(struct usb_pipe *pipe); +extern int usb_poll_intr(struct usb_pipe *pipe, uint8_t *buf); +extern int usb_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data); +extern struct usb_dev *usb_devpool_get(void); +extern void usb_devpool_put(struct usb_dev *); +extern int setup_new_device(struct usb_dev *dev, unsigned int port); +extern int slof_usb_handle(struct usb_dev *dev); +extern int usb_dev_populate_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, + void *buf, size_t len); +extern int usb_hid_kbd_init(struct usb_dev *dev); +extern int usb_hid_kbd_exit(struct usb_dev *dev); +extern void usb_msc_resetrecovery(struct usb_dev *dev); +#endif diff --git a/qemu/roms/SLOF/lib/libusb/usb-ehci.c b/qemu/roms/SLOF/lib/libusb/usb-ehci.c new file mode 100644 index 000000000..4cca0da15 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-ehci.c @@ -0,0 +1,609 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include "usb.h" +#include "usb-core.h" +#include "usb-ehci.h" +#include "tools.h" +#include "paflof.h" + +#undef EHCI_DEBUG +//#define EHCI_DEBUG +#ifdef EHCI_DEBUG +#define dprintf(_x ...) do { printf(_x); } while(0) +#else +#define dprintf(_x ...) +#endif + +#ifdef EHCI_DEBUG +static void dump_ehci_regs(struct ehci_hcd *ehcd) +{ + struct ehci_cap_regs *cap_regs; + struct ehci_op_regs *op_regs; + + cap_regs = ehcd->cap_regs; + op_regs = ehcd->op_regs; + + dprintf("\n - CAPLENGTH %02X", read_reg8(&cap_regs->caplength)); + dprintf("\n - HCIVERSION %04X", read_reg16(&cap_regs->hciversion)); + dprintf("\n - HCSPARAMS %08X", read_reg32(&cap_regs->hcsparams)); + dprintf("\n - HCCPARAMS %08X", read_reg32(&cap_regs->hccparams)); + dprintf("\n - HCSP_PORTROUTE %016llX", read_reg64(&cap_regs->portroute)); + dprintf("\n"); + + dprintf("\n - USBCMD %08X", read_reg32(&op_regs->usbcmd)); + dprintf("\n - USBSTS %08X", read_reg32(&op_regs->usbsts)); + dprintf("\n - USBINTR %08X", read_reg32(&op_regs->usbintr)); + dprintf("\n - FRINDEX %08X", read_reg32(&op_regs->frindex)); + dprintf("\n - CTRLDSSEGMENT %08X", read_reg32(&op_regs->ctrldssegment)); + dprintf("\n - PERIODICLISTBASE %08X", read_reg32(&op_regs->periodiclistbase)); + dprintf("\n - ASYNCLISTADDR %08X", read_reg32(&op_regs->asynclistaddr)); + dprintf("\n - CONFIGFLAG %08X", read_reg32(&op_regs->configflag)); + dprintf("\n - PORTSC %08X", read_reg32(&op_regs->portsc[0])); + dprintf("\n"); +} +#endif + +static int ehci_hub_check_ports(struct ehci_hcd *ehcd) +{ + uint32_t num_ports, portsc, i; + struct usb_dev *dev; + + dprintf("%s: enter\n", __func__); + num_ports = read_reg32(&ehcd->cap_regs->hcsparams) & HCS_NPORTS_MASK; + for (i = 0; i < num_ports; i++) { + dprintf("%s: device %d\n", __func__, i); + portsc = read_reg32(&ehcd->op_regs->portsc[i]); + if (portsc & PORT_CONNECT) { /* Device present */ + dprintf("usb-ehci: Device present on port %d\n", i); + /* Reset the port */ + portsc = read_reg32(&ehcd->op_regs->portsc[i]); + portsc = (portsc & ~PORT_PE) | PORT_RESET; + write_reg32(&ehcd->op_regs->portsc[i], portsc); + SLOF_msleep(20); + portsc = read_reg32(&ehcd->op_regs->portsc[i]); + portsc &= ~PORT_RESET; + write_reg32(&ehcd->op_regs->portsc[i], portsc); + SLOF_msleep(20); + dev = usb_devpool_get(); + dprintf("usb-ehci: allocated device %p\n", dev); + dev->hcidev = ehcd->hcidev; + dev->speed = USB_HIGH_SPEED; /* TODO: Check for Low/Full speed device */ + if (!setup_new_device(dev, i)) + printf("usb-ehci: unable to setup device on port %d\n", i); + } + } + dprintf("%s: exit\n", __func__); + return 0; +} + +static int ehci_hcd_init(struct ehci_hcd *ehcd) +{ + uint32_t usbcmd; + uint32_t time; + struct ehci_framelist *fl; + struct ehci_qh *qh_intr, *qh_async; + int i; + long fl_phys = 0, qh_intr_phys = 0, qh_async_phys; + + /* Reset the host controller */ + time = SLOF_GetTimer() + 250; + usbcmd = read_reg32(&ehcd->op_regs->usbcmd); + write_reg32(&ehcd->op_regs->usbcmd, (usbcmd & ~(CMD_PSE | CMD_ASE)) | CMD_HCRESET); + while (time > SLOF_GetTimer()) + cpu_relax(); + usbcmd = read_reg32(&ehcd->op_regs->usbcmd); + if (usbcmd & CMD_HCRESET) { + printf("usb-ehci: reset failed\n"); + return -1; + } + + /* Initialize periodic list */ + fl = SLOF_dma_alloc(sizeof(*fl)); + if (!fl) { + printf("usb-ehci: Unable to allocate frame list\n"); + goto fail; + } + fl_phys = SLOF_dma_map_in(fl, sizeof(*fl), true); + dprintf("fl %p, fl_phys %lx\n", fl, fl_phys); + + /* TODO: allocate qh pool */ + qh_intr = SLOF_dma_alloc(sizeof(*qh_intr)); + if (!qh_intr) { + printf("usb-ehci: Unable to allocate interrupt queue head\n"); + goto fail_qh_intr; + } + qh_intr_phys = SLOF_dma_map_in(qh_intr, sizeof(*qh_intr), true); + dprintf("qh_intr %p, qh_intr_phys %lx\n", qh_intr, qh_intr_phys); + + memset(qh_intr, 0, sizeof(*qh_intr)); + qh_intr->qh_ptr = QH_PTR_TERM; + qh_intr->ep_cap2 = cpu_to_le32(0x01 << QH_SMASK_SHIFT); + qh_intr->next_qtd = qh_intr->alt_next_qtd = QH_PTR_TERM; + qh_intr->token = cpu_to_le32(QH_STS_HALTED); + for (i = 0; i < FL_SIZE; i++) + fl->fl_ptr[i] = cpu_to_le32(qh_intr_phys | EHCI_TYP_QH); + write_reg32(&ehcd->op_regs->periodiclistbase, fl_phys); + + /* Initialize async list */ + qh_async = SLOF_dma_alloc(sizeof(*qh_async)); + if (!qh_async) { + printf("usb-ehci: Unable to allocate async queue head\n"); + goto fail_qh_async; + } + qh_async_phys = SLOF_dma_map_in(qh_async, sizeof(*qh_async), true); + dprintf("qh_async %p, qh_async_phys %lx\n", qh_async, qh_async_phys); + + memset(qh_async, 0, sizeof(*qh_async)); + qh_async->qh_ptr = cpu_to_le32(qh_async_phys | EHCI_TYP_QH); + qh_async->ep_cap1 = cpu_to_le32(QH_CAP_H); + qh_async->next_qtd = qh_async->alt_next_qtd = QH_PTR_TERM; + qh_async->token = cpu_to_le32(QH_STS_HALTED); + write_reg32(&ehcd->op_regs->asynclistaddr, qh_async_phys); + ehcd->qh_async = qh_async; + ehcd->qh_async_phys = qh_async_phys; + ehcd->qh_intr = qh_intr; + ehcd->qh_intr_phys = qh_intr_phys; + ehcd->fl = fl; + ehcd->fl_phys = fl_phys; + + write_reg32(&ehcd->op_regs->usbcmd, usbcmd | CMD_ASE | CMD_RUN); + write_reg32(&ehcd->op_regs->configflag, 1); + + return 0; + +fail_qh_async: + SLOF_dma_map_out(qh_intr_phys, qh_intr, sizeof(*qh_intr)); + SLOF_dma_free(qh_intr, sizeof(*qh_intr)); +fail_qh_intr: + SLOF_dma_map_out(fl_phys, fl, sizeof(*fl)); + SLOF_dma_free(fl, sizeof(*fl)); +fail: + return -1; +} + +static int ehci_hcd_exit(struct ehci_hcd *ehcd) +{ + uint32_t usbcmd; + + if (!ehcd) { + dprintf("NULL pointer\n"); + return false; + } + + usbcmd = read_reg32(&ehcd->op_regs->usbcmd); + write_reg32(&ehcd->op_regs->usbcmd, usbcmd | ~CMD_RUN); + write_reg32(&ehcd->op_regs->periodiclistbase, 0); + + if (ehcd->pool) { + SLOF_dma_map_out(ehcd->pool_phys, ehcd->pool, EHCI_PIPE_POOL_SIZE); + SLOF_dma_free(ehcd->pool, EHCI_PIPE_POOL_SIZE); + } + if (ehcd->qh_intr) { + SLOF_dma_map_out(ehcd->qh_intr_phys, ehcd->qh_intr, sizeof(struct ehci_qh)); + SLOF_dma_free(ehcd->qh_intr, sizeof(struct ehci_qh)); + } + if (ehcd->qh_async) { + SLOF_dma_map_out(ehcd->qh_async_phys, ehcd->qh_async, sizeof(struct ehci_qh)); + SLOF_dma_free(ehcd->qh_async, sizeof(struct ehci_qh)); + } + if (ehcd->fl) { + SLOF_dma_map_out(ehcd->fl_phys, ehcd->fl, sizeof(struct ehci_framelist)); + SLOF_dma_free(ehcd->fl, sizeof(struct ehci_framelist)); + } + return true; +} + +static int ehci_alloc_pipe_pool(struct ehci_hcd *ehcd) +{ + struct ehci_pipe *epipe, *curr, *prev; + unsigned int i, count; + long epipe_phys = 0; + + count = EHCI_PIPE_POOL_SIZE/sizeof(*epipe); + ehcd->pool = epipe = SLOF_dma_alloc(EHCI_PIPE_POOL_SIZE); + if (!epipe) + return -1; + ehcd->pool_phys = epipe_phys = SLOF_dma_map_in(epipe, EHCI_PIPE_POOL_SIZE, true); + dprintf("%s: epipe %p, epipe_phys %lx\n", __func__, epipe, epipe_phys); + + /* Although an array, link them */ + for (i = 0, curr = epipe, prev = NULL; i < count; i++, curr++) { + if (prev) + prev->pipe.next = &curr->pipe; + curr->pipe.next = NULL; + prev = curr; + curr->qh_phys = epipe_phys + (curr - epipe) * sizeof(*curr) + + offset_of(struct ehci_pipe, qh); + dprintf("%s - %d: qh %p, qh_phys %lx\n", __func__, + i, &curr->qh, curr->qh_phys); + } + + if (!ehcd->freelist) + ehcd->freelist = &epipe->pipe; + else + ehcd->end->next = &epipe->pipe; + ehcd->end = &prev->pipe; + + return 0; +} + +static void ehci_init(struct usb_hcd_dev *hcidev) +{ + struct ehci_hcd *ehcd; + + printf(" EHCI: Initializing\n"); + dprintf("%s: device base address %p\n", __func__, hcidev->base); + + ehcd = SLOF_alloc_mem(sizeof(*ehcd)); + if (!ehcd) { + printf("usb-ehci: Unable to allocate memory\n"); + return; + } + memset(ehcd, 0, sizeof(*ehcd)); + + hcidev->nextaddr = 1; + hcidev->priv = ehcd; + ehcd->hcidev = hcidev; + ehcd->cap_regs = (struct ehci_cap_regs *)(hcidev->base); + ehcd->op_regs = (struct ehci_op_regs *)(hcidev->base + + read_reg8(&ehcd->cap_regs->caplength)); +#ifdef EHCI_DEBUG + dump_ehci_regs(ehcd); +#endif + ehci_hcd_init(ehcd); + ehci_hub_check_ports(ehcd); +} + +static void ehci_exit(struct usb_hcd_dev *hcidev) +{ + struct ehci_hcd *ehcd; + static int count = 0; + + dprintf("%s: enter \n", __func__); + + if (!hcidev && !hcidev->priv) { + return; + } + count++; + if (count > 1) { + printf("%s: already called once \n", __func__); + return; + } + ehcd = hcidev->priv; + ehci_hcd_exit(ehcd); + SLOF_free_mem(ehcd, sizeof(*ehcd)); + hcidev->priv = NULL; +} + +static void ehci_detect(void) +{ + +} + +static void ehci_disconnect(void) +{ + +} + +static int ehci_handshake(struct ehci_hcd *ehcd, uint32_t timeout) +{ + uint32_t usbsts = 0, time; + uint32_t usbcmd; + mb(); + usbcmd = read_reg32(&ehcd->op_regs->usbcmd); + /* Ring a doorbell */ + write_reg32(&ehcd->op_regs->usbcmd, usbcmd | CMD_IAAD); + mb(); + time = SLOF_GetTimer() + timeout; + while ((time > SLOF_GetTimer())) { + /* Wait for controller to confirm */ + usbsts = read_reg32(&ehcd->op_regs->usbsts); + if (usbsts & STS_IAA) { + /* Acknowledge it, for next doorbell to work */ + write_reg32(&ehcd->op_regs->usbsts, STS_IAA); + return true; + } + cpu_relax(); + } + return false; +} + +static int fill_qtd_buff(struct ehci_qtd *qtd, long data, uint32_t size) +{ + long i, rem; + long pos = (data + 0x1000) & ~0xfff; + + qtd->buffer[0] = cpu_to_le32(PTR_U32(data)); + for (i = 1; i < 5; i++) { + if ((data + size - 1) >= pos) { + //dprintf("data spans page boundary: %d, %p\n", i, pos); + qtd->buffer[i] = cpu_to_le32(pos); + pos += 0x1000; + } else + break; + } + if ((data + size) > pos) + rem = data + size - pos; + else + rem = 0; + return rem; +} + +static int ehci_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data) +{ + struct ehci_hcd *ehcd; + struct ehci_qtd *qtd, *qtds, *qtds_phys; + struct ehci_pipe *epipe; + uint32_t transfer_size = sizeof(*req); + uint32_t datalen, pid; + uint32_t time; + long req_phys = 0, data_phys = 0; + int ret = true; + + if (pipe->type != USB_EP_TYPE_CONTROL) { + printf("usb-ehci: Not a control pipe.\n"); + return false; + } + + ehcd = pipe->dev->hcidev->priv; + qtds = qtd = SLOF_dma_alloc(sizeof(*qtds) * 3); + if (!qtds) { + printf("Error allocating qTDs.\n"); + return false; + } + qtds_phys = (struct ehci_qtd *)SLOF_dma_map_in(qtds, sizeof(*qtds) * 3, true); + memset(qtds, 0, sizeof(*qtds) * 3); + req_phys = SLOF_dma_map_in(req, sizeof(struct usb_dev_req), true); + qtd->next_qtd = cpu_to_le32(PTR_U32(&qtds_phys[1])); + qtd->alt_next_qtd = QH_PTR_TERM; + qtd->token = cpu_to_le32((transfer_size << TOKEN_TBTT_SHIFT) | + (3 << TOKEN_CERR_SHIFT) | + (PID_SETUP << TOKEN_PID_SHIFT) | + (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)); + fill_qtd_buff(qtd, req_phys, sizeof(*req)); + + qtd++; + datalen = cpu_to_le16(req->wLength); + pid = (req->bmRequestType & REQT_DIR_IN) ? PID_IN : PID_OUT; + if (datalen) { + data_phys = SLOF_dma_map_in(data, datalen, true); + qtd->next_qtd = cpu_to_le32(PTR_U32(&qtds_phys[2])); + qtd->alt_next_qtd = QH_PTR_TERM; + qtd->token = cpu_to_le32((1 << TOKEN_DT_SHIFT) | + (datalen << TOKEN_TBTT_SHIFT) | + (3 << TOKEN_CERR_SHIFT) | + (pid << TOKEN_PID_SHIFT) | + (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)); + fill_qtd_buff(qtd, data_phys, datalen); + qtd++; + } + + if (pid == PID_IN) + pid = PID_OUT; + else + pid = PID_IN; + qtd->next_qtd = QH_PTR_TERM; + qtd->alt_next_qtd = QH_PTR_TERM; + qtd->token = cpu_to_le32((1 << TOKEN_DT_SHIFT) | + (3 << TOKEN_CERR_SHIFT) | + (pid << TOKEN_PID_SHIFT) | + (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)); + + /* link qtd to qh and attach to ehcd */ + mb(); + epipe = container_of(pipe, struct ehci_pipe, pipe); + epipe->qh.next_qtd = cpu_to_le32(PTR_U32(qtds_phys)); + epipe->qh.qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH); + epipe->qh.ep_cap1 = cpu_to_le32((pipe->mps << QH_MPS_SHIFT) | + (pipe->speed << QH_EPS_SHIFT) | + (pipe->epno << QH_EP_SHIFT) | + (pipe->dev->addr << QH_DEV_ADDR_SHIFT)); + mb(); + + ehcd->qh_async->qh_ptr = cpu_to_le32(epipe->qh_phys | EHCI_TYP_QH); + + /* transfer data */ + mb(); + qtd = &qtds[0]; + time = SLOF_GetTimer() + USB_TIMEOUT; + do { + if (le32_to_cpu(qtd->token) & (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)) + mb(); + else + qtd++; + + if (time < SLOF_GetTimer()) { /* timed out */ + printf("usb-ehci: control transfer timed out_\n"); + ret = false; + break; + } + } while (qtd->next_qtd != QH_PTR_TERM); + + ehcd->qh_async->qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH); + mb(); + if (!ehci_handshake(ehcd, USB_TIMEOUT)) { + printf("%s: handshake failed\n", __func__); + ret = false; + } + + SLOF_dma_map_out(req_phys, req, sizeof(struct usb_dev_req)); + SLOF_dma_map_out(data_phys, data, datalen); + SLOF_dma_map_out(PTR_U32(qtds_phys), qtds, sizeof(*qtds) * 3); + SLOF_dma_free(qtds, sizeof(*qtds) * 3); + + return ret; +} + +static int ehci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys, + void *data_phys, int size) +{ + struct ehci_hcd *ehcd; + struct ehci_qtd *qtd, *qtd_phys; + struct ehci_pipe *epipe; + uint32_t pid; + int i, rem, ret = true; + uint32_t time; + long ptr; + + dprintf("usb-ehci: bulk transfer: data %p, size %d, td %p, td_phys %p\n", + data_phys, size, td, td_phys); + + if (pipe->type != USB_EP_TYPE_BULK) { + printf("usb-ehci: Not a bulk pipe.\n"); + return false; + } + + if (size > QTD_MAX_TRANSFER_LEN) { + printf("usb-ehci: bulk transfer size too big\n"); + return false; + } + + ehcd = pipe->dev->hcidev->priv; + pid = (pipe->dir == USB_PIPE_OUT) ? PID_OUT : PID_IN; + qtd = (struct ehci_qtd *)td; + qtd_phys = (struct ehci_qtd *)td_phys; + ptr = (long)data_phys; + for (i = 0; i < NUM_BULK_QTDS; i++) { + memset(qtd, 0, sizeof(*qtd)); + rem = fill_qtd_buff(qtd, ptr, size); + qtd->token = cpu_to_le32((1 << TOKEN_DT_SHIFT) | + ((size - rem) << TOKEN_TBTT_SHIFT) | + (3 << TOKEN_CERR_SHIFT) | + (pid << TOKEN_PID_SHIFT) | + (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)); + if (rem) { + qtd->next_qtd = cpu_to_le32(PTR_U32(&qtd_phys[i+1])); + qtd->alt_next_qtd = QH_PTR_TERM; + ptr += size - rem; + size = rem; + qtd++; + } else { + qtd->next_qtd = qtd->alt_next_qtd = QH_PTR_TERM; + break; /* no more data */ + } + } + + /* link qtd to qh and attach to ehcd */ + mb(); + epipe = container_of(pipe, struct ehci_pipe, pipe); + epipe->qh.next_qtd = cpu_to_le32(PTR_U32(qtd_phys)); + epipe->qh.qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH); + epipe->qh.ep_cap1 = cpu_to_le32((pipe->mps << QH_MPS_SHIFT) | + (pipe->speed << QH_EPS_SHIFT) | + (pipe->epno << QH_EP_SHIFT) | + (pipe->dev->addr << QH_DEV_ADDR_SHIFT)); + mb(); + + ehcd->qh_async->qh_ptr = cpu_to_le32(epipe->qh_phys | EHCI_TYP_QH); + + /* transfer data */ + mb(); + qtd = (struct ehci_qtd *)td; + for (i = 0; i < NUM_BULK_QTDS; i++) { + time = SLOF_GetTimer() + USB_TIMEOUT; + while ((time > SLOF_GetTimer()) && + (le32_to_cpu(qtd->token) & (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT))) + cpu_relax(); + mb(); + if (qtd->next_qtd == QH_PTR_TERM) + break; + + if (le32_to_cpu(qtd->token) & (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)) { + printf("usb-ehci: bulk transfer timed out_\n"); + ret = false; + break; + } + qtd++; + } + + ehcd->qh_async->qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH); + mb(); + if (!ehci_handshake(ehcd, USB_TIMEOUT)) { + printf("%s: handshake failed\n", __func__); + ret = false; + } + return ret; +} + +static struct usb_pipe *ehci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, + char *buf, size_t len) +{ + struct ehci_hcd *ehcd; + struct usb_pipe *new = NULL; + + if (!dev) + return NULL; + + ehcd = (struct ehci_hcd *)dev->hcidev->priv; + if (!ehcd->freelist) { + dprintf("usb-ehci: %s allocating pool\n", __func__); + if (ehci_alloc_pipe_pool(ehcd)) + return NULL; + } + + new = ehcd->freelist; + ehcd->freelist = ehcd->freelist->next; + if (!ehcd->freelist) + ehcd->end = NULL; + + memset(new, 0, sizeof(*new)); + new->dev = dev; + new->next = NULL; + new->type = ep->bmAttributes & USB_EP_TYPE_MASK; + new->speed = dev->speed; + new->mps = ep->wMaxPacketSize; + new->dir = (ep->bEndpointAddress & 0x80) >> 7; + new->epno = ep->bEndpointAddress & 0x0f; + + return new; +} + +static void ehci_put_pipe(struct usb_pipe *pipe) +{ + struct ehci_hcd *ehcd; + + dprintf("usb-ehci: %s enter - %p\n", __func__, pipe); + if (!pipe || !pipe->dev) + return; + ehcd = pipe->dev->hcidev->priv; + if (ehcd->end) + ehcd->end->next = pipe; + else + ehcd->freelist = pipe; + + ehcd->end = pipe; + pipe->next = NULL; + pipe->dev = NULL; + memset(pipe, 0, sizeof(*pipe)); + dprintf("usb-ehci: %s exit\n", __func__); +} + +struct usb_hcd_ops ehci_ops = { + .name = "ehci-hcd", + .init = ehci_init, + .exit = ehci_exit, + .detect = ehci_detect, + .disconnect = ehci_disconnect, + .get_pipe = ehci_get_pipe, + .put_pipe = ehci_put_pipe, + .send_ctrl = ehci_send_ctrl, + .transfer_bulk = ehci_transfer_bulk, + .usb_type = USB_EHCI, + .next = NULL, +}; + +void usb_ehci_register(void) +{ + usb_hcd_register(&ehci_ops); +} diff --git a/qemu/roms/SLOF/lib/libusb/usb-ehci.h b/qemu/roms/SLOF/lib/libusb/usb-ehci.h new file mode 100644 index 000000000..2955a9cb8 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-ehci.h @@ -0,0 +1,155 @@ +/****************************************************************************** + * Copyright (c) 2007, 2012, 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * Definitions for EHCI Controller + * + */ + +#ifndef USB_EHCI_H +#define USB_EHCI_H + +#include <stdint.h> +#include "usb-core.h" + +#define FL_SIZE 1024 + +struct ehci_cap_regs { + uint8_t caplength; + uint8_t reserved; + uint16_t hciversion; + uint32_t hcsparams; + uint32_t hccparams; + uint64_t portroute; +} __attribute__ ((packed)); + +struct ehci_op_regs { + uint32_t usbcmd; + uint32_t usbsts; + uint32_t usbintr; + uint32_t frindex; + uint32_t ctrldssegment; + uint32_t periodiclistbase; + uint32_t asynclistaddr; + uint32_t reserved[9]; + uint32_t configflag; + uint32_t portsc[0]; +} __attribute__ ((packed)); + +struct ehci_framelist { + uint32_t fl_ptr[FL_SIZE]; +} __attribute__ ((packed)); + +struct ehci_hcd { + struct ehci_cap_regs *cap_regs; + struct ehci_op_regs *op_regs; + struct usb_hcd_dev *hcidev; + struct ehci_qh *qh_async; + struct ehci_qh *qh_intr; + struct usb_pipe *freelist; + struct usb_pipe *end; + struct ehci_framelist *fl; + long qh_async_phys; + long qh_intr_phys; + long fl_phys; + void *pool; + long pool_phys; +}; + +struct ehci_qtd { + uint32_t next_qtd; + uint32_t alt_next_qtd; + uint32_t token; + uint32_t buffer[5]; +} __attribute__ ((packed)); + +struct ehci_qh { + uint32_t qh_ptr; + uint32_t ep_cap1; + uint32_t ep_cap2; + uint32_t curr_qtd; + uint32_t next_qtd; + uint32_t alt_next_qtd; + uint32_t token; + uint32_t buffer[5]; +} __attribute__ ((packed)) __attribute__((aligned(32))); + +struct ehci_pipe { + struct ehci_qh qh; + struct usb_pipe pipe; + long qh_phys; +}; + +#define EHCI_PIPE_POOL_SIZE 4096 + +#define EHCI_TYP_ITD 0x00 +#define EHCI_TYP_QH 0x02 +#define EHCI_TYP_SITD 0x04 +#define EHCI_TYP_FSTN 0x06 + +#define PID_OUT 0x00 +#define PID_IN 0x01 +#define PID_SETUP 0x02 + +#define HCS_NPORTS_MASK 0x000f + +#define CMD_IAAD (1 << 6) +#define CMD_ASE (1 << 5) +#define CMD_PSE (1 << 4) +#define CMD_FLS_MASK (3 << 2) +#define CMD_HCRESET (1 << 1) +#define CMD_RUN (1 << 0) + +#define STS_IAA (1 << 5) + +#define PORT_RESET (1 << 8) +#define PORT_PE (1 << 2) +#define PORT_CSC (1 << 1) +#define PORT_CONNECT (1 << 0) + +#define QH_LOW_SPEED 0 +#define QH_FULL_SPEED 1 +#define QH_HIGH_SPEED 2 + +#define QH_RL_SHIFT 28 +#define QH_CAP_C (1 << 27) +#define QH_MPS_SHIFT 16 +#define QH_CAP_H (1 << 15) +#define QH_CAP_DTC (1 << 14) +#define QH_EPS_SHIFT 12 +#define QH_EP_SHIFT 8 +#define QH_CAP_I (1 << 7) +#define QH_DEV_ADDR_SHIFT 0 + +#define QH_PTR_TERM __builtin_bswap32(1) +#define QH_SMASK_SHIFT 0 +#define QH_STS_ACTIVE (1 << 7) +#define QH_STS_HALTED (1 << 6) +#define QH_STS_DBE (1 << 5) +#define QH_STS_BABBLE (1 << 4) +#define QH_STS_XACTERR (1 << 3) +#define QH_STS_MMF (1 << 2) +#define QH_STS_SXS (1 << 1) +#define QH_STS_PING (1 << 0) + +#define NUM_BULK_QTDS 4 +#define MAX_XFER_PER_QTD (20 * 1024) +#define QTD_MAX_TRANSFER_LEN (NUM_BULK_QTDS * MAX_XFER_PER_QTD) + +#define TOKEN_DT_SHIFT 31 +#define TOKEN_TBTT_SHIFT 16 +#define TOKEN_IOC_SHIFT 15 +#define TOKEN_CPAGE_SHIFT 12 +#define TOKEN_CERR_SHIFT 10 +#define TOKEN_PID_SHIFT 8 +#define TOKEN_STATUS_SHIFT 0 + +#endif /* USB_EHCI_H */ diff --git a/qemu/roms/SLOF/lib/libusb/usb-hid.c b/qemu/roms/SLOF/lib/libusb/usb-hid.c new file mode 100644 index 000000000..f0cab8a69 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-hid.c @@ -0,0 +1,453 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <termctrl.h> + +#include "usb-core.h" +#include "usb-key.h" + +/* + * HID Spec Version 1.11 + */ + +#define HID_REQ_GET_REPORT 0x01 +#define HID_REQ_GET_IDLE 0x02 +#define HID_REQ_GET_PROTOCOL 0x03 +#define HID_REQ_SET_REPORT 0x09 +#define HID_REQ_SET_IDLE 0x0A +#define HID_REQ_SET_PROTOCOL 0x0B + +//#define KEY_DEBUG + +/* HID SPEC - 7.2.6 Set_Protocol Request */ +static int usb_hid_set_protocol(struct usb_dev *dev, uint16_t value) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_OUT; + req.bRequest = HID_REQ_SET_PROTOCOL; + req.wValue = cpu_to_le16(value); + req.wIndex = cpu_to_le16(dev->intf_num); + req.wLength = 0; + return usb_send_ctrl(dev->control, &req, NULL); +} + +/* HID SPEC - 7.2.4 Set_Idle Request */ +static int usb_hid_set_idle(struct usb_dev *dev, uint16_t ms_delay) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_OUT; + req.bRequest = HID_REQ_SET_IDLE; + req.wValue = cpu_to_le16((ms_delay/4) << 8); + req.wIndex = cpu_to_le16(dev->intf_num); + req.wLength = 0; + return usb_send_ctrl(dev->control, &req, NULL); +} + +/* HID SPEC - 7.2.1 Get Report Request */ +static int usb_hid_get_report(struct usb_dev *dev, void *data, size_t size) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_IN; + req.bRequest = HID_REQ_GET_REPORT; + req.wIndex = cpu_to_le16(dev->intf_num); + + req.wLength = cpu_to_le16((uint16_t)size); + req.wValue = cpu_to_le16(1 << 8); + return usb_send_ctrl(dev->control, &req, data); +} + +/* ring buffer with RD/WR indices for key buffering */ +static uint8_t keybuf[256]; /* size fixed to byte range ! */ +uint8_t r_ptr = 0; /* RD-index for Keyboard-Buffer */ +uint8_t w_ptr = 0; /* WR-index for Keyboard-Buffer */ + +/* variables for LED status */ +uint8_t set_leds; +const uint8_t *key_std = NULL; +const uint8_t *key_std_shift = NULL; + +/** + * read character from Keyboard-Buffer + * + * @param - + * @return > 0 Keycode + * = 0 if no key available + */ +static int read_key(void) +{ + if (r_ptr != w_ptr) + return (int)keybuf[r_ptr++]; + else + return false; +} + +/** + * Store character into Keyboard-Buffer + * + * @param Key = detected ASCII-Key (> 0) + * @return - + */ +static void write_key(uint8_t key) +{ + if ((w_ptr + 1) != r_ptr) + keybuf[w_ptr++] = key; +} + +/** + * Convert keyboard usage-ID to ANSI-Code + * + * @param Ctrl=Modifier Byte + * Key =Usage ID from USB Keyboard + * @return - + */ +static void get_char(uint8_t ctrl, uint8_t keypos) +{ + uint8_t ch; + +#ifdef KEY_DEBUG + printf("pos %02X\n", keypos); +#endif + + if (set_leds & LED_CAPS_LOCK) /* is CAPS Lock set ? */ + ctrl |= MODIFIER_SHIFT; /* simulate shift */ + + if (ctrl == 0) { + ch = key_std[keypos]; + if (ch != 0) + write_key(ch); + return; + } + + if (ctrl & MODIFIER_SHIFT) { + ch = key_std_shift[keypos]; + if (ch != 0) + write_key(ch); + return; + } + + if (ctrl & MODIFIER_CTRL) { + ch = keycodes_ctrl[keypos]; + if (ch != 0) + write_key(ch); + return; + } + + if (ctrl == MODIFIER_ALT_GR) { + ch = keycodes_alt_GR[keypos]; + if (ch != 0) + write_key(ch); + return; + } +} + +static void check_key_code(uint8_t *buf) +{ + static uint8_t key_last[6]; /* list of processed keys */ + uint8_t i, j, key_pos; + + /* set translation table to defaults */ + if ((key_std == NULL) || (key_std_shift == NULL)) { + key_std = keycodes_std_US; + key_std_shift = keycodes_shift_US; + } + + if (buf[0] & MODIFIER_SHIFT) /* any shift key pressed ? */ + set_leds &= ~LED_CAPS_LOCK; /* CAPS-LOCK-LED always off */ + + i = 2; /* skip modifier byte and reserved byte */ + while (i < 8) { + key_pos = buf[i]; + if ((key_pos != 0) && (key_pos <= 100)) { /* support for 101 keys */ + j = 0; + /* search if already processed */ + while ((j < 6) && (key_pos != key_last[j])) + j++; + + if (j >= 6) { /* not found (= not processed) */ + switch (key_pos) { + case 0x39: /* caps-lock key ? */ + case 0x32: /* caps-lock key ? */ + set_leds ^= LED_CAPS_LOCK; + break; + + case 0x3a: /* F1 */ + write_key(0x1b); + write_key(0x5b); + write_key(0x31); + write_key(0x31); + write_key(0x7e); + break; + + case 0x3b: /* F2 */ + write_key(0x1b); + write_key(0x5b); + write_key(0x31); + write_key(0x32); + write_key(0x7e); + break; + + case 0x3c: + write_key(0x1b); /* F3 */ + write_key(0x5b); + write_key(0x31); + write_key(0x33); + write_key(0x7e); + break; + + case 0x3d: + write_key(0x1b); /* F4 */ + write_key(0x5b); + write_key(0x31); + write_key(0x34); + write_key(0x7e); + break; + + case 0x3e: + write_key(0x1b); /* F5 */ + write_key(0x5b); + write_key(0x31); + write_key(0x35); + write_key(0x7e); + break; + + case 0x3f: + write_key(0x1b); /* F6 */ + write_key(0x5b); + write_key(0x31); + write_key(0x37); + write_key(0x7e); + break; + + case 0x40: + write_key(0x1b); /* F7 */ + write_key(0x5b); + write_key(0x31); + write_key(0x38); + write_key(0x7e); + break; + + case 0x41: + write_key(0x1b); /* F8 */ + write_key(0x5b); + write_key(0x31); + write_key(0x39); + write_key(0x7e); + break; + + case 0x42: + write_key(0x1b); /* F9 */ + write_key(0x5b); + write_key(0x31); + write_key(0x30); + write_key(0x7e); + break; + + case 0x43: + write_key(0x1b); /* F10 */ + write_key(0x5b); + write_key(0x31); + write_key(0x31); + write_key(0x7e); + break; + + case 0x44: + write_key(0x1b); /* F11 */ + write_key(0x5b); + write_key(0x31); + write_key(0x33); + write_key(0x7e); + break; + + case 0x45: + write_key(0x1b); /* F12 */ + write_key(0x5b); + write_key(0x31); + write_key(0x34); + write_key(0x7e); + break; + + case 0x47: /* scroll-lock key ? */ + set_leds ^= LED_SCROLL_LOCK; + break; + + case 0x49: + write_key(0x1b); /* INS */ + write_key(0x5b); + write_key(0x31); + write_key(0x7e); + break; + + case 0x4a: + write_key(0x1b); /* HOME */ + write_key(0x5b); + write_key(0x32); + write_key(0x7e); + break; + + case 0x4b: + write_key(0x1b); /* PgUp */ + write_key(0x5b); + write_key(0x33); + write_key(0x7e); + break; + + case 0x4c: + write_key(0x1b); /* DEL */ + write_key(0x5b); + write_key(0x34); + write_key(0x7e); + break; + + case 0x4d: + write_key(0x1b); /* END */ + write_key(0x5b); + write_key(0x35); + write_key(0x7e); + break; + + case 0x4e: + write_key(0x1b); /* PgDn */ + write_key(0x5b); + write_key(0x36); + write_key(0x7e); + break; + + case 0x4f: + write_key(0x1b); /* R-Arrow */ + write_key(0x5b); + write_key(0x43); + break; + + case 0x50: + write_key(0x1b); /* L-Arrow */ + write_key(0x5b); + write_key(0x44); + break; + + case 0x51: + write_key(0x1b); /* D-Arrow */ + write_key(0x5b); + write_key(0x42); + break; + + case 0x52: + write_key(0x1b); /* U-Arrow */ + write_key(0x5b); + write_key(0x41); + break; + + case 0x53: /* num-lock key ? */ + set_leds ^= LED_NUM_LOCK; + break; + + default: + /* convert key position to ASCII code */ + get_char(buf[0], key_pos); + break; + } + } + } + i++; + } + /*****************************************/ + /* all keys are processed, create a copy */ + /* to flag them as processed */ + /*****************************************/ + for (i = 2, j = 0; j < 6; i++, j++) + key_last[j] = buf[i]; /* copy all actual keys to last */ +} + +#define USB_HID_SIZE 128 +uint32_t *kbd_buffer; + +int usb_hid_kbd_init(struct usb_dev *dev) +{ + int i; + uint8_t key[8]; + + usb_hid_set_protocol(dev, 0); + usb_hid_set_idle(dev, 500); + + memset(key, 0, 8); + if (usb_hid_get_report(dev, key, 8)) + check_key_code(key); + + kbd_buffer = SLOF_dma_alloc(USB_HID_SIZE); + if (!kbd_buffer) { + printf("%s: unable to allocate keyboard buffer\n", __func__); + return false; + } + +#ifdef KEY_DEBUG + printf("HID kbd init %d\n", dev->ep_cnt); +#endif + for (i = 0; i < dev->ep_cnt; i++) { + if ((dev->ep[i].bmAttributes & USB_EP_TYPE_MASK) + == USB_EP_TYPE_INTR) + usb_dev_populate_pipe(dev, &dev->ep[i], kbd_buffer, USB_HID_SIZE); + } + return true; +} + +int usb_hid_kbd_exit(struct usb_dev *dev) +{ + if (dev->intr) { + usb_put_pipe(dev->intr); + dev->intr = NULL; + } + SLOF_dma_free(kbd_buffer, USB_HID_SIZE); + return true; +} + +static int usb_poll_key(void *vdev) +{ + struct usb_dev *dev = vdev; + uint8_t key[8]; + int rc; + + memset(key, 0, 8); + rc = usb_poll_intr(dev->intr, key); + if (rc) + check_key_code(key); + return rc; +} + +unsigned char usb_key_available(void *dev) +{ + if (!dev) + return false; + + usb_poll_key(dev); + if (r_ptr != w_ptr) + return true; + else + return false; +} + +unsigned char usb_read_keyb(void *vdev) +{ + if (!vdev) + return false; + + while (usb_poll_key(vdev)) { + /* loop for all pending keys */ + } + return read_key(); +} diff --git a/qemu/roms/SLOF/lib/libusb/usb-hub.c b/qemu/roms/SLOF/lib/libusb/usb-hub.c new file mode 100644 index 000000000..7059cd019 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-hub.c @@ -0,0 +1,183 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include "usb-core.h" + +#undef HUB_DEBUG +//#define HUB_DEBUG +#ifdef HUB_DEBUG +#define dprintf(_x ...) do { printf(_x); } while(0) +#else +#define dprintf(_x ...) +#endif + +/* + * USB Spec 1.1 + * 11.16.2 Class-specific Requests + */ +struct usb_hub_ps { + uint16_t wPortStatus; + uint16_t wPortChange; +} __attribute__((packed)); + +#define HUB_PS_CONNECTION (1 << 0) +#define HUB_PS_ENABLE (1 << 1) +#define HUB_PS_SUSPEND (1 << 2) +#define HUB_PS_OVER_CURRENT (1 << 3) +#define HUB_PS_RESET (1 << 4) +#define HUB_PS_POWER (1 << 8) +#define HUB_PS_LOW_SPEED (1 << 9) + +#define HUB_PF_CONNECTION 0 +#define HUB_PF_ENABLE 1 +#define HUB_PF_SUSPEND 2 +#define HUB_PF_OVER_CURRENT 3 +#define HUB_PF_RESET 4 +#define HUB_PF_POWER 8 +#define HUB_PF_LOWSPEED 9 +#define HUB_PF_C_CONNECTION 16 +#define HUB_PF_C_ENABLE 17 +#define HUB_PF_C_SUSPEND 18 +#define HUB_PF_C_OVER_CURRENT 19 +#define HUB_PF_C_RESET 20 + +static int usb_get_hub_desc(struct usb_dev *dev, void *data, size_t size) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_DIR_IN | REQT_TYPE_CLASS | REQT_REC_DEVICE; + req.bRequest = REQ_GET_DESCRIPTOR; + req.wIndex = 0; + req.wLength = cpu_to_le16((uint16_t) size); + req.wValue = cpu_to_le16(DESCR_TYPE_HUB << 8); + return usb_send_ctrl(dev->control, &req, data); +} + +static int hub_get_port_status(struct usb_dev *dev, int port, void *data, size_t size) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_DIR_IN | REQT_TYPE_CLASS | REQT_REC_OTHER; + req.bRequest = REQ_GET_STATUS; + req.wValue = 0; + req.wIndex = cpu_to_le16((uint16_t)(port + 1)); + req.wLength = cpu_to_le16((uint16_t)size); + return usb_send_ctrl(dev->control, &req, data); +} + +static int hub_set_port_feature(struct usb_dev *dev, int port, int feature) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_DIR_OUT | REQT_TYPE_CLASS | REQT_REC_OTHER; + req.bRequest = REQ_SET_FEATURE; + req.wLength = 0; + req.wValue = cpu_to_le16((uint16_t)feature); + req.wIndex = cpu_to_le16((uint16_t)(port + 1)); + return usb_send_ctrl(dev->control, &req, NULL); +} + +#if 0 +static int hub_clear_port_feature(struct usb_dev *dev, int port, int feature) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_DIR_OUT | REQT_TYPE_CLASS | REQT_REC_OTHER; + req.bRequest = REQ_CLEAR_FEATURE; + req.wLength = 0; + req.wValue = cpu_to_le16((uint16_t)feature); + req.wIndex = cpu_to_le16((uint16_t)(port + 1)); + return usb_send_ctrl(dev->control, &req, NULL); +} +#endif + +static int hub_check_port(struct usb_dev *dev, int port) +{ + struct usb_hub_ps ps; + uint32_t time; + + if (!hub_get_port_status(dev, port, &ps, sizeof(ps))) + return false; + dprintf("Port Status %04X Port Change %04X\n", + le16_to_cpu(ps.wPortStatus), + le16_to_cpu(ps.wPortChange)); + + if (!(le16_to_cpu(ps.wPortStatus) & HUB_PS_POWER)) { + hub_set_port_feature(dev, port, HUB_PF_POWER); + SLOF_msleep(100); + time = SLOF_GetTimer() + USB_TIMEOUT; + while (time > SLOF_GetTimer()) { + cpu_relax(); + hub_get_port_status(dev, port, &ps, sizeof(ps)); + if (le16_to_cpu(ps.wPortStatus) & HUB_PS_CONNECTION) { + dprintf("power on Port Status %04X Port Change %04X\n", + le16_to_cpu(ps.wPortStatus), + le16_to_cpu(ps.wPortChange)); + break; + } + } + } + + if (le16_to_cpu(ps.wPortStatus) & HUB_PS_CONNECTION) { + hub_set_port_feature(dev, port, HUB_PF_RESET); + SLOF_msleep(100); + time = SLOF_GetTimer() + USB_TIMEOUT; + while (time > SLOF_GetTimer()) { + cpu_relax(); + hub_get_port_status(dev, port, &ps, sizeof(ps)); + if (!(le16_to_cpu(ps.wPortStatus) & HUB_PS_RESET)) { + dprintf("reset Port Status %04X Port Change %04X\n", + le16_to_cpu(ps.wPortStatus), + le16_to_cpu(ps.wPortChange)); + return true; + } + } + } + return false; +} + +unsigned int usb_hub_init(void *hubdev) +{ + struct usb_dev *dev = hubdev; + struct usb_dev_hub_descr hub; + struct usb_dev *newdev; + int i; + + dprintf("%s: enter %p\n", __func__, dev); + if (!dev) { + printf("usb-hub: NULL\n"); + return false; + } + memset(&hub, 0, sizeof(hub)); + usb_get_hub_desc(dev, &hub, sizeof(hub)); + dprintf("usb-hub: ports connected %d\n", hub.bNbrPorts); + for (i = 0; i < hub.bNbrPorts; i++) { + dprintf("usb-hub: ports scanning %d\n", i); + if (hub_check_port(dev, i)) { + dprintf("***********************************************\n"); + dprintf("\t\tusb-hub: device found %d\n", i); + dprintf("***********************************************\n"); + newdev = usb_devpool_get(); + dprintf("usb-hub: allocated device %p\n", newdev); + newdev->hcidev = dev->hcidev; + if (!setup_new_device(newdev, i)) + printf("usb-hub: unable to setup device on port %d\n", i); + } + } + return true; +} diff --git a/qemu/roms/SLOF/lib/libusb/usb-key.c b/qemu/roms/SLOF/lib/libusb/usb-key.c new file mode 100644 index 000000000..7fb45da2c --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-key.c @@ -0,0 +1,446 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdint.h> + +/***********************************/ +/* Keycodes for US Keyboard */ +/* - no control keys pressed - */ +/***********************************/ +const uint8_t keycodes_std_US[] = { + 0, /* 0 00 Reserved (no event indicated) */ + 0, /* 1 01 Keyboard ErrorRollOver */ + 0, /* 2 02 Keyboard POSTFail */ + 0, /* 3 03 Keyboard ErrorUndefined */ + 'a', /* 4 04 Keyboard a and A 31 */ + 'b', /* 5 05 Keyboard b and B 50 */ + 'c', /* 6 06 Keyboard c and C 48 */ + 'd', /* 7 07 Keyboard d and D 33 */ + 'e', /* 8 08 Keyboard e and E 19 */ + 'f', /* 9 09 Keyboard f and F 34 */ + 'g', /* 10 0A Keyboard g and G 35 */ + 'h', /* 11 0B Keyboard h and H 36 */ + 'i', /* 12 0C Keyboard i and I 24 */ + 'j', /* 13 0D Keyboard j and J 37 */ + 'k', /* 14 0E Keyboard k and K 38 */ + 'l', /* 15 0F Keyboard l and L 39 */ + 'm', /* 16 10 Keyboard m and M 52 */ + 'n', /* 17 11 Keyboard n and N 51 */ + 'o', /* 18 12 Keyboard o and O 25 */ + 'p', /* 19 13 Keyboard p and P 26 */ + 'q', /* 20 14 Keyboard q and Q 17 */ + 'r', /* 21 15 Keyboard r and R 20 */ + 's', /* 22 16 Keyboard s and S 32 */ + 't', /* 23 17 Keyboard t and T 21 */ + 'u', /* 24 18 Keyboard u and U 23 */ + 'v', /* 25 19 Keyboard v and V 49 */ + 'w', /* 26 1A Keyboard w and W 18 */ + 'x', /* 27 1B Keyboard x and X 47 */ + 'y', /* 28 1C Keyboard y and Y 22 */ + 'z', /* 29 1D Keyboard z and Z 46 */ + '1', /* 30 1E Keyboard 1 and ! 2 */ + '2', /* 31 1F Keyboard 2 and @ 3 */ + '3', /* 32 20 Keyboard 3 and # 4 */ + '4', /* 33 21 Keyboard 4 and $ 5 */ + '5', /* 34 22 Keyboard 5 and % 6 */ + '6', /* 35 23 Keyboard 6 and ^ 7 */ + '7', /* 36 24 Keyboard 7 and & 8 */ + '8', /* 37 25 Keyboard 8 and * 9 */ + '9', /* 38 26 Keyboard 9 and ( 10 */ + '0', /* 39 27 Keyboard 0 and ) 11 */ + 13, /* 40 28 Keyboard Return (ENTER) 43 */ + 27, /* 41 29 Keyboard ESCAPE 110 */ + 8, /* 42 2A Keyboard DELETE (BS) 15 */ + 9, /* 43 2B Keyboard Tab 16 */ + ' ', /* 44 2C Keyboard Spacebar 61 */ + '-', /* 45 2D Keyboard - and (underscore) 12 */ + '=', /* 46 2E Keyboard = and + 13 */ + '[', /* 47 2F Keyboard [ and { 27 */ + ']', /* 48 30 Keyboard ] and } 28 */ + '\\', /* 49 31 Keyboard \ and | 29 */ + '\\', /* 50 32 Keyboard \ and | 42 */ + ';', /* 51 33 Keyboard ; and : 40 */ + 39, /* 52 34 Keyboard ' and " 41 */ + 96, /* 53 35 Keyboard Grave Accent and Tilde 1 */ + ',', /* 54 36 Keyboard , and < 53 */ + '.', /* 55 37 Keyboard . and > 54 */ + '/', /* 56 38 Keyboard / and ? 55 */ + 0, /* 57 39 Keyboard Caps Lock 30 */ + 0, /* 58 3A Keyboard F1 112 */ + 0, /* 59 3B Keyboard F2 113 */ + 0, /* 60 3C Keyboard F3 114 */ + 0, /* 61 3D Keyboard F4 115 */ + 0, /* 62 3E Keyboard F5 116 */ + 0, /* 63 3F Keyboard F6 117 */ + 0, /* 64 40 Keyboard F7 118 */ + 0, /* 65 41 Keyboard F8 119 */ + 0, /* 66 42 Keyboard F9 120 */ + 0, /* 67 43 Keyboard F10 121 */ + 0, /* 68 44 Keyboard F11 122 */ + 0, /* 69 45 Keyboard F12 123 */ + 0, /* 70 46 Keyboard PrintScreen 124 */ + 0, /* 71 47 Keyboard Scroll Lock 125 */ + 0, /* 72 48 Keyboard Pause 126 */ + 0, /* 73 49 Keyboard Insert 75 */ + 0, /* 74 4A Keyboard Home 80 */ + 0, /* 75 4B Keyboard PageUp 85 */ + 0, /* 76 4C Keyboard Delete Forward 76 */ + 0, /* 77 4D Keyboard End 81 */ + 0, /* 78 4E Keyboard PageDown 86 */ + 0, /* 79 4F Keyboard RightArrow 89 */ + 0, /* 80 50 Keyboard LeftArrow 79 */ + 0, /* 81 51 Keyboard DownArrow 84 */ + 0, /* 82 52 Keyboard UpArrow 83 */ + 0, /* 83 53 Keypad Num Lock and Clear 90 */ + '/', /* 84 54 Keypad / 95 */ + '*', /* 85 55 Keypad * 100 */ + '-', /* 86 56 Keypad - 105 */ + '+', /* 87 57 Keypad + 106 */ + 13, /* 88 58 Keypad ENTER 108 */ + '1', /* 89 59 Keypad 1 and End 93 */ + '2', /* 90 5A Keypad 2 and Down Arrow 98 */ + '3', /* 91 5B Keypad 3 and PageDn 103 */ + '4', /* 92 5C Keypad 4 and Left Arrow 92 */ + '5', /* 93 5D Keypad 5 97 */ + '6', /* 94 5E Keypad 6 and Right Arrow 102 */ + '7', /* 95 5F Keypad 7 and Home 91 */ + '8', /* 96 60 Keypad 8 and Up Arrow 96 */ + '9', /* 97 61 Keypad 9 and PageUp 101 */ + '0', /* 98 62 Keypad 0 and Insert 99 */ + '.', /* 99 63 Keypad . and Delete 104 */ + '\\' /* 100 64 Keyboard Non-US \ and | 45 */ +}; + +/***********************************/ +/* Keycodes for US Keyboard */ +/* - SHIFT-KEY pressed - */ +/***********************************/ +const uint8_t keycodes_shift_US[] = { + 0, /* 0 00 Reserved (no event indicated) */ + 0, /* 1 01 Keyboard ErrorRollOver */ + 0, /* 2 02 Keyboard POSTFail */ + 0, /* 3 03 Keyboard ErrorUndefined */ + 'A', /* 4 04 Keyboard a and A 31 */ + 'B', /* 5 05 Keyboard b and B 50 */ + 'C', /* 6 06 Keyboard c and C 48 */ + 'D', /* 7 07 Keyboard d and D 33 */ + 'E', /* 8 08 Keyboard e and E 19 */ + 'F', /* 9 09 Keyboard f and F 34 */ + 'G', /* 10 0A Keyboard g and G 35 */ + 'H', /* 11 0B Keyboard h and H 36 */ + 'I', /* 12 0C Keyboard i and I 24 */ + 'J', /* 13 0D Keyboard j and J 37 */ + 'K', /* 14 0E Keyboard k and K 38 */ + 'L', /* 15 0F Keyboard l and L 39 */ + 'M', /* 16 10 Keyboard m and M 52 */ + 'N', /* 17 11 Keyboard n and N 51 */ + 'O', /* 18 12 Keyboard o and O 25 */ + 'P', /* 19 13 Keyboard p and P 26 */ + 'Q', /* 20 14 Keyboard q and Q 17 */ + 'R', /* 21 15 Keyboard r and R 20 */ + 'S', /* 22 16 Keyboard s and S 32 */ + 'T', /* 23 17 Keyboard t and T 21 */ + 'U', /* 24 18 Keyboard u and U 23 */ + 'V', /* 25 19 Keyboard v and V 49 */ + 'W', /* 26 1A Keyboard w and W 18 */ + 'X', /* 27 1B Keyboard x and X 47 */ + 'Y', /* 28 1C Keyboard y and Y 22 */ + 'Z', /* 29 1D Keyboard z and Z 46 */ + '!', /* 30 1E Keyboard 1 and ! 2 */ + '@', /* 31 1F Keyboard 2 and @ 3 */ + '#', /* 32 20 Keyboard 3 and # 4 */ + '$', /* 33 21 Keyboard 4 and $ 5 */ + '%', /* 34 22 Keyboard 5 and % 6 */ + '^', /* 35 23 Keyboard 6 and ^ 7 */ + '&', /* 36 24 Keyboard 7 and & 8 */ + '*', /* 37 25 Keyboard 8 and * 9 */ + '(', /* 38 26 Keyboard 9 and ( 10 */ + ')', /* 39 27 Keyboard 0 and ) 11 */ + 13, /* 40 28 Keyboard Return (ENTER) 43 */ + 27, /* 41 29 Keyboard ESCAPE 110 */ + 8, /* 42 2A Keyboard DELETE (BS) 15 */ + 9, /* 43 2B Keyboard Tab 16 */ + ' ', /* 44 2C Keyboard Spacebar 61 */ + '_', /* 45 2D Keyboard - and (underscore) 12 */ + '+', /* 46 2E Keyboard = and + 13 */ + '{', /* 47 2F Keyboard [ and { 27 */ + '}', /* 48 30 Keyboard ] and } 28 */ + '|', /* 49 31 Keyboard \ and | 29 */ + '|', /* 50 32 Keyboard \ and | 42 */ + ':', /* 51 33 Keyboard ; and : 40 */ + '"', /* 52 34 Keyboard ' and " 41 */ + '~', /* 53 35 Keyboard Grave Accent and Tilde 1 */ + '<', /* 54 36 Keyboard , and < 53 */ + '>', /* 55 37 Keyboard . and > 54 */ + '?', /* 56 38 Keyboard / and ? 55 */ + 0, /* 57 39 Keyboard Caps Lock 30 */ + 0, /* 58 3A Keyboard F1 112 */ + 0, /* 59 3B Keyboard F2 113 */ + 0, /* 60 3C Keyboard F3 114 */ + 0, /* 61 3D Keyboard F4 115 */ + 0, /* 62 3E Keyboard F5 116 */ + 0, /* 63 3F Keyboard F6 117 */ + 0, /* 64 40 Keyboard F7 118 */ + 0, /* 65 41 Keyboard F8 119 */ + 0, /* 66 42 Keyboard F9 120 */ + 0, /* 67 43 Keyboard F10 121 */ + 0, /* 68 44 Keyboard F11 122 */ + 0, /* 69 45 Keyboard F12 123 */ + 0, /* 70 46 Keyboard PrintScreen 124 */ + 0, /* 71 47 Keyboard Scroll Lock 125 */ + 0, /* 72 48 Keyboard Pause 126 */ + 48, /* 73 49 Keyboard Insert 75 */ + 55, /* 74 4A Keyboard Home 80 */ + 57, /* 75 4B Keyboard PageUp 85 */ + 46, /* 76 4C Keyboard Delete Forward 76 */ + 49, /* 77 4D Keyboard End 81 */ + 51, /* 78 4E Keyboard PageDown 86 */ + 54, /* 79 4F Keyboard RightArrow 89 */ + 52, /* 80 50 Keyboard LeftArrow 79 */ + 50, /* 81 51 Keyboard DownArrow 84 */ + 56, /* 82 52 Keyboard UpArrow 83 */ + 0, /* 83 53 Keypad Num Lock and Clear 90 */ + '/', /* 84 54 Keypad / 95 */ + '*', /* 85 55 Keypad * 100 */ + '-', /* 86 56 Keypad - 105 */ + '+', /* 87 57 Keypad + 106 */ + 13, /* 88 58 Keypad ENTER 108 */ + '1', /* 89 59 Keypad 1 and End 93 */ + '2', /* 90 5A Keypad 2 and Down Arrow 98 */ + '3', /* 91 5B Keypad 3 and PageDn 103 */ + '4', /* 92 5C Keypad 4 and Left Arrow 92 */ + '5', /* 93 5D Keypad 5 97 */ + '6', /* 94 5E Keypad 6 and Right Arrow 102 */ + '7', /* 95 5F Keypad 7 and Home 91 */ + '8', /* 96 60 Keypad 8 and Up Arrow 96 */ + '9', /* 97 61 Keypad 9 and PageUp 101 */ + '0', /* 98 62 Keypad 0 and Insert 99 */ + '.', /* 99 63 Keypad . and Delete 104 */ + '|' /* 100 64 Keyboard Non-US \ and | 45 */ +}; + +/***********************************/ +/* Keycodes for 1 byte translation */ +/* - CONTROL-KEY pressed - */ +/***********************************/ +const uint8_t keycodes_alt_GR[] = { + 0, /* 0 00 Reserved (no event indicated) */ + 0, /* 1 01 Keyboard ErrorRollOver */ + 0, /* 2 02 Keyboard POSTFail */ + 0, /* 3 03 Keyboard ErrorUndefined */ + 0, /* 4 04 Keyboard a and A 31 */ + 0, /* 5 05 Keyboard b and B 50 */ + 0, /* 6 06 Keyboard c and C 48 */ + 0, /* 7 07 Keyboard d and D 33 */ + 0, /* 8 08 Keyboard e and E 19 */ + 0, /* 9 09 Keyboard f and F 34 */ + 0, /* 10 0A Keyboard g and G 35 */ + 0, /* 11 0B Keyboard h and H 36 */ + 0, /* 12 0C Keyboard i and I 24 */ + 0, /* 13 0D Keyboard j and J 37 */ + 0, /* 14 0E Keyboard k and K 38 */ + 0, /* 15 0F Keyboard l and L 39 */ + 0, /* 16 10 Keyboard m and M 52 */ + 0, /* 17 11 Keyboard n and N 51 */ + 0, /* 18 12 Keyboard o and O 25 */ + 0, /* 19 13 Keyboard p and P 26 */ + '@', /* 20 14 Keyboard q and Q 17 */ + 0, /* 21 15 Keyboard r and R 20 */ + 0, /* 22 16 Keyboard s and S 32 */ + 0, /* 23 17 Keyboard t and T 21 */ + 0, /* 24 18 Keyboard u and U 23 */ + 0, /* 25 19 Keyboard v and V 49 */ + 0, /* 26 1A Keyboard w and W 18 */ + 0, /* 27 1B Keyboard x and X 47 */ + 0, /* 28 1C Keyboard y and Y 22 */ + 0, /* 29 1D Keyboard z and Z 46 */ + 0, /* 30 1E Keyboard 1 and ! 2 */ + 0, /* 31 1F Keyboard 2 and @ 3 */ + 0, /* 32 20 Keyboard 3 and # 4 */ + 0, /* 33 21 Keyboard 4 and $ 5 */ + 0, /* 34 22 Keyboard 5 and % 6 */ + 0, /* 35 23 Keyboard 6 and ^ 7 */ + '{', /* 36 24 Keyboard 7 and & 8 */ + '[', /* 37 25 Keyboard 8 and * 9 */ + ']', /* 38 26 Keyboard 9 and ( 10 */ + '}', /* 39 27 Keyboard 0 and ) 11 */ + 0, /* 40 28 Keyboard Return (ENTER) 43 */ + 0, /* 41 29 Keyboard ESCAPE 110 */ + 0, /* 42 2A Keyboard DELETE (BS) 15 */ + 0, /* 43 2B Keyboard Tab 16 */ + 0, /* 44 2C Keyboard Spacebar 61 */ + '\\', /* 45 2D Keyboard - and (underscore) 12 */ + 0, /* 46 2E Keyboard = and + 13 */ + 0, /* 47 2F Keyboard [ and { 27 */ + '~', /* 48 30 Keyboard ] and } 28 */ + 0, /* 49 31 Keyboard \ and | 29 */ + 0, /* 50 32 Keyboard Non-US # and ~ 42 */ + 0, /* 51 33 Keyboard ; and : 40 */ + 0, /* 52 34 Keyboard ' and " 41 */ + 0, /* 53 35 Keyboard Grave Accent and Tilde 1 */ + 0, /* 54 36 Keyboard , and < 53 */ + 0, /* 55 37 Keyboard . and > 54 */ + 0, /* 56 38 Keyboard / and ? 55 */ + 0, /* 57 39 Keyboard Caps Lock 30 */ + 0, /* 58 3A Keyboard F1 112 */ + 0, /* 59 3B Keyboard F2 113 */ + 0, /* 60 3C Keyboard F3 114 */ + 0, /* 61 3D Keyboard F4 115 */ + 0, /* 62 3E Keyboard F5 116 */ + 0, /* 63 3F Keyboard F6 117 */ + 0, /* 64 40 Keyboard F7 118 */ + 0, /* 65 41 Keyboard F8 119 */ + 0, /* 66 42 Keyboard F9 120 */ + 0, /* 67 43 Keyboard F10 121 */ + 0, /* 68 44 Keyboard F11 122 */ + 0, /* 69 45 Keyboard F12 123 */ + 0, /* 70 46 Keyboard PrintScreen 124 */ + 0, /* 71 47 Keyboard Scroll Lock 125 */ + 0, /* 72 48 Keyboard Pause 126 */ + 0, /* 73 49 Keyboard Insert 75 */ + 0, /* 74 4A Keyboard Home 80 */ + 0, /* 75 4B Keyboard PageUp 85 */ + 0, /* 76 4C Keyboard Delete Forward 76 */ + 0, /* 77 4D Keyboard End 81 */ + 0, /* 78 4E Keyboard PageDown 86 */ + 0, /* 79 4F Keyboard RightArrow 89 */ + 0, /* 80 50 Keyboard LeftArrow 79 */ + 0, /* 81 51 Keyboard DownArrow 84 */ + 0, /* 82 52 Keyboard UpArrow 83 */ + 0, /* 83 53 Keypad Num Lock and Clear 90 */ + 0, /* 84 54 Keypad / 95 */ + 0, /* 85 55 Keypad * 100 */ + 0, /* 86 56 Keypad - 105 */ + 0, /* 87 57 Keypad + 106 */ + 0, /* 88 58 Keypad ENTER 108 */ + 0, /* 89 59 Keypad 1 and End 93 */ + 0, /* 90 5A Keypad 2 and Down Arrow 98 */ + 0, /* 91 5B Keypad 3 and PageDn 103 */ + 0, /* 92 5C Keypad 4 and Left Arrow 92 */ + 0, /* 93 5D Keypad 5 97 */ + 0, /* 94 5E Keypad 6 and Right Arrow 102 */ + 0, /* 95 5F Keypad 7 and Home 91 */ + 0, /* 96 60 Keypad 8 and Up Arrow 96 */ + 0, /* 97 61 Keypad 9 and PageUp 101 */ + 0, /* 98 62 Keypad 0 and Insert 99 */ + 0, /* 99 63 Keypad . and Delete 104 */ + '|' /* 100 64 Keyboard Non-US \ and | 45 */ +}; + + +/***********************************/ +/* Keycodes for 1 byte translation */ +/* - CONTROL-KEY pressed - */ +/***********************************/ +const uint8_t keycodes_ctrl[] = { + 0, /* 0 00 Reserved (no event indicated) */ + 0, /* 1 01 Keyboard ErrorRollOver */ + 0, /* 2 02 Keyboard POSTFail */ + 0, /* 3 03 Keyboard ErrorUndefined */ + 1, /* 4 04 Keyboard a and A 31 */ + 2, /* 5 05 Keyboard b and B 50 */ + 3, /* 6 06 Keyboard c and C 48 */ + 4, /* 7 07 Keyboard d and D 33 */ + 5, /* 8 08 Keyboard e and E 19 */ + 6, /* 9 09 Keyboard f and F 34 */ + 7, /* 10 0A Keyboard g and G 35 */ + 8, /* 11 0B Keyboard h and H 36 */ + 9, /* 12 0C Keyboard i and I 24 */ + 10, /* 13 0D Keyboard j and J 37 */ + 11, /* 14 0E Keyboard k and K 38 */ + 12, /* 15 0F Keyboard l and L 39 */ + 13, /* 16 10 Keyboard m and M 52 */ + 14, /* 17 11 Keyboard n and N 51 */ + 15, /* 18 12 Keyboard o and O 25 */ + 16, /* 19 13 Keyboard p and P 26 */ + 17, /* 20 14 Keyboard q and Q 17 */ + 18, /* 21 15 Keyboard r and R 20 */ + 19, /* 22 16 Keyboard s and S 32 */ + 20, /* 23 17 Keyboard t and T 21 */ + 21, /* 24 18 Keyboard u and U 23 */ + 22, /* 25 19 Keyboard v and V 49 */ + 23, /* 26 1A Keyboard w and W 18 */ + 24, /* 27 1B Keyboard x and X 47 */ + 25, /* 28 1C Keyboard y and Y 22 */ + 26, /* 29 1D Keyboard z and Z 46 */ + 0, /* 30 1E Keyboard 1 and ! 2 */ + 0, /* 31 1F Keyboard 2 and @ 3 */ + 0, /* 32 20 Keyboard 3 and # 4 */ + 0, /* 33 21 Keyboard 4 and $ 5 */ + 0, /* 34 22 Keyboard 5 and % 6 */ + 0, /* 35 23 Keyboard 6 and ^ 7 */ + 0, /* 36 24 Keyboard 7 and & 8 */ + 0, /* 37 25 Keyboard 8 and * 9 */ + 0, /* 38 26 Keyboard 9 and ( 10 */ + 0, /* 39 27 Keyboard 0 and ) 11 */ + 0, /* 40 28 Keyboard Return (ENTER) 43 */ + 0, /* 41 29 Keyboard ESCAPE 110 */ + 0, /* 42 2A Keyboard DELETE (BS) 15 */ + 0, /* 43 2B Keyboard Tab 16 */ + 0, /* 44 2C Keyboard Spacebar 61 */ + 0, /* 45 2D Keyboard - and (underscore) 12 */ + 0, /* 46 2E Keyboard = and + 13 */ + 0, /* 47 2F Keyboard [ and { 27 */ + 0, /* 48 30 Keyboard ] and } 28 */ + 0, /* 49 31 Keyboard \ and | 29 */ + 0, /* 50 32 Keyboard Non-US # and ~ 42 */ + 0, /* 51 33 Keyboard ; and : 40 */ + 0, /* 52 34 Keyboard ' and " 41 */ + 0, /* 53 35 Keyboard Grave Accent and Tilde 1 */ + 0, /* 54 36 Keyboard , and < 53 */ + 0, /* 55 37 Keyboard . and > 54 */ + 0, /* 56 38 Keyboard / and ? 55 */ + 0, /* 57 39 Keyboard Caps Lock 30 */ + 0, /* 58 3A Keyboard F1 112 */ + 0, /* 59 3B Keyboard F2 113 */ + 0, /* 60 3C Keyboard F3 114 */ + 0, /* 61 3D Keyboard F4 115 */ + 0, /* 62 3E Keyboard F5 116 */ + 0, /* 63 3F Keyboard F6 117 */ + 0, /* 64 40 Keyboard F7 118 */ + 0, /* 65 41 Keyboard F8 119 */ + 0, /* 66 42 Keyboard F9 120 */ + 0, /* 67 43 Keyboard F10 121 */ + 0, /* 68 44 Keyboard F11 122 */ + 0, /* 69 45 Keyboard F12 123 */ + 0, /* 70 46 Keyboard PrintScreen 124 */ + 0, /* 71 47 Keyboard Scroll Lock 125 */ + 0, /* 72 48 Keyboard Pause 126 */ + 0, /* 73 49 Keyboard Insert 75 */ + 0, /* 74 4A Keyboard Home 80 */ + 0, /* 75 4B Keyboard PageUp 85 */ + 0, /* 76 4C Keyboard Delete Forward 76 */ + 0, /* 77 4D Keyboard End 81 */ + 0, /* 78 4E Keyboard PageDown 86 */ + 0, /* 79 4F Keyboard RightArrow 89 */ + 0, /* 80 50 Keyboard LeftArrow 79 */ + 0, /* 81 51 Keyboard DownArrow 84 */ + 0, /* 82 52 Keyboard UpArrow 83 */ + 0, /* 83 53 Keypad Num Lock and Clear 90 */ + 0, /* 84 54 Keypad / 95 */ + 0, /* 85 55 Keypad * 100 */ + 0, /* 86 56 Keypad - 105 */ + 0, /* 87 57 Keypad + 106 */ + 0, /* 88 58 Keypad ENTER 108 */ + 0, /* 89 59 Keypad 1 and End 93 */ + 0, /* 90 5A Keypad 2 and Down Arrow 98 */ + 0, /* 91 5B Keypad 3 and PageDn 103 */ + 0, /* 92 5C Keypad 4 and Left Arrow 92 */ + 0, /* 93 5D Keypad 5 97 */ + 0, /* 94 5E Keypad 6 and Right Arrow 102 */ + 0, /* 95 5F Keypad 7 and Home 91 */ + 0, /* 96 60 Keypad 8 and Up Arrow 96 */ + 0, /* 97 61 Keypad 9 and PageUp 101 */ + 0, /* 98 62 Keypad 0 and Insert 99 */ + 0, /* 99 63 Keypad . and Delete 104 */ + 0 /* 100 64 Keyboard Non-US \ and | 45 */ +}; diff --git a/qemu/roms/SLOF/lib/libusb/usb-key.h b/qemu/roms/SLOF/lib/libusb/usb-key.h new file mode 100644 index 000000000..1871a9956 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-key.h @@ -0,0 +1,42 @@ +#ifndef _USB_KEYB_H +#define _USB_KEYB_H + +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#define BIT_0 1 +#define BIT_1 (BIT_0 << 1) +#define BIT_2 (BIT_0 << 2) +#define BIT_3 (BIT_0 << 3) +#define BIT_4 (BIT_0 << 4) +#define BIT_5 (BIT_0 << 5) +#define BIT_6 (BIT_0 << 6) +#define BIT_7 (BIT_0 << 7) + +/* bits from modifier input */ +#define MODIFIER_CTRL (BIT_0 | BIT_4) +#define MODIFIER_SHIFT (BIT_1 | BIT_5) +#define MODIFIER_ALT (BIT_2 | BIT_6) +#define MODIFIER_GUI (BIT_3 | BIT_7) +#define MODIFIER_ALT_GR BIT_6 + +/* bits representing Keyboard-LEDs */ +#define LED_NUM_LOCK BIT_0 +#define LED_CAPS_LOCK BIT_1 +#define LED_SCROLL_LOCK BIT_2 + +extern const uint8_t keycodes_std_US[]; +extern const uint8_t keycodes_shift_US[]; +extern const uint8_t keycodes_alt_GR[]; +extern const uint8_t keycodes_ctrl[]; + +#endif diff --git a/qemu/roms/SLOF/lib/libusb/usb-ohci.c b/qemu/roms/SLOF/lib/libusb/usb-ohci.c new file mode 100644 index 000000000..0e8400481 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-ohci.c @@ -0,0 +1,1055 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include <byteorder.h> +#include "usb.h" +#include "usb-core.h" +#include "usb-ohci.h" + +#undef OHCI_DEBUG +//#define OHCI_DEBUG +#ifdef OHCI_DEBUG +#define dprintf(_x ...) do { printf(_x); } while(0) +#else +#define dprintf(_x ...) +#endif + +#undef OHCI_DEBUG_PACKET +//#define OHCI_DEBUG_PACKET +#ifdef OHCI_DEBUG_PACKET +#define dpprintf(_x ...) do { printf(_x); } while(0) +#else +#define dpprintf(_x ...) +#endif + + +/* + * Dump OHCI register + * + * @param - ohci_hcd + * @return - + */ +static void ohci_dump_regs(struct ohci_regs *regs) +{ + dprintf("\n - HcRevision %08X", read_reg32(®s->rev)); + dprintf(" - HcControl %08X", read_reg32(®s->control)); + dprintf("\n - HcCommandStatus %08X", read_reg32(®s->cmd_status)); + dprintf(" - HcInterruptStatus %08X", read_reg32(®s->intr_status)); + dprintf("\n - HcInterruptEnable %08X", read_reg32(®s->intr_enable)); + dprintf(" - HcInterruptDisable %08X", read_reg32(®s->intr_disable)); + dprintf("\n - HcHCCA %08X", read_reg32(®s->hcca)); + dprintf(" - HcPeriodCurrentED %08X", read_reg32(®s->period_curr_ed)); + dprintf("\n - HcControlHeadED %08X", read_reg32(®s->cntl_head_ed)); + dprintf(" - HcControlCurrentED %08X", read_reg32(®s->cntl_curr_ed)); + dprintf("\n - HcBulkHeadED %08X", read_reg32(®s->bulk_head_ed)); + dprintf(" - HcBulkCurrentED %08X", read_reg32(®s->bulk_curr_ed)); + dprintf("\n - HcDoneHead %08X", read_reg32(®s->done_head)); + dprintf(" - HcFmInterval %08X", read_reg32(®s->fm_interval)); + dprintf("\n - HcFmRemaining %08X", read_reg32(®s->fm_remaining)); + dprintf(" - HcFmNumber %08X", read_reg32(®s->fm_num)); + dprintf("\n - HcPeriodicStart %08X", read_reg32(®s->period_start)); + dprintf(" - HcLSThreshold %08X", read_reg32(®s->ls_threshold)); + dprintf("\n - HcRhDescriptorA %08X", read_reg32(®s->rh_desc_a)); + dprintf(" - HcRhDescriptorB %08X", read_reg32(®s->rh_desc_b)); + dprintf("\n - HcRhStatus %08X", read_reg32(®s->rh_status)); + dprintf("\n"); +} + +/* + * OHCI Spec 5.1.1 + * OHCI Spec 7.4 Root Hub Partition + */ +static int ohci_hcd_reset(struct ohci_regs *regs) +{ + uint32_t time; + + /* USBRESET - 1sec */ + write_reg32(®s->control, 0); + SLOF_msleep(100); + + write_reg32(®s->intr_disable, ~0); + write_reg32(®s->cmd_status, OHCI_CMD_STATUS_HCR); + mb(); + + time = 30; /* wait for not more than 30usec */ + while ((read_reg32(®s->cmd_status) & OHCI_CMD_STATUS_HCR) != 0) { + time--; + if (!time) { + printf(" ** HCD Reset failed..."); + return -1; + } + SLOF_usleep(1); + } + return 0; +} + +static int ohci_hcd_init(struct ohci_hcd *ohcd) +{ + struct ohci_regs *regs; + struct ohci_ed *ed; + long ed_phys = 0; + unsigned int i; + uint32_t oldrwc; + struct usb_dev *rhdev = NULL; + struct usb_ep_descr ep; + uint32_t reg; + + if (!ohcd) + return -1; + + regs = ohcd->regs; + rhdev = &ohcd->rhdev; + dprintf("%s: HCCA memory %p\n", __func__, ohcd->hcca); + dprintf("%s: OHCI Regs %p\n", __func__, regs); + + rhdev->hcidev = ohcd->hcidev; + ep.bmAttributes = USB_EP_TYPE_INTR; + ep.wMaxPacketSize = 8; + rhdev->intr = usb_get_pipe(rhdev, &ep, NULL, 0); + if (!rhdev->intr) { + printf("usb-ohci: oops could not allocate intr_pipe\n"); + return -1; + } + + /* + * OHCI Spec 4.4: Host Controller Communications Area + */ + ed = ohci_pipe_get_ed(rhdev->intr); + ed_phys = ohci_pipe_get_ed_phys(rhdev->intr); + memset(ohcd->hcca, 0, HCCA_SIZE); + memset(ed, 0, sizeof(struct ohci_ed)); + ed->attr = cpu_to_le32(EDA_SKIP); + for (i = 0; i < HCCA_INTR_NUM; i++) + ohcd->hcca->intr_table[i] = cpu_to_le32(ed_phys); + + write_reg32(®s->hcca, ohcd->hcca_phys); + write_reg32(®s->cntl_head_ed, 0); + write_reg32(®s->bulk_head_ed, 0); + + /* OHCI Spec 7.1.2 HcControl Register */ + oldrwc = read_reg32(®s->control) & OHCI_CTRL_RWC; + write_reg32(®s->control, (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | + OHCI_CTRL_BLE | OHCI_CTRL_PLE | + OHCI_USB_OPER | oldrwc)); + SLOF_msleep(100); + /* + * For JS20/21 need to rewrite it after setting it to + * operational state + */ + write_reg32(®s->fm_interval, FRAME_INTERVAL); + write_reg32(®s->period_start, PERIODIC_START); + reg = read_reg32(®s->rh_desc_a); + reg &= ~( RHDA_PSM_INDIVIDUAL | RHDA_OCPM_PERPORT ); + reg |= RHDA_NPS_ENABLE; + write_reg32(®s->rh_desc_a, reg); + write_reg32(®s->rh_desc_b, 0); + mb(); + SLOF_msleep(100); + ohci_dump_regs(regs); + return 0; +} + +/* + * OHCI Spec 7.4 Root Hub Partition + */ +static void ohci_hub_check_ports(struct ohci_hcd *ohcd) +{ + struct ohci_regs *regs; + struct usb_dev *dev; + unsigned int ports, i, port_status, port_clear = 0; + + regs = ohcd->regs; + ports = read_reg32(®s->rh_desc_a) & RHDA_NDP; + write_reg32(®s->rh_status, RH_STATUS_LPSC); + SLOF_msleep(100); + dprintf("usb-ohci: ports connected %d\n", ports); + for (i = 0; i < ports; i++) { + dprintf("usb-ohci: ports scanning %d\n", i); + port_status = read_reg32(®s->rh_ps[i]); + if (port_status & RH_PS_CSC) { + if (port_status & RH_PS_CCS) { + write_reg32(®s->rh_ps[i], RH_PS_PRS); + mb(); + port_clear |= RH_PS_CSC; + dprintf("Start enumerating device\n"); + SLOF_msleep(100); + } else + printf("Start removing device\n"); + } + port_status = read_reg32(®s->rh_ps[i]); + if (port_status & RH_PS_PRSC) { + port_clear |= RH_PS_PRSC; + dev = usb_devpool_get(); + dprintf("usb-ohci: Device reset, setting up %p\n", dev); + dev->hcidev = ohcd->hcidev; + if (!setup_new_device(dev, i)) + printf("usb-ohci: unable to setup device on port %d\n", i); + } + if (port_status & RH_PS_PESC) { + port_clear |= RH_PS_PESC; + if (port_status & RH_PS_PES) + dprintf("enabled\n"); + else + dprintf("disabled\n"); + } + if (port_status & RH_PS_PSSC) { + port_clear |= RH_PS_PESC; + dprintf("suspended\n"); + } + port_clear &= 0xFFFF0000; + if (port_clear) + write_reg32(®s->rh_ps[i], port_clear); + } +} + +static inline struct ohci_ed *ohci_pipe_get_ed(struct usb_pipe *pipe) +{ + struct ohci_pipe *opipe; + opipe = container_of(pipe, struct ohci_pipe, pipe); + dpprintf("%s: ed is %p\n", __func__, &opipe->ed); + return &opipe->ed; +} + +static inline long ohci_pipe_get_ed_phys(struct usb_pipe *pipe) +{ + struct ohci_pipe *opipe; + opipe = container_of(pipe, struct ohci_pipe, pipe); + dpprintf("%s: ed_phys is %x\n", __func__, opipe->ed_phys); + return opipe->ed_phys; +} + +static inline struct ohci_pipe *ohci_pipe_get_opipe(struct usb_pipe *pipe) +{ + struct ohci_pipe *opipe; + opipe = container_of(pipe, struct ohci_pipe, pipe); + dpprintf("%s: opipe is %p\n", __func__, opipe); + return opipe; +} + +static int ohci_alloc_pipe_pool(struct ohci_hcd *ohcd) +{ + struct ohci_pipe *opipe, *curr, *prev; + long opipe_phys = 0; + unsigned int i, count; +#ifdef OHCI_DEBUG_PACKET + struct usb_pipe *pipe; +#endif + + dprintf("usb-ohci: %s enter\n", __func__); + count = OHCI_PIPE_POOL_SIZE/sizeof(*opipe); + ohcd->pool = opipe = SLOF_dma_alloc(OHCI_PIPE_POOL_SIZE); + if (!opipe) + return false; + + ohcd->pool_phys = opipe_phys = SLOF_dma_map_in(opipe, OHCI_PIPE_POOL_SIZE, true); + dprintf("usb-ohci: %s opipe %x, opipe_phys %x size %d count %d\n", + __func__, opipe, opipe_phys, sizeof(*opipe), count); + /* Although an array, link them*/ + for (i = 0, curr = opipe, prev = NULL; i < count; i++, curr++) { + if (prev) + prev->pipe.next = &curr->pipe; + curr->pipe.next = NULL; + prev = curr; + + if (((uint64_t)&curr->ed) % 16) + printf("usb-ohci: Warning ED not aligned to 16byte boundary"); + curr->ed_phys = opipe_phys + (curr - opipe) * sizeof(*curr) + + offset_of(struct ohci_pipe, ed); + } + + if (!ohcd->freelist) + ohcd->freelist = &opipe->pipe; + else + ohcd->end->next = &opipe->pipe; + ohcd->end = &prev->pipe; + +#ifdef OHCI_DEBUG_PACKET + for (i = 0, pipe = ohcd->freelist; pipe; pipe = pipe->next) + dprintf("usb-ohci: %d: pipe cur %p ed %p ed_phys %x\n", + i++, pipe, ohci_pipe_get_ed(pipe), + ohci_pipe_get_ed_phys(pipe)); +#endif + + dprintf("usb-ohci: %s exit\n", __func__); + return true; +} + +static void ohci_init(struct usb_hcd_dev *hcidev) +{ + struct ohci_hcd *ohcd; + + printf(" OHCI: initializing\n"); + dprintf("%s: device base address %p\n", __func__, hcidev->base); + + ohcd = SLOF_alloc_mem(sizeof(struct ohci_hcd)); + if (!ohcd) { + printf("usb-ohci: Unable to allocate memory\n"); + goto out; + } + + hcidev->nextaddr = 1; + hcidev->priv = ohcd; + memset(ohcd, 0, sizeof(*ohcd)); + ohcd->hcidev = hcidev; + ohcd->freelist = NULL; + ohcd->end = NULL; + ohcd->regs = (struct ohci_regs *)(hcidev->base); + ohcd->hcca = SLOF_dma_alloc(sizeof(struct ohci_hcca)); + if (!ohcd->hcca || PTR_U32(ohcd->hcca) & HCCA_ALIGN) { + printf("usb-ohci: Unable to allocate/unaligned HCCA memory %p\n", + ohcd->hcca); + goto out_free_hcd; + } + ohcd->hcca_phys = SLOF_dma_map_in(ohcd->hcca, + sizeof(struct ohci_hcca), true); + dprintf("usb-ohci: HCCA memory %p HCCA-dev memory %08lx\n", + ohcd->hcca, ohcd->hcca_phys); + + ohci_hcd_reset(ohcd->regs); + ohci_hcd_init(ohcd); + ohci_hub_check_ports(ohcd); + return; + +out_free_hcd: + SLOF_dma_free(ohcd->hcca, sizeof(struct ohci_hcca)); + SLOF_free_mem(ohcd, sizeof(struct ohci_hcd)); +out: + return; +} + +static void ohci_exit(struct usb_hcd_dev *hcidev) +{ + struct ohci_hcd *ohcd = NULL; + + dprintf("%s: enter \n", __func__); + if (!hcidev && !hcidev->priv) + return; + + ohcd = hcidev->priv; + write_reg32(&ohcd->regs->control, (OHCI_CTRL_CBSR | OHCI_USB_SUSPEND)); + SLOF_msleep(20); + write_reg32(&ohcd->regs->hcca, cpu_to_le32(0)); + + if (ohcd->pool) { + SLOF_dma_map_out(ohcd->pool_phys, ohcd->pool, OHCI_PIPE_POOL_SIZE); + SLOF_dma_free(ohcd->pool, OHCI_PIPE_POOL_SIZE); + } + if (ohcd->hcca) { + SLOF_dma_map_out(ohcd->hcca_phys, ohcd->hcca, sizeof(struct ohci_hcca)); + SLOF_dma_free(ohcd->hcca, sizeof(struct ohci_hcca)); + } + SLOF_free_mem(ohcd, sizeof(struct ohci_hcd)); + return; +} + +static void ohci_detect(void) +{ + +} + +static void ohci_disconnect(void) +{ + +} + +#define OHCI_CTRL_TDS 3 + +static void ohci_fill_td(struct ohci_td *td, long next, + long req, size_t size, unsigned int attr) +{ + if (size && req) { + td->cbp = cpu_to_le32(req); + td->be = cpu_to_le32(req + size - 1); + } else { + td->cbp = 0; + td->be = 0; + } + td->attr = cpu_to_le32(attr); + td->next_td = cpu_to_le32(next); + + dpprintf("%s: cbp %08X attr %08X next_td %08X be %08X\n", __func__, + le32_to_cpu(td->cbp), le32_to_cpu(td->attr), + le32_to_cpu(td->next_td), le32_to_cpu(td->be)); +} + +static void ohci_fill_ed(struct ohci_ed *ed, long headp, long tailp, + unsigned int attr, long next_ed) +{ + ed->attr = cpu_to_le32(attr); + ed->headp = cpu_to_le32(headp) | (ed->headp & ~EDA_HEADP_MASK_LE); + ed->tailp = cpu_to_le32(tailp); + ed->next_ed = cpu_to_le32(next_ed); + dpprintf("%s: headp %08X tailp %08X next_td %08X attr %08X\n", __func__, + le32_to_cpu(ed->headp), le32_to_cpu(ed->tailp), + le32_to_cpu(ed->next_ed), le32_to_cpu(ed->attr)); + +} + +static long ohci_get_td_phys(struct ohci_td *curr, struct ohci_td *start, long td_phys) +{ + dpprintf("position %d\n", curr - start); + return td_phys + (curr - start) * sizeof(*start); +} + +static long ohci_get_td_virt(struct ohci_td *curr, struct ohci_td *start, long td_virt, long total_count) +{ + dpprintf("position %d\n", curr - start); + if ( (curr - start) >= total_count) { + /* busted position, should ignore this */ + return 0; + } + return td_virt + (curr - start) * sizeof(*start); +} + +/* OHCI Spec: 4.4.2.3 HccaDoneHead*/ +static int ohci_process_done_head(struct ohci_hcd *ohcd, + struct ohci_td *td_start, + long __td_start_phys, long total_count) +{ + struct ohci_hcca *hcca; + struct ohci_td *td_phys = NULL, *td_start_phys; + struct ohci_td *td, *prev_td = NULL; + uint32_t reg = 0, time = 0; + int ret = true; + long count; + + count = total_count; + td_start_phys = (struct ohci_td *) __td_start_phys; + hcca = ohcd->hcca; + time = SLOF_GetTimer() + USB_TIMEOUT; + dpprintf("Claiming %ld\n", count); + +again: + mb(); + /* Check if there is an interrupt */ + reg = read_reg32(&ohcd->regs->intr_status); + while(!(reg & OHCI_INTR_STATUS_WD)) + { + if (time < SLOF_GetTimer()) { + printf("Timed out waiting for interrupt %x\n", reg); + return false; + } + mb(); + reg = read_reg32(&ohcd->regs->intr_status); + } + + /* Interrupt is there, read from done_head pointer */ + td_phys = (struct ohci_td *)(uint64_t) le32_to_cpu(hcca->done_head); + if (!td_phys) { + dprintf("Again td_phys null %ld\n"); + goto again; + } + hcca->done_head = 0; + mb(); + + while (td_phys && (count > 0)) { + td = (struct ohci_td *)(uint64_t) ohci_get_td_virt(td_phys, + td_start_phys, + PTR_U32(td_start), + total_count); + + if (!td) { + printf("USB: Error TD null %p\n", td_phys); + break; + } + count--; + dprintf("Claimed %p(%p) td_start %p count %ld\n", + td, td_phys, td_start_phys, count); + dpprintf("%s: cbp %08X attr %08X next_td %08X be %08X\n", + __func__, + le32_to_cpu(td->cbp), le32_to_cpu(td->attr), + le32_to_cpu(td->next_td), le32_to_cpu(td->be)); + mb(); + reg = (le32_to_cpu(td->attr) & TDA_CC) >> 28; + if (reg) { + dprintf("%s: cbp %08X attr %08X next_td %08X be %08X\n", + __func__, + le32_to_cpu(td->cbp), le32_to_cpu(td->attr), + le32_to_cpu(td->next_td), le32_to_cpu(td->be)); + printf("USB: Error %s %p\n", tda_cc_error[reg], td); + if (reg > 3) /* Return negative error code */ + ret = reg * -1; + } + prev_td = td; + td_phys = (struct ohci_td *)(uint64_t) le32_to_cpu(td->next_td); + prev_td->attr |= cpu_to_le32(TDA_DONE); + prev_td->next_td = 0; + mb(); + } + /* clear the WD interrupt status */ + write_reg32(&ohcd->regs->intr_status, OHCI_INTR_STATUS_WD); + mb(); + read_reg32(&ohcd->regs->intr_status); + + if (count > 0) { + dpprintf("Pending count %d\n", count); + goto again; + } + dprintf("TD claims done\n"); + return ret; +} + +/* + * OHCI Spec: + * 4.2 Endpoint Descriptor + * 4.3.1 General Transfer Descriptor + * 5.2.8 Transfer Descriptor Queues + */ +static int ohci_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data) +{ + struct ohci_ed *ed; + struct ohci_td *tds, *td, *td_phys; + struct ohci_regs *regs; + struct ohci_hcd *ohcd; + uint32_t datalen; + uint32_t dir, attr = 0; + uint32_t time; + int ret = true, i; + long req_phys = 0, data_phys = 0, td_next = 0, td_count = 0; + unsigned char *dbuf; + + datalen = le16_to_cpu(req->wLength); + dir = (req->bmRequestType & REQT_DIR_IN) ? 1 : 0; + + dprintf("usb-ohci: %s len %d DIR_IN %d\n", __func__, datalen, dir); + + tds = td = (struct ohci_td *) SLOF_dma_alloc(sizeof(*td) * OHCI_CTRL_TDS); + td_phys = (struct ohci_td *) SLOF_dma_map_in(td, sizeof(*td) * OHCI_CTRL_TDS, true); + memset(td, 0, sizeof(*td) * OHCI_CTRL_TDS); + + req_phys = SLOF_dma_map_in(req, sizeof(struct usb_dev_req), true); + attr = TDA_DP_SETUP | TDA_CC | TDA_TOGGLE_DATA0; + td_next = ohci_get_td_phys(td + 1, tds, PTR_U32(td_phys)); + ohci_fill_td(td, td_next, req_phys, sizeof(*req), attr); + td++; td_count++; + + if (datalen) { + data_phys = SLOF_dma_map_in(data, datalen, true); + attr = 0; + attr = (dir ? TDA_DP_IN : TDA_DP_OUT) | TDA_TOGGLE_DATA1 | TDA_CC; + td_next = ohci_get_td_phys(td + 1, tds, PTR_U32(td_phys)); + ohci_fill_td(td, td_next, data_phys, datalen, attr); + td++; td_count++; + } + + attr = 0; + attr = (dir ? TDA_DP_OUT : TDA_DP_IN) | TDA_CC | TDA_TOGGLE_DATA1; + td_next = ohci_get_td_phys(td + 1, tds, PTR_U32(td_phys)); + ohci_fill_td(td, 0, 0, 0, attr); + td_count++; + + ed = ohci_pipe_get_ed(pipe); + attr = 0; + attr = EDA_FADDR(pipe->dev->addr) | EDA_MPS(pipe->mps) | EDA_SKIP; + ohci_fill_ed(ed, PTR_U32(td_phys), td_next, attr, 0); + ed->tailp = 0; /* HACK */ + dprintf("usb-ohci: %s - td_start %x td_end %x req %x\n", __func__, + td_phys, td_next, req_phys); + mb(); + ed->attr &= cpu_to_le32(~EDA_SKIP); + + ohcd = pipe->dev->hcidev->priv; + regs = ohcd->regs; + write_reg32(®s->cntl_head_ed, ohci_pipe_get_ed_phys(pipe)); + mb(); + write_reg32(®s->cmd_status, OHCI_CMD_STATUS_CLF); + + time = SLOF_GetTimer() + USB_TIMEOUT; + while ((time > SLOF_GetTimer()) && + ((ed->headp & EDA_HEADP_MASK_LE) != ed->tailp)) + cpu_relax(); + + if ((ed->headp & EDA_HEADP_MASK_LE) == ed->tailp) { + dprintf("%s: packet sent\n", __func__); +#ifdef OHCI_DEBUG_PACKET + dpprintf("Request: "); + dbuf = (unsigned char *)req; + for(i = 0; i < 8; i++) + printf("%02X ", dbuf[i]); + dpprintf("\n"); + if (datalen) { + dbuf = (unsigned char *)data; + dpprintf("Reply: "); + for(i = 0; i < datalen; i++) + printf("%02X ", dbuf[i]); + dpprintf("\n"); + } +#endif + } + else { + printf("%s: timed out - failed\n", __func__); + dpprintf("%s: headp %08X tailp %08X next_td %08X attr %08X\n", + __func__, + le32_to_cpu(ed->headp), le32_to_cpu(ed->tailp), + le32_to_cpu(ed->next_ed), le32_to_cpu(ed->attr)); + printf("Request: "); + dbuf = (unsigned char *)req; + for(i = 0; i < 8; i++) + printf("%02X ", dbuf[i]); + printf("\n"); + } + ret = ohci_process_done_head(ohcd, tds, (long)td_phys, td_count); + mb(); + ed->attr |= cpu_to_le32(EDA_SKIP); + mb(); + write_reg32(®s->cntl_head_ed, 0); + write_reg32(®s->cntl_curr_ed, 0); + + SLOF_dma_map_out(req_phys, req, sizeof(struct usb_dev_req)); + if (datalen) + SLOF_dma_map_out(data_phys, data, datalen); + SLOF_dma_map_out(PTR_U32(td_phys), tds, sizeof(*td) * OHCI_CTRL_TDS); + SLOF_dma_free(tds, sizeof(*td) * OHCI_CTRL_TDS); + return (ret > 0) ? true : false; +} + +static int ohci_transfer_bulk(struct usb_pipe *pipe, void *td_ptr, + void *td_phys_ptr, void *data_phys, int datalen) +{ + struct ohci_ed *ed; + struct ohci_td *td, *tds; + struct ohci_regs *regs; + struct ohci_hcd *ohcd; + long td_phys = 0, td_next, ed_phys, ptr, td_count = 0; + uint32_t dir, attr = 0, count; + size_t len, packet_len; + uint32_t time; + int ret = true; + + if (pipe->type != USB_EP_TYPE_BULK) { + printf("usb-ohci: Not a bulk pipe.\n"); + ret = false; + goto end; + } + + dir = (pipe->dir == USB_PIPE_OUT) ? 0 : 1; + count = datalen / OHCI_MAX_BULK_SIZE; + if (count > OHCI_MAX_TDS) { + printf("usb-ohci: buffer size not supported - %d\n", datalen); + ret = false; + goto end; + } + + td = tds = (struct ohci_td *) td_ptr; + td_phys = (long)td_phys_ptr; + dprintf("usb-ohci: %s pipe %p data_phys %p len %d DIR_IN %d td %p td_phys %p\n", + __func__, pipe, data_phys, datalen, dir, td, td_phys); + + if (!tds) { + printf("%s: tds NULL recieved\n", __func__); + ret = false; + goto end; + } + memset(td, 0, sizeof(*td) * OHCI_MAX_TDS); + + len = datalen; + ptr = (long)data_phys; + attr = 0; + attr = (dir ? TDA_DP_IN : TDA_DP_OUT) | TDA_CC | TDA_ROUNDING; + while (len) { + packet_len = (OHCI_MAX_BULK_SIZE < len)? OHCI_MAX_BULK_SIZE : len; + td_next = ohci_get_td_phys((td + 1), tds, td_phys); + ohci_fill_td(td, td_next, ptr, packet_len, attr); + ptr = ptr + packet_len; + len = len - packet_len; + td++; td_count++; + } + + ed = ohci_pipe_get_ed(pipe); + attr = 0; + dir = pipe->dir ? EDA_DIR_IN : EDA_DIR_OUT; + attr = dir | EDA_FADDR(pipe->dev->addr) | EDA_MPS(pipe->mps) + | EDA_SKIP | pipe->dev->speed | EDA_EP(pipe->epno); + td_next = ohci_get_td_phys(td, tds, td_phys); + ohci_fill_ed(ed, td_phys, td_next, attr, 0); + dprintf("usb-ohci: %s - tds %p td %p\n", __func__, td_phys, td_next); + mb(); + ed->attr &= cpu_to_le32(~EDA_SKIP); + + ohcd = pipe->dev->hcidev->priv; + regs = ohcd->regs; + ed_phys = ohci_pipe_get_ed_phys(pipe); + write_reg32(®s->bulk_head_ed, ed_phys); + mb(); + write_reg32(®s->cmd_status, 0x4); + + time = SLOF_GetTimer() + USB_TIMEOUT; + while ((time > SLOF_GetTimer()) && + ((ed->headp & EDA_HEADP_MASK_LE) != ed->tailp)) + cpu_relax(); + + if ((ed->headp & EDA_HEADP_MASK_LE) == ed->tailp) + dprintf("%s: packet sent\n", __func__); + else { + dpprintf("%s: headp %08X tailp %08X next_td %08X attr %08X\n", __func__, + le32_to_cpu(ed->headp), le32_to_cpu(ed->tailp), + le32_to_cpu(ed->next_ed), le32_to_cpu(ed->attr)); + } + mb(); + ret = ohci_process_done_head(ohcd, tds, td_phys, td_count); + mb(); + ed->attr |= cpu_to_le32(EDA_SKIP); + mb(); + write_reg32(®s->bulk_head_ed, 0); + write_reg32(®s->bulk_curr_ed, 0); + + if (le32_to_cpu(ed->headp) & EDA_HEADP_HALTED) { + printf("ED Halted\n"); + printf("%s: headp %08X tailp %08X next_td %08X attr %08X\n", __func__, + le32_to_cpu(ed->headp), le32_to_cpu(ed->tailp), + le32_to_cpu(ed->next_ed), le32_to_cpu(ed->attr)); + ed->headp &= ~cpu_to_le32(EDA_HEADP_HALTED); + mb(); + if (ret == USB_STALL) /* Call reset recovery */ + usb_msc_resetrecovery(pipe->dev); + } + +end: + return (ret > 0) ? true : false; +} + +/* Populate the hcca intr region with periodic intr */ +static int ohci_get_pipe_intr(struct usb_pipe *pipe, struct ohci_hcd *ohcd, + char *buf, size_t buflen) +{ + struct ohci_hcca *hcca; + struct ohci_pipe *opipe; + struct ohci_ed *ed; + struct usb_dev *dev; + struct ohci_td *tds, *td; + int32_t count = 0, i; + uint8_t *ptr; + uint16_t mps; + long ed_phys, td_phys, td_next, buf_phys; + + if (!pipe || !ohcd) + return false; + + hcca = ohcd->hcca; + dev = pipe->dev; + if (dev->class != DEV_HID_KEYB && dev->class != DEV_HUB) + return false; + + opipe = ohci_pipe_get_opipe(pipe); + ed = &(opipe->ed); + ed_phys = opipe->ed_phys; + mps = pipe->mps; + ed->attr = cpu_to_le32(EDA_DIR_IN | + EDA_FADDR(dev->addr) | + dev->speed | + EDA_MPS(pipe->mps) | + EDA_SKIP | + EDA_EP(pipe->epno)); + dprintf("%s: pipe %p ed %p dev %p opipe %p\n", __func__, + pipe, ed, dev, opipe); + count = (buflen/mps) + 1; + tds = td = SLOF_dma_alloc(sizeof(*td) * count); + if (!tds) { + printf("%s: alloc failed\n", __func__); + return false; + } + td_phys = SLOF_dma_map_in(td, sizeof(*td) * count, false); + + memset(tds, 0, sizeof(*tds) * count); + memset(buf, 0, buflen); + buf_phys = SLOF_dma_map_in(buf, buflen, false); + opipe->td = td; + opipe->td_phys = td_phys; + opipe->count = count; + opipe->buf = buf; + opipe->buflen = buflen; + opipe->buf_phys = buf_phys; + + ptr = (uint8_t *)buf_phys; + for (i = 0; i < count - 1; i++, ptr += mps) { + td = &tds[i]; + td_next = ohci_get_td_phys(td + 1, &tds[0], td_phys); + td->cbp = cpu_to_le32(PTR_U32(ptr)); + td->attr = cpu_to_le32(TDA_DP_IN | TDA_ROUNDING | TDA_CC); + td->next_td = cpu_to_le32(td_next); + td->be = cpu_to_le32(PTR_U32(ptr) + mps - 1); + dprintf("td %x td++ %x ptr %x be %x\n", + td, le32_to_cpu(td->next_td), + ptr, (PTR_U32(ptr) + mps - 1)); + } + td->next_td = 0; + td_next = ohci_get_td_phys(td, &tds[0], td_phys); + ed->headp = cpu_to_le32(td_phys); + ed->tailp = cpu_to_le32(td_next); + + dprintf("%s: head %08X tail %08X, count %d, mps %d\n", __func__, + le32_to_cpu(ed->headp), + le32_to_cpu(ed->tailp), + count, mps); + ed->next_ed = 0; + + + switch (dev->class) { + case DEV_HID_KEYB: + dprintf("%s: Keyboard class %d\n", __func__, dev->class); + hcca->intr_table[0] = cpu_to_le32(ed_phys); + hcca->intr_table[8] = cpu_to_le32(ed_phys); + hcca->intr_table[16] = cpu_to_le32(ed_phys); + hcca->intr_table[24] = cpu_to_le32(ed_phys); + ed->attr &= cpu_to_le32(~EDA_SKIP); + break; + + case DEV_HUB: + dprintf("%s: HUB class %x\n", __func__, dev->class); + hcca->intr_table[1] = cpu_to_le32(ed_phys); + ed->attr &= cpu_to_le32(~EDA_SKIP); + break; + + default: + dprintf("%s: unhandled class %d\n", __func__, dev->class); + } + return true; +} + +static int ohci_put_pipe_intr(struct usb_pipe *pipe, struct ohci_hcd *ohcd) +{ + struct ohci_hcca *hcca; + struct ohci_pipe *opipe; + struct ohci_ed *ed; + struct usb_dev *dev; + struct ohci_td *td; + long ed_phys; + + if (!pipe || !ohcd) + return false; + + hcca = ohcd->hcca; + dev = pipe->dev; + + if (dev->class != DEV_HID_KEYB && dev->class != DEV_HUB) + return false; + + opipe = ohci_pipe_get_opipe(pipe); + ed = &(opipe->ed); + ed_phys = opipe->ed_phys; + dprintf("%s: td %p td_phys %08lx buf %p buf_phys %08lx\n", __func__, + opipe->td, opipe->td_phys, opipe->buf, opipe->buf_phys); + + ed->attr |= cpu_to_le32(EDA_SKIP); + mb(); + ed->headp = 0; + ed->tailp = 0; + ed->next_ed = 0; + SLOF_dma_map_out(opipe->buf_phys, opipe->buf, opipe->buflen); + SLOF_dma_map_out(opipe->td_phys, opipe->td, sizeof(*td) * opipe->count); + SLOF_dma_free(opipe->td, sizeof(*td) * opipe->count); + + switch (dev->class) { + case DEV_HID_KEYB: + dprintf("%s: Keyboard class %d\n", __func__, dev->class); + hcca->intr_table[0] = cpu_to_le32(ed_phys); + hcca->intr_table[8] = cpu_to_le32(ed_phys); + hcca->intr_table[16] = cpu_to_le32(ed_phys); + hcca->intr_table[24] = cpu_to_le32(ed_phys); + break; + + case DEV_HUB: + dprintf("%s: HUB class %d\n", __func__, dev->class); + hcca->intr_table[1] = cpu_to_le32(ed_phys); + break; + + default: + dprintf("%s: unhandled class %d\n", __func__, dev->class); + } + return true; +} + +static int ohci_init_bulk_ed(struct usb_dev *dev, struct usb_pipe *pipe) +{ + struct ohci_pipe *opipe; + struct ohci_ed *ed; + uint32_t dir; + + if (!pipe || !dev) + return false; + + opipe = ohci_pipe_get_opipe(pipe); + ed = &(opipe->ed); + dir = pipe->dir ? EDA_DIR_IN : EDA_DIR_OUT; + + ed->attr = cpu_to_le32(dir | + EDA_FADDR(dev->addr) | + dev->speed | + EDA_MPS(pipe->mps) | + EDA_SKIP | + EDA_EP(pipe->epno)); + + dprintf("%s: pipe %p attr %x\n", __func__, pipe, + le32_to_cpu(ed->attr)); + return true; +} + +static struct usb_pipe *ohci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, + char *buf, size_t buflen) +{ + struct ohci_hcd *ohcd; + struct usb_pipe *new = NULL; + + dprintf("usb-ohci: %s enter %p\n", __func__, dev); + if (!dev) + return NULL; + + ohcd = (struct ohci_hcd *)dev->hcidev->priv; + if (!ohcd->freelist) { + dprintf("usb-ohci: %s allocating pool\n", __func__); + if (!ohci_alloc_pipe_pool(ohcd)) + return NULL; + } + + new = ohcd->freelist; + ohcd->freelist = ohcd->freelist->next; + if (!ohcd->freelist) + ohcd->end = NULL; + + memset(new, 0, sizeof(*new)); + new->dev = dev; + new->next = NULL; + new->type = ep->bmAttributes & USB_EP_TYPE_MASK; + new->speed = dev->speed; + new->mps = le16_to_cpu(ep->wMaxPacketSize); + new->epno = ep->bEndpointAddress & 0xF; + new->dir = ep->bEndpointAddress & 0x80; + if (new->type == USB_EP_TYPE_INTR) + if (!ohci_get_pipe_intr(new, ohcd, buf, buflen)) + dprintf("usb-ohci: %s alloc_intr failed %p\n", + __func__, new); + if (new->type == USB_EP_TYPE_BULK) + ohci_init_bulk_ed(dev, new); + + dprintf("usb-ohci: %s exit %p\n", __func__, new); + return new; +} + +static void ohci_put_pipe(struct usb_pipe *pipe) +{ + struct ohci_hcd *ohcd; + + dprintf("usb-ohci: %s enter - %p\n", __func__, pipe); + if (!pipe || !pipe->dev) + return; + ohcd = pipe->dev->hcidev->priv; + if (ohcd->end) + ohcd->end->next = pipe; + else + ohcd->freelist = pipe; + + if (pipe->type == USB_EP_TYPE_INTR) + if (!ohci_put_pipe_intr(pipe, ohcd)) + dprintf("usb-ohci: %s alloc_intr failed %p\n", + __func__, pipe); + + ohcd->end = pipe; + pipe->next = NULL; + pipe->dev = NULL; + memset(pipe, 0, sizeof(*pipe)); + dprintf("usb-ohci: %s exit\n", __func__); +} + +static uint16_t ohci_get_last_frame(struct usb_dev *dev) +{ + struct ohci_hcd *ohcd; + struct ohci_regs *regs; + + ohcd = dev->hcidev->priv; + regs = ohcd->regs; + return read_reg32(®s->fm_num); +} + +static int ohci_poll_intr(struct usb_pipe *pipe, uint8_t *data) +{ + struct ohci_pipe *opipe; + struct ohci_ed *ed; + struct ohci_td *head, *tail, *curr, *next; + struct ohci_td *head_phys, *tail_phys, *curr_phys; + uint8_t *ptr = NULL; + unsigned int i, pos; + static uint16_t last_frame; + long ptr_phys = 0; + long td_next; + + if (!pipe || last_frame == ohci_get_last_frame(pipe->dev)) + return 0; + + dprintf("%s: enter\n", __func__); + + last_frame = ohci_get_last_frame(pipe->dev); + opipe = ohci_pipe_get_opipe(pipe); + ed = &opipe->ed; + + head_phys = (struct ohci_td *)(long)(le32_to_cpu(ed->headp) & EDA_HEADP_MASK); + tail_phys = (struct ohci_td *)(long)le32_to_cpu(ed->tailp); + curr_phys = (struct ohci_td *) opipe->td_phys; + pos = (tail_phys - curr_phys + 1) % (opipe->count - 1); + dprintf("pos %d %ld -- %d\n", pos, (tail_phys - curr_phys + 1), + opipe->count); + curr = opipe->td + pos; + head = opipe->td + (head_phys - (struct ohci_td *) opipe->td_phys); + tail = opipe->td + (tail_phys - (struct ohci_td *) opipe->td_phys); + + /* dprintf("%08X %08X %08X %08X\n", + opipe->td_phys, head_phys, tail_phys, curr_phys); + dprintf("%08X %08X %08X %08X\n", opipe->td, head, tail, curr); */ + + if (curr != head) { + ptr = (uint8_t *) ((long)opipe->buf + pipe->mps * pos); + ptr_phys = opipe->buf_phys + pipe->mps * pos; + if (le32_to_cpu(*(uint32_t *)ptr) != 0) { + for (i = 0; i < 8; i++) + data[i] = *(ptr + i); + } + + next = curr + 1; + if (next == (opipe->td + opipe->count - 1)) + next = opipe->td; + + curr->attr = cpu_to_le32(TDA_DP_IN | TDA_ROUNDING | TDA_CC); + curr->next_td = cpu_to_le32(0); + curr->cbp = cpu_to_le32(PTR_U32(ptr_phys)); + curr->be = cpu_to_le32(PTR_U32(ptr_phys + pipe->mps - 1)); + td_next = ohci_get_td_phys(curr, opipe->td, opipe->td_phys); + dprintf("Connecting %p to %p(phys %08lx) ptr %p, " + "ptr_phys %08lx\n", tail, curr, td_next, ptr, ptr_phys); + tail->next_td = cpu_to_le32(td_next); + mb(); + ed->tailp = cpu_to_le32(td_next); + } else + return 0; + + dprintf("%s: exit\n", __func__); + return 1; +} + +struct usb_hcd_ops ohci_ops = { + .name = "ohci-hcd", + .init = ohci_init, + .exit = ohci_exit, + .detect = ohci_detect, + .disconnect = ohci_disconnect, + .get_pipe = ohci_get_pipe, + .put_pipe = ohci_put_pipe, + .send_ctrl = ohci_send_ctrl, + .transfer_bulk = ohci_transfer_bulk, + .poll_intr = ohci_poll_intr, + .usb_type = USB_OHCI, + .next = NULL, +}; + +void usb_ohci_register(void) +{ + usb_hcd_register(&ohci_ops); +} diff --git a/qemu/roms/SLOF/lib/libusb/usb-ohci.h b/qemu/roms/SLOF/lib/libusb/usb-ohci.h new file mode 100644 index 000000000..f4535fddd --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-ohci.h @@ -0,0 +1,217 @@ +/****************************************************************************** + * Copyright (c) 2007, 2012, 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * Definitions for OHCI Controller + * + * USB on the PowerStation: + * ohci0 - port 0 -> not connected + * ohci0 - port 1 - 2 -> Internal connector (J60_USBINT) + * ohci1 - port 0 -> not connected + * ohci1 - port 1 - 2 -> External connector (J10_USBEXT) + */ + +#ifndef USB_OHCI_H +#define USB_OHCI_H + +#include <stdint.h> + +struct ohci_regs { + uint32_t rev; + uint32_t control; + uint32_t cmd_status; + uint32_t intr_status; + uint32_t intr_enable; + uint32_t intr_disable; + uint32_t hcca; + uint32_t period_curr_ed; + uint32_t cntl_head_ed; + uint32_t cntl_curr_ed; + uint32_t bulk_head_ed; + uint32_t bulk_curr_ed; + uint32_t done_head; + uint32_t fm_interval; + uint32_t fm_remaining; + uint32_t fm_num; + uint32_t period_start; + uint32_t ls_threshold; + uint32_t rh_desc_a; + uint32_t rh_desc_b; + uint32_t rh_status; + uint32_t rh_ps[5]; +} __attribute__((packed)); + +#define EDA_FADDR(x) ((x & 0x7F)) +#define EDA_EP(x) ((x & 0x0F) << 7) +#define EDA_DIR_OUT (1 << 11) +#define EDA_DIR_IN (1 << 12) +#define EDA_LOW_SPEED (1 << 13) +#define EDA_SKIP (1 << 14) +#define EDA_SKIP_LE (0x400000) /* avoiding conversions */ +#define EDA_FORMAT_ISO (1 << 15) +#define EDA_MPS(x) ((x & 0x7FF) << 16) + +#define EDA_HEADP_MASK (0xFFFFFFFC) +#define EDA_HEADP_MASK_LE (cpu_to_le32(EDA_HEADP_MASK)) +#define EDA_HEADP_HALTED (0x1) +#define EDA_HEADP_CARRY (0x2) + +struct ohci_ed { + uint32_t attr; + uint32_t tailp; + uint32_t headp; + uint32_t next_ed; +} __attribute__((packed)); + +#define TDA_DONE (1 << 17) +#define TDA_ROUNDING (1 << 18) +#define TDA_DP_SETUP (0 << 19) +#define TDA_DP_OUT (1 << 19) +#define TDA_DP_IN (1 << 20) +#define TDA_DI_NO (0x7 << 21) +#define TDA_TOGGLE_DATA0 (0x02000000) +#define TDA_TOGGLE_DATA1 (0x03000000) +#define TDA_CC (0xF << 28) + +#define TDA_ERROR(x) ((x) * -1) + +/* Table 4-7: Completion Codes */ +const char *tda_cc_error[] = { +#define USB_NOERROR TDA_ERROR(0) + "NOERROR", + "CRC", + "BITSTUFFING", + "DATATOGGLEMISMATCH", +#define USB_STALL TDA_ERROR(4) + "STALL", + "DEVICENOTRESPONDING", + "PIDCHECKFAILURE", + "UNEXPECTEDPID", + "DATAOVERRUN", + "DATAUNDERRUN", + "reserved", + "reserved", + "BUFFEROVERRUN", + "BUFFERUNDERRUN", + "NOT ACCESSED", + "NOT ACCESSED", +}; + +struct ohci_td { + uint32_t attr; + uint32_t cbp; + uint32_t next_td; + uint32_t be; +} __attribute__((packed)); + +#define HCCA_SIZE 256 +#define HCCA_ALIGN (HCCA_SIZE - 1) +#define HCCA_INTR_NUM 32 +struct ohci_hcca { + uint32_t intr_table[HCCA_INTR_NUM]; + uint16_t frame_num; + uint16_t pad1; + uint32_t done_head; + uint32_t reserved[120]; +} __attribute__((packed)); + +struct ohci_pipe { + struct ohci_ed ed; /* has to be aligned at 16 byte address*/ + struct usb_pipe pipe; + struct ohci_td *td; + void *buf; + long ed_phys; + long td_phys; + long buf_phys; + uint32_t buflen; + uint32_t count; + uint8_t pad[0]; +}__attribute__((packed)); + +#define OHCI_PIPE_POOL_SIZE 4096 +#define OHCI_MAX_TDS 256 /* supports 16k buffers, i.e. 64 * 256 */ +#define OHCI_MAX_BULK_SIZE 4096 + +struct ohci_hcd { + struct ohci_hcca *hcca; + struct ohci_regs *regs; + struct usb_hcd_dev *hcidev; + struct usb_pipe *freelist; + struct usb_pipe *end; + struct usb_dev rhdev; + long hcca_phys; + void *pool; + long pool_phys; +}; + +#define OHCI_CTRL_CBSR (3 << 0) +#define OHCI_CTRL_PLE (1 << 2) +#define OHCI_CTRL_CLE (1 << 4) +#define OHCI_CTRL_BLE (1 << 5) +#define OHCI_CTRL_HCFS (3 << 6) +#define OHCI_USB_RESET (0 << 6) +#define OHCI_USB_OPER (2 << 6) +#define OHCI_USB_SUSPEND (3 << 6) +#define OHCI_CTRL_RWC (1 << 9) + +/* OHCI Command Status */ +#define OHCI_CMD_STATUS_HCR (1 << 0) +#define OHCI_CMD_STATUS_CLF (1 << 1) +#define OHCI_CMD_STATUS_BLF (1 << 2) + +/* OHCI Interrupt status */ +#define OHCI_INTR_STATUS_WD (1 << 1) + +/* Root Hub Descriptor A bits */ +#define RHDA_NDP (0xFF) +#define RHDA_PSM_INDIVIDUAL (1 << 8) +#define RHDA_NPS_ENABLE (1 << 9) +#define RHDA_DT (1 << 10) +#define RHDA_OCPM_PERPORT (1 << 11) +#define RHDA_NOCP_ENABLE (1 << 12) + +/* Root Hub Descriptor B bits */ +#define RHDB_PPCM_PORT_POWER (0xFFFE) +#define RHDB_PPCM_GLOBAL_POWER (0x0000) + +#define RH_STATUS_LPSC (1 << 16) +#define RH_STATUS_OCIC (1 << 17) +#define RH_STATUS_CREW (1 << 31) + +#define RH_PS_CCS (1 << 0) +#define RH_PS_PES (1 << 1) +#define RH_PS_PSS (1 << 2) +#define RH_PS_POCI (1 << 3) +#define RH_PS_PRS (1 << 4) +#define RH_PS_PPS (1 << 8) +#define RH_PS_LSDA (1 << 9) + +#define RH_PS_CSC (1 << 16) +#define RH_PS_PESC (1 << 17) +#define RH_PS_PSSC (1 << 18) +#define RH_PS_OCIC (1 << 19) +#define RH_PS_PRSC (1 << 20) + +/*********************************************************************/ +/* Values for USB Frame Timing */ +/* One USB frame (1ms) consists of 12000 bit-times as clock is 12MHz */ +/* controller can be adjusted for performance optimization */ +/* We use standard values (OHCI spec 6.3.1, 5.1.1.4, 5.4, 7.3.4) */ +/*********************************************************************/ +#define FRAME_INTERVAL (((((11999 - 210) * 6) / 7) << 16) | 11999) +#define PERIODIC_START ((11999 * 9) / 10) + + +static inline struct ohci_ed *ohci_pipe_get_ed(struct usb_pipe *pipe); +static inline long ohci_pipe_get_ed_phys(struct usb_pipe *pipe); +static int ohci_alloc_pipe_pool(struct ohci_hcd *ohcd); + +#endif /* USB_OHCI_H */ diff --git a/qemu/roms/SLOF/lib/libusb/usb-slof.c b/qemu/roms/SLOF/lib/libusb/usb-slof.c new file mode 100644 index 000000000..de841f0fb --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-slof.c @@ -0,0 +1,61 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * All functions concerning interface to slof + */ + +#include <string.h> +#include "helpers.h" +#include "usb-core.h" +#include "paflof.h" + +#undef SLOF_DEBUG +//#define SLOF_DEBUG +#ifdef SLOF_DEBUG +#define dprintf(_x ...) do { printf(_x); } while(0) +#else +#define dprintf(_x ...) +#endif + +int slof_usb_handle(struct usb_dev *dev) +{ + struct slof_usb_dev sdev; + sdev.port = dev->port; + sdev.addr = dev->addr; + sdev.hcitype = dev->hcidev->type; + sdev.num = dev->hcidev->num; + sdev.udev = dev; + + if (dev->class == DEV_HID_KEYB) { + dprintf("Keyboard %ld %ld\n", dev->hcidev->type, dev->hcidev->num); + sdev.devtype = DEVICE_KEYBOARD; + forth_push((long)&sdev); + forth_eval("s\" dev-keyb.fs\" INCLUDED"); + } else if (dev->class == DEV_HID_MOUSE) { + dprintf("Mouse %ld %ld\n", dev->hcidev->type, dev->hcidev->num); + sdev.devtype = DEVICE_MOUSE; + forth_push((long)&sdev); + forth_eval("s\" dev-mouse.fs\" INCLUDED"); + } else if ((dev->class >> 16 & 0xFF) == 8) { + dprintf("MASS Storage device %ld %ld\n", dev->hcidev->type, dev->hcidev->num); + sdev.devtype = DEVICE_DISK; + forth_push((long)&sdev); + forth_eval("s\" dev-storage.fs\" INCLUDED"); + } else if (dev->class == DEV_HUB) { + dprintf("Generic hub device %ld %ld\n", dev->hcidev->type, + dev->hcidev->num); + sdev.devtype = DEVICE_HUB; + forth_push((long)&sdev); + forth_eval("s\" dev-hub.fs\" INCLUDED"); + } + return true; +} diff --git a/qemu/roms/SLOF/lib/libusb/usb-xhci.c b/qemu/roms/SLOF/lib/libusb/usb-xhci.c new file mode 100644 index 000000000..0c3d6e47f --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-xhci.c @@ -0,0 +1,1316 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include "usb.h" +#include "usb-core.h" +#include "usb-xhci.h" +#include "tools.h" +#include "paflof.h" + +#undef XHCI_DEBUG +//#define XHCI_DEBUG +#ifdef XHCI_DEBUG +#define dprintf(_x ...) do { printf("%s: ", __func__); printf(_x); } while (0) +#else +#define dprintf(_x ...) +#endif + +static void dump_xhci_regs(struct xhci_hcd *xhcd) +{ +#ifdef XHCI_DEBUG + struct xhci_cap_regs *cap; + struct xhci_op_regs *op; + struct xhci_run_regs *run; + + cap = xhcd->cap_regs; + op = xhcd->op_regs; + run = xhcd->run_regs; + + dprintf("\n"); + dprintf(" - CAPLENGTH %02X\n", read_reg8 (&cap->caplength)); + dprintf(" - HCIVERSION %04X\n", read_reg16(&cap->hciversion)); + dprintf(" - HCSPARAMS1 %08X\n", read_reg32(&cap->hcsparams1)); + dprintf(" - HCSPARAMS2 %08X\n", read_reg32(&cap->hcsparams2)); + dprintf(" - HCSPARAMS3 %08X\n", read_reg32(&cap->hcsparams3)); + dprintf(" - HCCPARAMS %08X\n", read_reg32(&cap->hccparams)); + dprintf(" - DBOFF %08X\n", read_reg32(&cap->dboff)); + dprintf(" - RTSOFF %08X\n", read_reg32(&cap->rtsoff)); + dprintf("\n"); + + dprintf(" - USBCMD %08X\n", read_reg32(&op->usbcmd)); + dprintf(" - USBSTS %08X\n", read_reg32(&op->usbsts)); + dprintf(" - PAGESIZE %08X\n", read_reg32(&op->pagesize)); + dprintf(" - DNCTRL %08X\n", read_reg32(&op->dnctrl)); + dprintf(" - CRCR %016llX\n", read_reg64(&op->crcr)); + dprintf(" - DCBAAP %016llX\n", read_reg64(&op->dcbaap)); + dprintf(" - CONFIG %08X\n", read_reg32(&op->config)); + dprintf("\n"); + + dprintf(" - MFINDEX %08X\n", read_reg32(&run->mfindex)); + dprintf("\n"); +#endif +} + +static void print_port_status(struct xhci_port_regs *prs) +{ +#ifdef XHCI_DEBUG + uint32_t portsc; + uint32_t CCS, PED, PP, PLS, i, PR = 0; + + portsc = read_reg32(&prs->portsc); + dprintf("portsc %08x portpmsc %08x portli %08x\n", + portsc, + read_reg32(&prs->portpmsc), + read_reg32(&prs->portli)); + + if (portsc & PORTSC_CCS) { + printf("CCS "); + CCS = 1; + } + if (portsc & PORTSC_PED) { + printf("PED "); + PED = 1; + } + if (portsc & PORTSC_OCA) + printf("OCA "); + if (portsc & PORTSC_PR) + printf("OCA "); + PLS = (portsc & PORTSC_PLS_MASK) >> 5; + printf("PLS:%d ", PLS); + if (portsc & PORTSC_PP) { + printf("PP "); + PP = 1; + } + printf("PS:%d ", (portsc & PORTSC_PS_MASK) >> 10); + printf("PIC:%d ", (portsc & PORTSC_PIC_MASK) >> 14); + if (portsc & PORTSC_LWS) + printf("LWS "); + if (portsc & PORTSC_CSC) + printf("CSC "); + if (portsc & PORTSC_PEC) + printf("PEC "); + if (portsc & PORTSC_WRC) + printf("WRC "); + if (portsc & PORTSC_OCC) + printf("OCC "); + if (portsc & PORTSC_PRC) + printf("PRC "); + if (portsc & PORTSC_PLC) + printf("PLC "); + if (portsc & PORTSC_CEC) + printf("CEC "); + if (portsc & PORTSC_CAS) + printf("CAS "); + if (portsc & PORTSC_WCE) + printf("WCE "); + if (portsc & PORTSC_WDE) + printf("WDE "); + if (portsc & PORTSC_WOE) + printf("WOE "); + if (portsc & PORTSC_DR) + printf("DR "); + if (portsc & PORTSC_WPR) + printf("WPR "); + printf("\n"); + + for (i = 0 ; i < (sizeof(ps_array_usb3)/sizeof(struct port_state)); i++) { + if (PP == ps_array_usb3[i].PP) { + if (CCS == ps_array_usb3[i].CCS) { + if (PED == ps_array_usb3[i].PED) { + if (PR == ps_array_usb3[i].PR) { + dprintf("%s - PLS %d\n", ps_array_usb3[i].state, PLS); + break; + } + } + } + } + } +#endif + +} + +static inline bool xhci_is_hc_ready(uint32_t *usbsts) +{ + return !(read_reg32(usbsts) & XHCI_USBSTS_CNR); +} + +static inline bool xhci_wait_for_cnr(uint32_t *usbsts) +{ + /* Standard: + * Note: The xHC should halt within 16 ms. of software clearing the + * R/S bit to ‘0’. + * Give some more time... 32ms + */ + int count = 320; + dprintf("Waiting for Controller ready .."); + while (!xhci_is_hc_ready(usbsts)) { + dprintf("."); + count--; + if (!count) { + dprintf(" failed %08X\n", read_reg32(usbsts)); + return false; + } + SLOF_usleep(100); + } + dprintf(" done\n"); + return true; +} + +static bool xhci_hcd_set_runstop(struct xhci_op_regs *op, bool run_req) +{ + uint32_t reg; + + dprintf("Request %s\n", run_req ? "RUN" : "STOP"); + if (!xhci_is_hc_ready(&op->usbsts)) { + dprintf("Controller not ready\n"); + return false; + } + + reg = read_reg32(&op->usbcmd); + if (run_req) + reg |= run_req; + else + reg &= (uint32_t)~1; + dprintf("writing %08X\n", reg); + write_reg32(&op->usbcmd, reg); + mb(); + xhci_wait_for_cnr(&op->usbsts); + return true; +} + +static bool xhci_hcd_reset(struct xhci_op_regs *op) +{ + uint32_t reg; + + /* Check if the controller is halted, else halt it */ + if (!(read_reg32(&op->usbsts) & XHCI_USBSTS_HCH)) { + dprintf("HCHalted not set\n"); + if (!xhci_hcd_set_runstop(op, false)) + return false; + } + + if (read_reg32(&op->usbsts) & XHCI_USBSTS_CNR) { + dprintf("Controller not ready\n"); + return false; + } + + reg = read_reg32(&op->usbcmd) | XHCI_USBCMD_HCRST; + /* Ready to Reset the controller now */ + write_reg32(&op->usbcmd, reg); + xhci_wait_for_cnr(&op->usbsts); + return true; +} + +static void xhci_handle_cmd_completion(struct xhci_hcd *xhcd, + struct xhci_event_trb *event) +{ + uint32_t flags, slot_id, status; + + status = le32_to_cpu(event->status); + flags = le32_to_cpu(event->flags); + slot_id = TRB_SLOT_ID(flags); + if (TRB_STATUS(status) == COMP_SUCCESS) + xhcd->slot_id = slot_id; + else + xhcd->slot_id = 0; +} + +static struct xhci_event_trb *xhci_poll_event(struct xhci_hcd *xhcd, + uint32_t event_type) +{ + struct xhci_event_trb *event; + uint64_t val; + uint32_t flags, time; + int index; + + mb(); + event = (struct xhci_event_trb *)xhcd->ering.deq; + flags = le32_to_cpu(event->flags); + + dprintf("Reading from event ptr %p %08x\n", event, flags); + time = SLOF_GetTimer() + USB_TIMEOUT; + + while ((flags & TRB_CYCLE_STATE) != xhcd->ering.cycle_state) { + mb(); + flags = le32_to_cpu(event->flags); + if (time < SLOF_GetTimer()) + return NULL; + } + + mb(); + flags = le32_to_cpu(event->flags); + switch(TRB_TYPE(flags)) + { + case TRB_CMD_COMPLETION: + dprintf("CMD Completion\n"); + xhci_handle_cmd_completion(xhcd, event); + break; + case TRB_PORT_STATUS: + dprintf("Port status event\n"); + break; + case TRB_TRANSFER_EVENT: + dprintf("XFER event addr %16lx, status %08x, flags %08x\n", + le64_to_cpu(event->addr), + le32_to_cpu(event->status), + le32_to_cpu(event->flags)); + break; + default: + printf("TRB_TYPE %d\n", TRB_TYPE(flags)); + dprintf("Event addr %16lx, status %08x, flags %08x state %d\n", + le64_to_cpu(event->addr), + le32_to_cpu(event->status), + flags, xhcd->ering.cycle_state); + break; + } + xhcd->ering.deq = (uint64_t) (event + 1); + + event->addr = 0; + event->status = 0; + event->flags = cpu_to_le32(xhcd->ering.cycle_state); + + index = xhcd->ering.deq - (uint64_t)xhcd->ering.trbs; + val = xhcd->ering.trbs_dma; + val += (index % XHCI_EVENT_TRBS_SIZE); + if (!(index % XHCI_EVENT_TRBS_SIZE)) { + xhcd->ering.deq = (uint64_t)xhcd->ering.trbs; + xhcd->ering.cycle_state = xhcd->ering.cycle_state ? 0 : 1; + dprintf("Rounding %d\n", xhcd->ering.cycle_state); + } + dprintf("Update start %x deq %x index %d\n", + xhcd->ering.trbs_dma, val, index/sizeof(*event)); + write_reg64(&xhcd->run_regs->irs[0].erdp, val); + return event; +} + +static void xhci_send_cmd(struct xhci_hcd *xhcd, uint32_t field1, + uint32_t field2, uint32_t field3, uint32_t field4) +{ + struct xhci_db_regs *dbr; + struct xhci_command_trb *cmd; + uint32_t val, cycle_state; + + dbr = xhcd->db_regs; + cmd = (struct xhci_command_trb *)xhcd->crseg.enq; + + cmd->field[0] = cpu_to_le32(field1); + cmd->field[1] = cpu_to_le32(field2); + cmd->field[2] = cpu_to_le32(field3); + + val = le32_to_cpu(cmd->field[3]); + cycle_state = (val & 0x1) ? 0 : 1; + val = field4 | cycle_state; + cmd->field[3] = cpu_to_le32(val); + + dprintf("CMD %016lx val %08x cycle_state %d field1 %08x, field2 %08x, field3 %08x field4 %08x\n", + cmd, val, cycle_state, + le32_to_cpu(cmd->field[0]), + le32_to_cpu(cmd->field[1]), + le32_to_cpu(cmd->field[2]), + le32_to_cpu(cmd->field[3]) + ); + + /* Ring the doorbell */ + write_reg32(&dbr->db[0], 0); + xhci_poll_event(xhcd, 0); + cmd++; + xhcd->crseg.enq = (uint64_t)cmd; + return; +} + +static void xhci_send_enable_slot(struct xhci_hcd *xhcd, uint32_t port) +{ + uint32_t field1, field2, field3, field4; + + field1 = 0; + field2 = 0; + field3 = 0; + field4 = TRB_CMD_TYPE(TRB_ENABLE_SLOT); + xhci_send_cmd(xhcd, field1, field2, field3, field4); +} + +static void xhci_send_addr_device(struct xhci_hcd *xhcd, uint32_t slot_id, + uint64_t dma_in_ctx) +{ + uint32_t field1, field2, field3, field4; + + dprintf("Address device %lx, low %x, high %x\n", dma_in_ctx, + TRB_ADDR_LOW(dma_in_ctx), + TRB_ADDR_HIGH(dma_in_ctx)); + field1 = TRB_ADDR_LOW(dma_in_ctx) & ~0xF; + field2 = TRB_ADDR_HIGH(dma_in_ctx); + field3 = 0; + field4 = TRB_CMD_TYPE(TRB_ADDRESS_DEV) | TRB_CMD_SLOT_ID(slot_id); + xhci_send_cmd(xhcd, field1, field2, field3, field4); +} + +static uint32_t xhci_get_epno(struct usb_pipe *pipe) +{ + uint32_t x_epno; + x_epno = pipe->dir | 2 * pipe->epno; + dprintf("EPno %d:%d DIR %d\n", pipe->epno, x_epno, pipe->dir); + return x_epno; +} + +static void xhci_configure_ep(struct xhci_hcd *xhcd, uint32_t slot_id, + uint64_t dma_in_ctx) +{ + uint32_t field1, field2, field3, field4; + + dprintf("Configure EP %lx, low %x, high %x\n", dma_in_ctx, + TRB_ADDR_LOW(dma_in_ctx), + TRB_ADDR_HIGH(dma_in_ctx)); + field1 = TRB_ADDR_LOW(dma_in_ctx) & ~0xF; + field2 = TRB_ADDR_HIGH(dma_in_ctx); + field3 = 0; + field4 = TRB_CMD_TYPE(TRB_CONFIG_EP) | TRB_CMD_SLOT_ID(slot_id); + xhci_send_cmd(xhcd, field1, field2, field3, field4); +} + +static void xhci_init_seg(struct xhci_seg *seg, uint32_t size, uint32_t type) +{ + struct xhci_link_trb *link; + + seg->size = size / XHCI_TRB_SIZE; + seg->next = NULL; + seg->type = type; + seg->cycle_state = 1; + seg->enq = (uint64_t)seg->trbs; + seg->deq = (uint64_t)seg->trbs; + memset((void *)seg->trbs, 0, size); + + link =(struct xhci_link_trb *) (seg->trbs + seg->size - 1); + link->addr = cpu_to_le64(seg->trbs_dma); + link->field2 = 0; + link->field3 = cpu_to_le32(0x1 | TRB_CMD_TYPE(TRB_LINK)); + return; +} + +static bool xhci_alloc_seg(struct xhci_seg *seg, uint32_t size, uint32_t type) +{ + seg->trbs = (union xhci_trb *)SLOF_dma_alloc(size); + if (!seg->trbs) { + dprintf("Alloc failed\n"); + return false; + } + xhci_init_seg(seg, size, type); + seg->trbs_dma = SLOF_dma_map_in((void *)seg->trbs, size, false); + + dprintf(" TRBs %016lX TRBS-DMA %016lX\n", seg->trbs, seg->trbs_dma); + return true; +} + +static void xhci_free_seg(struct xhci_seg *seg, uint32_t size) +{ + if (seg->trbs) { + dprintf(" TRBs %016lX TRBS-DMA %016lX size %x\n", seg->trbs, seg->trbs_dma, size); + SLOF_dma_map_out(seg->trbs_dma, (void *)seg->trbs, size); + SLOF_dma_free((void *)seg->trbs, size); + } + memset(seg, 0, sizeof(*seg)); +} + +#define CTX_SIZE(x) ( (x) ? 64 : 32 ) + +static bool xhci_alloc_ctx(struct xhci_ctx *ctx, uint32_t size, uint32_t type) +{ + ctx->addr = (uint8_t *)SLOF_dma_alloc(size); + if (!ctx->addr) { + dprintf("Alloc failed\n"); + return false; + } + ctx->size = size; + ctx->type = type; + memset((void *)ctx->addr, 0, size); + ctx->dma_addr = SLOF_dma_map_in((void *)ctx->addr, size, false); + dprintf("ctx %llx, ctx_dma %llx\n", ctx->addr, ctx->dma_addr); + return true; +} + +static struct xhci_control_ctx *xhci_get_control_ctx(struct xhci_ctx *ctx) +{ + if (ctx->type == XHCI_CTX_TYPE_INPUT) + return (struct xhci_control_ctx *) ctx->addr; + return NULL; +} + +static struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctx *ctx, uint32_t ctx_size) +{ + uint32_t offset = 0; + + if (ctx->type == XHCI_CTX_TYPE_INPUT) + offset += ctx_size; + return (struct xhci_slot_ctx *)(ctx->addr + offset); +} + +static struct xhci_ep_ctx *xhci_get_ep0_ctx(struct xhci_ctx *ctx, uint32_t ctx_size) +{ + uint32_t offset = 0; + + offset = ctx_size; + if (ctx->type == XHCI_CTX_TYPE_INPUT) + offset += ctx_size; + return (struct xhci_ep_ctx *)(ctx->addr + offset); +} + +static struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_ctx *ctx, uint32_t ctx_size, + uint32_t epno) +{ + uint32_t offset = 0; + + offset = ctx_size * epno; + if (ctx->type == XHCI_CTX_TYPE_INPUT) + offset += ctx_size; + return (struct xhci_ep_ctx *)(ctx->addr + offset); +} + +static void xhci_free_ctx(struct xhci_ctx *ctx, uint32_t size) +{ + SLOF_dma_map_out(ctx->dma_addr, (void *)ctx->addr, size); + SLOF_dma_free((void *)ctx->addr, size); +} + +static uint32_t usb_control_max_packet(uint32_t speed) +{ + uint32_t max_packet = 0; + + switch(speed) + { + case USB_LOW_SPEED: + max_packet = 8; + break; + case USB_FULL_SPEED: + max_packet = 8; + break; + case USB_HIGH_SPEED: + max_packet = 64; + break; + case USB_SUPER_SPEED: + max_packet = 512; + break; + default: + /* should not reach here */ + dprintf("Unknown speed\n"); + } + return max_packet; +} + +static bool xhci_alloc_dev(struct xhci_hcd *xhcd, uint32_t slot_id, uint32_t port) +{ + struct usb_dev *dev; + struct xhci_dev *xdev; + struct xhci_slot_ctx *slot; + struct xhci_control_ctx *ctrl; + struct xhci_ep_ctx *ep0; + uint32_t ctx_size, val; + uint16_t max_packet; + uint32_t newport; + + ctx_size = CTX_SIZE(xhcd->hcc_csz_64); + xdev = &xhcd->xdevs[slot_id]; + xdev->slot_id = slot_id; + xdev->ctx_size = ctx_size; + + /* 4.3.3 Device Slot initialization */ + /* Step 1 */ + if (!xhci_alloc_ctx(&xdev->in_ctx, XHCI_CTX_BUF_SIZE, XHCI_CTX_TYPE_INPUT)) { + dprintf("Failed allocating in_ctx\n"); + return false; + } + + /* Step 2 */ + ctrl = xhci_get_control_ctx(&xdev->in_ctx); + ctrl->a_flags = cpu_to_le32(0x3); /* A0, A1 */ + ctrl->d_flags = 0; + + /* Step 3 */ + slot = xhci_get_slot_ctx(&xdev->in_ctx, ctx_size); + newport = port + 1; + val = LAST_CONTEXT(1) | SLOT_SPEED_SS | (newport << 16); /* FIXME speed, read from PS */ + slot->field1 = cpu_to_le32(val); + slot->field2 = cpu_to_le32(ROOT_HUB_PORT(newport)); /* FIXME how to get port no */ + + /* Step 4 */ + if (!xhci_alloc_seg(&xdev->control, XHCI_CONTROL_TRBS_SIZE, TYPE_CTRL)) { + dprintf("Failed allocating control\n"); + goto fail_in_ctx; + } + + /* Step 5 */ + ep0 = xhci_get_ep0_ctx(&xdev->in_ctx, ctx_size); + val = 0; + max_packet = usb_control_max_packet(USB_SUPER_SPEED); + max_packet = 64; + val = EP_TYPE(EP_CTRL) | MAX_BURST(0) | ERROR_COUNT(3) | + MAX_PACKET_SIZE(max_packet); + ep0->field2 = cpu_to_le32(val);; + ep0->deq_addr = cpu_to_le64(xdev->control.trbs_dma | xdev->control.cycle_state); + ep0->field4 = cpu_to_le32(8); + + /* Step 6 */ + if (!xhci_alloc_ctx(&xdev->out_ctx, XHCI_CTX_BUF_SIZE, XHCI_CTX_TYPE_DEVICE)) { + dprintf("Failed allocating out_ctx\n"); + goto fail_control_seg; + } + + /* Step 7 */ + xhcd->dcbaap[slot_id] = cpu_to_le64(xdev->out_ctx.dma_addr); + + /* Step 8 */ + slot = xhci_get_slot_ctx(&xdev->out_ctx, ctx_size); + ep0 = xhci_get_ep0_ctx(&xdev->out_ctx, ctx_size); + + dprintf("Slot State %x \n", SLOT_STATE(le32_to_cpu(slot->field4))); + xhci_send_addr_device(xhcd, slot_id, xdev->in_ctx.dma_addr); + mb(); + dprintf("Slot State %x \n", SLOT_STATE(le32_to_cpu(slot->field4))); + + dprintf("EP0 f0 %08X f1 %08X %016lX %08X\n", + le32_to_cpu(ep0->field1), + le32_to_cpu(ep0->field2), + le64_to_cpu(ep0->deq_addr), + le32_to_cpu(ep0->field4)); + + /* Step 9 - configure ep */ + ctrl->a_flags = cpu_to_le32(0x1); /* A0 */ + ctrl->d_flags = 0; + xhci_configure_ep(xhcd, slot_id, xdev->in_ctx.dma_addr); + mb(); + dprintf("Slot State %x \n", SLOT_STATE(le32_to_cpu(slot->field4))); + dprintf("USB Device address %d \n", USB_DEV_ADDRESS(le32_to_cpu(slot->field4))); + dprintf("EP0 f0 %08X f1 %08X %016lX %08X\n", + le32_to_cpu(ep0->field1), + le32_to_cpu(ep0->field2), + le64_to_cpu(ep0->deq_addr), + le32_to_cpu(ep0->field4)); + + dev = usb_devpool_get(); + dprintf("allocated device %p\n", dev); + dev->hcidev = xhcd->hcidev; + dev->speed = USB_SUPER_SPEED; + dev->addr = USB_DEV_ADDRESS(slot->field4); + dev->port = newport; + dev->priv = xdev; + xdev->dev = dev; + if (setup_new_device(dev, newport)) + return true; + + xhci_free_ctx(&xdev->out_ctx, XHCI_CTX_BUF_SIZE); +fail_control_seg: + xhci_free_seg(&xdev->control, XHCI_CONTROL_TRBS_SIZE); +fail_in_ctx: + xhci_free_ctx(&xdev->in_ctx, XHCI_CTX_BUF_SIZE); + return false; +} + +static void xhci_free_dev(struct xhci_dev *xdev) +{ + xhci_free_seg(&xdev->bulk_in, XHCI_DATA_TRBS_SIZE); + xhci_free_seg(&xdev->bulk_out, XHCI_DATA_TRBS_SIZE); + xhci_free_seg(&xdev->control, XHCI_CONTROL_TRBS_SIZE); + xhci_free_ctx(&xdev->in_ctx, XHCI_CTX_BUF_SIZE); + xhci_free_ctx(&xdev->out_ctx, XHCI_CTX_BUF_SIZE); +} + +static bool usb3_dev_init(struct xhci_hcd *xhcd, uint32_t port) +{ + /* Device enable slot */ + xhci_send_enable_slot(xhcd, port); + if (!xhcd->slot_id) { + dprintf("Unable to get slot id\n"); + return false; + } + dprintf("SLOT ID: %d\n", xhcd->slot_id); + if (!xhci_alloc_dev(xhcd, xhcd->slot_id, port)) { + dprintf("Unable to allocate device\n"); + return false; + } + return true; +} + +static int xhci_hub_check_ports(struct xhci_hcd *xhcd) +{ + uint32_t num_ports, portsc, i; + struct xhci_op_regs *op; + struct xhci_port_regs *prs; + struct xhci_cap_regs *cap; + uint32_t xecp_off; + uint32_t *xecp_addr, *base; + uint32_t port_off = 1, port_cnt; + + dprintf("enter\n"); + + op = xhcd->op_regs; + cap = xhcd->cap_regs; + port_cnt = num_ports = read_reg32(&cap->hcsparams1) >> 24; + + /* Read the xHCI extented capability to find usb3 ports and offset*/ + xecp_off = XHCI_HCCPARAMS_XECP(read_reg32(&cap->hccparams)); + base = (uint32_t *)cap; + while (xecp_off > 0) { + xecp_addr = base + xecp_off; + dprintf(stderr, "xecp_off %d %p %p \n", xecp_off, base, xecp_addr); + + if (XHCI_XECP_CAP_ID(read_reg32(xecp_addr)) == XHCI_XECP_CAP_SP && + XHCI_XECP_CAP_SP_MJ(read_reg32(xecp_addr)) == 3 && + XHCI_XECP_CAP_SP_MN(read_reg32(xecp_addr)) == 0) { + port_cnt = XHCI_XECP_CAP_SP_PC(read_reg32(xecp_addr + 2)); + port_off = XHCI_XECP_CAP_SP_PO(read_reg32(xecp_addr + 2)); + dprintf(stderr, "PortCount %d Portoffset %d\n", port_cnt, port_off); + } + base = xecp_addr; + xecp_off = XHCI_XECP_NEXT_PTR(read_reg32(xecp_addr)); + } + if (port_off == 0) /* port_off should always start from 1 */ + return false; + for (i = (port_off - 1); i < (port_off + port_cnt - 1); i++) { + prs = &op->prs[i]; + portsc = read_reg32(&prs->portsc); + if ((portsc & PORTSC_CCS) && + (portsc & PORTSC_PP) && + (portsc & PORTSC_PED)) { + /* Device present and enabled */ + dprintf("Device present on port %d\n", i); + /* Reset the port */ + portsc = read_reg32(&prs->portsc); + portsc = portsc | PORTSC_PR; + write_reg32(&prs->portsc, portsc); + /* FIXME poll for port event */ + SLOF_msleep(20); + xhci_poll_event(xhcd, 0); + portsc = read_reg32(&prs->portsc); + if (portsc & ~PORTSC_PRC) { + dprintf("Port reset complete %d\n", i); + } + print_port_status(prs); + if (!usb3_dev_init(xhcd, (i - (port_off - 1)))) { + dprintf("USB device initialization failed\n"); + } + } + } + dprintf("exit\n"); + return true; +} + +static bool xhci_hcd_init(struct xhci_hcd *xhcd) +{ + struct xhci_op_regs *op; + struct xhci_int_regs *irs; + uint64_t val; + uint32_t reg; + + if (!xhcd) { + dprintf("NULL pointer\n"); + goto fail; + } + + op = xhcd->op_regs; + irs = &xhcd->run_regs->irs[0]; + if (!xhci_hcd_reset(op)) { + dprintf("Reset failed\n"); + goto fail; + } + + write_reg32(&op->config, XHCI_CONFIG_MAX_SLOT); + reg = read_reg32(&xhcd->cap_regs->hccparams); + /* 64byte context !! */ + xhcd->hcc_csz_64 = (reg & XHCI_HCCPARAMS_CSZ) ? 1 : 0; + + if (xhcd->hcc_csz_64) { + printf("usb-xhci: 64 Byte context not supported\n"); + goto fail; + } + /* + * 6.1 Device Context Base Address Array + * + * Allocate memory and initialize + */ + xhcd->dcbaap = (uint64_t *)SLOF_dma_alloc(XHCI_DCBAAP_MAX_SIZE); + if (!xhcd->dcbaap) { + dprintf("Alloc failed\n"); + goto fail; + } + memset((void *)xhcd->dcbaap, 0, XHCI_DCBAAP_MAX_SIZE); + xhcd->dcbaap_dma = SLOF_dma_map_in((void *)xhcd->dcbaap, + XHCI_DCBAAP_MAX_SIZE, false); + dprintf("dcbaap %llx, dcbaap_phys %llx\n", xhcd->dcbaap, xhcd->dcbaap_dma); + write_reg64(&op->dcbaap, xhcd->dcbaap_dma); + + /* + * Command Ring Control - TRB + * FIXME - better way to allocate it... + */ + if (!xhci_alloc_seg(&xhcd->crseg, XHCI_CRCR_CRP_SIZE, TYPE_COMMAND)) + goto fail_dcbaap; + + val = read_reg64(&op->crcr) & ~XHCI_CRCR_CRP_MASK; + val = val | (xhcd->crseg.trbs_dma & XHCI_CRCR_CRP_MASK); + write_reg64(&op->crcr, val); + + /* + * Event Ring Control - TRB + * Allocate event TRBS + */ + if (!xhci_alloc_seg(&xhcd->ering, XHCI_EVENT_TRBS_SIZE, TYPE_EVENT)) + goto fail_crseg; + + /* + * Populate event ring segment table. + * Note: only using one segment. + */ + xhcd->erst.entries = SLOF_dma_alloc(XHCI_EVENT_TRBS_SIZE); + if (!xhcd->erst.entries) + goto fail_ering; + xhcd->erst.dma = SLOF_dma_map_in((void *)xhcd->erst.entries, + XHCI_EVENT_TRBS_SIZE, false); + xhcd->erst.num_segs = XHCI_ERST_NUM_SEGS; + + /* populate entries[0] */ + write_reg64(&xhcd->erst.entries->addr, xhcd->ering.trbs_dma); + write_reg32(&xhcd->erst.entries->size, xhcd->ering.size); + write_reg32(&xhcd->erst.entries->reserved, 0); + + /* populate erdp */ + val = read_reg64(&irs->erdp) & ~XHCI_ERDP_MASK; + val = val | (xhcd->ering.trbs_dma & XHCI_ERDP_MASK); + write_reg64(&irs->erdp, val); + + /* populate erstsz */ + val = read_reg32(&irs->erstsz) & ~XHCI_ERST_SIZE_MASK; + val = val | xhcd->erst.num_segs; + write_reg32(&irs->erstsz, val); + + /* Now write the erstba */ + val = read_reg64(&irs->erstba) & ~XHCI_ERST_ADDR_MASK; + val = val | (xhcd->erst.dma & XHCI_ERST_ADDR_MASK); + write_reg64(&irs->erstba, val); + + dprintf("ERDP %llx TRB-DMA %llx\n", read_reg64(&irs->erdp), + xhcd->ering.trbs_dma); + dprintf("ERST %llx, ERST DMA %llx, size %d\n", + (uint64_t)xhcd->erst.entries, xhcd->erst.dma, + xhcd->erst.num_segs); + + mb(); + if (!xhci_hcd_set_runstop(op, true)) + goto fail_erst_entries; + + if (!xhci_hub_check_ports(xhcd)) + goto fail_erst_entries; + + return true; +fail_erst_entries: + write_reg64(&irs->erstba, 0); + mb(); + SLOF_dma_map_out(xhcd->erst.dma, (void *)xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE); + SLOF_dma_free((void *)xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE); +fail_ering: + xhci_free_seg(&xhcd->ering, XHCI_EVENT_TRBS_SIZE); +fail_crseg: + val = read_reg64(&op->crcr) & ~XHCI_CRCR_CRP_MASK; + write_reg64(&op->crcr, val); + mb(); + xhci_free_seg(&xhcd->crseg, XHCI_CRCR_CRP_SIZE); +fail_dcbaap: + write_reg64(&op->dcbaap, 0); + mb(); + SLOF_dma_map_out(xhcd->dcbaap_dma, (void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); + SLOF_dma_free((void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); +fail: + return false; +} + +static bool xhci_hcd_exit(struct xhci_hcd *xhcd) +{ + struct xhci_op_regs *op; + struct xhci_int_regs *irs; + uint64_t val; + int i; + + if (!xhcd) { + dprintf("NULL pointer\n"); + return false; + } + op = xhcd->op_regs; + + if (!xhci_hcd_set_runstop(op, false)) { + dprintf("NULL pointer\n"); + } + + for (i = 1; i < XHCI_CONFIG_MAX_SLOT; i++) { + if (xhcd->xdevs[i].dev) + xhci_free_dev(&xhcd->xdevs[i]); + } + + irs = &xhcd->run_regs->irs[0]; + write_reg64(&irs->erstba, 0); + mb(); + if (xhcd->erst.entries) { + SLOF_dma_map_out(xhcd->erst.dma, xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE); + SLOF_dma_free(xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE); + } + xhci_free_seg(&xhcd->ering, XHCI_EVENT_TRBS_SIZE); + + val = read_reg64(&op->crcr) & ~XHCI_CRCR_CRP_MASK; + write_reg64(&op->crcr, val); + xhci_free_seg(&xhcd->crseg, XHCI_CRCR_CRP_SIZE); + write_reg64(&op->dcbaap, 0); + if (xhcd->dcbaap) { + SLOF_dma_map_out(xhcd->dcbaap_dma, (void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); + SLOF_dma_free((void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); + } + return true; +} + +static void xhci_init(struct usb_hcd_dev *hcidev) +{ + struct xhci_hcd *xhcd; + + printf(" XHCI: Initializing\n"); + dprintf("device base address %p\n", hcidev->base); + + hcidev->base = (void *)((uint64_t)hcidev->base & ~7); + xhcd = SLOF_alloc_mem(sizeof(*xhcd)); + if (!xhcd) { + printf("usb-xhci: Unable to allocate memory\n"); + return; + } + memset(xhcd, 0, sizeof(*xhcd)); + + hcidev->nextaddr = 1; + hcidev->priv = xhcd; + xhcd->hcidev = hcidev; + xhcd->cap_regs = (struct xhci_cap_regs *)(hcidev->base); + xhcd->op_regs = (struct xhci_op_regs *)(hcidev->base + + read_reg8(&xhcd->cap_regs->caplength)); + xhcd->run_regs = (struct xhci_run_regs *)(hcidev->base + + read_reg32(&xhcd->cap_regs->rtsoff)); + xhcd->db_regs = (struct xhci_db_regs *)(hcidev->base + + read_reg32(&xhcd->cap_regs->dboff)); + dump_xhci_regs(xhcd); + if (!xhci_hcd_init(xhcd)) + printf("usb-xhci: failed to initialize XHCI controller.\n"); + dump_xhci_regs(xhcd); +} + +static void xhci_exit(struct usb_hcd_dev *hcidev) +{ + struct xhci_hcd *xhcd; + + dprintf("%s: enter \n", __func__); + if (!hcidev && !hcidev->priv) { + return; + } + + xhcd = hcidev->priv; + xhci_hcd_exit(xhcd); + SLOF_free_mem(xhcd, sizeof(*xhcd)); + hcidev->priv = NULL; +} + +static void fill_trb_buff(struct xhci_command_trb *cmd, uint32_t field1, + uint32_t field2, uint32_t field3, uint32_t field4) +{ + uint32_t val, cycle_state; + + cmd->field[0] = cpu_to_le32(field1); + cmd->field[1] = cpu_to_le32(field2); + cmd->field[2] = cpu_to_le32(field3); + + val = le32_to_cpu(cmd->field[3]); + cycle_state = (val & 0x1) ? 0 : 1; + val = cycle_state | (field4 & ~0x1); + cmd->field[3] = cpu_to_le32(val); + + dprintf("CMD %016lx val %08x cycle_state %d field1 %08x, field2 %08x, field3 %08x field4 %08x\n", + cmd, val, cycle_state, + le32_to_cpu(cmd->field[0]), + le32_to_cpu(cmd->field[1]), + le32_to_cpu(cmd->field[2]), + le32_to_cpu(cmd->field[3]) + ); + + return; +} + +static void fill_setup_trb(struct xhci_command_trb *cmd, struct usb_dev_req *req, + uint32_t size) +{ + uint32_t field1, field2, field3, field4 = 0; + uint64_t req_raw; + uint32_t datalen = 0, pid = 0; + + req_raw = *((uint64_t *)req); + dprintf("%lx %lx \n", *((uint64_t *)req), req_raw); + /* req_raw is already in right byte order... */ + field1 = cpu_to_le32(TRB_ADDR_HIGH(req_raw)); + field2 = cpu_to_le32(TRB_ADDR_LOW(req_raw)); + field3 = 8; /* ALWAYS 8 */ + + datalen = cpu_to_le16(req->wLength); + if (datalen) { + pid = (req->bmRequestType & REQT_DIR_IN) ? 3 : 2; + field4 = TRB_TRT(pid); + } + field4 |= TRB_CMD_TYPE(TRB_SETUP_STAGE) | TRB_IDT; + fill_trb_buff(cmd, field1, field2, field3, field4); +} + +static void fill_setup_data(struct xhci_command_trb *cmd, void *data, + uint32_t size, uint32_t dir) +{ + uint32_t field1, field2, field3, field4; + + field1 = TRB_ADDR_LOW(data); + field2 = TRB_ADDR_HIGH(data); + field3 = size; + if (dir) + field4 = TRB_DIR_IN; + field4 |= TRB_CMD_TYPE(TRB_DATA_STAGE); + fill_trb_buff(cmd, field1, field2, field3, field4); +} + +static void fill_status_trb(struct xhci_command_trb *cmd, uint32_t dir) +{ + uint32_t field1, field2, field3, field4; + + field1 = 0; + field2 = 0; + field3 = 0; + if (dir) + field4 = TRB_DIR_IN; + + field4 |= TRB_CMD_TYPE(TRB_STATUS_STAGE) | TRB_IOC; + fill_trb_buff(cmd, field1, field2, field3, field4); +} + +static void fill_normal_trb(struct xhci_transfer_trb *trb, void *data, + uint32_t size) +{ + uint32_t field1, field2, field3, field4; + + field1 = TRB_ADDR_LOW(data); + field2 = TRB_ADDR_HIGH(data); + field3 = size; + field4 = TRB_CMD_TYPE(TRB_NORMAL) | TRB_IOC; + fill_trb_buff((struct xhci_command_trb *)trb, field1, field2, field3, field4); +} + +static int xhci_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data) +{ + struct xhci_dev *xdev; + struct xhci_seg *ctrl; + struct xhci_hcd *xhcd; + struct xhci_command_trb *cmd; + struct xhci_db_regs *dbr; + long req_phys = 0, data_phys = 0; + int ret = true; + uint32_t slot_id, pid = 0, datalen = 0; + + if (!pipe->dev || !pipe->dev->hcidev) { + dprintf(" NULL pointer\n"); + return false; + } + + xdev = pipe->dev->priv; + slot_id = xdev->slot_id; + ctrl = &xdev->control; + xhcd = (struct xhci_hcd *)pipe->dev->hcidev->priv; + dbr = xhcd->db_regs; + if (!ctrl || !xdev || !xhcd) { + dprintf(" NULL pointer\n"); + return false; + } + + cmd = (struct xhci_command_trb *)ctrl->enq; + req_phys = SLOF_dma_map_in(req, sizeof(struct usb_dev_req), true); + fill_setup_trb(cmd, req, sizeof(*req)); + + cmd++; + datalen = cpu_to_le16(req->wLength); + if (datalen) + pid = 1; + if (datalen) { + data_phys = SLOF_dma_map_in(data, datalen, true); + fill_setup_data(cmd, (void *) data_phys, datalen, pid); + cmd++; + } + + fill_status_trb(cmd, pid); + cmd++; + + /* Ring the doorbell - ep0 */ + write_reg32(&dbr->db[slot_id], 1); + if (!xhci_poll_event(xhcd, 0)) { + dprintf("Command failed\n"); + ret = false; + } + ctrl->enq = (uint64_t) cmd; + SLOF_dma_map_out(req_phys, req, sizeof(struct usb_dev_req)); + if (datalen) + SLOF_dma_map_out(data_phys, data, datalen); + return ret; +} + +static inline struct xhci_pipe *xhci_pipe_get_xpipe(struct usb_pipe *pipe) +{ + struct xhci_pipe *xpipe; + xpipe = container_of(pipe, struct xhci_pipe, pipe); + dprintf("%s: xpipe is %p\n", __func__, xpipe); + return xpipe; +} + +static inline struct xhci_seg *xhci_pipe_get_seg(struct usb_pipe *pipe) +{ + struct xhci_pipe *xpipe; + xpipe = xhci_pipe_get_xpipe(pipe); + return xpipe->seg; +} + +static inline void *xhci_get_trb(struct xhci_seg *seg) +{ + uint64_t val, enq; + uint32_t size; + struct xhci_link_trb *link; + + enq = val = seg->enq; + val = val + XHCI_TRB_SIZE; + size = seg->size * XHCI_TRB_SIZE; + /* TRBs being a cyclic buffer, here we cycle back to beginning. */ + if ((val % size) == 0) { + seg->enq = (uint64_t)seg->trbs; + enq = seg->enq; + seg->enq = seg->enq + XHCI_TRB_SIZE; + val = 0; + seg->cycle_state ^= seg->cycle_state; + link = (struct xhci_link_trb *) (seg->trbs + seg->size - 1); + link->addr = cpu_to_le64(seg->trbs_dma); + link->field2 = 0; + link->field3 = cpu_to_le32(0x1 | TRB_CMD_TYPE(TRB_LINK)); + mb(); + } + else { + seg->enq = seg->enq + XHCI_TRB_SIZE; + } + + return (void *)enq; +} + +static int xhci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys, + void *data, int datalen) +{ + struct xhci_dev *xdev; + struct xhci_seg *seg; + struct xhci_hcd *xhcd; + struct xhci_transfer_trb *trb; + struct xhci_db_regs *dbr; + int ret = true; + uint32_t slot_id, epno; + + if (!pipe->dev || !pipe->dev->hcidev) { + dprintf(" NULL pointer\n"); + dprintf(" pipe dev %p hcidev %p\n", pipe->dev, pipe->dev->hcidev); + return false; + } + + xdev = pipe->dev->priv; + slot_id = xdev->slot_id; + seg = xhci_pipe_get_seg(pipe); + xhcd = (struct xhci_hcd *)pipe->dev->hcidev->priv; + dbr = xhcd->db_regs; + if (!seg || !xdev || !xhcd) { + dprintf(" NULL pointer\n"); + dprintf(" seg %p xdev %p xhcd %p\n", seg, xdev, xhcd); + return false; + } + + if (datalen > XHCI_MAX_BULK_SIZE) { + printf("usb-xhci: bulk transfer size too big\n"); + return false; + } + + trb = xhci_get_trb(seg); + fill_normal_trb(trb, (void *)data, datalen); + + epno = xhci_get_epno(pipe); + write_reg32(&dbr->db[slot_id], epno); + if (!xhci_poll_event(xhcd, 0)) { + dprintf("Bulk failed\n"); + ret = false; + } + trb->addr = 0; + trb->len = 0; + trb->flags = 0; + mb(); + + return ret; +} + +static int xhci_alloc_pipe_pool(struct xhci_hcd *xhcd) +{ + struct xhci_pipe *xpipe, *curr, *prev; + unsigned int i, count; + long xpipe_phys = 0; + + count = XHCI_PIPE_POOL_SIZE/sizeof(*xpipe); + xhcd->pool = xpipe = SLOF_dma_alloc(XHCI_PIPE_POOL_SIZE); + if (!xpipe) + return -1; + xhcd->pool_phys = xpipe_phys = SLOF_dma_map_in(xpipe, XHCI_PIPE_POOL_SIZE, true); + dprintf("%s: xpipe %p, xpipe_phys %lx\n", __func__, xpipe, xpipe_phys); + + /* Although an array, link them */ + for (i = 0, curr = xpipe, prev = NULL; i < count; i++, curr++) { + if (prev) + prev->pipe.next = &curr->pipe; + curr->pipe.next = NULL; + prev = curr; + } + + if (!xhcd->freelist) + xhcd->freelist = &xpipe->pipe; + else + xhcd->end->next = &xpipe->pipe; + xhcd->end = &prev->pipe; + + return 0; +} + +static void xhci_init_bulk_ep(struct usb_dev *dev, struct usb_pipe *pipe) +{ + struct xhci_hcd *xhcd; + struct xhci_dev *xdev; + struct xhci_seg *seg; + struct xhci_pipe *xpipe; + struct xhci_control_ctx *ctrl; + struct xhci_ep_ctx *ep; + uint32_t x_epno, val, type; + + if (!pipe || !dev || !dev->priv) + return; + + xdev = dev->priv; + xhcd = dev->hcidev->priv; + dprintf("dir %d\n", pipe->dir); + seg = xhci_pipe_get_seg(pipe); + xpipe = xhci_pipe_get_xpipe(pipe); + if (pipe->dir) { + type = EP_BULK_IN; + seg = &xdev->bulk_in; + } + else { + type = EP_BULK_OUT; + seg = &xdev->bulk_out; + } + + if (!seg->trbs) { + if (!xhci_alloc_seg(seg, XHCI_DATA_TRBS_SIZE, TYPE_BULK)) { + dprintf("Failed allocating seg\n"); + } + } else { + xhci_init_seg(seg, XHCI_DATA_TRBS_SIZE, TYPE_BULK); + } + + pipe->mps = XHCI_MAX_BULK_SIZE; + ctrl = xhci_get_control_ctx(&xdev->in_ctx); + x_epno = xhci_get_epno(pipe); + ep = xhci_get_ep_ctx(&xdev->in_ctx, xdev->ctx_size, x_epno); + val = EP_TYPE(type) | MAX_BURST(0) | ERROR_COUNT(3) | + MAX_PACKET_SIZE(pipe->mps); + ep->field2 = cpu_to_le32(val);; + ep->deq_addr = cpu_to_le64(seg->trbs_dma | seg->cycle_state); + ep->field4 = cpu_to_le32(8); + ctrl->a_flags = cpu_to_le32(BIT(x_epno) | 0x1); + ctrl->d_flags = 0; + xhci_configure_ep(xhcd, xdev->slot_id, xdev->in_ctx.dma_addr); + xpipe->seg = seg; +} + +static struct usb_pipe* xhci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, char *buf, size_t len) +{ + struct xhci_hcd *xhcd; + struct usb_pipe *new = NULL; + + if (!dev) + return NULL; + + xhcd = (struct xhci_hcd *)dev->hcidev->priv; + if (!xhcd->freelist) { + dprintf("usb-xhci: %s allocating pool\n", __func__); + if (xhci_alloc_pipe_pool(xhcd)) + return NULL; + } + + new = xhcd->freelist; + xhcd->freelist = xhcd->freelist->next; + if (!xhcd->freelist) + xhcd->end = NULL; + + memset(new, 0, sizeof(*new)); + new->dev = dev; + new->next = NULL; + new->type = ep->bmAttributes & USB_EP_TYPE_MASK; + new->speed = dev->speed; + new->mps = ep->wMaxPacketSize; + new->dir = (ep->bEndpointAddress & 0x80) >> 7; + new->epno = ep->bEndpointAddress & 0x0f; + + if (new->type == USB_EP_TYPE_BULK) + xhci_init_bulk_ep(dev, new); + + return new; +} + +static void xhci_put_pipe(struct usb_pipe *pipe) +{ + struct xhci_hcd *xhcd; + struct xhci_pipe *xpipe; + + dprintf("usb-xhci: %s enter - %p\n", __func__, pipe); + if (!pipe || !pipe->dev) + return; + xhcd = pipe->dev->hcidev->priv; + + dprintf("dir %d\n", pipe->dir); + if (pipe->type == USB_EP_TYPE_BULK) { + xpipe = xhci_pipe_get_xpipe(pipe); + xpipe->seg = NULL; + } + if (xhcd->end) + xhcd->end->next = pipe; + else + xhcd->freelist = pipe; + + xhcd->end = pipe; + pipe->next = NULL; + pipe->dev = NULL; + memset(pipe, 0, sizeof(*pipe)); + + dprintf("usb-xhci: %s exit\n", __func__); +} + +struct usb_hcd_ops xhci_ops = { + .name = "xhci-hcd", + .init = xhci_init, + .exit = xhci_exit, + .usb_type = USB_XHCI, + .get_pipe = xhci_get_pipe, + .put_pipe = xhci_put_pipe, + .send_ctrl = xhci_send_ctrl, + .transfer_bulk = xhci_transfer_bulk, + .next = NULL, +}; + +void usb_xhci_register(void) +{ + usb_hcd_register(&xhci_ops); +} diff --git a/qemu/roms/SLOF/lib/libusb/usb-xhci.h b/qemu/roms/SLOF/lib/libusb/usb-xhci.h new file mode 100644 index 000000000..faeb07ead --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-xhci.h @@ -0,0 +1,386 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * Definitions for XHCI Controller - Revision 1.0 (5/21/10) + * + */ + +#ifndef USB_XHCI_H +#define USB_XHCI_H + +#include <stdint.h> +#include "usb-core.h" + +#define BIT(x) (1 << x) + +/* 5.3 Host Controller Capability Registers + * Table 19 + */ +struct xhci_cap_regs { + uint8_t caplength; + uint8_t reserved; + uint16_t hciversion; + uint32_t hcsparams1; + uint32_t hcsparams2; + uint32_t hcsparams3; + uint32_t hccparams; +#define XHCI_HCCPARAMS_CSZ BIT(2) +#define XHCI_HCCPARAMS_XECP(x) ((x & 0xFFFF0000) >> 16) + uint32_t dboff; + uint32_t rtsoff; +} __attribute__ ((packed)); + +/* USB 3.0: Section 7 and 7.2 */ +#define XHCI_XECP_CAP_ID(x) ((x & 0xF)) +#define XHCI_XECP_CAP_SP 2 +#define XHCI_XECP_CAP_SP_MN(x) ((x & 0xFF0000) >> 16) +#define XHCI_XECP_CAP_SP_MJ(x) ((x & 0xFF000000) >> 24) +#define XHCI_XECP_CAP_SP_PC(x) ((x & 0xFF00) >> 8) +#define XHCI_XECP_CAP_SP_PO(x) (x & 0xFF) +#define XHCI_XECP_NEXT_PTR(x) ((x & 0xFF00) >> 8) + +/* Table 27: Host Controller USB Port Register Set */ +struct xhci_port_regs { + uint32_t portsc; +#define PORTSC_CCS BIT(0) +#define PORTSC_PED BIT(1) +#define PORTSC_OCA BIT(3) +#define PORTSC_PR BIT(4) +#define PORTSC_PLS_MASK (0xF << 5) +#define PORTSC_PLS_U0 0 +#define PORTSC_PLS_U1 1 +#define PORTSC_PLS_U2 2 +#define PORTSC_PLS_U3 3 +#define PORTSC_PLS_DISABLED 4 +#define PORTSC_PLS_RXDETECT 5 +#define PORTSC_PLS_INACTIVE 6 +#define PORTSC_PLS_POLLING 7 +#define PORTSC_PLS_RECOVERY 8 +#define PORTSC_PLS_HOTRESET 9 +#define PORTSC_PLS_COMP_MODE 10 +#define PORTSC_PLS_TEST_MODE 11 +#define PORTSC_PLS_RESUME 15 +#define PORTSC_PP BIT(9) +#define PORTSC_PS_MASK (0xF << 10) +#define PORTSC_PIC_MASK (0x3 << 14) +#define PORTSC_LWS BIT(16) +#define PORTSC_CSC BIT(17) +#define PORTSC_PEC BIT(18) +#define PORTSC_WRC BIT(19) +#define PORTSC_OCC BIT(20) +#define PORTSC_PRC BIT(21) +#define PORTSC_PLC BIT(22) +#define PORTSC_CEC BIT(23) +#define PORTSC_CAS BIT(24) +#define PORTSC_WCE BIT(25) +#define PORTSC_WDE BIT(26) +#define PORTSC_WOE BIT(27) +#define PORTSC_DR BIT(30) +#define PORTSC_WPR BIT(31) + + uint32_t portpmsc; + uint32_t portli; + uint32_t reserved; +} __attribute__ ((packed)); + +struct port_state { + bool PP; + bool CCS; + bool PED; + bool PR; + uint8_t PLS; + char *state; +}; + + +struct port_state ps_array_usb2[] = { + {1, 0, 0, 0, PORTSC_PLS_U0, "ERROR"} +}; + +struct port_state ps_array_usb3[] = { + {0, 0, 0, 0, PORTSC_PLS_DISABLED, "Powered-OFF"}, + {1, 0, 0, 0, PORTSC_PLS_POLLING, "Polling"}, + {1, 0, 0, 0, PORTSC_PLS_U0, "Polling"}, + {1, 0, 0, 0, PORTSC_PLS_RXDETECT, "*** Disconnected ***"}, + {1, 0, 0, 0, PORTSC_PLS_DISABLED, "Disabled"}, + {1, 0, 0, 0, PORTSC_PLS_INACTIVE, "Error"}, + {1, 0, 0, 0, PORTSC_PLS_TEST_MODE,"Loopback"}, + {1, 0, 0, 0, PORTSC_PLS_COMP_MODE,"Compliancek"}, + {1, 1, 0, 1, PORTSC_PLS_U0, "****** Reset ******"}, + {1, 1, 1, 0, PORTSC_PLS_U0, "****** Enabled ******"}, +}; + +/* 5.4 Host Controller Operational Registers + * Table 26 + */ +struct xhci_op_regs { + uint32_t usbcmd; +#define XHCI_USBCMD_RS BIT(0) +#define XHCI_USBCMD_HCRST BIT(1) + + uint32_t usbsts; +#define XHCI_USBSTS_HCH BIT(0) +#define XHCI_USBSTS_CNR BIT(11) + + uint32_t pagesize; + uint8_t reserved[8]; /* 0C - 13 */ + uint32_t dnctrl; /* Device notification control */ + uint64_t crcr; /* Command ring control */ +#define XHCI_CRCR_CRP_MASK 0xFFFFFFFFFFFFFFC0 +#define XHCI_CRCR_CRR BIT(3) +#define XHCI_CRCR_CRP_SIZE 4096 + + uint8_t reserved1[16]; /* 20 - 2F */ + uint64_t dcbaap; /* Device Context Base Address Array Pointer */ +#define XHCI_DCBAAP_MAX_SIZE 2048 + + uint32_t config; /* Configure */ +#define XHCI_CONFIG_MAX_SLOT 4 + + uint8_t reserved2[964]; /* 3C - 3FF */ + /* USB Port register set */ +#define XHCI_PORT_MAX 256 + struct xhci_port_regs prs[XHCI_PORT_MAX]; +} __attribute__ ((packed)); + +/* + * 5.5.2 Interrupter Register Set + * Table 42: Interrupter Registers + */ +struct xhci_int_regs { + uint32_t iman; + uint32_t imod; + uint32_t erstsz; +#define XHCI_ERST_SIZE_MASK 0xFFFF + uint32_t reserved; + uint64_t erstba; +#define XHCI_ERST_ADDR_MASK (~(0x3FUL)) + uint64_t erdp; +#define XHCI_ERDP_MASK (~(0xFUL)) +} __attribute__ ((packed)); + +/* 5.5 Host Controller Runtime Registers */ +struct xhci_run_regs { + uint32_t mfindex; /* microframe index */ + uint8_t reserved[28]; +#define XHCI_IRS_MAX 1024 + struct xhci_int_regs irs[XHCI_IRS_MAX]; +} __attribute__ ((packed)); + +/* 5.6 Doorbell Registers*/ +struct xhci_db_regs { + uint32_t db[256]; +} __attribute__ ((packed)); + +#define COMP_SUCCESS 1 + +#define TRB_SLOT_ID(x) (((x) & (0xFF << 24)) >> 24) +#define TRB_CMD_SLOT_ID(x) ((x & 0xFF) << 24) +#define TRB_TYPE(x) (((x) & (0x3F << 10)) >> 10) +#define TRB_CMD_TYPE(x) ((x & 0x3F) << 10) +#define TRB_STATUS(x) (((x) & (0xFF << 24)) >> 24) +#define TRB_ADDR_LOW(x) ((uint32_t)((uint64_t)(x))) +#define TRB_ADDR_HIGH(x) ((uint32_t)((uint64_t)(x) >> 32)) +#define TRB_TRT(x) (((x) & 0x3) << 16 ) +#define TRB_DIR_IN BIT(16) +#define TRB_IOC BIT(5) +#define TRB_IDT BIT(6) + +#define TRB_CYCLE_STATE BIT(0) + +struct xhci_transfer_trb { + uint64_t addr; + uint32_t len; + uint32_t flags; +} __attribute__ ((packed)); + +struct xhci_link_trb { + uint64_t addr; + uint32_t field2; + uint32_t field3; +} __attribute__ ((packed)); + +/* Event TRB */ +struct xhci_event_trb { + uint64_t addr; + uint32_t status; + uint32_t flags; +} __attribute__ ((packed)); + +#define TRB_NORMAL 1 +#define TRB_SETUP_STAGE 2 +#define TRB_DATA_STAGE 3 +#define TRB_STATUS_STAGE 4 +#define TRB_ISOCH 5 +#define TRB_LINK 6 +#define TRB_EVENT_DATA 7 +#define TRB_NOOP 8 +#define TRB_ENABLE_SLOT 9 +#define TRB_DISABLE_SLOT 10 +#define TRB_ADDRESS_DEV 11 +#define TRB_CONFIG_EP 12 +#define TRB_EVAL_CNTX 13 +#define TRB_TRANSFER_EVENT 32 +#define TRB_CMD_COMPLETION 33 +#define TRB_PORT_STATUS 34 + +struct xhci_command_trb { + uint32_t field[4]; +}__attribute__ ((packed)); + +union xhci_trb { + struct xhci_event_trb event; + struct xhci_transfer_trb xfer; + struct xhci_command_trb cmd; + struct xhci_link_trb link; +}; + +enum xhci_seg_type { + TYPE_CTRL = 0, + TYPE_BULK, + TYPE_COMMAND, + TYPE_EVENT, +}; + +struct xhci_seg { + union xhci_trb *trbs; + struct xhci_seg *next; + uint64_t enq; + uint64_t deq; + uint64_t trbs_dma; + uint32_t size; + uint32_t cycle_state; + enum xhci_seg_type type; +}; + +#define XHCI_TRB_SIZE 16 +#define XHCI_EVENT_TRBS_SIZE 4096 +#define XHCI_CONTROL_TRBS_SIZE 4096 +#define XHCI_DATA_TRBS_SIZE 4096 +#define XHCI_ERST_NUM_SEGS 1 + +#define XHCI_MAX_BULK_SIZE 0xF000 + +struct xhci_erst_entry { + uint64_t addr; + uint32_t size; + uint32_t reserved; +} __attribute__ ((packed)); + +struct xhci_erst { + struct xhci_erst_entry *entries; + uint64_t dma; + uint32_t num_segs; /* number of segments */ +}; + +struct xhci_control_ctx { + uint32_t d_flags; + uint32_t a_flags; + uint32_t reserved[6]; +} __attribute__ ((packed)); + +struct xhci_slot_ctx { + uint32_t field1; +#define SLOT_SPEED_FS BIT(20) +#define SLOT_SPEED_LS BIT(21) +#define SLOT_SPEED_HS BIT(22) +#define SLOT_SPEED_SS BIT(23) +#define LAST_CONTEXT(x) (x << 27) + + uint32_t field2; +#define ROOT_HUB_PORT(x) ((x & 0xff) << 16) + + uint32_t field3; + uint32_t field4; +#define USB_DEV_ADDRESS(x) (x & 0xFFU) +#define SLOT_STATE(x) ((x >> 27) & 0x1FU) +#define SLOT_STATE_DIS_ENA 0 +#define SLOT_STATE_DEFAULT 1 +#define SLOT_STATE_ADDRESSED 2 +#define SLOT_STATE_CONFIGURED 3 + + + uint32_t reserved[4]; +} __attribute__ ((packed)); + +struct xhci_ep_ctx { + uint32_t field1; + uint32_t field2; +#define MAX_PACKET_SIZE(x) (((x) & 0xFFFF) << 16) +#define MAX_BURST(x) (((x) & 0xFF) << 8) +#define EP_TYPE(x) (((x) & 0x07) << 3) +#define EP_ISOC_OUT 1 +#define EP_BULK_OUT 2 +#define EP_INT_OUT 3 +#define EP_CTRL 4 +#define EP_ISOC_IN 5 +#define EP_BULK_IN 6 +#define EP_INT_IN 7 + +#define ERROR_COUNT(x) (((x) & 0x03) << 1) + + uint64_t deq_addr; + uint32_t field4; + uint32_t reserved[3]; +} __attribute__ ((packed)); + +struct xhci_ctx { + uint8_t type; +#define XHCI_CTX_TYPE_DEVICE 0x1 +#define XHCI_CTX_TYPE_INPUT 0x2 + uint32_t size; + uint8_t *addr; +#define XHCI_CTX_BUF_SIZE 4096 + uint64_t dma_addr; +}; + +struct xhci_dev { + struct usb_dev *dev; + uint32_t slot_id; + struct xhci_ctx in_ctx; + struct xhci_ctx out_ctx; + struct xhci_seg control; + struct xhci_seg bulk_in; + struct xhci_seg bulk_out; + uint32_t ctx_size; +}; + +struct xhci_hcd { + struct xhci_cap_regs *cap_regs; + struct xhci_op_regs *op_regs; + struct xhci_run_regs *run_regs; + struct xhci_db_regs *db_regs; + struct usb_hcd_dev *hcidev; + struct xhci_dev xdevs[XHCI_CONFIG_MAX_SLOT + 1]; + struct usb_pipe *freelist; + struct usb_pipe *end; + uint64_t *dcbaap; + uint64_t dcbaap_dma; + struct xhci_seg ering; + struct xhci_seg crseg; + struct xhci_erst erst; + uint64_t erds_dma; + uint32_t erds_size; + uint32_t slot_id; + uint32_t hcc_csz_64; + void *pool; +#define XHCI_PIPE_POOL_SIZE 4096 + + long pool_phys; +}; + +struct xhci_pipe { + struct usb_pipe pipe; + struct xhci_seg *seg; +}; + +#endif /* USB_XHCI_H */ diff --git a/qemu/roms/SLOF/lib/libusb/usb.code b/qemu/roms/SLOF/lib/libusb/usb.code new file mode 100644 index 000000000..fd92d9e78 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb.code @@ -0,0 +1,162 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * libusb bindings for SLOF - implementation + */ + +#include <usb.h> + + +/************************************************/ +/* Register with the usb-core */ +/* SLOF: USB-OHCI-REGISTER ( -- ) */ +/* LIBNEWUSB: usb_ohci_register(void) */ +/************************************************/ +PRIM(USB_X2d_OHCI_X2d_REGISTER) + usb_ohci_register(); +MIRP + +/************************************************/ +/* Register with the usb-core */ +/* SLOF: USB-EHCI-REGISTER ( -- ) */ +/* LIBNEWUSB: usb_ehci_register(void) */ +/************************************************/ +PRIM(USB_X2d_EHCI_X2d_REGISTER) + usb_ehci_register(); +MIRP + +/************************************************/ +/* Register with the usb-core */ +/* SLOF: USB-XHCI-REGISTER ( -- ) */ +/* LIBNEWUSB: usb_xhci_register(void) */ +/************************************************/ +PRIM(USB_X2d_XHCI_X2d_REGISTER) + usb_xhci_register(); +MIRP + +/************************************************/ +/* Initialize hcidev with the usb-core */ +/* SLOF: USB-HCD-INIT ( hcidev -- ) */ +/* LIBNEWUSB: usb_hcd_init(hcidev) */ +/************************************************/ +PRIM(USB_X2d_HCD_X2d_INIT) + void *hcidev = TOS.a; POP; + usb_hcd_init(hcidev); +MIRP + +/************************************************/ +/* Remove hcidev with the usb-core */ +/* SLOF: USB-HCD-EXIT ( hcidev -- ) */ +/* LIBNEWUSB: usb_hcd_exit(hcidev) */ +/************************************************/ +PRIM(USB_X2d_HCD_X2d_EXIT) + void *hcidev = TOS.a; POP; + usb_hcd_exit(hcidev); +MIRP + +/************************************************/ +/* Initialize hid */ +/* SLOF: USB-HID-INIT ( dev -- true | false )*/ +/* LIBNEWUSB: usb_hid_init(hcidev) */ +/************************************************/ +PRIM(USB_X2d_HID_X2d_INIT) + void *dev = TOS.a; + TOS.n = usb_hid_init(dev); +MIRP + +/************************************************/ +/* Exit hid */ +/* SLOF: USB-HID-EXIT ( dev -- true | false )*/ +/* LIBNEWUSB: usb_hid_exit(hcidev) */ +/************************************************/ +PRIM(USB_X2d_HID_X2d_EXIT) + void *dev = TOS.a; + TOS.n = usb_hid_exit(dev); +MIRP + +/************************************************/ +/* Read usb keyboard for key */ +/* SLOF: USB-READ-KEYB ( dev -- */ +/* ( key | false )) */ +/* LIBNEWUSB: usb_read_keyb */ +/************************************************/ +PRIM(USB_X2d_READ_X2d_KEYB) + void *dev = TOS.a; + TOS.n = usb_read_keyb(dev); +MIRP + +/************************************************/ +/* Is USB KEY available */ +/* SLOF: USB-KEY-AVAILABLE ( dev -- ( true | */ +/* false ))*/ +/* LIBNEWUSB: usb_key_available */ +/************************************************/ +PRIM(USB_X2d_KEY_X2d_AVAILABLE) + void *dev = TOS.a; + TOS.n = usb_key_available(dev); +MIRP + +/************************************************/ +/* Initialize and enumerate generic hub */ +/* SLOF: USB-HUB-INIT ( dev -- true | false ) */ +/* LIBNEWUSB: usb_hub_init */ +/************************************************/ +PRIM(USB_X2d_HUB_X2d_INIT) + void *dev = TOS.a; + TOS.n = usb_hub_init(dev); +MIRP + +/************************************************/ +/* Initialize msc */ +/* SLOF: USB-MSC-INIT ( dev -- true | false )*/ +/* LIBNEWUSB: usb_msc_init(hcidev) */ +/************************************************/ +PRIM(USB_X2d_MSC_X2d_INIT) + void *dev = TOS.a; + TOS.n = usb_msc_init(dev); +MIRP + +/************************************************/ +/* Exit msc */ +/* SLOF: USB-MSC-EXIT ( dev -- true | false )*/ +/* LIBNEWUSB: usb_msc_exit(hcidev) */ +/************************************************/ +PRIM(USB_X2d_MSC_X2d_EXIT) + void *dev = TOS.a; + TOS.n = usb_msc_exit(dev); +MIRP + +/*****************************************************************************/ +/* Transfer data through control endpoint */ +/* SLOF: USB-TRANSFER_CTRL ( dev req data -- true | false ) */ +/* LIBNEWUSB: int usb_transfer_ctrl(void *dev, void *req, void *data) */ +/*****************************************************************************/ +PRIM(USB_X2d_TRANSFER_X2d_CTRL) + void *data = TOS.a; POP; + void *req = TOS.a; POP; + TOS.n = usb_transfer_ctrl(TOS.a, req, data); +MIRP + +/*****************************************************************************/ +/* Transfer data through bulk endpoint */ +/* SLOF: USB-TRANSFER_BULK ( dev dir td td-phys data size -- true | false ) */ +/* LIBNEWUSB: int usb_transfer_bulk(void *dev, int dir, void *td, */ +/* void *td_phys, void *data, int size) */ +/*****************************************************************************/ +PRIM(USB_X2d_TRANSFER_X2d_BULK) + int size = TOS.u; POP; + void *data = TOS.a; POP; + void *td_phys = TOS.a; POP; + void *td = TOS.a; POP; + int dir = TOS.u; POP; + TOS.n = usb_transfer_bulk(TOS.a, dir, td, td_phys, data, size); +MIRP diff --git a/qemu/roms/SLOF/lib/libusb/usb.h b/qemu/roms/SLOF/lib/libusb/usb.h new file mode 100644 index 000000000..fba19d2a1 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb.h @@ -0,0 +1,77 @@ +/****************************************************************************** + * Copyright (c) 2006, 2012, 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * prototypes for libusb implementation used in libusb.code + */ + +#ifndef __LIBUSB_H +#define __LIBUSB_H + +/*******************************************/ +/* SLOF: USB-OHCI-REGISTER */ +/*******************************************/ +extern void usb_ohci_register(void); +/*******************************************/ +/* SLOF: USB-EHCI-REGISTER */ +/*******************************************/ +extern void usb_ehci_register(void); +/*******************************************/ +/* SLOF: USB-XHCI-REGISTER */ +/*******************************************/ +extern void usb_xhci_register(void); +/*******************************************/ +/* SLOF: USB-HCD-INIT */ +/*******************************************/ +extern void usb_hcd_init(void *hcidev); +/*******************************************/ +/* SLOF: USB-HCD-EXIT */ +/*******************************************/ +extern void usb_hcd_exit(void *hcidev); +/*******************************************/ +/* SLOF: USB-HID-INIT */ +/*******************************************/ +extern int usb_hid_init(void *dev); +/*******************************************/ +/* SLOF: USB-HID-EXIT */ +/*******************************************/ +extern int usb_hid_exit(void *dev); +/*******************************************/ +/* SLOF: USB-READ-KEYB */ +/*******************************************/ +extern unsigned char usb_read_keyb(void *dev); +/*******************************************/ +/* SLOF: USB-KEY-AVAILABLE */ +/*******************************************/ +extern unsigned char usb_key_available(void *dev); +/*******************************************/ +/* SLOF: USB-HUB-INIT */ +/*******************************************/ +extern unsigned int usb_hub_init(void *dev); +/*******************************************/ +/* SLOF: USB-MSC-INIT */ +/*******************************************/ +extern int usb_msc_init(void *dev); +/*******************************************/ +/* SLOF: USB-MSC-EXIT */ +/*******************************************/ +extern int usb_msc_exit(void *dev); +/*******************************************/ +/* SLOF: USB-TRANSFER-CTRL */ +/*******************************************/ +extern int usb_transfer_ctrl(void *dev, void *req, void *data); +/*******************************************/ +/* SLOF: USB-TRANSFER-BULK */ +/*******************************************/ +extern int usb_transfer_bulk(void *dev, int dir, void *td, + void *td_phys, void *data, int size); + +#endif diff --git a/qemu/roms/SLOF/lib/libusb/usb.in b/qemu/roms/SLOF/lib/libusb/usb.in new file mode 100644 index 000000000..7ceba7d2d --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb.in @@ -0,0 +1,29 @@ +/****************************************************************************** + * Copyright (c) 2007, 2012, 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * libusb bindings for SLOF - definitions + */ + +cod(USB-OHCI-REGISTER) +cod(USB-EHCI-REGISTER) +cod(USB-XHCI-REGISTER) +cod(USB-HCD-INIT) +cod(USB-HCD-EXIT) +cod(USB-HID-INIT) +cod(USB-HID-EXIT) +cod(USB-READ-KEYB) +cod(USB-KEY-AVAILABLE) +cod(USB-HUB-INIT) +cod(USB-MSC-INIT) +cod(USB-MSC-EXIT) +cod(USB-TRANSFER-CTRL) +cod(USB-TRANSFER-BULK) |