USB: isp1760: Support board-specific hardware configurations
authorNate Case <ncase@xes-inc.com>
Tue, 17 Jun 2008 16:11:38 +0000 (11:11 -0500)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 21 Jul 2008 22:16:33 +0000 (15:16 -0700)
This adds support for hardware configurations that don't match the
chip default register settings (e.g., 16-bit data bus, DACK and
DREQ pulled up instead of down, analog overcurrent mode).

These settings are passed in via the OF device tree.  The PCI
interface still assumes the same default values.

Signed-off-by: Nate Case <ncase@xes-inc.com>
Acked-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/isp1760-hcd.c
drivers/usb/host/isp1760-hcd.h
drivers/usb/host/isp1760-if.c

index d318af39c27f4cf773887708a13f2e18799d1009..c858f2adb929904631746151469696c73b22cd22 100644 (file)
@@ -38,6 +38,7 @@ struct isp1760_hcd {
        unsigned                i_thresh;
        unsigned long           reset_done;
        unsigned long           next_statechange;
+       unsigned int            devflags;
 };
 
 static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd)
@@ -378,9 +379,31 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
 {
        struct isp1760_hcd *priv = hcd_to_priv(hcd);
        int result;
-       u32 scratch;
+       u32 scratch, hwmode;
+
+       /* Setup HW Mode Control: This assumes a level active-low interrupt */
+       hwmode = HW_DATA_BUS_32BIT;
+
+       if (priv->devflags & ISP1760_FLAG_BUS_WIDTH_16)
+               hwmode &= ~HW_DATA_BUS_32BIT;
+       if (priv->devflags & ISP1760_FLAG_ANALOG_OC)
+               hwmode |= HW_ANA_DIGI_OC;
+       if (priv->devflags & ISP1760_FLAG_DACK_POL_HIGH)
+               hwmode |= HW_DACK_POL_HIGH;
+       if (priv->devflags & ISP1760_FLAG_DREQ_POL_HIGH)
+               hwmode |= HW_DREQ_POL_HIGH;
+
+       /*
+        * We have to set this first in case we're in 16-bit mode.
+        * Write it twice to ensure correct upper bits if switching
+        * to 16-bit mode.
+        */
+       isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL);
+       isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL);
 
        isp1760_writel(0xdeadbabe, hcd->regs + HC_SCRATCH_REG);
+       /* Change bus pattern */
+       scratch = isp1760_readl(hcd->regs + HC_CHIP_ID_REG);
        scratch = isp1760_readl(hcd->regs + HC_SCRATCH_REG);
        if (scratch != 0xdeadbabe) {
                printk(KERN_ERR "ISP1760: Scratch test failed.\n");
@@ -403,17 +426,29 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
 
        /* Step 11 passed */
 
-       isp1760_writel(INTERRUPT_ENABLE_MASK, hcd->regs + HC_INTERRUPT_REG);
-       isp1760_writel(INTERRUPT_ENABLE_MASK, hcd->regs + HC_INTERRUPT_ENABLE);
+       isp1760_info(priv, "bus width: %d, oc: %s\n",
+                          (priv->devflags & ISP1760_FLAG_BUS_WIDTH_16) ?
+                          16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ?
+                          "analog" : "digital");
 
        /* ATL reset */
-       scratch = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL);
-       isp1760_writel(scratch | ALL_ATX_RESET, hcd->regs + HC_HW_MODE_CTRL);
+       isp1760_writel(hwmode | ALL_ATX_RESET, hcd->regs + HC_HW_MODE_CTRL);
        mdelay(10);
-       isp1760_writel(scratch, hcd->regs + HC_HW_MODE_CTRL);
+       isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL);
 
-       isp1760_writel(PORT1_POWER | PORT1_INIT2, hcd->regs + HC_PORT1_CTRL);
-       mdelay(10);
+       isp1760_writel(INTERRUPT_ENABLE_MASK, hcd->regs + HC_INTERRUPT_REG);
+       isp1760_writel(INTERRUPT_ENABLE_MASK, hcd->regs + HC_INTERRUPT_ENABLE);
+
+       /*
+        * PORT 1 Control register of the ISP1760 is the OTG control
+        * register on ISP1761.
+        */
+       if (!(priv->devflags & ISP1760_FLAG_ISP1761) &&
+           !(priv->devflags & ISP1760_FLAG_PORT1_DIS)) {
+               isp1760_writel(PORT1_POWER | PORT1_INIT2,
+                              hcd->regs + HC_PORT1_CTRL);
+               mdelay(10);
+       }
 
        priv->hcs_params = isp1760_readl(hcd->regs + HC_HCSPARAMS);
 
