Newer
Older
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
if ((reg & PORT_PLS_MASK) == XDEV_U3)
tmpbuf[0] |= USB_PORT_STAT_SUSPEND;
if (reg & PORT_OC)
tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT;
if (reg & PORT_RESET)
tmpbuf[0] |= USB_PORT_STAT_RESET;
if (reg & PORT_POWER)
/*
* XXX: This Port power bit (for USB 3.0 hub)
* we are faking in USB 2.0 hub port status;
* since there's a change in bit positions in
* two:
* USB 2.0 port status PP is at position[8]
* USB 3.0 port status PP is at position[9]
* So, we are still keeping it at position [8]
*/
tmpbuf[1] |= USB_PORT_STAT_POWER >> 8;
if (reg & PORT_CSC)
tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION;
if (reg & PORT_PEC)
tmpbuf[2] |= USB_PORT_STAT_C_ENABLE;
if (reg & PORT_OCC)
tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT;
if (reg & PORT_RC)
tmpbuf[2] |= USB_PORT_STAT_C_RESET;
srcptr = tmpbuf;
srclen = 4;
break;
case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
reg = xhci_readl(status_reg);
reg = xhci_port_state_to_neutral(reg);
switch (le16_to_cpu(req->value)) {
case USB_PORT_FEAT_ENABLE:
reg |= PORT_PE;
xhci_writel(status_reg, reg);
break;
case USB_PORT_FEAT_POWER:
reg |= PORT_POWER;
xhci_writel(status_reg, reg);
break;
case USB_PORT_FEAT_RESET:
reg |= PORT_RESET;
xhci_writel(status_reg, reg);
break;
default:
printf("unknown feature %x\n", le16_to_cpu(req->value));
goto unknown;
}
break;
case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
reg = xhci_readl(status_reg);
reg = xhci_port_state_to_neutral(reg);
switch (le16_to_cpu(req->value)) {
case USB_PORT_FEAT_ENABLE:
reg &= ~PORT_PE;
break;
case USB_PORT_FEAT_POWER:
reg &= ~PORT_POWER;
break;
case USB_PORT_FEAT_C_RESET:
case USB_PORT_FEAT_C_CONNECTION:
case USB_PORT_FEAT_C_OVER_CURRENT:
case USB_PORT_FEAT_C_ENABLE:
xhci_clear_port_change_bit((le16_to_cpu(req->value)),
le16_to_cpu(req->index),
status_reg, reg);
break;
default:
printf("unknown feature %x\n", le16_to_cpu(req->value));
goto unknown;
}
xhci_writel(status_reg, reg);
break;
default:
puts("Unknown request\n");
goto unknown;
}
debug("scrlen = %d\n req->length = %d\n",
srclen, le16_to_cpu(req->length));
len = min(srclen, (int)le16_to_cpu(req->length));
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
if (srcptr != NULL && len > 0)
memcpy(buffer, srcptr, len);
else
debug("Len is 0\n");
udev->act_len = len;
udev->status = 0;
return 0;
unknown:
udev->act_len = 0;
udev->status = USB_ST_STALLED;
return -ENODEV;
}
/**
* Submits the INT request to XHCI Host cotroller
*
* @param udev pointer to the USB device
* @param pipe contains the DIR_IN or OUT , devnum
* @param buffer buffer to be read/written based on the request
* @param length length of the buffer
* @param interval interval of the interrupt
* @return 0
*/
static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe,
void *buffer, int length, int interval)
if (usb_pipetype(pipe) != PIPE_INTERRUPT) {
printf("non-interrupt pipe (type=%lu)", usb_pipetype(pipe));
return -EINVAL;
}
* xHCI uses normal TRBs for both bulk and interrupt. When the
* interrupt endpoint is to be serviced, the xHC will consume
* (at most) one TD. A TD (comprised of sg list entries) can
* take several service intervals to transmit.
return xhci_bulk_tx(udev, pipe, length, buffer);
}
/**
* submit the BULK type of request to the USB Device
*
* @param udev pointer to the USB device
* @param pipe contains the DIR_IN or OUT , devnum
* @param buffer buffer to be read/written based on the request
* @param length length of the buffer
* @return returns 0 if successful else -1 on failure
*/
static int _xhci_submit_bulk_msg(struct usb_device *udev, unsigned long pipe,
void *buffer, int length)
{
if (usb_pipetype(pipe) != PIPE_BULK) {
printf("non-bulk pipe (type=%lu)", usb_pipetype(pipe));
return -EINVAL;
}
return xhci_bulk_tx(udev, pipe, length, buffer);
}
/**
* submit the control type of request to the Root hub/Device based on the devnum
*
* @param udev pointer to the USB device
* @param pipe contains the DIR_IN or OUT , devnum
* @param buffer buffer to be read/written based on the request
* @param length length of the buffer
* @param setup Request type
* @param root_portnr Root port number that this device is on
* @return returns 0 if successful else -1 on failure
*/
static int _xhci_submit_control_msg(struct usb_device *udev, unsigned long pipe,
void *buffer, int length,
struct devrequest *setup, int root_portnr)
struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
int ret = 0;
if (usb_pipetype(pipe) != PIPE_CONTROL) {
printf("non-control pipe (type=%lu)", usb_pipetype(pipe));
return -EINVAL;
}
if (usb_pipedevice(pipe) == ctrl->rootdev)
return xhci_submit_root(udev, pipe, buffer, setup);
Ted Chen
committed
if (setup->request == USB_REQ_SET_ADDRESS &&
(setup->requesttype & USB_TYPE_MASK) == USB_TYPE_STANDARD)
return xhci_address_device(udev, root_portnr);
Ted Chen
committed
if (setup->request == USB_REQ_SET_CONFIGURATION &&
(setup->requesttype & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
ret = xhci_set_configuration(udev);
if (ret) {
puts("Failed to configure xHCI endpoint\n");
return ret;
}
}
return xhci_ctrl_tx(udev, pipe, setup, length, buffer);
}
static int xhci_lowlevel_init(struct xhci_ctrl *ctrl)
struct xhci_hccr *hccr;
struct xhci_hcor *hcor;
uint32_t val;
uint32_t val2;
uint32_t reg;
hccr = ctrl->hccr;
hcor = ctrl->hcor;
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
/*
* Program the Number of Device Slots Enabled field in the CONFIG
* register with the max value of slots the HC can handle.
*/
val = (xhci_readl(&hccr->cr_hcsparams1) & HCS_SLOTS_MASK);
val2 = xhci_readl(&hcor->or_config);
val |= (val2 & ~HCS_SLOTS_MASK);
xhci_writel(&hcor->or_config, val);
/* initializing xhci data structures */
if (xhci_mem_init(ctrl, hccr, hcor) < 0)
return -ENOMEM;
reg = xhci_readl(&hccr->cr_hcsparams1);
descriptor.hub.bNbrPorts = ((reg & HCS_MAX_PORTS_MASK) >>
HCS_MAX_PORTS_SHIFT);
printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts);
/* Port Indicators */
reg = xhci_readl(&hccr->cr_hccparams);
if (HCS_INDICATOR(reg))
put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics)
| 0x80, &descriptor.hub.wHubCharacteristics);
/* Port Power Control */
if (HCC_PPC(reg))
put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics)
| 0x01, &descriptor.hub.wHubCharacteristics);
if (xhci_start(hcor)) {
xhci_reset(hcor);
return -ENODEV;
}
/* Zero'ing IRQ control register and IRQ pending register */
xhci_writel(&ctrl->ir_set->irq_control, 0x0);
xhci_writel(&ctrl->ir_set->irq_pending, 0x0);
reg = HC_VERSION(xhci_readl(&hccr->cr_capbase));
printf("USB XHCI %x.%02x\n", reg >> 8, reg & 0xff);
return 0;
}
static int xhci_lowlevel_stop(struct xhci_ctrl *ctrl)
{
u32 temp;
xhci_reset(ctrl->hcor);
debug("// Disabling event ring interrupts\n");
temp = xhci_readl(&ctrl->hcor->or_usbsts);
xhci_writel(&ctrl->hcor->or_usbsts, temp & ~STS_EINT);
temp = xhci_readl(&ctrl->ir_set->irq_pending);
xhci_writel(&ctrl->ir_set->irq_pending, ER_IRQ_DISABLE(temp));
int submit_control_msg(struct usb_device *udev, unsigned long pipe,
void *buffer, int length, struct devrequest *setup)
{
struct usb_device *hop = udev;
if (hop->parent)
while (hop->parent->parent)
hop = hop->parent;
return _xhci_submit_control_msg(udev, pipe, buffer, length, setup,
hop->portnr);
}
int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
int length)
{
return _xhci_submit_bulk_msg(udev, pipe, buffer, length);
}
int submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
int length, int interval)
{
return _xhci_submit_int_msg(udev, pipe, buffer, length, interval);
}
/**
* Intialises the XHCI host controller
* and allocates the necessary data structures
*
* @param index index to the host controller data structure
* @return pointer to the intialised controller
*/
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
{
struct xhci_hccr *hccr;
struct xhci_hcor *hcor;
struct xhci_ctrl *ctrl;
int ret;
*controller = NULL;
if (xhci_hcd_init(index, &hccr, (struct xhci_hcor **)&hcor) != 0)
return -ENODEV;
if (xhci_reset(hcor) != 0)
return -ENODEV;
ctrl = &xhcic[index];
ctrl->hccr = hccr;
ctrl->hcor = hcor;
ret = xhci_lowlevel_init(ctrl);
if (ret) {
ctrl->hccr = NULL;
ctrl->hcor = NULL;
} else {
*controller = &xhcic[index];
}
return ret;
}
/**
* Stops the XHCI host controller
* and cleans up all the related data structures
*
* @param index index to the host controller data structure
* @return none
*/
int usb_lowlevel_stop(int index)
{
struct xhci_ctrl *ctrl = (xhcic + index);
if (ctrl->hcor) {
xhci_lowlevel_stop(ctrl);
xhci_hcd_stop(index);
xhci_cleanup(ctrl);
}
#endif /* CONFIG_DM_USB */
#ifdef CONFIG_DM_USB
static int xhci_submit_control_msg(struct udevice *dev, struct usb_device *udev,
unsigned long pipe, void *buffer, int length,
struct devrequest *setup)
{
struct usb_device *uhop;
struct udevice *hub;
int root_portnr = 0;
debug("%s: dev='%s', udev=%p, udev->dev='%s', portnr=%d\n", __func__,
dev->name, udev, udev->dev->name, udev->portnr);
hub = udev->dev;
if (device_get_uclass_id(hub) == UCLASS_USB_HUB) {
/* Figure out our port number on the root hub */
if (usb_hub_is_root_hub(hub)) {
root_portnr = udev->portnr;
} else {
while (!usb_hub_is_root_hub(hub->parent))
uhop = dev_get_parent_priv(hub);
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
root_portnr = uhop->portnr;
}
}
/*
struct usb_device *hop = udev;
if (hop->parent)
while (hop->parent->parent)
hop = hop->parent;
*/
return _xhci_submit_control_msg(udev, pipe, buffer, length, setup,
root_portnr);
}
static int xhci_submit_bulk_msg(struct udevice *dev, struct usb_device *udev,
unsigned long pipe, void *buffer, int length)
{
debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
return _xhci_submit_bulk_msg(udev, pipe, buffer, length);
}
static int xhci_submit_int_msg(struct udevice *dev, struct usb_device *udev,
unsigned long pipe, void *buffer, int length,
int interval)
{
debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
return _xhci_submit_int_msg(udev, pipe, buffer, length, interval);
}
static int xhci_alloc_device(struct udevice *dev, struct usb_device *udev)
{
debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
return _xhci_alloc_device(udev);
}
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
static int xhci_update_hub_device(struct udevice *dev, struct usb_device *udev)
{
struct xhci_ctrl *ctrl = dev_get_priv(dev);
struct usb_hub_device *hub = dev_get_uclass_priv(udev->dev);
struct xhci_virt_device *virt_dev;
struct xhci_input_control_ctx *ctrl_ctx;
struct xhci_container_ctx *out_ctx;
struct xhci_container_ctx *in_ctx;
struct xhci_slot_ctx *slot_ctx;
int slot_id = udev->slot_id;
unsigned think_time;
debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
/* Ignore root hubs */
if (usb_hub_is_root_hub(udev->dev))
return 0;
virt_dev = ctrl->devs[slot_id];
BUG_ON(!virt_dev);
out_ctx = virt_dev->out_ctx;
in_ctx = virt_dev->in_ctx;
ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
/* Initialize the input context control */
ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
ctrl_ctx->drop_flags = 0;
xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size);
/* slot context */
xhci_slot_copy(ctrl, in_ctx, out_ctx);
slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx);
/* Update hub related fields */
slot_ctx->dev_info |= cpu_to_le32(DEV_HUB);
if (hub->tt.multi && udev->speed == USB_SPEED_HIGH)
slot_ctx->dev_info |= cpu_to_le32(DEV_MTT);
slot_ctx->dev_info2 |= cpu_to_le32(XHCI_MAX_PORTS(udev->maxchild));
/*
* Set TT think time - convert from ns to FS bit times.
* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns
*
* 0 = 8 FS bit times, 1 = 16 FS bit times,
* 2 = 24 FS bit times, 3 = 32 FS bit times.
*
* This field shall be 0 if the device is not a high-spped hub.
*/
think_time = hub->tt.think_time;
if (think_time != 0)
think_time = (think_time / 666) - 1;
if (udev->speed == USB_SPEED_HIGH)
slot_ctx->tt_info |= cpu_to_le32(TT_THINK_TIME(think_time));
return xhci_configure_endpoints(udev, false);
}
static int xhci_get_max_xfer_size(struct udevice *dev, size_t *size)
{
/*
* xHCD allocates one segment which includes 64 TRBs for each endpoint
* and the last TRB in this segment is configured as a link TRB to form
* a TRB ring. Each TRB can transfer up to 64K bytes, however data
* buffers referenced by transfer TRBs shall not span 64KB boundaries.
* Hence the maximum number of TRBs we can use in one transfer is 62.
*/
*size = (TRBS_PER_SEGMENT - 2) * TRB_MAX_BUFF_SIZE;
return 0;
}
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
int xhci_register(struct udevice *dev, struct xhci_hccr *hccr,
struct xhci_hcor *hcor)
{
struct xhci_ctrl *ctrl = dev_get_priv(dev);
struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
int ret;
debug("%s: dev='%s', ctrl=%p, hccr=%p, hcor=%p\n", __func__, dev->name,
ctrl, hccr, hcor);
ctrl->dev = dev;
/*
* XHCI needs to issue a Address device command to setup
* proper device context structures, before it can interact
* with the device. So a get_descriptor will fail before any
* of that is done for XHCI unlike EHCI.
*/
priv->desc_before_addr = false;
ret = xhci_reset(hcor);
if (ret)
goto err;
ctrl->hccr = hccr;
ctrl->hcor = hcor;
ret = xhci_lowlevel_init(ctrl);
if (ret)
goto err;
return 0;
err:
free(ctrl);
debug("%s: failed, ret=%d\n", __func__, ret);
return ret;
}
int xhci_deregister(struct udevice *dev)
{
struct xhci_ctrl *ctrl = dev_get_priv(dev);
xhci_lowlevel_stop(ctrl);
xhci_cleanup(ctrl);
return 0;
}
struct dm_usb_ops xhci_usb_ops = {
.control = xhci_submit_control_msg,
.bulk = xhci_submit_bulk_msg,
.interrupt = xhci_submit_int_msg,
.alloc_device = xhci_alloc_device,
.update_hub_device = xhci_update_hub_device,
.get_max_xfer_size = xhci_get_max_xfer_size,