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