Move xbox's i2c support to a separate file, and add support of xbox's LEDs switching...
[reactos.git] / reactos / boot / freeldr / freeldr / arch / i386 / xboxi2c.c
1 /* $Id: xboxhw.c 19190 2005-11-13 04:50:55Z fireball $
2 *
3 * FreeLoader
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <freeldr.h>
21
22 // These functions are used only inside xbox-specific code
23 // thus I didn't include them in header
24
25 #define I2C_IO_BASE 0xc000
26
27 static BOOL
28 WriteToSMBus(UCHAR Address, UCHAR bRegister, UCHAR Size, ULONG Data_to_smbus)
29 {
30 int nRetriesToLive=50;
31
32 while(READ_PORT_USHORT((PUSHORT) (I2C_IO_BASE+0)) & 0x0800)
33 {
34 ; // Franz's spin while bus busy with any master traffic
35 }
36
37 while(nRetriesToLive--)
38 {
39 UCHAR b;
40 unsigned int temp;
41
42 WRITE_PORT_UCHAR((PUCHAR)(I2C_IO_BASE + 4), (Address << 1) | 0);
43 WRITE_PORT_UCHAR((PUCHAR)(I2C_IO_BASE + 8), bRegister);
44
45 switch (Size)
46 {
47 case 4:
48 WRITE_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 9), Data_to_smbus & 0xff);
49 WRITE_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 9), (Data_to_smbus >> 8) & 0xff );
50 WRITE_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 9), (Data_to_smbus >> 16) & 0xff );
51 WRITE_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 9), (Data_to_smbus >> 24) & 0xff );
52 WRITE_PORT_USHORT((PUSHORT) (I2C_IO_BASE + 6), 4);
53 break;
54 case 2:
55 WRITE_PORT_USHORT((PUSHORT) (I2C_IO_BASE + 6), Data_to_smbus&0xffff);
56 break;
57 default: // 1
58 WRITE_PORT_USHORT((PUSHORT) (I2C_IO_BASE + 6), Data_to_smbus&0xff);
59 break;
60 }
61
62
63 temp = READ_PORT_USHORT((PUSHORT) (I2C_IO_BASE + 0));
64 WRITE_PORT_USHORT((PUSHORT) (I2C_IO_BASE + 0), temp); // clear down all preexisting errors
65
66 switch (Size)
67 {
68 case 4:
69 WRITE_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 2), 0x1d); // DWORD modus
70 break;
71 case 2:
72 WRITE_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 2), 0x1b); // WORD modus
73 break;
74 default: // 1
75 WRITE_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 2), 0x1a); // BYTE modus
76 break;
77 }
78
79 b = 0;
80
81 while( (b&0x36)==0 )
82 {
83 b=READ_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 0));
84 }
85
86 if ((b&0x10) != 0)
87 {
88 return TRUE;
89 }
90
91 StallExecutionProcessor(1);
92 }
93
94 return FALSE;
95 }
96
97
98 static BOOL
99 ReadfromSMBus(UCHAR Address, UCHAR bRegister, UCHAR Size, ULONG *Data_to_smbus)
100 {
101 int nRetriesToLive=50;
102
103 while (0 != (READ_PORT_USHORT((PUSHORT) (I2C_IO_BASE + 0)) & 0x0800))
104 {
105 ; /* Franz's spin while bus busy with any master traffic */
106 }
107
108 while (0 != nRetriesToLive--)
109 {
110 UCHAR b;
111 int temp;
112
113 WRITE_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 4), (Address << 1) | 1);
114 WRITE_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 8), bRegister);
115
116 temp = READ_PORT_USHORT((USHORT *) (I2C_IO_BASE + 0));
117 WRITE_PORT_USHORT((PUSHORT) (I2C_IO_BASE + 0), temp); /* clear down all preexisting errors */
118
119 switch (Size)
120 {
121 case 4:
122 WRITE_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 2), 0x0d); /* DWORD modus ? */
123 break;
124 case 2:
125 WRITE_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 2), 0x0b); /* WORD modus */
126 break;
127 default:
128 WRITE_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 2), 0x0a); // BYTE
129 break;
130 }
131
132 b = 0;
133
134 while (0 == (b & 0x36))
135 {
136 b = READ_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 0));
137 }
138
139 if (0 != (b & 0x24))
140 {
141 /* printf("I2CTransmitByteGetReturn error %x\n", b); */
142 }
143
144 if(0 == (b & 0x10))
145 {
146 /* printf("I2CTransmitByteGetReturn no complete, retry\n"); */
147 }
148 else
149 {
150 switch (Size)
151 {
152 case 4:
153 READ_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 6));
154 READ_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 9));
155 READ_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 9));
156 READ_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 9));
157 READ_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 9));
158 break;
159 case 2:
160 *Data_to_smbus = READ_PORT_USHORT((USHORT *) (I2C_IO_BASE + 6));
161 break;
162 default:
163 *Data_to_smbus = READ_PORT_UCHAR((PUCHAR) (I2C_IO_BASE + 6));
164 break;
165 }
166
167
168 return TRUE;
169 }
170 }
171
172 return FALSE;
173 }
174
175 BOOL
176 I2CTransmitByteGetReturn(UCHAR bPicAddressI2cFormat, UCHAR bDataToWrite, ULONG *Return)
177 {
178 return ReadfromSMBus(bPicAddressI2cFormat, bDataToWrite, 1, Return);
179 }
180
181 // transmit a word, no returned data from I2C device
182 static BOOL
183 I2CTransmitWord(UCHAR bPicAddressI2cFormat, USHORT wDataToWrite)
184 {
185 return WriteToSMBus(bPicAddressI2cFormat,(wDataToWrite>>8)&0xff,1,(wDataToWrite&0xff));
186 }
187
188 static void
189 I2cSetFrontpanelLed(UCHAR b)
190 {
191 I2CTransmitWord( 0x10, 0x800 | b); // sequencing thanks to Jarin the Penguin!
192 I2CTransmitWord( 0x10, 0x701);
193 }
194
195 // Set the pattern of the LED.
196 // r = Red, g = Green, o = Orange, x = Off
197 // This func is taken from cromwell, all credits goes for them
198 void
199 XboxSetLED(UCHAR *pattern) {
200 char *x = pattern;
201 int r, g;
202
203 if(strlen(pattern) == 4) {
204 r = g = 0;
205 while (*x) {
206 r *= 2;
207 g *= 2;
208 switch (*x) {
209 case 'r':
210 r++;
211 break;
212 case 'g':
213 g++;
214 break;
215 case 'o':
216 r++;
217 g++;
218 break;
219 }
220 x++;
221 }
222 I2cSetFrontpanelLed(((r<<4) & 0xF0) + (g & 0xF));
223 }
224 }