USB: xhci: Print NEC firmware version.
authorSarah Sharp <sarah.a.sharp@linux.intel.com>
Mon, 24 May 2010 20:25:28 +0000 (13:25 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 4 Jun 2010 20:16:19 +0000 (13:16 -0700)
The NEC xHCI host controller firmware version can be found by putting a
vendor-specific command on the command ring and extracting the BCD
encoded-version out of the vendor-specific event TRB.

The firmware version debug line in dmesg will look like:

xhci_hcd 0000:05:00.0: NEC firmware version 30.21

(NEC merged with Renesas Technologies and became Renesas Electronics on
April 1, 2010.  I have their OK to merge this vendor-specific code.)

Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: Satoshi Otani <satoshi.otani.xm@renesas.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/xhci-pci.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h

index edffd81..11482b6 100644 (file)
@@ -78,6 +78,8 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
                        xhci_dbg(xhci, "QUIRK: Fresco Logic xHC needs configure"
                                        " endpoint cmd after reset endpoint\n");
        }
+       if (pdev->vendor == PCI_VENDOR_ID_NEC)
+               xhci->quirks |= XHCI_NEC_HOST;
 
        /* Make sure the HC is halted. */
        retval = xhci_halt(xhci);
index 36c858e..9012098 100644 (file)
@@ -1071,6 +1071,15 @@ bandwidth_change:
                        xhci_warn(xhci, "Reset device command completion "
                                        "for disabled slot %u\n", slot_id);
                break;
+       case TRB_TYPE(TRB_NEC_GET_FW):
+               if (!(xhci->quirks & XHCI_NEC_HOST)) {
+                       xhci->error_bitmask |= 1 << 6;
+                       break;
+               }
+               xhci_dbg(xhci, "NEC firmware version %2x.%02x\n",
+                               NEC_FW_MAJOR(event->status),
+                               NEC_FW_MINOR(event->status));
+               break;
        default:
                /* Skip over unknown commands on the event ring */
                xhci->error_bitmask |= 1 << 6;
@@ -1079,6 +1088,17 @@ bandwidth_change:
        inc_deq(xhci, xhci->cmd_ring, false);
 }
 
+static void handle_vendor_event(struct xhci_hcd *xhci,
+               union xhci_trb *event)
+{
+       u32 trb_type;
+
+       trb_type = TRB_FIELD_TO_TYPE(event->generic.field[3]);
+       xhci_dbg(xhci, "Vendor specific event TRB type = %u\n", trb_type);
+       if (trb_type == TRB_NEC_CMD_COMP && (xhci->quirks & XHCI_NEC_HOST))
+               handle_cmd_completion(xhci, &event->event_cmd);
+}
+
 static void handle_port_status(struct xhci_hcd *xhci,
                union xhci_trb *event)
 {
@@ -1659,7 +1679,10 @@ void xhci_handle_event(struct xhci_hcd *xhci)
                        update_ptrs = 0;
                break;
        default:
-               xhci->error_bitmask |= 1 << 3;
+               if ((event->event_cmd.flags & TRB_TYPE_BITMASK) >= TRB_TYPE(48))
+                       handle_vendor_event(xhci, event);
+               else
+                       xhci->error_bitmask |= 1 << 3;
        }
        /* Any of the above functions may drop and re-acquire the lock, so check
         * to make sure a watchdog timer didn't mark the host as non-responsive.
@@ -2378,6 +2401,12 @@ int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
                        false);
 }
 
+int xhci_queue_vendor_command(struct xhci_hcd *xhci,
+               u32 field1, u32 field2, u32 field3, u32 field4)
+{
+       return queue_command(xhci, field1, field2, field3, field4, false);
+}
+
 /* Queue a reset device command TRB */
 int xhci_queue_reset_device(struct xhci_hcd *xhci, u32 slot_id)
 {
index 8a49c67..27345cd 100644 (file)
@@ -486,6 +486,9 @@ int xhci_run(struct usb_hcd *hcd)
 
        if (NUM_TEST_NOOPS > 0)
                doorbell = xhci_setup_one_noop(xhci);
+       if (xhci->quirks & XHCI_NEC_HOST)
+               xhci_queue_vendor_command(xhci, 0, 0, 0,
+                               TRB_TYPE(TRB_NEC_GET_FW));
 
        if (xhci_start(xhci)) {
                xhci_halt(xhci);
@@ -495,6 +498,8 @@ int xhci_run(struct usb_hcd *hcd)
        xhci_dbg(xhci, "// @%p = 0x%x\n", &xhci->op_regs->command, temp);
        if (doorbell)
                (*doorbell)(xhci);
+       if (xhci->quirks & XHCI_NEC_HOST)
+               xhci_ring_cmd_db(xhci);
 
        xhci_dbg(xhci, "Finished xhci_run\n");
        return 0;
index dada2fb..8b4b7d3 100644 (file)
@@ -925,6 +925,7 @@ union xhci_trb {
 /* TRB bit mask */
 #define        TRB_TYPE_BITMASK        (0xfc00)
 #define TRB_TYPE(p)            ((p) << 10)
+#define TRB_FIELD_TO_TYPE(p)   (((p) & TRB_TYPE_BITMASK) >> 10)
 /* TRB type IDs */
 /* bulk, interrupt, isoc scatter/gather, and control data stage */
 #define TRB_NORMAL             1
@@ -992,6 +993,14 @@ union xhci_trb {
 #define TRB_MFINDEX_WRAP       39
 /* TRB IDs 40-47 reserved, 48-63 is vendor-defined */
 
+/* Nec vendor-specific command completion event. */
+#define        TRB_NEC_CMD_COMP        48
+/* Get NEC firmware revision. */
+#define        TRB_NEC_GET_FW          49
+
+#define NEC_FW_MINOR(p)                (((p) >> 0) & 0xff)
+#define NEC_FW_MAJOR(p)                (((p) >> 8) & 0xff)
+
 /*
  * TRBS_PER_SEGMENT must be a multiple of 4,
  * since the command ring is 64-byte aligned.
@@ -1172,6 +1181,7 @@ struct xhci_hcd {
        unsigned int            quirks;
 #define        XHCI_LINK_TRB_QUIRK     (1 << 0)
 #define XHCI_RESET_EP_QUIRK    (1 << 1)
+#define XHCI_NEC_HOST          (1 << 2)
 };
 
 /* For testing purposes */
@@ -1379,6 +1389,8 @@ void xhci_set_hc_event_deq(struct xhci_hcd *xhci);
 int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id);
 int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
                u32 slot_id);
+int xhci_queue_vendor_command(struct xhci_hcd *xhci,
+               u32 field1, u32 field2, u32 field3, u32 field4);
 int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id,
                unsigned int ep_index);
 int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,