32x: a hack for dealing with pwm inaccuracy
[picodrive.git] / platform / gp2x / 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
48 /* parse stuff */
49 static int parse_lcd_timings(const char *str, void *data)
50 {
51         int *lcd_timings = data;
52         const char *p = str;
53         int ret, c;
54         ret = sscanf(str, "%d,%d,%d,%d,%d,%d,%d,%d",
55                         &lcd_timings[0], &lcd_timings[1], &lcd_timings[2], &lcd_timings[3],
56                         &lcd_timings[4], &lcd_timings[5], &lcd_timings[6], &lcd_timings[7]);
57         if (ret != 8)
58                 return -1;
59         /* skip seven commas */
60         for (c = 0; c < 7 && *p != 0; p++)
61                 if (*p == ',')
62                         c++;
63         if (c != 7)
64                 return -1;
65         /* skip last number */
66         while ('0' <= *p && *p <= '9')
67                 p++;
68
69         return p - str;
70 }
71
72 static int parse_ram_timings(const char *str, void *data)
73 {
74         int *ram_timings = data;
75         const char *p = str;
76         int ret, c;
77         float cas;
78
79         ret = sscanf(p, "%f,%d,%d,%d,%d,%d,%d",
80                         &cas, &ram_timings[1], &ram_timings[2], &ram_timings[3],
81                         &ram_timings[4], &ram_timings[5], &ram_timings[6]);
82         if (ret != 7)
83                 return -1;
84         if (cas == 2)
85                 ram_timings[0] = 1;
86         else if (cas == 2.5)
87                 ram_timings[0] = 2;
88         else if (cas == 3)
89                 ram_timings[0] = 3;
90         else
91                 return -1;
92         for (c = 0; c < 6 && *p != 0; p++)
93                 if (*p == ',')
94                         c++;
95         if (c != 6)
96                 return -1;
97         while ('0' <= *p && *p <= '9')
98                 p++;
99
100         return p - str;
101 }
102
103 static int parse_decimal(const char *str, void *data)
104 {
105         char *ep;
106
107         *(int *)data = strtoul(str, &ep, 10);
108         if (ep == str)
109                 return -1;
110
111         return ep - str;
112 }
113
114 /* validate and apply stuff */
115 static int apply_lcd_timings(volatile unsigned short *memregs, void *data)
116 {
117         int *lcd_timings = data;
118         int i;
119
120         for (i = 0; i < 8; i++) {
121                 if (lcd_timings[i] & ~0xffff) {
122                         fprintf(stderr, "pollux_set: invalid lcd timing %d: %d\n", i, lcd_timings[i]);
123                         return -1;
124                 }
125         }
126
127         for (i = 0; i < 8; i++)
128                 memregs[(0x307c>>1) + i] = lcd_timings[i];
129
130         return 0;
131 }
132
133 static const struct {
134         signed char adj;        /* how to adjust value passed by user */
135         signed short min;       /* range of */
136         signed short max;       /* allowed values (inclusive) */
137 }
138 ram_ranges[] = {
139         {  0,  1,  3 }, /* cas (cl) */
140         { -2,  0, 15 }, /* trc */
141         { -2,  0, 15 }, /* tras */
142         {  0,  0, 15 }, /* twr */
143         {  0,  0, 15 }, /* tmrd */
144         {  0,  0, 15 }, /* trp */
145         {  0,  0, 15 }, /* trcd */
146 };
147
148 static int apply_ram_timings(volatile unsigned short *memregs, void *data)
149 {
150         int *ram_timings = data;
151         int i, val;
152
153         for (i = 0; i < 7; i++)
154         {
155                 ram_timings[i] += ram_ranges[i].adj;
156                 if (ram_timings[i] < ram_ranges[i].min || ram_timings[i] > ram_ranges[i].max) {
157                         fprintf(stderr, "pollux_set: invalid RAM timing %d\n", i);
158                         return -1;
159                 }
160         }
161
162         val = memregs[0x14802>>1] & 0x0f00;
163         val |= (ram_timings[4] << 12) | (ram_timings[5] << 4) | ram_timings[6];
164         memregs[0x14802>>1] = val;
165
166         val = memregs[0x14804>>1] & 0x4000;
167         val |= (ram_timings[0] << 12) | (ram_timings[1] << 8) |
168                 (ram_timings[2] << 4) | ram_timings[3];
169         val |= 0x8000;
170         memregs[0x14804>>1] = val;
171
172         for (i = 0; i < 0x100000 && (memregs[0x14804>>1] & 0x8000); i++)
173                 ;
174
175         return 0;
176 }
177
178 static int apply_dpc_clkdiv0(volatile unsigned short *memregs, void *data)
179 {
180         int pcd = *(int *)data;
181         int tmp;
182
183         if ((pcd - 1) & ~0x3f) {
184                 fprintf(stderr, "pollux_set: invalid lcd clkdiv0: %d\n", pcd);
185                 return -1;
186         }
187
188         pcd = (pcd - 1) & 0x3f;
189         tmp = memregs[0x31c4>>1];
190         memregs[0x31c4>>1] = (tmp & ~0x3f0) | (pcd << 4);
191
192         return 0;
193 }
194
195 static int apply_cpuclk(volatile unsigned short *memregs, void *data)
196 {
197         volatile unsigned int *memregl = (volatile void *)memregs;
198         int mhz = *(int *)data;
199         int adiv, mdiv, pdiv, sdiv = 0;
200         int i, vf000, vf004;
201
202         // m = MDIV, p = PDIV, s = SDIV
203         #define SYS_CLK_FREQ 27
204         pdiv = 9;
205         mdiv = (mhz * pdiv) / SYS_CLK_FREQ;
206         if (mdiv & ~0x3ff)
207                 return -1;
208         vf004 = (pdiv<<18) | (mdiv<<8) | sdiv;
209
210         // attempt to keep AHB the divider close to 250, but not higher
211         for (adiv = 1; mhz / adiv > 250; adiv++)
212                 ;
213
214         vf000 = memregl[0xf000>>2];
215         vf000 = (vf000 & ~0x3c0) | ((adiv - 1) << 6);
216         memregl[0xf000>>2] = vf000;
217         memregl[0xf004>>2] = vf004;
218         memregl[0xf07c>>2] |= 0x8000;
219         for (i = 0; (memregl[0xf07c>>2] & 0x8000) && i < 0x100000; i++)
220                 ;
221
222         printf("clock set to %dMHz, AHB set to %dMHz\n", mhz, mhz / adiv);
223         return 0;
224 }
225
226 static int lcd_timings[8];
227 static int ram_timings[7];
228 static int dpc_clkdiv0;
229 static int cpuclk;
230
231 static const char lcd_t_help[] = "htotal,hswidth,hastart,haend,vtotal,vswidth,vastart,vaend";
232 static const char ram_t_help[] = "CAS,tRC,tRAS,tWR,tMRD,tRP,tRCD";
233
234 static const struct {
235         const char *name;
236         const char *help;
237         int (*parse)(const char *str, void *data);
238         int (*apply)(volatile unsigned short *memregs, void *data);
239         void *data;
240 }
241 all_params[] = {
242         { "lcd_timings", lcd_t_help, parse_lcd_timings, apply_lcd_timings, lcd_timings  },
243         { "ram_timings", ram_t_help, parse_ram_timings, apply_ram_timings, ram_timings  },
244         { "dpc_clkdiv0", "divider",  parse_decimal,     apply_dpc_clkdiv0, &dpc_clkdiv0 },
245         { "clkdiv0",     "divider",  parse_decimal,     apply_dpc_clkdiv0, &dpc_clkdiv0 }, /* alias */
246         { "cpuclk",      "MHZ",      parse_decimal,     apply_cpuclk,      &cpuclk      },
247 };
248 #define ALL_PARAM_COUNT (sizeof(all_params) / sizeof(all_params[0]))
249
250 /*
251  * set timings based on preformated string
252  * returns 0 on success.
253  */
254 int pollux_set(volatile unsigned short *memregs, const char *str)
255 {
256         int parsed_params[ALL_PARAM_COUNT];
257         int applied_params[ALL_PARAM_COUNT];
258         int applied_something = 0;
259         const char *p, *po;
260         int i, ret;
261
262         if (str == NULL)
263                 return -1;
264
265         memset(parsed_params, 0, sizeof(parsed_params));
266         memset(applied_params, 0, sizeof(applied_params));
267
268         p = str;
269         while (1)
270         {
271 again:
272                 while (*p == ';' || *p == ' ')
273                         p++;
274                 if (*p == 0)
275                         break;
276
277                 for (i = 0; i < ALL_PARAM_COUNT; i++)
278                 {
279                         int param_len = strlen(all_params[i].name);
280                         if (strncmp(p, all_params[i].name, param_len) == 0 && p[param_len] == '=')
281                         {
282                                 p += param_len + 1;
283                                 ret = all_params[i].parse(p, all_params[i].data);
284                                 if (ret < 0) {
285                                         fprintf(stderr, "pollux_set parser: error at %-10s\n", p);
286                                         fprintf(stderr, "  valid format is: <%s>\n", all_params[i].help);
287                                         return -1;
288                                 }
289                                 parsed_params[i] = 1;
290                                 p += ret;
291                                 goto again;
292                         }
293                 }
294
295                 /* Unknown param. Attempt to be forward compatible and ignore it. */
296                 for (po = p; *p != 0 && *p != ';'; p++)
297                         ;
298
299                 fprintf(stderr, "unhandled param: ");
300                 fwrite(po, 1, p - po, stderr);
301                 fprintf(stderr, "\n");
302         }
303
304         /* validate and apply */
305         for (i = 0; i < ALL_PARAM_COUNT; i++)
306         {
307                 if (!parsed_params[i])
308                         continue;
309
310                 ret = all_params[i].apply(memregs, all_params[i].data);
311                 if (ret < 0) {
312                         fprintf(stderr, "pollux_set: failed to apply %s (bad value?)\n",
313                                 all_params[i].name);
314                         continue;
315                 }
316
317                 applied_something = 1;
318                 applied_params[i] = 1;
319         }
320
321         if (applied_something)
322         {
323                 int c;
324                 printf("applied: ");
325                 for (i = c = 0; i < ALL_PARAM_COUNT; i++)
326                 {
327                         if (!applied_params[i])
328                                 continue;
329                         if (c != 0)
330                                 printf(", ");
331                         printf("%s", all_params[i].name);
332                         c++;
333                 }
334                 printf("\n");
335         }
336
337         return 0;
338 }
339
340 #ifdef BINARY
341 #include <sys/types.h>
342 #include <sys/stat.h>
343 #include <fcntl.h>
344 #include <sys/mman.h>
345 #include <unistd.h>
346
347 static void usage(const char *binary)
348 {
349         int i;
350         printf("usage:\n%s <set_str[;set_str[;...]]>\n"
351                 "set_str:\n", binary);
352         for (i = 0; i < ALL_PARAM_COUNT; i++)
353                 printf("  %s=<%s>\n", all_params[i].name, all_params[i].help);
354 }
355
356 int main(int argc, char *argv[])
357 {
358         volatile unsigned short *memregs;
359         int ret, memdev;
360
361         if (argc != 2) {
362                 usage(argv[0]);
363                 return 1;
364         }
365
366         memdev = open("/dev/mem", O_RDWR);
367         if (memdev == -1)
368         {
369                 perror("open(/dev/mem) failed");
370                 return 1;
371         }
372
373         memregs = mmap(0, 0x20000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000);
374         if (memregs == MAP_FAILED)
375         {
376                 perror("mmap(memregs) failed");
377                 close(memdev);
378                 return 1;
379         }
380
381         ret = pollux_set(memregs, argv[1]);
382
383         munmap((void *)memregs, 0x20000);
384         close(memdev);
385
386         return ret;
387 }
388 #endif