some routines for removing a device on demand. thist do not work properly
[ppcskel.git] / usb / host / ohci.c
index a7ef894147e48028dc06bbff225dc0e1b0270bba..64b77deda2634fe669f85bd3fef6713b8f372a7a 100644 (file)
@@ -34,11 +34,12 @@ Copyright (C) 2009     Sebastian Falbesoner <sebastian.falbesoner@gmail.com>
 static struct general_td *allocate_general_td();
 static void dbg_op_state(u32 reg);
 static void configure_ports(u8 from_init, u32 reg);
-static void setup_port(u32 ohci, u32 reg, u8 from_init);
+static struct usb_device *setup_port(u32 ohci, u32 reg, u8 pport, u8 from_init);
+static void set_target_hcca(u32 reg);
 
 static struct ohci_hcca hcca_oh0;
 static struct ohci_hcca hcca_oh1;
-
+struct ohci_hcca *hcca;
 
 static struct general_td *allocate_general_td()
 {
@@ -154,12 +155,33 @@ void hcdi_fire(u32 reg)
        if(edhead == 0)
                return;
 
+       u8 itmp;
+       switch(edhead->type) {
+               case USB_CTRL:
 #ifdef _USE_C_Q
-       /* quirk... 11ms seems to be a minimum :O */
-       udelay(11000);
-#endif
+                       /* quirk... 11ms seems to be a minimum :O */
+                       udelay(11000);
+#endif
+                       write32(reg+OHCI_HC_CTRL_HEAD_ED, virt_to_phys(edhead));
+               break;
+
+               case USB_INTR:
+                       //udelay(11000);
+                       set_target_hcca(reg);
+                       sync_before_read(hcca, sizeof(struct ohci_hcca));
+                       for(itmp = 0; itmp < NUM_INITS; itmp++) {
+                               hcca->int_table[itmp] = LE(virt_to_phys(edhead));
+                       }
+                       sync_after_write(hcca, sizeof(struct ohci_hcca));
+               break;
 
-       write32(reg+OHCI_HC_CTRL_HEAD_ED, virt_to_phys(edhead));
+               case USB_BULK:
+                       write32(reg+OHCI_HC_BULK_HEAD_ED, virt_to_phys(edhead));
+               break;
+
+               case USB_ISOC:
+               break;
+       }
 
        /* sync it all */
        sync_after_write(edhead, sizeof(struct endpoint_descriptor));
@@ -183,18 +205,36 @@ void hcdi_fire(u32 reg)
                x = phys_to_virt(LE(x->nexttd));
        }
 
-       /* trigger control list */
-       set32(reg+OHCI_HC_CONTROL, OHCI_CTRL_CLE);
-       write32(reg+OHCI_HC_COMMAND_STATUS, OHCI_CLF);
+       /* start transfer */
+       switch(edhead->type) {
+               case USB_CTRL:
+                       /* trigger control list */
+                       set32(reg+OHCI_HC_CONTROL, OHCI_CTRL_CLE);
+                       write32(reg+OHCI_HC_COMMAND_STATUS, OHCI_CLF);
+                       break;
+
+               case USB_INTR:
+                       /* trigger periodic list */
+                       set32(reg+OHCI_HC_CONTROL, OHCI_CTRL_PLE);
+                       break;
+
+               case USB_BULK:
+                       /* trigger bulk list */
+                       set32(reg+OHCI_HC_CONTROL, OHCI_CTRL_BLE);
+                       write32(reg+OHCI_HC_COMMAND_STATUS, OHCI_BLF);
+                       break;
+
+               case USB_ISOC:
+                       break;
+       }
 
        struct general_td *n=0, *prev = 0, *next = 0;
        /* poll until edhead->headp is null */
        do {
                sync_before_read(edhead, sizeof(struct endpoint_descriptor));
-//#ifdef _DU_OHCI_F
+#ifdef _DU_OHCI_F
                printf("edhead->headp: 0x%08X\n", LE(edhead->headp));
-//#endif
-               udelay(10000);
+#endif
 
                /* if halted, debug output plz. will break the transfer */
                if((LE(edhead->headp) & OHCI_ENDPOINT_HALTED)) {
@@ -282,16 +322,33 @@ void hcdi_fire(u32 reg)
                free(prev);
        }
 
-       if(reg == OHCI0_REG_BASE) {
-               hcca_oh0.done_head = 0;
-               sync_after_write(&hcca_oh0, sizeof(hcca_oh0));
-       } else if (reg == OHCI1_REG_BASE) {
-               hcca_oh1.done_head = 0;
-               sync_after_write(&hcca_oh1, sizeof(hcca_oh1));
+out:
+       set_target_hcca(reg);
+       sync_before_read(hcca, sizeof(struct ohci_hcca));
+
+       u8 jtmp;
+       switch(edhead->type) {
+               case USB_CTRL:
+                       write32(reg+OHCI_HC_CONTROL, read32(reg+OHCI_HC_CONTROL)&~OHCI_CTRL_CLE);
+                       break;
+
+               case USB_INTR:
+                       write32(reg+OHCI_HC_CONTROL, read32(reg+OHCI_HC_CONTROL)&~OHCI_CTRL_PLE);
+                       for(jtmp = 0; jtmp < NUM_INITS; jtmp++) {
+                               hcca->int_table[jtmp] = 0;
+                       }
+                       break;
+
+               case USB_BULK:
+                       write32(reg+OHCI_HC_CONTROL, read32(reg+OHCI_HC_CONTROL)&~OHCI_CTRL_BLE);
+                       break;
+
+               case USB_ISOC:
+                       break;
        }
 
-out:
-       write32(reg+OHCI_HC_CONTROL, read32(reg+OHCI_HC_CONTROL)&~OHCI_CTRL_CLE);
+       hcca->done_head = 0;
+       sync_after_write(hcca, sizeof(struct ohci_hcca));
 
        edhead = 0;
 
@@ -321,6 +378,7 @@ u8 hcdi_enqueue(const struct usb_transfer_descriptor *td, u32 reg) {
                                OHCI_ENDPOINT_SET_ENDPOINT_NUMBER(td->endpoint) |
                                OHCI_ENDPOINT_SET_MAX_PACKET_SIZE(td->maxp));
                edhead->tdcount = 0;
+               edhead->type = td->type;
        }
 
        struct general_td *tdhw = allocate_general_td();
@@ -404,13 +462,9 @@ void hcdi_init(u32 reg)
        write32(reg+OHCI_HC_BULK_HEAD_ED, 0);
 
        /* set hcca adress */
-       if(reg == OHCI0_REG_BASE) {
-               sync_after_write(&hcca_oh0, 256);
-               write32(reg+OHCI_HC_HCCA, virt_to_phys(&hcca_oh0));
-       } else {
-               sync_after_write(&hcca_oh1, 256);
-               write32(reg+OHCI_HC_HCCA, virt_to_phys(&hcca_oh1));
-       }
+       set_target_hcca(reg);
+       sync_after_write(hcca, 256);
+       write32(reg+OHCI_HC_HCCA, virt_to_phys(hcca));
 
        /* set periodicstart */
 #define FIT (1<<31)
@@ -444,6 +498,7 @@ void hcdi_init(u32 reg)
        dbg_op_state(reg);
 }
 
