|
@@ -40,6 +40,7 @@
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/list.h>
|
|
|
#include <linux/ioctl.h>
|
|
|
+#include <linux/pci_ids.h>
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/kref.h>
|
|
@@ -51,6 +52,10 @@ MODULE_AUTHOR("Tony Olech");
|
|
|
MODULE_DESCRIPTION("FTDI ELAN driver");
|
|
|
MODULE_LICENSE("GPL");
|
|
|
#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444)
|
|
|
+static int distrust_firmware = 1;
|
|
|
+module_param(distrust_firmware, bool, 0);
|
|
|
+MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurren"
|
|
|
+ "t setup");
|
|
|
extern struct platform_driver u132_platform_driver;
|
|
|
static struct workqueue_struct *status_queue;
|
|
|
static struct workqueue_struct *command_queue;
|
|
@@ -66,7 +71,9 @@ static struct list_head ftdi_static_list;
|
|
|
* end of the global variables protected by ftdi_module_lock
|
|
|
*/
|
|
|
#include "usb_u132.h"
|
|
|
-#define TD_DEVNOTRESP 5
|
|
|
+#include <asm/io.h>
|
|
|
+#include "../core/hcd.h"
|
|
|
+#include "../host/ohci.h"
|
|
|
/* Define these values to match your devices*/
|
|
|
#define USB_FTDI_ELAN_VENDOR_ID 0x0403
|
|
|
#define USB_FTDI_ELAN_PRODUCT_ID 0xd6ea
|
|
@@ -551,7 +558,7 @@ static void ftdi_elan_status_work(struct work_struct *work)
|
|
|
} else {
|
|
|
dev_err(&ftdi->udev->dev, "initialized failed - trying "
|
|
|
"again in 10 seconds\n");
|
|
|
- work_delay_in_msec = 10 *1000;
|
|
|
+ work_delay_in_msec = 1 *1000;
|
|
|
}
|
|
|
} else if (ftdi->registered == 0) {
|
|
|
work_delay_in_msec = 10;
|
|
@@ -2288,82 +2295,288 @@ static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi)
|
|
|
+
|
|
|
+#define ftdi_read_pcimem(ftdi, member, data) ftdi_elan_read_pcimem(ftdi, \
|
|
|
+ offsetof(struct ohci_regs, member), 0, data);
|
|
|
+#define ftdi_write_pcimem(ftdi, member, data) ftdi_elan_write_pcimem(ftdi, \
|
|
|
+ offsetof(struct ohci_regs, member), 0, data);
|
|
|
+#define OHCI_QUIRK_AMD756 0x01
|
|
|
+#define OHCI_QUIRK_SUPERIO 0x02
|
|
|
+#define OHCI_QUIRK_INITRESET 0x04
|
|
|
+#define OHCI_BIG_ENDIAN 0x08
|
|
|
+#define OHCI_QUIRK_ZFMICRO 0x10
|
|
|
+#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
|
|
|
+#define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \
|
|
|
+ OHCI_INTR_WDH)
|
|
|
+static int ftdi_elan_check_controller(struct usb_ftdi *ftdi, int quirk)
|
|
|
+{
|
|
|
+ int devices = 0;
|
|
|
+ int retval;
|
|
|
+ u32 hc_control;
|
|
|
+ int num_ports;
|
|
|
+ u32 control;
|
|
|
+ u32 rh_a = -1;
|
|
|
+ u32 status;
|
|
|
+ u32 fminterval;
|
|
|
+ u32 hc_fminterval;
|
|
|
+ u32 periodicstart;
|
|
|
+ u32 cmdstatus;
|
|
|
+ u32 roothub_a;
|
|
|
+ int mask = OHCI_INTR_INIT;
|
|
|
+ int sleep_time = 0;
|
|
|
+ int reset_timeout = 30; /* ... allow extra time */
|
|
|
+ int temp;
|
|
|
+ retval = ftdi_write_pcimem(ftdi, intrdisable, OHCI_INTR_MIE);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_read_pcimem(ftdi, control, &control);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_read_pcimem(ftdi, roothub.a, &rh_a);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ num_ports = rh_a & RH_A_NDP;
|
|
|
+ retval = ftdi_read_pcimem(ftdi, fminterval, &hc_fminterval);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ hc_fminterval &= 0x3fff;
|
|
|
+ if (hc_fminterval != FI) {
|
|
|
+ }
|
|
|
+ hc_fminterval |= FSMP(hc_fminterval) << 16;
|
|
|
+ retval = ftdi_read_pcimem(ftdi, control, &hc_control);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ switch (hc_control & OHCI_CTRL_HCFS) {
|
|
|
+ case OHCI_USB_OPER:
|
|
|
+ sleep_time = 0;
|
|
|
+ break;
|
|
|
+ case OHCI_USB_SUSPEND:
|
|
|
+ case OHCI_USB_RESUME:
|
|
|
+ hc_control &= OHCI_CTRL_RWC;
|
|
|
+ hc_control |= OHCI_USB_RESUME;
|
|
|
+ sleep_time = 10;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ hc_control &= OHCI_CTRL_RWC;
|
|
|
+ hc_control |= OHCI_USB_RESET;
|
|
|
+ sleep_time = 50;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ retval = ftdi_write_pcimem(ftdi, control, hc_control);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_read_pcimem(ftdi, control, &control);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ msleep(sleep_time);
|
|
|
+ retval = ftdi_read_pcimem(ftdi, roothub.a, &roothub_a);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ if (!(roothub_a & RH_A_NPS)) { /* power down each port */
|
|
|
+ for (temp = 0; temp < num_ports; temp++) {
|
|
|
+ retval = ftdi_write_pcimem(ftdi,
|
|
|
+ roothub.portstatus[temp], RH_PS_LSDA);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ retval = ftdi_read_pcimem(ftdi, control, &control);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retry:retval = ftdi_read_pcimem(ftdi, cmdstatus, &status);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_write_pcimem(ftdi, cmdstatus, OHCI_HCR);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ extra:{
|
|
|
+ retval = ftdi_read_pcimem(ftdi, cmdstatus, &status);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ if (0 != (status & OHCI_HCR)) {
|
|
|
+ if (--reset_timeout == 0) {
|
|
|
+ dev_err(&ftdi->udev->dev, "USB HC reset timed o"
|
|
|
+ "ut!\n");
|
|
|
+ return -ENODEV;
|
|
|
+ } else {
|
|
|
+ msleep(5);
|
|
|
+ goto extra;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (quirk & OHCI_QUIRK_INITRESET) {
|
|
|
+ retval = ftdi_write_pcimem(ftdi, control, hc_control);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_read_pcimem(ftdi, control, &control);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+ retval = ftdi_write_pcimem(ftdi, ed_controlhead, 0x00000000);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_write_pcimem(ftdi, ed_bulkhead, 0x11000000);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_write_pcimem(ftdi, hcca, 0x00000000);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_read_pcimem(ftdi, fminterval, &fminterval);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_write_pcimem(ftdi, fminterval,
|
|
|
+ ((fminterval & FIT) ^ FIT) | hc_fminterval);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_write_pcimem(ftdi, periodicstart,
|
|
|
+ ((9 *hc_fminterval) / 10) & 0x3fff);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_read_pcimem(ftdi, fminterval, &fminterval);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_read_pcimem(ftdi, periodicstart, &periodicstart);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ if (0 == (fminterval & 0x3fff0000) || 0 == periodicstart) {
|
|
|
+ if (!(quirk & OHCI_QUIRK_INITRESET)) {
|
|
|
+ quirk |= OHCI_QUIRK_INITRESET;
|
|
|
+ goto retry;
|
|
|
+ } else
|
|
|
+ dev_err(&ftdi->udev->dev, "init err(%08x %04x)\n",
|
|
|
+ fminterval, periodicstart);
|
|
|
+ } /* start controller operations */
|
|
|
+ hc_control &= OHCI_CTRL_RWC;
|
|
|
+ hc_control |= OHCI_CONTROL_INIT | OHCI_CTRL_BLE | OHCI_USB_OPER;
|
|
|
+ retval = ftdi_write_pcimem(ftdi, control, hc_control);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_write_pcimem(ftdi, cmdstatus, OHCI_BLF);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_read_pcimem(ftdi, cmdstatus, &cmdstatus);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_read_pcimem(ftdi, control, &control);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_write_pcimem(ftdi, roothub.status, RH_HS_DRWE);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_write_pcimem(ftdi, intrstatus, mask);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_write_pcimem(ftdi, intrdisable,
|
|
|
+ OHCI_INTR_MIE | OHCI_INTR_OC | OHCI_INTR_RHSC | OHCI_INTR_FNO |
|
|
|
+ OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_SF | OHCI_INTR_WDH |
|
|
|
+ OHCI_INTR_SO);
|
|
|
+ if (retval)
|
|
|
+ return retval; /* handle root hub init quirks ... */
|
|
|
+ retval = ftdi_read_pcimem(ftdi, roothub.a, &roothub_a);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ roothub_a &= ~(RH_A_PSM | RH_A_OCPM);
|
|
|
+ if (quirk & OHCI_QUIRK_SUPERIO) {
|
|
|
+ roothub_a |= RH_A_NOCP;
|
|
|
+ roothub_a &= ~(RH_A_POTPGT | RH_A_NPS);
|
|
|
+ retval = ftdi_write_pcimem(ftdi, roothub.a, roothub_a);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ } else if ((quirk & OHCI_QUIRK_AMD756) || distrust_firmware) {
|
|
|
+ roothub_a |= RH_A_NPS;
|
|
|
+ retval = ftdi_write_pcimem(ftdi, roothub.a, roothub_a);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+ retval = ftdi_write_pcimem(ftdi, roothub.status, RH_HS_LPSC);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_write_pcimem(ftdi, roothub.b,
|
|
|
+ (roothub_a & RH_A_NPS) ? 0 : RH_B_PPCM);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ retval = ftdi_read_pcimem(ftdi, control, &control);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ mdelay((roothub_a >> 23) & 0x1fe);
|
|
|
+ for (temp = 0; temp < num_ports; temp++) {
|
|
|
+ u32 portstatus;
|
|
|
+ retval = ftdi_read_pcimem(ftdi, roothub.portstatus[temp],
|
|
|
+ &portstatus);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ if (1 & portstatus)
|
|
|
+ devices += 1;
|
|
|
+ }
|
|
|
+ return devices;
|
|
|
+}
|
|
|
+
|
|
|
+static int ftdi_elan_setup_controller(struct usb_ftdi *ftdi, int fn)
|
|
|
{
|
|
|
u32 latence_timer;
|
|
|
- u32 controlreg;
|
|
|
int UxxxStatus;
|
|
|
u32 pcidata;
|
|
|
int reg = 0;
|
|
|
- int foundOHCI = 0;
|
|
|
- u8 fn;
|
|
|
- int activePCIfn = 0;
|
|
|
- u32 pciVID = 0;
|
|
|
- u32 pciPID = 0;
|
|
|
- UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
|
|
|
- if (UxxxStatus)
|
|
|
- return UxxxStatus;
|
|
|
- UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000000L);
|
|
|
- if (UxxxStatus)
|
|
|
- return UxxxStatus;
|
|
|
- msleep(750);
|
|
|
- UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000200L | 0x100);
|
|
|
+ int activePCIfn = fn << 8;
|
|
|
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x2800);
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
|
- UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000200L | 0x500);
|
|
|
+ reg = 16;
|
|
|
+ UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0,
|
|
|
+ 0xFFFFFFFF);
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
|
- UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
|
|
|
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
|
|
|
+ &pcidata);
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
|
- UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020CL | 0x000);
|
|
|
+ UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0,
|
|
|
+ 0xF0000000);
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
|
- UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020DL | 0x000);
|
|
|
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
|
|
|
+ &pcidata);
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
|
- msleep(250);
|
|
|
- UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020FL | 0x000);
|
|
|
+ reg = 12;
|
|
|
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
|
|
|
+ &latence_timer);
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
|
- UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
|
|
|
+ latence_timer &= 0xFFFF00FF;
|
|
|
+ latence_timer |= 0x00001600;
|
|
|
+ UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00,
|
|
|
+ latence_timer);
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
|
- UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x800);
|
|
|
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
|
|
|
+ &pcidata);
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
|
- UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
|
|
|
+ reg = 4;
|
|
|
+ UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00,
|
|
|
+ 0x06);
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
|
- UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
|
|
|
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
|
|
|
+ &pcidata);
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
|
- msleep(1000);
|
|
|
- for (fn = 0; (fn < 4) && (!foundOHCI); fn++) {
|
|
|
- activePCIfn = fn << 8;
|
|
|
- ftdi->function = fn + 1;
|
|
|
- UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
|
|
|
- &pcidata);
|
|
|
+ for (reg = 0; reg <= 0x54; reg += 4) {
|
|
|
+ UxxxStatus = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
|
- pciVID = pcidata & 0xFFFF;
|
|
|
- pciPID = (pcidata >> 16) & 0xFFFF;
|
|
|
- if ((pciVID == 0x1045) && (pciPID == 0xc861)) {
|
|
|
- foundOHCI = 1;
|
|
|
- } else if ((pciVID == 0x1033) && (pciPID == 0x0035)) {
|
|
|
- foundOHCI = 1;
|
|
|
- } else if ((pciVID == 0x10b9) && (pciPID == 0x5237)) {
|
|
|
- foundOHCI = 1;
|
|
|
- } else if ((pciVID == 0x11c1) && (pciPID == 0x5802)) {
|
|
|
- foundOHCI = 1;
|
|
|
- } else if ((pciVID == 0x11AB) && (pciPID == 0x1FA6)) {
|
|
|
- }
|
|
|
- }
|
|
|
- if (foundOHCI == 0) {
|
|
|
- return -ENXIO;
|
|
|
}
|
|
|
- ftdi->platform_data.vendor = pciVID;
|
|
|
- ftdi->platform_data.device = pciPID;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int ftdi_elan_close_controller(struct usb_ftdi *ftdi, int fn)
|
|
|
+{
|
|
|
+ u32 latence_timer;
|
|
|
+ int UxxxStatus;
|
|
|
+ u32 pcidata;
|
|
|
+ int reg = 0;
|
|
|
+ int activePCIfn = fn << 8;
|
|
|
UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x2800);
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
@@ -2377,7 +2590,7 @@ static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi)
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
|
UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0,
|
|
|
- 0xF0000000);
|
|
|
+ 0x00000000);
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
|
UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
|
|
@@ -2401,7 +2614,7 @@ static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi)
|
|
|
return UxxxStatus;
|
|
|
reg = 4;
|
|
|
UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00,
|
|
|
- 0x06);
|
|
|
+ 0x00);
|
|
|
if (UxxxStatus)
|
|
|
return UxxxStatus;
|
|
|
UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
|
|
@@ -2411,159 +2624,139 @@ static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int ftdi_elan_found_controller(struct usb_ftdi *ftdi, int fn, int quirk)
|
|
|
+{
|
|
|
+ int result;
|
|
|
+ int UxxxStatus;
|
|
|
+ UxxxStatus = ftdi_elan_setup_controller(ftdi, fn);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ result = ftdi_elan_check_controller(ftdi, quirk);
|
|
|
+ UxxxStatus = ftdi_elan_close_controller(ftdi, fn);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi)
|
|
|
+{
|
|
|
+ u32 controlreg;
|
|
|
+ u8 sensebits;
|
|
|
+ int UxxxStatus;
|
|
|
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000000L);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ msleep(750);
|
|
|
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000200L | 0x100);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000200L | 0x500);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020CL | 0x000);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020DL | 0x000);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ msleep(250);
|
|
|
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020FL | 0x000);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x800);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ msleep(1000);
|
|
|
+ sensebits = (controlreg >> 16) & 0x000F;
|
|
|
+ if (0x0D == sensebits)
|
|
|
+ return 0;
|
|
|
+ else
|
|
|
+ return - ENXIO;
|
|
|
+}
|
|
|
+
|
|
|
static int ftdi_elan_setupOHCI(struct usb_ftdi *ftdi)
|
|
|
{
|
|
|
+ int UxxxStatus;
|
|
|
u32 pcidata;
|
|
|
- int U132Status;
|
|
|
- int reg;
|
|
|
- int reset_repeat = 0;
|
|
|
- do_reset:reg = 8;
|
|
|
- U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x0e, 0x01);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reset_check:{
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- if (pcidata & 1) {
|
|
|
- msleep(500);
|
|
|
- if (reset_repeat++ > 100) {
|
|
|
- reset_repeat = 0;
|
|
|
- goto do_reset;
|
|
|
- } else
|
|
|
- goto reset_check;
|
|
|
+ int reg = 0;
|
|
|
+ u8 fn;
|
|
|
+ int activePCIfn = 0;
|
|
|
+ int max_devices = 0;
|
|
|
+ int controllers = 0;
|
|
|
+ int unrecognized = 0;
|
|
|
+ ftdi->function = 0;
|
|
|
+ for (fn = 0; (fn < 4); fn++) {
|
|
|
+ u32 pciVID = 0;
|
|
|
+ u32 pciPID = 0;
|
|
|
+ int devices = 0;
|
|
|
+ activePCIfn = fn << 8;
|
|
|
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
|
|
|
+ &pcidata);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ pciVID = pcidata & 0xFFFF;
|
|
|
+ pciPID = (pcidata >> 16) & 0xFFFF;
|
|
|
+ if ((pciVID == PCI_VENDOR_ID_OPTI) && (pciPID == 0xc861)) {
|
|
|
+ devices = ftdi_elan_found_controller(ftdi, fn, 0);
|
|
|
+ controllers += 1;
|
|
|
+ } else if ((pciVID == PCI_VENDOR_ID_NEC) && (pciPID == 0x0035))
|
|
|
+ {
|
|
|
+ devices = ftdi_elan_found_controller(ftdi, fn, 0);
|
|
|
+ controllers += 1;
|
|
|
+ } else if ((pciVID == PCI_VENDOR_ID_AL) && (pciPID == 0x5237)) {
|
|
|
+ devices = ftdi_elan_found_controller(ftdi, fn, 0);
|
|
|
+ controllers += 1;
|
|
|
+ } else if ((pciVID == PCI_VENDOR_ID_ATT) && (pciPID == 0x5802))
|
|
|
+ {
|
|
|
+ devices = ftdi_elan_found_controller(ftdi, fn, 0);
|
|
|
+ controllers += 1;
|
|
|
+ } else if (pciVID == PCI_VENDOR_ID_AMD && pciPID == 0x740c) {
|
|
|
+ devices = ftdi_elan_found_controller(ftdi, fn,
|
|
|
+ OHCI_QUIRK_AMD756);
|
|
|
+ controllers += 1;
|
|
|
+ } else if (pciVID == PCI_VENDOR_ID_COMPAQ && pciPID == 0xa0f8) {
|
|
|
+ devices = ftdi_elan_found_controller(ftdi, fn,
|
|
|
+ OHCI_QUIRK_ZFMICRO);
|
|
|
+ controllers += 1;
|
|
|
+ } else if (0 == pcidata) {
|
|
|
+ } else
|
|
|
+ unrecognized += 1;
|
|
|
+ if (devices > max_devices) {
|
|
|
+ max_devices = devices;
|
|
|
+ ftdi->function = fn + 1;
|
|
|
+ ftdi->platform_data.vendor = pciVID;
|
|
|
+ ftdi->platform_data.device = pciPID;
|
|
|
}
|
|
|
}
|
|
|
- goto dump_regs;
|
|
|
- msleep(500);
|
|
|
- reg = 0x28;
|
|
|
- U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x11000000);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reg = 0x40;
|
|
|
- U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x2edf);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reg = 0x34;
|
|
|
- U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x2edf2edf);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reg = 4;
|
|
|
- U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0xA0);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- msleep(250);
|
|
|
- reg = 8;
|
|
|
- U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x0e, 0x04);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reg = 0x28;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reg = 8;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reg = 0x48;
|
|
|
- U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x00001200);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reg = 0x54;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reg = 0x58;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reg = 0x34;
|
|
|
- U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x28002edf);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- msleep(100);
|
|
|
- reg = 0x50;
|
|
|
- U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x10000);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reg = 0x54;
|
|
|
- power_check:U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- if (!(pcidata & 1)) {
|
|
|
- msleep(500);
|
|
|
- goto power_check;
|
|
|
- }
|
|
|
- msleep(3000);
|
|
|
- reg = 0x54;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reg = 0x58;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reg = 0x54;
|
|
|
- U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x02);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reg = 0x54;
|
|
|
- U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x10);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- msleep(750);
|
|
|
- reg = 0x54;
|
|
|
- if (0) {
|
|
|
- U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x02);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- }
|
|
|
- if (0) {
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- }
|
|
|
- reg = 0x54;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- reg = 0x58;
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
- dump_regs:for (reg = 0; reg <= 0x54; reg += 4) {
|
|
|
- U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
|
|
|
- if (U132Status)
|
|
|
- return U132Status;
|
|
|
+ if (ftdi->function > 0) {
|
|
|
+ UxxxStatus = ftdi_elan_setup_controller(ftdi,
|
|
|
+ ftdi->function - 1);
|
|
|
+ if (UxxxStatus)
|
|
|
+ return UxxxStatus;
|
|
|
+ return 0;
|
|
|
+ } else if (controllers > 0) {
|
|
|
+ return -ENXIO;
|
|
|
+ } else if (unrecognized > 0) {
|
|
|
+ return -ENXIO;
|
|
|
+ } else {
|
|
|
+ ftdi->enumerated = 0;
|
|
|
+ return -ENXIO;
|
|
|
}
|
|
|
- return 0;
|
|
|
}
|
|
|
|
|
|
|
|
@@ -2688,6 +2881,7 @@ static void ftdi_elan_disconnect(struct usb_interface *interface)
|
|
|
platform_device_unregister(&ftdi->platform_dev);
|
|
|
ftdi->synchronized = 0;
|
|
|
ftdi->enumerated = 0;
|
|
|
+ ftdi->initialized = 0;
|
|
|
ftdi->registered = 0;
|
|
|
}
|
|
|
flush_workqueue(status_queue);
|