Merge pull request #35 from loganmc10/master
[pcsx_rearmed.git] / frontend / 320240 / pollux_set.c
1 /*
2  * quick tool to set various timings for Wiz
3  *
4  * Copyright (c) GraÅžvydas "notaz" Ignotas, 2009
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *     * Redistributions of source code must retain the above copyright
9  *       notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above copyright
11  *       notice, this list of conditions and the following disclaimer in the
12  *       documentation and/or other materials provided with the distribution.
13  *     * Neither the name of the organization nor the
14  *       names of its contributors may be used to endorse or promote products
15  *       derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * HTOTAL:    X VTOTAL:  341
29  * HSWIDTH:   1 VSWIDTH:   0
30  * HASTART:  37 VASTART:  17
31  * HAEND:   277 VAEND:   337
32  *
33  * 120Hz
34  * pcd  8, 447: + 594us
35  * pcd  9, 397: +  36us
36  * pcd 10, 357: - 523us
37  * pcd 11, 325: +1153us
38  *
39  * 'lcd_timings=397,1,37,277,341,0,17,337;dpc_clkdiv0=9'
40  * 'ram_timings=2,9,4,1,1,1,1'
41  */
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 //#include "pollux_set.h"
47 #define BINARY
48
49 /* parse stuff */
50 static int parse_lcd_timings(const char *str, void *data)
51 {
52         int *lcd_timings = data;
53         const char *p = str;
54         int ret, c;
55         ret = sscanf(str, "%d,%d,%d,%d,%d,%d,%d,%d",
56                         &lcd_timings[0], &lcd_timings[1], &lcd_timings[2], &lcd_timings[3],
57                         &lcd_timings[4], &lcd_timings[5], &lcd_timings[6], &lcd_timings[7]);
58         if (ret != 8)
59                 return -1;
60         /* skip seven commas */
61         for (c = 0; c < 7 && *p != 0; p++)
62                 if (*p == ',')
63                         c++;
64         if (c != 7)
65                 return -1;
66         /* skip last number */
67         while ('0' <= *p && *p <= '9')
68                 p++;
69
70         return p - str;
71 }
72
73 static int parse_ram_timings(const char *str, void *data)
74 {
75         int *ram_timings = data;
76         const char *p = str;
77         int ret, c;
78         float cas;
79
80         ret = sscanf(p, "%f,%d,%d,%d,%d,%d,%d",
81                         &cas, &ram_timings[1], &ram_timings[2], &ram_timings[3],
82                         &ram_timings[4], &ram_timings[5], &ram_timings[6]);
83         if (ret != 7)
84                 return -1;
85         if (cas == 2)
86                 ram_timings[0] = 1;
87         else if (cas == 2.5)
88                 ram_timings[0] = 2;
89         else if (cas == 3)
90                 ram_timings[0] = 3;
91         else
92                 return -1;
93         for (c = 0; c < 6 && *p != 0; p++)
94                 if (*p == ',')
95                         c++;
96         if (c != 6)
97                 return -1;
98         while ('0' <= *p && *p <= '9')
99                 p++;
100
101         return p - str;
102 }
103
104 static int parse_decimal(const char *str, void *data)
105 {
106         char *ep;
107
108         *(int *)data = strtoul(str, &ep, 10);
109         if (ep == str)
110                 return -1;
111
112         return ep - str;
113 }
114
115 /* validate and apply stuff */
116 static int apply_lcd_timings(volatile unsigned short *memregs, void *data)
117 {
118         int *lcd_timings = data;
119         int i;
120
121         for (i = 0; i < 8; i++) {
122                 if (lcd_timings[i] & ~0xffff) {
123                         fprintf(stderr, "pollux_set: invalid lcd timing %d: %d\n", i, lcd_timings[i]);
124                         return -1;
125                 }
126         }
127
128         for (i = 0; i < 8; i++)
129                 memregs[(0x307c>>1) + i] = lcd_timings[i];
130
131         return 0;
132 }
133
134 static const struct {
135         signed char adj;        /* how to adjust value passed by user */
136         signed short min;       /* range of */
137         signed short max;       /* allowed values (inclusive) */
138 }
139 ram_ranges[] = {
140         {  0,  1,  3 }, /* cas (cl) */
141         { -2,  0, 15 }, /* trc */
142         { -2,  0, 15 }, /* tras */
143         {  0,  0, 15 }, /* twr */
144         {  0,  0, 15 }, /* tmrd */
145         {  0,  0, 15 }, /* trp */
146         {  0,  0, 15 }, /* trcd */
147 };
148
149 static int apply_ram_timings(volatile unsigned short *memregs, void *data)
150 {
151         int *ram_timings = data;
152         int i, val;
153
154         for (i = 0; i < 7; i++)
155         {
156                 ram_timings[i] += ram_ranges[i].adj;
157                 if (ram_timings[i] < ram_ranges[i].min || ram_timings[i] > ram_ranges[i].max) {
158                         fprintf(stderr, "pollux_set: invalid RAM timing %d\n", i);
159                         return -1;
160                 }
161         }
162
163         val = memregs[0x14802>>1] & 0x0f00;
164         val |= (ram_timings[4] << 12) | (ram_timings[5] << 4) | ram_timings[6];
165         memregs[0x14802>>1] = val;
166
167         val = memregs[0x14804>>1] & 0x4000;
168         val |= (ram_timings[0] << 12) | (ram_timings[1] << 8) |
169                 (ram_timings[2] << 4) | ram_timings[3];
170         val |= 0x8000;
171         memregs[0x14804>>1] = val;
172
173         for (i = 0; i < 0x100000 && (memregs[0x14804>>1] & 0x8000); i++)
174                 ;
175
176         return 0;
177 }
178
179 static int apply_dpc_clkdiv0(volatile unsigned short *memregs, void *data)
180 {
181         int pcd = *(int *)data;
182         int tmp;
183
184         if ((pcd - 1) & ~0x3f) {
185                 fprintf(stderr, "pollux_set: invalid lcd clkdiv0: %d\n", pcd);
186                 return -1;
187         }
188
189         pcd = (pcd - 1) & 0x3f;
190         tmp = memregs[0x31c4>>1];
191         memregs[0x31c4>>1] = (tmp & ~0x3f0) | (pcd << 4);
192
193         return 0;
194 }
195
196 static int apply_cpuclk(volatile unsigned short *memregs, void *data)
197 {
198         volatile unsigned int *memregl = (volatile void *)memregs;
199         int mhz = *(int *)data;
200         int adiv, mdiv, pdiv, sdiv = 0;
201         int i, vf000, vf004;
202
203         // m = MDIV, p = PDIV, s = SDIV
204         #define SYS_CLK_FREQ 27
205         pdiv = 9;
206         mdiv = (mhz * pdiv) / SYS_CLK_FREQ;
207         if (mdiv & ~0x3ff)
208                 return -1;
209         vf004 = (pdiv<<18) | (mdiv<<8) | sdiv;
210
211         // attempt to keep AHB the divider close to 250, but not higher
212         for (adiv = 1; mhz / adiv > 250; adiv++)
213                 ;
214
215         vf000 = memregl[0xf000>>2];
216         vf000 = (vf000 & ~0x3c0) | ((adiv - 1) << 6);
217         memregl[0xf000>>2] = vf000;
218         memregl[0xf004>>2] = vf004;
219         memregl[0xf07c>>2] |= 0x8000;
220         for (i = 0; (memregl[0xf07c>>2] & 0x8000) && i < 0x100000; i++)
221                 ;
222
223         printf("clock set to %dMHz, AHB set to %dMHz\n", mhz, mhz / adiv);
224         return 0;
225 }
226
227 static int lcd_timings[8];
228 static int ram_timings[7];
229 static int dpc_clkdiv0;
230 static int cpuclk;
231
232 static const char lcd_t_help[] = "htotal,hswidth,hastart,haend,vtotal,vswidth,vastart,vaend";
233 static const char ram_t_help[] = "CAS,tRC,tRAS,tWR,tMRD,tRP,tRCD";
234
235 static const struct {
236         const char *name;
237         const char *help;
238         int (*parse)(const char *str, void *data);
239         int (*apply)(volatile unsigned short *memregs, void *data);
240         void *data;
241 }
242 all_params[] = {
243         { "lcd_timings", lcd_t_help, parse_lcd_timings, apply_lcd_timings, lcd_timings  },
244         { "ram_timings", ram_t_help, parse_ram_timings, apply_ram_timings, ram_timings  },
245         { "dpc_clkdiv0", "divider",  parse_decimal,     apply_dpc_clkdiv0, &dpc_clkdiv0 },
246         { "clkdiv0",     "divider",  parse_decimal,     apply_dpc_clkdiv0, &dpc_clkdiv0 }, /* alias */
247         { "cpuclk",      "MHZ",      parse_decimal,     apply_cpuclk,      &cpuclk      },
248 };
249 #define ALL_PARAM_COUNT (sizeof(all_params) / sizeof(all_params[0]))
250
251 /*
252  * set timings based on preformated string
253  * returns 0 on success.
254  */
255 int pollux_set(volatile unsigned short *memregs, const char *str)
256 {
257         int parsed_params[ALL_PARAM_COUNT];
258         int applied_params[ALL_PARAM_COUNT];
259         int applied_something = 0;
260         const char *p, *po;
261         int i, ret;
262
263         if (str == NULL)
264                 return -1;
265
266         memset(parsed_params, 0, sizeof(parsed_params));
267         memset(applied_params, 0, sizeof(applied_params));
268
269         p = str;
270         while (1)
271         {
272 again:
273                 while (*p == ';' || *p == ' ')
274                         p++;
275                 if (*p == 0)
276                         break;
277
278                 for (i = 0; i < ALL_PARAM_COUNT; i++)
279                 {
280                         int param_len = strlen(all_params[i].name);
281                         if (strncmp(p, all_params[i].name, param_len) == 0 && p[param_len] == '=')
282                         {
283                                 p += param_len + 1;
284                                 ret = all_params[i].parse(p, all_params[i].data);
285                                 if (ret < 0) {
286                                         fprintf(stderr, "pollux_set parser: error at %-10s\n", p);
287                                         fprintf(stderr, "  valid format is: <%s>\n", all_params[i].help);
288                                         return -1;
289                                 }
290                                 parsed_params[i] = 1;
291                                 p += ret;
292                                 goto again;
293                         }
294                 }
295
296                 /* Unknown param. Attempt to be forward compatible and ignore it. */
297                 for (po = p; *p != 0 && *p != ';'; p++)
298                         ;
299
300                 fprintf(stderr, "unhandled param: ");
301                 fwrite(po, 1, p - po, stderr);
302                 fprintf(stderr, "\n");
303         }
304
305         /* validate and apply */
306         for (i = 0; i < ALL_PARAM_COUNT; i++)
307         {
308                 if (!parsed_params[i])
309                         continue;
310
311                 ret = all_params[i].apply(memregs, all_params[i].data);
312                 if (ret < 0) {
313                         fprintf(stderr, "pollux_set: failed to apply %s (bad value?)\n",
314                                 all_params[i].name);
315                         continue;
316                 }
317
318                 applied_something = 1;
319                 applied_params[i] = 1;
320         }
321
322         if (applied_something)
323         {
324                 int c;
325                 printf("applied: ");
326                 for (i = c = 0; i < ALL_PARAM_COUNT; i++)
327                 {
328                         if (!applied_params[i])
329                                 continue;
330                         if (c != 0)
331                                 printf(", ");
332                         printf("%s", all_params[i].name);
333                         c++;
334                 }
335                 printf("\n");
336         }
337
338         return 0;
339 }
340
341 #ifdef BINARY
342 #include <sys/types.h>
343 #include <sys/stat.h>
344 #include <fcntl.h>
345 #include <sys/mman.h>
346 #include <unistd.h>
347
348 static void usage(const char *binary)
349 {
350         int i;
351         printf("usage:\n%s <set_str[;set_str[;...]]>\n"
352                 "set_str:\n", binary);
353         for (i = 0; i < ALL_PARAM_COUNT; i++)
354                 printf("  %s=<%s>\n", all_params[i].name, all_params[i].help);
355 }
356
357 int main(int argc, char *argv[])
358 {
359         volatile unsigned short *memregs;
360         int ret, memdev;
361
362         if (argc != 2) {
363                 usage(argv[0]);
364                 return 1;
365         }
366
367         memdev = open("/dev/mem", O_RDWR);
368         if (memdev == -1)
369         {
370                 perror("open(/dev/mem) failed");
371                 return 1;
372         }
373
374         memregs = mmap(0, 0x20000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000);
375         if (memregs == MAP_FAILED)
376         {
377                 perror("mmap(memregs) failed");
378                 close(memdev);
379                 return 1;
380         }
381
382         ret = pollux_set(memregs, argv[1]);
383
384         munmap((void *)memregs, 0x20000);
385         close(memdev);
386
387         return ret;
388 }
389 #endif