@@ -453,8 +488,7 @@ static int isp1760_run(struct usb_hcd *hcd)
        hcd->state = HC_STATE_RUNNING;
        isp1760_enable_interrupts(hcd);
        temp = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL);
-       temp |= FINAL_HW_CONFIG;
-       isp1760_writel(temp, hcd->regs + HC_HW_MODE_CTRL);
+       isp1760_writel(temp | HW_GLOBAL_INTR_EN, hcd->regs + HC_HW_MODE_CTRL);
 
        command = isp1760_readl(hcd->regs + HC_USBCMD);
        command &= ~(CMD_LRESET|CMD_RESET);
@@ -2112,6 +2146,7 @@ static int isp1760_get_frame(struct usb_hcd *hcd)
 static void isp1760_stop(struct usb_hcd *hcd)
 {
        struct isp1760_hcd *priv = hcd_to_priv(hcd);
+       u32 temp;
 
        isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1,
                        NULL, 0);
@@ -2120,7 +2155,8 @@ static void isp1760_stop(struct usb_hcd *hcd)
        spin_lock_irq(&priv->lock);
        ehci_reset(priv);
        /* Disable IRQ */
-       isp1760_writel(HW_DATA_BUS_32BIT, hcd->regs + HC_HW_MODE_CTRL);
+       temp = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL);
+       isp1760_writel(temp &= ~HW_GLOBAL_INTR_EN, hcd->regs + HC_HW_MODE_CTRL);
        spin_unlock_irq(&priv->lock);
 
        isp1760_writel(0, hcd->regs + HC_CONFIGFLAG);
@@ -2128,10 +2164,11 @@ static void isp1760_stop(struct usb_hcd *hcd)
 
 static void isp1760_shutdown(struct usb_hcd *hcd)
 {
-       u32 command;
+       u32 command, temp;
 
        isp1760_stop(hcd);
-       isp1760_writel(HW_DATA_BUS_32BIT, hcd->regs + HC_HW_MODE_CTRL);
+       temp = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL);
+       isp1760_writel(temp &= ~HW_GLOBAL_INTR_EN, hcd->regs + HC_HW_MODE_CTRL);
 
        command = isp1760_readl(hcd->regs + HC_USBCMD);
        command &= ~CMD_RUN;
