LibXenon
Bare-metal Xbox 360 homebrew library
Loading...
Searching...
No Matches
xenon_power.c
Go to the documentation of this file.
1#include <stdio.h>
6#include <ppc/register.h>
7#include <ppc/xenonsprs.h>
8#include <ppc/atomic.h>
9#include <time/time.h>
10#include <pci/io.h>
11
12#include <debug.h>
13
14extern volatile int wait[12];
15extern volatile int secondary_alive;
16extern unsigned int secondary_lock;
17
18static volatile int thread_state[6];
19
20void xenon_yield(void)
21{
22}
23
24static inline void wakeup_secondary(void)
25{
26 mtspr(ctrlwr, 0xc00000); // CTRL.TE{0,1} = 11
27}
28
29void wakeup_cpus(void)
30{
31 void *irq_cntrl = (void*)0x20050000;
32
33 std(irq_cntrl + 0x2070, 0x7c);
34 std(irq_cntrl + 0x2008, 0);
35 std(irq_cntrl + 0x2000, 4);
36
37 std(irq_cntrl + 0x4070, 0x7c);
38 std(irq_cntrl + 0x4008, 0);
39 std(irq_cntrl + 0x4000, 0x10);
40
41 std(irq_cntrl + 0x10, 0x140078);
42 wakeup_secondary();
43}
44
46{
47 void *irq = (void*)0x20050000;
48 int id = mfspr(pir); // PIR
49
50 std(irq + id * 0x1000 + 8, 0x7c);
51 mtdec(0x7fffffff);
52 if (!(id & 1))
53 while (mfspr(ctrlrd) & (1 << 22)); /* wait until second thread dies */
54 mtspr(hdec, 0x7fffffff);
55 mtspr(tscr, (mfspr(tscr) & ~0x700000) | ((id & 1) ? 0 : (1<<20))); // WEXT for secondary threads
56}
57
58extern void cpusleep(void), secondary_wakeup(void);
59
61{
62 int id = mfspr(pir);
63 uint64_t save[6];
64 prepare_sleep(save);
65 thread_state[id] = 1;
66 cpusleep();
67 thread_state[id] = 0;
68 if (!(id & 1))
69 wakeup_secondary();
70}
71
72void xenon_set_speed(int new_speed, int vid_delta)
73{
74 uint64_t v;
75
76 void *cpuregs = (void*)0x20061000;
77 void *cpuregs2 = (void*)0x20060000;
78 void *irq = (void*)0x20050000;
79
80 std(irq + 0x6000, 0);
81 std(irq + 0x6010, 0);
82 std(irq + 0x6020, 0);
83 std(irq + 0x6030, 0);
84 std(irq + 0x6040, 0);
85
86 std(cpuregs2 + 0xb58, ld(cpuregs2 + 0xb58) | 0x8000000000000000ULL);
87 std(cpuregs + 0x50, ld(cpuregs + 0x50) & (0xFFFEULL<<33));
88 std(cpuregs + 0x60, ld(cpuregs + 0x60) | (1ULL << 33));
89
90 udelay(1);
91
92 std(irq + 0x6020, 0x400);
93 while (ld(irq + 0x6020) & 0x2000);
94
95 v = ld(cpuregs + 0x188);
96
97 int base_vid = (v >> 48) & 0x3F;
98
99 /*
100 The CPU VID outputs connect directly to the
101 ADP3190A chip. This allows a Vcore voltage
102 from 0.83V to 1.6V.
103
104 The formula to calculate the output voltage is:
105
106 if VID <= 0x14:
107 Vcore = 0.8375V + (0x14 - VID) * 0.0125V
108 else:
109 Vcore = 1.1000V + (0x3D - VID) * 0.0125V
110
111 To simplify this, we define a "normalized VID"
112
113 if VID < 0x15:
114 nVID = VID + 0x3E
115 else:
116 nVID = VID
117
118 Each chip has a (fuse-burned?) VID. An offset to
119 that VID is stored in the configuration data, and
120 is added to this value.
121
122 nVID_final = nVID_fused + VID_delta
123
124 Then nVID has to be converted back to a VID, and
125 written.
126
127 An example from a box running the KK hack:
128
129 The 0x61188 value says: 382c00000000b001
130
131 The "Base VID" (=VID_fused) here is 0x2C,
132 the "current VID" (=VID_final) is 0x30.
133 The speed ratio is 1 (=full speed).
134
135 The Delta data stored in the flash config is
136
137 Delta = 0x80, 0x84
138
139 So:
140
141 Current_VID = Base_VID + Delta[1] - 0x80
142
143 It seems that the CPU runs stable with much less voltage, but
144 this would require a bit more effort to make this sure.
145 */
146
147 uint32_t rev700up=mfspr(pvr)>=0x710700;
148
149 if(!rev700up){
150 int vlt = 11000 + (0x3D - ((base_vid < 0x15) ? base_vid + 0x3E : base_vid)) * 125;
151
152 printf(" * default VID: %02x (%d.%04dV)\n", base_vid, vlt / 10000, vlt % 10000);
153
154 if (base_vid < 0x15)
155 base_vid += 0x3e;
156 }
157
158 int new_vid = base_vid + vid_delta - 0x80;
159
160 if(!rev700up){
161 if (new_vid >= 0x3e)
162 new_vid -= 0x3e;
163
164 int vlt = 11000 + (0x3D - ((new_vid < 0x15) ? new_vid + 0x3E : new_vid)) * 125;
165
166 printf(" * using new VID: %02x (%d.%04dV)\n", new_vid, vlt / 10000, vlt % 10000);
167 }
168
169 v &= ~0xBF08ULL;
170 v |= new_vid << 8;
171 v |= 0x4000;
172 v |= 0xFF << 24;
173
174 std(cpuregs + 0x188, v);
175
176 while (!(ld(irq + 0x6020) & 0x2000));
177
178 std(cpuregs + 0x188, ld(cpuregs + 0x188) | 0x8000);
179
180 std(irq + 0x6020, 0x1047c);
181 //while (ld(irq + 0x6020) & 0x2000);
182 std(irq + 8, 0x78);
183
184 v = ld(cpuregs + 0x188);
185
186 v &= ~0xC007ULL;
187 v |= new_speed & 7;
188 v |= 0xFF0000;
189 v |= 8;
190 std(cpuregs + 0x188, v);
191
192 cpusleep();
193
194 std(cpuregs + 0x188, ld(cpuregs + 0x188) | 0x8000);
195
196 std(irq + 0x50, ld(irq + 0x60));
197 std(irq + 8, 0x7c);
198 std(irq + 0x6020, 0);
199
200}
201
203{
204 void *cpuregs = (void*)0x20061000;
205 return ld(cpuregs + 0x188) & 0x7;
206}
207
208int xenon_run_thread_task(int thread, void *stack, void *task)
209{
210 if (wait[thread * 2])
211 return -1; // busy
212 wait[thread * 2 + 1] = (int)stack;
213 wait[thread * 2] = (int)task;
214 return 0;
215}
216
218{
219 if (wait[thread * 2])
220 return -1; // busy
221 return 0;
222}
223
224static unsigned char stack[5 * 0x1000];
225
226void xenon_make_it_faster(int speed)
227{
228 int i,delta;
229
230 if (xenon_get_speed()==speed){
231 printf(" * Starting threads only, CPU was already made faster !\n");
233 return;
234 }
235
238
239 if (delta<0){
240 printf(" !!! could not read VID delta, aborting\n");
241 return;
242 }
243
244 printf(" * Make it faster by making it sleep...\n");
245
247
248 printf(" * Make it faster by making it consume more power...\n");
249
251
252 xenon_set_speed(speed,delta);
253
254 printf(" * Make it faster by awaking the other cores...\n");
255 wakeup_cpus();
256
257 for (i = 1; i < 6; ++i)
258 while (thread_state[i])
259 xenon_yield();
260 printf(" * fine, they all came back.\n");
261}
262
264{
265 if (secondary_alive == 0x3f) return;
266 atomic_clearset(&secondary_lock, 0, 4 | 16); /* start primary threads */
267 while (secondary_alive != 0x3f); /* wait until all are alive */
268}
269
270void xenon_sleep_thread(int thread)
271{
272 xenon_run_thread_task(thread, stack + thread * 0x1000 - 0x100, xenon_thread_sleep);
273}
274
275
277 int i;
278
280
281 for (i = 1; i < 6; ++i)
282 while (xenon_run_thread_task(i, stack + i * 0x1000 - 0x100, xenon_thread_sleep));
283
284 for (i = 1; i < 6; ++i)
285 while (!thread_state[i])
286 xenon_yield();
287
288 uint64_t save[6];
289 prepare_sleep(save);
290}
void atomic_clearset(unsigned int *v, unsigned int andc, unsigned int or_)
u32 uint32_t
Definition: libfdt_env.h:11
u64 uint64_t
Definition: libfdt_env.h:12
#define mfspr(rn)
Definition: register.h:16
#define mtspr(rn, v)
Definition: register.h:24
#define mtdec(v)
Definition: register.h:14
void udelay(int u)
Definition: time.c:12
void xenon_config_init(void)
Definition: xenon_config.c:22
int xenon_config_get_vid_delta()
Definition: xenon_config.c:95
int xenon_run_thread_task(int thread, void *stack, void *task)
Definition: xenon_power.c:208
void xenon_make_it_faster(int speed)
Definition: xenon_power.c:226
void xenon_thread_startup(void)
Definition: xenon_power.c:263
void prepare_sleep(uint64_t *save)
Definition: xenon_power.c:45
int xenon_is_thread_task_running(int thread)
Definition: xenon_power.c:217
void xenon_sleep_thread(int thread)
Definition: xenon_power.c:270
void xenon_set_speed(int new_speed, int vid_delta)
Definition: xenon_power.c:72
int xenon_get_speed()
Definition: xenon_power.c:202
unsigned int secondary_lock
void xenon_set_single_thread_mode()
Definition: xenon_power.c:276
void wakeup_cpus(void)
Definition: xenon_power.c:29
void secondary_wakeup(void)
void xenon_thread_sleep(void)
Definition: xenon_power.c:60
void cpusleep(void)
void xenon_yield(void)
Definition: xenon_power.c:20
volatile int wait[12]
volatile int secondary_alive
void xenon_smc_set_fan_algorithm(int algorithm)
Definition: xenon_smc.c:306
#define hdec
Definition: xenonsprs.h:16
#define ctrlwr
Definition: xenonsprs.h:8
#define tscr
Definition: xenonsprs.h:30
#define pir
Definition: xenonsprs.h:59
#define pvr
Definition: xenonsprs.h:9
#define ctrlrd
Definition: xenonsprs.h:7