diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-07-31 13:39:52 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-07-31 13:39:52 -0700 |
commit | 49b1e3ea19b1c95c2f012b8331ffb3b169e4c042 (patch) | |
tree | 4ccf519a20c9b5bb3701c8b4d38b01af8bef854e /drivers | |
parent | ce38cac48209d270d07fd6d1a8e94446b37abcd5 (diff) | |
parent | 8d950cb8896fc95a9444d190885779438bb9d01c (diff) | |
download | linux-49b1e3ea19b1c95c2f012b8331ffb3b169e4c042.tar.gz linux-49b1e3ea19b1c95c2f012b8331ffb3b169e4c042.tar.bz2 linux-49b1e3ea19b1c95c2f012b8331ffb3b169e4c042.zip |
Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
* 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc:
[POWERPC] Minor comment fix for misc_64.S
[POWERPC] Use H_CEDE on non-SMT
[POWERPC] force 64bit mode in fwnmi handlers to workaround firmware bugs
[POWERPC] PMAC_APM_EMU should depend on ADB_PMU
[POWERPC] Fix new interrupt code (MPIC detection)
[POWERPC] Fix new interrupt code (MPIC endianness)
[POWERPC] Add cpufreq support for Xserve G5
[POWERPC] Xserve G5 thermal control fixes
[POWERPC] Fix mem= handling when the memory limit is > RMO size
[POWERPC] More offb/bootx fixes
[POWERPC] Fix legacy_serial.c error handling on 32 bits
[POWERPC] Fix default clock for udbg_16550
[POWERPC] Fix non-MPIC CHRPs with CONFIG_SMP set
[POWERPC] Fix 32 bits warning in prom_init.c
[POWERPC] Workaround Pegasos incorrect ISA "ranges"
[POWERPC] fix up front-LED Kconfig
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ide/Kconfig | 14 | ||||
-rw-r--r-- | drivers/macintosh/Kconfig | 11 | ||||
-rw-r--r-- | drivers/macintosh/therm_pm72.c | 218 | ||||
-rw-r--r-- | drivers/macintosh/therm_pm72.h | 33 | ||||
-rw-r--r-- | drivers/macintosh/via-pmu-led.c | 2 | ||||
-rw-r--r-- | drivers/video/offb.c | 307 |
6 files changed, 406 insertions, 179 deletions
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 556958ebbf03..b6fb167e20f6 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -774,20 +774,6 @@ config BLK_DEV_IDEDMA_PMAC to transfer data to and from memory. Saying Y is safe and improves performance. -config BLK_DEV_IDE_PMAC_BLINK - bool "Blink laptop LED on drive activity (DEPRECATED)" - depends on BLK_DEV_IDE_PMAC && ADB_PMU - select ADB_PMU_LED - select LEDS_TRIGGERS - select LEDS_TRIGGER_IDE_DISK - help - This option enables the use of the sleep LED as a hard drive - activity LED. - This option is deprecated, it only selects ADB_PMU_LED and - LEDS_TRIGGER_IDE_DISK and changes the code in the new led class - device to default to the ide-disk trigger (which should be set - from userspace via sysfs). - config BLK_DEV_IDE_SWARM tristate "IDE for Sibyte evaluation boards" depends on SIBYTE_SB1xxx_SOC diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index d162307e309f..d5d649f5ccdb 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig @@ -90,6 +90,15 @@ config ADB_PMU_LED and the ide-disk LED trigger and configure appropriately through sysfs. +config ADB_PMU_LED_IDE + bool "Use front LED as IDE LED by default" + depends on ADB_PMU_LED + select LEDS_TRIGGERS + select LEDS_TRIGGER_IDE_DISK + help + This option makes the front LED default to the IDE trigger + so that it blinks on IDE activity. + config PMAC_SMU bool "Support for SMU based PowerMacs" depends on PPC_PMAC64 @@ -100,7 +109,7 @@ config PMAC_SMU config PMAC_APM_EMU tristate "APM emulation" - depends on PPC_PMAC && PPC32 && PM + depends on PPC_PMAC && PPC32 && PM && ADB_PMU config PMAC_MEDIABAY bool "Support PowerBook hotswap media bay" diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c index c1fe0b368f76..20bf67244e2c 100644 --- a/drivers/macintosh/therm_pm72.c +++ b/drivers/macintosh/therm_pm72.c @@ -95,6 +95,17 @@ * - Use min/max macros here or there * - Latest darwin updated U3H min fan speed to 20% PWM * + * July. 06, 2006 : 1.3 + * - Fix setting of RPM fans on Xserve G5 (they were going too fast) + * - Add missing slots fan control loop for Xserve G5 + * - Lower fixed slots fan speed from 50% to 40% on desktop G5s. We + * still can't properly implement the control loop for these, so let's + * reduce the noise a little bit, it appears that 40% still gives us + * a pretty good air flow + * - Add code to "tickle" the FCU regulary so it doesn't think that + * we are gone while in fact, the machine just didn't need any fan + * speed change lately + * */ #include <linux/types.h> @@ -121,7 +132,7 @@ #include "therm_pm72.h" -#define VERSION "1.2b2" +#define VERSION "1.3" #undef DEBUG @@ -146,6 +157,7 @@ static struct basckside_pid_params backside_params; static struct backside_pid_state backside_state; static struct drives_pid_state drives_state; static struct dimm_pid_state dimms_state; +static struct slots_pid_state slots_state; static int state; static int cpu_count; static int cpu_pid_type; @@ -154,7 +166,8 @@ static struct completion ctrl_complete; static int critical_state; static int rackmac; static s32 dimm_output_clamp; - +static int fcu_rpm_shift; +static int fcu_tickle_ticks; static DECLARE_MUTEX(driver_lock); /* @@ -495,13 +508,20 @@ static int start_fcu(void) rc = fan_write_reg(0x2e, &buf, 1); if (rc < 0) return -EIO; + rc = fan_read_reg(0, &buf, 1); + if (rc < 0) + return -EIO; + fcu_rpm_shift = (buf == 1) ? 2 : 3; + printk(KERN_DEBUG "FCU Initialized, RPM fan shift is %d\n", + fcu_rpm_shift); + return 0; } static int set_rpm_fan(int fan_index, int rpm) { unsigned char buf[2]; - int rc, id; + int rc, id, min, max; if (fcu_fans[fan_index].type != FCU_FAN_RPM) return -EINVAL; @@ -509,12 +529,15 @@ static int set_rpm_fan(int fan_index, int rpm) if (id == FCU_FAN_ABSENT_ID) return -EINVAL; - if (rpm < 300) - rpm = 300; - else if (rpm > 8191) - rpm = 8191; - buf[0] = rpm >> 5; - buf[1] = rpm << 3; + min = 2400 >> fcu_rpm_shift; + max = 56000 >> fcu_rpm_shift; + + if (rpm < min) + rpm = min; + else if (rpm > max) + rpm = max; + buf[0] = rpm >> (8 - fcu_rpm_shift); + buf[1] = rpm << fcu_rpm_shift; rc = fan_write_reg(0x10 + (id * 2), buf, 2); if (rc < 0) return -EIO; @@ -551,7 +574,7 @@ static int get_rpm_fan(int fan_index, int programmed) if (rc != 2) return -EIO; - return (buf[0] << 5) | buf[1] >> 3; + return (buf[0] << (8 - fcu_rpm_shift)) | buf[1] >> fcu_rpm_shift; } static int set_pwm_fan(int fan_index, int pwm) @@ -609,6 +632,26 @@ static int get_pwm_fan(int fan_index) return (buf[0] * 1000) / 2559; } +static void tickle_fcu(void) +{ + int pwm; + + pwm = get_pwm_fan(SLOTS_FAN_PWM_INDEX); + + DBG("FCU Tickle, slots fan is: %d\n", pwm); + if (pwm < 0) + pwm = 100; + + if (!rackmac) { + pwm = SLOTS_FAN_DEFAULT_PWM; + } else if (pwm < SLOTS_PID_OUTPUT_MIN) + pwm = SLOTS_PID_OUTPUT_MIN; + + /* That is hopefully enough to make the FCU happy */ + set_pwm_fan(SLOTS_FAN_PWM_INDEX, pwm); +} + + /* * Utility routine to read the CPU calibration EEPROM data * from the device-tree @@ -715,6 +758,9 @@ BUILD_SHOW_FUNC_INT(backside_fan_pwm, backside_state.pwm) BUILD_SHOW_FUNC_FIX(drives_temperature, drives_state.last_temp) BUILD_SHOW_FUNC_INT(drives_fan_rpm, drives_state.rpm) +BUILD_SHOW_FUNC_FIX(slots_temperature, slots_state.last_temp) +BUILD_SHOW_FUNC_INT(slots_fan_pwm, slots_state.pwm) + BUILD_SHOW_FUNC_FIX(dimms_temperature, dimms_state.last_temp) static DEVICE_ATTR(cpu0_temperature,S_IRUGO,show_cpu0_temperature,NULL); @@ -735,6 +781,9 @@ static DEVICE_ATTR(backside_fan_pwm,S_IRUGO,show_backside_fan_pwm,NULL); static DEVICE_ATTR(drives_temperature,S_IRUGO,show_drives_temperature,NULL); static DEVICE_ATTR(drives_fan_rpm,S_IRUGO,show_drives_fan_rpm,NULL); +static DEVICE_ATTR(slots_temperature,S_IRUGO,show_slots_temperature,NULL); +static DEVICE_ATTR(slots_fan_pwm,S_IRUGO,show_slots_fan_pwm,NULL); + static DEVICE_ATTR(dimms_temperature,S_IRUGO,show_dimms_temperature,NULL); /* @@ -1076,6 +1125,9 @@ static void do_monitor_cpu_rack(struct cpu_pid_state *state) fan_min = dimm_output_clamp; fan_min = max(fan_min, (int)state->mpu.rminn_intake_fan); + DBG(" CPU min mpu = %d, min dimm = %d\n", + state->mpu.rminn_intake_fan, dimm_output_clamp); + state->rpm = max(state->rpm, (int)fan_min); state->rpm = min(state->rpm, (int)state->mpu.rmaxn_intake_fan); state->intake_rpm = state->rpm; @@ -1374,7 +1426,8 @@ static void do_monitor_drives(struct drives_pid_state *state) DBG(" current rpm: %d\n", state->rpm); /* Get some sensor readings */ - temp = le16_to_cpu(i2c_smbus_read_word_data(state->monitor, DS1775_TEMP)) << 8; + temp = le16_to_cpu(i2c_smbus_read_word_data(state->monitor, + DS1775_TEMP)) << 8; state->last_temp = temp; DBG(" temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp), FIX32TOPRINT(DRIVES_PID_INPUT_TARGET)); @@ -1575,7 +1628,7 @@ static int init_dimms_state(struct dimm_pid_state *state) } /* - * Dispose of the state data for the drives control loop + * Dispose of the state data for the DIMM control loop */ static void dispose_dimms_state(struct dimm_pid_state *state) { @@ -1588,6 +1641,127 @@ static void dispose_dimms_state(struct dimm_pid_state *state) state->monitor = NULL; } +/* + * Slots fan control loop + */ +static void do_monitor_slots(struct slots_pid_state *state) +{ + s32 temp, integral, derivative; + s64 integ_p, deriv_p, prop_p, sum; + int i, rc; + + if (--state->ticks != 0) + return; + state->ticks = SLOTS_PID_INTERVAL; + + DBG("slots:\n"); + + /* Check fan status */ + rc = get_pwm_fan(SLOTS_FAN_PWM_INDEX); + if (rc < 0) { + printk(KERN_WARNING "Error %d reading slots fan !\n", rc); + /* XXX What do we do now ? */ + } else + state->pwm = rc; + DBG(" current pwm: %d\n", state->pwm); + + /* Get some sensor readings */ + temp = le16_to_cpu(i2c_smbus_read_word_data(state->monitor, + DS1775_TEMP)) << 8; + state->last_temp = temp; + DBG(" temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp), + FIX32TOPRINT(SLOTS_PID_INPUT_TARGET)); + + /* Store temperature and error in history array */ + state->cur_sample = (state->cur_sample + 1) % SLOTS_PID_HISTORY_SIZE; + state->sample_history[state->cur_sample] = temp; + state->error_history[state->cur_sample] = temp - SLOTS_PID_INPUT_TARGET; + + /* If first loop, fill the history table */ + if (state->first) { + for (i = 0; i < (SLOTS_PID_HISTORY_SIZE - 1); i++) { + state->cur_sample = (state->cur_sample + 1) % + SLOTS_PID_HISTORY_SIZE; + state->sample_history[state->cur_sample] = temp; + state->error_history[state->cur_sample] = + temp - SLOTS_PID_INPUT_TARGET; + } + state->first = 0; + } + + /* Calculate the integral term */ + sum = 0; + integral = 0; + for (i = 0; i < SLOTS_PID_HISTORY_SIZE; i++) + integral += state->error_history[i]; + integral *= SLOTS_PID_INTERVAL; + DBG(" integral: %08x\n", integral); + integ_p = ((s64)SLOTS_PID_G_r) * (s64)integral; + DBG(" integ_p: %d\n", (int)(integ_p >> 36)); + sum += integ_p; + + /* Calculate the derivative term */ + derivative = state->error_history[state->cur_sample] - + state->error_history[(state->cur_sample + SLOTS_PID_HISTORY_SIZE - 1) + % SLOTS_PID_HISTORY_SIZE]; + derivative /= SLOTS_PID_INTERVAL; + deriv_p = ((s64)SLOTS_PID_G_d) * (s64)derivative; + DBG(" deriv_p: %d\n", (int)(deriv_p >> 36)); + sum += deriv_p; + + /* Calculate the proportional term */ + prop_p = ((s64)SLOTS_PID_G_p) * (s64)(state->error_history[state->cur_sample]); + DBG(" prop_p: %d\n", (int)(prop_p >> 36)); + sum += prop_p; + + /* Scale sum */ + sum >>= 36; + + DBG(" sum: %d\n", (int)sum); + state->pwm = (s32)sum; + + state->pwm = max(state->pwm, SLOTS_PID_OUTPUT_MIN); + state->pwm = min(state->pwm, SLOTS_PID_OUTPUT_MAX); + + DBG("** DRIVES PWM: %d\n", (int)state->pwm); + set_pwm_fan(SLOTS_FAN_PWM_INDEX, state->pwm); +} + +/* + * Initialize the state structure for the slots bay fan control loop + */ +static int init_slots_state(struct slots_pid_state *state) +{ + state->ticks = 1; + state->first = 1; + state->pwm = 50; + + state->monitor = attach_i2c_chip(XSERVE_SLOTS_LM75, "slots_temp"); + if (state->monitor == NULL) + return -ENODEV; + + device_create_file(&of_dev->dev, &dev_attr_slots_temperature); + device_create_file(&of_dev->dev, &dev_attr_slots_fan_pwm); + + return 0; +} + +/* + * Dispose of the state data for the slots control loop + */ +static void dispose_slots_state(struct slots_pid_state *state) +{ + if (state->monitor == NULL) + return; + + device_remove_file(&of_dev->dev, &dev_attr_slots_temperature); + device_remove_file(&of_dev->dev, &dev_attr_slots_fan_pwm); + + detach_i2c_chip(state->monitor); + state->monitor = NULL; +} + + static int call_critical_overtemp(void) { char *argv[] = { critical_overtemp_path, NULL }; @@ -1617,14 +1791,17 @@ static int main_control_loop(void *x) goto out; } - /* Set the PCI fan once for now */ - set_pwm_fan(SLOTS_FAN_PWM_INDEX, SLOTS_FAN_DEFAULT_PWM); + /* Set the PCI fan once for now on non-RackMac */ + if (!rackmac) + set_pwm_fan(SLOTS_FAN_PWM_INDEX, SLOTS_FAN_DEFAULT_PWM); /* Initialize ADCs */ initialize_adc(&cpu_state[0]); if (cpu_state[1].monitor != NULL) initialize_adc(&cpu_state[1]); + fcu_tickle_ticks = FCU_TICKLE_TICKS; + up(&driver_lock); while (state == state_attached) { @@ -1634,6 +1811,12 @@ static int main_control_loop(void *x) down(&driver_lock); + /* Tickle the FCU just in case */ + if (--fcu_tickle_ticks < 0) { + fcu_tickle_ticks = FCU_TICKLE_TICKS; + tickle_fcu(); + } + /* First, we always calculate the new DIMMs state on an Xserve */ if (rackmac) do_monitor_dimms(&dimms_state); @@ -1654,7 +1837,9 @@ static int main_control_loop(void *x) } /* Then, the rest */ do_monitor_backside(&backside_state); - if (!rackmac) + if (rackmac) + do_monitor_slots(&slots_state); + else do_monitor_drives(&drives_state); up(&driver_lock); @@ -1696,6 +1881,7 @@ static void dispose_control_loops(void) dispose_cpu_state(&cpu_state[1]); dispose_backside_state(&backside_state); dispose_drives_state(&drives_state); + dispose_slots_state(&slots_state); dispose_dimms_state(&dimms_state); } @@ -1745,6 +1931,8 @@ static int create_control_loops(void) goto fail; if (rackmac && init_dimms_state(&dimms_state)) goto fail; + if (rackmac && init_slots_state(&slots_state)) + goto fail; if (!rackmac && init_drives_state(&drives_state)) goto fail; diff --git a/drivers/macintosh/therm_pm72.h b/drivers/macintosh/therm_pm72.h index fc7e9b7ecaf2..393cc9df94e1 100644 --- a/drivers/macintosh/therm_pm72.h +++ b/drivers/macintosh/therm_pm72.h @@ -105,6 +105,7 @@ static char * critical_overtemp_path = "/sbin/critical_overtemp"; #define DRIVES_DALLAS_ID 0x94 #define BACKSIDE_MAX_ID 0x98 #define XSERVE_DIMMS_LM87 0x25a +#define XSERVE_SLOTS_LM75 0x290 /* * Some MAX6690, DS1775, LM87 register definitions @@ -198,7 +199,7 @@ struct drives_pid_state #define SLOTS_FAN_PWM_DEFAULT_ID 2 #define SLOTS_FAN_PWM_INDEX 2 -#define SLOTS_FAN_DEFAULT_PWM 50 /* Do better here ! */ +#define SLOTS_FAN_DEFAULT_PWM 40 /* Do better here ! */ /* @@ -206,7 +207,7 @@ struct drives_pid_state */ #define DIMM_PID_G_d 0 #define DIMM_PID_G_p 0 -#define DIMM_PID_G_r 0x6553600 +#define DIMM_PID_G_r 0x06553600 #define DIMM_PID_INPUT_TARGET 3276800 #define DIMM_PID_INTERVAL 1 #define DIMM_PID_OUTPUT_MAX 14000 @@ -226,6 +227,31 @@ struct dimm_pid_state }; +/* + * PID factors for the Xserve Slots control loop + */ +#define SLOTS_PID_G_d 0 +#define SLOTS_PID_G_p 0 +#define SLOTS_PID_G_r 0x00100000 +#define SLOTS_PID_INPUT_TARGET 3200000 +#define SLOTS_PID_INTERVAL 1 +#define SLOTS_PID_OUTPUT_MAX 100 +#define SLOTS_PID_OUTPUT_MIN 20 +#define SLOTS_PID_HISTORY_SIZE 20 + +struct slots_pid_state +{ + int ticks; + struct i2c_client * monitor; + s32 sample_history[SLOTS_PID_HISTORY_SIZE]; + s32 error_history[SLOTS_PID_HISTORY_SIZE]; + int cur_sample; + s32 last_temp; + int first; + int pwm; +}; + + /* Desktops */ @@ -283,6 +309,9 @@ struct cpu_pid_state s32 pump_max; }; +/* Tickle FCU every 10 seconds */ +#define FCU_TICKLE_TICKS 10 + /* * Driver state */ diff --git a/drivers/macintosh/via-pmu-led.c b/drivers/macintosh/via-pmu-led.c index af8375ed0f5e..5189d5454b1f 100644 --- a/drivers/macintosh/via-pmu-led.c +++ b/drivers/macintosh/via-pmu-led.c @@ -74,7 +74,7 @@ static void pmu_led_set(struct led_classdev *led_cdev, static struct led_classdev pmu_led = { .name = "pmu-front-led", -#ifdef CONFIG_BLK_DEV_IDE_PMAC_BLINK +#ifdef CONFIG_ADB_PMU_LED_IDE .default_trigger = "ide-disk", #endif .brightness_set = pmu_led_set, diff --git a/drivers/video/offb.c b/drivers/video/offb.c index ce5f3031b99b..0013311e0564 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c @@ -62,8 +62,6 @@ struct offb_par default_par; * Interface used by the world */ -int offb_init(void); - static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *info); static int offb_blank(int blank, struct fb_info *info); @@ -72,11 +70,6 @@ static int offb_blank(int blank, struct fb_info *info); extern boot_infos_t *boot_infos; #endif -static void offb_init_nodriver(struct device_node *); -static void offb_init_fb(const char *name, const char *full_name, - int width, int height, int depth, int pitch, - unsigned long address, struct device_node *dp); - static struct fb_ops offb_ops = { .owner = THIS_MODULE, .fb_setcolreg = offb_setcolreg, @@ -229,123 +222,17 @@ static int offb_blank(int blank, struct fb_info *info) return 0; } - /* - * Initialisation - */ -int __init offb_init(void) +static void __iomem *offb_map_reg(struct device_node *np, int index, + unsigned long offset, unsigned long size) { - struct device_node *dp = NULL, *boot_disp = NULL; - - if (fb_get_options("offb", NULL)) - return -ENODEV; + struct resource r; - for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { - if (get_property(dp, "linux,opened", NULL) && - get_property(dp, "linux,boot-display", NULL)) { - boot_disp = dp; - offb_init_nodriver(dp); - } - } - for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { - if (get_property(dp, "linux,opened", NULL) && - dp != boot_disp) - offb_init_nodriver(dp); - } - - return 0; -} - - -static void __init offb_init_nodriver(struct device_node *dp) -{ - unsigned int len; - int i, width = 640, height = 480, depth = 8, pitch = 640; - unsigned int flags, rsize, addr_prop = 0; - unsigned long max_size = 0; - u64 rstart, address = OF_BAD_ADDR; - u32 *pp, *addrp, *up; - u64 asize; - - pp = (u32 *)get_property(dp, "linux,bootx-depth", &len); - if (pp == NULL) - pp = (u32 *)get_property(dp, "depth", &len); - if (pp && len == sizeof(u32)) - depth = *pp; - - pp = (u32 *)get_property(dp, "linux,bootx-width", &len); - if (pp == NULL) - pp = (u32 *)get_property(dp, "width", &len); - if (pp && len == sizeof(u32)) - width = *pp; - - pp = (u32 *)get_property(dp, "linux,bootx-height", &len); - if (pp == NULL) - pp = (u32 *)get_property(dp, "height", &len); - if (pp && len == sizeof(u32)) - height = *pp; - - pp = (u32 *)get_property(dp, "linux,bootx-linebytes", &len); - if (pp == NULL) - pp = (u32 *)get_property(dp, "linebytes", &len); - if (pp && len == sizeof(u32)) - pitch = *pp; - else - pitch = width * ((depth + 7) / 8); - - rsize = (unsigned long)pitch * (unsigned long)height; - - /* Ok, now we try to figure out the address of the framebuffer. - * - * Unfortunately, Open Firmware doesn't provide a standard way to do - * so. All we can do is a dodgy heuristic that happens to work in - * practice. On most machines, the "address" property contains what - * we need, though not on Matrox cards found in IBM machines. What I've - * found that appears to give good results is to go through the PCI - * ranges and pick one that is both big enough and if possible encloses - * the "address" property. If none match, we pick the biggest - */ - up = (u32 *)get_property(dp, "linux,bootx-addr", &len); - if (up == NULL) - up = (u32 *)get_property(dp, "address", &len); - if (up && len == sizeof(u32)) - addr_prop = *up; - - for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags)) - != NULL; i++) { - int match_addrp = 0; - - if (!(flags & IORESOURCE_MEM)) - continue; - if (asize < rsize) - continue; - rstart = of_translate_address(dp, addrp); - if (rstart == OF_BAD_ADDR) - continue; - if (addr_prop && (rstart <= addr_prop) && - ((rstart + asize) >= (addr_prop + rsize))) - match_addrp = 1; - if (match_addrp) { - address = addr_prop; - break; - } - if (rsize > max_size) { - max_size = rsize; - address = OF_BAD_ADDR; - } - - if (address == OF_BAD_ADDR) - address = rstart; - } - if (address == OF_BAD_ADDR && addr_prop) - address = (u64)addr_prop; - if (address != OF_BAD_ADDR) { - /* kludge for valkyrie */ - if (strcmp(dp->name, "valkyrie") == 0) - address += 0x1000; - offb_init_fb(dp->name, dp->full_name, width, height, depth, - pitch, address, dp); - } + if (of_address_to_resource(np, index, &r)) + return 0; + if ((r.start + offset + size) > r.end) + return 0; + return ioremap(r.start + offset, size); } static void __init offb_init_fb(const char *name, const char *full_name, @@ -402,45 +289,39 @@ static void __init offb_init_fb(const char *name, const char *full_name, par->cmap_type = cmap_unknown; if (depth == 8) { - /* Palette hacks disabled for now */ -#if 0 if (dp && !strncmp(name, "ATY,Rage128", 11)) { - unsigned long regbase = dp->addrs[2].address; - par->cmap_adr = ioremap(regbase, 0x1FFF); - par->cmap_type = cmap_r128; + par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); + if (par->cmap_adr) + par->cmap_type = cmap_r128; } else if (dp && (!strncmp(name, "ATY,RageM3pA", 12) || !strncmp(name, "ATY,RageM3p12A", 14))) { - unsigned long regbase = - dp->parent->addrs[2].address; - par->cmap_adr = ioremap(regbase, 0x1FFF); - par->cmap_type = cmap_M3A; + par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); + if (par->cmap_adr) + par->cmap_type = cmap_M3A; } else if (dp && !strncmp(name, "ATY,RageM3pB", 12)) { - unsigned long regbase = - dp->parent->addrs[2].address; - par->cmap_adr = ioremap(regbase, 0x1FFF); - par->cmap_type = cmap_M3B; + par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); + if (par->cmap_adr) + par->cmap_type = cmap_M3B; } else if (dp && !strncmp(name, "ATY,Rage6", 9)) { - unsigned long regbase = dp->addrs[1].address; - par->cmap_adr = ioremap(regbase, 0x1FFF); - par->cmap_type = cmap_radeon; + par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff); + if (par->cmap_adr) + par->cmap_type = cmap_radeon; } else if (!strncmp(name, "ATY,", 4)) { unsigned long base = address & 0xff000000UL; par->cmap_adr = ioremap(base + 0x7ff000, 0x1000) + 0xcc0; par->cmap_data = par->cmap_adr + 1; par->cmap_type = cmap_m64; - } else if (device_is_compatible(dp, "pci1014,b7")) { - unsigned long regbase = dp->addrs[0].address; - par->cmap_adr = ioremap(regbase + 0x6000, 0x1000); - par->cmap_type = cmap_gxt2000; + } else if (dp && device_is_compatible(dp, "pci1014,b7")) { + par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000); + if (par->cmap_adr) + par->cmap_type = cmap_gxt2000; } -#endif - fix->visual = par->cmap_adr ? FB_VISUAL_PSEUDOCOLOR - : FB_VISUAL_STATIC_PSEUDOCOLOR; + fix->visual = (par->cmap_type != cmap_unknown) ? + FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR; } else - fix->visual = /* par->cmap_adr ? FB_VISUAL_DIRECTCOLOR - : */ FB_VISUAL_TRUECOLOR; + fix->visual = FB_VISUAL_TRUECOLOR; var->xoffset = var->yoffset = 0; switch (depth) { @@ -520,5 +401,139 @@ static void __init offb_init_fb(const char *name, const char *full_name, info->node, full_name); } + +static void __init offb_init_nodriver(struct device_node *dp, int no_real_node) +{ + unsigned int len; + int i, width = 640, height = 480, depth = 8, pitch = 640; + unsigned int flags, rsize, addr_prop = 0; + unsigned long max_size = 0; + u64 rstart, address = OF_BAD_ADDR; + u32 *pp, *addrp, *up; + u64 asize; + + pp = (u32 *)get_property(dp, "linux,bootx-depth", &len); + if (pp == NULL) + pp = (u32 *)get_property(dp, "depth", &len); + if (pp && len == sizeof(u32)) + depth = *pp; + + pp = (u32 *)get_property(dp, "linux,bootx-width", &len); + if (pp == NULL) + pp = (u32 *)get_property(dp, "width", &len); + if (pp && len == sizeof(u32)) + width = *pp; + + pp = (u32 *)get_property(dp, "linux,bootx-height", &len); + if (pp == NULL) + pp = (u32 *)get_property(dp, "height", &len); + if (pp && len == sizeof(u32)) + height = *pp; + + pp = (u32 *)get_property(dp, "linux,bootx-linebytes", &len); + if (pp == NULL) + pp = (u32 *)get_property(dp, "linebytes", &len); + if (pp && len == sizeof(u32)) + pitch = *pp; + else + pitch = width * ((depth + 7) / 8); + + rsize = (unsigned long)pitch * (unsigned long)height; + + /* Ok, now we try to figure out the address of the framebuffer. + * + * Unfortunately, Open Firmware doesn't provide a standard way to do + * so. All we can do is a dodgy heuristic that happens to work in + * practice. On most machines, the "address" property contains what + * we need, though not on Matrox cards found in IBM machines. What I've + * found that appears to give good results is to go through the PCI + * ranges and pick one that is both big enough and if possible encloses + * the "address" property. If none match, we pick the biggest + */ + up = (u32 *)get_property(dp, "linux,bootx-addr", &len); + if (up == NULL) + up = (u32 *)get_property(dp, "address", &len); + if (up && len == sizeof(u32)) + addr_prop = *up; + + /* Hack for when BootX is passing us */ + if (no_real_node) + goto skip_addr; + + for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags)) + != NULL; i++) { + int match_addrp = 0; + + if (!(flags & IORESOURCE_MEM)) + continue; + if (asize < rsize) + continue; + rstart = of_translate_address(dp, addrp); + if (rstart == OF_BAD_ADDR) + continue; + if (addr_prop && (rstart <= addr_prop) && + ((rstart + asize) >= (addr_prop + rsize))) + match_addrp = 1; + if (match_addrp) { + address = addr_prop; + break; + } + if (rsize > max_size) { + max_size = rsize; + address = OF_BAD_ADDR; + } + + if (address == OF_BAD_ADDR) + address = rstart; + } + skip_addr: + if (address == OF_BAD_ADDR && addr_prop) + address = (u64)addr_prop; + if (address != OF_BAD_ADDR) { + /* kludge for valkyrie */ + if (strcmp(dp->name, "valkyrie") == 0) + address += 0x1000; + offb_init_fb(no_real_node ? "bootx" : dp->name, + no_real_node ? "display" : dp->full_name, + width, height, depth, pitch, address, + no_real_node ? dp : NULL); + } +} + +static int __init offb_init(void) +{ + struct device_node *dp = NULL, *boot_disp = NULL; + + if (fb_get_options("offb", NULL)) + return -ENODEV; + + /* Check if we have a MacOS display without a node spec */ + if (get_property(of_chosen, "linux,bootx-noscreen", NULL) != NULL) { + /* The old code tried to work out which node was the MacOS + * display based on the address. I'm dropping that since the + * lack of a node spec only happens with old BootX versions + * (users can update) and with this code, they'll still get + * a display (just not the palette hacks). + */ + offb_init_nodriver(of_chosen, 1); + } + + for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { + if (get_property(dp, "linux,opened", NULL) && + get_property(dp, "linux,boot-display", NULL)) { + boot_disp = dp; + offb_init_nodriver(dp, 0); + } + } + for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { + if (get_property(dp, "linux,opened", NULL) && + dp != boot_disp) + offb_init_nodriver(dp, 0); + } + + return 0; +} + + module_init(offb_init); MODULE_LICENSE("GPL"); |