@@ -2183,7 +2220,8 @@ void deinit_kmem_cache(void)
 }
 
 struct usb_hcd *isp1760_register(u64 res_start, u64 res_len, int irq,
-               u64 irqflags, struct device *dev, const char *busname)
+               u64 irqflags, struct device *dev, const char *busname,
+               unsigned int devflags)
 {
        struct usb_hcd *hcd;
        struct isp1760_hcd *priv;
@@ -2200,6 +2238,7 @@ struct usb_hcd *isp1760_register(u64 res_start, u64 res_len, int irq,
                return ERR_PTR(-ENOMEM);
 
        priv = hcd_to_priv(hcd);
+       priv->devflags = devflags;
        init_memory(priv);
        hcd->regs = ioremap(res_start, res_len);
        if (!hcd->regs) {
index 3d86d0f6b1471346f33bb5c846c121f1f802a8f1..6473dd86993cb8d19b288dbdcae926e723c5f9c9 100644 (file)
@@ -3,7 +3,8 @@
 
 /* exports for if */
 struct usb_hcd *isp1760_register(u64 res_start, u64 res_len, int irq,
-               u64 irqflags, struct device *dev, const char *busname);
+               u64 irqflags, struct device *dev, const char *busname,
+               unsigned int devflags);
 int init_kmem_once(void);
 void deinit_kmem_cache(void);
 
@@ -31,6 +32,7 @@ void deinit_kmem_cache(void);
 /* Configuration Register */
 #define HC_HW_MODE_CTRL                0x300
 #define ALL_ATX_RESET          (1 << 31)
+#define HW_ANA_DIGI_OC         (1 << 15)
 #define HW_DATA_BUS_32BIT      (1 << 8)
 #define HW_DACK_POL_HIGH       (1 << 6)
 #define HW_DREQ_POL_HIGH       (1 << 5)
@@ -56,13 +58,14 @@ void deinit_kmem_cache(void);
 #define PORT1_POWER            (3 << 3)
 #define PORT1_INIT1            (1 << 7)
 #define PORT1_INIT2            (1 << 23)
+#define HW_OTG_CTRL_SET                0x374
+#define HW_OTG_CTRL_CLR                0x376
 
 /* Interrupt Register */
 #define HC_INTERRUPT_REG       0x310
 
 #define HC_INTERRUPT_ENABLE    0x314
 #define INTERRUPT_ENABLE_MASK  (HC_INTL_INT | HC_ATL_INT | HC_EOT_INT)
-#define FINAL_HW_CONFIG        (HW_GLOBAL_INTR_EN | HW_DATA_BUS_32BIT)
 
 #define HC_ISO_INT             (1 << 9)
 #define HC_ATL_INT             (1 << 8)
@@ -122,6 +125,19 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
 #define isp1760_err(priv, fmt, args...) \
        dev_err(priv_to_hcd(priv)->self.controller, fmt, ##args)
 
+/*
+ * Device flags that can vary from board to board.  All of these
+ * indicate the most "atypical" case, so that a devflags of 0 is
+ * a sane default configuration.
+ */
+#define ISP1760_FLAG_PORT1_DIS         0x00000001 /* Port 1 disabled */
+#define ISP1760_FLAG_BUS_WIDTH_16      0x00000002 /* 16-bit data bus width */
+#define ISP1760_FLAG_OTG_EN            0x00000004 /* Port 1 supports OTG */
+#define ISP1760_FLAG_ANALOG_OC         0x00000008 /* Analog overcurrent */
+#define ISP1760_FLAG_DACK_POL_HIGH     0x00000010 /* DACK active high */
+#define ISP1760_FLAG_DREQ_POL_HIGH     0x00000020 /* DREQ active high */
+#define ISP1760_FLAG_ISP1761           0x00000040 /* Chip is ISP1761 */
+
 /* chip memory management */
 struct memory_chunk {
        unsigned int start;
index ad833661ff34f14ff38025c9b87ef7b419bc389c..051ef7b6bdc640f99e0e17f2079b3929e73cab6f 100644 (file)
@@ -35,6 +35,8 @@ static int of_isp1760_probe(struct of_device *dev,
        int virq;
        u64 res_len;
        int ret;
+       const unsigned int *prop;
+       unsigned int devflags = 0;
 
        ret = of_address_to_resource(dp, 0, &memory);
        if (ret)
@@ -55,8 +57,32 @@ static int of_isp1760_probe(struct of_device *dev,
        virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
                        oirq.size);
 
+       if (of_device_is_compatible(dp, "nxp,usb-isp1761"))
+               devflags |= ISP1760_FLAG_ISP1761;
+
+       if (of_get_property(dp, "port1-disable", NULL) != NULL)
+               devflags |= ISP1760_FLAG_PORT1_DIS;
+
+       /* Some systems wire up only 16 of the 32 data lines */
+       prop = of_get_property(dp, "bus-width", NULL);
+       if (prop && *prop == 16)
+               devflags |= ISP1760_FLAG_BUS_WIDTH_16;
+
+       if (of_get_property(dp, "port1-otg", NULL) != NULL)
+               devflags |= ISP1760_FLAG_OTG_EN;
+
+       if (of_get_property(dp, "analog-oc", NULL) != NULL)
+               devflags |= ISP1760_FLAG_ANALOG_OC;
+
+       if (of_get_property(dp, "dack-polarity", NULL) != NULL)
+               devflags |= ISP1760_FLAG_DACK_POL_HIGH;
+
+       if (of_get_property(dp, "dreq-polarity", NULL) != NULL)
+               devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
+
        hcd = isp1760_register(memory.start, res_len, virq,
-               IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev));
+               IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev),
+               devflags);
        if (IS_ERR(hcd)) {
                ret = PTR_ERR(hcd);
                goto release_reg;
@@ -87,6 +113,9 @@ static struct of_device_id of_isp1760_match[] = {
        {
                .compatible = "nxp,usb-isp1760",
        },
+       {
+               .compatible = "nxp,usb-isp1761",
+       },
        { },
 };
 MODULE_DEVICE_TABLE(of, of_isp1760_match);
@@ -116,6 +145,7 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev,
        int length;
        int status = 1;
        struct usb_hcd *hcd;
+       unsigned int devflags = 0;
 
        if (usb_disabled())
                return -ENODEV;
@@ -200,7 +230,8 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev,
 
        dev->dev.dma_mask = NULL;
        hcd = isp1760_register(pci_mem_phy0, length, dev->irq,
-               IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev));
+               IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev),
+               devflags);
        pci_set_drvdata(dev, hcd);
        if (!hcd)
                return 0;