+static struct usb_device *connected[2] = {NULL, NULL};
 static void configure_ports(u8 from_init, u32 reg)
 {
 #ifdef _DU_OHCI_RH
@@ -455,14 +510,31 @@ static void configure_ports(u8 from_init, u32 reg)
        printf("OHCI_HC_RH_PORT_STATUS_2:\t0x%08X\n", read32(reg+OHCI_HC_RH_PORT_STATUS_2));
 #endif
 
-       setup_port(reg, reg+OHCI_HC_RH_PORT_STATUS_1, from_init);
-       setup_port(reg, reg+OHCI_HC_RH_PORT_STATUS_2, from_init);
+       struct usb_device *dtmp;
+       if(!(dtmp = setup_port(reg, reg+OHCI_HC_RH_PORT_STATUS_1, 0, from_init))) {
+               if(connected[0]) {
+                       usb_remove_device(connected[0]);
+                       connected[0] = NULL;
+               }
+       } else {
+               connected[0] = dtmp;
+       }
+
+       if(!(dtmp = setup_port(reg, reg+OHCI_HC_RH_PORT_STATUS_2, 1, from_init))) {
+               if(connected[1]) {
+                       usb_remove_device(connected[1]);
+                       connected[1] = NULL;
+               }
+       } else {
+               connected[1] = dtmp;
+       }
+
 #ifdef _DU_OHCI_RH
        printf("configure_ports done\n");
 #endif
 }
 
-static void setup_port(u32 ohci, u32 reg, u8 from_init)
+static struct usb_device *setup_port(u32 ohci, u32 reg, u8 pport, u8 from_init)
 {
        u32 port = read32(reg);
        if((port & RH_PS_CCS) && ((port & RH_PS_CSC) || from_init)) {
@@ -476,7 +548,7 @@ static void setup_port(u32 ohci, u32 reg, u8 from_init)
 #ifdef _DU_OHCI_RH
                        printf("fu\n");
 #endif
-                       return;
+                       return NULL;
                }
 
                write32(reg, RH_PS_PRS);
@@ -488,8 +560,12 @@ static void setup_port(u32 ohci, u32 reg, u8 from_init)
 #endif
 
                /* returns usb_device struct */
-               (void) usb_add_device((read32(reg) & RH_PS_LSDA) >> 8, ohci);
+               return usb_add_device((read32(reg) & RH_PS_LSDA) >> 8, ohci);
+       }
+       if(port & RH_PS_CCS) {
+               return connected[pport];
        }
+       return NULL;
 }
 
 void hcdi_irq(u32 reg)
@@ -559,13 +635,18 @@ void hcdi_irq(u32 reg)
        }
 }
 
-void show_frame_no(u32 reg)
+/* Before you access the HCCA structure in any way, call this to set the pointer correctly! */
+static void set_target_hcca(u32 reg)
 {
-       if(reg == OHCI0_REG_BASE) {
-               sync_before_read(&hcca_oh0, 256);
-               printf("***** frame_no: %d *****\n", LE(hcca_oh0.frame_no));
-       } else if (reg == OHCI1_REG_BASE) {
-               sync_before_read(&hcca_oh1, 256);
-               printf("***** frame_no: %d *****\n", LE(hcca_oh1.frame_no));
+       switch(reg) {
+       case OHCI0_REG_BASE: hcca = &hcca_oh0; break;
+       case OHCI1_REG_BASE: hcca = &hcca_oh1; break;
        }
 }
+
+void show_frame_no(u32 reg)
+{
+       set_target_hcca(reg);
+       sync_before_read(hcca, 256);
+       printf("***** frame_no: %d *****\n", LE(hcca->frame_no));
+}