libpicofe/gp2x/pollux_set.c
notaz b0eec79345 pollux overclocking with dividers
git-svn-id: file:///home/notaz/opt/svn/PicoDrive/platform@724 be3aeb3a-fb24-0410-a615-afba39da0efa
2009-08-01 14:33:09 +00:00

388 lines
9.5 KiB
C

/*
* quick tool to set various timings for Wiz
*
* Copyright (c) Gražvydas "notaz" Ignotas, 2009
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the organization nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* HTOTAL: X VTOTAL: 341
* HSWIDTH: 1 VSWIDTH: 0
* HASTART: 37 VASTART: 17
* HAEND: 277 VAEND: 337
*
* 120Hz
* pcd 8, 447: + 594us
* pcd 9, 397: + 36us
* pcd 10, 357: - 523us
* pcd 11, 325: +1153us
*
* 'lcd_timings=397,1,37,277,341,0,17,337;dpc_clkdiv0=9'
* 'ram_timings=2,9,4,1,1,1,1'
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pollux_set.h"
/* parse stuff */
static int parse_lcd_timings(const char *str, void *data)
{
int *lcd_timings = data;
const char *p = str;
int ret, c;
ret = sscanf(str, "%d,%d,%d,%d,%d,%d,%d,%d",
&lcd_timings[0], &lcd_timings[1], &lcd_timings[2], &lcd_timings[3],
&lcd_timings[4], &lcd_timings[5], &lcd_timings[6], &lcd_timings[7]);
if (ret != 8)
return -1;
/* skip seven commas */
for (c = 0; c < 7 && *p != 0; p++)
if (*p == ',')
c++;
if (c != 7)
return -1;
/* skip last number */
while ('0' <= *p && *p <= '9')
p++;
return p - str;
}
static int parse_ram_timings(const char *str, void *data)
{
int *ram_timings = data;
const char *p = str;
int ret, c;
float cas;
ret = sscanf(p, "%f,%d,%d,%d,%d,%d,%d",
&cas, &ram_timings[1], &ram_timings[2], &ram_timings[3],
&ram_timings[4], &ram_timings[5], &ram_timings[6]);
if (ret != 7)
return -1;
if (cas == 2)
ram_timings[0] = 1;
else if (cas == 2.5)
ram_timings[0] = 2;
else if (cas == 3)
ram_timings[0] = 3;
else
return -1;
for (c = 0; c < 6 && *p != 0; p++)
if (*p == ',')
c++;
if (c != 6)
return -1;
while ('0' <= *p && *p <= '9')
p++;
return p - str;
}
static int parse_decimal(const char *str, void *data)
{
char *ep;
*(int *)data = strtoul(str, &ep, 10);
if (ep == str)
return -1;
return ep - str;
}
/* validate and apply stuff */
static int apply_lcd_timings(volatile unsigned short *memregs, void *data)
{
int *lcd_timings = data;
int i;
for (i = 0; i < 8; i++) {
if (lcd_timings[i] & ~0xffff) {
fprintf(stderr, "pollux_set: invalid lcd timing %d: %d\n", i, lcd_timings[i]);
return -1;
}
}
for (i = 0; i < 8; i++)
memregs[(0x307c>>1) + i] = lcd_timings[i];
return 0;
}
static const struct {
signed char adj; /* how to adjust value passed by user */
signed short min; /* range of */
signed short max; /* allowed values (inclusive) */
}
ram_ranges[] = {
{ 0, 1, 3 }, /* cas (cl) */
{ -2, 0, 15 }, /* trc */
{ -2, 0, 15 }, /* tras */
{ 0, 0, 15 }, /* twr */
{ 0, 0, 15 }, /* tmrd */
{ 0, 0, 15 }, /* trp */
{ 0, 0, 15 }, /* trcd */
};
static int apply_ram_timings(volatile unsigned short *memregs, void *data)
{
int *ram_timings = data;
int i, val;
for (i = 0; i < 7; i++)
{
ram_timings[i] += ram_ranges[i].adj;
if (ram_timings[i] < ram_ranges[i].min || ram_timings[i] > ram_ranges[i].max) {
fprintf(stderr, "pollux_set: invalid RAM timing %d\n", i);
return -1;
}
}
val = memregs[0x14802>>1] & 0x0f00;
val |= (ram_timings[4] << 12) | (ram_timings[5] << 4) | ram_timings[6];
memregs[0x14802>>1] = val;
val = memregs[0x14804>>1] & 0x4000;
val |= (ram_timings[0] << 12) | (ram_timings[1] << 8) |
(ram_timings[2] << 4) | ram_timings[3];
val |= 0x8000;
memregs[0x14804>>1] = val;
for (i = 0; i < 0x100000 && (memregs[0x14804>>1] & 0x8000); i++)
;
return 0;
}
static int apply_dpc_clkdiv0(volatile unsigned short *memregs, void *data)
{
int pcd = *(int *)data;
int tmp;
if ((pcd - 1) & ~0x3f) {
fprintf(stderr, "pollux_set: invalid lcd clkdiv0: %d\n", pcd);
return -1;
}
pcd = (pcd - 1) & 0x3f;
tmp = memregs[0x31c4>>1];
memregs[0x31c4>>1] = (tmp & ~0x3f0) | (pcd << 4);
return 0;
}
static int apply_cpuclk(volatile unsigned short *memregs, void *data)
{
volatile unsigned int *memregl = (volatile void *)memregs;
int mhz = *(int *)data;
int adiv, mdiv, pdiv, sdiv = 0;
int i, vf000, vf004;
// m = MDIV, p = PDIV, s = SDIV
#define SYS_CLK_FREQ 27
pdiv = 9;
mdiv = (mhz * pdiv) / SYS_CLK_FREQ;
if (mdiv & ~0x3ff)
return -1;
vf004 = (pdiv<<18) | (mdiv<<8) | sdiv;
// attempt to keep AHB the divider close to 250, but not higher
for (adiv = 1; mhz / adiv > 250; adiv++)
;
vf000 = memregl[0xf000>>2];
vf000 = (vf000 & ~0x3c0) | ((adiv - 1) << 6);
memregl[0xf000>>2] = vf000;
memregl[0xf004>>2] = vf004;
memregl[0xf07c>>2] |= 0x8000;
for (i = 0; (memregl[0xf07c>>2] & 0x8000) && i < 0x100000; i++)
;
printf("clock set to %dMHz, AHB set to %dMHz\n", mhz, mhz / adiv);
return 0;
}
static int lcd_timings[8];
static int ram_timings[7];
static int dpc_clkdiv0;
static int cpuclk;
static const char lcd_t_help[] = "htotal,hswidth,hastart,haend,vtotal,vswidth,vastart,vaend";
static const char ram_t_help[] = "CAS,tRC,tRAS,tWR,tMRD,tRP,tRCD";
static const struct {
const char *name;
const char *help;
int (*parse)(const char *str, void *data);
int (*apply)(volatile unsigned short *memregs, void *data);
void *data;
}
all_params[] = {
{ "lcd_timings", lcd_t_help, parse_lcd_timings, apply_lcd_timings, lcd_timings },
{ "ram_timings", ram_t_help, parse_ram_timings, apply_ram_timings, ram_timings },
{ "dpc_clkdiv0", "divider", parse_decimal, apply_dpc_clkdiv0, &dpc_clkdiv0 },
{ "clkdiv0", "divider", parse_decimal, apply_dpc_clkdiv0, &dpc_clkdiv0 }, /* alias */
{ "cpuclk", "MHZ", parse_decimal, apply_cpuclk, &cpuclk },
};
#define ALL_PARAM_COUNT (sizeof(all_params) / sizeof(all_params[0]))
/*
* set timings based on preformated string
* returns 0 on success.
*/
int pollux_set(volatile unsigned short *memregs, const char *str)
{
int parsed_params[ALL_PARAM_COUNT];
int applied_params[ALL_PARAM_COUNT];
int applied_something = 0;
const char *p, *po;
int i, ret;
if (str == NULL)
return -1;
memset(parsed_params, 0, sizeof(parsed_params));
memset(applied_params, 0, sizeof(applied_params));
p = str;
while (1)
{
again:
while (*p == ';' || *p == ' ')
p++;
if (*p == 0)
break;
for (i = 0; i < ALL_PARAM_COUNT; i++)
{
int param_len = strlen(all_params[i].name);
if (strncmp(p, all_params[i].name, param_len) == 0 && p[param_len] == '=')
{
p += param_len + 1;
ret = all_params[i].parse(p, all_params[i].data);
if (ret < 0) {
fprintf(stderr, "pollux_set parser: error at %-10s\n", p);
fprintf(stderr, " valid format is: <%s>\n", all_params[i].help);
return -1;
}
parsed_params[i] = 1;
p += ret;
goto again;
}
}
/* Unknown param. Attempt to be forward compatible and ignore it. */
for (po = p; *p != 0 && *p != ';'; p++)
;
fprintf(stderr, "unhandled param: ");
fwrite(po, 1, p - po, stderr);
fprintf(stderr, "\n");
}
/* validate and apply */
for (i = 0; i < ALL_PARAM_COUNT; i++)
{
if (!parsed_params[i])
continue;
ret = all_params[i].apply(memregs, all_params[i].data);
if (ret < 0) {
fprintf(stderr, "pollux_set: failed to apply %s (bad value?)\n",
all_params[i].name);
continue;
}
applied_something = 1;
applied_params[i] = 1;
}
if (applied_something)
{
int c;
printf("applied: ");
for (i = c = 0; i < ALL_PARAM_COUNT; i++)
{
if (!applied_params[i])
continue;
if (c != 0)
printf(", ");
printf("%s", all_params[i].name);
c++;
}
printf("\n");
}
return 0;
}
#ifdef BINARY
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
static void usage(const char *binary)
{
int i;
printf("usage:\n%s <set_str[;set_str[;...]]>\n"
"set_str:\n", binary);
for (i = 0; i < ALL_PARAM_COUNT; i++)
printf(" %s=<%s>\n", all_params[i].name, all_params[i].help);
}
int main(int argc, char *argv[])
{
volatile unsigned short *memregs;
int ret, memdev;
if (argc != 2) {
usage(argv[0]);
return 1;
}
memdev = open("/dev/mem", O_RDWR);
if (memdev == -1)
{
perror("open(/dev/mem) failed");
return 1;
}
memregs = mmap(0, 0x20000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000);
if (memregs == MAP_FAILED)
{
perror("mmap(memregs) failed");
close(memdev);
return 1;
}
ret = pollux_set(memregs, argv[1]);
munmap((void *)memregs, 0x20000);
close(memdev);
return ret;
}
#endif