first adaption of 'usbport' by Benedikt Sauter
[ppcskel.git] / usb / host / ohci.c
1 /*
2        ppcskel - a Free Software replacement for the Nintendo/BroadOn bootloader.
3        ohci hardware support
4
5 Copyright (C) 2009     Bernhard Urban <lewurm@gmx.net>
6 Copyright (C) 2009     Sebastian Falbesoner <sebastian.falbesoner@gmail.com>
7
8 # This code is licensed to you under the terms of the GNU GPL, version 2;
9 # see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
10 */
11
12 #include "../../bootmii_ppc.h"
13 #include "../../hollywood.h"
14 #include "../../irq.h"
15 #include "../../string.h"
16 #include "ohci.h"
17 #include "host.h"
18
19 static struct ohci_hcca hcca_oh0;
20
21 static void dbg_op_state() 
22 {
23         switch (read32(OHCI0_HC_CONTROL) & OHCI_CTRL_HCFS) {
24                 case OHCI_USB_SUSPEND:
25                         printf("ohci-- OHCI_USB_SUSPEND\n");
26                         break;
27                 case OHCI_USB_RESET:
28                         printf("ohci-- OHCI_USB_RESET\n");
29                         break;
30                 case OHCI_USB_OPER:
31                         printf("ohci-- OHCI_USB_OPER\n");
32                         break;
33                 case OHCI_USB_RESUME:
34                         printf("ohci-- OHCI_USB_RESUME\n");
35                         break;
36         }
37 }
38
39
40 /**
41  * Enqueue a transfer descriptor.
42  */
43 u8 hcdi_enqueue(usb_transfer_descriptor *td) {
44         return 0;
45 }
46
47 /**
48  * Remove an transfer descriptor from transfer queue.
49  */
50 u8 hcdi_dequeue(usb_transfer_descriptor *td) {
51         return 0;
52 }
53
54 void hcdi_init() 
55 {
56         printf("ohci-- init\n");
57         dbg_op_state();
58
59         /* disable hc interrupts */
60         set32(OHCI0_HC_INT_DISABLE, OHCI_INTR_MIE);
61
62         /* save fmInterval and calculate FSMPS */
63 #define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7))
64 #define FI 0x2edf /* 12000 bits per frame (-1) */
65         u32 fmint = read32(OHCI0_HC_FM_INTERVAL) & 0x3fff;
66         if(fmint != FI)
67                 printf("ohci-- fminterval delta: %d\n", fmint - FI);
68         fmint |= FSMP (fmint) << 16;
69
70         /* enable interrupts of both usb host controllers */
71         set32(EHCI_CTL, EHCI_CTL_OH0INTE | EHCI_CTL_OH1INTE | 0xe0000);
72
73         /* reset HC */
74         write32(OHCI0_HC_COMMAND_STATUS, OHCI_HCR);
75
76         /* wait max. 30us */
77         u32 ts = 30;
78         while ((read32(OHCI0_HC_COMMAND_STATUS) & OHCI_HCR) != 0) {
79                  if(--ts == 0) {
80                         printf("ohci-- FAILED");
81                         return;
82                  }
83                  udelay(1);
84         }
85
86         /* disable interrupts; 2ms timelimit here! 
87            now we're in the SUSPEND state ... must go OPERATIONAL
88            within 2msec else HC enters RESUME */
89
90         u32 cookie = irq_kill();
91
92         /* Tell the controller where the control and bulk lists are
93          * The lists are empty now. */
94         write32(OHCI0_HC_CTRL_HEAD_ED, 0);
95         write32(OHCI0_HC_BULK_HEAD_ED, 0);
96
97         /* set hcca adress */
98         sync_after_write(&hcca_oh0, 256);
99         write32(OHCI0_HC_HCCA, virt_to_phys(&hcca_oh0));
100
101         /* set periodicstart */
102 #define FIT (1<<31)
103         u32 fmInterval = read32(OHCI0_HC_FM_INTERVAL) &0x3fff;
104         u32 fit = read32(OHCI0_HC_FM_INTERVAL) & FIT;
105
106         write32(OHCI0_HC_FM_INTERVAL, fmint | (fit ^ FIT));
107         write32(OHCI0_HC_PERIODIC_START, ((9*fmInterval)/10)&0x3fff);
108
109         /* testing bla */
110         if ((read32(OHCI0_HC_FM_INTERVAL) & 0x3fff0000) == 0 || !read32(OHCI0_HC_PERIODIC_START)) {
111                 printf("ohci-- w00t, fail!! see ohci-hcd.c:669\n");
112         }
113         
114         /* start HC operations */
115         write32(OHCI0_HC_CONTROL, OHCI_CONTROL_INIT | OHCI_USB_OPER);
116
117         /* wake on ConnectStatusChange, matching external hubs */
118         set32(OHCI0_HC_RH_STATUS, RH_HS_DRWE);
119
120         /* Choose the interrupts we care about now, others later on demand */
121         write32(OHCI0_HC_INT_STATUS, ~0);
122         write32(OHCI0_HC_INT_ENABLE, OHCI_INTR_INIT);
123
124         irq_restore(cookie);
125
126         dbg_op_state();
127 }
128
129 void hcdi_irq()
130 {
131         /* read interrupt status */
132         u32 flags = read32(OHCI0_HC_INT_STATUS);
133
134         /* when all bits are set to 1 some problem occured */
135         if (flags == 0xffffffff) {
136                 printf("ohci-- Houston, we have a serious problem! :(\n");
137                 return;
138         }
139
140         /* only care about interrupts that are enabled */
141         flags &= read32(OHCI0_HC_INT_ENABLE);
142
143         /* nothing to do? */
144         if (flags == 0)
145                 return;
146
147         printf("OHCI Interrupt occured: ");
148         /* UnrecoverableError */
149         if (flags & OHCI_INTR_UE) {
150                 printf("UnrecoverableError\n");
151                 /* TODO: well, I don't know... nothing,
152                  *       because it won't happen anyway? ;-) */
153         }
154
155         /* RootHubStatusChange */
156         if (flags & OHCI_INTR_RHSC) {
157                 printf("RootHubStatusChange\n");
158                 /* TODO: set some next_statechange variable... */
159                 write32(OHCI0_HC_INT_STATUS, OHCI_INTR_RD | OHCI_INTR_RHSC);
160         }
161         /* ResumeDetected */
162         else if (flags & OHCI_INTR_RD) {
163                 printf("ResumeDetected\n");
164                 write32(OHCI0_HC_INT_STATUS, OHCI_INTR_RD);
165                 /* TODO: figure out what the linux kernel does here... */
166         }
167
168         /* WritebackDoneHead */
169         if (flags & OHCI_INTR_WDH) {
170                 printf("WritebackDoneHead\n");
171                 /* TODO: figure out what the linux kernel does here... */
172         }
173
174         /* TODO: handle any pending URB/ED unlinks... */
175
176 #define HC_IS_RUNNING() 1 /* dirty, i know... just a temporary solution */
177         if (HC_IS_RUNNING()) {
178                 write32(OHCI0_HC_INT_STATUS, flags);
179                 write32(OHCI0_HC_INT_ENABLE, OHCI_INTR_MIE);
180         }
181 }
182