aboutsummaryrefslogtreecommitdiff
path: root/drivers/macintosh
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/macintosh')
-rw-r--r--drivers/macintosh/Kconfig99
-rw-r--r--drivers/macintosh/Makefile23
-rw-r--r--drivers/macintosh/adb-iop.c15
-rw-r--r--drivers/macintosh/adb.c363
-rw-r--r--drivers/macintosh/adbhid.c198
-rw-r--r--drivers/macintosh/ams/Makefile8
-rw-r--r--drivers/macintosh/ams/ams-core.c250
-rw-r--r--drivers/macintosh/ams/ams-i2c.c277
-rw-r--r--drivers/macintosh/ams/ams-input.c161
-rw-r--r--drivers/macintosh/ams/ams-pmu.c201
-rw-r--r--drivers/macintosh/ams/ams.h70
-rw-r--r--drivers/macintosh/ans-lcd.c58
-rw-r--r--drivers/macintosh/ans-lcd.h11
-rw-r--r--drivers/macintosh/apm_emu.c525
-rw-r--r--drivers/macintosh/mac_hid.c286
-rw-r--r--drivers/macintosh/macio-adb.c30
-rw-r--r--drivers/macintosh/macio_asic.c235
-rw-r--r--drivers/macintosh/macio_sysfs.c38
-rw-r--r--drivers/macintosh/mediabay.c390
-rw-r--r--drivers/macintosh/nvram.c19
-rw-r--r--drivers/macintosh/rack-meter.c618
-rw-r--r--drivers/macintosh/smu.c145
-rw-r--r--drivers/macintosh/therm_adt746x.c509
-rw-r--r--drivers/macintosh/therm_pm72.c369
-rw-r--r--drivers/macintosh/therm_pm72.h2
-rw-r--r--drivers/macintosh/therm_windtunnel.c214
-rw-r--r--drivers/macintosh/via-cuda.c145
-rw-r--r--drivers/macintosh/via-macii.c592
-rw-r--r--drivers/macintosh/via-maciisi.c28
-rw-r--r--drivers/macintosh/via-pmu-backlight.c100
-rw-r--r--drivers/macintosh/via-pmu-led.c49
-rw-r--r--drivers/macintosh/via-pmu.c1103
-rw-r--r--drivers/macintosh/via-pmu68k.c275
-rw-r--r--drivers/macintosh/windfarm.h51
-rw-r--r--drivers/macintosh/windfarm_ad7417_sensor.c347
-rw-r--r--drivers/macintosh/windfarm_core.c52
-rw-r--r--drivers/macintosh/windfarm_cpufreq_clamp.c6
-rw-r--r--drivers/macintosh/windfarm_fcu_controls.c600
-rw-r--r--drivers/macintosh/windfarm_lm75_sensor.c181
-rw-r--r--drivers/macintosh/windfarm_lm87_sensor.c201
-rw-r--r--drivers/macintosh/windfarm_max6690_sensor.c150
-rw-r--r--drivers/macintosh/windfarm_mpu.h105
-rw-r--r--drivers/macintosh/windfarm_pm112.c32
-rw-r--r--drivers/macintosh/windfarm_pm121.c1053
-rw-r--r--drivers/macintosh/windfarm_pm72.c847
-rw-r--r--drivers/macintosh/windfarm_pm81.c69
-rw-r--r--drivers/macintosh/windfarm_pm91.c75
-rw-r--r--drivers/macintosh/windfarm_rm31.c740
-rw-r--r--drivers/macintosh/windfarm_smu_controls.c19
-rw-r--r--drivers/macintosh/windfarm_smu_sat.c220
-rw-r--r--drivers/macintosh/windfarm_smu_sensors.c15
51 files changed, 8354 insertions, 3815 deletions
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
index d5d649f5ccd..3067d56b11a 100644
--- a/drivers/macintosh/Kconfig
+++ b/drivers/macintosh/Kconfig
@@ -1,6 +1,15 @@
-menu "Macintosh device drivers"
- depends on PPC || MAC
+menuconfig MACINTOSH_DRIVERS
+ bool "Macintosh device drivers"
+ depends on PPC || MAC || X86
+ default y if (PPC_PMAC || MAC)
+ ---help---
+ Say Y here to get to see options for devices used with Macintosh
+ computers. This option alone does not add any kernel code.
+
+ If you say N, all options in this submenu will be skipped and disabled.
+
+if MACINTOSH_DRIVERS
config ADB
bool "Apple Desktop Bus (ADB) support"
@@ -23,7 +32,7 @@ config ADB_MACII
config ADB_MACIISI
bool "Include Mac IIsi ADB driver"
- depends on ADB && MAC
+ depends on ADB && MAC && BROKEN
help
Say Y here if want your kernel to support Macintosh systems that use
the Mac IIsi style ADB. This includes the IIsi, IIvi, IIvx, Classic
@@ -93,6 +102,8 @@ config ADB_PMU_LED
config ADB_PMU_LED_IDE
bool "Use front LED as IDE LED by default"
depends on ADB_PMU_LED
+ depends on LEDS_CLASS
+ depends on IDE_GD_ATA
select LEDS_TRIGGERS
select LEDS_TRIGGER_IDE_DISK
help
@@ -109,11 +120,12 @@ config PMAC_SMU
config PMAC_APM_EMU
tristate "APM emulation"
- depends on PPC_PMAC && PPC32 && PM && ADB_PMU
+ select APM_EMULATION
+ depends on ADB_PMU && PM && PPC32
config PMAC_MEDIABAY
bool "Support PowerBook hotswap media bay"
- depends on PPC_PMAC && PPC32
+ depends on PPC_PMAC && PPC32 && BLOCK
help
This option adds support for older PowerBook's hotswap media bay
that can contains batteries, floppy drives, or IDE devices. PCI
@@ -128,7 +140,7 @@ config PMAC_BACKLIGHT
Say Y here to enable Macintosh specific extensions of the generic
backlight code. With this enabled, the brightness keys on older
PowerBooks will be enabled so you can change the screen brightness.
- Newer models should use an userspace daemon like pbbuttonsd.
+ Newer models should use a userspace daemon like pbbuttonsd.
config PMAC_BACKLIGHT_LEGACY
bool "Provide legacy ioctl's on /dev/pmu for the backlight"
@@ -161,8 +173,8 @@ config INPUT_ADBHID
If unsure, say Y.
config MAC_EMUMOUSEBTN
- bool "Support for mouse button 2+3 emulation"
- depends on INPUT_ADBHID
+ tristate "Support for mouse button 2+3 emulation"
+ depends on SYSCTL && INPUT
help
This provides generic support for emulating the 2nd and 3rd mouse
button with keypresses. If you say Y here, the emulation is still
@@ -174,6 +186,9 @@ config MAC_EMUMOUSEBTN
If you have an Apple machine with a 1-button mouse, say Y here.
+ To compile this driver as a module, choose M here: the
+ module will be called mac_hid.
+
config THERM_WINDTUNNEL
tristate "Support for thermal management on Windtunnel G4s"
depends on I2C && I2C_POWERMAC && PPC_PMAC && !PPC_PMAC64
@@ -186,15 +201,18 @@ config THERM_ADT746X
depends on I2C && I2C_POWERMAC && PPC_PMAC && !PPC_PMAC64
help
This driver provides some thermostat and fan control for the
- iBook G4, and the ATI based aluminium PowerBooks, allowing slighlty
+ iBook G4, and the ATI based aluminium PowerBooks, allowing slightly
better fan behaviour by default, and some manual control.
config THERM_PM72
- tristate "Support for thermal management on PowerMac G5"
+ tristate "Support for thermal management on PowerMac G5 (AGP)"
depends on I2C && I2C_POWERMAC && PPC_PMAC64
+ default n
help
This driver provides thermostat and fan control for the desktop
- G5 machines.
+ G5 machines.
+
+ This is deprecated, use windfarm instead.
config WINDFARM
tristate "New PowerMac thermal control infrastructure"
@@ -207,6 +225,22 @@ config WINDFARM_PM81
help
This driver provides thermal control for the iMacG5
+config WINDFARM_PM72
+ tristate "Support for thermal management on PowerMac G5 (AGP)"
+ depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && ADB_PMU
+ select I2C_POWERMAC
+ help
+ This driver provides thermal control for the PowerMac G5
+ "AGP" variants (PowerMac 7,2 and 7,3)
+
+config WINDFARM_RM31
+ tristate "Support for thermal management on Xserve G5"
+ depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && ADB_PMU
+ select I2C_POWERMAC
+ help
+ This driver provides thermal control for the Xserve G5
+ (RackMac3,1)
+
config WINDFARM_PM91
tristate "Support for thermal management on PowerMac9,1"
depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU
@@ -224,8 +258,49 @@ config WINDFARM_PM112
which are the recent dual and quad G5 machines using the
970MP dual-core processor.
+config WINDFARM_PM121
+ tristate "Support for thermal management on PowerMac12,1"
+ depends on WINDFARM && I2C && PMAC_SMU
+ select I2C_POWERMAC
+ help
+ This driver provides thermal control for the PowerMac12,1
+ which is the iMac G5 (iSight).
+
config ANSLCD
tristate "Support for ANS LCD display"
depends on ADB_CUDA && PPC_PMAC
-endmenu
+config PMAC_RACKMETER
+ tristate "Support for Apple XServe front panel LEDs"
+ depends on PPC_PMAC
+ help
+ This driver provides some support to control the front panel
+ blue LEDs "vu-meter" of the XServer macs.
+
+config SENSORS_AMS
+ tristate "Apple Motion Sensor driver"
+ depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C)
+ select INPUT_POLLDEV
+ help
+ Support for the motion sensor included in PowerBooks. Includes
+ implementations for PMU and I2C.
+
+ This driver can also be built as a module. If so, the module
+ will be called ams.
+
+config SENSORS_AMS_PMU
+ bool "PMU variant"
+ depends on SENSORS_AMS && ADB_PMU
+ default y
+ help
+ PMU variant of motion sensor, found in late 2005 PowerBooks.
+
+config SENSORS_AMS_I2C
+ bool "I2C variant"
+ depends on SENSORS_AMS && I2C
+ default y
+ help
+ I2C variant of motion sensor, found in early 2005 PowerBooks and
+ iBooks.
+
+endif # MACINTOSH_DRIVERS
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile
index b53d45f87b0..d2f0120bc87 100644
--- a/drivers/macintosh/Makefile
+++ b/drivers/macintosh/Makefile
@@ -29,6 +29,21 @@ obj-$(CONFIG_THERM_PM72) += therm_pm72.o
obj-$(CONFIG_THERM_WINDTUNNEL) += therm_windtunnel.o
obj-$(CONFIG_THERM_ADT746X) += therm_adt746x.o
obj-$(CONFIG_WINDFARM) += windfarm_core.o
+obj-$(CONFIG_WINDFARM_PM72) += windfarm_fcu_controls.o \
+ windfarm_ad7417_sensor.o \
+ windfarm_lm75_sensor.o \
+ windfarm_max6690_sensor.o \
+ windfarm_pid.o \
+ windfarm_cpufreq_clamp.o \
+ windfarm_pm72.o
+obj-$(CONFIG_WINDFARM_RM31) += windfarm_fcu_controls.o \
+ windfarm_ad7417_sensor.o \
+ windfarm_lm75_sensor.o \
+ windfarm_lm87_sensor.o \
+ windfarm_max6690_sensor.o \
+ windfarm_pid.o \
+ windfarm_cpufreq_clamp.o \
+ windfarm_rm31.o
obj-$(CONFIG_WINDFARM_PM81) += windfarm_smu_controls.o \
windfarm_smu_sensors.o \
windfarm_lm75_sensor.o windfarm_pid.o \
@@ -42,3 +57,11 @@ obj-$(CONFIG_WINDFARM_PM112) += windfarm_pm112.o windfarm_smu_sat.o \
windfarm_smu_sensors.o \
windfarm_max6690_sensor.o \
windfarm_lm75_sensor.o windfarm_pid.o
+obj-$(CONFIG_WINDFARM_PM121) += windfarm_pm121.o windfarm_smu_sat.o \
+ windfarm_smu_controls.o \
+ windfarm_smu_sensors.o \
+ windfarm_max6690_sensor.o \
+ windfarm_lm75_sensor.o windfarm_pid.o
+obj-$(CONFIG_PMAC_RACKMETER) += rack-meter.o
+
+obj-$(CONFIG_SENSORS_AMS) += ams/
diff --git a/drivers/macintosh/adb-iop.c b/drivers/macintosh/adb-iop.c
index d56d400b6aa..f5f4da3d0b6 100644
--- a/drivers/macintosh/adb-iop.c
+++ b/drivers/macintosh/adb-iop.c
@@ -19,7 +19,6 @@
#include <linux/init.h>
#include <linux/proc_fs.h>
-#include <asm/bootinfo.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_iop.h>
@@ -30,7 +29,7 @@
/*#define DEBUG_ADB_IOP*/
-extern void iop_ism_irq(int, void *, struct pt_regs *);
+extern void iop_ism_irq(int, void *);
static struct adb_request *current_req;
static struct adb_request *last_req;
@@ -78,10 +77,10 @@ static void adb_iop_end_req(struct adb_request *req, int state)
* This will be called when a packet has been successfully sent.
*/
-static void adb_iop_complete(struct iop_msg *msg, struct pt_regs *regs)
+static void adb_iop_complete(struct iop_msg *msg)
{
struct adb_request *req;
- uint flags;
+ unsigned long flags;
local_irq_save(flags);
@@ -100,11 +99,11 @@ static void adb_iop_complete(struct iop_msg *msg, struct pt_regs *regs)
* commands or autopoll packets) are received.
*/
-static void adb_iop_listen(struct iop_msg *msg, struct pt_regs *regs)
+static void adb_iop_listen(struct iop_msg *msg)
{
struct adb_iopmsg *amsg = (struct adb_iopmsg *) msg->message;
struct adb_request *req;
- uint flags;
+ unsigned long flags;
#ifdef DEBUG_ADB_IOP
int i;
#endif
@@ -143,7 +142,7 @@ static void adb_iop_listen(struct iop_msg *msg, struct pt_regs *regs)
req->reply_len = amsg->count + 1;
memcpy(req->reply, &amsg->cmd, req->reply_len);
} else {
- adb_input(&amsg->cmd, amsg->count + 1, regs,
+ adb_input(&amsg->cmd, amsg->count + 1,
amsg->flags & ADB_IOP_AUTOPOLL);
}
memcpy(msg->reply, msg->message, IOP_MSG_LEN);
@@ -266,7 +265,7 @@ int adb_iop_autopoll(int devs)
void adb_iop_poll(void)
{
if (adb_iop_state == idle) adb_iop_start();
- iop_ism_irq(0, (void *) ADB_IOP, NULL);
+ iop_ism_irq(0, (void *) ADB_IOP);
}
int adb_iop_reset_bus(void)
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
index 360f93f6fcd..9e9c56758a0 100644
--- a/drivers/macintosh/adb.c
+++ b/drivers/macintosh/adb.c
@@ -24,7 +24,6 @@
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/sched.h>
-#include <linux/smp_lock.h>
#include <linux/adb.h>
#include <linux/cuda.h>
#include <linux/pmu.h>
@@ -35,16 +34,17 @@
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/device.h>
+#include <linux/kthread.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
-#include <asm/uaccess.h>
-#include <asm/semaphore.h>
+#include <linux/uaccess.h>
#ifdef CONFIG_PPC
#include <asm/prom.h>
#include <asm/machdep.h>
#endif
-EXPORT_SYMBOL(adb_controller);
EXPORT_SYMBOL(adb_client_list);
extern struct adb_driver via_macii_driver;
@@ -54,6 +54,7 @@ extern struct adb_driver adb_iop_driver;
extern struct adb_driver via_pmu_driver;
extern struct adb_driver macio_adb_driver;
+static DEFINE_MUTEX(adb_mutex);
static struct adb_driver *adb_driver_list[] = {
#ifdef CONFIG_ADB_MACII
&via_macii_driver,
@@ -78,39 +79,29 @@ static struct adb_driver *adb_driver_list[] = {
static struct class *adb_dev_class;
-struct adb_driver *adb_controller;
+static struct adb_driver *adb_controller;
BLOCKING_NOTIFIER_HEAD(adb_client_list);
static int adb_got_sleep;
static int adb_inited;
-static pid_t adb_probe_task_pid;
-static DECLARE_MUTEX(adb_probe_mutex);
-static struct completion adb_probe_task_comp;
+static DEFINE_SEMAPHORE(adb_probe_mutex);
static int sleepy_trackpad;
static int autopoll_devs;
int __adb_probe_sync;
-#ifdef CONFIG_PM
-static int adb_notify_sleep(struct pmu_sleep_notifier *self, int when);
-static struct pmu_sleep_notifier adb_sleep_notifier = {
- adb_notify_sleep,
- SLEEP_LEVEL_ADB,
-};
-#endif
-
static int adb_scan_bus(void);
static int do_adb_reset_bus(void);
static void adbdev_init(void);
static int try_handler_change(int, int);
static struct adb_handler {
- void (*handler)(unsigned char *, int, struct pt_regs *, int);
+ void (*handler)(unsigned char *, int, int);
int original_address;
int handler_id;
int busy;
} adb_handler[16];
/*
- * The adb_handler_sem mutex protects all accesses to the original_address
+ * The adb_handler_mutex mutex protects all accesses to the original_address
* and handler_id fields of adb_handler[i] for all i, and changes to the
* handler field.
* Accesses to the handler field are protected by the adb_handler_lock
@@ -118,7 +109,7 @@ static struct adb_handler {
* time adb_unregister returns, we know that the old handler isn't being
* called.
*/
-static DECLARE_MUTEX(adb_handler_sem);
+static DEFINE_MUTEX(adb_handler_mutex);
static DEFINE_RWLOCK(adb_handler_lock);
#if 0
@@ -134,16 +125,6 @@ static void printADBreply(struct adb_request *req)
}
#endif
-
-static __inline__ void adb_wait_ms(unsigned int ms)
-{
- if (current->pid && adb_probe_task_pid &&
- adb_probe_task_pid == current->pid)
- msleep(ms);
- else
- mdelay(ms);
-}
-
static int adb_scan_bus(void)
{
int i, highFree=0, noMovement;
@@ -212,8 +193,7 @@ static int adb_scan_bus(void)
break;
noMovement = 0;
- }
- else {
+ } else {
/*
* No devices left at address i; move the
* one(s) we moved to `highFree' back to i.
@@ -248,31 +228,22 @@ static int adb_scan_bus(void)
static int
adb_probe_task(void *x)
{
- sigset_t blocked;
-
- strcpy(current->comm, "kadbprobe");
-
- sigfillset(&blocked);
- sigprocmask(SIG_BLOCK, &blocked, NULL);
- flush_signals(current);
-
printk(KERN_INFO "adb: starting probe task...\n");
do_adb_reset_bus();
printk(KERN_INFO "adb: finished probe task...\n");
-
- adb_probe_task_pid = 0;
+
up(&adb_probe_mutex);
-
+
return 0;
}
static void
-__adb_probe_task(void *data)
+__adb_probe_task(struct work_struct *bullshit)
{
- adb_probe_task_pid = kernel_thread(adb_probe_task, NULL, SIGCHLD | CLONE_KERNEL);
+ kthread_run(adb_probe_task, NULL, "kadbprobe");
}
-static DECLARE_WORK(adb_reset_work, __adb_probe_task, NULL);
+static DECLARE_WORK(adb_reset_work, __adb_probe_task);
int
adb_reset_bus(void)
@@ -287,7 +258,57 @@ adb_reset_bus(void)
return 0;
}
-int __init adb_init(void)
+#ifdef CONFIG_PM
+/*
+ * notify clients before sleep
+ */
+static int __adb_suspend(struct platform_device *dev, pm_message_t state)
+{
+ adb_got_sleep = 1;
+ /* We need to get a lock on the probe thread */
+ down(&adb_probe_mutex);
+ /* Stop autopoll */
+ if (adb_controller->autopoll)
+ adb_controller->autopoll(0);
+ blocking_notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL);
+
+ return 0;
+}
+
+static int adb_suspend(struct device *dev)
+{
+ return __adb_suspend(to_platform_device(dev), PMSG_SUSPEND);
+}
+
+static int adb_freeze(struct device *dev)
+{
+ return __adb_suspend(to_platform_device(dev), PMSG_FREEZE);
+}
+
+static int adb_poweroff(struct device *dev)
+{
+ return __adb_suspend(to_platform_device(dev), PMSG_HIBERNATE);
+}
+
+/*
+ * reset bus after sleep
+ */
+static int __adb_resume(struct platform_device *dev)
+{
+ adb_got_sleep = 0;
+ up(&adb_probe_mutex);
+ adb_reset_bus();
+
+ return 0;
+}
+
+static int adb_resume(struct device *dev)
+{
+ return __adb_resume(to_platform_device(dev));
+}
+#endif /* CONFIG_PM */
+
+static int __init adb_init(void)
{
struct adb_driver *driver;
int i;
@@ -315,75 +336,30 @@ int __init adb_init(void)
break;
}
}
- if ((adb_controller == NULL) || adb_controller->init()) {
- printk(KERN_WARNING "Warning: no ADB interface detected\n");
+ if (adb_controller != NULL && adb_controller->init &&
+ adb_controller->init())
adb_controller = NULL;
+ if (adb_controller == NULL) {
+ printk(KERN_WARNING "Warning: no ADB interface detected\n");
} else {
-#ifdef CONFIG_PM
- pmu_register_sleep_notifier(&adb_sleep_notifier);
-#endif /* CONFIG_PM */
#ifdef CONFIG_PPC
- if (machine_is_compatible("AAPL,PowerBook1998") ||
- machine_is_compatible("PowerBook1,1"))
+ if (of_machine_is_compatible("AAPL,PowerBook1998") ||
+ of_machine_is_compatible("PowerBook1,1"))
sleepy_trackpad = 1;
#endif /* CONFIG_PPC */
- init_completion(&adb_probe_task_comp);
+
adbdev_init();
adb_reset_bus();
}
return 0;
}
-__initcall(adb_init);
-
-#ifdef CONFIG_PM
-/*
- * notify clients before sleep and reset bus afterwards
- */
-int
-adb_notify_sleep(struct pmu_sleep_notifier *self, int when)
-{
- int ret;
-
- switch (when) {
- case PBOOK_SLEEP_REQUEST:
- adb_got_sleep = 1;
- /* We need to get a lock on the probe thread */
- down(&adb_probe_mutex);
- /* Stop autopoll */
- if (adb_controller->autopoll)
- adb_controller->autopoll(0);
- ret = blocking_notifier_call_chain(&adb_client_list,
- ADB_MSG_POWERDOWN, NULL);
- if (ret & NOTIFY_STOP_MASK) {
- up(&adb_probe_mutex);
- return PBOOK_SLEEP_REFUSE;
- }
- break;
- case PBOOK_SLEEP_REJECT:
- if (adb_got_sleep) {
- adb_got_sleep = 0;
- up(&adb_probe_mutex);
- adb_reset_bus();
- }
- break;
-
- case PBOOK_SLEEP_NOW:
- break;
- case PBOOK_WAKE:
- adb_got_sleep = 0;
- up(&adb_probe_mutex);
- adb_reset_bus();
- break;
- }
- return PBOOK_SLEEP_OK;
-}
-#endif /* CONFIG_PM */
+device_initcall(adb_init);
static int
do_adb_reset_bus(void)
{
- int ret, nret;
+ int ret;
if (adb_controller == NULL)
return -ENXIO;
@@ -391,20 +367,15 @@ do_adb_reset_bus(void)
if (adb_controller->autopoll)
adb_controller->autopoll(0);
- nret = blocking_notifier_call_chain(&adb_client_list,
- ADB_MSG_PRE_RESET, NULL);
- if (nret & NOTIFY_STOP_MASK) {
- if (adb_controller->autopoll)
- adb_controller->autopoll(autopoll_devs);
- return -EBUSY;
- }
+ blocking_notifier_call_chain(&adb_client_list,
+ ADB_MSG_PRE_RESET, NULL);
if (sleepy_trackpad) {
/* Let the trackpad settle down */
- adb_wait_ms(500);
+ msleep(500);
}
- down(&adb_handler_sem);
+ mutex_lock(&adb_handler_mutex);
write_lock_irq(&adb_handler_lock);
memset(adb_handler, 0, sizeof(adb_handler));
write_unlock_irq(&adb_handler_lock);
@@ -417,7 +388,7 @@ do_adb_reset_bus(void)
if (sleepy_trackpad) {
/* Let the trackpad settle down */
- adb_wait_ms(1500);
+ msleep(1500);
}
if (!ret) {
@@ -425,12 +396,10 @@ do_adb_reset_bus(void)
if (adb_controller->autopoll)
adb_controller->autopoll(autopoll_devs);
}
- up(&adb_handler_sem);
+ mutex_unlock(&adb_handler_mutex);
- nret = blocking_notifier_call_chain(&adb_client_list,
- ADB_MSG_POST_RESET, NULL);
- if (nret & NOTIFY_STOP_MASK)
- return -EBUSY;
+ blocking_notifier_call_chain(&adb_client_list,
+ ADB_MSG_POST_RESET, NULL);
return ret;
}
@@ -443,41 +412,27 @@ adb_poll(void)
adb_controller->poll();
}
-static void
-adb_probe_wakeup(struct adb_request *req)
+static void adb_sync_req_done(struct adb_request *req)
{
- complete(&adb_probe_task_comp);
-}
+ struct completion *comp = req->arg;
-/* Static request used during probe */
-static struct adb_request adb_sreq;
-static unsigned long adb_sreq_lock; // Use semaphore ! */
+ complete(comp);
+}
int
adb_request(struct adb_request *req, void (*done)(struct adb_request *),
int flags, int nbytes, ...)
{
va_list list;
- int i, use_sreq;
+ int i;
int rc;
+ struct completion comp;
if ((adb_controller == NULL) || (adb_controller->send_request == NULL))
return -ENXIO;
if (nbytes < 1)
return -EINVAL;
- if (req == NULL && (flags & ADBREQ_NOSEND))
- return -EINVAL;
-
- if (req == NULL) {
- if (test_and_set_bit(0,&adb_sreq_lock)) {
- printk("adb.c: Warning: contention on static request !\n");
- return -EPERM;
- }
- req = &adb_sreq;
- flags |= ADBREQ_SYNC;
- use_sreq = 1;
- } else
- use_sreq = 0;
+
req->nbytes = nbytes+1;
req->done = done;
req->reply_expected = flags & ADBREQ_REPLY;
@@ -490,25 +445,18 @@ adb_request(struct adb_request *req, void (*done)(struct adb_request *),
if (flags & ADBREQ_NOSEND)
return 0;
- /* Synchronous requests send from the probe thread cause it to
- * block. Beware that the "done" callback will be overriden !
- */
- if ((flags & ADBREQ_SYNC) &&
- (current->pid && adb_probe_task_pid &&
- adb_probe_task_pid == current->pid)) {
- req->done = adb_probe_wakeup;
- rc = adb_controller->send_request(req, 0);
- if (rc || req->complete)
- goto bail;
- wait_for_completion(&adb_probe_task_comp);
- rc = 0;
- goto bail;
+ /* Synchronous requests block using an on-stack completion */
+ if (flags & ADBREQ_SYNC) {
+ WARN_ON(done);
+ req->done = adb_sync_req_done;
+ req->arg = &comp;
+ init_completion(&comp);
}
- rc = adb_controller->send_request(req, flags & ADBREQ_SYNC);
-bail:
- if (use_sreq)
- clear_bit(0, &adb_sreq_lock);
+ rc = adb_controller->send_request(req, 0);
+
+ if ((flags & ADBREQ_SYNC) && !rc && !req->complete)
+ wait_for_completion(&comp);
return rc;
}
@@ -522,11 +470,11 @@ bail:
the handler_id id it doesn't match. */
int
adb_register(int default_id, int handler_id, struct adb_ids *ids,
- void (*handler)(unsigned char *, int, struct pt_regs *, int))
+ void (*handler)(unsigned char *, int, int))
{
int i;
- down(&adb_handler_sem);
+ mutex_lock(&adb_handler_mutex);
ids->nids = 0;
for (i = 1; i < 16; i++) {
if ((adb_handler[i].original_address == default_id) &&
@@ -544,7 +492,7 @@ adb_register(int default_id, int handler_id, struct adb_ids *ids,
ids->id[ids->nids++] = i;
}
}
- up(&adb_handler_sem);
+ mutex_unlock(&adb_handler_mutex);
return ids->nids;
}
@@ -553,7 +501,7 @@ adb_unregister(int index)
{
int ret = -ENODEV;
- down(&adb_handler_sem);
+ mutex_lock(&adb_handler_mutex);
write_lock_irq(&adb_handler_lock);
if (adb_handler[index].handler) {
while(adb_handler[index].busy) {
@@ -565,18 +513,18 @@ adb_unregister(int index)
adb_handler[index].handler = NULL;
}
write_unlock_irq(&adb_handler_lock);
- up(&adb_handler_sem);
+ mutex_unlock(&adb_handler_mutex);
return ret;
}
void
-adb_input(unsigned char *buf, int nb, struct pt_regs *regs, int autopoll)
+adb_input(unsigned char *buf, int nb, int autopoll)
{
int i, id;
- static int dump_adb_input = 0;
+ static int dump_adb_input;
unsigned long flags;
- void (*handler)(unsigned char *, int, struct pt_regs *, int);
+ void (*handler)(unsigned char *, int, int);
/* We skip keystrokes and mouse moves when the sleep process
* has been started. We stop autopoll, but this is another security
@@ -597,7 +545,7 @@ adb_input(unsigned char *buf, int nb, struct pt_regs *regs, int autopoll)
adb_handler[id].busy = 1;
write_unlock_irqrestore(&adb_handler_lock, flags);
if (handler != NULL) {
- (*handler)(buf, nb, regs, autopoll);
+ (*handler)(buf, nb, autopoll);
wmb();
adb_handler[id].busy = 0;
}
@@ -629,19 +577,19 @@ adb_try_handler_change(int address, int new_id)
{
int ret;
- down(&adb_handler_sem);
+ mutex_lock(&adb_handler_mutex);
ret = try_handler_change(address, new_id);
- up(&adb_handler_sem);
+ mutex_unlock(&adb_handler_mutex);
return ret;
}
int
adb_get_infos(int address, int *original_address, int *handler_id)
{
- down(&adb_handler_sem);
+ mutex_lock(&adb_handler_mutex);
*original_address = adb_handler[address].original_address;
*handler_id = adb_handler[address].handler_id;
- up(&adb_handler_sem);
+ mutex_unlock(&adb_handler_mutex);
return (*original_address != 0);
}
@@ -695,15 +643,14 @@ do_adb_query(struct adb_request *req)
{
int ret = -EINVAL;
- switch(req->data[1])
- {
+ switch(req->data[1]) {
case ADB_QUERY_GETDEVINFO:
if (req->nbytes < 3)
break;
- down(&adb_handler_sem);
+ mutex_lock(&adb_handler_mutex);
req->reply[0] = adb_handler[req->data[2]].original_address;
req->reply[1] = adb_handler[req->data[2]].handler_id;
- up(&adb_handler_sem);
+ mutex_unlock(&adb_handler_mutex);
req->complete = 1;
req->reply_len = 2;
adb_write_done(req);
@@ -716,12 +663,18 @@ do_adb_query(struct adb_request *req)
static int adb_open(struct inode *inode, struct file *file)
{
struct adbdev_state *state;
+ int ret = 0;
- if (iminor(inode) > 0 || adb_controller == NULL)
- return -ENXIO;
+ mutex_lock(&adb_mutex);
+ if (iminor(inode) > 0 || adb_controller == NULL) {
+ ret = -ENXIO;
+ goto out;
+ }
state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL);
- if (state == 0)
- return -ENOMEM;
+ if (state == 0) {
+ ret = -ENOMEM;
+ goto out;
+ }
file->private_data = state;
spin_lock_init(&state->lock);
atomic_set(&state->n_pending, 0);
@@ -729,7 +682,9 @@ static int adb_open(struct inode *inode, struct file *file)
init_waitqueue_head(&state->wait_queue);
state->inuse = 1;
- return 0;
+out:
+ mutex_unlock(&adb_mutex);
+ return ret;
}
static int adb_release(struct inode *inode, struct file *file)
@@ -737,7 +692,7 @@ static int adb_release(struct inode *inode, struct file *file)
struct adbdev_state *state = file->private_data;
unsigned long flags;
- lock_kernel();
+ mutex_lock(&adb_mutex);
if (state) {
file->private_data = NULL;
spin_lock_irqsave(&state->lock, flags);
@@ -750,7 +705,7 @@ static int adb_release(struct inode *inode, struct file *file)
spin_unlock_irqrestore(&state->lock, flags);
}
}
- unlock_kernel();
+ mutex_unlock(&adb_mutex);
return 0;
}
@@ -760,7 +715,7 @@ static ssize_t adb_read(struct file *file, char __user *buf,
int ret = 0;
struct adbdev_state *state = file->private_data;
struct adb_request *req;
- wait_queue_t wait = __WAITQUEUE_INITIALIZER(wait,current);
+ DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
if (count < 2)
@@ -773,7 +728,7 @@ static ssize_t adb_read(struct file *file, char __user *buf,
req = NULL;
spin_lock_irqsave(&state->lock, flags);
add_wait_queue(&state->wait_queue, &wait);
- current->state = TASK_INTERRUPTIBLE;
+ set_current_state(TASK_INTERRUPTIBLE);
for (;;) {
req = state->completed;
@@ -797,7 +752,7 @@ static ssize_t adb_read(struct file *file, char __user *buf,
spin_lock_irqsave(&state->lock, flags);
}
- current->state = TASK_RUNNING;
+ set_current_state(TASK_RUNNING);
remove_wait_queue(&state->wait_queue, &wait);
spin_unlock_irqrestore(&state->lock, flags);
@@ -828,7 +783,7 @@ static ssize_t adb_write(struct file *file, const char __user *buf,
if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT;
- req = (struct adb_request *) kmalloc(sizeof(struct adb_request),
+ req = kmalloc(sizeof(struct adb_request),
GFP_KERNEL);
if (req == NULL)
return -ENOMEM;
@@ -857,8 +812,8 @@ static ssize_t adb_write(struct file *file, const char __user *buf,
}
/* Special case for ADB_BUSRESET request, all others are sent to
the controller */
- else if ((req->data[0] == ADB_PACKET)&&(count > 1)
- &&(req->data[1] == ADB_BUSRESET)) {
+ else if ((req->data[0] == ADB_PACKET) && (count > 1)
+ && (req->data[1] == ADB_BUSRESET)) {
ret = do_adb_reset_bus();
up(&adb_probe_mutex);
atomic_dec(&state->n_pending);
@@ -885,7 +840,7 @@ out:
return ret;
}
-static struct file_operations adb_fops = {
+static const struct file_operations adb_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = adb_read,
@@ -894,7 +849,40 @@ static struct file_operations adb_fops = {
.release = adb_release,
};
-static void
+#ifdef CONFIG_PM
+static const struct dev_pm_ops adb_dev_pm_ops = {
+ .suspend = adb_suspend,
+ .resume = adb_resume,
+ /* Hibernate hooks */
+ .freeze = adb_freeze,
+ .thaw = adb_resume,
+ .poweroff = adb_poweroff,
+ .restore = adb_resume,
+};
+#endif
+
+static struct platform_driver adb_pfdrv = {
+ .driver = {
+ .name = "adb",
+#ifdef CONFIG_PM
+ .pm = &adb_dev_pm_ops,
+#endif
+ },
+};
+
+static struct platform_device adb_pfdev = {
+ .name = "adb",
+};
+
+static int __init
+adb_dummy_probe(struct platform_device *dev)
+{
+ if (dev == &adb_pfdev)
+ return 0;
+ return -ENODEV;
+}
+
+static void __init
adbdev_init(void)
{
if (register_chrdev(ADB_MAJOR, "adb", &adb_fops)) {
@@ -905,5 +893,8 @@ adbdev_init(void)
adb_dev_class = class_create(THIS_MODULE, "adb");
if (IS_ERR(adb_dev_class))
return;
- class_device_create(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0), NULL, "adb");
+ device_create(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0), NULL, "adb");
+
+ platform_device_register(&adb_pfdev);
+ platform_driver_probe(&adb_pfdrv, adb_dummy_probe);
}
diff --git a/drivers/macintosh/adbhid.c b/drivers/macintosh/adbhid.c
index efd51e01c06..09d72bb00d1 100644
--- a/drivers/macintosh/adbhid.c
+++ b/drivers/macintosh/adbhid.c
@@ -1,5 +1,5 @@
/*
- * drivers/input/adbhid.c
+ * drivers/macintosh/adbhid.c
*
* ADB HID driver for Power Macintosh computers.
*
@@ -52,6 +52,11 @@
MODULE_AUTHOR("Franz Sirl <Franz.Sirl-kernel@lauterbach.com>");
+static int restore_capslock_events;
+module_param(restore_capslock_events, int, 0644);
+MODULE_PARM_DESC(restore_capslock_events,
+ "Produce keypress events for capslock on both keyup and keydown.");
+
#define KEYB_KEYREG 0 /* register # for key up/down data */
#define KEYB_LEDREG 2 /* register # for leds on ADB keyboard */
#define MOUSE_DATAREG 0 /* reg# for movement/button codes from mouse */
@@ -70,7 +75,7 @@ static struct notifier_block adbhid_adb_notifier = {
#define ADB_KEY_POWER_OLD 0x7e
#define ADB_KEY_POWER 0x7f
-u8 adb_to_linux_keycodes[128] = {
+static const u16 adb_to_linux_keycodes[128] = {
/* 0x00 */ KEY_A, /* 30 */
/* 0x01 */ KEY_S, /* 31 */
/* 0x02 */ KEY_D, /* 32 */
@@ -134,7 +139,7 @@ u8 adb_to_linux_keycodes[128] = {
/* 0x3c */ KEY_RIGHT, /* 106 */
/* 0x3d */ KEY_DOWN, /* 108 */
/* 0x3e */ KEY_UP, /* 103 */
- /* 0x3f */ 0,
+ /* 0x3f */ KEY_FN, /* 0x1d0 */
/* 0x40 */ 0,
/* 0x41 */ KEY_KPDOT, /* 83 */
/* 0x42 */ 0,
@@ -208,21 +213,25 @@ struct adbhid {
int original_handler_id;
int current_handler_id;
int mouse_kind;
- unsigned char *keycode;
+ u16 *keycode;
char name[64];
char phys[32];
int flags;
};
-#define FLAG_FN_KEY_PRESSED 0x00000001
-#define FLAG_POWER_FROM_FN 0x00000002
-#define FLAG_EMU_FWDEL_DOWN 0x00000004
+#define FLAG_FN_KEY_PRESSED 0x00000001
+#define FLAG_POWER_FROM_FN 0x00000002
+#define FLAG_EMU_FWDEL_DOWN 0x00000004
+#define FLAG_CAPSLOCK_TRANSLATE 0x00000008
+#define FLAG_CAPSLOCK_DOWN 0x00000010
+#define FLAG_CAPSLOCK_IGNORE_NEXT 0x00000020
+#define FLAG_POWER_KEY_PRESSED 0x00000040
static struct adbhid *adbhid[16];
static void adbhid_probe(void);
-static void adbhid_input_keycode(int, int, int, struct pt_regs *);
+static void adbhid_input_keycode(int, int, int);
static void init_trackpad(int id);
static void init_trackball(int id);
@@ -253,7 +262,7 @@ static struct adb_ids buttons_ids;
#define ADBMOUSE_MACALLY2 9 /* MacAlly 2-button mouse */
static void
-adbhid_keyboard_input(unsigned char *data, int nb, struct pt_regs *regs, int apoll)
+adbhid_keyboard_input(unsigned char *data, int nb, int apoll)
{
int id = (data[0] >> 4) & 0x0f;
@@ -266,27 +275,67 @@ adbhid_keyboard_input(unsigned char *data, int nb, struct pt_regs *regs, int apo
/* first check this is from register 0 */
if (nb != 3 || (data[0] & 3) != KEYB_KEYREG)
return; /* ignore it */
- adbhid_input_keycode(id, data[1], 0, regs);
+ adbhid_input_keycode(id, data[1], 0);
if (!(data[2] == 0xff || (data[2] == 0x7f && data[1] == 0x7f)))
- adbhid_input_keycode(id, data[2], 0, regs);
+ adbhid_input_keycode(id, data[2], 0);
}
static void
-adbhid_input_keycode(int id, int keycode, int repeat, struct pt_regs *regs)
+adbhid_input_keycode(int id, int scancode, int repeat)
{
struct adbhid *ahid = adbhid[id];
- int up_flag;
-
- up_flag = (keycode & 0x80);
- keycode &= 0x7f;
+ int keycode, up_flag, key;
+
+ keycode = scancode & 0x7f;
+ up_flag = scancode & 0x80;
+
+ if (restore_capslock_events) {
+ if (keycode == ADB_KEY_CAPSLOCK && !up_flag) {
+ /* Key pressed, turning on the CapsLock LED.
+ * The next 0xff will be interpreted as a release. */
+ if (ahid->flags & FLAG_CAPSLOCK_IGNORE_NEXT) {
+ /* Throw away this key event if it happens
+ * just after resume. */
+ ahid->flags &= ~FLAG_CAPSLOCK_IGNORE_NEXT;
+ return;
+ } else {
+ ahid->flags |= FLAG_CAPSLOCK_TRANSLATE
+ | FLAG_CAPSLOCK_DOWN;
+ }
+ } else if (scancode == 0xff &&
+ !(ahid->flags & FLAG_POWER_KEY_PRESSED)) {
+ /* Scancode 0xff usually signifies that the capslock
+ * key was either pressed or released, or that the
+ * power button was released. */
+ if (ahid->flags & FLAG_CAPSLOCK_TRANSLATE) {
+ keycode = ADB_KEY_CAPSLOCK;
+ if (ahid->flags & FLAG_CAPSLOCK_DOWN) {
+ /* Key released */
+ up_flag = 1;
+ ahid->flags &= ~FLAG_CAPSLOCK_DOWN;
+ } else {
+ /* Key pressed */
+ up_flag = 0;
+ ahid->flags &= ~FLAG_CAPSLOCK_TRANSLATE;
+ }
+ } else {
+ printk(KERN_INFO "Spurious caps lock event "
+ "(scancode 0xff).\n");
+ }
+ }
+ }
switch (keycode) {
- case ADB_KEY_CAPSLOCK: /* Generate down/up events for CapsLock everytime. */
- input_regs(ahid->input, regs);
- input_report_key(ahid->input, KEY_CAPSLOCK, 1);
- input_report_key(ahid->input, KEY_CAPSLOCK, 0);
- input_sync(ahid->input);
- return;
+ case ADB_KEY_CAPSLOCK:
+ if (!restore_capslock_events) {
+ /* Generate down/up events for CapsLock every time. */
+ input_report_key(ahid->input, KEY_CAPSLOCK, 1);
+ input_sync(ahid->input);
+ input_report_key(ahid->input, KEY_CAPSLOCK, 0);
+ input_sync(ahid->input);
+ return;
+ }
+ break;
#ifdef CONFIG_PPC_PMAC
case ADB_KEY_POWER_OLD: /* Power key on PBook 3400 needs remapping */
switch(pmac_call_feature(PMAC_FTR_GET_MB_INFO,
@@ -297,7 +346,13 @@ adbhid_input_keycode(int id, int keycode, int repeat, struct pt_regs *regs)
keycode = ADB_KEY_POWER;
}
break;
- case ADB_KEY_POWER:
+ case ADB_KEY_POWER:
+ /* Keep track of the power key state */
+ if (up_flag)
+ ahid->flags &= ~FLAG_POWER_KEY_PRESSED;
+ else
+ ahid->flags |= FLAG_POWER_KEY_PRESSED;
+
/* Fn + Command will produce a bogus "power" keycode */
if (ahid->flags & FLAG_FN_KEY_PRESSED) {
keycode = ADB_KEY_CMD;
@@ -322,8 +377,7 @@ adbhid_input_keycode(int id, int keycode, int repeat, struct pt_regs *regs)
}
} else
ahid->flags |= FLAG_FN_KEY_PRESSED;
- /* Swallow the key press */
- return;
+ break;
case ADB_KEY_DEL:
/* Emulate Fn+delete = forward delete */
if (ahid->flags & FLAG_FN_KEY_PRESSED) {
@@ -337,10 +391,9 @@ adbhid_input_keycode(int id, int keycode, int repeat, struct pt_regs *regs)
#endif /* CONFIG_PPC_PMAC */
}
- if (adbhid[id]->keycode[keycode]) {
- input_regs(adbhid[id]->input, regs);
- input_report_key(adbhid[id]->input,
- adbhid[id]->keycode[keycode], !up_flag);
+ key = adbhid[id]->keycode[keycode];
+ if (key) {
+ input_report_key(adbhid[id]->input, key, !up_flag);
input_sync(adbhid[id]->input);
} else
printk(KERN_INFO "Unhandled ADB key (scancode %#02x) %s.\n", keycode,
@@ -349,7 +402,7 @@ adbhid_input_keycode(int id, int keycode, int repeat, struct pt_regs *regs)
}
static void
-adbhid_mouse_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll)
+adbhid_mouse_input(unsigned char *data, int nb, int autopoll)
{
int id = (data[0] >> 4) & 0x0f;
@@ -432,8 +485,6 @@ adbhid_mouse_input(unsigned char *data, int nb, struct pt_regs *regs, int autopo
break;
}
- input_regs(adbhid[id]->input, regs);
-
input_report_key(adbhid[id]->input, BTN_LEFT, !((data[1] >> 7) & 1));
input_report_key(adbhid[id]->input, BTN_MIDDLE, !((data[2] >> 7) & 1));
@@ -449,7 +500,7 @@ adbhid_mouse_input(unsigned char *data, int nb, struct pt_regs *regs, int autopo
}
static void
-adbhid_buttons_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll)
+adbhid_buttons_input(unsigned char *data, int nb, int autopoll)
{
int id = (data[0] >> 4) & 0x0f;
@@ -458,8 +509,6 @@ adbhid_buttons_input(unsigned char *data, int nb, struct pt_regs *regs, int auto
return;
}
- input_regs(adbhid[id]->input, regs);
-
switch (adbhid[id]->original_handler_id) {
default:
case 0x02: /* Adjustable keyboard button device */
@@ -580,8 +629,8 @@ static struct adb_request led_request;
static int leds_pending[16];
static int leds_req_pending;
static int pending_devs[16];
-static int pending_led_start=0;
-static int pending_led_end=0;
+static int pending_led_start;
+static int pending_led_end;
static DEFINE_SPINLOCK(leds_lock);
static void leds_done(struct adb_request *req)
@@ -634,28 +683,43 @@ static void real_leds(unsigned char leds, int device)
*/
static int adbhid_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
- struct adbhid *adbhid = dev->private;
+ struct adbhid *adbhid = input_get_drvdata(dev);
unsigned char leds;
switch (type) {
case EV_LED:
- leds = (test_bit(LED_SCROLLL, dev->led) ? 4 : 0)
- | (test_bit(LED_NUML, dev->led) ? 1 : 0)
- | (test_bit(LED_CAPSL, dev->led) ? 2 : 0);
- real_leds(leds, adbhid->id);
- return 0;
+ leds = (test_bit(LED_SCROLLL, dev->led) ? 4 : 0) |
+ (test_bit(LED_NUML, dev->led) ? 1 : 0) |
+ (test_bit(LED_CAPSL, dev->led) ? 2 : 0);
+ real_leds(leds, adbhid->id);
+ return 0;
}
return -1;
}
+static void
+adbhid_kbd_capslock_remember(void)
+{
+ struct adbhid *ahid;
+ int i;
+
+ for (i = 1; i < 16; i++) {
+ ahid = adbhid[i];
+
+ if (ahid && ahid->id == ADB_KEYBOARD)
+ if (ahid->flags & FLAG_CAPSLOCK_TRANSLATE)
+ ahid->flags |= FLAG_CAPSLOCK_IGNORE_NEXT;
+ }
+}
+
static int
adb_message_handler(struct notifier_block *this, unsigned long code, void *x)
{
switch (code) {
case ADB_MSG_PRE_RESET:
case ADB_MSG_POWERDOWN:
- /* Stop the repeat timer. Autopoll is already off at this point */
+ /* Stop the repeat timer. Autopoll is already off at this point */
{
int i;
for (i = 1; i < 16; i++) {
@@ -665,8 +729,17 @@ adb_message_handler(struct notifier_block *this, unsigned long code, void *x)
}
/* Stop pending led requests */
- while(leds_req_pending)
+ while (leds_req_pending)
adb_poll();
+
+ /* After resume, and if the capslock LED is on, the PMU will
+ * send a "capslock down" key event. This confuses the
+ * restore_capslock_events logic. Remember if the capslock
+ * LED was on before suspend so the unwanted key event can
+ * be ignored after resume. */
+ if (restore_capslock_events)
+ adbhid_kbd_capslock_remember();
+
break;
case ADB_MSG_POST_RESET:
@@ -695,7 +768,6 @@ adbhid_input_register(int id, int default_id, int original_handler_id,
if (!hid || !input_dev) {
err = -ENOMEM;
goto fail;
-
}
sprintf(hid->phys, "adb%d:%d.%02x/input", id, default_id, original_handler_id);
@@ -706,7 +778,7 @@ adbhid_input_register(int id, int default_id, int original_handler_id,
hid->current_handler_id = current_handler_id;
hid->mouse_kind = mouse_kind;
hid->flags = 0;
- input_dev->private = hid;
+ input_set_drvdata(input_dev, hid);
input_dev->name = hid->name;
input_dev->phys = hid->phys;
input_dev->id.bustype = BUS_ADB;
@@ -761,26 +833,30 @@ adbhid_input_register(int id, int default_id, int original_handler_id,
if (hid->keycode[i])
set_bit(hid->keycode[i], input_dev->keybit);
- input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
- input_dev->ledbit[0] = BIT(LED_SCROLLL) | BIT(LED_CAPSL) | BIT(LED_NUML);
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
+ BIT_MASK(EV_REP);
+ input_dev->ledbit[0] = BIT_MASK(LED_SCROLLL) |
+ BIT_MASK(LED_CAPSL) | BIT_MASK(LED_NUML);
input_dev->event = adbhid_kbd_event;
- input_dev->keycodemax = 127;
- input_dev->keycodesize = 1;
+ input_dev->keycodemax = KEY_FN;
+ input_dev->keycodesize = sizeof(hid->keycode[0]);
break;
case ADB_MOUSE:
sprintf(hid->name, "ADB mouse");
- input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
- input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
- input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+ input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
break;
case ADB_MISC:
switch (original_handler_id) {
case 0x02: /* Adjustable keyboard button device */
sprintf(hid->name, "ADB adjustable keyboard buttons");
- input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) |
+ BIT_MASK(EV_REP);
set_bit(KEY_SOUND, input_dev->keybit);
set_bit(KEY_MUTE, input_dev->keybit);
set_bit(KEY_VOLUMEUP, input_dev->keybit);
@@ -788,7 +864,8 @@ adbhid_input_register(int id, int default_id, int original_handler_id,
break;
case 0x1f: /* Powerbook button device */
sprintf(hid->name, "ADB Powerbook buttons");
- input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) |
+ BIT_MASK(EV_REP);
set_bit(KEY_MUTE, input_dev->keybit);
set_bit(KEY_VOLUMEUP, input_dev->keybit);
set_bit(KEY_VOLUMEDOWN, input_dev->keybit);
@@ -813,7 +890,9 @@ adbhid_input_register(int id, int default_id, int original_handler_id,
input_dev->keycode = hid->keycode;
- input_register_device(input_dev);
+ err = input_register_device(input_dev);
+ if (err)
+ goto fail;
if (default_id == ADB_KEYBOARD) {
/* HACK WARNING!! This should go away as soon there is an utility
@@ -826,7 +905,10 @@ adbhid_input_register(int id, int default_id, int original_handler_id,
return 0;
fail: input_free_device(input_dev);
- kfree(hid);
+ if (hid) {
+ kfree(hid->keycode);
+ kfree(hid);
+ }
adbhid[id] = NULL;
return err;
}
diff --git a/drivers/macintosh/ams/Makefile b/drivers/macintosh/ams/Makefile
new file mode 100644
index 00000000000..41c95b2089d
--- /dev/null
+++ b/drivers/macintosh/ams/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for Apple Motion Sensor driver
+#
+
+ams-y := ams-core.o ams-input.o
+ams-$(CONFIG_SENSORS_AMS_PMU) += ams-pmu.o
+ams-$(CONFIG_SENSORS_AMS_I2C) += ams-i2c.o
+obj-$(CONFIG_SENSORS_AMS) += ams.o
diff --git a/drivers/macintosh/ams/ams-core.c b/drivers/macintosh/ams/ams-core.c
new file mode 100644
index 00000000000..36a4fdddd64
--- /dev/null
+++ b/drivers/macintosh/ams/ams-core.c
@@ -0,0 +1,250 @@
+/*
+ * Apple Motion Sensor driver
+ *
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/of_platform.h>
+#include <asm/pmac_pfunc.h>
+
+#include "ams.h"
+
+/* There is only one motion sensor per machine */
+struct ams ams_info;
+
+static bool verbose;
+module_param(verbose, bool, 0644);
+MODULE_PARM_DESC(verbose, "Show free falls and shocks in kernel output");
+
+/* Call with ams_info.lock held! */
+void ams_sensors(s8 *x, s8 *y, s8 *z)
+{
+ u32 orient = ams_info.vflag? ams_info.orient1 : ams_info.orient2;
+
+ if (orient & 0x80)
+ /* X and Y swapped */
+ ams_info.get_xyz(y, x, z);
+ else
+ ams_info.get_xyz(x, y, z);
+
+ if (orient & 0x04)
+ *z = ~(*z);
+ if (orient & 0x02)
+ *y = ~(*y);
+ if (orient & 0x01)
+ *x = ~(*x);
+}
+
+static ssize_t ams_show_current(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ s8 x, y, z;
+
+ mutex_lock(&ams_info.lock);
+ ams_sensors(&x, &y, &z);
+ mutex_unlock(&ams_info.lock);
+
+ return snprintf(buf, PAGE_SIZE, "%d %d %d\n", x, y, z);
+}
+
+static DEVICE_ATTR(current, S_IRUGO, ams_show_current, NULL);
+
+static void ams_handle_irq(void *data)
+{
+ enum ams_irq irq = *((enum ams_irq *)data);
+
+ spin_lock(&ams_info.irq_lock);
+
+ ams_info.worker_irqs |= irq;
+ schedule_work(&ams_info.worker);
+
+ spin_unlock(&ams_info.irq_lock);
+}
+
+static enum ams_irq ams_freefall_irq_data = AMS_IRQ_FREEFALL;
+static struct pmf_irq_client ams_freefall_client = {
+ .owner = THIS_MODULE,
+ .handler = ams_handle_irq,
+ .data = &ams_freefall_irq_data,
+};
+
+static enum ams_irq ams_shock_irq_data = AMS_IRQ_SHOCK;
+static struct pmf_irq_client ams_shock_client = {
+ .owner = THIS_MODULE,
+ .handler = ams_handle_irq,
+ .data = &ams_shock_irq_data,
+};
+
+/* Once hard disk parking is implemented in the kernel, this function can
+ * trigger it.
+ */
+static void ams_worker(struct work_struct *work)
+{
+ unsigned long flags;
+ u8 irqs_to_clear;
+
+ mutex_lock(&ams_info.lock);
+
+ spin_lock_irqsave(&ams_info.irq_lock, flags);
+ irqs_to_clear = ams_info.worker_irqs;
+
+ if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) {
+ if (verbose)
+ printk(KERN_INFO "ams: freefall detected!\n");
+
+ ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL;
+ }
+
+ if (ams_info.worker_irqs & AMS_IRQ_SHOCK) {
+ if (verbose)
+ printk(KERN_INFO "ams: shock detected!\n");
+
+ ams_info.worker_irqs &= ~AMS_IRQ_SHOCK;
+ }
+
+ spin_unlock_irqrestore(&ams_info.irq_lock, flags);
+
+ ams_info.clear_irq(irqs_to_clear);
+
+ mutex_unlock(&ams_info.lock);
+}
+
+/* Call with ams_info.lock held! */
+int ams_sensor_attach(void)
+{
+ int result;
+ const u32 *prop;
+
+ /* Get orientation */
+ prop = of_get_property(ams_info.of_node, "orientation", NULL);
+ if (!prop)
+ return -ENODEV;
+ ams_info.orient1 = *prop;
+ ams_info.orient2 = *(prop + 1);
+
+ /* Register freefall interrupt handler */
+ result = pmf_register_irq_client(ams_info.of_node,
+ "accel-int-1",
+ &ams_freefall_client);
+ if (result < 0)
+ return -ENODEV;
+
+ /* Reset saved irqs */
+ ams_info.worker_irqs = 0;
+
+ /* Register shock interrupt handler */
+ result = pmf_register_irq_client(ams_info.of_node,
+ "accel-int-2",
+ &ams_shock_client);
+ if (result < 0)
+ goto release_freefall;
+
+ /* Create device */
+ ams_info.of_dev = of_platform_device_create(ams_info.of_node, "ams", NULL);
+ if (!ams_info.of_dev) {
+ result = -ENODEV;
+ goto release_shock;
+ }
+
+ /* Create attributes */
+ result = device_create_file(&ams_info.of_dev->dev, &dev_attr_current);
+ if (result)
+ goto release_of;
+
+ ams_info.vflag = !!(ams_info.get_vendor() & 0x10);
+
+ /* Init input device */
+ result = ams_input_init();
+ if (result)
+ goto release_device_file;
+
+ return result;
+release_device_file:
+ device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
+release_of:
+ of_device_unregister(ams_info.of_dev);
+release_shock:
+ pmf_unregister_irq_client(&ams_shock_client);
+release_freefall:
+ pmf_unregister_irq_client(&ams_freefall_client);
+ return result;
+}
+
+int __init ams_init(void)
+{
+ struct device_node *np;
+
+ spin_lock_init(&ams_info.irq_lock);
+ mutex_init(&ams_info.lock);
+ INIT_WORK(&ams_info.worker, ams_worker);
+
+#ifdef CONFIG_SENSORS_AMS_I2C
+ np = of_find_node_by_name(NULL, "accelerometer");
+ if (np && of_device_is_compatible(np, "AAPL,accelerometer_1"))
+ /* Found I2C motion sensor */
+ return ams_i2c_init(np);
+#endif
+
+#ifdef CONFIG_SENSORS_AMS_PMU
+ np = of_find_node_by_name(NULL, "sms");
+ if (np && of_device_is_compatible(np, "sms"))
+ /* Found PMU motion sensor */
+ return ams_pmu_init(np);
+#endif
+ return -ENODEV;
+}
+
+void ams_sensor_detach(void)
+{
+ /* Remove input device */
+ ams_input_exit();
+
+ /* Remove attributes */
+ device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
+
+ /* Flush interrupt worker
+ *
+ * We do this after ams_info.exit(), because an interrupt might
+ * have arrived before disabling them.
+ */
+ flush_work(&ams_info.worker);
+
+ /* Remove device */
+ of_device_unregister(ams_info.of_dev);
+
+ /* Remove handler */
+ pmf_unregister_irq_client(&ams_shock_client);
+ pmf_unregister_irq_client(&ams_freefall_client);
+}
+
+static void __exit ams_exit(void)
+{
+ /* Shut down implementation */
+ ams_info.exit();
+}
+
+MODULE_AUTHOR("Stelian Pop, Michael Hanselmann");
+MODULE_DESCRIPTION("Apple Motion Sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(ams_init);
+module_exit(ams_exit);
diff --git a/drivers/macintosh/ams/ams-i2c.c b/drivers/macintosh/ams/ams-i2c.c
new file mode 100644
index 00000000000..978eda8d667
--- /dev/null
+++ b/drivers/macintosh/ams/ams-i2c.c
@@ -0,0 +1,277 @@
+/*
+ * Apple Motion Sensor driver (I2C variant)
+ *
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
+ *
+ * Clean room implementation based on the reverse engineered Mac OS X driver by
+ * Johannes Berg <johannes@sipsolutions.net>, documentation available at
+ * http://johannes.sipsolutions.net/PowerBook/Apple_Motion_Sensor_Specification
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "ams.h"
+
+/* AMS registers */
+#define AMS_COMMAND 0x00 /* command register */
+#define AMS_STATUS 0x01 /* status register */
+#define AMS_CTRL1 0x02 /* read control 1 (number of values) */
+#define AMS_CTRL2 0x03 /* read control 2 (offset?) */
+#define AMS_CTRL3 0x04 /* read control 3 (size of each value?) */
+#define AMS_DATA1 0x05 /* read data 1 */
+#define AMS_DATA2 0x06 /* read data 2 */
+#define AMS_DATA3 0x07 /* read data 3 */
+#define AMS_DATA4 0x08 /* read data 4 */
+#define AMS_DATAX 0x20 /* data X */
+#define AMS_DATAY 0x21 /* data Y */
+#define AMS_DATAZ 0x22 /* data Z */
+#define AMS_FREEFALL 0x24 /* freefall int control */
+#define AMS_SHOCK 0x25 /* shock int control */
+#define AMS_SENSLOW 0x26 /* sensitivity low limit */
+#define AMS_SENSHIGH 0x27 /* sensitivity high limit */
+#define AMS_CTRLX 0x28 /* control X */
+#define AMS_CTRLY 0x29 /* control Y */
+#define AMS_CTRLZ 0x2A /* control Z */
+#define AMS_UNKNOWN1 0x2B /* unknown 1 */
+#define AMS_UNKNOWN2 0x2C /* unknown 2 */
+#define AMS_UNKNOWN3 0x2D /* unknown 3 */
+#define AMS_VENDOR 0x2E /* vendor */
+
+/* AMS commands - use with the AMS_COMMAND register */
+enum ams_i2c_cmd {
+ AMS_CMD_NOOP = 0,
+ AMS_CMD_VERSION,
+ AMS_CMD_READMEM,
+ AMS_CMD_WRITEMEM,
+ AMS_CMD_ERASEMEM,
+ AMS_CMD_READEE,
+ AMS_CMD_WRITEEE,
+ AMS_CMD_RESET,
+ AMS_CMD_START,
+};
+
+static int ams_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int ams_i2c_remove(struct i2c_client *client);
+
+static const struct i2c_device_id ams_id[] = {
+ { "MAC,accelerometer_1", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ams_id);
+
+static struct i2c_driver ams_i2c_driver = {
+ .driver = {
+ .name = "ams",
+ .owner = THIS_MODULE,
+ },
+ .probe = ams_i2c_probe,
+ .remove = ams_i2c_remove,
+ .id_table = ams_id,
+};
+
+static s32 ams_i2c_read(u8 reg)
+{
+ return i2c_smbus_read_byte_data(ams_info.i2c_client, reg);
+}
+
+static int ams_i2c_write(u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(ams_info.i2c_client, reg, value);
+}
+
+static int ams_i2c_cmd(enum ams_i2c_cmd cmd)
+{
+ s32 result;
+ int count = 3;
+
+ ams_i2c_write(AMS_COMMAND, cmd);
+ msleep(5);
+
+ while (count--) {
+ result = ams_i2c_read(AMS_COMMAND);
+ if (result == 0 || result & 0x80)
+ return 0;
+
+ schedule_timeout_uninterruptible(HZ / 20);
+ }
+
+ return -1;
+}
+
+static void ams_i2c_set_irq(enum ams_irq reg, char enable)
+{
+ if (reg & AMS_IRQ_FREEFALL) {
+ u8 val = ams_i2c_read(AMS_CTRLX);
+ if (enable)
+ val |= 0x80;
+ else
+ val &= ~0x80;
+ ams_i2c_write(AMS_CTRLX, val);
+ }
+
+ if (reg & AMS_IRQ_SHOCK) {
+ u8 val = ams_i2c_read(AMS_CTRLY);
+ if (enable)
+ val |= 0x80;
+ else
+ val &= ~0x80;
+ ams_i2c_write(AMS_CTRLY, val);
+ }
+
+ if (reg & AMS_IRQ_GLOBAL) {
+ u8 val = ams_i2c_read(AMS_CTRLZ);
+ if (enable)
+ val |= 0x80;
+ else
+ val &= ~0x80;
+ ams_i2c_write(AMS_CTRLZ, val);
+ }
+}
+
+static void ams_i2c_clear_irq(enum ams_irq reg)
+{
+ if (reg & AMS_IRQ_FREEFALL)
+ ams_i2c_write(AMS_FREEFALL, 0);
+
+ if (reg & AMS_IRQ_SHOCK)
+ ams_i2c_write(AMS_SHOCK, 0);
+}
+
+static u8 ams_i2c_get_vendor(void)
+{
+ return ams_i2c_read(AMS_VENDOR);
+}
+
+static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z)
+{
+ *x = ams_i2c_read(AMS_DATAX);
+ *y = ams_i2c_read(AMS_DATAY);
+ *z = ams_i2c_read(AMS_DATAZ);
+}
+
+static int ams_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int vmaj, vmin;
+ int result;
+
+ /* There can be only one */
+ if (unlikely(ams_info.has_device))
+ return -ENODEV;
+
+ ams_info.i2c_client = client;
+
+ if (ams_i2c_cmd(AMS_CMD_RESET)) {
+ printk(KERN_INFO "ams: Failed to reset the device\n");
+ return -ENODEV;
+ }
+
+ if (ams_i2c_cmd(AMS_CMD_START)) {
+ printk(KERN_INFO "ams: Failed to start the device\n");
+ return -ENODEV;
+ }
+
+ /* get version/vendor information */
+ ams_i2c_write(AMS_CTRL1, 0x02);
+ ams_i2c_write(AMS_CTRL2, 0x85);
+ ams_i2c_write(AMS_CTRL3, 0x01);
+
+ ams_i2c_cmd(AMS_CMD_READMEM);
+
+ vmaj = ams_i2c_read(AMS_DATA1);
+ vmin = ams_i2c_read(AMS_DATA2);
+ if (vmaj != 1 || vmin != 52) {
+ printk(KERN_INFO "ams: Incorrect device version (%d.%d)\n",
+ vmaj, vmin);
+ return -ENODEV;
+ }
+
+ ams_i2c_cmd(AMS_CMD_VERSION);
+
+ vmaj = ams_i2c_read(AMS_DATA1);
+ vmin = ams_i2c_read(AMS_DATA2);
+ if (vmaj != 0 || vmin != 1) {
+ printk(KERN_INFO "ams: Incorrect firmware version (%d.%d)\n",
+ vmaj, vmin);
+ return -ENODEV;
+ }
+
+ /* Disable interrupts */
+ ams_i2c_set_irq(AMS_IRQ_ALL, 0);
+
+ result = ams_sensor_attach();
+ if (result < 0)
+ return result;
+
+ /* Set default values */
+ ams_i2c_write(AMS_SENSLOW, 0x15);
+ ams_i2c_write(AMS_SENSHIGH, 0x60);
+ ams_i2c_write(AMS_CTRLX, 0x08);
+ ams_i2c_write(AMS_CTRLY, 0x0F);
+ ams_i2c_write(AMS_CTRLZ, 0x4F);
+ ams_i2c_write(AMS_UNKNOWN1, 0x14);
+
+ /* Clear interrupts */
+ ams_i2c_clear_irq(AMS_IRQ_ALL);
+
+ ams_info.has_device = 1;
+
+ /* Enable interrupts */
+ ams_i2c_set_irq(AMS_IRQ_ALL, 1);
+
+ printk(KERN_INFO "ams: Found I2C based motion sensor\n");
+
+ return 0;
+}
+
+static int ams_i2c_remove(struct i2c_client *client)
+{
+ if (ams_info.has_device) {
+ ams_sensor_detach();
+
+ /* Disable interrupts */
+ ams_i2c_set_irq(AMS_IRQ_ALL, 0);
+
+ /* Clear interrupts */
+ ams_i2c_clear_irq(AMS_IRQ_ALL);
+
+ printk(KERN_INFO "ams: Unloading\n");
+
+ ams_info.has_device = 0;
+ }
+
+ return 0;
+}
+
+static void ams_i2c_exit(void)
+{
+ i2c_del_driver(&ams_i2c_driver);
+}
+
+int __init ams_i2c_init(struct device_node *np)
+{
+ int result;
+
+ /* Set implementation stuff */
+ ams_info.of_node = np;
+ ams_info.exit = ams_i2c_exit;
+ ams_info.get_vendor = ams_i2c_get_vendor;
+ ams_info.get_xyz = ams_i2c_get_xyz;
+ ams_info.clear_irq = ams_i2c_clear_irq;
+ ams_info.bustype = BUS_I2C;
+
+ result = i2c_add_driver(&ams_i2c_driver);
+
+ return result;
+}
diff --git a/drivers/macintosh/ams/ams-input.c b/drivers/macintosh/ams/ams-input.c
new file mode 100644
index 00000000000..2edae7dfcab
--- /dev/null
+++ b/drivers/macintosh/ams/ams-input.c
@@ -0,0 +1,161 @@
+/*
+ * Apple Motion Sensor driver (joystick emulation)
+ *
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "ams.h"
+
+static bool joystick;
+module_param(joystick, bool, S_IRUGO);
+MODULE_PARM_DESC(joystick, "Enable the input class device on module load");
+
+static bool invert;
+module_param(invert, bool, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(invert, "Invert input data on X and Y axis");
+
+static DEFINE_MUTEX(ams_input_mutex);
+
+static void ams_idev_poll(struct input_polled_dev *dev)
+{
+ struct input_dev *idev = dev->input;
+ s8 x, y, z;
+
+ mutex_lock(&ams_info.lock);
+
+ ams_sensors(&x, &y, &z);
+
+ x -= ams_info.xcalib;
+ y -= ams_info.ycalib;
+ z -= ams_info.zcalib;
+
+ input_report_abs(idev, ABS_X, invert ? -x : x);
+ input_report_abs(idev, ABS_Y, invert ? -y : y);
+ input_report_abs(idev, ABS_Z, z);
+
+ input_sync(idev);
+
+ mutex_unlock(&ams_info.lock);
+}
+
+/* Call with ams_info.lock held! */
+static int ams_input_enable(void)
+{
+ struct input_dev *input;
+ s8 x, y, z;
+ int error;
+
+ ams_sensors(&x, &y, &z);
+ ams_info.xcalib = x;
+ ams_info.ycalib = y;
+ ams_info.zcalib = z;
+
+ ams_info.idev = input_allocate_polled_device();
+ if (!ams_info.idev)
+ return -ENOMEM;
+
+ ams_info.idev->poll = ams_idev_poll;
+ ams_info.idev->poll_interval = 25;
+
+ input = ams_info.idev->input;
+ input->name = "Apple Motion Sensor";
+ input->id.bustype = ams_info.bustype;
+ input->id.vendor = 0;
+ input->dev.parent = &ams_info.of_dev->dev;
+
+ input_set_abs_params(input, ABS_X, -50, 50, 3, 0);
+ input_set_abs_params(input, ABS_Y, -50, 50, 3, 0);
+ input_set_abs_params(input, ABS_Z, -50, 50, 3, 0);
+
+ set_bit(EV_ABS, input->evbit);
+ set_bit(EV_KEY, input->evbit);
+ set_bit(BTN_TOUCH, input->keybit);
+
+ error = input_register_polled_device(ams_info.idev);
+ if (error) {
+ input_free_polled_device(ams_info.idev);
+ ams_info.idev = NULL;
+ return error;
+ }
+
+ joystick = 1;
+
+ return 0;
+}
+
+static void ams_input_disable(void)
+{
+ if (ams_info.idev) {
+ input_unregister_polled_device(ams_info.idev);
+ input_free_polled_device(ams_info.idev);
+ ams_info.idev = NULL;
+ }
+
+ joystick = 0;
+}
+
+static ssize_t ams_input_show_joystick(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", joystick);
+}
+
+static ssize_t ams_input_store_joystick(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long enable;
+ int error = 0;
+ int ret;
+
+ ret = kstrtoul(buf, 0, &enable);
+ if (ret)
+ return ret;
+ if (enable > 1)
+ return -EINVAL;
+
+ mutex_lock(&ams_input_mutex);
+
+ if (enable != joystick) {
+ if (enable)
+ error = ams_input_enable();
+ else
+ ams_input_disable();
+ }
+
+ mutex_unlock(&ams_input_mutex);
+
+ return error ? error : count;
+}
+
+static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR,
+ ams_input_show_joystick, ams_input_store_joystick);
+
+int ams_input_init(void)
+{
+ if (joystick)
+ ams_input_enable();
+
+ return device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick);
+}
+
+void ams_input_exit(void)
+{
+ device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick);
+
+ mutex_lock(&ams_input_mutex);
+ ams_input_disable();
+ mutex_unlock(&ams_input_mutex);
+}
diff --git a/drivers/macintosh/ams/ams-pmu.c b/drivers/macintosh/ams/ams-pmu.c
new file mode 100644
index 00000000000..4f61b3ee1b0
--- /dev/null
+++ b/drivers/macintosh/ams/ams-pmu.c
@@ -0,0 +1,201 @@
+/*
+ * Apple Motion Sensor driver (PMU variant)
+ *
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+
+#include "ams.h"
+
+/* Attitude */
+#define AMS_X 0x00
+#define AMS_Y 0x01
+#define AMS_Z 0x02
+
+/* Not exactly known, maybe chip vendor */
+#define AMS_VENDOR 0x03
+
+/* Freefall registers */
+#define AMS_FF_CLEAR 0x04
+#define AMS_FF_ENABLE 0x05
+#define AMS_FF_LOW_LIMIT 0x06
+#define AMS_FF_DEBOUNCE 0x07
+
+/* Shock registers */
+#define AMS_SHOCK_CLEAR 0x08
+#define AMS_SHOCK_ENABLE 0x09
+#define AMS_SHOCK_HIGH_LIMIT 0x0a
+#define AMS_SHOCK_DEBOUNCE 0x0b
+
+/* Global interrupt and power control register */
+#define AMS_CONTROL 0x0c
+
+static u8 ams_pmu_cmd;
+
+static void ams_pmu_req_complete(struct adb_request *req)
+{
+ complete((struct completion *)req->arg);
+}
+
+/* Only call this function from task context */
+static void ams_pmu_set_register(u8 reg, u8 value)
+{
+ static struct adb_request req;
+ DECLARE_COMPLETION(req_complete);
+
+ req.arg = &req_complete;
+ if (pmu_request(&req, ams_pmu_req_complete, 4, ams_pmu_cmd, 0x00, reg, value))
+ return;
+
+ wait_for_completion(&req_complete);
+}
+
+/* Only call this function from task context */
+static u8 ams_pmu_get_register(u8 reg)
+{
+ static struct adb_request req;
+ DECLARE_COMPLETION(req_complete);
+
+ req.arg = &req_complete;
+ if (pmu_request(&req, ams_pmu_req_complete, 3, ams_pmu_cmd, 0x01, reg))
+ return 0;
+
+ wait_for_completion(&req_complete);
+
+ if (req.reply_len > 0)
+ return req.reply[0];
+ else
+ return 0;
+}
+
+/* Enables or disables the specified interrupts */
+static void ams_pmu_set_irq(enum ams_irq reg, char enable)
+{
+ if (reg & AMS_IRQ_FREEFALL) {
+ u8 val = ams_pmu_get_register(AMS_FF_ENABLE);
+ if (enable)
+ val |= 0x80;
+ else
+ val &= ~0x80;
+ ams_pmu_set_register(AMS_FF_ENABLE, val);
+ }
+
+ if (reg & AMS_IRQ_SHOCK) {
+ u8 val = ams_pmu_get_register(AMS_SHOCK_ENABLE);
+ if (enable)
+ val |= 0x80;
+ else
+ val &= ~0x80;
+ ams_pmu_set_register(AMS_SHOCK_ENABLE, val);
+ }
+
+ if (reg & AMS_IRQ_GLOBAL) {
+ u8 val = ams_pmu_get_register(AMS_CONTROL);
+ if (enable)
+ val |= 0x80;
+ else
+ val &= ~0x80;
+ ams_pmu_set_register(AMS_CONTROL, val);
+ }
+}
+
+static void ams_pmu_clear_irq(enum ams_irq reg)
+{
+ if (reg & AMS_IRQ_FREEFALL)
+ ams_pmu_set_register(AMS_FF_CLEAR, 0x00);
+
+ if (reg & AMS_IRQ_SHOCK)
+ ams_pmu_set_register(AMS_SHOCK_CLEAR, 0x00);
+}
+
+static u8 ams_pmu_get_vendor(void)
+{
+ return ams_pmu_get_register(AMS_VENDOR);
+}
+
+static void ams_pmu_get_xyz(s8 *x, s8 *y, s8 *z)
+{
+ *x = ams_pmu_get_register(AMS_X);
+ *y = ams_pmu_get_register(AMS_Y);
+ *z = ams_pmu_get_register(AMS_Z);
+}
+
+static void ams_pmu_exit(void)
+{
+ ams_sensor_detach();
+
+ /* Disable interrupts */
+ ams_pmu_set_irq(AMS_IRQ_ALL, 0);
+
+ /* Clear interrupts */
+ ams_pmu_clear_irq(AMS_IRQ_ALL);
+
+ ams_info.has_device = 0;
+
+ printk(KERN_INFO "ams: Unloading\n");
+}
+
+int __init ams_pmu_init(struct device_node *np)
+{
+ const u32 *prop;
+ int result;
+
+ /* Set implementation stuff */
+ ams_info.of_node = np;
+ ams_info.exit = ams_pmu_exit;
+ ams_info.get_vendor = ams_pmu_get_vendor;
+ ams_info.get_xyz = ams_pmu_get_xyz;
+ ams_info.clear_irq = ams_pmu_clear_irq;
+ ams_info.bustype = BUS_HOST;
+
+ /* Get PMU command, should be 0x4e, but we can never know */
+ prop = of_get_property(ams_info.of_node, "reg", NULL);
+ if (!prop)
+ return -ENODEV;
+
+ ams_pmu_cmd = ((*prop) >> 8) & 0xff;
+
+ /* Disable interrupts */
+ ams_pmu_set_irq(AMS_IRQ_ALL, 0);
+
+ /* Clear interrupts */
+ ams_pmu_clear_irq(AMS_IRQ_ALL);
+
+ result = ams_sensor_attach();
+ if (result < 0)
+ return result;
+
+ /* Set default values */
+ ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15);
+ ams_pmu_set_register(AMS_FF_ENABLE, 0x08);
+ ams_pmu_set_register(AMS_FF_DEBOUNCE, 0x14);
+
+ ams_pmu_set_register(AMS_SHOCK_HIGH_LIMIT, 0x60);
+ ams_pmu_set_register(AMS_SHOCK_ENABLE, 0x0f);
+ ams_pmu_set_register(AMS_SHOCK_DEBOUNCE, 0x14);
+
+ ams_pmu_set_register(AMS_CONTROL, 0x4f);
+
+ /* Clear interrupts */
+ ams_pmu_clear_irq(AMS_IRQ_ALL);
+
+ ams_info.has_device = 1;
+
+ /* Enable interrupts */
+ ams_pmu_set_irq(AMS_IRQ_ALL, 1);
+
+ printk(KERN_INFO "ams: Found PMU based motion sensor\n");
+
+ return 0;
+}
diff --git a/drivers/macintosh/ams/ams.h b/drivers/macintosh/ams/ams.h
new file mode 100644
index 00000000000..90f094d4545
--- /dev/null
+++ b/drivers/macintosh/ams/ams.h
@@ -0,0 +1,70 @@
+#include <linux/i2c.h>
+#include <linux/input-polldev.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/of_device.h>
+
+enum ams_irq {
+ AMS_IRQ_FREEFALL = 0x01,
+ AMS_IRQ_SHOCK = 0x02,
+ AMS_IRQ_GLOBAL = 0x04,
+ AMS_IRQ_ALL =
+ AMS_IRQ_FREEFALL |
+ AMS_IRQ_SHOCK |
+ AMS_IRQ_GLOBAL,
+};
+
+struct ams {
+ /* Locks */
+ spinlock_t irq_lock;
+ struct mutex lock;
+
+ /* General properties */
+ struct device_node *of_node;
+ struct platform_device *of_dev;
+ char has_device;
+ char vflag;
+ u32 orient1;
+ u32 orient2;
+
+ /* Interrupt worker */
+ struct work_struct worker;
+ u8 worker_irqs;
+
+ /* Implementation
+ *
+ * Only call these functions with the main lock held.
+ */
+ void (*exit)(void);
+
+ void (*get_xyz)(s8 *x, s8 *y, s8 *z);
+ u8 (*get_vendor)(void);
+
+ void (*clear_irq)(enum ams_irq reg);
+
+#ifdef CONFIG_SENSORS_AMS_I2C
+ /* I2C properties */
+ struct i2c_client *i2c_client;
+#endif
+
+ /* Joystick emulation */
+ struct input_polled_dev *idev;
+ __u16 bustype;
+
+ /* calibrated null values */
+ int xcalib, ycalib, zcalib;
+};
+
+extern struct ams ams_info;
+
+extern void ams_sensors(s8 *x, s8 *y, s8 *z);
+extern int ams_sensor_attach(void);
+extern void ams_sensor_detach(void);
+
+extern int ams_pmu_init(struct device_node *np);
+extern int ams_i2c_init(struct device_node *np);
+
+extern int ams_input_init(void);
+extern void ams_input_exit(void);
diff --git a/drivers/macintosh/ans-lcd.c b/drivers/macintosh/ans-lcd.c
index 2b8a6e821d4..1a57e88a38f 100644
--- a/drivers/macintosh/ans-lcd.c
+++ b/drivers/macintosh/ans-lcd.c
@@ -14,9 +14,10 @@
#include <asm/uaccess.h>
#include <asm/sections.h>
#include <asm/prom.h>
-#include <asm/ans-lcd.h>
#include <asm/io.h>
+#include "ans-lcd.h"
+
#define ANSLCD_ADDR 0xf301c000
#define ANSLCD_CTRL_IX 0x00
#define ANSLCD_DATA_IX 0x10
@@ -24,6 +25,7 @@
static unsigned long anslcd_short_delay = 80;
static unsigned long anslcd_long_delay = 3280;
static volatile unsigned char __iomem *anslcd_ptr;
+static DEFINE_MUTEX(anslcd_mutex);
#undef DEBUG
@@ -63,26 +65,31 @@ anslcd_write( struct file * file, const char __user * buf,
if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT;
+
+ mutex_lock(&anslcd_mutex);
for ( i = *ppos; count > 0; ++i, ++p, --count )
{
char c;
__get_user(c, p);
anslcd_write_byte_data( c );
}
+ mutex_unlock(&anslcd_mutex);
*ppos = i;
return p - buf;
}
-static int
-anslcd_ioctl( struct inode * inode, struct file * file,
- unsigned int cmd, unsigned long arg )
+static long
+anslcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
char ch, __user *temp;
+ long ret = 0;
#ifdef DEBUG
printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg);
#endif
+ mutex_lock(&anslcd_mutex);
+
switch ( cmd )
{
case ANSLCD_CLEAR:
@@ -91,7 +98,7 @@ anslcd_ioctl( struct inode * inode, struct file * file,
anslcd_write_byte_ctrl ( 0x06 );
anslcd_write_byte_ctrl ( 0x01 );
anslcd_write_byte_ctrl ( 0x02 );
- return 0;
+ break;
case ANSLCD_SENDCTRL:
temp = (char __user *) arg;
__get_user(ch, temp);
@@ -99,20 +106,25 @@ anslcd_ioctl( struct inode * inode, struct file * file,
anslcd_write_byte_ctrl ( ch );
__get_user(ch, temp);
}
- return 0;
+ break;
case ANSLCD_SETSHORTDELAY:
if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
- anslcd_short_delay=arg;
- return 0;
+ ret =-EACCES;
+ else
+ anslcd_short_delay=arg;
+ break;
case ANSLCD_SETLONGDELAY:
if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
- anslcd_long_delay=arg;
- return 0;
+ ret = -EACCES;
+ else
+ anslcd_long_delay=arg;
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
}
+
+ mutex_unlock(&anslcd_mutex);
+ return ret;
}
static int
@@ -121,10 +133,11 @@ anslcd_open( struct inode * inode, struct file * file )
return 0;
}
-struct file_operations anslcd_fops = {
- .write = anslcd_write,
- .ioctl = anslcd_ioctl,
- .open = anslcd_open,
+const struct file_operations anslcd_fops = {
+ .write = anslcd_write,
+ .unlocked_ioctl = anslcd_ioctl,
+ .open = anslcd_open,
+ .llseek = default_llseek,
};
static struct miscdevice anslcd_dev = {
@@ -145,11 +158,12 @@ anslcd_init(void)
int retval;
struct device_node* node;
- node = find_devices("lcd");
- if (!node || !node->parent)
- return -ENODEV;
- if (strcmp(node->parent->name, "gc"))
+ node = of_find_node_by_name(NULL, "lcd");
+ if (!node || !node->parent || strcmp(node->parent->name, "gc")) {
+ of_node_put(node);
return -ENODEV;
+ }
+ of_node_put(node);
anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20);
@@ -164,6 +178,7 @@ anslcd_init(void)
printk(KERN_DEBUG "LCD: init\n");
#endif
+ mutex_lock(&anslcd_mutex);
anslcd_write_byte_ctrl ( 0x38 );
anslcd_write_byte_ctrl ( 0x0c );
anslcd_write_byte_ctrl ( 0x06 );
@@ -172,6 +187,7 @@ anslcd_init(void)
for(a=0;a<80;a++) {
anslcd_write_byte_data(anslcd_logo[a]);
}
+ mutex_unlock(&anslcd_mutex);
return 0;
}
diff --git a/drivers/macintosh/ans-lcd.h b/drivers/macintosh/ans-lcd.h
new file mode 100644
index 00000000000..d795b9fd2db
--- /dev/null
+++ b/drivers/macintosh/ans-lcd.h
@@ -0,0 +1,11 @@
+#ifndef _PPC_ANS_LCD_H
+#define _PPC_ANS_LCD_H
+
+#define ANSLCD_MINOR 156
+
+#define ANSLCD_CLEAR 0x01
+#define ANSLCD_SENDCTRL 0x02
+#define ANSLCD_SETSHORTDELAY 0x03
+#define ANSLCD_SETLONGDELAY 0x04
+
+#endif
diff --git a/drivers/macintosh/apm_emu.c b/drivers/macintosh/apm_emu.c
index 1293876a2eb..9821e6361e6 100644
--- a/drivers/macintosh/apm_emu.c
+++ b/drivers/macintosh/apm_emu.c
@@ -1,9 +1,7 @@
-/* APM emulation layer for PowerMac
- *
- * Copyright 2001 Benjamin Herrenschmidt (benh@kernel.crashing.org)
+/*
+ * APM emulation for PMU-based machines
*
- * Lots of code inherited from apm.c, see appropriate notice in
- * arch/i386/kernel/apm.c
+ * Copyright 2001 Benjamin Herrenschmidt (benh@kernel.crashing.org)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -18,434 +16,39 @@
*
*/
-#include <linux/module.h>
-
-#include <linux/poll.h>
-#include <linux/types.h>
-#include <linux/stddef.h>
-#include <linux/timer.h>
-#include <linux/fcntl.h>
-#include <linux/slab.h>
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-#include <linux/miscdevice.h>
-#include <linux/apm_bios.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/pm.h>
#include <linux/kernel.h>
-#include <linux/smp_lock.h>
-
+#include <linux/module.h>
+#include <linux/apm-emulation.h>
#include <linux/adb.h>
#include <linux/pmu.h>
-#include <asm/system.h>
-#include <asm/uaccess.h>
-#include <asm/machdep.h>
-
-#undef DEBUG
-
-#ifdef DEBUG
-#define DBG(args...) printk(KERN_DEBUG args)
-//#define DBG(args...) xmon_printf(args)
-#else
-#define DBG(args...) do { } while (0)
-#endif
-
-/*
- * The apm_bios device is one of the misc char devices.
- * This is its minor number.
- */
-#define APM_MINOR_DEV 134
-
-/*
- * Maximum number of events stored
- */
-#define APM_MAX_EVENTS 20
-
-#define FAKE_APM_BIOS_VERSION 0x0101
-
-#define APM_USER_NOTIFY_TIMEOUT (5*HZ)
-
-/*
- * The per-file APM data
- */
-struct apm_user {
- int magic;
- struct apm_user * next;
- int suser: 1;
- int suspend_waiting: 1;
- int suspends_pending;
- int suspends_read;
- int event_head;
- int event_tail;
- apm_event_t events[APM_MAX_EVENTS];
-};
-
-/*
- * The magic number in apm_user
- */
-#define APM_BIOS_MAGIC 0x4101
-
-/*
- * Local variables
- */
-static int suspends_pending;
-
-static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
-static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
-static struct apm_user * user_list;
-
-static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when);
-static struct pmu_sleep_notifier apm_sleep_notifier = {
- apm_notify_sleep,
- SLEEP_LEVEL_USERLAND,
-};
-
-static char driver_version[] = "0.5"; /* no spaces */
-
-#ifdef DEBUG
-static char * apm_event_name[] = {
- "system standby",
- "system suspend",
- "normal resume",
- "critical resume",
- "low battery",
- "power status change",
- "update time",
- "critical suspend",
- "user standby",
- "user suspend",
- "system standby resume",
- "capabilities change"
-};
-#define NR_APM_EVENT_NAME \
- (sizeof(apm_event_name) / sizeof(apm_event_name[0]))
-
-#endif
-
-static int queue_empty(struct apm_user *as)
-{
- return as->event_head == as->event_tail;
-}
-
-static apm_event_t get_queued_event(struct apm_user *as)
-{
- as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
- return as->events[as->event_tail];
-}
-
-static void queue_event(apm_event_t event, struct apm_user *sender)
-{
- struct apm_user * as;
-
- DBG("apm_emu: queue_event(%s)\n", apm_event_name[event-1]);
- if (user_list == NULL)
- return;
- for (as = user_list; as != NULL; as = as->next) {
- if (as == sender)
- continue;
- as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
- if (as->event_head == as->event_tail) {
- static int notified;
-
- if (notified++ == 0)
- printk(KERN_ERR "apm_emu: an event queue overflowed\n");
- as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
- }
- as->events[as->event_head] = event;
- if (!as->suser)
- continue;
- switch (event) {
- case APM_SYS_SUSPEND:
- case APM_USER_SUSPEND:
- as->suspends_pending++;
- suspends_pending++;
- break;
- case APM_NORMAL_RESUME:
- as->suspend_waiting = 0;
- break;
- }
- }
- wake_up_interruptible(&apm_waitqueue);
-}
-
-static int check_apm_user(struct apm_user *as, const char *func)
-{
- if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
- printk(KERN_ERR "apm_emu: %s passed bad filp\n", func);
- return 1;
- }
- return 0;
-}
-
-static ssize_t do_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)
-{
- struct apm_user * as;
- size_t i;
- apm_event_t event;
- DECLARE_WAITQUEUE(wait, current);
-
- as = fp->private_data;
- if (check_apm_user(as, "read"))
- return -EIO;
- if (count < sizeof(apm_event_t))
- return -EINVAL;
- if (queue_empty(as)) {
- if (fp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- add_wait_queue(&apm_waitqueue, &wait);
-repeat:
- set_current_state(TASK_INTERRUPTIBLE);
- if (queue_empty(as) && !signal_pending(current)) {
- schedule();
- goto repeat;
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&apm_waitqueue, &wait);
- }
- i = count;
- while ((i >= sizeof(event)) && !queue_empty(as)) {
- event = get_queued_event(as);
- DBG("apm_emu: do_read, returning: %s\n", apm_event_name[event-1]);
- if (copy_to_user(buf, &event, sizeof(event))) {
- if (i < count)
- break;
- return -EFAULT;
- }
- switch (event) {
- case APM_SYS_SUSPEND:
- case APM_USER_SUSPEND:
- as->suspends_read++;
- break;
- }
- buf += sizeof(event);
- i -= sizeof(event);
- }
- if (i < count)
- return count - i;
- if (signal_pending(current))
- return -ERESTARTSYS;
- return 0;
-}
-
-static unsigned int do_poll(struct file *fp, poll_table * wait)
-{
- struct apm_user * as;
-
- as = fp->private_data;
- if (check_apm_user(as, "poll"))
- return 0;
- poll_wait(fp, &apm_waitqueue, wait);
- if (!queue_empty(as))
- return POLLIN | POLLRDNORM;
- return 0;
-}
-
-static int do_ioctl(struct inode * inode, struct file *filp,
- u_int cmd, u_long arg)
-{
- struct apm_user * as;
- DECLARE_WAITQUEUE(wait, current);
-
- as = filp->private_data;
- if (check_apm_user(as, "ioctl"))
- return -EIO;
- if (!as->suser)
- return -EPERM;
- switch (cmd) {
- case APM_IOC_SUSPEND:
- /* If a suspend message was sent to userland, we
- * consider this as a confirmation message
- */
- if (as->suspends_read > 0) {
- as->suspends_read--;
- as->suspends_pending--;
- suspends_pending--;
- } else {
- // Route to PMU suspend ?
- break;
- }
- as->suspend_waiting = 1;
- add_wait_queue(&apm_waitqueue, &wait);
- DBG("apm_emu: ioctl waking up sleep waiter !\n");
- wake_up(&apm_suspend_waitqueue);
- mb();
- while(as->suspend_waiting && !signal_pending(current)) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&apm_waitqueue, &wait);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int do_release(struct inode * inode, struct file * filp)
-{
- struct apm_user * as;
-
- as = filp->private_data;
- if (check_apm_user(as, "release"))
- return 0;
- filp->private_data = NULL;
- lock_kernel();
- if (as->suspends_pending > 0) {
- suspends_pending -= as->suspends_pending;
- if (suspends_pending <= 0)
- wake_up(&apm_suspend_waitqueue);
- }
- if (user_list == as)
- user_list = as->next;
- else {
- struct apm_user * as1;
-
- for (as1 = user_list;
- (as1 != NULL) && (as1->next != as);
- as1 = as1->next)
- ;
- if (as1 == NULL)
- printk(KERN_ERR "apm: filp not in user list\n");
- else
- as1->next = as->next;
- }
- unlock_kernel();
- kfree(as);
- return 0;
-}
-
-static int do_open(struct inode * inode, struct file * filp)
-{
- struct apm_user * as;
-
- as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL);
- if (as == NULL) {
- printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n",
- sizeof(*as));
- return -ENOMEM;
- }
- as->magic = APM_BIOS_MAGIC;
- as->event_tail = as->event_head = 0;
- as->suspends_pending = 0;
- as->suspends_read = 0;
- /*
- * XXX - this is a tiny bit broken, when we consider BSD
- * process accounting. If the device is opened by root, we
- * instantly flag that we used superuser privs. Who knows,
- * we might close the device immediately without doing a
- * privileged operation -- cevans
- */
- as->suser = capable(CAP_SYS_ADMIN);
- as->next = user_list;
- user_list = as;
- filp->private_data = as;
-
- DBG("apm_emu: opened by %s, suser: %d\n", current->comm, (int)as->suser);
-
- return 0;
-}
-
-/* Wait for all clients to ack the suspend request. APM API
- * doesn't provide a way to NAK, but this could be added
- * here.
- */
-static int wait_all_suspend(void)
-{
- DECLARE_WAITQUEUE(wait, current);
-
- add_wait_queue(&apm_suspend_waitqueue, &wait);
- DBG("apm_emu: wait_all_suspend(), suspends_pending: %d\n", suspends_pending);
- while(suspends_pending > 0) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule();
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&apm_suspend_waitqueue, &wait);
-
- DBG("apm_emu: wait_all_suspend() - complete !\n");
-
- return 1;
-}
-
-static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when)
-{
- switch(when) {
- case PBOOK_SLEEP_REQUEST:
- queue_event(APM_SYS_SUSPEND, NULL);
- if (!wait_all_suspend())
- return PBOOK_SLEEP_REFUSE;
- break;
- case PBOOK_SLEEP_REJECT:
- case PBOOK_WAKE:
- queue_event(APM_NORMAL_RESUME, NULL);
- break;
- }
- return PBOOK_SLEEP_OK;
-}
-
#define APM_CRITICAL 10
#define APM_LOW 30
-static int apm_emu_get_info(char *buf, char **start, off_t fpos, int length)
+static void pmu_apm_get_power_status(struct apm_power_info *info)
{
- /* Arguments, with symbols from linux/apm_bios.h. Information is
- from the Get Power Status (0x0a) call unless otherwise noted.
+ int percentage = -1;
+ int batteries = 0;
+ int time_units = -1;
+ int real_count = 0;
+ int i;
+ char charging = 0;
+ long charge = -1;
+ long amperage = 0;
+ unsigned long btype = 0;
+
+ info->battery_status = APM_BATTERY_STATUS_UNKNOWN;
+ info->battery_flag = APM_BATTERY_FLAG_UNKNOWN;
+ info->units = APM_UNITS_MINS;
+
+ if (pmu_power_flags & PMU_PWR_AC_PRESENT)
+ info->ac_line_status = APM_AC_ONLINE;
+ else
+ info->ac_line_status = APM_AC_OFFLINE;
- 0) Linux driver version (this will change if format changes)
- 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2.
- 2) APM flags from APM Installation Check (0x00):
- bit 0: APM_16_BIT_SUPPORT
- bit 1: APM_32_BIT_SUPPORT
- bit 2: APM_IDLE_SLOWS_CLOCK
- bit 3: APM_BIOS_DISABLED
- bit 4: APM_BIOS_DISENGAGED
- 3) AC line status
- 0x00: Off-line
- 0x01: On-line
- 0x02: On backup power (BIOS >= 1.1 only)
- 0xff: Unknown
- 4) Battery status
- 0x00: High
- 0x01: Low
- 0x02: Critical
- 0x03: Charging
- 0x04: Selected battery not present (BIOS >= 1.2 only)
- 0xff: Unknown
- 5) Battery flag
- bit 0: High
- bit 1: Low
- bit 2: Critical
- bit 3: Charging
- bit 7: No system battery
- 0xff: Unknown
- 6) Remaining battery life (percentage of charge):
- 0-100: valid
- -1: Unknown
- 7) Remaining battery life (time units):
- Number of remaining minutes or seconds
- -1: Unknown
- 8) min = minutes; sec = seconds */
-
- unsigned short ac_line_status;
- unsigned short battery_status = 0;
- unsigned short battery_flag = 0xff;
- int percentage = -1;
- int time_units = -1;
- int real_count = 0;
- int i;
- char * p = buf;
- char charging = 0;
- long charge = -1;
- long amperage = 0;
- unsigned long btype = 0;
-
- ac_line_status = ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0);
for (i=0; i<pmu_battery_count; i++) {
if (pmu_batteries[i].flags & PMU_BATT_PRESENT) {
- battery_status++;
+ batteries++;
if (percentage < 0)
percentage = 0;
if (charge < 0)
@@ -461,9 +64,9 @@ static int apm_emu_get_info(char *buf, char **start, off_t fpos, int length)
charging++;
}
}
- if (0 == battery_status)
- ac_line_status = 1;
- battery_status = 0xff;
+ if (batteries == 0)
+ info->ac_line_status = APM_AC_ONLINE;
+
if (real_count) {
if (amperage < 0) {
if (btype == PMU_BATT_TYPE_SMART)
@@ -473,84 +76,44 @@ static int apm_emu_get_info(char *buf, char **start, off_t fpos, int length)
}
percentage /= real_count;
if (charging > 0) {
- battery_status = 0x03;
- battery_flag = 0x08;
+ info->battery_status = APM_BATTERY_STATUS_CHARGING;
+ info->battery_flag = APM_BATTERY_FLAG_CHARGING;
} else if (percentage <= APM_CRITICAL) {
- battery_status = 0x02;
- battery_flag = 0x04;
+ info->battery_status = APM_BATTERY_STATUS_CRITICAL;
+ info->battery_flag = APM_BATTERY_FLAG_CRITICAL;
} else if (percentage <= APM_LOW) {
- battery_status = 0x01;
- battery_flag = 0x02;
+ info->battery_status = APM_BATTERY_STATUS_LOW;
+ info->battery_flag = APM_BATTERY_FLAG_LOW;
} else {
- battery_status = 0x00;
- battery_flag = 0x01;
+ info->battery_status = APM_BATTERY_STATUS_HIGH;
+ info->battery_flag = APM_BATTERY_FLAG_HIGH;
}
}
- p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
- driver_version,
- (FAKE_APM_BIOS_VERSION >> 8) & 0xff,
- FAKE_APM_BIOS_VERSION & 0xff,
- 0,
- ac_line_status,
- battery_status,
- battery_flag,
- percentage,
- time_units,
- "min");
- return p - buf;
+ info->battery_life = percentage;
+ info->time = time_units;
}
-static struct file_operations apm_bios_fops = {
- .owner = THIS_MODULE,
- .read = do_read,
- .poll = do_poll,
- .ioctl = do_ioctl,
- .open = do_open,
- .release = do_release,
-};
-
-static struct miscdevice apm_device = {
- APM_MINOR_DEV,
- "apm_bios",
- &apm_bios_fops
-};
-
static int __init apm_emu_init(void)
{
- struct proc_dir_entry *apm_proc;
-
- if (sys_ctrler != SYS_CTRLER_PMU) {
- printk(KERN_INFO "apm_emu: Requires a machine with a PMU.\n");
- return -ENODEV;
- }
-
- apm_proc = create_proc_info_entry("apm", 0, NULL, apm_emu_get_info);
- if (apm_proc)
- apm_proc->owner = THIS_MODULE;
+ apm_get_power_status = pmu_apm_get_power_status;
- misc_register(&apm_device);
-
- pmu_register_sleep_notifier(&apm_sleep_notifier);
-
- printk(KERN_INFO "apm_emu: APM Emulation %s initialized.\n", driver_version);
+ printk(KERN_INFO "apm_emu: PMU APM Emulation initialized.\n");
return 0;
}
static void __exit apm_emu_exit(void)
{
- pmu_unregister_sleep_notifier(&apm_sleep_notifier);
- misc_deregister(&apm_device);
- remove_proc_entry("apm", NULL);
+ if (apm_get_power_status == pmu_apm_get_power_status)
+ apm_get_power_status = NULL;
- printk(KERN_INFO "apm_emu: APM Emulation removed.\n");
+ printk(KERN_INFO "apm_emu: PMU APM Emulation removed.\n");
}
module_init(apm_emu_init);
module_exit(apm_emu_exit);
MODULE_AUTHOR("Benjamin Herrenschmidt");
-MODULE_DESCRIPTION("APM emulation layer for PowerMac");
+MODULE_DESCRIPTION("APM emulation for PowerMac");
MODULE_LICENSE("GPL");
-
diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c
index 6b129eef798..80d30e8e338 100644
--- a/drivers/macintosh/mac_hid.c
+++ b/drivers/macintosh/mac_hid.c
@@ -13,131 +13,271 @@
#include <linux/sysctl.h>
#include <linux/input.h>
#include <linux/module.h>
+#include <linux/slab.h>
+MODULE_LICENSE("GPL");
-static struct input_dev *emumousebtn;
-static int emumousebtn_input_register(void);
-static int mouse_emulate_buttons = 0;
+static int mouse_emulate_buttons;
static int mouse_button2_keycode = KEY_RIGHTCTRL; /* right control key */
static int mouse_button3_keycode = KEY_RIGHTALT; /* right option key */
-static int mouse_last_keycode = 0;
-#if defined(CONFIG_SYSCTL)
+static struct input_dev *mac_hid_emumouse_dev;
+
+static DEFINE_MUTEX(mac_hid_emumouse_mutex);
+
+static int mac_hid_create_emumouse(void)
+{
+ static struct lock_class_key mac_hid_emumouse_dev_event_class;
+ static struct lock_class_key mac_hid_emumouse_dev_mutex_class;
+ int err;
+
+ mac_hid_emumouse_dev = input_allocate_device();
+ if (!mac_hid_emumouse_dev)
+ return -ENOMEM;
+
+ lockdep_set_class(&mac_hid_emumouse_dev->event_lock,
+ &mac_hid_emumouse_dev_event_class);
+ lockdep_set_class(&mac_hid_emumouse_dev->mutex,
+ &mac_hid_emumouse_dev_mutex_class);
+
+ mac_hid_emumouse_dev->name = "Macintosh mouse button emulation";
+ mac_hid_emumouse_dev->id.bustype = BUS_ADB;
+ mac_hid_emumouse_dev->id.vendor = 0x0001;
+ mac_hid_emumouse_dev->id.product = 0x0001;
+ mac_hid_emumouse_dev->id.version = 0x0100;
+
+ mac_hid_emumouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ mac_hid_emumouse_dev->keybit[BIT_WORD(BTN_MOUSE)] =
+ BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+ mac_hid_emumouse_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+ err = input_register_device(mac_hid_emumouse_dev);
+ if (err) {
+ input_free_device(mac_hid_emumouse_dev);
+ mac_hid_emumouse_dev = NULL;
+ return err;
+ }
+
+ return 0;
+}
+
+static void mac_hid_destroy_emumouse(void)
+{
+ input_unregister_device(mac_hid_emumouse_dev);
+ mac_hid_emumouse_dev = NULL;
+}
+
+static bool mac_hid_emumouse_filter(struct input_handle *handle,
+ unsigned int type, unsigned int code,
+ int value)
+{
+ unsigned int btn;
+
+ if (type != EV_KEY)
+ return false;
+
+ if (code == mouse_button2_keycode)
+ btn = BTN_MIDDLE;
+ else if (code == mouse_button3_keycode)
+ btn = BTN_RIGHT;
+ else
+ return false;
+
+ input_report_key(mac_hid_emumouse_dev, btn, value);
+ input_sync(mac_hid_emumouse_dev);
+
+ return true;
+}
+
+static int mac_hid_emumouse_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ struct input_handle *handle;
+ int error;
+
+ /* Don't bind to ourselves */
+ if (dev == mac_hid_emumouse_dev)
+ return -ENODEV;
+
+ handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = "mac-button-emul";
+
+ error = input_register_handle(handle);
+ if (error) {
+ printk(KERN_ERR
+ "mac_hid: Failed to register button emulation handle, "
+ "error %d\n", error);
+ goto err_free;
+ }
+
+ error = input_open_device(handle);
+ if (error) {
+ printk(KERN_ERR
+ "mac_hid: Failed to open input device, error %d\n",
+ error);
+ goto err_unregister;
+ }
+
+ return 0;
+
+ err_unregister:
+ input_unregister_handle(handle);
+ err_free:
+ kfree(handle);
+ return error;
+}
+
+static void mac_hid_emumouse_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+static const struct input_device_id mac_hid_emumouse_ids[] = {
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+ .evbit = { BIT_MASK(EV_KEY) },
+ },
+ { },
+};
+
+MODULE_DEVICE_TABLE(input, mac_hid_emumouse_ids);
+
+static struct input_handler mac_hid_emumouse_handler = {
+ .filter = mac_hid_emumouse_filter,
+ .connect = mac_hid_emumouse_connect,
+ .disconnect = mac_hid_emumouse_disconnect,
+ .name = "mac-button-emul",
+ .id_table = mac_hid_emumouse_ids,
+};
+
+static int mac_hid_start_emulation(void)
+{
+ int err;
+
+ err = mac_hid_create_emumouse();
+ if (err)
+ return err;
+
+ err = input_register_handler(&mac_hid_emumouse_handler);
+ if (err) {
+ mac_hid_destroy_emumouse();
+ return err;
+ }
+
+ return 0;
+}
+
+static void mac_hid_stop_emulation(void)
+{
+ input_unregister_handler(&mac_hid_emumouse_handler);
+ mac_hid_destroy_emumouse();
+}
+
+static int mac_hid_toggle_emumouse(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ int *valp = table->data;
+ int old_val = *valp;
+ int rc;
+
+ rc = mutex_lock_killable(&mac_hid_emumouse_mutex);
+ if (rc)
+ return rc;
+
+ rc = proc_dointvec(table, write, buffer, lenp, ppos);
+
+ if (rc == 0 && write && *valp != old_val) {
+ if (*valp == 1)
+ rc = mac_hid_start_emulation();
+ else if (*valp == 0)
+ mac_hid_stop_emulation();
+ else
+ rc = -EINVAL;
+ }
+
+ /* Restore the old value in case of error */
+ if (rc)
+ *valp = old_val;
+
+ mutex_unlock(&mac_hid_emumouse_mutex);
+
+ return rc;
+}
+
/* file(s) in /proc/sys/dev/mac_hid */
-ctl_table mac_hid_files[] = {
+static struct ctl_table mac_hid_files[] = {
{
- .ctl_name = DEV_MAC_HID_MOUSE_BUTTON_EMULATION,
.procname = "mouse_button_emulation",
.data = &mouse_emulate_buttons,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = mac_hid_toggle_emumouse,
},
{
- .ctl_name = DEV_MAC_HID_MOUSE_BUTTON2_KEYCODE,
.procname = "mouse_button2_keycode",
.data = &mouse_button2_keycode,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
- .ctl_name = DEV_MAC_HID_MOUSE_BUTTON3_KEYCODE,
.procname = "mouse_button3_keycode",
.data = &mouse_button3_keycode,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
- { .ctl_name = 0 }
+ { }
};
/* dir in /proc/sys/dev */
-ctl_table mac_hid_dir[] = {
+static struct ctl_table mac_hid_dir[] = {
{
- .ctl_name = DEV_MAC_HID,
.procname = "mac_hid",
.maxlen = 0,
.mode = 0555,
.child = mac_hid_files,
},
- { .ctl_name = 0 }
+ { }
};
/* /proc/sys/dev itself, in case that is not there yet */
-ctl_table mac_hid_root_dir[] = {
+static struct ctl_table mac_hid_root_dir[] = {
{
- .ctl_name = CTL_DEV,
.procname = "dev",
.maxlen = 0,
.mode = 0555,
.child = mac_hid_dir,
},
- { .ctl_name = 0 }
+ { }
};
static struct ctl_table_header *mac_hid_sysctl_header;
-#endif /* endif CONFIG_SYSCTL */
-
-int mac_hid_mouse_emulate_buttons(int caller, unsigned int keycode, int down)
+static int __init mac_hid_init(void)
{
- switch (caller) {
- case 1:
- /* Called from keyboard.c */
- if (mouse_emulate_buttons
- && (keycode == mouse_button2_keycode
- || keycode == mouse_button3_keycode)) {
- if (mouse_emulate_buttons == 1) {
- input_report_key(emumousebtn,
- keycode == mouse_button2_keycode ? BTN_MIDDLE : BTN_RIGHT,
- down);
- input_sync(emumousebtn);
- return 1;
- }
- mouse_last_keycode = down ? keycode : 0;
- }
- break;
- }
- return 0;
-}
-
-EXPORT_SYMBOL(mac_hid_mouse_emulate_buttons);
-
-static int emumousebtn_input_register(void)
-{
- emumousebtn = input_allocate_device();
- if (!emumousebtn)
+ mac_hid_sysctl_header = register_sysctl_table(mac_hid_root_dir);
+ if (!mac_hid_sysctl_header)
return -ENOMEM;
- emumousebtn->name = "Macintosh mouse button emulation";
- emumousebtn->id.bustype = BUS_ADB;
- emumousebtn->id.vendor = 0x0001;
- emumousebtn->id.product = 0x0001;
- emumousebtn->id.version = 0x0100;
-
- emumousebtn->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
- emumousebtn->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
- emumousebtn->relbit[0] = BIT(REL_X) | BIT(REL_Y);
-
- input_register_device(emumousebtn);
-
return 0;
}
+module_init(mac_hid_init);
-int __init mac_hid_init(void)
+static void __exit mac_hid_exit(void)
{
- int err;
+ unregister_sysctl_table(mac_hid_sysctl_header);
- err = emumousebtn_input_register();
- if (err)
- return err;
-
-#if defined(CONFIG_SYSCTL)
- mac_hid_sysctl_header = register_sysctl_table(mac_hid_root_dir, 1);
-#endif /* CONFIG_SYSCTL */
-
- return 0;
+ if (mouse_emulate_buttons)
+ mac_hid_stop_emulation();
}
-
-device_initcall(mac_hid_init);
+module_exit(mac_hid_exit);
diff --git a/drivers/macintosh/macio-adb.c b/drivers/macintosh/macio-adb.c
index 4b08852c35e..87de8d9bcfa 100644
--- a/drivers/macintosh/macio-adb.c
+++ b/drivers/macintosh/macio-adb.c
@@ -6,7 +6,6 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/delay.h>
-#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <asm/prom.h>
@@ -15,7 +14,6 @@
#include <asm/pgtable.h>
#include <asm/hydra.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <linux/init.h>
#include <linux/ioport.h>
@@ -64,7 +62,7 @@ static DEFINE_SPINLOCK(macio_lock);
static int macio_probe(void);
static int macio_init(void);
-static irqreturn_t macio_adb_interrupt(int irq, void *arg, struct pt_regs *regs);
+static irqreturn_t macio_adb_interrupt(int irq, void *arg);
static int macio_send_request(struct adb_request *req, int sync);
static int macio_adb_autopoll(int devs);
static void macio_adb_poll(void);
@@ -83,7 +81,14 @@ struct adb_driver macio_adb_driver = {
int macio_probe(void)
{
- return find_compatible_devices("adb", "chrp,adb0")? 0: -ENODEV;
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, "adb", "chrp,adb0");
+ if (np) {
+ of_node_put(np);
+ return 0;
+ }
+ return -ENODEV;
}
int macio_init(void)
@@ -92,12 +97,14 @@ int macio_init(void)
struct resource r;
unsigned int irq;
- adbs = find_compatible_devices("adb", "chrp,adb0");
+ adbs = of_find_compatible_node(NULL, "adb", "chrp,adb0");
if (adbs == 0)
return -ENXIO;
- if (of_address_to_resource(adbs, 0, &r))
+ if (of_address_to_resource(adbs, 0, &r)) {
+ of_node_put(adbs);
return -ENXIO;
+ }
adb = ioremap(r.start, sizeof(struct adb_regs));
out_8(&adb->ctrl.r, 0);
@@ -108,6 +115,7 @@ int macio_init(void)
out_8(&adb->autopoll.r, APE);
irq = irq_of_parse_and_map(adbs, 0);
+ of_node_put(adbs);
if (request_irq(irq, macio_adb_interrupt, 0, "ADB", (void *)0)) {
printk(KERN_ERR "ADB: can't get irq %d\n", irq);
return -EAGAIN;
@@ -138,7 +146,7 @@ static int macio_adb_reset_bus(void)
/* Hrm... we may want to not lock interrupts for so
* long ... oh well, who uses that chip anyway ? :)
- * That function will be seldomly used during boot
+ * That function will be seldom used during boot
* on rare machines, so...
*/
spin_lock_irqsave(&macio_lock, flags);
@@ -146,6 +154,7 @@ static int macio_adb_reset_bus(void)
while ((in_8(&adb->ctrl.r) & ADB_RST) != 0) {
if (--timeout == 0) {
out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) & ~ADB_RST);
+ spin_unlock_irqrestore(&macio_lock, flags);
return -1;
}
}
@@ -189,8 +198,7 @@ static int macio_send_request(struct adb_request *req, int sync)
return 0;
}
-static irqreturn_t macio_adb_interrupt(int irq, void *arg,
- struct pt_regs *regs)
+static irqreturn_t macio_adb_interrupt(int irq, void *arg)
{
int i, n, err;
struct adb_request *req = NULL;
@@ -260,7 +268,7 @@ static irqreturn_t macio_adb_interrupt(int irq, void *arg,
(*done)(req);
}
if (ibuf_len)
- adb_input(ibuf, ibuf_len, regs, autopoll);
+ adb_input(ibuf, ibuf_len, autopoll);
return IRQ_RETVAL(handled);
}
@@ -271,6 +279,6 @@ static void macio_adb_poll(void)
local_irq_save(flags);
if (in_8(&adb->intr.r) != 0)
- macio_adb_interrupt(0, NULL, NULL);
+ macio_adb_interrupt(0, NULL);
local_irq_restore(flags);
}
diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c
index d5621606754..4f12c6f01fe 100644
--- a/drivers/macintosh/macio_asic.c
+++ b/drivers/macintosh/macio_asic.c
@@ -24,6 +24,8 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <asm/machdep.h>
#include <asm/macio.h>
@@ -33,20 +35,18 @@
#undef DEBUG
-#define MAX_NODE_NAME_SIZE (BUS_ID_SIZE - 12)
+#define MAX_NODE_NAME_SIZE (20 - 12)
static struct macio_chip *macio_on_hold;
static int macio_bus_match(struct device *dev, struct device_driver *drv)
{
- struct macio_dev * macio_dev = to_macio_device(dev);
- struct macio_driver * macio_drv = to_macio_driver(drv);
- const struct of_device_id * matches = macio_drv->match_table;
+ const struct of_device_id * matches = drv->of_match_table;
if (!matches)
return 0;
- return of_match_device(matches, &macio_dev->ofdev) != NULL;
+ return of_match_device(matches, dev) != NULL;
}
struct macio_dev *macio_dev_get(struct macio_dev *dev)
@@ -84,7 +84,7 @@ static int macio_device_probe(struct device *dev)
macio_dev_get(macio_dev);
- match = of_match_device(drv->match_table, &macio_dev->ofdev);
+ match = of_match_device(drv->driver.of_match_table, dev);
if (match)
error = drv->probe(macio_dev, match);
if (error)
@@ -134,108 +134,12 @@ static int macio_device_resume(struct device * dev)
return 0;
}
-static int macio_uevent(struct device *dev, char **envp, int num_envp,
- char *buffer, int buffer_size)
-{
- struct macio_dev * macio_dev;
- struct of_device * of;
- char *scratch;
- const char *compat, *compat2;
-
- int i = 0;
- int length, cplen, cplen2, seen = 0;
-
- if (!dev)
- return -ENODEV;
-
- macio_dev = to_macio_device(dev);
- if (!macio_dev)
- return -ENODEV;
-
- of = &macio_dev->ofdev;
-
- /* stuff we want to pass to /sbin/hotplug */
- envp[i++] = scratch = buffer;
- length = scnprintf (scratch, buffer_size, "OF_NAME=%s", of->node->name);
- ++length;
- buffer_size -= length;
- if ((buffer_size <= 0) || (i >= num_envp))
- return -ENOMEM;
- scratch += length;
-
- envp[i++] = scratch;
- length = scnprintf (scratch, buffer_size, "OF_TYPE=%s", of->node->type);
- ++length;
- buffer_size -= length;
- if ((buffer_size <= 0) || (i >= num_envp))
- return -ENOMEM;
- scratch += length;
-
- /* Since the compatible field can contain pretty much anything
- * it's not really legal to split it out with commas. We split it
- * up using a number of environment variables instead. */
-
- compat = get_property(of->node, "compatible", &cplen);
- compat2 = compat;
- cplen2= cplen;
- while (compat && cplen > 0) {
- envp[i++] = scratch;
- length = scnprintf (scratch, buffer_size,
- "OF_COMPATIBLE_%d=%s", seen, compat);
- ++length;
- buffer_size -= length;
- if ((buffer_size <= 0) || (i >= num_envp))
- return -ENOMEM;
- scratch += length;
- length = strlen (compat) + 1;
- compat += length;
- cplen -= length;
- seen++;
- }
-
- envp[i++] = scratch;
- length = scnprintf (scratch, buffer_size, "OF_COMPATIBLE_N=%d", seen);
- ++length;
- buffer_size -= length;
- if ((buffer_size <= 0) || (i >= num_envp))
- return -ENOMEM;
- scratch += length;
-
- envp[i++] = scratch;
- length = scnprintf (scratch, buffer_size, "MODALIAS=of:N%sT%s",
- of->node->name, of->node->type);
- /* overwrite '\0' */
- buffer_size -= length;
- if ((buffer_size <= 0) || (i >= num_envp))
- return -ENOMEM;
- scratch += length;
-
- if (!compat2) {
- compat2 = "";
- cplen2 = 1;
- }
- while (cplen2 > 0) {
- length = snprintf (scratch, buffer_size, "C%s", compat2);
- buffer_size -= length;
- if (buffer_size <= 0)
- return -ENOMEM;
- scratch += length;
- length = strlen (compat2) + 1;
- compat2 += length;
- cplen2 -= length;
- }
-
- envp[i] = NULL;
-
- return 0;
-}
-
extern struct device_attribute macio_dev_attrs[];
struct bus_type macio_bus_type = {
.name = "macio",
.match = macio_bus_match,
- .uevent = macio_uevent,
+ .uevent = of_device_uevent_modalias,
.probe = macio_device_probe,
.remove = macio_device_remove,
.shutdown = macio_device_shutdown,
@@ -336,7 +240,7 @@ static void macio_create_fixup_irq(struct macio_dev *dev, int index,
if (irq != NO_IRQ) {
dev->interrupt[index].start = irq;
dev->interrupt[index].flags = IORESOURCE_IRQ;
- dev->interrupt[index].name = dev->ofdev.dev.bus_id;
+ dev->interrupt[index].name = dev_name(&dev->ofdev.dev);
}
if (dev->n_interrupts <= index)
dev->n_interrupts = index + 1;
@@ -344,7 +248,7 @@ static void macio_create_fixup_irq(struct macio_dev *dev, int index,
static void macio_add_missing_resources(struct macio_dev *dev)
{
- struct device_node *np = dev->ofdev.node;
+ struct device_node *np = dev->ofdev.dev.of_node;
unsigned int irq_base;
/* Gatwick has some missing interrupts on child nodes */
@@ -385,21 +289,22 @@ static void macio_add_missing_resources(struct macio_dev *dev)
static void macio_setup_interrupts(struct macio_dev *dev)
{
- struct device_node *np = dev->ofdev.node;
+ struct device_node *np = dev->ofdev.dev.of_node;
unsigned int irq;
int i = 0, j = 0;
for (;;) {
- struct resource *res = &dev->interrupt[j];
+ struct resource *res;
if (j >= MACIO_DEV_COUNT_IRQS)
break;
+ res = &dev->interrupt[j];
irq = irq_of_parse_and_map(np, i++);
if (irq == NO_IRQ)
break;
res->start = irq;
res->flags = IORESOURCE_IRQ;
- res->name = dev->ofdev.dev.bus_id;
+ res->name = dev_name(&dev->ofdev.dev);
if (macio_resource_quirks(np, res, i - 1)) {
memset(res, 0, sizeof(struct resource));
continue;
@@ -412,16 +317,17 @@ static void macio_setup_interrupts(struct macio_dev *dev)
static void macio_setup_resources(struct macio_dev *dev,
struct resource *parent_res)
{
- struct device_node *np = dev->ofdev.node;
+ struct device_node *np = dev->ofdev.dev.of_node;
struct resource r;
int index;
for (index = 0; of_address_to_resource(np, index, &r) == 0; index++) {
- struct resource *res = &dev->resource[index];
+ struct resource *res;
if (index >= MACIO_DEV_COUNT_RESOURCES)
break;
+ res = &dev->resource[index];
*res = r;
- res->name = dev->ofdev.dev.bus_id;
+ res->name = dev_name(&dev->ofdev.dev);
if (macio_resource_quirks(np, res, index)) {
memset(res, 0, sizeof(struct resource));
@@ -434,7 +340,7 @@ static void macio_setup_resources(struct macio_dev *dev,
if (insert_resource(parent_res, res)) {
printk(KERN_WARNING "Can't request resource "
"%d for MacIO device %s\n",
- index, dev->ofdev.dev.bus_id);
+ index, dev_name(&dev->ofdev.dev));
}
}
dev->n_resources = index;
@@ -461,19 +367,33 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip,
if (np == NULL)
return NULL;
- dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return NULL;
- memset(dev, 0, sizeof(*dev));
dev->bus = &chip->lbus;
dev->media_bay = in_bay;
- dev->ofdev.node = np;
- dev->ofdev.dma_mask = 0xffffffffUL;
- dev->ofdev.dev.dma_mask = &dev->ofdev.dma_mask;
+ dev->ofdev.dev.of_node = np;
+ dev->ofdev.archdata.dma_mask = 0xffffffffUL;
+ dev->ofdev.dev.dma_mask = &dev->ofdev.archdata.dma_mask;
dev->ofdev.dev.parent = parent;
dev->ofdev.dev.bus = &macio_bus_type;
dev->ofdev.dev.release = macio_release_dev;
+ dev->ofdev.dev.dma_parms = &dev->dma_parms;
+
+ /* Standard DMA paremeters */
+ dma_set_max_seg_size(&dev->ofdev.dev, 65536);
+ dma_set_seg_boundary(&dev->ofdev.dev, 0xffffffff);
+
+#ifdef CONFIG_PCI
+ /* Set the DMA ops to the ones from the PCI device, this could be
+ * fishy if we didn't know that on PowerMac it's always direct ops
+ * or iommu ops that will work fine
+ *
+ * To get all the fields, copy all archdata
+ */
+ dev->ofdev.dev.archdata = chip->lbus.pdev->dev.archdata;
+#endif /* CONFIG_PCI */
#ifdef DEBUG
printk("preparing mdev @%p, ofdev @%p, dev @%p, kobj @%p\n",
@@ -482,8 +402,8 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip,
/* MacIO itself has a different reg, we use it's PCI base */
if (np == chip->of_node) {
- sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s",
- chip->lbus.index,
+ dev_set_name(&dev->ofdev.dev, "%1d.%08x:%.*s",
+ chip->lbus.index,
#ifdef CONFIG_PCI
(unsigned int)pci_resource_start(chip->lbus.pdev, 0),
#else
@@ -491,10 +411,10 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip,
#endif
MAX_NODE_NAME_SIZE, np->name);
} else {
- reg = get_property(np, "reg", NULL);
- sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s",
- chip->lbus.index,
- reg ? *reg : 0, MAX_NODE_NAME_SIZE, np->name);
+ reg = of_get_property(np, "reg", NULL);
+ dev_set_name(&dev->ofdev.dev, "%1d.%08x:%.*s",
+ chip->lbus.index,
+ reg ? *reg : 0, MAX_NODE_NAME_SIZE, np->name);
}
/* Setup interrupts & resources */
@@ -505,7 +425,7 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip,
/* Register with core */
if (of_device_register(&dev->ofdev) != 0) {
printk(KERN_DEBUG"macio: device registration error for %s!\n",
- dev->ofdev.dev.bus_id);
+ dev_name(&dev->ofdev.dev));
kfree(dev);
return NULL;
}
@@ -573,9 +493,9 @@ static void macio_pci_add_devices(struct macio_chip *chip)
}
/* Add media bay devices if any */
- if (mbdev)
- for (np = NULL; (np = of_get_next_child(mbdev->ofdev.node, np))
- != NULL;) {
+ if (mbdev) {
+ pnode = mbdev->ofdev.dev.of_node;
+ for (np = NULL; (np = of_get_next_child(pnode, np)) != NULL;) {
if (macio_skip_device(np))
continue;
of_node_get(np);
@@ -583,11 +503,12 @@ static void macio_pci_add_devices(struct macio_chip *chip)
mbdev, root_res) == NULL)
of_node_put(np);
}
+ }
/* Add serial ports if any */
if (sdev) {
- for (np = NULL; (np = of_get_next_child(sdev->ofdev.node, np))
- != NULL;) {
+ pnode = sdev->ofdev.dev.of_node;
+ for (np = NULL; (np = of_get_next_child(pnode, np)) != NULL;) {
if (macio_skip_device(np))
continue;
of_node_get(np);
@@ -606,7 +527,6 @@ static void macio_pci_add_devices(struct macio_chip *chip)
int macio_register_driver(struct macio_driver *drv)
{
/* initialize common driver fields */
- drv->driver.name = drv->name;
drv->driver.bus = &macio_bus_type;
/* register with core */
@@ -622,6 +542,42 @@ void macio_unregister_driver(struct macio_driver *drv)
driver_unregister(&drv->driver);
}
+/* Managed MacIO resources */
+struct macio_devres {
+ u32 res_mask;
+};
+
+static void maciom_release(struct device *gendev, void *res)
+{
+ struct macio_dev *dev = to_macio_device(gendev);
+ struct macio_devres *dr = res;
+ int i, max;
+
+ max = min(dev->n_resources, 32);
+ for (i = 0; i < max; i++) {
+ if (dr->res_mask & (1 << i))
+ macio_release_resource(dev, i);
+ }
+}
+
+int macio_enable_devres(struct macio_dev *dev)
+{
+ struct macio_devres *dr;
+
+ dr = devres_find(&dev->ofdev.dev, maciom_release, NULL, NULL);
+ if (!dr) {
+ dr = devres_alloc(maciom_release, sizeof(*dr), GFP_KERNEL);
+ if (!dr)
+ return -ENOMEM;
+ }
+ return devres_get(&dev->ofdev.dev, dr, NULL, NULL) != NULL;
+}
+
+static struct macio_devres * find_macio_dr(struct macio_dev *dev)
+{
+ return devres_find(&dev->ofdev.dev, maciom_release, NULL, NULL);
+}
+
/**
* macio_request_resource - Request an MMIO resource
* @dev: pointer to the device holding the resource
@@ -639,6 +595,8 @@ void macio_unregister_driver(struct macio_driver *drv)
int macio_request_resource(struct macio_dev *dev, int resource_no,
const char *name)
{
+ struct macio_devres *dr = find_macio_dr(dev);
+
if (macio_resource_len(dev, resource_no) == 0)
return 0;
@@ -646,6 +604,9 @@ int macio_request_resource(struct macio_dev *dev, int resource_no,
macio_resource_len(dev, resource_no),
name))
goto err_out;
+
+ if (dr && resource_no < 32)
+ dr->res_mask |= 1 << resource_no;
return 0;
@@ -655,7 +616,7 @@ err_out:
resource_no,
macio_resource_len(dev, resource_no),
macio_resource_start(dev, resource_no),
- dev->ofdev.dev.bus_id);
+ dev_name(&dev->ofdev.dev));
return -EBUSY;
}
@@ -666,10 +627,14 @@ err_out:
*/
void macio_release_resource(struct macio_dev *dev, int resource_no)
{
+ struct macio_devres *dr = find_macio_dr(dev);
+
if (macio_resource_len(dev, resource_no) == 0)
return;
release_mem_region(macio_resource_start(dev, resource_no),
macio_resource_len(dev, resource_no));
+ if (dr && resource_no < 32)
+ dr->res_mask &= ~(1 << resource_no);
}
/**
@@ -716,7 +681,7 @@ void macio_release_resources(struct macio_dev *dev)
#ifdef CONFIG_PCI
-static int __devinit macio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int macio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct device_node* np;
struct macio_chip* chip;
@@ -776,7 +741,7 @@ static int __devinit macio_pci_probe(struct pci_dev *pdev, const struct pci_devi
return 0;
}
-static void __devexit macio_pci_remove(struct pci_dev* pdev)
+static void macio_pci_remove(struct pci_dev* pdev)
{
panic("removing of macio-asic not supported !\n");
}
@@ -785,7 +750,7 @@ static void __devexit macio_pci_remove(struct pci_dev* pdev)
* MacIO is matched against any Apple ID, it's probe() function
* will then decide wether it applies or not
*/
-static const struct pci_device_id __devinitdata pci_ids [] = { {
+static const struct pci_device_id pci_ids[] = { {
.vendor = PCI_VENDOR_ID_APPLE,
.device = PCI_ANY_ID,
.subvendor = PCI_ANY_ID,
@@ -828,3 +793,5 @@ EXPORT_SYMBOL(macio_request_resource);
EXPORT_SYMBOL(macio_release_resource);
EXPORT_SYMBOL(macio_request_resources);
EXPORT_SYMBOL(macio_release_resources);
+EXPORT_SYMBOL(macio_enable_devres);
+
diff --git a/drivers/macintosh/macio_sysfs.c b/drivers/macintosh/macio_sysfs.c
index 8566bdfdd4b..8eb40afbd0f 100644
--- a/drivers/macintosh/macio_sysfs.c
+++ b/drivers/macintosh/macio_sysfs.c
@@ -9,19 +9,19 @@ field##_show (struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
struct macio_dev *mdev = to_macio_device (dev); \
- return sprintf (buf, format_string, mdev->ofdev.node->field); \
+ return sprintf (buf, format_string, mdev->ofdev.dev.of_node->field); \
}
static ssize_t
compatible_show (struct device *dev, struct device_attribute *attr, char *buf)
{
- struct of_device *of;
+ struct platform_device *of;
const char *compat;
int cplen;
int length = 0;
of = &to_macio_device (dev)->ofdev;
- compat = get_property(of->node, "compatible", &cplen);
+ compat = of_get_property(of->dev.of_node, "compatible", &cplen);
if (!compat) {
*buf = '\0';
return 0;
@@ -41,26 +41,21 @@ compatible_show (struct device *dev, struct device_attribute *attr, char *buf)
static ssize_t modalias_show (struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct of_device *of;
- const char *compat;
- int cplen;
- int length;
+ int len = of_device_get_modalias(dev, buf, PAGE_SIZE - 2);
- of = &to_macio_device (dev)->ofdev;
- compat = get_property(of->node, "compatible", &cplen);
- if (!compat) compat = "", cplen = 1;
- length = sprintf (buf, "of:N%sT%s", of->node->name, of->node->type);
- buf += length;
- while (cplen > 0) {
- int l;
- length += sprintf (buf, "C%s", compat);
- buf += length;
- l = strlen (compat) + 1;
- compat += l;
- cplen -= l;
- }
+ buf[len] = '\n';
+ buf[len+1] = 0;
- return length;
+ return len+1;
+}
+
+static ssize_t devspec_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *ofdev;
+
+ ofdev = to_platform_device(dev);
+ return sprintf(buf, "%s\n", ofdev->dev.of_node->full_name);
}
macio_config_of_attr (name, "%s\n");
@@ -71,5 +66,6 @@ struct device_attribute macio_dev_attrs[] = {
__ATTR_RO(type),
__ATTR_RO(compatible),
__ATTR_RO(modalias),
+ __ATTR_RO(devspec),
__ATTR_NULL
};
diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c
index 0acf2f7fd9d..d98e566a8f5 100644
--- a/drivers/macintosh/mediabay.c
+++ b/drivers/macintosh/mediabay.c
@@ -16,10 +16,10 @@
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/timer.h>
-#include <linux/hdreg.h>
#include <linux/stddef.h>
#include <linux/init.h>
-#include <linux/ide.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
#include <asm/prom.h>
#include <asm/pgtable.h>
#include <asm/io.h>
@@ -33,16 +33,6 @@
#include <linux/adb.h>
#include <linux/pmu.h>
-
-#define MB_DEBUG
-#define MB_IGNORE_SIGNALS
-
-#ifdef MB_DEBUG
-#define MBDBG(fmt, arg...) printk(KERN_INFO fmt , ## arg)
-#else
-#define MBDBG(fmt, arg...) do { } while (0)
-#endif
-
#define MB_FCR32(bay, r) ((bay)->base + ((r) >> 2))
#define MB_FCR8(bay, r) (((volatile u8 __iomem *)((bay)->base)) + (r))
@@ -73,29 +63,18 @@ struct media_bay_info {
int value_count;
int timer;
struct macio_dev *mdev;
- struct mb_ops* ops;
+ const struct mb_ops* ops;
int index;
int cached_gpio;
int sleeping;
- struct semaphore lock;
-#ifdef CONFIG_BLK_DEV_IDE
- void __iomem *cd_base;
- int cd_index;
- int cd_irq;
- int cd_retry;
-#endif
+ int user_lock;
+ struct mutex lock;
};
#define MAX_BAYS 2
static struct media_bay_info media_bays[MAX_BAYS];
-int media_bay_count = 0;
-
-#ifdef CONFIG_BLK_DEV_IDE
-/* check the busy bit in the media-bay ide interface
- (assumes the media-bay contains an ide device) */
-#define MB_IDE_READY(i) ((readb(media_bays[i].cd_base + 0x70) & 0x80) == 0)
-#endif
+static int media_bay_count = 0;
/*
* Wait that number of ms between each step in normal polling mode
@@ -128,21 +107,11 @@ int media_bay_count = 0;
/*
* Wait this many ticks after an IDE device (e.g. CD-ROM) is inserted
- * (or until the device is ready) before waiting for busy bit to disappear
+ * (or until the device is ready) before calling into the driver
*/
#define MB_IDE_WAIT 1000
/*
- * Timeout waiting for busy bit of an IDE device to go down
- */
-#define MB_IDE_TIMEOUT 5000
-
-/*
- * Max retries of the full power up/down sequence for an IDE device
- */
-#define MAX_CD_RETRIES 3
-
-/*
* States of a media bay
*/
enum {
@@ -151,7 +120,6 @@ enum {
mb_enabling_bay, /* enable bits set, waiting MB_RESET_DELAY */
mb_resetting, /* reset bit unset, waiting MB_SETUP_DELAY */
mb_ide_resetting, /* IDE reset bit unser, waiting MB_IDE_WAIT */
- mb_ide_waiting, /* Waiting for BUSY bit to go away until MB_IDE_TIMEOUT */
mb_up, /* Media bay full */
mb_powering_down /* Powering down (avoid too fast down/up) */
};
@@ -371,12 +339,12 @@ static inline void set_mb_power(struct media_bay_info* bay, int onoff)
if (onoff) {
bay->ops->power(bay, 1);
bay->state = mb_powering_up;
- MBDBG("mediabay%d: powering up\n", bay->index);
+ pr_debug("mediabay%d: powering up\n", bay->index);
} else {
/* Make sure everything is powered down & disabled */
bay->ops->power(bay, 0);
bay->state = mb_powering_down;
- MBDBG("mediabay%d: powering down\n", bay->index);
+ pr_debug("mediabay%d: powering down\n", bay->index);
}
bay->timer = msecs_to_jiffies(MB_POWER_DELAY);
}
@@ -385,107 +353,117 @@ static void poll_media_bay(struct media_bay_info* bay)
{
int id = bay->ops->content(bay);
- if (id == bay->last_value) {
- if (id != bay->content_id) {
- bay->value_count += msecs_to_jiffies(MB_POLL_DELAY);
- if (bay->value_count >= msecs_to_jiffies(MB_STABLE_DELAY)) {
- /* If the device type changes without going thru
- * "MB_NO", we force a pass by "MB_NO" to make sure
- * things are properly reset
- */
- if ((id != MB_NO) && (bay->content_id != MB_NO)) {
- id = MB_NO;
- MBDBG("mediabay%d: forcing MB_NO\n", bay->index);
- }
- MBDBG("mediabay%d: switching to %d\n", bay->index, id);
- set_mb_power(bay, id != MB_NO);
- bay->content_id = id;
- if (id == MB_NO) {
-#ifdef CONFIG_BLK_DEV_IDE
- bay->cd_retry = 0;
-#endif
- printk(KERN_INFO "media bay %d is empty\n", bay->index);
- }
- }
- }
- } else {
+ static char *mb_content_types[] = {
+ "a floppy drive",
+ "a floppy drive",
+ "an unsupported audio device",
+ "an ATA device",
+ "an unsupported PCI device",
+ "an unknown device",
+ };
+
+ if (id != bay->last_value) {
bay->last_value = id;
bay->value_count = 0;
+ return;
+ }
+ if (id == bay->content_id)
+ return;
+
+ bay->value_count += msecs_to_jiffies(MB_POLL_DELAY);
+ if (bay->value_count >= msecs_to_jiffies(MB_STABLE_DELAY)) {
+ /* If the device type changes without going thru
+ * "MB_NO", we force a pass by "MB_NO" to make sure
+ * things are properly reset
+ */
+ if ((id != MB_NO) && (bay->content_id != MB_NO)) {
+ id = MB_NO;
+ pr_debug("mediabay%d: forcing MB_NO\n", bay->index);
+ }
+ pr_debug("mediabay%d: switching to %d\n", bay->index, id);
+ set_mb_power(bay, id != MB_NO);
+ bay->content_id = id;
+ if (id >= MB_NO || id < 0)
+ printk(KERN_INFO "mediabay%d: Bay is now empty\n", bay->index);
+ else
+ printk(KERN_INFO "mediabay%d: Bay contains %s\n",
+ bay->index, mb_content_types[id]);
}
}
-int check_media_bay(struct device_node *which_bay, int what)
+int check_media_bay(struct macio_dev *baydev)
{
-#ifdef CONFIG_BLK_DEV_IDE
- int i;
+ struct media_bay_info* bay;
+ int id;
- for (i=0; i<media_bay_count; i++)
- if (media_bays[i].mdev && which_bay == media_bays[i].mdev->ofdev.node) {
- if ((what == media_bays[i].content_id) && media_bays[i].state == mb_up)
- return 0;
- media_bays[i].cd_index = -1;
- return -EINVAL;
- }
-#endif /* CONFIG_BLK_DEV_IDE */
- return -ENODEV;
+ if (baydev == NULL)
+ return MB_NO;
+
+ /* This returns an instant snapshot, not locking, sine
+ * we may be called with the bay lock held. The resulting
+ * fuzzyness of the result if called at the wrong time is
+ * not actually a huge deal
+ */
+ bay = macio_get_drvdata(baydev);
+ if (bay == NULL)
+ return MB_NO;
+ id = bay->content_id;
+ if (bay->state != mb_up)
+ return MB_NO;
+ if (id == MB_FD1)
+ return MB_FD;
+ return id;
}
-EXPORT_SYMBOL(check_media_bay);
+EXPORT_SYMBOL_GPL(check_media_bay);
-int check_media_bay_by_base(unsigned long base, int what)
+void lock_media_bay(struct macio_dev *baydev)
{
-#ifdef CONFIG_BLK_DEV_IDE
- int i;
+ struct media_bay_info* bay;
- for (i=0; i<media_bay_count; i++)
- if (media_bays[i].mdev && base == (unsigned long) media_bays[i].cd_base) {
- if ((what == media_bays[i].content_id) && media_bays[i].state == mb_up)
- return 0;
- media_bays[i].cd_index = -1;
- return -EINVAL;
- }
-#endif
-
- return -ENODEV;
+ if (baydev == NULL)
+ return;
+ bay = macio_get_drvdata(baydev);
+ if (bay == NULL)
+ return;
+ mutex_lock(&bay->lock);
+ bay->user_lock = 1;
}
+EXPORT_SYMBOL_GPL(lock_media_bay);
-int media_bay_set_ide_infos(struct device_node* which_bay, unsigned long base,
- int irq, int index)
+void unlock_media_bay(struct macio_dev *baydev)
{
-#ifdef CONFIG_BLK_DEV_IDE
- int i;
+ struct media_bay_info* bay;
- for (i=0; i<media_bay_count; i++) {
- struct media_bay_info* bay = &media_bays[i];
-
- if (bay->mdev && which_bay == bay->mdev->ofdev.node) {
- int timeout = 5000;
-
- down(&bay->lock);
-
- bay->cd_base = (void __iomem *) base;
- bay->cd_irq = irq;
-
- if ((MB_CD != bay->content_id) || bay->state != mb_up) {
- up(&bay->lock);
- return 0;
- }
- printk(KERN_DEBUG "Registered ide%d for media bay %d\n", index, i);
- do {
- if (MB_IDE_READY(i)) {
- bay->cd_index = index;
- up(&bay->lock);
- return 0;
- }
- mdelay(1);
- } while(--timeout);
- printk(KERN_DEBUG "Timeount waiting IDE in bay %d\n", i);
- up(&bay->lock);
- return -ENODEV;
- }
+ if (baydev == NULL)
+ return;
+ bay = macio_get_drvdata(baydev);
+ if (bay == NULL)
+ return;
+ if (bay->user_lock) {
+ bay->user_lock = 0;
+ mutex_unlock(&bay->lock);
}
-#endif /* CONFIG_BLK_DEV_IDE */
-
- return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(unlock_media_bay);
+
+static int mb_broadcast_hotplug(struct device *dev, void *data)
+{
+ struct media_bay_info* bay = data;
+ struct macio_dev *mdev;
+ struct macio_driver *drv;
+ int state;
+
+ if (dev->bus != &macio_bus_type)
+ return 0;
+
+ state = bay->state == mb_up ? bay->content_id : MB_NO;
+ if (state == MB_FD1)
+ state = MB_FD;
+ mdev = to_macio_device(dev);
+ drv = to_macio_driver(dev->driver);
+ if (dev->driver && drv->mediabay_event)
+ drv->mediabay_event(mdev, state);
+ return 0;
}
static void media_bay_step(int i)
@@ -496,8 +474,8 @@ static void media_bay_step(int i)
if (bay->state != mb_powering_down)
poll_media_bay(bay);
- /* If timer expired or polling IDE busy, run state machine */
- if ((bay->state != mb_ide_waiting) && (bay->timer != 0)) {
+ /* If timer expired run state machine */
+ if (bay->timer != 0) {
bay->timer -= msecs_to_jiffies(MB_POLL_DELAY);
if (bay->timer > 0)
return;
@@ -507,107 +485,50 @@ static void media_bay_step(int i)
switch(bay->state) {
case mb_powering_up:
if (bay->ops->setup_bus(bay, bay->last_value) < 0) {
- MBDBG("mediabay%d: device not supported (kind:%d)\n", i, bay->content_id);
+ pr_debug("mediabay%d: device not supported (kind:%d)\n",
+ i, bay->content_id);
set_mb_power(bay, 0);
break;
}
bay->timer = msecs_to_jiffies(MB_RESET_DELAY);
bay->state = mb_enabling_bay;
- MBDBG("mediabay%d: enabling (kind:%d)\n", i, bay->content_id);
+ pr_debug("mediabay%d: enabling (kind:%d)\n", i, bay->content_id);
break;
case mb_enabling_bay:
bay->ops->un_reset(bay);
bay->timer = msecs_to_jiffies(MB_SETUP_DELAY);
bay->state = mb_resetting;
- MBDBG("mediabay%d: waiting reset (kind:%d)\n", i, bay->content_id);
+ pr_debug("mediabay%d: releasing bay reset (kind:%d)\n",
+ i, bay->content_id);
break;
-
case mb_resetting:
if (bay->content_id != MB_CD) {
- MBDBG("mediabay%d: bay is up (kind:%d)\n", i, bay->content_id);
+ pr_debug("mediabay%d: bay is up (kind:%d)\n", i,
+ bay->content_id);
bay->state = mb_up;
+ device_for_each_child(&bay->mdev->ofdev.dev,
+ bay, mb_broadcast_hotplug);
break;
}
-#ifdef CONFIG_BLK_DEV_IDE
- MBDBG("mediabay%d: waiting IDE reset (kind:%d)\n", i, bay->content_id);
+ pr_debug("mediabay%d: releasing ATA reset (kind:%d)\n",
+ i, bay->content_id);
bay->ops->un_reset_ide(bay);
bay->timer = msecs_to_jiffies(MB_IDE_WAIT);
bay->state = mb_ide_resetting;
-#else
- printk(KERN_DEBUG "media-bay %d is ide (not compiled in kernel)\n", i);
- set_mb_power(bay, 0);
-#endif /* CONFIG_BLK_DEV_IDE */
break;
-
-#ifdef CONFIG_BLK_DEV_IDE
+
case mb_ide_resetting:
- bay->timer = msecs_to_jiffies(MB_IDE_TIMEOUT);
- bay->state = mb_ide_waiting;
- MBDBG("mediabay%d: waiting IDE ready (kind:%d)\n", i, bay->content_id);
+ pr_debug("mediabay%d: bay is up (kind:%d)\n", i, bay->content_id);
+ bay->state = mb_up;
+ device_for_each_child(&bay->mdev->ofdev.dev,
+ bay, mb_broadcast_hotplug);
break;
-
- case mb_ide_waiting:
- if (bay->cd_base == NULL) {
- bay->timer = 0;
- bay->state = mb_up;
- MBDBG("mediabay%d: up before IDE init\n", i);
- break;
- } else if (MB_IDE_READY(i)) {
- bay->timer = 0;
- bay->state = mb_up;
- if (bay->cd_index < 0) {
- hw_regs_t hw;
-
- printk("mediabay %d, registering IDE...\n", i);
- pmu_suspend();
- ide_init_hwif_ports(&hw, (unsigned long) bay->cd_base, (unsigned long) 0, NULL);
- hw.irq = bay->cd_irq;
- hw.chipset = ide_pmac;
- bay->cd_index = ide_register_hw(&hw, NULL);
- pmu_resume();
- }
- if (bay->cd_index == -1) {
- /* We eventually do a retry */
- bay->cd_retry++;
- printk("IDE register error\n");
- set_mb_power(bay, 0);
- } else {
- printk(KERN_DEBUG "media-bay %d is ide%d\n", i, bay->cd_index);
- MBDBG("mediabay %d IDE ready\n", i);
- }
- break;
- } else if (bay->timer > 0)
- bay->timer -= msecs_to_jiffies(MB_POLL_DELAY);
- if (bay->timer <= 0) {
- printk("\nIDE Timeout in bay %d !, IDE state is: 0x%02x\n",
- i, readb(bay->cd_base + 0x70));
- MBDBG("mediabay%d: nIDE Timeout !\n", i);
- set_mb_power(bay, 0);
- bay->timer = 0;
- }
- break;
-#endif /* CONFIG_BLK_DEV_IDE */
case mb_powering_down:
bay->state = mb_empty;
-#ifdef CONFIG_BLK_DEV_IDE
- if (bay->cd_index >= 0) {
- printk(KERN_DEBUG "Unregistering mb %d ide, index:%d\n", i,
- bay->cd_index);
- ide_unregister(bay->cd_index);
- bay->cd_index = -1;
- }
- if (bay->cd_retry) {
- if (bay->cd_retry > MAX_CD_RETRIES) {
- /* Should add an error sound (sort of beep in dmasound) */
- printk("\nmedia-bay %d, IDE device badly inserted or unrecognised\n", i);
- } else {
- /* Force a new power down/up sequence */
- bay->content_id = MB_NO;
- }
- }
-#endif /* CONFIG_BLK_DEV_IDE */
- MBDBG("mediabay%d: end of power down\n", i);
+ device_for_each_child(&bay->mdev->ofdev.dev,
+ bay, mb_broadcast_hotplug);
+ pr_debug("mediabay%d: end of power down\n", i);
break;
}
}
@@ -622,26 +543,21 @@ static int media_bay_task(void *x)
{
int i;
- strcpy(current->comm, "media-bay");
-#ifdef MB_IGNORE_SIGNALS
- sigfillset(&current->blocked);
-#endif
-
- for (;;) {
+ while (!kthread_should_stop()) {
for (i = 0; i < media_bay_count; ++i) {
- down(&media_bays[i].lock);
+ mutex_lock(&media_bays[i].lock);
if (!media_bays[i].sleeping)
media_bay_step(i);
- up(&media_bays[i].lock);
+ mutex_unlock(&media_bays[i].lock);
}
msleep_interruptible(MB_POLL_DELAY);
- if (signal_pending(current))
- return 0;
}
+ return 0;
}
-static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_device_id *match)
+static int media_bay_attach(struct macio_dev *mdev,
+ const struct of_device_id *match)
{
struct media_bay_info* bay;
u32 __iomem *regbase;
@@ -649,7 +565,7 @@ static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_de
unsigned long base;
int i;
- ofnode = mdev->ofdev.node;
+ ofnode = mdev->ofdev.dev.of_node;
if (macio_resource_count(mdev) < 1)
return -ENODEV;
@@ -673,7 +589,7 @@ static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_de
bay->index = i;
bay->ops = match->data;
bay->sleeping = 0;
- init_MUTEX(&bay->lock);
+ mutex_init(&bay->lock);
/* Init HW probing */
if (bay->ops->init)
@@ -688,18 +604,13 @@ static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_de
bay->last_value = bay->ops->content(bay);
bay->value_count = msecs_to_jiffies(MB_STABLE_DELAY);
bay->state = mb_empty;
- do {
- msleep(MB_POLL_DELAY);
- media_bay_step(i);
- } while((bay->state != mb_empty) &&
- (bay->state != mb_up));
/* Mark us ready by filling our mdev data */
macio_set_drvdata(mdev, bay);
/* Startup kernel thread */
if (i == 0)
- kernel_thread(media_bay_task, NULL, CLONE_KERNEL);
+ kthread_run(media_bay_task, NULL, "media-bay");
return 0;
@@ -709,11 +620,12 @@ static int media_bay_suspend(struct macio_dev *mdev, pm_message_t state)
{
struct media_bay_info *bay = macio_get_drvdata(mdev);
- if (state.event != mdev->ofdev.dev.power.power_state.event && state.event == PM_EVENT_SUSPEND) {
- down(&bay->lock);
+ if (state.event != mdev->ofdev.dev.power.power_state.event
+ && (state.event & PM_EVENT_SLEEP)) {
+ mutex_lock(&bay->lock);
bay->sleeping = 1;
set_mb_power(bay, 0);
- up(&bay->lock);
+ mutex_unlock(&bay->lock);
msleep(MB_POLL_DELAY);
mdev->ofdev.dev.power.power_state = state;
}
@@ -732,28 +644,25 @@ static int media_bay_resume(struct macio_dev *mdev)
they seem to help the 3400 get it right.
*/
/* Force MB power to 0 */
- down(&bay->lock);
+ mutex_lock(&bay->lock);
set_mb_power(bay, 0);
msleep(MB_POWER_DELAY);
if (bay->ops->content(bay) != bay->content_id) {
- printk("mediabay%d: content changed during sleep...\n", bay->index);
- up(&bay->lock);
+ printk("mediabay%d: Content changed during sleep...\n", bay->index);
+ mutex_unlock(&bay->lock);
return 0;
}
set_mb_power(bay, 1);
bay->last_value = bay->content_id;
bay->value_count = msecs_to_jiffies(MB_STABLE_DELAY);
bay->timer = msecs_to_jiffies(MB_POWER_DELAY);
-#ifdef CONFIG_BLK_DEV_IDE
- bay->cd_retry = 0;
-#endif
do {
msleep(MB_POLL_DELAY);
media_bay_step(bay->index);
} while((bay->state != mb_empty) &&
(bay->state != mb_up));
bay->sleeping = 0;
- up(&bay->lock);
+ mutex_unlock(&bay->lock);
}
return 0;
}
@@ -761,7 +670,7 @@ static int media_bay_resume(struct macio_dev *mdev)
/* Definitions of "ops" structures.
*/
-static struct mb_ops ohare_mb_ops = {
+static const struct mb_ops ohare_mb_ops = {
.name = "Ohare",
.content = ohare_mb_content,
.power = ohare_mb_power,
@@ -770,7 +679,7 @@ static struct mb_ops ohare_mb_ops = {
.un_reset_ide = ohare_mb_un_reset_ide,
};
-static struct mb_ops heathrow_mb_ops = {
+static const struct mb_ops heathrow_mb_ops = {
.name = "Heathrow",
.content = heathrow_mb_content,
.power = heathrow_mb_power,
@@ -779,7 +688,7 @@ static struct mb_ops heathrow_mb_ops = {
.un_reset_ide = heathrow_mb_un_reset_ide,
};
-static struct mb_ops keylargo_mb_ops = {
+static const struct mb_ops keylargo_mb_ops = {
.name = "KeyLargo",
.init = keylargo_mb_init,
.content = keylargo_mb_content,
@@ -820,8 +729,10 @@ static struct of_device_id media_bay_match[] =
static struct macio_driver media_bay_driver =
{
- .name = "media-bay",
- .match_table = media_bay_match,
+ .driver = {
+ .name = "media-bay",
+ .of_match_table = media_bay_match,
+ },
.probe = media_bay_attach,
.suspend = media_bay_suspend,
.resume = media_bay_resume
@@ -834,9 +745,6 @@ static int __init media_bay_init(void)
for (i=0; i<MAX_BAYS; i++) {
memset((char *)&media_bays[i], 0, sizeof(struct media_bay_info));
media_bays[i].content_id = -1;
-#ifdef CONFIG_BLK_DEV_IDE
- media_bays[i].cd_index = -1;
-#endif
}
if (!machine_is(powermac))
return 0;
diff --git a/drivers/macintosh/nvram.c b/drivers/macintosh/nvram.c
index 30791875fc9..f0e03e7937e 100644
--- a/drivers/macintosh/nvram.c
+++ b/drivers/macintosh/nvram.c
@@ -13,7 +13,6 @@
#include <linux/fcntl.h>
#include <linux/nvram.h>
#include <linux/init.h>
-#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/nvram.h>
@@ -21,21 +20,22 @@
static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
{
- lock_kernel();
switch (origin) {
+ case 0:
+ break;
case 1:
offset += file->f_pos;
break;
case 2:
offset += NVRAM_SIZE;
break;
+ default:
+ offset = -1;
}
- if (offset < 0) {
- unlock_kernel();
+ if (offset < 0)
return -EINVAL;
- }
+
file->f_pos = offset;
- unlock_kernel();
return file->f_pos;
}
@@ -76,8 +76,7 @@ static ssize_t write_nvram(struct file *file, const char __user *buf,
return p - buf;
}
-static int nvram_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static long nvram_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch(cmd) {
case PMAC_NVRAM_GET_OFFSET:
@@ -100,12 +99,12 @@ static int nvram_ioctl(struct inode *inode, struct file *file,
return 0;
}
-struct file_operations nvram_fops = {
+const struct file_operations nvram_fops = {
.owner = THIS_MODULE,
.llseek = nvram_llseek,
.read = read_nvram,
.write = write_nvram,
- .ioctl = nvram_ioctl,
+ .unlocked_ioctl = nvram_ioctl,
};
static struct miscdevice nvram_dev = {
diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c
new file mode 100644
index 00000000000..4192901cab4
--- /dev/null
+++ b/drivers/macintosh/rack-meter.c
@@ -0,0 +1,618 @@
+/*
+ * RackMac vu-meter driver
+ *
+ * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp.
+ * <benh@kernel.crashing.org>
+ *
+ * Released under the term of the GNU GPL v2.
+ *
+ * Support the CPU-meter LEDs of the Xserve G5
+ *
+ * TODO: Implement PWM to do variable intensity and provide userland
+ * interface for fun. Also, the CPU-meter could be made nicer by being
+ * a bit less "immediate" but giving instead a more average load over
+ * time. Patches welcome :-)
+ *
+ */
+#undef DEBUG
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel_stat.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/dbdma.h>
+#include <asm/macio.h>
+#include <asm/keylargo.h>
+
+/* Number of samples in a sample buffer */
+#define SAMPLE_COUNT 256
+
+/* CPU meter sampling rate in ms */
+#define CPU_SAMPLING_RATE 250
+
+struct rackmeter_dma {
+ struct dbdma_cmd cmd[4] ____cacheline_aligned;
+ u32 mark ____cacheline_aligned;
+ u32 buf1[SAMPLE_COUNT] ____cacheline_aligned;
+ u32 buf2[SAMPLE_COUNT] ____cacheline_aligned;
+} ____cacheline_aligned;
+
+struct rackmeter_cpu {
+ struct delayed_work sniffer;
+ struct rackmeter *rm;
+ cputime64_t prev_wall;
+ cputime64_t prev_idle;
+ int zero;
+} ____cacheline_aligned;
+
+struct rackmeter {
+ struct macio_dev *mdev;
+ unsigned int irq;
+ struct device_node *i2s;
+ u8 *ubuf;
+ struct dbdma_regs __iomem *dma_regs;
+ void __iomem *i2s_regs;
+ dma_addr_t dma_buf_p;
+ struct rackmeter_dma *dma_buf_v;
+ int stale_irq;
+ struct rackmeter_cpu cpu[2];
+ int paused;
+ struct mutex sem;
+};
+
+/* To be set as a tunable */
+static int rackmeter_ignore_nice;
+
+/* This GPIO is whacked by the OS X driver when initializing */
+#define RACKMETER_MAGIC_GPIO 0x78
+
+/* This is copied from cpufreq_ondemand, maybe we should put it in
+ * a common header somewhere
+ */
+static inline cputime64_t get_cpu_idle_time(unsigned int cpu)
+{
+ u64 retval;
+
+ retval = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE] +
+ kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT];
+
+ if (rackmeter_ignore_nice)
+ retval += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE];
+
+ return retval;
+}
+
+static void rackmeter_setup_i2s(struct rackmeter *rm)
+{
+ struct macio_chip *macio = rm->mdev->bus->chip;
+
+ /* First whack magic GPIO */
+ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, RACKMETER_MAGIC_GPIO, 5);
+
+
+ /* Call feature code to enable the sound channel and the proper
+ * clock sources
+ */
+ pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, rm->i2s, 0, 1);
+
+ /* Power i2s and stop i2s clock. We whack MacIO FCRs directly for now.
+ * This is a bit racy, thus we should add new platform functions to
+ * handle that. snd-aoa needs that too
+ */
+ MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_ENABLE);
+ MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT);
+ (void)MACIO_IN32(KEYLARGO_FCR1);
+ udelay(10);
+
+ /* Then setup i2s. For now, we use the same magic value that
+ * the OS X driver seems to use. We might want to play around
+ * with the clock divisors later
+ */
+ out_le32(rm->i2s_regs + 0x10, 0x01fa0000);
+ (void)in_le32(rm->i2s_regs + 0x10);
+ udelay(10);
+
+ /* Fully restart i2s*/
+ MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE |
+ KL1_I2S0_CLK_ENABLE_BIT);
+ (void)MACIO_IN32(KEYLARGO_FCR1);
+ udelay(10);
+}
+
+static void rackmeter_set_default_pattern(struct rackmeter *rm)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ if (i < 8)
+ rm->ubuf[i] = (i & 1) * 255;
+ else
+ rm->ubuf[i] = ((~i) & 1) * 255;
+ }
+}
+
+static void rackmeter_do_pause(struct rackmeter *rm, int pause)
+{
+ struct rackmeter_dma *rdma = rm->dma_buf_v;
+
+ pr_debug("rackmeter: %s\n", pause ? "paused" : "started");
+
+ rm->paused = pause;
+ if (pause) {
+ DBDMA_DO_STOP(rm->dma_regs);
+ return;
+ }
+ memset(rdma->buf1, 0, SAMPLE_COUNT & sizeof(u32));
+ memset(rdma->buf2, 0, SAMPLE_COUNT & sizeof(u32));
+
+ rm->dma_buf_v->mark = 0;
+
+ mb();
+ out_le32(&rm->dma_regs->cmdptr_hi, 0);
+ out_le32(&rm->dma_regs->cmdptr, rm->dma_buf_p);
+ out_le32(&rm->dma_regs->control, (RUN << 16) | RUN);
+}
+
+static void rackmeter_setup_dbdma(struct rackmeter *rm)
+{
+ struct rackmeter_dma *db = rm->dma_buf_v;
+ struct dbdma_cmd *cmd = db->cmd;
+
+ /* Make sure dbdma is reset */
+ DBDMA_DO_RESET(rm->dma_regs);
+
+ pr_debug("rackmeter: mark offset=0x%zx\n",
+ offsetof(struct rackmeter_dma, mark));
+ pr_debug("rackmeter: buf1 offset=0x%zx\n",
+ offsetof(struct rackmeter_dma, buf1));
+ pr_debug("rackmeter: buf2 offset=0x%zx\n",
+ offsetof(struct rackmeter_dma, buf2));
+
+ /* Prepare 4 dbdma commands for the 2 buffers */
+ memset(cmd, 0, 4 * sizeof(struct dbdma_cmd));
+ st_le16(&cmd->req_count, 4);
+ st_le16(&cmd->command, STORE_WORD | INTR_ALWAYS | KEY_SYSTEM);
+ st_le32(&cmd->phy_addr, rm->dma_buf_p +
+ offsetof(struct rackmeter_dma, mark));
+ st_le32(&cmd->cmd_dep, 0x02000000);
+ cmd++;
+
+ st_le16(&cmd->req_count, SAMPLE_COUNT * 4);
+ st_le16(&cmd->command, OUTPUT_MORE);
+ st_le32(&cmd->phy_addr, rm->dma_buf_p +
+ offsetof(struct rackmeter_dma, buf1));
+ cmd++;
+
+ st_le16(&cmd->req_count, 4);
+ st_le16(&cmd->command, STORE_WORD | INTR_ALWAYS | KEY_SYSTEM);
+ st_le32(&cmd->phy_addr, rm->dma_buf_p +
+ offsetof(struct rackmeter_dma, mark));
+ st_le32(&cmd->cmd_dep, 0x01000000);
+ cmd++;
+
+ st_le16(&cmd->req_count, SAMPLE_COUNT * 4);
+ st_le16(&cmd->command, OUTPUT_MORE | BR_ALWAYS);
+ st_le32(&cmd->phy_addr, rm->dma_buf_p +
+ offsetof(struct rackmeter_dma, buf2));
+ st_le32(&cmd->cmd_dep, rm->dma_buf_p);
+
+ rackmeter_do_pause(rm, 0);
+}
+
+static void rackmeter_do_timer(struct work_struct *work)
+{
+ struct rackmeter_cpu *rcpu =
+ container_of(work, struct rackmeter_cpu, sniffer.work);
+ struct rackmeter *rm = rcpu->rm;
+ unsigned int cpu = smp_processor_id();
+ cputime64_t cur_jiffies, total_idle_ticks;
+ unsigned int total_ticks, idle_ticks;
+ int i, offset, load, cumm, pause;
+
+ cur_jiffies = jiffies64_to_cputime64(get_jiffies_64());
+ total_ticks = (unsigned int) (cur_jiffies - rcpu->prev_wall);
+ rcpu->prev_wall = cur_jiffies;
+
+ total_idle_ticks = get_cpu_idle_time(cpu);
+ idle_ticks = (unsigned int) (total_idle_ticks - rcpu->prev_idle);
+ rcpu->prev_idle = total_idle_ticks;
+
+ /* We do a very dumb calculation to update the LEDs for now,
+ * we'll do better once we have actual PWM implemented
+ */
+ load = (9 * (total_ticks - idle_ticks)) / total_ticks;
+
+ offset = cpu << 3;
+ cumm = 0;
+ for (i = 0; i < 8; i++) {
+ u8 ub = (load > i) ? 0xff : 0;
+ rm->ubuf[i + offset] = ub;
+ cumm |= ub;
+ }
+ rcpu->zero = (cumm == 0);
+
+ /* Now check if LEDs are all 0, we can stop DMA */
+ pause = (rm->cpu[0].zero && rm->cpu[1].zero);
+ if (pause != rm->paused) {
+ mutex_lock(&rm->sem);
+ pause = (rm->cpu[0].zero && rm->cpu[1].zero);
+ rackmeter_do_pause(rm, pause);
+ mutex_unlock(&rm->sem);
+ }
+ schedule_delayed_work_on(cpu, &rcpu->sniffer,
+ msecs_to_jiffies(CPU_SAMPLING_RATE));
+}
+
+static void rackmeter_init_cpu_sniffer(struct rackmeter *rm)
+{
+ unsigned int cpu;
+
+ /* This driver works only with 1 or 2 CPUs numbered 0 and 1,
+ * but that's really all we have on Apple Xserve. It doesn't
+ * play very nice with CPU hotplug neither but we don't do that
+ * on those machines yet
+ */
+
+ rm->cpu[0].rm = rm;
+ INIT_DELAYED_WORK(&rm->cpu[0].sniffer, rackmeter_do_timer);
+ rm->cpu[1].rm = rm;
+ INIT_DELAYED_WORK(&rm->cpu[1].sniffer, rackmeter_do_timer);
+
+ for_each_online_cpu(cpu) {
+ struct rackmeter_cpu *rcpu;
+
+ if (cpu > 1)
+ continue;
+ rcpu = &rm->cpu[cpu];
+ rcpu->prev_idle = get_cpu_idle_time(cpu);
+ rcpu->prev_wall = jiffies64_to_cputime64(get_jiffies_64());
+ schedule_delayed_work_on(cpu, &rm->cpu[cpu].sniffer,
+ msecs_to_jiffies(CPU_SAMPLING_RATE));
+ }
+}
+
+static void rackmeter_stop_cpu_sniffer(struct rackmeter *rm)
+{
+ cancel_delayed_work_sync(&rm->cpu[0].sniffer);
+ cancel_delayed_work_sync(&rm->cpu[1].sniffer);
+}
+
+static int rackmeter_setup(struct rackmeter *rm)
+{
+ pr_debug("rackmeter: setting up i2s..\n");
+ rackmeter_setup_i2s(rm);
+
+ pr_debug("rackmeter: setting up default pattern..\n");
+ rackmeter_set_default_pattern(rm);
+
+ pr_debug("rackmeter: setting up dbdma..\n");
+ rackmeter_setup_dbdma(rm);
+
+ pr_debug("rackmeter: start CPU measurements..\n");
+ rackmeter_init_cpu_sniffer(rm);
+
+ printk(KERN_INFO "RackMeter initialized\n");
+
+ return 0;
+}
+
+/* XXX FIXME: No PWM yet, this is 0/1 */
+static u32 rackmeter_calc_sample(struct rackmeter *rm, unsigned int index)
+{
+ int led;
+ u32 sample = 0;
+
+ for (led = 0; led < 16; led++) {
+ sample >>= 1;
+ sample |= ((rm->ubuf[led] >= 0x80) << 15);
+ }
+ return (sample << 17) | (sample >> 15);
+}
+
+static irqreturn_t rackmeter_irq(int irq, void *arg)
+{
+ struct rackmeter *rm = arg;
+ struct rackmeter_dma *db = rm->dma_buf_v;
+ unsigned int mark, i;
+ u32 *buf;
+
+ /* Flush PCI buffers with an MMIO read. Maybe we could actually
+ * check the status one day ... in case things go wrong, though
+ * this never happened to me
+ */
+ (void)in_le32(&rm->dma_regs->status);
+
+ /* Make sure the CPU gets us in order */
+ rmb();
+
+ /* Read mark */
+ mark = db->mark;
+ if (mark != 1 && mark != 2) {
+ printk(KERN_WARNING "rackmeter: Incorrect DMA mark 0x%08x\n",
+ mark);
+ /* We allow for 3 errors like that (stale DBDMA irqs) */
+ if (++rm->stale_irq > 3) {
+ printk(KERN_ERR "rackmeter: Too many errors,"
+ " stopping DMA\n");
+ DBDMA_DO_RESET(rm->dma_regs);
+ }
+ return IRQ_HANDLED;
+ }
+
+ /* Next buffer we need to fill is mark value */
+ buf = mark == 1 ? db->buf1 : db->buf2;
+
+ /* Fill it now. This routine converts the 8 bits depth sample array
+ * into the PWM bitmap for each LED.
+ */
+ for (i = 0; i < SAMPLE_COUNT; i++)
+ buf[i] = rackmeter_calc_sample(rm, i);
+
+
+ return IRQ_HANDLED;
+}
+
+static int rackmeter_probe(struct macio_dev* mdev,
+ const struct of_device_id *match)
+{
+ struct device_node *i2s = NULL, *np = NULL;
+ struct rackmeter *rm = NULL;
+ struct resource ri2s, rdma;
+ int rc = -ENODEV;
+
+ pr_debug("rackmeter_probe()\n");
+
+ /* Get i2s-a node */
+ while ((i2s = of_get_next_child(mdev->ofdev.dev.of_node, i2s)) != NULL)
+ if (strcmp(i2s->name, "i2s-a") == 0)
+ break;
+ if (i2s == NULL) {
+ pr_debug(" i2s-a child not found\n");
+ goto bail;
+ }
+ /* Get lightshow or virtual sound */
+ while ((np = of_get_next_child(i2s, np)) != NULL) {
+ if (strcmp(np->name, "lightshow") == 0)
+ break;
+ if ((strcmp(np->name, "sound") == 0) &&
+ of_get_property(np, "virtual", NULL) != NULL)
+ break;
+ }
+ if (np == NULL) {
+ pr_debug(" lightshow or sound+virtual child not found\n");
+ goto bail;
+ }
+
+ /* Create and initialize our instance data */
+ rm = kzalloc(sizeof(struct rackmeter), GFP_KERNEL);
+ if (rm == NULL) {
+ printk(KERN_ERR "rackmeter: failed to allocate memory !\n");
+ rc = -ENOMEM;
+ goto bail_release;
+ }
+ rm->mdev = mdev;
+ rm->i2s = i2s;
+ mutex_init(&rm->sem);
+ dev_set_drvdata(&mdev->ofdev.dev, rm);
+ /* Check resources availability. We need at least resource 0 and 1 */
+#if 0 /* Use that when i2s-a is finally an mdev per-se */
+ if (macio_resource_count(mdev) < 2 || macio_irq_count(mdev) < 2) {
+ printk(KERN_ERR
+ "rackmeter: found match but lacks resources: %s"
+ " (%d resources, %d interrupts)\n",
+ mdev->ofdev.node->full_name);
+ rc = -ENXIO;
+ goto bail_free;
+ }
+ if (macio_request_resources(mdev, "rackmeter")) {
+ printk(KERN_ERR
+ "rackmeter: failed to request resources: %s\n",
+ mdev->ofdev.node->full_name);
+ rc = -EBUSY;
+ goto bail_free;
+ }
+ rm->irq = macio_irq(mdev, 1);
+#else
+ rm->irq = irq_of_parse_and_map(i2s, 1);
+ if (rm->irq == NO_IRQ ||
+ of_address_to_resource(i2s, 0, &ri2s) ||
+ of_address_to_resource(i2s, 1, &rdma)) {
+ printk(KERN_ERR
+ "rackmeter: found match but lacks resources: %s",
+ mdev->ofdev.dev.of_node->full_name);
+ rc = -ENXIO;
+ goto bail_free;
+ }
+#endif
+
+ pr_debug(" i2s @0x%08x\n", (unsigned int)ri2s.start);
+ pr_debug(" dma @0x%08x\n", (unsigned int)rdma.start);
+ pr_debug(" irq %d\n", rm->irq);
+
+ rm->ubuf = (u8 *)__get_free_page(GFP_KERNEL);
+ if (rm->ubuf == NULL) {
+ printk(KERN_ERR
+ "rackmeter: failed to allocate samples page !\n");
+ rc = -ENOMEM;
+ goto bail_release;
+ }
+
+ rm->dma_buf_v = dma_alloc_coherent(&macio_get_pci_dev(mdev)->dev,
+ sizeof(struct rackmeter_dma),
+ &rm->dma_buf_p, GFP_KERNEL);
+ if (rm->dma_buf_v == NULL) {
+ printk(KERN_ERR
+ "rackmeter: failed to allocate dma buffer !\n");
+ rc = -ENOMEM;
+ goto bail_free_samples;
+ }
+#if 0
+ rm->i2s_regs = ioremap(macio_resource_start(mdev, 0), 0x1000);
+#else
+ rm->i2s_regs = ioremap(ri2s.start, 0x1000);
+#endif
+ if (rm->i2s_regs == NULL) {
+ printk(KERN_ERR
+ "rackmeter: failed to map i2s registers !\n");
+ rc = -ENXIO;
+ goto bail_free_dma;
+ }
+#if 0
+ rm->dma_regs = ioremap(macio_resource_start(mdev, 1), 0x100);
+#else
+ rm->dma_regs = ioremap(rdma.start, 0x100);
+#endif
+ if (rm->dma_regs == NULL) {
+ printk(KERN_ERR
+ "rackmeter: failed to map dma registers !\n");
+ rc = -ENXIO;
+ goto bail_unmap_i2s;
+ }
+
+ rc = rackmeter_setup(rm);
+ if (rc) {
+ printk(KERN_ERR
+ "rackmeter: failed to initialize !\n");
+ rc = -ENXIO;
+ goto bail_unmap_dma;
+ }
+
+ rc = request_irq(rm->irq, rackmeter_irq, 0, "rackmeter", rm);
+ if (rc != 0) {
+ printk(KERN_ERR
+ "rackmeter: failed to request interrupt !\n");
+ goto bail_stop_dma;
+ }
+ of_node_put(np);
+ return 0;
+
+ bail_stop_dma:
+ DBDMA_DO_RESET(rm->dma_regs);
+ bail_unmap_dma:
+ iounmap(rm->dma_regs);
+ bail_unmap_i2s:
+ iounmap(rm->i2s_regs);
+ bail_free_dma:
+ dma_free_coherent(&macio_get_pci_dev(mdev)->dev,
+ sizeof(struct rackmeter_dma),
+ rm->dma_buf_v, rm->dma_buf_p);
+ bail_free_samples:
+ free_page((unsigned long)rm->ubuf);
+ bail_release:
+#if 0
+ macio_release_resources(mdev);
+#endif
+ bail_free:
+ kfree(rm);
+ bail:
+ of_node_put(i2s);
+ of_node_put(np);
+ dev_set_drvdata(&mdev->ofdev.dev, NULL);
+ return rc;
+}
+
+static int rackmeter_remove(struct macio_dev* mdev)
+{
+ struct rackmeter *rm = dev_get_drvdata(&mdev->ofdev.dev);
+
+ /* Stop CPU sniffer timer & work queues */
+ rackmeter_stop_cpu_sniffer(rm);
+
+ /* Clear reference to private data */
+ dev_set_drvdata(&mdev->ofdev.dev, NULL);
+
+ /* Stop/reset dbdma */
+ DBDMA_DO_RESET(rm->dma_regs);
+
+ /* Release the IRQ */
+ free_irq(rm->irq, rm);
+
+ /* Unmap registers */
+ iounmap(rm->dma_regs);
+ iounmap(rm->i2s_regs);
+
+ /* Free DMA */
+ dma_free_coherent(&macio_get_pci_dev(mdev)->dev,
+ sizeof(struct rackmeter_dma),
+ rm->dma_buf_v, rm->dma_buf_p);
+
+ /* Free samples */
+ free_page((unsigned long)rm->ubuf);
+
+#if 0
+ /* Release resources */
+ macio_release_resources(mdev);
+#endif
+
+ /* Get rid of me */
+ kfree(rm);
+
+ return 0;
+}
+
+static int rackmeter_shutdown(struct macio_dev* mdev)
+{
+ struct rackmeter *rm = dev_get_drvdata(&mdev->ofdev.dev);
+
+ if (rm == NULL)
+ return -ENODEV;
+
+ /* Stop CPU sniffer timer & work queues */
+ rackmeter_stop_cpu_sniffer(rm);
+
+ /* Stop/reset dbdma */
+ DBDMA_DO_RESET(rm->dma_regs);
+
+ return 0;
+}
+
+static struct of_device_id rackmeter_match[] = {
+ { .name = "i2s" },
+ { }
+};
+
+static struct macio_driver rackmeter_driver = {
+ .driver = {
+ .name = "rackmeter",
+ .owner = THIS_MODULE,
+ .of_match_table = rackmeter_match,
+ },
+ .probe = rackmeter_probe,
+ .remove = rackmeter_remove,
+ .shutdown = rackmeter_shutdown,
+};
+
+
+static int __init rackmeter_init(void)
+{
+ pr_debug("rackmeter_init()\n");
+
+ return macio_register_driver(&rackmeter_driver);
+}
+
+static void __exit rackmeter_exit(void)
+{
+ pr_debug("rackmeter_exit()\n");
+
+ macio_unregister_driver(&rackmeter_driver);
+}
+
+module_init(rackmeter_init);
+module_exit(rackmeter_exit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("RackMeter: Support vu-meter on XServe front panel");
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c
index 090e40fc501..4eab93aa570 100644
--- a/drivers/macintosh/smu.c
+++ b/drivers/macintosh/smu.c
@@ -12,7 +12,7 @@
* - maybe add timeout to commands ?
* - blocking version of time functions
* - polling version of i2c commands (including timer that works with
- * interrutps off)
+ * interrupts off)
* - maybe avoid some data copies with i2c by directly using the smu cmd
* buffer and a lower level internal interface
* - understand SMU -> CPU events and implement reception of them via
@@ -32,9 +32,12 @@
#include <linux/completion.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
-#include <linux/sysdev.h>
#include <linux/poll.h>
#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
#include <asm/byteorder.h>
#include <asm/io.h>
@@ -43,9 +46,7 @@
#include <asm/pmac_feature.h>
#include <asm/smu.h>
#include <asm/sections.h>
-#include <asm/abs_addr.h>
#include <asm/uaccess.h>
-#include <asm/of_device.h>
#define VERSION "0.7"
#define AUTHOR "(c) 2005 Benjamin Herrenschmidt, IBM Corp."
@@ -72,7 +73,7 @@ struct smu_cmd_buf {
struct smu_device {
spinlock_t lock;
struct device_node *of_node;
- struct of_device *of_dev;
+ struct platform_device *of_dev;
int doorbell; /* doorbell gpio */
u32 __iomem *db_buf; /* doorbell buffer */
struct device_node *db_node;
@@ -84,6 +85,7 @@ struct smu_device {
u32 cmd_buf_abs; /* command buffer absolute */
struct list_head cmd_list;
struct smu_cmd *cmd_cur; /* pending command */
+ int broken_nap;
struct list_head cmd_i2c_list;
struct smu_i2c_cmd *cmd_i2c_cur; /* pending i2c command */
struct timer_list i2c_timer;
@@ -93,6 +95,7 @@ struct smu_device {
* I don't think there will ever be more than one SMU, so
* for now, just hard code that
*/
+static DEFINE_MUTEX(smu_mutex);
static struct smu_device *smu;
static DEFINE_MUTEX(smu_part_access);
static int smu_irq_inited;
@@ -118,11 +121,7 @@ static void smu_start_cmd(void)
DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd,
cmd->data_len);
- DPRINTK("SMU: data buffer: %02x %02x %02x %02x %02x %02x %02x %02x\n",
- ((u8 *)cmd->data_buf)[0], ((u8 *)cmd->data_buf)[1],
- ((u8 *)cmd->data_buf)[2], ((u8 *)cmd->data_buf)[3],
- ((u8 *)cmd->data_buf)[4], ((u8 *)cmd->data_buf)[5],
- ((u8 *)cmd->data_buf)[6], ((u8 *)cmd->data_buf)[7]);
+ DPRINTK("SMU: data buffer: %8ph\n", cmd->data_buf);
/* Fill the SMU command buffer */
smu->cmd_buf->cmd = cmd->cmd;
@@ -134,6 +133,19 @@ static void smu_start_cmd(void)
fend = faddr + smu->cmd_buf->length + 2;
flush_inval_dcache_range(faddr, fend);
+
+ /* We also disable NAP mode for the duration of the command
+ * on U3 based machines.
+ * This is slightly racy as it can be written back to 1 by a sysctl
+ * but that never happens in practice. There seem to be an issue with
+ * U3 based machines such as the iMac G5 where napping for the
+ * whole duration of the command prevents the SMU from fetching it
+ * from memory. This might be related to the strange i2c based
+ * mechanism the SMU uses to access memory.
+ */
+ if (smu->broken_nap)
+ powersave_nap = 0;
+
/* This isn't exactly a DMA mapping here, I suspect
* the SMU is actually communicating with us via i2c to the
* northbridge or the CPU to access RAM.
@@ -145,7 +157,7 @@ static void smu_start_cmd(void)
}
-static irqreturn_t smu_db_intr(int irq, void *arg, struct pt_regs *regs)
+static irqreturn_t smu_db_intr(int irq, void *arg)
{
unsigned long flags;
struct smu_cmd *cmd;
@@ -178,7 +190,7 @@ static irqreturn_t smu_db_intr(int irq, void *arg, struct pt_regs *regs)
/* CPU might have brought back the cache line, so we need
* to flush again before peeking at the SMU response. We
* flush the entire buffer for now as we haven't read the
- * reply lenght (it's only 2 cache lines anyway)
+ * reply length (it's only 2 cache lines anyway)
*/
faddr = (unsigned long)smu->cmd_buf;
flush_inval_dcache_range(faddr, faddr + 256);
@@ -210,6 +222,10 @@ static irqreturn_t smu_db_intr(int irq, void *arg, struct pt_regs *regs)
misc = cmd->misc;
mb();
cmd->status = rc;
+
+ /* Re-enable NAP mode */
+ if (smu->broken_nap)
+ powersave_nap = 1;
bail:
/* Start next command if any */
smu_start_cmd();
@@ -224,7 +240,7 @@ static irqreturn_t smu_db_intr(int irq, void *arg, struct pt_regs *regs)
}
-static irqreturn_t smu_msg_intr(int irq, void *arg, struct pt_regs *regs)
+static irqreturn_t smu_msg_intr(int irq, void *arg)
{
/* I don't quite know what to do with this one, we seem to never
* receive it, so I suspect we have to arm it someway in the SMU
@@ -309,7 +325,7 @@ void smu_poll(void)
gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, smu->doorbell);
if ((gpio & 7) == 7)
- smu_db_intr(smu->db_irq, smu, NULL);
+ smu_db_intr(smu->db_irq, smu);
}
EXPORT_SYMBOL(smu_poll);
@@ -455,22 +471,21 @@ int __init smu_init (void)
{
struct device_node *np;
const u32 *data;
+ int ret = 0;
np = of_find_node_by_type(NULL, "smu");
if (np == NULL)
return -ENODEV;
- printk(KERN_INFO "SMU driver %s %s\n", VERSION, AUTHOR);
+ printk(KERN_INFO "SMU: Driver %s %s\n", VERSION, AUTHOR);
if (smu_cmdbuf_abs == 0) {
printk(KERN_ERR "SMU: Command buffer not allocated !\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto fail_np;
}
smu = alloc_bootmem(sizeof(struct smu_device));
- if (smu == NULL)
- return -ENOMEM;
- memset(smu, 0, sizeof(*smu));
spin_lock_init(&smu->lock);
INIT_LIST_HEAD(&smu->cmd_list);
@@ -483,19 +498,19 @@ int __init smu_init (void)
* 32 bits value safely
*/
smu->cmd_buf_abs = (u32)smu_cmdbuf_abs;
- smu->cmd_buf = (struct smu_cmd_buf *)abs_to_virt(smu_cmdbuf_abs);
+ smu->cmd_buf = __va(smu_cmdbuf_abs);
smu->db_node = of_find_node_by_name(NULL, "smu-doorbell");
if (smu->db_node == NULL) {
printk(KERN_ERR "SMU: Can't find doorbell GPIO !\n");
- goto fail;
+ ret = -ENXIO;
+ goto fail_bootmem;
}
- data = get_property(smu->db_node, "reg", NULL);
+ data = of_get_property(smu->db_node, "reg", NULL);
if (data == NULL) {
- of_node_put(smu->db_node);
- smu->db_node = NULL;
printk(KERN_ERR "SMU: Can't find doorbell GPIO address !\n");
- goto fail;
+ ret = -ENXIO;
+ goto fail_db_node;
}
/* Current setup has one doorbell GPIO that does both doorbell
@@ -511,7 +526,7 @@ int __init smu_init (void)
smu->msg_node = of_find_node_by_name(NULL, "smu-interrupt");
if (smu->msg_node == NULL)
break;
- data = get_property(smu->msg_node, "reg", NULL);
+ data = of_get_property(smu->msg_node, "reg", NULL);
if (data == NULL) {
of_node_put(smu->msg_node);
smu->msg_node = NULL;
@@ -529,16 +544,29 @@ int __init smu_init (void)
smu->db_buf = ioremap(0x8000860c, 0x1000);
if (smu->db_buf == NULL) {
printk(KERN_ERR "SMU: Can't map doorbell buffer pointer !\n");
- goto fail;
+ ret = -ENXIO;
+ goto fail_msg_node;
}
+ /* U3 has an issue with NAP mode when issuing SMU commands */
+ smu->broken_nap = pmac_get_uninorth_variant() < 4;
+ if (smu->broken_nap)
+ printk(KERN_INFO "SMU: using NAP mode workaround\n");
+
sys_ctrler = SYS_CTRLER_SMU;
return 0;
- fail:
+fail_msg_node:
+ if (smu->msg_node)
+ of_node_put(smu->msg_node);
+fail_db_node:
+ of_node_put(smu->db_node);
+fail_bootmem:
+ free_bootmem(__pa(smu), sizeof(struct smu_device));
smu = NULL;
- return -ENXIO;
-
+fail_np:
+ of_node_put(np);
+ return ret;
}
@@ -600,20 +628,19 @@ core_initcall(smu_late_init);
* sysfs visibility
*/
-static void smu_expose_childs(void *unused)
+static void smu_expose_childs(struct work_struct *unused)
{
struct device_node *np;
for (np = NULL; (np = of_get_next_child(smu->of_node, np)) != NULL;)
- if (device_is_compatible(np, "smu-sensors"))
+ if (of_device_is_compatible(np, "smu-sensors"))
of_platform_device_create(np, "smu-sensors",
&smu->of_dev->dev);
}
-static DECLARE_WORK(smu_expose_childs_work, smu_expose_childs, NULL);
+static DECLARE_WORK(smu_expose_childs_work, smu_expose_childs);
-static int smu_platform_probe(struct of_device* dev,
- const struct of_device_id *match)
+static int smu_platform_probe(struct platform_device* dev)
{
if (!smu)
return -ENODEV;
@@ -628,7 +655,7 @@ static int smu_platform_probe(struct of_device* dev,
return 0;
}
-static struct of_device_id smu_platform_match[] =
+static const struct of_device_id smu_platform_match[] =
{
{
.type = "smu",
@@ -636,30 +663,30 @@ static struct of_device_id smu_platform_match[] =
{},
};
-static struct of_platform_driver smu_of_platform_driver =
+static struct platform_driver smu_of_platform_driver =
{
- .name = "smu",
- .match_table = smu_platform_match,
+ .driver = {
+ .name = "smu",
+ .owner = THIS_MODULE,
+ .of_match_table = smu_platform_match,
+ },
.probe = smu_platform_probe,
};
static int __init smu_init_sysfs(void)
{
/*
- * Due to sysfs bogosity, a sysdev is not a real device, so
- * we should in fact create both if we want sysdev semantics
- * for power management.
* For now, we don't power manage machines with an SMU chip,
* I'm a bit too far from figuring out how that works with those
* new chipsets, but that will come back and bite us
*/
- of_register_driver(&smu_of_platform_driver);
+ platform_driver_register(&smu_of_platform_driver);
return 0;
}
device_initcall(smu_init_sysfs);
-struct of_device *smu_get_ofdev(void)
+struct platform_device *smu_get_ofdev(void)
{
if (!smu)
return NULL;
@@ -870,7 +897,7 @@ int smu_queue_i2c(struct smu_i2c_cmd *cmd)
static int smu_read_datablock(u8 *dest, unsigned int addr, unsigned int len)
{
- DECLARE_COMPLETION(comp);
+ DECLARE_COMPLETION_ONSTACK(comp);
unsigned int chunk;
struct smu_cmd cmd;
int rc;
@@ -917,7 +944,7 @@ static int smu_read_datablock(u8 *dest, unsigned int addr, unsigned int len)
static struct smu_sdbp_header *smu_create_sdb_partition(int id)
{
- DECLARE_COMPLETION(comp);
+ DECLARE_COMPLETION_ONSTACK(comp);
struct smu_simple_cmd cmd;
unsigned int addr, len, tlen;
struct smu_sdbp_header *hdr;
@@ -944,14 +971,14 @@ static struct smu_sdbp_header *smu_create_sdb_partition(int id)
*/
tlen = sizeof(struct property) + len + 18;
- prop = kcalloc(tlen, 1, GFP_KERNEL);
+ prop = kzalloc(tlen, GFP_KERNEL);
if (prop == NULL)
return NULL;
hdr = (struct smu_sdbp_header *)(prop + 1);
prop->name = ((char *)prop) + tlen - 18;
sprintf(prop->name, "sdb-partition-%02x", id);
prop->length = len;
- prop->value = (unsigned char *)hdr;
+ prop->value = hdr;
prop->next = NULL;
/* Read the datablock */
@@ -967,7 +994,7 @@ static struct smu_sdbp_header *smu_create_sdb_partition(int id)
"%02x !\n", id, hdr->id);
goto failure;
}
- if (prom_add_property(smu->of_node, prop)) {
+ if (of_add_property(smu->of_node, prop)) {
printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x "
"property !\n", id);
goto failure;
@@ -1003,7 +1030,7 @@ const struct smu_sdbp_header *__smu_get_sdb_partition(int id,
} else
mutex_lock(&smu_part_access);
- part = get_property(smu->of_node, pname, size);
+ part = of_get_property(smu->of_node, pname, size);
if (part == NULL) {
DPRINTK("trying to extract from SMU ...\n");
part = smu_create_sdb_partition(id);
@@ -1052,18 +1079,19 @@ static int smu_open(struct inode *inode, struct file *file)
struct smu_private *pp;
unsigned long flags;
- pp = kmalloc(sizeof(struct smu_private), GFP_KERNEL);
+ pp = kzalloc(sizeof(struct smu_private), GFP_KERNEL);
if (pp == 0)
return -ENOMEM;
- memset(pp, 0, sizeof(struct smu_private));
spin_lock_init(&pp->lock);
pp->mode = smu_file_commands;
init_waitqueue_head(&pp->wait);
+ mutex_lock(&smu_mutex);
spin_lock_irqsave(&smu_clist_lock, flags);
list_add(&pp->list, &smu_clist);
spin_unlock_irqrestore(&smu_clist_lock, flags);
file->private_data = pp;
+ mutex_unlock(&smu_mutex);
return 0;
}
@@ -1149,8 +1177,10 @@ static ssize_t smu_read_command(struct file *file, struct smu_private *pp,
return -EOVERFLOW;
spin_lock_irqsave(&pp->lock, flags);
if (pp->cmd.status == 1) {
- if (file->f_flags & O_NONBLOCK)
+ if (file->f_flags & O_NONBLOCK) {
+ spin_unlock_irqrestore(&pp->lock, flags);
return -EAGAIN;
+ }
add_wait_queue(&pp->wait, &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
@@ -1227,7 +1257,8 @@ static unsigned int smu_fpoll(struct file *file, poll_table *wait)
if (pp->busy && pp->cmd.status != 1)
mask |= POLLIN;
spin_unlock_irqrestore(&pp->lock, flags);
- } if (pp->mode == smu_file_events) {
+ }
+ if (pp->mode == smu_file_events) {
/* Not yet implemented */
}
return mask;
@@ -1258,9 +1289,9 @@ static int smu_release(struct inode *inode, struct file *file)
set_current_state(TASK_UNINTERRUPTIBLE);
if (pp->cmd.status != 1)
break;
- spin_lock_irqsave(&pp->lock, flags);
- schedule();
spin_unlock_irqrestore(&pp->lock, flags);
+ schedule();
+ spin_lock_irqsave(&pp->lock, flags);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&pp->wait, &wait);
@@ -1276,7 +1307,7 @@ static int smu_release(struct inode *inode, struct file *file)
}
-static struct file_operations smu_device_fops = {
+static const struct file_operations smu_device_fops = {
.llseek = no_llseek,
.read = smu_read,
.write = smu_write,
diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c
index a0f30d0853e..f433521a6f9 100644
--- a/drivers/macintosh/therm_adt746x.c
+++ b/drivers/macintosh/therm_adt746x.c
@@ -3,9 +3,9 @@
*
* Copyright (C) 2003, 2004 Colin Leroy, Rasmus Rohde, Benjamin Herrenschmidt
*
- * Documentation from
- * http://www.analog.com/UploadedFiles/Data_Sheets/115254175ADT7467_pra.pdf
- * http://www.analog.com/UploadedFiles/Data_Sheets/3686221171167ADT7460_b.pdf
+ * Documentation from 115254175ADT7467_pra.pdf and 3686221171167ADT7460_b.pdf
+ * http://www.onsemi.com/PowerSolutions/product.do?id=ADT7467
+ * http://www.onsemi.com/PowerSolutions/product.do?id=ADT7460
*
*/
@@ -19,24 +19,24 @@
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
#include <linux/wait.h>
#include <linux/suspend.h>
#include <linux/kthread.h>
#include <linux/moduleparam.h>
+#include <linux/freezer.h>
+#include <linux/of_platform.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
-#include <asm/of_device.h>
#undef DEBUG
#define CONFIG_REG 0x40
#define MANUAL_MASK 0xe0
#define AUTO_MASK 0x20
+#define INVERT_MASK 0x10
static u8 TEMP_REG[3] = {0x26, 0x25, 0x27}; /* local, sensor1, sensor2 */
static u8 LIMIT_REG[3] = {0x6b, 0x6a, 0x6c}; /* local, sensor1, sensor2 */
@@ -47,11 +47,11 @@ static u8 FAN_SPD_SET[2] = {0x30, 0x31};
static u8 default_limits_local[3] = {70, 50, 70}; /* local, sensor1, sensor2 */
static u8 default_limits_chip[3] = {80, 65, 80}; /* local, sensor1, sensor2 */
-static const char *sensor_location[3] = {NULL, NULL, NULL};
+static const char *sensor_location[3] = { "?", "?", "?" };
-static int limit_adjust = 0;
+static int limit_adjust;
static int fan_speed = -1;
-static int verbose = 0;
+static bool verbose;
MODULE_AUTHOR("Colin Leroy <colin@colino.net>");
MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and "
@@ -71,24 +71,22 @@ MODULE_PARM_DESC(verbose,"Verbose log operations "
"(default 0)");
struct thermostat {
- struct i2c_client clt;
+ struct i2c_client *clt;
u8 temps[3];
u8 cached_temp[3];
u8 initial_limits[3];
u8 limits[3];
int last_speed[2];
int last_var[2];
+ int pwm_inv[2];
+ struct task_struct *thread;
+ struct platform_device *pdev;
+ enum {
+ ADT7460,
+ ADT7467
+ } type;
};
-static enum {ADT7460, ADT7467} therm_type;
-static int therm_bus, therm_address;
-static struct of_device * of_dev;
-static struct thermostat* thermostat;
-static struct task_struct *thread_therm = NULL;
-
-static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,
- int busno);
-
static void write_both_fan_speed(struct thermostat *th, int speed);
static void write_fan_speed(struct thermostat *th, int speed, int fan);
@@ -100,7 +98,7 @@ write_reg(struct thermostat* th, int reg, u8 data)
tmp[0] = reg;
tmp[1] = data;
- rc = i2c_master_send(&th->clt, (const char *)tmp, 2);
+ rc = i2c_master_send(th->clt, (const char *)tmp, 2);
if (rc < 0)
return rc;
if (rc != 2)
@@ -115,73 +113,17 @@ read_reg(struct thermostat* th, int reg)
int rc;
reg_addr = (u8)reg;
- rc = i2c_master_send(&th->clt, &reg_addr, 1);
+ rc = i2c_master_send(th->clt, &reg_addr, 1);
if (rc < 0)
return rc;
if (rc != 1)
return -ENODEV;
- rc = i2c_master_recv(&th->clt, (char *)&data, 1);
+ rc = i2c_master_recv(th->clt, (char *)&data, 1);
if (rc < 0)
return rc;
return data;
}
-static int
-attach_thermostat(struct i2c_adapter *adapter)
-{
- unsigned long bus_no;
-
- if (strncmp(adapter->name, "uni-n", 5))
- return -ENODEV;
- bus_no = simple_strtoul(adapter->name + 6, NULL, 10);
- if (bus_no != therm_bus)
- return -ENODEV;
- return attach_one_thermostat(adapter, therm_address, bus_no);
-}
-
-static int
-detach_thermostat(struct i2c_adapter *adapter)
-{
- struct thermostat* th;
- int i;
-
- if (thermostat == NULL)
- return 0;
-
- th = thermostat;
-
- if (thread_therm != NULL) {
- kthread_stop(thread_therm);
- }
-
- printk(KERN_INFO "adt746x: Putting max temperatures back from "
- "%d, %d, %d to %d, %d, %d\n",
- th->limits[0], th->limits[1], th->limits[2],
- th->initial_limits[0], th->initial_limits[1],
- th->initial_limits[2]);
-
- for (i = 0; i < 3; i++)
- write_reg(th, LIMIT_REG[i], th->initial_limits[i]);
-
- write_both_fan_speed(th, -1);
-
- i2c_detach_client(&th->clt);
-
- thermostat = NULL;
-
- kfree(th);
-
- return 0;
-}
-
-static struct i2c_driver thermostat_driver = {
- .driver = {
- .name = "therm_adt746x",
- },
- .attach_adapter = attach_thermostat,
- .detach_adapter = detach_thermostat,
-};
-
static int read_fan_speed(struct thermostat *th, u8 addr)
{
u8 tmp[2];
@@ -199,7 +141,7 @@ static int read_fan_speed(struct thermostat *th, u8 addr)
static void write_both_fan_speed(struct thermostat *th, int speed)
{
write_fan_speed(th, speed, 0);
- if (therm_type == ADT7460)
+ if (th->type == ADT7460)
write_fan_speed(th, speed, 1);
}
@@ -212,7 +154,7 @@ static void write_fan_speed(struct thermostat *th, int speed, int fan)
else if (speed < -1)
speed = 0;
- if (therm_type == ADT7467 && fan == 1)
+ if (th->type == ADT7467 && fan == 1)
return;
if (th->last_speed[fan] != speed) {
@@ -229,18 +171,23 @@ static void write_fan_speed(struct thermostat *th, int speed, int fan)
if (speed >= 0) {
manual = read_reg(th, MANUAL_MODE[fan]);
- write_reg(th, MANUAL_MODE[fan], manual|MANUAL_MASK);
+ manual &= ~INVERT_MASK;
+ write_reg(th, MANUAL_MODE[fan],
+ manual | MANUAL_MASK | th->pwm_inv[fan]);
write_reg(th, FAN_SPD_SET[fan], speed);
} else {
/* back to automatic */
- if(therm_type == ADT7460) {
+ if(th->type == ADT7460) {
manual = read_reg(th,
MANUAL_MODE[fan]) & (~MANUAL_MASK);
-
+ manual &= ~INVERT_MASK;
+ manual |= th->pwm_inv[fan];
write_reg(th,
MANUAL_MODE[fan], manual|REM_CONTROL[fan]);
} else {
manual = read_reg(th, MANUAL_MODE[fan]);
+ manual &= ~INVERT_MASK;
+ manual |= th->pwm_inv[fan];
write_reg(th, MANUAL_MODE[fan], manual&(~AUTO_MASK));
}
}
@@ -284,7 +231,7 @@ static void update_fans_speed (struct thermostat *th)
/* we don't care about local sensor, so we start at sensor 1 */
for (i = 1; i < 3; i++) {
int started = 0;
- int fan_number = (therm_type == ADT7460 && i == 2);
+ int fan_number = (th->type == ADT7460 && i == 2);
int var = th->temps[i] - th->limits[i];
if (var > -1) {
@@ -306,7 +253,7 @@ static void update_fans_speed (struct thermostat *th)
if (verbose)
printk(KERN_DEBUG "adt746x: Setting fans speed to %d "
- "(limit exceeded by %d on %s) \n",
+ "(limit exceeded by %d on %s)\n",
new_speed, var,
sensor_location[fan_number+1]);
write_both_fan_speed(th, new_speed);
@@ -335,6 +282,7 @@ static int monitor_task(void *arg)
{
struct thermostat* th = arg;
+ set_freezable();
while(!kthread_should_stop()) {
try_to_freeze();
msleep_interruptible(2000);
@@ -360,110 +308,22 @@ static int monitor_task(void *arg)
static void set_limit(struct thermostat *th, int i)
{
- /* Set sensor1 limit higher to avoid powerdowns */
- th->limits[i] = default_limits_chip[i] + limit_adjust;
- write_reg(th, LIMIT_REG[i], th->limits[i]);
+ /* Set sensor1 limit higher to avoid powerdowns */
+ th->limits[i] = default_limits_chip[i] + limit_adjust;
+ write_reg(th, LIMIT_REG[i], th->limits[i]);
- /* set our limits to normal */
- th->limits[i] = default_limits_local[i] + limit_adjust;
+ /* set our limits to normal */
+ th->limits[i] = default_limits_local[i] + limit_adjust;
}
-static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,
- int busno)
-{
- struct thermostat* th;
- int rc;
- int i;
-
- if (thermostat)
- return 0;
-
- th = (struct thermostat *)
- kmalloc(sizeof(struct thermostat), GFP_KERNEL);
-
- if (!th)
- return -ENOMEM;
-
- memset(th, 0, sizeof(*th));
- th->clt.addr = addr;
- th->clt.adapter = adapter;
- th->clt.driver = &thermostat_driver;
- strcpy(th->clt.name, "thermostat");
-
- rc = read_reg(th, 0);
- if (rc < 0) {
- printk(KERN_ERR "adt746x: Thermostat failed to read config "
- "from bus %d !\n",
- busno);
- kfree(th);
- return -ENODEV;
- }
-
- /* force manual control to start the fan quieter */
- if (fan_speed == -1)
- fan_speed = 64;
-
- if(therm_type == ADT7460) {
- printk(KERN_INFO "adt746x: ADT7460 initializing\n");
- /* The 7460 needs to be started explicitly */
- write_reg(th, CONFIG_REG, 1);
- } else
- printk(KERN_INFO "adt746x: ADT7467 initializing\n");
-
- for (i = 0; i < 3; i++) {
- th->initial_limits[i] = read_reg(th, LIMIT_REG[i]);
- set_limit(th, i);
- }
-
- printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d"
- " to %d, %d, %d\n",
- th->initial_limits[0], th->initial_limits[1],
- th->initial_limits[2], th->limits[0], th->limits[1],
- th->limits[2]);
-
- thermostat = th;
-
- if (i2c_attach_client(&th->clt)) {
- printk(KERN_INFO "adt746x: Thermostat failed to attach "
- "client !\n");
- thermostat = NULL;
- kfree(th);
- return -ENODEV;
- }
-
- /* be sure to really write fan speed the first time */
- th->last_speed[0] = -2;
- th->last_speed[1] = -2;
- th->last_var[0] = -80;
- th->last_var[1] = -80;
-
- if (fan_speed != -1) {
- /* manual mode, stop fans */
- write_both_fan_speed(th, 0);
- } else {
- /* automatic mode */
- write_both_fan_speed(th, -1);
- }
-
- thread_therm = kthread_run(monitor_task, th, "kfand");
-
- if (thread_therm == ERR_PTR(-ENOMEM)) {
- printk(KERN_INFO "adt746x: Kthread creation failed\n");
- thread_therm = NULL;
- return -ENOMEM;
- }
-
- return 0;
+#define BUILD_SHOW_FUNC_INT(name, data) \
+static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+ struct thermostat *th = dev_get_drvdata(dev); \
+ return sprintf(buf, "%d\n", data); \
}
-/*
- * Now, unfortunately, sysfs doesn't give us a nice void * we could
- * pass around to the attribute functions, so we don't really have
- * choice but implement a bunch of them...
- *
- * FIXME, it does now...
- */
-#define BUILD_SHOW_FUNC_INT(name, data) \
+#define BUILD_SHOW_FUNC_INT_LITE(name, data) \
static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return sprintf(buf, "%d\n", data); \
@@ -478,30 +338,32 @@ static ssize_t show_##name(struct device *dev, struct device_attribute *attr, ch
#define BUILD_SHOW_FUNC_FAN(name, data) \
static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
+ struct thermostat *th = dev_get_drvdata(dev); \
return sprintf(buf, "%d (%d rpm)\n", \
- thermostat->last_speed[data], \
- read_fan_speed(thermostat, FAN_SPEED[data]) \
+ th->last_speed[data], \
+ read_fan_speed(th, FAN_SPEED[data]) \
); \
}
#define BUILD_STORE_FUNC_DEG(name, data) \
static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \
{ \
+ struct thermostat *th = dev_get_drvdata(dev); \
int val; \
int i; \
val = simple_strtol(buf, NULL, 10); \
printk(KERN_INFO "Adjusting limits by %d degrees\n", val); \
limit_adjust = val; \
for (i=0; i < 3; i++) \
- set_limit(thermostat, i); \
+ set_limit(th, i); \
return n; \
}
#define BUILD_STORE_FUNC_INT(name, data) \
static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \
{ \
- u32 val; \
- val = simple_strtoul(buf, NULL, 10); \
+ int val; \
+ val = simple_strtol(buf, NULL, 10); \
if (val < 0 || val > 255) \
return -EINVAL; \
printk(KERN_INFO "Setting specified fan speed to %d\n", val); \
@@ -509,20 +371,21 @@ static ssize_t store_##name(struct device *dev, struct device_attribute *attr, c
return n; \
}
-BUILD_SHOW_FUNC_INT(sensor1_temperature, (read_reg(thermostat, TEMP_REG[1])))
-BUILD_SHOW_FUNC_INT(sensor2_temperature, (read_reg(thermostat, TEMP_REG[2])))
-BUILD_SHOW_FUNC_INT(sensor1_limit, thermostat->limits[1])
-BUILD_SHOW_FUNC_INT(sensor2_limit, thermostat->limits[2])
+BUILD_SHOW_FUNC_INT(sensor1_temperature, (read_reg(th, TEMP_REG[1])))
+BUILD_SHOW_FUNC_INT(sensor2_temperature, (read_reg(th, TEMP_REG[2])))
+BUILD_SHOW_FUNC_INT(sensor1_limit, th->limits[1])
+BUILD_SHOW_FUNC_INT(sensor2_limit, th->limits[2])
BUILD_SHOW_FUNC_STR(sensor1_location, sensor_location[1])
BUILD_SHOW_FUNC_STR(sensor2_location, sensor_location[2])
-BUILD_SHOW_FUNC_INT(specified_fan_speed, fan_speed)
+BUILD_SHOW_FUNC_INT_LITE(specified_fan_speed, fan_speed)
+BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed)
+
BUILD_SHOW_FUNC_FAN(sensor1_fan_speed, 0)
BUILD_SHOW_FUNC_FAN(sensor2_fan_speed, 1)
-BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed)
-BUILD_SHOW_FUNC_INT(limit_adjust, limit_adjust)
-BUILD_STORE_FUNC_DEG(limit_adjust, thermostat)
+BUILD_SHOW_FUNC_INT_LITE(limit_adjust, limit_adjust)
+BUILD_STORE_FUNC_DEG(limit_adjust, th)
static DEVICE_ATTR(sensor1_temperature, S_IRUGO,
show_sensor1_temperature,NULL);
@@ -548,51 +411,81 @@ static DEVICE_ATTR(sensor2_fan_speed, S_IRUGO,
static DEVICE_ATTR(limit_adjust, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
show_limit_adjust, store_limit_adjust);
-
-static int __init
-thermostat_init(void)
+static void thermostat_create_files(struct thermostat *th)
{
- struct device_node* np;
- const u32 *prop;
- int i = 0, offset = 0;
-
- np = of_find_node_by_name(NULL, "fan");
- if (!np)
- return -ENODEV;
- if (device_is_compatible(np, "adt7460"))
- therm_type = ADT7460;
- else if (device_is_compatible(np, "adt7467"))
- therm_type = ADT7467;
- else
- return -ENODEV;
-
- prop = get_property(np, "hwsensor-params-version", NULL);
- printk(KERN_INFO "adt746x: version %d (%ssupported)\n", *prop,
- (*prop == 1)?"":"un");
- if (*prop != 1)
- return -ENODEV;
+ struct device_node *np = th->clt->dev.of_node;
+ struct device *dev;
+ int err;
+
+ /* To maintain ABI compatibility with userspace, create
+ * the old style platform driver and attach the attributes
+ * to it here
+ */
+ th->pdev = of_platform_device_create(np, "temperatures", NULL);
+ if (!th->pdev)
+ return;
+ dev = &th->pdev->dev;
+ dev_set_drvdata(dev, th);
+ err = device_create_file(dev, &dev_attr_sensor1_temperature);
+ err |= device_create_file(dev, &dev_attr_sensor2_temperature);
+ err |= device_create_file(dev, &dev_attr_sensor1_limit);
+ err |= device_create_file(dev, &dev_attr_sensor2_limit);
+ err |= device_create_file(dev, &dev_attr_sensor1_location);
+ err |= device_create_file(dev, &dev_attr_sensor2_location);
+ err |= device_create_file(dev, &dev_attr_limit_adjust);
+ err |= device_create_file(dev, &dev_attr_specified_fan_speed);
+ err |= device_create_file(dev, &dev_attr_sensor1_fan_speed);
+ if(th->type == ADT7460)
+ err |= device_create_file(dev, &dev_attr_sensor2_fan_speed);
+ if (err)
+ printk(KERN_WARNING
+ "Failed to create temperature attribute file(s).\n");
+}
- prop = get_property(np, "reg", NULL);
- if (!prop)
- return -ENODEV;
+static void thermostat_remove_files(struct thermostat *th)
+{
+ struct device *dev;
- /* look for bus either by path or using "reg" */
- if (strstr(np->full_name, "/i2c-bus@") != NULL) {
- const char *tmp_bus = (strstr(np->full_name, "/i2c-bus@") + 9);
- therm_bus = tmp_bus[0]-'0';
- } else {
- therm_bus = ((*prop) >> 8) & 0x0f;
- }
+ if (!th->pdev)
+ return;
+ dev = &th->pdev->dev;
+ device_remove_file(dev, &dev_attr_sensor1_temperature);
+ device_remove_file(dev, &dev_attr_sensor2_temperature);
+ device_remove_file(dev, &dev_attr_sensor1_limit);
+ device_remove_file(dev, &dev_attr_sensor2_limit);
+ device_remove_file(dev, &dev_attr_sensor1_location);
+ device_remove_file(dev, &dev_attr_sensor2_location);
+ device_remove_file(dev, &dev_attr_limit_adjust);
+ device_remove_file(dev, &dev_attr_specified_fan_speed);
+ device_remove_file(dev, &dev_attr_sensor1_fan_speed);
+ if (th->type == ADT7460)
+ device_remove_file(dev, &dev_attr_sensor2_fan_speed);
+ of_device_unregister(th->pdev);
- therm_address = ((*prop) & 0xff) >> 1;
+}
- printk(KERN_INFO "adt746x: Thermostat bus: %d, address: 0x%02x, "
- "limit_adjust: %d, fan_speed: %d\n",
- therm_bus, therm_address, limit_adjust, fan_speed);
+static int probe_thermostat(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device_node *np = client->dev.of_node;
+ struct thermostat* th;
+ const __be32 *prop;
+ int i, rc, vers, offset = 0;
- if (get_property(np, "hwsensor-location", NULL)) {
+ if (!np)
+ return -ENXIO;
+ prop = of_get_property(np, "hwsensor-params-version", NULL);
+ if (!prop)
+ return -ENXIO;
+ vers = be32_to_cpup(prop);
+ printk(KERN_INFO "adt746x: version %d (%ssupported)\n",
+ vers, vers == 1 ? "" : "un");
+ if (vers != 1)
+ return -ENXIO;
+
+ if (of_get_property(np, "hwsensor-location", NULL)) {
for (i = 0; i < 3; i++) {
- sensor_location[i] = get_property(np,
+ sensor_location[i] = of_get_property(np,
"hwsensor-location", NULL) + offset;
if (sensor_location[i] == NULL)
@@ -601,31 +494,119 @@ thermostat_init(void)
printk(KERN_INFO "sensor %d: %s\n", i, sensor_location[i]);
offset += strlen(sensor_location[i]) + 1;
}
- } else {
- sensor_location[0] = "?";
- sensor_location[1] = "?";
- sensor_location[2] = "?";
}
- of_dev = of_platform_device_create(np, "temperatures", NULL);
-
- if (of_dev == NULL) {
- printk(KERN_ERR "Can't register temperatures device !\n");
+ th = kzalloc(sizeof(struct thermostat), GFP_KERNEL);
+ if (!th)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, th);
+ th->clt = client;
+ th->type = id->driver_data;
+
+ rc = read_reg(th, CONFIG_REG);
+ if (rc < 0) {
+ dev_err(&client->dev, "Thermostat failed to read config!\n");
+ kfree(th);
return -ENODEV;
}
+
+ /* force manual control to start the fan quieter */
+ if (fan_speed == -1)
+ fan_speed = 64;
+
+ if (th->type == ADT7460) {
+ printk(KERN_INFO "adt746x: ADT7460 initializing\n");
+ /* The 7460 needs to be started explicitly */
+ write_reg(th, CONFIG_REG, 1);
+ } else
+ printk(KERN_INFO "adt746x: ADT7467 initializing\n");
+
+ for (i = 0; i < 3; i++) {
+ th->initial_limits[i] = read_reg(th, LIMIT_REG[i]);
+ set_limit(th, i);
+ }
+
+ printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d"
+ " to %d, %d, %d\n",
+ th->initial_limits[0], th->initial_limits[1],
+ th->initial_limits[2], th->limits[0], th->limits[1],
+ th->limits[2]);
+
+ /* record invert bit status because fw can corrupt it after suspend */
+ th->pwm_inv[0] = read_reg(th, MANUAL_MODE[0]) & INVERT_MASK;
+ th->pwm_inv[1] = read_reg(th, MANUAL_MODE[1]) & INVERT_MASK;
+
+ /* be sure to really write fan speed the first time */
+ th->last_speed[0] = -2;
+ th->last_speed[1] = -2;
+ th->last_var[0] = -80;
+ th->last_var[1] = -80;
+
+ if (fan_speed != -1) {
+ /* manual mode, stop fans */
+ write_both_fan_speed(th, 0);
+ } else {
+ /* automatic mode */
+ write_both_fan_speed(th, -1);
+ }
+
+ th->thread = kthread_run(monitor_task, th, "kfand");
+ if (th->thread == ERR_PTR(-ENOMEM)) {
+ printk(KERN_INFO "adt746x: Kthread creation failed\n");
+ th->thread = NULL;
+ return -ENOMEM;
+ }
+
+ thermostat_create_files(th);
+
+ return 0;
+}
+
+static int remove_thermostat(struct i2c_client *client)
+{
+ struct thermostat *th = i2c_get_clientdata(client);
+ int i;
- device_create_file(&of_dev->dev, &dev_attr_sensor1_temperature);
- device_create_file(&of_dev->dev, &dev_attr_sensor2_temperature);
- device_create_file(&of_dev->dev, &dev_attr_sensor1_limit);
- device_create_file(&of_dev->dev, &dev_attr_sensor2_limit);
- device_create_file(&of_dev->dev, &dev_attr_sensor1_location);
- device_create_file(&of_dev->dev, &dev_attr_sensor2_location);
- device_create_file(&of_dev->dev, &dev_attr_limit_adjust);
- device_create_file(&of_dev->dev, &dev_attr_specified_fan_speed);
- device_create_file(&of_dev->dev, &dev_attr_sensor1_fan_speed);
- if(therm_type == ADT7460)
- device_create_file(&of_dev->dev, &dev_attr_sensor2_fan_speed);
+ thermostat_remove_files(th);
+
+ if (th->thread != NULL)
+ kthread_stop(th->thread);
+
+ printk(KERN_INFO "adt746x: Putting max temperatures back from "
+ "%d, %d, %d to %d, %d, %d\n",
+ th->limits[0], th->limits[1], th->limits[2],
+ th->initial_limits[0], th->initial_limits[1],
+ th->initial_limits[2]);
+
+ for (i = 0; i < 3; i++)
+ write_reg(th, LIMIT_REG[i], th->initial_limits[i]);
+ write_both_fan_speed(th, -1);
+
+ kfree(th);
+
+ return 0;
+}
+
+static const struct i2c_device_id therm_adt746x_id[] = {
+ { "MAC,adt7460", ADT7460 },
+ { "MAC,adt7467", ADT7467 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, therm_adt746x_id);
+
+static struct i2c_driver thermostat_driver = {
+ .driver = {
+ .name = "therm_adt746x",
+ },
+ .probe = probe_thermostat,
+ .remove = remove_thermostat,
+ .id_table = therm_adt746x_id,
+};
+
+static int __init thermostat_init(void)
+{
#ifndef CONFIG_I2C_POWERMAC
request_module("i2c-powermac");
#endif
@@ -633,26 +614,8 @@ thermostat_init(void)
return i2c_add_driver(&thermostat_driver);
}
-static void __exit
-thermostat_exit(void)
+static void __exit thermostat_exit(void)
{
- if (of_dev) {
- device_remove_file(&of_dev->dev, &dev_attr_sensor1_temperature);
- device_remove_file(&of_dev->dev, &dev_attr_sensor2_temperature);
- device_remove_file(&of_dev->dev, &dev_attr_sensor1_limit);
- device_remove_file(&of_dev->dev, &dev_attr_sensor2_limit);
- device_remove_file(&of_dev->dev, &dev_attr_sensor1_location);
- device_remove_file(&of_dev->dev, &dev_attr_sensor2_location);
- device_remove_file(&of_dev->dev, &dev_attr_limit_adjust);
- device_remove_file(&of_dev->dev, &dev_attr_specified_fan_speed);
- device_remove_file(&of_dev->dev, &dev_attr_sensor1_fan_speed);
-
- if(therm_type == ADT7460)
- device_remove_file(&of_dev->dev,
- &dev_attr_sensor2_fan_speed);
-
- of_device_unregister(of_dev);
- }
i2c_del_driver(&thermostat_driver);
}
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c
index d00c0c37a12..97cfc5ac9fd 100644
--- a/drivers/macintosh/therm_pm72.c
+++ b/drivers/macintosh/therm_pm72.c
@@ -44,11 +44,11 @@
* TODO: - Check MPU structure version/signature
* - Add things like /sbin/overtemp for non-critical
* overtemp conditions so userland can take some policy
- * decisions, like slewing down CPUs
+ * decisions, like slowing down CPUs
* - Deal with fan and i2c failures in a better way
* - Maybe do a generic PID based on params used for
* U3 and Drives ? Definitely need to factor code a bit
- * bettter... also make sensor detection more robust using
+ * better... also make sensor detection more robust using
* the device-tree to probe for them
* - Figure out how to get the slots consumption and set the
* slots fan accordingly
@@ -91,7 +91,7 @@
*
* Mar. 10, 2005 : 1.2
* - Add basic support for Xserve G5
- * - Retreive pumps min/max from EEPROM image in device-tree (broken)
+ * - Retrieve pumps min/max from EEPROM image in device-tree (broken)
* - Use min/max macros here or there
* - Latest darwin updated U3H min fan speed to 20% PWM
*
@@ -114,20 +114,20 @@
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/sched.h>
-#include <linux/slab.h>
#include <linux/init.h>
#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
#include <linux/wait.h>
#include <linux/reboot.h>
#include <linux/kmod.h>
#include <linux/i2c.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
-#include <asm/of_device.h>
#include <asm/macio.h>
#include "therm_pm72.h"
@@ -147,12 +147,12 @@
* Driver statics
*/
-static struct of_device * of_dev;
+static struct platform_device * of_dev;
static struct i2c_adapter * u3_0;
static struct i2c_adapter * u3_1;
static struct i2c_adapter * k2;
static struct i2c_client * fcu;
-static struct cpu_pid_state cpu_state[2];
+static struct cpu_pid_state processor_state[2];
static struct basckside_pid_params backside_params;
static struct backside_pid_state backside_state;
static struct drives_pid_state drives_state;
@@ -161,14 +161,14 @@ static struct slots_pid_state slots_state;
static int state;
static int cpu_count;
static int cpu_pid_type;
-static pid_t ctrl_task;
+static struct task_struct *ctrl_task;
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);
+static DEFINE_MUTEX(driver_lock);
/*
* We have 3 types of CPU PID control. One is "split" old style control
@@ -284,21 +284,7 @@ struct fcu_fan_table fcu_fans[] = {
},
};
-/*
- * i2c_driver structure to attach to the host i2c controller
- */
-
-static int therm_pm72_attach(struct i2c_adapter *adapter);
-static int therm_pm72_detach(struct i2c_adapter *adapter);
-
-static struct i2c_driver therm_pm72_driver =
-{
- .driver = {
- .name = "therm_pm72",
- },
- .attach_adapter = therm_pm72_attach,
- .detach_adapter = therm_pm72_detach,
-};
+static struct i2c_driver therm_pm72_driver;
/*
* Utility function to create an i2c_client structure and
@@ -308,6 +294,7 @@ static struct i2c_client *attach_i2c_chip(int id, const char *name)
{
struct i2c_client *clt;
struct i2c_adapter *adap;
+ struct i2c_board_info info;
if (id & 0x200)
adap = k2;
@@ -318,32 +305,21 @@ static struct i2c_client *attach_i2c_chip(int id, const char *name)
if (adap == NULL)
return NULL;
- clt = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (clt == NULL)
- return NULL;
- memset(clt, 0, sizeof(struct i2c_client));
-
- clt->addr = (id >> 1) & 0x7f;
- clt->adapter = adap;
- clt->driver = &therm_pm72_driver;
- strncpy(clt->name, name, I2C_NAME_SIZE-1);
-
- if (i2c_attach_client(clt)) {
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = (id >> 1) & 0x7f;
+ strlcpy(info.type, "therm_pm72", I2C_NAME_SIZE);
+ clt = i2c_new_device(adap, &info);
+ if (!clt) {
printk(KERN_ERR "therm_pm72: Failed to attach to i2c ID 0x%x\n", id);
- kfree(clt);
return NULL;
}
- return clt;
-}
-/*
- * Utility function to get rid of the i2c_client structure
- * (will also detach from the adapter hopepfully)
- */
-static void detach_i2c_chip(struct i2c_client *clt)
-{
- i2c_detach_client(clt);
- kfree(clt);
+ /*
+ * Let i2c-core delete that device on driver removal.
+ * This is safe because i2c-core holds the core_lock mutex for us.
+ */
+ list_add_tail(&clt->detected, &therm_pm72_driver.clients);
+ return clt;
}
/*
@@ -398,7 +374,7 @@ static int read_smon_adc(struct cpu_pid_state *state, int chan)
rc = i2c_master_send(state->monitor, buf, 2);
if (rc <= 0)
goto error;
- /* Wait for convertion */
+ /* Wait for conversion */
msleep(1);
/* Switch to data register */
buf[0] = 4;
@@ -466,7 +442,7 @@ static int fan_read_reg(int reg, unsigned char *buf, int nb)
tries = 0;
for (;;) {
nr = i2c_master_recv(fcu, buf, nb);
- if (nr > 0 || (nr < 0 && nr != ENODEV) || tries >= 100)
+ if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100)
break;
msleep(10);
++tries;
@@ -487,7 +463,7 @@ static int fan_write_reg(int reg, const unsigned char *ptr, int nb)
tries = 0;
for (;;) {
nw = i2c_master_send(fcu, buf, nb);
- if (nw > 0 || (nw < 0 && nw != EIO) || tries >= 100)
+ if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
break;
msleep(10);
++tries;
@@ -673,7 +649,7 @@ static int read_eeprom(int cpu, struct mpu_data *out)
printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid node from device-tree\n");
return -ENODEV;
}
- data = get_property(np, "cpuid", &len);
+ data = of_get_property(np, "cpuid", &len);
if (data == NULL) {
printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid property from device-tree\n");
of_node_put(np);
@@ -687,8 +663,8 @@ static int read_eeprom(int cpu, struct mpu_data *out)
static void fetch_cpu_pumps_minmax(void)
{
- struct cpu_pid_state *state0 = &cpu_state[0];
- struct cpu_pid_state *state1 = &cpu_state[1];
+ struct cpu_pid_state *state0 = &processor_state[0];
+ struct cpu_pid_state *state1 = &processor_state[1];
u16 pump_min = 0, pump_max = 0xffff;
u16 tmp[4];
@@ -729,9 +705,9 @@ static void fetch_cpu_pumps_minmax(void)
static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
ssize_t r; \
- down(&driver_lock); \
+ mutex_lock(&driver_lock); \
r = sprintf(buf, "%d.%03d", FIX32TOPRINT(data)); \
- up(&driver_lock); \
+ mutex_unlock(&driver_lock); \
return r; \
}
#define BUILD_SHOW_FUNC_INT(name, data) \
@@ -740,17 +716,17 @@ static ssize_t show_##name(struct device *dev, struct device_attribute *attr, ch
return sprintf(buf, "%d", data); \
}
-BUILD_SHOW_FUNC_FIX(cpu0_temperature, cpu_state[0].last_temp)
-BUILD_SHOW_FUNC_FIX(cpu0_voltage, cpu_state[0].voltage)
-BUILD_SHOW_FUNC_FIX(cpu0_current, cpu_state[0].current_a)
-BUILD_SHOW_FUNC_INT(cpu0_exhaust_fan_rpm, cpu_state[0].rpm)
-BUILD_SHOW_FUNC_INT(cpu0_intake_fan_rpm, cpu_state[0].intake_rpm)
+BUILD_SHOW_FUNC_FIX(cpu0_temperature, processor_state[0].last_temp)
+BUILD_SHOW_FUNC_FIX(cpu0_voltage, processor_state[0].voltage)
+BUILD_SHOW_FUNC_FIX(cpu0_current, processor_state[0].current_a)
+BUILD_SHOW_FUNC_INT(cpu0_exhaust_fan_rpm, processor_state[0].rpm)
+BUILD_SHOW_FUNC_INT(cpu0_intake_fan_rpm, processor_state[0].intake_rpm)
-BUILD_SHOW_FUNC_FIX(cpu1_temperature, cpu_state[1].last_temp)
-BUILD_SHOW_FUNC_FIX(cpu1_voltage, cpu_state[1].voltage)
-BUILD_SHOW_FUNC_FIX(cpu1_current, cpu_state[1].current_a)
-BUILD_SHOW_FUNC_INT(cpu1_exhaust_fan_rpm, cpu_state[1].rpm)
-BUILD_SHOW_FUNC_INT(cpu1_intake_fan_rpm, cpu_state[1].intake_rpm)
+BUILD_SHOW_FUNC_FIX(cpu1_temperature, processor_state[1].last_temp)
+BUILD_SHOW_FUNC_FIX(cpu1_voltage, processor_state[1].voltage)
+BUILD_SHOW_FUNC_FIX(cpu1_current, processor_state[1].current_a)
+BUILD_SHOW_FUNC_INT(cpu1_exhaust_fan_rpm, processor_state[1].rpm)
+BUILD_SHOW_FUNC_INT(cpu1_intake_fan_rpm, processor_state[1].intake_rpm)
BUILD_SHOW_FUNC_FIX(backside_temperature, backside_state.last_temp)
BUILD_SHOW_FUNC_INT(backside_fan_pwm, backside_state.pwm)
@@ -942,8 +918,8 @@ static void do_cpu_pid(struct cpu_pid_state *state, s32 temp, s32 power)
static void do_monitor_cpu_combined(void)
{
- struct cpu_pid_state *state0 = &cpu_state[0];
- struct cpu_pid_state *state1 = &cpu_state[1];
+ struct cpu_pid_state *state0 = &processor_state[0];
+ struct cpu_pid_state *state1 = &processor_state[1];
s32 temp0, power0, temp1, power1;
s32 temp_combi, power_combi;
int rc, intake, pump;
@@ -970,10 +946,16 @@ static void do_monitor_cpu_combined(void)
printk(KERN_WARNING "Warning ! Temperature way above maximum (%d) !\n",
temp_combi >> 16);
state0->overtemp += CPU_MAX_OVERTEMP / 4;
- } else if (temp_combi > (state0->mpu.tmax << 16))
+ } else if (temp_combi > (state0->mpu.tmax << 16)) {
state0->overtemp++;
- else
+ printk(KERN_WARNING "Temperature %d above max %d. overtemp %d\n",
+ temp_combi >> 16, state0->mpu.tmax, state0->overtemp);
+ } else {
+ if (state0->overtemp)
+ printk(KERN_WARNING "Temperature back down to %d\n",
+ temp_combi >> 16);
state0->overtemp = 0;
+ }
if (state0->overtemp >= CPU_MAX_OVERTEMP)
critical_state = 1;
if (state0->overtemp > 0) {
@@ -1045,10 +1027,16 @@ static void do_monitor_cpu_split(struct cpu_pid_state *state)
" (%d) !\n",
state->index, temp >> 16);
state->overtemp += CPU_MAX_OVERTEMP / 4;
- } else if (temp > (state->mpu.tmax << 16))
+ } else if (temp > (state->mpu.tmax << 16)) {
state->overtemp++;
- else
+ printk(KERN_WARNING "CPU %d temperature %d above max %d. overtemp %d\n",
+ state->index, temp >> 16, state->mpu.tmax, state->overtemp);
+ } else {
+ if (state->overtemp)
+ printk(KERN_WARNING "CPU %d temperature back down to %d\n",
+ state->index, temp >> 16);
state->overtemp = 0;
+ }
if (state->overtemp >= CPU_MAX_OVERTEMP)
critical_state = 1;
if (state->overtemp > 0) {
@@ -1107,10 +1095,16 @@ static void do_monitor_cpu_rack(struct cpu_pid_state *state)
" (%d) !\n",
state->index, temp >> 16);
state->overtemp = CPU_MAX_OVERTEMP / 4;
- } else if (temp > (state->mpu.tmax << 16))
+ } else if (temp > (state->mpu.tmax << 16)) {
state->overtemp++;
- else
+ printk(KERN_WARNING "CPU %d temperature %d above max %d. overtemp %d\n",
+ state->index, temp >> 16, state->mpu.tmax, state->overtemp);
+ } else {
+ if (state->overtemp)
+ printk(KERN_WARNING "CPU %d temperature back down to %d\n",
+ state->index, temp >> 16);
state->overtemp = 0;
+ }
if (state->overtemp >= CPU_MAX_OVERTEMP)
critical_state = 1;
if (state->overtemp > 0) {
@@ -1155,8 +1149,10 @@ static void do_monitor_cpu_rack(struct cpu_pid_state *state)
/*
* Initialize the state structure for one CPU control loop
*/
-static int init_cpu_state(struct cpu_pid_state *state, int index)
+static int init_processor_state(struct cpu_pid_state *state, int index)
{
+ int err;
+
state->index = index;
state->first = 1;
state->rpm = (cpu_pid_type == CPU_PID_TYPE_RACKMAC) ? 4000 : 1000;
@@ -1182,23 +1178,24 @@ static int init_cpu_state(struct cpu_pid_state *state, int index)
DBG("CPU %d Using %d power history entries\n", index, state->count_power);
if (index == 0) {
- device_create_file(&of_dev->dev, &dev_attr_cpu0_temperature);
- device_create_file(&of_dev->dev, &dev_attr_cpu0_voltage);
- device_create_file(&of_dev->dev, &dev_attr_cpu0_current);
- device_create_file(&of_dev->dev, &dev_attr_cpu0_exhaust_fan_rpm);
- device_create_file(&of_dev->dev, &dev_attr_cpu0_intake_fan_rpm);
+ err = device_create_file(&of_dev->dev, &dev_attr_cpu0_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_voltage);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_current);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_exhaust_fan_rpm);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_intake_fan_rpm);
} else {
- device_create_file(&of_dev->dev, &dev_attr_cpu1_temperature);
- device_create_file(&of_dev->dev, &dev_attr_cpu1_voltage);
- device_create_file(&of_dev->dev, &dev_attr_cpu1_current);
- device_create_file(&of_dev->dev, &dev_attr_cpu1_exhaust_fan_rpm);
- device_create_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
+ err = device_create_file(&of_dev->dev, &dev_attr_cpu1_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_voltage);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_current);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_exhaust_fan_rpm);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
}
+ if (err)
+ printk(KERN_WARNING "Failed to create some of the attribute"
+ "files for CPU %d\n", index);
return 0;
fail:
- if (state->monitor)
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
return -ENODEV;
@@ -1207,7 +1204,7 @@ static int init_cpu_state(struct cpu_pid_state *state, int index)
/*
* Dispose of the state data for one CPU control loop
*/
-static void dispose_cpu_state(struct cpu_pid_state *state)
+static void dispose_processor_state(struct cpu_pid_state *state)
{
if (state->monitor == NULL)
return;
@@ -1226,7 +1223,6 @@ static void dispose_cpu_state(struct cpu_pid_state *state)
device_remove_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
}
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
}
@@ -1329,6 +1325,7 @@ static int init_backside_state(struct backside_pid_state *state)
{
struct device_node *u3;
int u3h = 1; /* conservative by default */
+ int err;
/*
* There are different PID params for machines with U3 and machines
@@ -1336,7 +1333,7 @@ static int init_backside_state(struct backside_pid_state *state)
*/
u3 = of_find_node_by_path("/u3@0,f8000000");
if (u3 != NULL) {
- const u32 *vers = get_property(u3, "device-rev", NULL);
+ const u32 *vers = of_get_property(u3, "device-rev", NULL);
if (vers)
if (((*vers) & 0x3f) < 0x34)
u3h = 0;
@@ -1380,8 +1377,11 @@ static int init_backside_state(struct backside_pid_state *state)
if (state->monitor == NULL)
return -ENODEV;
- device_create_file(&of_dev->dev, &dev_attr_backside_temperature);
- device_create_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
+ err = device_create_file(&of_dev->dev, &dev_attr_backside_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
+ if (err)
+ printk(KERN_WARNING "Failed to create attribute file(s)"
+ " for backside fan\n");
return 0;
}
@@ -1397,7 +1397,6 @@ static void dispose_backside_state(struct backside_pid_state *state)
device_remove_file(&of_dev->dev, &dev_attr_backside_temperature);
device_remove_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
}
@@ -1492,6 +1491,8 @@ static void do_monitor_drives(struct drives_pid_state *state)
*/
static int init_drives_state(struct drives_pid_state *state)
{
+ int err;
+
state->ticks = 1;
state->first = 1;
state->rpm = 1000;
@@ -1500,8 +1501,11 @@ static int init_drives_state(struct drives_pid_state *state)
if (state->monitor == NULL)
return -ENODEV;
- device_create_file(&of_dev->dev, &dev_attr_drives_temperature);
- device_create_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
+ err = device_create_file(&of_dev->dev, &dev_attr_drives_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
+ if (err)
+ printk(KERN_WARNING "Failed to create attribute file(s)"
+ " for drives bay fan\n");
return 0;
}
@@ -1517,7 +1521,6 @@ static void dispose_drives_state(struct drives_pid_state *state)
device_remove_file(&of_dev->dev, &dev_attr_drives_temperature);
device_remove_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
}
@@ -1622,7 +1625,9 @@ static int init_dimms_state(struct dimm_pid_state *state)
if (state->monitor == NULL)
return -ENODEV;
- device_create_file(&of_dev->dev, &dev_attr_dimms_temperature);
+ if (device_create_file(&of_dev->dev, &dev_attr_dimms_temperature))
+ printk(KERN_WARNING "Failed to create attribute file"
+ " for DIMM temperature\n");
return 0;
}
@@ -1637,7 +1642,6 @@ static void dispose_dimms_state(struct dimm_pid_state *state)
device_remove_file(&of_dev->dev, &dev_attr_dimms_temperature);
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
}
@@ -1732,6 +1736,8 @@ static void do_monitor_slots(struct slots_pid_state *state)
*/
static int init_slots_state(struct slots_pid_state *state)
{
+ int err;
+
state->ticks = 1;
state->first = 1;
state->pwm = 50;
@@ -1740,8 +1746,11 @@ static int init_slots_state(struct slots_pid_state *state)
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);
+ err = device_create_file(&of_dev->dev, &dev_attr_slots_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_slots_fan_pwm);
+ if (err)
+ printk(KERN_WARNING "Failed to create attribute file(s)"
+ " for slots bay fan\n");
return 0;
}
@@ -1757,7 +1766,6 @@ static void dispose_slots_state(struct slots_pid_state *state)
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;
}
@@ -1770,7 +1778,8 @@ static int call_critical_overtemp(void)
"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
NULL };
- return call_usermodehelper(critical_overtemp_path, argv, envp, 0);
+ return call_usermodehelper(critical_overtemp_path,
+ argv, envp, UMH_WAIT_EXEC);
}
@@ -1779,15 +1788,13 @@ static int call_critical_overtemp(void)
*/
static int main_control_loop(void *x)
{
- daemonize("kfand");
-
DBG("main_control_loop started\n");
- down(&driver_lock);
+ mutex_lock(&driver_lock);
if (start_fcu() < 0) {
printk(KERN_ERR "kfand: failed to start FCU\n");
- up(&driver_lock);
+ mutex_unlock(&driver_lock);
goto out;
}
@@ -1796,20 +1803,20 @@ static int main_control_loop(void *x)
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]);
+ initialize_adc(&processor_state[0]);
+ if (processor_state[1].monitor != NULL)
+ initialize_adc(&processor_state[1]);
fcu_tickle_ticks = FCU_TICKLE_TICKS;
- up(&driver_lock);
+ mutex_unlock(&driver_lock);
while (state == state_attached) {
unsigned long elapsed, start;
start = jiffies;
- down(&driver_lock);
+ mutex_lock(&driver_lock);
/* Tickle the FCU just in case */
if (--fcu_tickle_ticks < 0) {
@@ -1825,14 +1832,14 @@ static int main_control_loop(void *x)
if (cpu_pid_type == CPU_PID_TYPE_COMBINED)
do_monitor_cpu_combined();
else if (cpu_pid_type == CPU_PID_TYPE_RACKMAC) {
- do_monitor_cpu_rack(&cpu_state[0]);
- if (cpu_state[1].monitor != NULL)
- do_monitor_cpu_rack(&cpu_state[1]);
+ do_monitor_cpu_rack(&processor_state[0]);
+ if (processor_state[1].monitor != NULL)
+ do_monitor_cpu_rack(&processor_state[1]);
// better deal with UP
} else {
- do_monitor_cpu_split(&cpu_state[0]);
- if (cpu_state[1].monitor != NULL)
- do_monitor_cpu_split(&cpu_state[1]);
+ do_monitor_cpu_split(&processor_state[0]);
+ if (processor_state[1].monitor != NULL)
+ do_monitor_cpu_split(&processor_state[1]);
// better deal with UP
}
/* Then, the rest */
@@ -1841,7 +1848,7 @@ static int main_control_loop(void *x)
do_monitor_slots(&slots_state);
else
do_monitor_drives(&drives_state);
- up(&driver_lock);
+ mutex_unlock(&driver_lock);
if (critical_state == 1) {
printk(KERN_WARNING "Temperature control detected a critical condition\n");
@@ -1877,8 +1884,8 @@ static int main_control_loop(void *x)
*/
static void dispose_control_loops(void)
{
- dispose_cpu_state(&cpu_state[0]);
- dispose_cpu_state(&cpu_state[1]);
+ dispose_processor_state(&processor_state[0]);
+ dispose_processor_state(&processor_state[1]);
dispose_backside_state(&backside_state);
dispose_drives_state(&drives_state);
dispose_slots_state(&slots_state);
@@ -1908,7 +1915,7 @@ static int create_control_loops(void)
*/
if (rackmac)
cpu_pid_type = CPU_PID_TYPE_RACKMAC;
- else if (machine_is_compatible("PowerMac7,3")
+ else if (of_machine_is_compatible("PowerMac7,3")
&& (cpu_count > 1)
&& fcu_fans[CPUA_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID
&& fcu_fans[CPUB_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID) {
@@ -1920,12 +1927,12 @@ static int create_control_loops(void)
/* Create control loops for everything. If any fail, everything
* fails
*/
- if (init_cpu_state(&cpu_state[0], 0))
+ if (init_processor_state(&processor_state[0], 0))
goto fail;
if (cpu_pid_type == CPU_PID_TYPE_COMBINED)
fetch_cpu_pumps_minmax();
- if (cpu_count > 1 && init_cpu_state(&cpu_state[1], 1))
+ if (cpu_count > 1 && init_processor_state(&processor_state[1], 1))
goto fail;
if (init_backside_state(&backside_state))
goto fail;
@@ -1956,7 +1963,7 @@ static void start_control_loops(void)
{
init_completion(&ctrl_complete);
- ctrl_task = kernel_thread(main_control_loop, NULL, SIGCHLD | CLONE_KERNEL);
+ ctrl_task = kthread_run(main_control_loop, NULL, "kfand");
}
/*
@@ -1964,7 +1971,7 @@ static void start_control_loops(void)
*/
static void stop_control_loops(void)
{
- if (ctrl_task != 0)
+ if (ctrl_task)
wait_for_completion(&ctrl_complete);
}
@@ -1987,8 +1994,6 @@ static int attach_fcu(void)
*/
static void detach_fcu(void)
{
- if (fcu)
- detach_i2c_chip(fcu);
fcu = NULL;
}
@@ -1999,13 +2004,13 @@ static void detach_fcu(void)
*/
static int therm_pm72_attach(struct i2c_adapter *adapter)
{
- down(&driver_lock);
+ mutex_lock(&driver_lock);
/* Check state */
if (state == state_detached)
state = state_attaching;
if (state != state_attaching) {
- up(&driver_lock);
+ mutex_unlock(&driver_lock);
return 0;
}
@@ -2034,27 +2039,36 @@ static int therm_pm72_attach(struct i2c_adapter *adapter)
state = state_attached;
start_control_loops();
}
- up(&driver_lock);
+ mutex_unlock(&driver_lock);
return 0;
}
+static int therm_pm72_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ /* Always succeed, the real work was done in therm_pm72_attach() */
+ return 0;
+}
+
/*
- * Called on every adapter when the driver or the i2c controller
+ * Called when any of the devices which participates into thermal management
* is going away.
*/
-static int therm_pm72_detach(struct i2c_adapter *adapter)
+static int therm_pm72_remove(struct i2c_client *client)
{
- down(&driver_lock);
+ struct i2c_adapter *adapter = client->adapter;
+
+ mutex_lock(&driver_lock);
if (state != state_detached)
state = state_detaching;
/* Stop control loops if any */
DBG("stopping control loops\n");
- up(&driver_lock);
+ mutex_unlock(&driver_lock);
stop_control_loops();
- down(&driver_lock);
+ mutex_lock(&driver_lock);
if (u3_0 != NULL && !strcmp(adapter->name, "u3 0")) {
DBG("lost U3-0, disposing control loops\n");
@@ -2070,11 +2084,35 @@ static int therm_pm72_detach(struct i2c_adapter *adapter)
if (u3_0 == NULL && u3_1 == NULL)
state = state_detached;
- up(&driver_lock);
+ mutex_unlock(&driver_lock);
return 0;
}
+/*
+ * i2c_driver structure to attach to the host i2c controller
+ */
+
+static const struct i2c_device_id therm_pm72_id[] = {
+ /*
+ * Fake device name, thermal management is done by several
+ * chips but we don't need to differentiate between them at
+ * this point.
+ */
+ { "therm_pm72", 0 },
+ { }
+};
+
+static struct i2c_driver therm_pm72_driver = {
+ .driver = {
+ .name = "therm_pm72",
+ },
+ .attach_adapter = therm_pm72_attach,
+ .probe = therm_pm72_probe,
+ .remove = therm_pm72_remove,
+ .id_table = therm_pm72_id,
+};
+
static int fan_check_loc_match(const char *loc, int fan)
{
char tmp[64];
@@ -2128,8 +2166,8 @@ static void fcu_lookup_fans(struct device_node *fcu_node)
continue;
/* Lookup for a matching location */
- loc = get_property(np, "location", NULL);
- reg = get_property(np, "reg", NULL);
+ loc = of_get_property(np, "location", NULL);
+ reg = of_get_property(np, "reg", NULL);
if (loc == NULL || reg == NULL)
continue;
DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg);
@@ -2171,36 +2209,43 @@ static void fcu_lookup_fans(struct device_node *fcu_node)
}
}
-static int fcu_of_probe(struct of_device* dev, const struct of_device_id *match)
+static int fcu_of_probe(struct platform_device* dev)
{
state = state_detached;
+ of_dev = dev;
+
+ dev_info(&dev->dev, "PowerMac G5 Thermal control driver %s\n", VERSION);
/* Lookup the fans in the device tree */
- fcu_lookup_fans(dev->node);
+ fcu_lookup_fans(dev->dev.of_node);
/* Add the driver */
return i2c_add_driver(&therm_pm72_driver);
}
-static int fcu_of_remove(struct of_device* dev)
+static int fcu_of_remove(struct platform_device* dev)
{
i2c_del_driver(&therm_pm72_driver);
return 0;
}
-static struct of_device_id fcu_match[] =
+static const struct of_device_id fcu_match[] =
{
{
.type = "fcu",
},
{},
};
+MODULE_DEVICE_TABLE(of, fcu_match);
-static struct of_platform_driver fcu_of_platform_driver =
+static struct platform_driver fcu_of_platform_driver =
{
- .name = "temperature",
- .match_table = fcu_match,
+ .driver = {
+ .name = "temperature",
+ .owner = THIS_MODULE,
+ .of_match_table = fcu_match,
+ },
.probe = fcu_of_probe,
.remove = fcu_of_remove
};
@@ -2210,43 +2255,19 @@ static struct of_platform_driver fcu_of_platform_driver =
*/
static int __init therm_pm72_init(void)
{
- struct device_node *np;
-
- rackmac = machine_is_compatible("RackMac3,1");
+ rackmac = of_machine_is_compatible("RackMac3,1");
- if (!machine_is_compatible("PowerMac7,2") &&
- !machine_is_compatible("PowerMac7,3") &&
+ if (!of_machine_is_compatible("PowerMac7,2") &&
+ !of_machine_is_compatible("PowerMac7,3") &&
!rackmac)
return -ENODEV;
- printk(KERN_INFO "PowerMac G5 Thermal control driver %s\n", VERSION);
-
- np = of_find_node_by_type(NULL, "fcu");
- if (np == NULL) {
- /* Some machines have strangely broken device-tree */
- np = of_find_node_by_path("/u3@0,f8000000/i2c@f8001000/fan@15e");
- if (np == NULL) {
- printk(KERN_ERR "Can't find FCU in device-tree !\n");
- return -ENODEV;
- }
- }
- of_dev = of_platform_device_create(np, "temperature", NULL);
- if (of_dev == NULL) {
- printk(KERN_ERR "Can't register FCU platform device !\n");
- return -ENODEV;
- }
-
- of_register_driver(&fcu_of_platform_driver);
-
- return 0;
+ return platform_driver_register(&fcu_of_platform_driver);
}
static void __exit therm_pm72_exit(void)
{
- of_unregister_driver(&fcu_of_platform_driver);
-
- if (of_dev)
- of_device_unregister(of_dev);
+ platform_driver_unregister(&fcu_of_platform_driver);
}
module_init(therm_pm72_init);
diff --git a/drivers/macintosh/therm_pm72.h b/drivers/macintosh/therm_pm72.h
index 393cc9df94e..df3680e2a22 100644
--- a/drivers/macintosh/therm_pm72.h
+++ b/drivers/macintosh/therm_pm72.h
@@ -269,7 +269,7 @@ struct slots_pid_state
#define CPU_TEMP_HISTORY_SIZE 2
#define CPU_POWER_HISTORY_SIZE 10
#define CPU_PID_INTERVAL 1
-#define CPU_MAX_OVERTEMP 30
+#define CPU_MAX_OVERTEMP 90
#define CPUA_PUMP_RPM_INDEX 7
#define CPUB_PUMP_RPM_INDEX 8
diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c
index 738faab1b22..3b4a157714b 100644
--- a/drivers/macintosh/therm_windtunnel.c
+++ b/drivers/macintosh/therm_windtunnel.c
@@ -15,7 +15,7 @@
*
* WARNING: This driver has only been testen on Apple's
* 1.25 MHz Dual G4 (March 03). It is tuned for a CPU
- * temperatur around 57 C.
+ * temperature around 57 C.
*
* Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
*
@@ -34,37 +34,24 @@
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/i2c.h>
-#include <linux/slab.h>
#include <linux/init.h>
+#include <linux/kthread.h>
+#include <linux/of_platform.h>
+
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
-#include <asm/of_device.h>
#include <asm/macio.h>
-#define LOG_TEMP 0 /* continously log temperature */
-
-#define I2C_DRIVERID_G4FAN 0x9001 /* fixme */
-
-static int do_probe( struct i2c_adapter *adapter, int addr, int kind);
-
-/* scan 0x48-0x4f (DS1775) and 0x2c-2x2f (ADM1030) */
-static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
- 0x4c, 0x4d, 0x4e, 0x4f,
- 0x2c, 0x2d, 0x2e, 0x2f,
- I2C_CLIENT_END };
-
-I2C_CLIENT_INSMOD;
+#define LOG_TEMP 0 /* continuously log temperature */
static struct {
volatile int running;
- struct completion completion;
- pid_t poll_task;
+ struct task_struct *poll_task;
- struct semaphore lock;
- struct of_device *of_dev;
+ struct mutex lock;
+ struct platform_device *of_dev;
struct i2c_client *thermostat;
struct i2c_client *fan;
@@ -222,6 +209,7 @@ static void
setup_hardware( void )
{
int val;
+ int err;
/* save registers (if we unload the module) */
x.r0 = read_reg( x.fan, 0x00, 1 );
@@ -249,8 +237,8 @@ setup_hardware( void )
* to be on the safe side (OSX doesn't)...
*/
if( x.overheat_temp == (80 << 8) ) {
- x.overheat_temp = 65 << 8;
- x.overheat_hyst = 60 << 8;
+ x.overheat_temp = 75 << 8;
+ x.overheat_hyst = 70 << 8;
write_reg( x.thermostat, 2, x.overheat_hyst, 2 );
write_reg( x.thermostat, 3, x.overheat_temp, 2 );
@@ -264,8 +252,11 @@ setup_hardware( void )
x.upind = -1;
/* tune_fan( fan_up_table[x.upind].fan_setting ); */
- device_create_file( &x.of_dev->dev, &dev_attr_cpu_temperature );
- device_create_file( &x.of_dev->dev, &dev_attr_case_temperature );
+ err = device_create_file( &x.of_dev->dev, &dev_attr_cpu_temperature );
+ err |= device_create_file( &x.of_dev->dev, &dev_attr_case_temperature );
+ if (err)
+ printk(KERN_WARNING
+ "Failed to create temperature attribute file(s).\n");
}
static void
@@ -281,27 +272,27 @@ restore_regs( void )
write_reg( x.fan, 0x00, x.r0, 1 );
}
-static int
-control_loop( void *dummy )
+static int control_loop(void *dummy)
{
- daemonize("g4fand");
-
- down( &x.lock );
+ mutex_lock(&x.lock);
setup_hardware();
+ mutex_unlock(&x.lock);
- while( x.running ) {
- up( &x.lock );
-
+ for (;;) {
msleep_interruptible(8000);
-
- down( &x.lock );
+ if (kthread_should_stop())
+ break;
+
+ mutex_lock(&x.lock);
poll_temp();
+ mutex_unlock(&x.lock);
}
+ mutex_lock(&x.lock);
restore_regs();
- up( &x.lock );
+ mutex_unlock(&x.lock);
- complete_and_exit( &x.completion, 0 );
+ return 0;
}
@@ -312,54 +303,54 @@ control_loop( void *dummy )
static int
do_attach( struct i2c_adapter *adapter )
{
- int ret = 0;
+ /* scan 0x48-0x4f (DS1775) and 0x2c-2x2f (ADM1030) */
+ static const unsigned short scan_ds1775[] = {
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ I2C_CLIENT_END
+ };
+ static const unsigned short scan_adm1030[] = {
+ 0x2c, 0x2d, 0x2e, 0x2f,
+ I2C_CLIENT_END
+ };
if( strncmp(adapter->name, "uni-n", 5) )
return 0;
if( !x.running ) {
- ret = i2c_probe( adapter, &addr_data, &do_probe );
+ struct i2c_board_info info;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "therm_ds1775", I2C_NAME_SIZE);
+ i2c_new_probed_device(adapter, &info, scan_ds1775, NULL);
+
+ strlcpy(info.type, "therm_adm1030", I2C_NAME_SIZE);
+ i2c_new_probed_device(adapter, &info, scan_adm1030, NULL);
+
if( x.thermostat && x.fan ) {
x.running = 1;
- init_completion( &x.completion );
- x.poll_task = kernel_thread( control_loop, NULL, SIGCHLD | CLONE_KERNEL );
+ x.poll_task = kthread_run(control_loop, NULL, "g4fand");
}
}
- return ret;
+ return 0;
}
static int
-do_detach( struct i2c_client *client )
+do_remove(struct i2c_client *client)
{
- int err;
-
- if( (err=i2c_detach_client(client)) )
- printk(KERN_ERR "failed to detach thermostat client\n");
- else {
- if( x.running ) {
- x.running = 0;
- wait_for_completion( &x.completion );
- }
- if( client == x.thermostat )
- x.thermostat = NULL;
- else if( client == x.fan )
- x.fan = NULL;
- else {
- printk(KERN_ERR "g4fan: bad client\n");
- }
- kfree( client );
+ if (x.running) {
+ x.running = 0;
+ kthread_stop(x.poll_task);
+ x.poll_task = NULL;
}
- return err;
-}
+ if (client == x.thermostat)
+ x.thermostat = NULL;
+ else if (client == x.fan)
+ x.fan = NULL;
+ else
+ printk(KERN_ERR "g4fan: bad client\n");
-static struct i2c_driver g4fan_driver = {
- .driver = {
- .name = "therm_windtunnel",
- },
- .id = I2C_DRIVERID_G4FAN,
- .attach_adapter = do_attach,
- .detach_client = do_detach,
-};
+ return 0;
+}
static int
attach_fan( struct i2c_client *cl )
@@ -372,13 +363,8 @@ attach_fan( struct i2c_client *cl )
goto out;
printk("ADM1030 fan controller [@%02x]\n", cl->addr );
- strlcpy( cl->name, "ADM1030 fan controller", sizeof(cl->name) );
-
- if( !i2c_attach_client(cl) )
- x.fan = cl;
+ x.fan = cl;
out:
- if( cl != x.fan )
- kfree( cl );
return 0;
}
@@ -410,66 +396,76 @@ attach_thermostat( struct i2c_client *cl )
x.temp = temp;
x.overheat_temp = os_temp;
x.overheat_hyst = hyst_temp;
-
- strlcpy( cl->name, "DS1775 thermostat", sizeof(cl->name) );
-
- if( !i2c_attach_client(cl) )
- x.thermostat = cl;
+ x.thermostat = cl;
out:
- if( cl != x.thermostat )
- kfree( cl );
return 0;
}
+enum chip { ds1775, adm1030 };
+
+static const struct i2c_device_id therm_windtunnel_id[] = {
+ { "therm_ds1775", ds1775 },
+ { "therm_adm1030", adm1030 },
+ { }
+};
+
static int
-do_probe( struct i2c_adapter *adapter, int addr, int kind )
+do_probe(struct i2c_client *cl, const struct i2c_device_id *id)
{
- struct i2c_client *cl;
+ struct i2c_adapter *adapter = cl->adapter;
if( !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA
| I2C_FUNC_SMBUS_WRITE_BYTE) )
return 0;
- if( !(cl=kmalloc(sizeof(*cl), GFP_KERNEL)) )
- return -ENOMEM;
- memset( cl, 0, sizeof(struct i2c_client) );
-
- cl->addr = addr;
- cl->adapter = adapter;
- cl->driver = &g4fan_driver;
- cl->flags = 0;
-
- if( addr < 0x48 )
+ switch (id->driver_data) {
+ case adm1030:
return attach_fan( cl );
- return attach_thermostat( cl );
+ case ds1775:
+ return attach_thermostat(cl);
+ }
+ return 0;
}
+static struct i2c_driver g4fan_driver = {
+ .driver = {
+ .name = "therm_windtunnel",
+ },
+ .attach_adapter = do_attach,
+ .probe = do_probe,
+ .remove = do_remove,
+ .id_table = therm_windtunnel_id,
+};
+
/************************************************************************/
/* initialization / cleanup */
/************************************************************************/
-static int
-therm_of_probe( struct of_device *dev, const struct of_device_id *match )
+static int therm_of_probe(struct platform_device *dev)
{
return i2c_add_driver( &g4fan_driver );
}
static int
-therm_of_remove( struct of_device *dev )
+therm_of_remove( struct platform_device *dev )
{
- return i2c_del_driver( &g4fan_driver );
+ i2c_del_driver( &g4fan_driver );
+ return 0;
}
-static struct of_device_id therm_of_match[] = {{
+static const struct of_device_id therm_of_match[] = {{
.name = "fan",
.compatible = "adm1030"
}, {}
};
-static struct of_platform_driver therm_of_driver = {
- .name = "temperature",
- .match_table = therm_of_match,
+static struct platform_driver therm_of_driver = {
+ .driver = {
+ .name = "temperature",
+ .owner = THIS_MODULE,
+ .of_match_table = therm_of_match,
+ },
.probe = therm_of_probe,
.remove = therm_of_remove,
};
@@ -487,14 +483,14 @@ g4fan_init( void )
const struct apple_thermal_info *info;
struct device_node *np;
- init_MUTEX( &x.lock );
+ mutex_init(&x.lock);
if( !(np=of_find_node_by_name(NULL, "power-mgt")) )
return -ENODEV;
- info = get_property(np, "thermal-info", NULL);
+ info = of_get_property(np, "thermal-info", NULL);
of_node_put(np);
- if( !info || !machine_is_compatible("PowerMac3,6") )
+ if( !info || !of_machine_is_compatible("PowerMac3,6") )
return -ENODEV;
if( info->id != 3 ) {
@@ -511,14 +507,14 @@ g4fan_init( void )
return -ENODEV;
}
- of_register_driver( &therm_of_driver );
+ platform_driver_register( &therm_of_driver );
return 0;
}
static void __exit
g4fan_exit( void )
{
- of_unregister_driver( &therm_of_driver );
+ platform_driver_unregister( &therm_of_driver );
if( x.of_dev )
of_device_unregister( x.of_dev );
diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c
index 7512d1c1520..d61f271d220 100644
--- a/drivers/macintosh/via-cuda.c
+++ b/drivers/macintosh/via-cuda.c
@@ -13,7 +13,6 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/delay.h>
-#include <linux/sched.h>
#include <linux/adb.h>
#include <linux/cuda.h>
#include <linux/spinlock.h>
@@ -24,11 +23,9 @@
#else
#include <asm/macintosh.h>
#include <asm/macints.h>
-#include <asm/machw.h>
#include <asm/mac_via.h>
#endif
#include <asm/io.h>
-#include <asm/system.h>
#include <linux/init.h>
static volatile unsigned char __iomem *via;
@@ -83,14 +80,14 @@ static unsigned char cuda_rbuf[16];
static unsigned char *reply_ptr;
static int reading_reply;
static int data_index;
+static int cuda_irq;
#ifdef CONFIG_PPC
static struct device_node *vias;
#endif
-static int cuda_fully_inited = 0;
+static int cuda_fully_inited;
#ifdef CONFIG_ADB
static int cuda_probe(void);
-static int cuda_init(void);
static int cuda_send_request(struct adb_request *req, int sync);
static int cuda_adb_autopoll(int devs);
static int cuda_reset_adb_bus(void);
@@ -98,8 +95,8 @@ static int cuda_reset_adb_bus(void);
static int cuda_init_via(void);
static void cuda_start(void);
-static irqreturn_t cuda_interrupt(int irq, void *arg, struct pt_regs *regs);
-static void cuda_input(unsigned char *buf, int nb, struct pt_regs *regs);
+static irqreturn_t cuda_interrupt(int irq, void *arg);
+static void cuda_input(unsigned char *buf, int nb);
void cuda_poll(void);
static int cuda_write(struct adb_request *req);
@@ -108,17 +105,42 @@ int cuda_request(struct adb_request *req,
#ifdef CONFIG_ADB
struct adb_driver via_cuda_driver = {
- "CUDA",
- cuda_probe,
- cuda_init,
- cuda_send_request,
- cuda_adb_autopoll,
- cuda_poll,
- cuda_reset_adb_bus
+ .name = "CUDA",
+ .probe = cuda_probe,
+ .send_request = cuda_send_request,
+ .autopoll = cuda_adb_autopoll,
+ .poll = cuda_poll,
+ .reset_bus = cuda_reset_adb_bus,
};
#endif /* CONFIG_ADB */
-#ifdef CONFIG_PPC
+#ifdef CONFIG_MAC
+int __init find_via_cuda(void)
+{
+ struct adb_request req;
+ int err;
+
+ if (macintosh_config->adb_type != MAC_ADB_CUDA)
+ return 0;
+
+ via = via1;
+ cuda_state = idle;
+
+ err = cuda_init_via();
+ if (err) {
+ printk(KERN_ERR "cuda_init_via() failed\n");
+ via = NULL;
+ return 0;
+ }
+
+ /* enable autopoll */
+ cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1);
+ while (!req.complete)
+ cuda_poll();
+
+ return 1;
+}
+#else
int __init find_via_cuda(void)
{
struct adb_request req;
@@ -132,7 +154,7 @@ int __init find_via_cuda(void)
if (vias == 0)
return 0;
- reg = get_property(vias, "reg", NULL);
+ reg = of_get_property(vias, "reg", NULL);
if (reg == NULL) {
printk(KERN_ERR "via-cuda: No \"reg\" property !\n");
goto fail;
@@ -161,10 +183,8 @@ int __init find_via_cuda(void)
/* Clear and enable interrupts, but only on PPC. On 68K it's done */
/* for us by the main VIA driver in arch/m68k/mac/via.c */
-#ifndef CONFIG_MAC
out_8(&via[IFR], 0x7f); /* clear interrupts by writing 1s */
out_8(&via[IER], IER_SET|SR_INT); /* enable interrupt from SR */
-#endif
/* enable autopoll */
cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1);
@@ -178,28 +198,26 @@ int __init find_via_cuda(void)
vias = NULL;
return 0;
}
-#endif /* CONFIG_PPC */
+#endif /* !defined CONFIG_MAC */
static int __init via_cuda_start(void)
{
- unsigned int irq;
-
if (via == NULL)
return -ENODEV;
#ifdef CONFIG_MAC
- irq = IRQ_MAC_ADB;
-#else /* CONFIG_MAC */
- irq = irq_of_parse_and_map(vias, 0);
- if (irq == NO_IRQ) {
+ cuda_irq = IRQ_MAC_ADB;
+#else
+ cuda_irq = irq_of_parse_and_map(vias, 0);
+ if (cuda_irq == NO_IRQ) {
printk(KERN_ERR "via-cuda: can't map interrupts for %s\n",
vias->full_name);
return -ENODEV;
}
-#endif /* CONFIG_MAP */
+#endif
- if (request_irq(irq, cuda_interrupt, 0, "ADB", cuda_interrupt)) {
- printk(KERN_ERR "via-cuda: can't request irq %d\n", irq);
+ if (request_irq(cuda_irq, cuda_interrupt, 0, "ADB", cuda_interrupt)) {
+ printk(KERN_ERR "via-cuda: can't request irq %d\n", cuda_irq);
return -EAGAIN;
}
@@ -221,27 +239,10 @@ cuda_probe(void)
#else
if (macintosh_config->adb_type != MAC_ADB_CUDA)
return -ENODEV;
- via = via1;
#endif
- return 0;
-}
-
-static int __init
-cuda_init(void)
-{
-#ifdef CONFIG_PPC
if (via == NULL)
return -ENODEV;
return 0;
-#else
- int err = cuda_init_via();
- if (err) {
- printk(KERN_ERR "cuda_init_via() failed\n");
- return -ENODEV;
- }
-
- return via_cuda_start();
-#endif
}
#endif /* CONFIG_ADB */
@@ -258,21 +259,23 @@ cuda_init(void)
} while (0)
static int
-cuda_init_via(void)
+__init cuda_init_via(void)
{
out_8(&via[DIRB], (in_8(&via[DIRB]) | TACK | TIP) & ~TREQ); /* TACK & TIP out */
out_8(&via[B], in_8(&via[B]) | TACK | TIP); /* negate them */
out_8(&via[ACR] ,(in_8(&via[ACR]) & ~SR_CTRL) | SR_EXT); /* SR data in */
(void)in_8(&via[SR]); /* clear any left-over data */
-#ifndef CONFIG_MAC
+#ifdef CONFIG_PPC
out_8(&via[IER], 0x7f); /* disable interrupts from VIA */
(void)in_8(&via[IER]);
+#else
+ out_8(&via[IER], SR_INT); /* disable SR interrupt from VIA */
#endif
/* delay 4ms and then clear any pending interrupt */
mdelay(4);
(void)in_8(&via[SR]);
- out_8(&via[IFR], in_8(&via[IFR]) & 0x7f);
+ out_8(&via[IFR], SR_INT);
/* sync with the CUDA - assert TACK without TIP */
out_8(&via[B], in_8(&via[B]) & ~TACK);
@@ -283,7 +286,7 @@ cuda_init_via(void)
/* wait for the interrupt and then clear it */
WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (2)");
(void)in_8(&via[SR]);
- out_8(&via[IFR], in_8(&via[IFR]) & 0x7f);
+ out_8(&via[IFR], SR_INT);
/* finish the sync by negating TACK */
out_8(&via[B], in_8(&via[B]) | TACK);
@@ -292,7 +295,7 @@ cuda_init_via(void)
WAIT_FOR(in_8(&via[B]) & TREQ, "CUDA response to sync (3)");
WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (4)");
(void)in_8(&via[SR]);
- out_8(&via[IFR], in_8(&via[IFR]) & 0x7f);
+ out_8(&via[IFR], SR_INT);
out_8(&via[B], in_8(&via[B]) | TIP); /* should be unnecessary */
return 0;
@@ -429,35 +432,43 @@ cuda_start(void)
void
cuda_poll(void)
{
- unsigned long flags;
-
/* cuda_interrupt only takes a normal lock, we disable
* interrupts here to avoid re-entering and thus deadlocking.
- * An option would be to disable only the IRQ source with
- * disable_irq(), would that work on m68k ? --BenH
*/
- local_irq_save(flags);
- cuda_interrupt(0, NULL, NULL);
- local_irq_restore(flags);
+ if (cuda_irq)
+ disable_irq(cuda_irq);
+ cuda_interrupt(0, NULL);
+ if (cuda_irq)
+ enable_irq(cuda_irq);
}
static irqreturn_t
-cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
+cuda_interrupt(int irq, void *arg)
{
int status;
struct adb_request *req = NULL;
unsigned char ibuf[16];
int ibuf_len = 0;
int complete = 0;
- unsigned char virq;
spin_lock(&cuda_lock);
- virq = in_8(&via[IFR]) & 0x7f;
- out_8(&via[IFR], virq);
- if ((virq & SR_INT) == 0) {
- spin_unlock(&cuda_lock);
- return IRQ_NONE;
+ /* On powermacs, this handler is registered for the VIA IRQ. But they use
+ * just the shift register IRQ -- other VIA interrupt sources are disabled.
+ * On m68k macs, the VIA IRQ sources are dispatched individually. Unless
+ * we are polling, the shift register IRQ flag has already been cleared.
+ */
+
+#ifdef CONFIG_MAC
+ if (!arg)
+#endif
+ {
+ if ((in_8(&via[IFR]) & SR_INT) == 0) {
+ spin_unlock(&cuda_lock);
+ return IRQ_NONE;
+ } else {
+ out_8(&via[IFR], SR_INT);
+ }
}
status = (~in_8(&via[B]) & (TIP|TREQ)) | (in_8(&via[ACR]) & SR_OUT);
@@ -594,12 +605,12 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
(*done)(req);
}
if (ibuf_len)
- cuda_input(ibuf, ibuf_len, regs);
+ cuda_input(ibuf, ibuf_len);
return IRQ_HANDLED;
}
static void
-cuda_input(unsigned char *buf, int nb, struct pt_regs *regs)
+cuda_input(unsigned char *buf, int nb)
{
int i;
@@ -615,7 +626,7 @@ cuda_input(unsigned char *buf, int nb, struct pt_regs *regs)
}
#endif /* CONFIG_XMON */
#ifdef CONFIG_ADB
- adb_input(buf+2, nb-2, regs, buf[1] & 0x40);
+ adb_input(buf+2, nb-2, buf[1] & 0x40);
#endif /* CONFIG_ADB */
break;
diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c
index 2a2ffe06016..3725f088f17 100644
--- a/drivers/macintosh/via-macii.c
+++ b/drivers/macintosh/via-macii.c
@@ -12,6 +12,15 @@
* 1999-08-02 (jmt) - Initial rewrite for Unified ADB.
* 2000-03-29 Tony Mantler <tonym@mac.linux-m68k.org>
* - Big overhaul, should actually work now.
+ * 2006-12-31 Finn Thain <fthain@telegraphics.com.au> - Another overhaul.
+ *
+ * Suggested reading:
+ * Inside Macintosh, ch. 5 ADB Manager
+ * Guide to the Macinstosh Family Hardware, ch. 8 Apple Desktop Bus
+ * Rockwell R6522 VIA datasheet
+ *
+ * Apple's "ADB Analyzer" bus sniffer is invaluable:
+ * ftp://ftp.apple.com/developer/Tool_Chest/Devices_-_Hardware/Apple_Desktop_Bus/
*/
#include <stdarg.h>
@@ -19,16 +28,12 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/delay.h>
-#include <linux/sched.h>
#include <linux/adb.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
-#include <asm/machw.h>
#include <asm/mac_via.h>
-#include <asm/io.h>
-#include <asm/system.h>
static volatile unsigned char *via;
@@ -52,9 +57,7 @@ static volatile unsigned char *via;
#define ANH (15*RS) /* A-side data, no handshake */
/* Bits in B data register: all active low */
-#define TREQ 0x08 /* Transfer request (input) */
-#define TACK 0x10 /* Transfer acknowledge (output) */
-#define TIP 0x20 /* Transfer in progress (output) */
+#define CTLR_IRQ 0x08 /* Controller rcv status (input) */
#define ST_MASK 0x30 /* mask for selecting ADB state bits */
/* Bits in ACR */
@@ -66,8 +69,6 @@ static volatile unsigned char *via;
#define IER_SET 0x80 /* set bits in IER */
#define IER_CLR 0 /* clear bits in IER */
#define SR_INT 0x04 /* Shift register full/empty */
-#define SR_DATA 0x08 /* Shift register data */
-#define SR_CLOCK 0x10 /* Shift register clock */
/* ADB transaction states according to GMHW */
#define ST_CMD 0x00 /* ADB state: command byte */
@@ -77,8 +78,7 @@ static volatile unsigned char *via;
static int macii_init_via(void);
static void macii_start(void);
-static irqreturn_t macii_interrupt(int irq, void *arg, struct pt_regs *regs);
-static void macii_retransmit(int);
+static irqreturn_t macii_interrupt(int irq, void *arg);
static void macii_queue_poll(void);
static int macii_probe(void);
@@ -104,29 +104,37 @@ static enum macii_state {
sending,
reading,
read_done,
- awaiting_reply
} macii_state;
-static int need_poll = 0;
-static int command_byte = 0;
-static int last_reply = 0;
-static int last_active = 0;
-
-static struct adb_request *current_req;
-static struct adb_request *last_req;
-static struct adb_request *retry_req;
-static unsigned char reply_buf[16];
-static unsigned char *reply_ptr;
-static int reply_len;
-static int reading_reply;
-static int data_index;
-static int first_byte;
-static int prefix_len;
-static int status = ST_IDLE|TREQ;
-static int last_status;
-static int driver_running = 0;
-
-/* debug level 10 required for ADB logging (should be && debug_adb, ideally) */
+static struct adb_request *current_req; /* first request struct in the queue */
+static struct adb_request *last_req; /* last request struct in the queue */
+static unsigned char reply_buf[16]; /* storage for autopolled replies */
+static unsigned char *reply_ptr; /* next byte in reply_buf or req->reply */
+static int reading_reply; /* store reply in reply_buf else req->reply */
+static int data_index; /* index of the next byte to send from req->data */
+static int reply_len; /* number of bytes received in reply_buf or req->reply */
+static int status; /* VIA's ADB status bits captured upon interrupt */
+static int last_status; /* status bits as at previous interrupt */
+static int srq_asserted; /* have to poll for the device that asserted it */
+static int command_byte; /* the most recent command byte transmitted */
+static int autopoll_devs; /* bits set are device addresses to be polled */
+
+/* Sanity check for request queue. Doesn't check for cycles. */
+static int request_is_queued(struct adb_request *req) {
+ struct adb_request *cur;
+ unsigned long flags;
+ local_irq_save(flags);
+ cur = current_req;
+ while (cur) {
+ if (cur == req) {
+ local_irq_restore(flags);
+ return 1;
+ }
+ cur = cur->next;
+ }
+ local_irq_restore(flags);
+ return 0;
+}
/* Check for MacII style ADB */
static int macii_probe(void)
@@ -148,15 +156,16 @@ int macii_init(void)
local_irq_save(flags);
err = macii_init_via();
- if (err) return err;
+ if (err) goto out;
- err = request_irq(IRQ_MAC_ADB, macii_interrupt, IRQ_FLG_LOCK, "ADB",
+ err = request_irq(IRQ_MAC_ADB, macii_interrupt, 0, "ADB",
macii_interrupt);
- if (err) return err;
+ if (err) goto out;
macii_state = idle;
+out:
local_irq_restore(flags);
- return 0;
+ return err;
}
/* initialize the hardware */
@@ -164,12 +173,12 @@ static int macii_init_via(void)
{
unsigned char x;
- /* Set the lines up. We want TREQ as input TACK|TIP as output */
- via[DIRB] = (via[DIRB] | TACK | TIP) & ~TREQ;
+ /* We want CTLR_IRQ as input and ST_EVEN | ST_ODD as output lines. */
+ via[DIRB] = (via[DIRB] | ST_EVEN | ST_ODD) & ~CTLR_IRQ;
/* Set up state: idle */
via[B] |= ST_IDLE;
- last_status = via[B] & (ST_MASK|TREQ);
+ last_status = via[B] & (ST_MASK|CTLR_IRQ);
/* Shift register on input */
via[ACR] = (via[ACR] & ~SR_CTRL) | SR_EXT;
@@ -180,81 +189,72 @@ static int macii_init_via(void)
return 0;
}
-/* Send an ADB poll (Talk Register 0 command, tagged on the front of the request queue) */
+/* Send an ADB poll (Talk Register 0 command prepended to the request queue) */
static void macii_queue_poll(void)
{
- static int device = 0;
- static int in_poll=0;
+ /* No point polling the active device as it will never assert SRQ, so
+ * poll the next device in the autopoll list. This could leave us
+ * stuck in a polling loop if an unprobed device is asserting SRQ.
+ * In theory, that could only happen if a device was plugged in after
+ * probing started. Unplugging it again will break the cycle.
+ * (Simply polling the next higher device often ends up polling almost
+ * every device (after wrapping around), which takes too long.)
+ */
+ int device_mask;
+ int next_device;
static struct adb_request req;
- unsigned long flags;
-
- if (in_poll) printk("macii_queue_poll: double poll!\n");
-
- in_poll++;
- if (++device > 15) device = 1;
-
- adb_request(&req, NULL, ADBREQ_REPLY|ADBREQ_NOSEND, 1,
- ADB_READREG(device, 0));
-
- local_irq_save(flags);
-
- req.next = current_req;
- current_req = &req;
- local_irq_restore(flags);
- macii_start();
- in_poll--;
-}
+ if (!autopoll_devs) return;
-/* Send an ADB retransmit (Talk, appended to the request queue) */
-static void macii_retransmit(int device)
-{
- static int in_retransmit = 0;
- static struct adb_request rt;
- unsigned long flags;
-
- if (in_retransmit) printk("macii_retransmit: double retransmit!\n");
+ device_mask = (1 << (((command_byte & 0xF0) >> 4) + 1)) - 1;
+ if (autopoll_devs & ~device_mask)
+ next_device = ffs(autopoll_devs & ~device_mask) - 1;
+ else
+ next_device = ffs(autopoll_devs) - 1;
- in_retransmit++;
+ BUG_ON(request_is_queued(&req));
- adb_request(&rt, NULL, ADBREQ_REPLY|ADBREQ_NOSEND, 1,
- ADB_READREG(device, 0));
+ adb_request(&req, NULL, ADBREQ_NOSEND, 1,
+ ADB_READREG(next_device, 0));
- local_irq_save(flags);
+ req.sent = 0;
+ req.complete = 0;
+ req.reply_len = 0;
+ req.next = current_req;
if (current_req != NULL) {
- last_req->next = &rt;
- last_req = &rt;
+ current_req = &req;
} else {
- current_req = &rt;
- last_req = &rt;
+ current_req = &req;
+ last_req = &req;
}
-
- if (macii_state == idle) macii_start();
-
- local_irq_restore(flags);
- in_retransmit--;
}
/* Send an ADB request; if sync, poll out the reply 'till it's done */
static int macii_send_request(struct adb_request *req, int sync)
{
- int i;
+ int err;
+ unsigned long flags;
- i = macii_write(req);
- if (i) return i;
+ BUG_ON(request_is_queued(req));
- if (sync) {
- while (!req->complete) macii_poll();
+ local_irq_save(flags);
+ err = macii_write(req);
+ local_irq_restore(flags);
+
+ if (!err && sync) {
+ while (!req->complete) {
+ macii_poll();
+ }
+ BUG_ON(request_is_queued(req));
}
- return 0;
+
+ return err;
}
-/* Send an ADB request */
+/* Send an ADB request (append to request queue) */
static int macii_write(struct adb_request *req)
{
- unsigned long flags;
-
if (req->nbytes < 2 || req->data[0] != ADB_PACKET || req->nbytes > 15) {
req->complete = 1;
return -EINVAL;
@@ -265,8 +265,6 @@ static int macii_write(struct adb_request *req)
req->complete = 0;
req->reply_len = 0;
- local_irq_save(flags);
-
if (current_req != NULL) {
last_req->next = req;
last_req = req;
@@ -275,28 +273,52 @@ static int macii_write(struct adb_request *req)
last_req = req;
if (macii_state == idle) macii_start();
}
-
- local_irq_restore(flags);
return 0;
}
/* Start auto-polling */
static int macii_autopoll(int devs)
{
- /* Just ping a random default address */
- if (!(current_req || retry_req))
- macii_retransmit( (last_active < 16 && last_active > 0) ? last_active : 3);
- return 0;
+ static struct adb_request req;
+ unsigned long flags;
+ int err = 0;
+
+ /* bit 1 == device 1, and so on. */
+ autopoll_devs = devs & 0xFFFE;
+
+ if (!autopoll_devs) return 0;
+
+ local_irq_save(flags);
+
+ if (current_req == NULL) {
+ /* Send a Talk Reg 0. The controller will repeatedly transmit
+ * this as long as it is idle.
+ */
+ adb_request(&req, NULL, ADBREQ_NOSEND, 1,
+ ADB_READREG(ffs(autopoll_devs) - 1, 0));
+ err = macii_write(&req);
+ }
+
+ local_irq_restore(flags);
+ return err;
+}
+
+static inline int need_autopoll(void) {
+ /* Was the last command Talk Reg 0
+ * and is the target on the autopoll list?
+ */
+ if ((command_byte & 0x0F) == 0x0C &&
+ ((1 << ((command_byte & 0xF0) >> 4)) & autopoll_devs))
+ return 0;
+ return 1;
}
/* Prod the chip without interrupts */
static void macii_poll(void)
{
- unsigned long flags;
-
- local_irq_save(flags);
- if (via[IFR] & SR_INT) macii_interrupt(0, NULL, NULL);
- local_irq_restore(flags);
+ disable_irq(IRQ_MAC_ADB);
+ macii_interrupt(0, NULL);
+ enable_irq(IRQ_MAC_ADB);
}
/* Reset the bus */
@@ -304,73 +326,34 @@ static int macii_reset_bus(void)
{
static struct adb_request req;
+ if (request_is_queued(&req))
+ return 0;
+
/* Command = 0, Address = ignored */
adb_request(&req, NULL, 0, 1, ADB_BUSRESET);
+ /* Don't want any more requests during the Global Reset low time. */
+ udelay(3000);
+
return 0;
}
/* Start sending ADB packet */
static void macii_start(void)
{
- unsigned long flags;
struct adb_request *req;
req = current_req;
- if (!req) return;
-
- /* assert macii_state == idle */
- if (macii_state != idle) {
- printk("macii_start: called while driver busy (%p %x %x)!\n",
- req, macii_state, (uint) via1[B] & (ST_MASK|TREQ));
- return;
- }
- local_irq_save(flags);
-
- /*
- * IRQ signaled ?? (means ADB controller wants to send, or might
- * be end of packet if we were reading)
- */
-#if 0 /* FIXME: This is broke broke broke, for some reason */
- if ((via[B] & TREQ) == 0) {
- printk("macii_start: weird poll stuff. huh?\n");
- /*
- * FIXME - we need to restart this on a timer
- * or a collision at boot hangs us.
- * Never set macii_state to idle here, or macii_start
- * won't be called again from send_request!
- * (need to re-check other cases ...)
- */
- /*
- * if the interrupt handler set the need_poll
- * flag, it's hopefully a SRQ poll or re-Talk
- * so we try to send here anyway
- */
- if (!need_poll) {
- if (console_loglevel == 10)
- printk("macii_start: device busy - retry %p state %d status %x!\n",
- req, macii_state,
- (uint) via[B] & (ST_MASK|TREQ));
- retry_req = req;
- /* set ADB status here ? */
- local_irq_restore(flags);
- return;
- } else {
- need_poll = 0;
- }
- }
-#endif
- /*
- * Another retry pending? (sanity check)
+ BUG_ON(req == NULL);
+
+ BUG_ON(macii_state != idle);
+
+ /* Now send it. Be careful though, that first byte of the request
+ * is actually ADB_PACKET; the real data begins at index 1!
+ * And req->nbytes is the number of bytes of real data plus one.
*/
- if (retry_req) {
- retry_req = NULL;
- }
- /* Now send it. Be careful though, that first byte of the request */
- /* is actually ADB_PACKET; the real data begins at index 1! */
-
/* store command byte */
command_byte = req->data[1];
/* Output mode */
@@ -382,115 +365,97 @@ static void macii_start(void)
macii_state = sending;
data_index = 2;
-
- local_irq_restore(flags);
}
/*
- * The notorious ADB interrupt handler - does all of the protocol handling,
- * except for starting new send operations. Relies heavily on the ADB
- * controller sending and receiving data, thereby generating SR interrupts
- * for us. This means there has to be always activity on the ADB bus, otherwise
- * the whole process dies and has to be re-kicked by sending TALK requests ...
- * CUDA-based Macs seem to solve this with the autopoll option, for MacII-type
- * ADB the problem isn't solved yet (retransmit of the latest active TALK seems
- * a good choice; either on timeout or on a timer interrupt).
+ * The notorious ADB interrupt handler - does all of the protocol handling.
+ * Relies on the ADB controller sending and receiving data, thereby
+ * generating shift register interrupts (SR_INT) for us. This means there has
+ * to be activity on the ADB bus. The chip will poll to achieve this.
*
* The basic ADB state machine was left unchanged from the original MacII code
* by Alan Cox, which was based on the CUDA driver for PowerMac.
- * The syntax of the ADB status lines seems to be totally different on MacII,
- * though. MacII uses the states Command -> Even -> Odd -> Even ->...-> Idle for
- * sending, and Idle -> Even -> Odd -> Even ->...-> Idle for receiving. Start
- * and end of a receive packet are signaled by asserting /IRQ on the interrupt
- * line. Timeouts are signaled by a sequence of 4 0xFF, with /IRQ asserted on
- * every other byte. SRQ is probably signaled by 3 or more 0xFF tacked on the
- * end of a packet. (Thanks to Guido Koerber for eavesdropping on the ADB
- * protocol with a logic analyzer!!)
- *
- * Note: As of 21/10/97, the MacII ADB part works including timeout detection
- * and retransmit (Talk to the last active device).
+ * The syntax of the ADB status lines is totally different on MacII,
+ * though. MacII uses the states Command -> Even -> Odd -> Even ->...-> Idle
+ * for sending and Idle -> Even -> Odd -> Even ->...-> Idle for receiving.
+ * Start and end of a receive packet are signalled by asserting /IRQ on the
+ * interrupt line (/IRQ means the CTLR_IRQ bit in port B; not to be confused
+ * with the VIA shift register interrupt. /IRQ never actually interrupts the
+ * processor, it's just an ordinary input.)
*/
-static irqreturn_t macii_interrupt(int irq, void *arg, struct pt_regs *regs)
+static irqreturn_t macii_interrupt(int irq, void *arg)
{
- int x, adbdir;
- unsigned long flags;
+ int x;
+ static int entered;
struct adb_request *req;
- last_status = status;
-
- /* prevent races due to SCSI enabling ints */
- local_irq_save(flags);
-
- if (driver_running) {
- local_irq_restore(flags);
- return IRQ_NONE;
+ if (!arg) {
+ /* Clear the SR IRQ flag when polling. */
+ if (via[IFR] & SR_INT)
+ via[IFR] = SR_INT;
+ else
+ return IRQ_NONE;
}
- driver_running = 1;
-
- status = via[B] & (ST_MASK|TREQ);
- adbdir = via[ACR] & SR_OUT;
+ BUG_ON(entered++);
+
+ last_status = status;
+ status = via[B] & (ST_MASK|CTLR_IRQ);
switch (macii_state) {
case idle:
+ if (reading_reply) {
+ reply_ptr = current_req->reply;
+ } else {
+ BUG_ON(current_req != NULL);
+ reply_ptr = reply_buf;
+ }
+
x = via[SR];
- first_byte = x;
- /* set ADB state = even for first data byte */
- via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
- reply_buf[0] = first_byte; /* was command_byte?? */
- reply_ptr = reply_buf + 1;
- reply_len = 1;
- prefix_len = 1;
- reading_reply = 0;
-
- macii_state = reading;
- break;
+ if ((status & CTLR_IRQ) && (x == 0xFF)) {
+ /* Bus timeout without SRQ sequence:
+ * data is "FF" while CTLR_IRQ is "H"
+ */
+ reply_len = 0;
+ srq_asserted = 0;
+ macii_state = read_done;
+ } else {
+ macii_state = reading;
+ *reply_ptr = x;
+ reply_len = 1;
+ }
- case awaiting_reply:
- /* handshake etc. for II ?? */
- x = via[SR];
- first_byte = x;
/* set ADB state = even for first data byte */
via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
-
- current_req->reply[0] = first_byte;
- reply_ptr = current_req->reply + 1;
- reply_len = 1;
- prefix_len = 1;
- reading_reply = 1;
-
- macii_state = reading;
break;
case sending:
req = current_req;
if (data_index >= req->nbytes) {
- /* print an error message if a listen command has no data */
- if (((command_byte & 0x0C) == 0x08)
- /* && (console_loglevel == 10) */
- && (data_index == 2))
- printk("MacII ADB: listen command with no data: %x!\n",
- command_byte);
- /* reset to shift in */
- via[ACR] &= ~SR_OUT;
- x = via[SR];
- /* set ADB state idle - might get SRQ */
- via[B] = (via[B] & ~ST_MASK) | ST_IDLE;
-
req->sent = 1;
+ macii_state = idle;
if (req->reply_expected) {
- macii_state = awaiting_reply;
+ reading_reply = 1;
} else {
req->complete = 1;
current_req = req->next;
if (req->done) (*req->done)(req);
- macii_state = idle;
- if (current_req || retry_req)
+
+ if (current_req)
macii_start();
else
- macii_retransmit((command_byte & 0xF0) >> 4);
+ if (need_autopoll())
+ macii_autopoll(autopoll_devs);
+ }
+
+ if (macii_state == idle) {
+ /* reset to shift in */
+ via[ACR] &= ~SR_OUT;
+ x = via[SR];
+ /* set ADB state idle - might get SRQ */
+ via[B] = (via[B] & ~ST_MASK) | ST_IDLE;
}
} else {
via[SR] = req->data[data_index++];
@@ -506,148 +471,79 @@ static irqreturn_t macii_interrupt(int irq, void *arg, struct pt_regs *regs)
break;
case reading:
+ x = via[SR];
+ BUG_ON((status & ST_MASK) == ST_CMD ||
+ (status & ST_MASK) == ST_IDLE);
+
+ /* Bus timeout with SRQ sequence:
+ * data is "XX FF" while CTLR_IRQ is "L L"
+ * End of packet without SRQ sequence:
+ * data is "XX...YY 00" while CTLR_IRQ is "L...H L"
+ * End of packet SRQ sequence:
+ * data is "XX...YY 00" while CTLR_IRQ is "L...L L"
+ * (where XX is the first response byte and
+ * YY is the last byte of valid response data.)
+ */
- /* timeout / SRQ handling for II hw */
- if( (first_byte == 0xFF && (reply_len-prefix_len)==2
- && memcmp(reply_ptr-2,"\xFF\xFF",2)==0) ||
- ((reply_len-prefix_len)==3
- && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0))
- {
- /*
- * possible timeout (in fact, most probably a
- * timeout, since SRQ can't be signaled without
- * transfer on the bus).
- * The last three bytes seen were FF, together
- * with the starting byte (in case we started
- * on 'idle' or 'awaiting_reply') this probably
- * makes four. So this is mostl likely #5!
- * The timeout signal is a pattern 1 0 1 0 0..
- * on /INT, meaning we missed it :-(
- */
- x = via[SR];
- if (x != 0xFF) printk("MacII ADB: mistaken timeout/SRQ!\n");
-
- if ((status & TREQ) == (last_status & TREQ)) {
- /* Not a timeout. Unsolicited SRQ? weird. */
- /* Terminate the SRQ packet and poll */
- need_poll = 1;
+ srq_asserted = 0;
+ if (!(status & CTLR_IRQ)) {
+ if (x == 0xFF) {
+ if (!(last_status & CTLR_IRQ)) {
+ macii_state = read_done;
+ reply_len = 0;
+ srq_asserted = 1;
+ }
+ } else if (x == 0x00) {
+ macii_state = read_done;
+ if (!(last_status & CTLR_IRQ))
+ srq_asserted = 1;
}
- /* There's no packet to get, so reply is blank */
- via[B] ^= ST_MASK;
- reply_ptr -= (reply_len-prefix_len);
- reply_len = prefix_len;
- macii_state = read_done;
- break;
- } /* end timeout / SRQ handling for II hw. */
-
- if((reply_len-prefix_len)>3
- && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0)
- {
- /* SRQ tacked on data packet */
- /* Terminate the packet (SRQ never ends) */
- x = via[SR];
- macii_state = read_done;
- reply_len -= 3;
- reply_ptr -= 3;
- need_poll = 1;
- /* need to continue; next byte not seen else */
- } else {
- /* Sanity check */
- if (reply_len > 15) reply_len = 0;
- /* read byte */
- x = via[SR];
- *reply_ptr = x;
+ }
+
+ if (macii_state == reading) {
+ BUG_ON(reply_len > 15);
reply_ptr++;
+ *reply_ptr = x;
reply_len++;
}
- /* The usual handshake ... */
-
- /*
- * NetBSD hints that the next to last byte
- * is sent with IRQ !!
- * Guido found out it's the last one (0x0),
- * but IRQ should be asserted already.
- * Problem with timeout detection: First
- * transition to /IRQ might be second
- * byte of timeout packet!
- * Timeouts are signaled by 4x FF.
- */
- if (((status & TREQ) == 0) && (x == 0x00)) { /* != 0xFF */
- /* invert state bits, toggle ODD/EVEN */
- via[B] ^= ST_MASK;
- /* adjust packet length */
- reply_len--;
- reply_ptr--;
- macii_state = read_done;
- } else {
- /* not caught: ST_CMD */
- /* required for re-entry 'reading'! */
- if ((status & ST_MASK) == ST_IDLE) {
- /* (in)sanity check - set even */
- via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
- } else {
- /* invert state bits */
- via[B] ^= ST_MASK;
- }
- }
+ /* invert state bits, toggle ODD/EVEN */
+ via[B] ^= ST_MASK;
break;
case read_done:
x = via[SR];
+
if (reading_reply) {
+ reading_reply = 0;
req = current_req;
- req->reply_len = reply_ptr - req->reply;
+ req->reply_len = reply_len;
req->complete = 1;
current_req = req->next;
if (req->done) (*req->done)(req);
- } else {
- adb_input(reply_buf, reply_ptr - reply_buf,
- regs, 0);
- }
+ } else if (reply_len && autopoll_devs)
+ adb_input(reply_buf, reply_len, 0);
- /*
- * remember this device ID; it's the latest we got a
- * reply from!
- */
- last_reply = command_byte;
- last_active = (command_byte & 0xF0) >> 4;
+ macii_state = idle;
/* SRQ seen before, initiate poll now */
- if (need_poll) {
- macii_state = idle;
+ if (srq_asserted)
macii_queue_poll();
- need_poll = 0;
- break;
- }
-
- /* set ADB state to idle */
- via[B] = (via[B] & ~ST_MASK) | ST_IDLE;
-
- /* /IRQ seen, so the ADB controller has data for us */
- if ((via[B] & TREQ) != 0) {
- macii_state = reading;
- reply_buf[0] = command_byte;
- reply_ptr = reply_buf + 1;
- reply_len = 1;
- prefix_len = 1;
- reading_reply = 0;
- } else {
- /* no IRQ, send next packet or wait */
- macii_state = idle;
- if (current_req)
- macii_start();
- else
- macii_retransmit(last_active);
- }
+ if (current_req)
+ macii_start();
+ else
+ if (need_autopoll())
+ macii_autopoll(autopoll_devs);
+
+ if (macii_state == idle)
+ via[B] = (via[B] & ~ST_MASK) | ST_IDLE;
break;
default:
break;
}
- /* reset mutex and interrupts */
- driver_running = 0;
- local_irq_restore(flags);
+
+ entered--;
return IRQ_HANDLED;
}
diff --git a/drivers/macintosh/via-maciisi.c b/drivers/macintosh/via-maciisi.c
index 0129fcc3b18..34d02a91b29 100644
--- a/drivers/macintosh/via-maciisi.c
+++ b/drivers/macintosh/via-maciisi.c
@@ -18,14 +18,12 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
-#include <linux/sched.h>
#include <linux/adb.h>
#include <linux/cuda.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
-#include <asm/machw.h>
#include <asm/mac_via.h>
static volatile unsigned char *via;
@@ -63,10 +61,10 @@ static volatile unsigned char *via;
#undef DEBUG_MACIISI_ADB
-static struct adb_request* current_req = NULL;
-static struct adb_request* last_req = NULL;
+static struct adb_request* current_req;
+static struct adb_request* last_req;
static unsigned char maciisi_rbuf[16];
-static unsigned char *reply_ptr = NULL;
+static unsigned char *reply_ptr;
static int data_index;
static int reading_reply;
static int reply_len;
@@ -84,8 +82,8 @@ static int maciisi_init(void);
static int maciisi_send_request(struct adb_request* req, int sync);
static void maciisi_sync(struct adb_request *req);
static int maciisi_write(struct adb_request* req);
-static irqreturn_t maciisi_interrupt(int irq, void* arg, struct pt_regs* regs);
-static void maciisi_input(unsigned char *buf, int nb, struct pt_regs *regs);
+static irqreturn_t maciisi_interrupt(int irq, void* arg);
+static void maciisi_input(unsigned char *buf, int nb);
static int maciisi_init_via(void);
static void maciisi_poll(void);
static int maciisi_start(void);
@@ -124,8 +122,8 @@ maciisi_init(void)
return err;
}
- if (request_irq(IRQ_MAC_ADB, maciisi_interrupt, IRQ_FLG_LOCK | IRQ_FLG_FAST,
- "ADB", maciisi_interrupt)) {
+ if (request_irq(IRQ_MAC_ADB, maciisi_interrupt, 0, "ADB",
+ maciisi_interrupt)) {
printk(KERN_ERR "maciisi_init: can't get irq %d\n", IRQ_MAC_ADB);
return -EAGAIN;
}
@@ -290,7 +288,7 @@ static void maciisi_sync(struct adb_request *req)
}
/* This could be BAD... when the ADB controller doesn't respond
* for this long, it's probably not coming back :-( */
- if(count >= 50) /* Hopefully shouldn't happen */
+ if (count > 50) /* Hopefully shouldn't happen */
printk(KERN_ERR "maciisi_send_request: poll timed out!\n");
}
@@ -421,7 +419,7 @@ maciisi_poll(void)
local_irq_save(flags);
if (via[IFR] & SR_INT) {
- maciisi_interrupt(0, NULL, NULL);
+ maciisi_interrupt(0, NULL);
}
else /* avoid calling this function too quickly in a loop */
udelay(ADB_DELAY);
@@ -433,7 +431,7 @@ maciisi_poll(void)
register is either full or empty. In practice, I have no idea what
it means :( */
static irqreturn_t
-maciisi_interrupt(int irq, void* arg, struct pt_regs* regs)
+maciisi_interrupt(int irq, void* arg)
{
int status;
struct adb_request *req;
@@ -612,7 +610,7 @@ maciisi_interrupt(int irq, void* arg, struct pt_regs* regs)
/* Obviously, we got it */
reading_reply = 0;
} else {
- maciisi_input(maciisi_rbuf, reply_ptr - maciisi_rbuf, regs);
+ maciisi_input(maciisi_rbuf, reply_ptr - maciisi_rbuf);
}
maciisi_state = idle;
status = via[B] & (TIP|TREQ);
@@ -657,7 +655,7 @@ maciisi_interrupt(int irq, void* arg, struct pt_regs* regs)
}
static void
-maciisi_input(unsigned char *buf, int nb, struct pt_regs *regs)
+maciisi_input(unsigned char *buf, int nb)
{
#ifdef DEBUG_MACIISI_ADB
int i;
@@ -665,7 +663,7 @@ maciisi_input(unsigned char *buf, int nb, struct pt_regs *regs)
switch (buf[0]) {
case ADB_PACKET:
- adb_input(buf+2, nb-2, regs, buf[1] & 0x40);
+ adb_input(buf+2, nb-2, buf[1] & 0x40);
break;
default:
#ifdef DEBUG_MACIISI_ADB
diff --git a/drivers/macintosh/via-pmu-backlight.c b/drivers/macintosh/via-pmu-backlight.c
index a82f313d9dc..b1d91170ded 100644
--- a/drivers/macintosh/via-pmu-backlight.c
+++ b/drivers/macintosh/via-pmu-backlight.c
@@ -15,14 +15,14 @@
#define MAX_PMU_LEVEL 0xFF
-static struct backlight_properties pmu_backlight_data;
-static spinlock_t pmu_backlight_lock;
-static int sleeping;
+static const struct backlight_ops pmu_backlight_data;
+static DEFINE_SPINLOCK(pmu_backlight_lock);
+static int sleeping, uses_pmu_bl;
static u8 bl_curve[FB_BACKLIGHT_LEVELS];
static void pmu_backlight_init_curve(u8 off, u8 min, u8 max)
{
- unsigned int i, flat, count, range = (max - min);
+ int i, flat, count, range = (max - min);
bl_curve[0] = off;
@@ -68,20 +68,14 @@ static int pmu_backlight_get_level_brightness(int level)
return pmulevel;
}
-static int pmu_backlight_update_status(struct backlight_device *bd)
+static int __pmu_backlight_update_status(struct backlight_device *bd)
{
struct adb_request req;
- unsigned long flags;
- int level = bd->props->brightness;
-
- spin_lock_irqsave(&pmu_backlight_lock, flags);
+ int level = bd->props.brightness;
- /* Don't update brightness when sleeping */
- if (sleeping)
- goto out;
- if (bd->props->power != FB_BLANK_UNBLANK ||
- bd->props->fb_blank != FB_BLANK_UNBLANK)
+ if (bd->props.power != FB_BLANK_UNBLANK ||
+ bd->props.fb_blank != FB_BLANK_UNBLANK)
level = 0;
if (level > 0) {
@@ -99,22 +93,32 @@ static int pmu_backlight_update_status(struct backlight_device *bd)
pmu_wait_complete(&req);
}
-out:
- spin_unlock_irqrestore(&pmu_backlight_lock, flags);
-
return 0;
}
+static int pmu_backlight_update_status(struct backlight_device *bd)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&pmu_backlight_lock, flags);
+ /* Don't update brightness when sleeping */
+ if (!sleeping)
+ rc = __pmu_backlight_update_status(bd);
+ spin_unlock_irqrestore(&pmu_backlight_lock, flags);
+ return rc;
+}
+
+
static int pmu_backlight_get_brightness(struct backlight_device *bd)
{
- return bd->props->brightness;
+ return bd->props.brightness;
}
-static struct backlight_properties pmu_backlight_data = {
- .owner = THIS_MODULE,
+static const struct backlight_ops pmu_backlight_data = {
.get_brightness = pmu_backlight_get_brightness,
.update_status = pmu_backlight_update_status,
- .max_brightness = (FB_BACKLIGHT_LEVELS - 1),
+
};
#ifdef CONFIG_PM
@@ -124,37 +128,53 @@ void pmu_backlight_set_sleep(int sleep)
spin_lock_irqsave(&pmu_backlight_lock, flags);
sleeping = sleep;
+ if (pmac_backlight && uses_pmu_bl) {
+ if (sleep) {
+ struct adb_request req;
+
+ pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
+ PMU_POW_BACKLIGHT | PMU_POW_OFF);
+ pmu_wait_complete(&req);
+ } else
+ __pmu_backlight_update_status(pmac_backlight);
+ }
spin_unlock_irqrestore(&pmu_backlight_lock, flags);
}
#endif /* CONFIG_PM */
void __init pmu_backlight_init()
{
+ struct backlight_properties props;
struct backlight_device *bd;
char name[10];
int level, autosave;
/* Special case for the old PowerBook since I can't test on it */
autosave =
- machine_is_compatible("AAPL,3400/2400") ||
- machine_is_compatible("AAPL,3500");
+ of_machine_is_compatible("AAPL,3400/2400") ||
+ of_machine_is_compatible("AAPL,3500");
if (!autosave &&
!pmac_has_backlight_type("pmu") &&
- !machine_is_compatible("AAPL,PowerBook1998") &&
- !machine_is_compatible("PowerBook1,1"))
+ !of_machine_is_compatible("AAPL,PowerBook1998") &&
+ !of_machine_is_compatible("PowerBook1,1"))
return;
snprintf(name, sizeof(name), "pmubl");
- bd = backlight_device_register(name, NULL, &pmu_backlight_data);
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
+ bd = backlight_device_register(name, NULL, NULL, &pmu_backlight_data,
+ &props);
if (IS_ERR(bd)) {
- printk("pmubl: Backlight registration failed\n");
- goto error;
+ printk(KERN_ERR "PMU Backlight registration failed\n");
+ return;
}
+ uses_pmu_bl = 1;
pmu_backlight_init_curve(0x7F, 0x46, 0x0E);
- level = pmu_backlight_data.max_brightness;
+ level = bd->props.max_brightness;
if (autosave) {
/* read autosaved value if available */
@@ -164,24 +184,12 @@ void __init pmu_backlight_init()
level = pmu_backlight_curve_lookup(
(req.reply[0] >> 4) *
- pmu_backlight_data.max_brightness / 15);
+ bd->props.max_brightness / 15);
}
- down(&bd->sem);
- bd->props->brightness = level;
- bd->props->power = FB_BLANK_UNBLANK;
- bd->props->update_status(bd);
- up(&bd->sem);
-
- mutex_lock(&pmac_backlight_mutex);
- if (!pmac_backlight)
- pmac_backlight = bd;
- mutex_unlock(&pmac_backlight_mutex);
-
- printk("pmubl: Backlight initialized (%s)\n", name);
-
- return;
+ bd->props.brightness = level;
+ bd->props.power = FB_BLANK_UNBLANK;
+ backlight_update_status(bd);
-error:
- return;
+ printk(KERN_INFO "PMU Backlight initialized (%s)\n", name);
}
diff --git a/drivers/macintosh/via-pmu-led.c b/drivers/macintosh/via-pmu-led.c
index 179af10105d..19c371809d7 100644
--- a/drivers/macintosh/via-pmu-led.c
+++ b/drivers/macintosh/via-pmu-led.c
@@ -31,7 +31,6 @@ static spinlock_t pmu_blink_lock;
static struct adb_request pmu_blink_req;
/* -1: no change, 0: request off, 1: request on */
static int requested_change;
-static int sleeping;
static void pmu_req_done(struct adb_request * req)
{
@@ -41,7 +40,7 @@ static void pmu_req_done(struct adb_request * req)
/* if someone requested a change in the meantime
* (we only see the last one which is fine)
* then apply it now */
- if (requested_change != -1 && !sleeping)
+ if (requested_change != -1 && !pmu_sys_suspended)
pmu_request(&pmu_blink_req, NULL, 4, 0xee, 4, 0, requested_change);
/* reset requested change */
requested_change = -1;
@@ -66,48 +65,20 @@ static void pmu_led_set(struct led_classdev *led_cdev,
break;
}
/* if request isn't done, then don't do anything */
- if (pmu_blink_req.complete && !sleeping)
+ if (pmu_blink_req.complete && !pmu_sys_suspended)
pmu_request(&pmu_blink_req, NULL, 4, 0xee, 4, 0, requested_change);
out:
spin_unlock_irqrestore(&pmu_blink_lock, flags);
}
static struct led_classdev pmu_led = {
- .name = "pmu-front-led",
+ .name = "pmu-led::front",
#ifdef CONFIG_ADB_PMU_LED_IDE
.default_trigger = "ide-disk",
#endif
.brightness_set = pmu_led_set,
};
-#ifdef CONFIG_PM
-static int pmu_led_sleep_call(struct pmu_sleep_notifier *self, int when)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&pmu_blink_lock, flags);
-
- switch (when) {
- case PBOOK_SLEEP_REQUEST:
- sleeping = 1;
- break;
- case PBOOK_WAKE:
- sleeping = 0;
- break;
- default:
- /* do nothing */
- break;
- }
- spin_unlock_irqrestore(&pmu_blink_lock, flags);
-
- return PBOOK_SLEEP_OK;
-}
-
-static struct pmu_sleep_notifier via_pmu_led_sleep_notif = {
- .notifier_call = pmu_led_sleep_call,
-};
-#endif
-
static int __init via_pmu_led_init(void)
{
struct device_node *dt;
@@ -120,11 +91,15 @@ static int __init via_pmu_led_init(void)
dt = of_find_node_by_path("/");
if (dt == NULL)
return -ENODEV;
- model = get_property(dt, "model", NULL);
- if (model == NULL)
+ model = of_get_property(dt, "model", NULL);
+ if (model == NULL) {
+ of_node_put(dt);
return -ENODEV;
+ }
if (strncmp(model, "PowerBook", strlen("PowerBook")) != 0 &&
- strncmp(model, "iBook", strlen("iBook")) != 0) {
+ strncmp(model, "iBook", strlen("iBook")) != 0 &&
+ strcmp(model, "PowerMac7,2") != 0 &&
+ strcmp(model, "PowerMac7,3") != 0) {
of_node_put(dt);
/* ignore */
return -ENODEV;
@@ -135,9 +110,7 @@ static int __init via_pmu_led_init(void)
/* no outstanding req */
pmu_blink_req.complete = 1;
pmu_blink_req.done = pmu_req_done;
-#ifdef CONFIG_PM
- pmu_register_sleep_notifier(&via_pmu_led_sleep_notif);
-#endif
+
return led_classdev_register(NULL, &pmu_led);
}
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index dda03985dcf..dee88e59f0d 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -10,16 +10,15 @@
*
* Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.
* Copyright (C) 2001-2002 Benjamin Herrenschmidt
+ * Copyright (C) 2006-2007 Johannes Berg
*
* THIS DRIVER IS BECOMING A TOTAL MESS !
* - Cleanup atomically disabling reply to PMU events after
* a sleep or a freq. switch
- * - Move sleep code out of here to pmac_pm, merge into new
- * common PM infrastructure
- * - Save/Restore PCI space properly
*
*/
#include <stdarg.h>
+#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -33,23 +32,26 @@
#include <linux/adb.h>
#include <linux/pmu.h>
#include <linux/cuda.h>
-#include <linux/smp_lock.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/pm.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/device.h>
-#include <linux/sysdev.h>
-#include <linux/suspend.h>
+#include <linux/syscore_ops.h>
+#include <linux/freezer.h>
#include <linux/syscalls.h>
+#include <linux/suspend.h>
#include <linux/cpu.h>
+#include <linux/compat.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
#include <asm/pgtable.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/irq.h>
#include <asm/pmac_feature.h>
@@ -64,9 +66,7 @@
#include "via-pmu-event.h"
/* Some compile options */
-#undef SUSPEND_USES_PMU
-#define DEBUG_SLEEP
-#undef HACKED_PCI_SAVE
+#undef DEBUG_SLEEP
/* Misc minor number allocated for /dev/pmu */
#define PMU_MINOR 154
@@ -74,6 +74,7 @@
/* How many iterations between battery polls */
#define BATTERY_POLLING_COUNT 2
+static DEFINE_MUTEX(pmu_info_proc_mutex);
static volatile unsigned char __iomem *via;
/* VIA registers - spaced 0x200 bytes apart */
@@ -140,23 +141,20 @@ static volatile int adb_int_pending;
static volatile int disable_poll;
static struct device_node *vias;
static int pmu_kind = PMU_UNKNOWN;
-static int pmu_fully_inited = 0;
+static int pmu_fully_inited;
static int pmu_has_adb;
static struct device_node *gpio_node;
-static unsigned char __iomem *gpio_reg = NULL;
+static unsigned char __iomem *gpio_reg;
static int gpio_irq = NO_IRQ;
static int gpio_irq_enabled = -1;
-static volatile int pmu_suspended = 0;
+static volatile int pmu_suspended;
static spinlock_t pmu_lock;
static u8 pmu_intr_mask;
static int pmu_version;
static int drop_interrupts;
-#if defined(CONFIG_PM) && defined(CONFIG_PPC32)
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
static int option_lid_wakeup = 1;
-#endif /* CONFIG_PM && CONFIG_PPC32 */
-#if (defined(CONFIG_PM)&&defined(CONFIG_PPC32))||defined(CONFIG_PMAC_BACKLIGHT_LEGACY)
-static int sleep_in_progress;
-#endif
+#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
static unsigned long async_req_locks;
static unsigned int pmu_irq_stats[11];
@@ -168,7 +166,7 @@ static int option_server_mode;
int pmu_battery_count;
int pmu_cur_battery;
-unsigned int pmu_power_flags;
+unsigned int pmu_power_flags = PMU_PWR_AC_PRESENT;
struct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES];
static int query_batt_timer = BATTERY_POLLING_COUNT;
static struct adb_request batt_req;
@@ -176,10 +174,9 @@ static struct proc_dir_entry *proc_pmu_batt[PMU_MAX_BATTERIES];
int __fake_sleep;
int asleep;
-BLOCKING_NOTIFIER_HEAD(sleep_notifier_list);
#ifdef CONFIG_ADB
-static int adb_dev_map = 0;
+static int adb_dev_map;
static int pmu_adb_flags;
static int pmu_probe(void);
@@ -191,19 +188,13 @@ static int pmu_adb_reset_bus(void);
static int init_pmu(void);
static void pmu_start(void);
-static irqreturn_t via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs);
-static irqreturn_t gpio1_interrupt(int irq, void *arg, struct pt_regs *regs);
-static int proc_get_info(char *page, char **start, off_t off,
- int count, int *eof, void *data);
-static int proc_get_irqstats(char *page, char **start, off_t off,
- int count, int *eof, void *data);
+static irqreturn_t via_pmu_interrupt(int irq, void *arg);
+static irqreturn_t gpio1_interrupt(int irq, void *arg);
+static const struct file_operations pmu_info_proc_fops;
+static const struct file_operations pmu_irqstats_proc_fops;
static void pmu_pass_intr(unsigned char *data, int len);
-static int proc_get_batt(char *page, char **start, off_t off,
- int count, int *eof, void *data);
-static int proc_read_options(char *page, char **start, off_t off,
- int count, int *eof, void *data);
-static int proc_write_options(struct file *file, const char __user *buffer,
- unsigned long count, void *data);
+static const struct file_operations pmu_battery_proc_fops;
+static const struct file_operations pmu_options_proc_fops;
#ifdef CONFIG_ADB
struct adb_driver via_pmu_driver = {
@@ -223,7 +214,7 @@ extern void enable_kernel_fp(void);
#ifdef DEBUG_SLEEP
int pmu_polled_request(struct adb_request *req);
-int pmu_wink(struct adb_request *req);
+void pmu_blink(int n);
#endif
/*
@@ -288,7 +279,7 @@ int __init find_via_pmu(void)
if (vias == NULL)
return 0;
- reg = get_property(vias, "reg", NULL);
+ reg = of_get_property(vias, "reg", NULL);
if (reg == NULL) {
printk(KERN_ERR "via-pmu: No \"reg\" property !\n");
goto fail;
@@ -309,19 +300,22 @@ int __init find_via_pmu(void)
PMU_INT_TICK;
if (vias->parent->name && ((strcmp(vias->parent->name, "ohare") == 0)
- || device_is_compatible(vias->parent, "ohare")))
+ || of_device_is_compatible(vias->parent, "ohare")))
pmu_kind = PMU_OHARE_BASED;
- else if (device_is_compatible(vias->parent, "paddington"))
+ else if (of_device_is_compatible(vias->parent, "paddington"))
pmu_kind = PMU_PADDINGTON_BASED;
- else if (device_is_compatible(vias->parent, "heathrow"))
+ else if (of_device_is_compatible(vias->parent, "heathrow"))
pmu_kind = PMU_HEATHROW_BASED;
- else if (device_is_compatible(vias->parent, "Keylargo")
- || device_is_compatible(vias->parent, "K2-Keylargo")) {
+ else if (of_device_is_compatible(vias->parent, "Keylargo")
+ || of_device_is_compatible(vias->parent, "K2-Keylargo")) {
struct device_node *gpiop;
+ struct device_node *adbp;
u64 gaddr = OF_BAD_ADDR;
pmu_kind = PMU_KEYLARGO_BASED;
- pmu_has_adb = (find_type_devices("adb") != NULL);
+ adbp = of_find_node_by_type(NULL, "adb");
+ pmu_has_adb = (adbp != NULL);
+ of_node_put(adbp);
pmu_intr_mask = PMU_INT_PCEJECT |
PMU_INT_SNDBRT |
PMU_INT_ADB |
@@ -330,14 +324,16 @@ int __init find_via_pmu(void)
gpiop = of_find_node_by_name(NULL, "gpio");
if (gpiop) {
- reg = get_property(gpiop, "reg", NULL);
+ reg = of_get_property(gpiop, "reg", NULL);
if (reg)
gaddr = of_translate_address(gpiop, reg);
if (gaddr != OF_BAD_ADDR)
gpio_reg = ioremap(gaddr, 0x10);
}
- if (gpio_reg == NULL)
+ if (gpio_reg == NULL) {
printk(KERN_ERR "via-pmu: Can't find GPIO reg !\n");
+ goto fail_gpio;
+ }
} else
pmu_kind = PMU_UNKNOWN;
@@ -365,6 +361,9 @@ int __init find_via_pmu(void)
return 1;
fail:
of_node_put(vias);
+ iounmap(gpio_reg);
+ gpio_reg = NULL;
+ fail_gpio:
vias = NULL;
return 0;
}
@@ -401,10 +400,15 @@ static int __init via_pmu_start(void)
irq = irq_of_parse_and_map(vias, 0);
if (irq == NO_IRQ) {
- printk(KERN_ERR "via-pmu: can't map interruptn");
+ printk(KERN_ERR "via-pmu: can't map interrupt\n");
return -ENODEV;
}
- if (request_irq(irq, via_pmu_interrupt, 0, "VIA-PMU", (void *)0)) {
+ /* We set IRQF_NO_SUSPEND because we don't want the interrupt
+ * to be disabled between the 2 passes of driver suspend, we
+ * control our own disabling for that one
+ */
+ if (request_irq(irq, via_pmu_interrupt, IRQF_NO_SUSPEND,
+ "VIA-PMU", (void *)0)) {
printk(KERN_ERR "via-pmu: can't request irq %d\n", irq);
return -ENODEV;
}
@@ -418,7 +422,7 @@ static int __init via_pmu_start(void)
gpio_irq = irq_of_parse_and_map(gpio_node, 0);
if (gpio_irq != NO_IRQ) {
- if (request_irq(gpio_irq, gpio1_interrupt, 0,
+ if (request_irq(gpio_irq, gpio1_interrupt, IRQF_TIMER,
"GPIO1 ADB", (void *)0))
printk(KERN_ERR "pmu: can't get irq %d"
" (GPIO1)\n", gpio_irq);
@@ -463,8 +467,8 @@ static int __init via_pmu_dev_init(void)
#endif
#ifdef CONFIG_PPC32
- if (machine_is_compatible("AAPL,3400/2400") ||
- machine_is_compatible("AAPL,3500")) {
+ if (of_machine_is_compatible("AAPL,3400/2400") ||
+ of_machine_is_compatible("AAPL,3500")) {
int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO,
NULL, PMAC_MB_INFO_MODEL, 0);
pmu_battery_count = 1;
@@ -472,16 +476,17 @@ static int __init via_pmu_dev_init(void)
pmu_batteries[0].flags |= PMU_BATT_TYPE_COMET;
else
pmu_batteries[0].flags |= PMU_BATT_TYPE_HOOPER;
- } else if (machine_is_compatible("AAPL,PowerBook1998") ||
- machine_is_compatible("PowerBook1,1")) {
+ } else if (of_machine_is_compatible("AAPL,PowerBook1998") ||
+ of_machine_is_compatible("PowerBook1,1")) {
pmu_battery_count = 2;
pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART;
pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART;
} else {
- struct device_node* prim = find_devices("power-mgt");
+ struct device_node* prim =
+ of_find_node_by_name(NULL, "power-mgt");
const u32 *prim_info = NULL;
if (prim)
- prim_info = get_property(prim, "prim-info", NULL);
+ prim_info = of_get_property(prim, "prim-info", NULL);
if (prim_info) {
/* Other stuffs here yet unknown */
pmu_battery_count = (prim_info[6] >> 16) & 0xff;
@@ -489,6 +494,7 @@ static int __init via_pmu_dev_init(void)
if (pmu_battery_count > 1)
pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART;
}
+ of_node_put(prim);
}
#endif /* CONFIG_PPC32 */
@@ -500,20 +506,15 @@ static int __init via_pmu_dev_init(void)
for (i=0; i<pmu_battery_count; i++) {
char title[16];
sprintf(title, "battery_%ld", i);
- proc_pmu_batt[i] = create_proc_read_entry(title, 0, proc_pmu_root,
- proc_get_batt, (void *)i);
+ proc_pmu_batt[i] = proc_create_data(title, 0, proc_pmu_root,
+ &pmu_battery_proc_fops, (void *)i);
}
- proc_pmu_info = create_proc_read_entry("info", 0, proc_pmu_root,
- proc_get_info, NULL);
- proc_pmu_irqstats = create_proc_read_entry("interrupts", 0, proc_pmu_root,
- proc_get_irqstats, NULL);
- proc_pmu_options = create_proc_entry("options", 0600, proc_pmu_root);
- if (proc_pmu_options) {
- proc_pmu_options->nlink = 1;
- proc_pmu_options->read_proc = proc_read_options;
- proc_pmu_options->write_proc = proc_write_options;
- }
+ proc_pmu_info = proc_create("info", 0, proc_pmu_root, &pmu_info_proc_fops);
+ proc_pmu_irqstats = proc_create("interrupts", 0, proc_pmu_root,
+ &pmu_irqstats_proc_fops);
+ proc_pmu_options = proc_create("options", 0600, proc_pmu_root,
+ &pmu_options_proc_fops);
}
return 0;
}
@@ -550,7 +551,7 @@ init_pmu(void)
}
if (pmu_state == idle)
adb_int_pending = 1;
- via_pmu_interrupt(0, NULL, NULL);
+ via_pmu_interrupt(0, NULL);
udelay(10);
}
@@ -751,8 +752,9 @@ done_battery_state_smart(struct adb_request* req)
voltage = (req->reply[8] << 8) | req->reply[9];
break;
default:
- printk(KERN_WARNING "pmu.c : unrecognized battery info, len: %d, %02x %02x %02x %02x\n",
- req->reply_len, req->reply[0], req->reply[1], req->reply[2], req->reply[3]);
+ pr_warn("pmu.c: unrecognized battery info, "
+ "len: %d, %4ph\n", req->reply_len,
+ req->reply);
break;
}
}
@@ -793,27 +795,33 @@ query_battery_state(void)
2, PMU_SMART_BATTERY_STATE, pmu_cur_battery+1);
}
-static int
-proc_get_info(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int pmu_info_proc_show(struct seq_file *m, void *v)
{
- char* p = page;
-
- p += sprintf(p, "PMU driver version : %d\n", PMU_DRIVER_VERSION);
- p += sprintf(p, "PMU firmware version : %02x\n", pmu_version);
- p += sprintf(p, "AC Power : %d\n",
+ seq_printf(m, "PMU driver version : %d\n", PMU_DRIVER_VERSION);
+ seq_printf(m, "PMU firmware version : %02x\n", pmu_version);
+ seq_printf(m, "AC Power : %d\n",
((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0) || pmu_battery_count == 0);
- p += sprintf(p, "Battery count : %d\n", pmu_battery_count);
+ seq_printf(m, "Battery count : %d\n", pmu_battery_count);
- return p - page;
+ return 0;
}
-static int
-proc_get_irqstats(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int pmu_info_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pmu_info_proc_show, NULL);
+}
+
+static const struct file_operations pmu_info_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = pmu_info_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int pmu_irqstats_proc_show(struct seq_file *m, void *v)
{
int i;
- char* p = page;
static const char *irq_names[] = {
"Total CB1 triggered events",
"Total GPIO1 triggered events",
@@ -829,60 +837,76 @@ proc_get_irqstats(char *page, char **start, off_t off,
};
for (i=0; i<11; i++) {
- p += sprintf(p, " %2u: %10u (%s)\n",
+ seq_printf(m, " %2u: %10u (%s)\n",
i, pmu_irq_stats[i], irq_names[i]);
}
- return p - page;
+ return 0;
}
-static int
-proc_get_batt(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int pmu_irqstats_proc_open(struct inode *inode, struct file *file)
{
- long batnum = (long)data;
- char *p = page;
+ return single_open(file, pmu_irqstats_proc_show, NULL);
+}
+
+static const struct file_operations pmu_irqstats_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = pmu_irqstats_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int pmu_battery_proc_show(struct seq_file *m, void *v)
+{
+ long batnum = (long)m->private;
- p += sprintf(p, "\n");
- p += sprintf(p, "flags : %08x\n",
- pmu_batteries[batnum].flags);
- p += sprintf(p, "charge : %d\n",
- pmu_batteries[batnum].charge);
- p += sprintf(p, "max_charge : %d\n",
- pmu_batteries[batnum].max_charge);
- p += sprintf(p, "current : %d\n",
- pmu_batteries[batnum].amperage);
- p += sprintf(p, "voltage : %d\n",
- pmu_batteries[batnum].voltage);
- p += sprintf(p, "time rem. : %d\n",
- pmu_batteries[batnum].time_remaining);
-
- return p - page;
+ seq_putc(m, '\n');
+ seq_printf(m, "flags : %08x\n", pmu_batteries[batnum].flags);
+ seq_printf(m, "charge : %d\n", pmu_batteries[batnum].charge);
+ seq_printf(m, "max_charge : %d\n", pmu_batteries[batnum].max_charge);
+ seq_printf(m, "current : %d\n", pmu_batteries[batnum].amperage);
+ seq_printf(m, "voltage : %d\n", pmu_batteries[batnum].voltage);
+ seq_printf(m, "time rem. : %d\n", pmu_batteries[batnum].time_remaining);
+ return 0;
}
-static int
-proc_read_options(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int pmu_battery_proc_open(struct inode *inode, struct file *file)
{
- char *p = page;
+ return single_open(file, pmu_battery_proc_show, PDE_DATA(inode));
+}
+
+static const struct file_operations pmu_battery_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = pmu_battery_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
-#if defined(CONFIG_PM) && defined(CONFIG_PPC32)
+static int pmu_options_proc_show(struct seq_file *m, void *v)
+{
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
if (pmu_kind == PMU_KEYLARGO_BASED &&
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
- p += sprintf(p, "lid_wakeup=%d\n", option_lid_wakeup);
+ seq_printf(m, "lid_wakeup=%d\n", option_lid_wakeup);
#endif
if (pmu_kind == PMU_KEYLARGO_BASED)
- p += sprintf(p, "server_mode=%d\n", option_server_mode);
+ seq_printf(m, "server_mode=%d\n", option_server_mode);
- return p - page;
+ return 0;
}
-
-static int
-proc_write_options(struct file *file, const char __user *buffer,
- unsigned long count, void *data)
+
+static int pmu_options_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pmu_options_proc_show, NULL);
+}
+
+static ssize_t pmu_options_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *pos)
{
char tmp[33];
char *label, *val;
- unsigned long fcount = count;
+ size_t fcount = count;
if (!count)
return -EINVAL;
@@ -906,7 +930,7 @@ proc_write_options(struct file *file, const char __user *buffer,
*(val++) = 0;
while(*val == ' ')
val++;
-#if defined(CONFIG_PM) && defined(CONFIG_PPC32)
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
if (pmu_kind == PMU_KEYLARGO_BASED &&
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
if (!strcmp(label, "lid_wakeup"))
@@ -921,10 +945,18 @@ proc_write_options(struct file *file, const char __user *buffer,
return fcount;
}
+static const struct file_operations pmu_options_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = pmu_options_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = pmu_options_proc_write,
+};
+
#ifdef CONFIG_ADB
/* Send an ADB command */
-static int
-pmu_send_request(struct adb_request *req, int sync)
+static int pmu_send_request(struct adb_request *req, int sync)
{
int i, ret;
@@ -1003,16 +1035,11 @@ pmu_send_request(struct adb_request *req, int sync)
}
/* Enable/disable autopolling */
-static int
-pmu_adb_autopoll(int devs)
+static int __pmu_adb_autopoll(int devs)
{
struct adb_request req;
- if ((vias == NULL) || (!pmu_fully_inited) || !pmu_has_adb)
- return -ENXIO;
-
if (devs) {
- adb_dev_map = devs;
pmu_request(&req, NULL, 5, PMU_ADB_CMD, 0, 0x86,
adb_dev_map >> 8, adb_dev_map);
pmu_adb_flags = 2;
@@ -1025,9 +1052,17 @@ pmu_adb_autopoll(int devs)
return 0;
}
+static int pmu_adb_autopoll(int devs)
+{
+ if ((vias == NULL) || (!pmu_fully_inited) || !pmu_has_adb)
+ return -ENXIO;
+
+ adb_dev_map = devs;
+ return __pmu_adb_autopoll(devs);
+}
+
/* Reset the ADB bus */
-static int
-pmu_adb_reset_bus(void)
+static int pmu_adb_reset_bus(void)
{
struct adb_request req;
int save_autopoll = adb_dev_map;
@@ -1036,13 +1071,13 @@ pmu_adb_reset_bus(void)
return -ENXIO;
/* anyone got a better idea?? */
- pmu_adb_autopoll(0);
+ __pmu_adb_autopoll(0);
- req.nbytes = 5;
+ req.nbytes = 4;
req.done = NULL;
req.data[0] = PMU_ADB_CMD;
- req.data[1] = 0;
- req.data[2] = ADB_BUSRESET;
+ req.data[1] = ADB_BUSRESET;
+ req.data[2] = 0;
req.data[3] = 0;
req.data[4] = 0;
req.reply_len = 0;
@@ -1054,7 +1089,7 @@ pmu_adb_reset_bus(void)
pmu_wait_complete(&req);
if (save_autopoll != 0)
- pmu_adb_autopoll(save_autopoll);
+ __pmu_adb_autopoll(save_autopoll);
return 0;
}
@@ -1210,7 +1245,7 @@ pmu_poll(void)
return;
if (disable_poll)
return;
- via_pmu_interrupt(0, NULL, NULL);
+ via_pmu_interrupt(0, NULL);
}
void
@@ -1223,7 +1258,7 @@ pmu_poll_adb(void)
/* Kicks ADB read when PMU is suspended */
adb_int_pending = 1;
do {
- via_pmu_interrupt(0, NULL, NULL);
+ via_pmu_interrupt(0, NULL);
} while (pmu_suspended && (adb_int_pending || pmu_state != idle
|| req_awaiting_reply));
}
@@ -1234,7 +1269,7 @@ pmu_wait_complete(struct adb_request *req)
if (!via)
return;
while((pmu_state != idle && pmu_state != locked) || !req->complete)
- via_pmu_interrupt(0, NULL, NULL);
+ via_pmu_interrupt(0, NULL);
}
/* This function loops until the PMU is idle and prevents it from
@@ -1246,9 +1281,7 @@ void
pmu_suspend(void)
{
unsigned long flags;
-#ifdef SUSPEND_USES_PMU
- struct adb_request *req;
-#endif
+
if (!via)
return;
@@ -1263,20 +1296,13 @@ pmu_suspend(void)
spin_unlock_irqrestore(&pmu_lock, flags);
if (req_awaiting_reply)
adb_int_pending = 1;
- via_pmu_interrupt(0, NULL, NULL);
+ via_pmu_interrupt(0, NULL);
spin_lock_irqsave(&pmu_lock, flags);
if (!adb_int_pending && pmu_state == idle && !req_awaiting_reply) {
-#ifdef SUSPEND_USES_PMU
- pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0);
- spin_unlock_irqrestore(&pmu_lock, flags);
- while(!req.complete)
- pmu_poll();
-#else /* SUSPEND_USES_PMU */
if (gpio_irq >= 0)
disable_irq_nosync(gpio_irq);
out_8(&via[IER], CB1_INT | IER_CLR);
spin_unlock_irqrestore(&pmu_lock, flags);
-#endif /* SUSPEND_USES_PMU */
break;
}
} while (1);
@@ -1297,23 +1323,16 @@ pmu_resume(void)
return;
}
adb_int_pending = 1;
-#ifdef SUSPEND_USES_PMU
- pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask);
- spin_unlock_irqrestore(&pmu_lock, flags);
- while(!req.complete)
- pmu_poll();
-#else /* SUSPEND_USES_PMU */
if (gpio_irq >= 0)
enable_irq(gpio_irq);
out_8(&via[IER], CB1_INT | IER_SET);
spin_unlock_irqrestore(&pmu_lock, flags);
pmu_poll();
-#endif /* SUSPEND_USES_PMU */
}
/* Interrupt data could be the result data from an ADB cmd */
static void
-pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
+pmu_handle_data(unsigned char *data, int len)
{
unsigned char ints, pirq;
int i = 0;
@@ -1388,7 +1407,7 @@ next:
if (!(pmu_kind == PMU_OHARE_BASED && len == 4
&& data[1] == 0x2c && data[3] == 0xff
&& (data[2] & ~1) == 0xf4))
- adb_input(data+1, len-1, regs, 1);
+ adb_input(data+1, len-1, 1);
#endif /* CONFIG_ADB */
}
}
@@ -1426,7 +1445,7 @@ next:
}
static struct adb_request*
-pmu_sr_intr(struct pt_regs *regs)
+pmu_sr_intr(void)
{
struct adb_request *req;
int bite = 0;
@@ -1511,7 +1530,7 @@ pmu_sr_intr(struct pt_regs *regs)
req = current_req;
/*
* For PMU sleep and freq change requests, we lock the
- * PMU until it's explicitely unlocked. This avoids any
+ * PMU until it's explicitly unlocked. This avoids any
* spurrious event polling getting in
*/
current_req = req->next;
@@ -1532,7 +1551,7 @@ pmu_sr_intr(struct pt_regs *regs)
}
static irqreturn_t
-via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs)
+via_pmu_interrupt(int irq, void *arg)
{
unsigned long flags;
int intr;
@@ -1562,7 +1581,7 @@ via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs)
pmu_irq_stats[0]++;
}
if (intr & SR_INT) {
- req = pmu_sr_intr(regs);
+ req = pmu_sr_intr();
if (req)
break;
}
@@ -1608,7 +1627,7 @@ no_free_slot:
/* Deal with interrupt datas outside of the lock */
if (int_data >= 0) {
- pmu_handle_data(interrupt_data[int_data], interrupt_data_len[int_data], regs);
+ pmu_handle_data(interrupt_data[int_data], interrupt_data_len[int_data]);
spin_lock_irqsave(&pmu_lock, flags);
++disable_poll;
int_data_state[int_data] = int_data_empty;
@@ -1633,7 +1652,7 @@ pmu_unlock(void)
static irqreturn_t
-gpio1_interrupt(int irq, void *arg, struct pt_regs *regs)
+gpio1_interrupt(int irq, void *arg)
{
unsigned long flags;
@@ -1646,7 +1665,7 @@ gpio1_interrupt(int irq, void *arg, struct pt_regs *regs)
pmu_irq_stats[1]++;
adb_int_pending = 1;
spin_unlock_irqrestore(&pmu_lock, flags);
- via_pmu_interrupt(0, NULL, NULL);
+ via_pmu_interrupt(0, NULL);
return IRQ_HANDLED;
}
return IRQ_NONE;
@@ -1728,239 +1747,7 @@ pmu_present(void)
return via != 0;
}
-#ifdef CONFIG_PM
-
-static LIST_HEAD(sleep_notifiers);
-
-int
-pmu_register_sleep_notifier(struct pmu_sleep_notifier *n)
-{
- struct list_head *list;
- struct pmu_sleep_notifier *notifier;
-
- for (list = sleep_notifiers.next; list != &sleep_notifiers;
- list = list->next) {
- notifier = list_entry(list, struct pmu_sleep_notifier, list);
- if (n->priority > notifier->priority)
- break;
- }
- __list_add(&n->list, list->prev, list);
- return 0;
-}
-EXPORT_SYMBOL(pmu_register_sleep_notifier);
-
-int
-pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n)
-{
- if (n->list.next == 0)
- return -ENOENT;
- list_del(&n->list);
- n->list.next = NULL;
- return 0;
-}
-EXPORT_SYMBOL(pmu_unregister_sleep_notifier);
-#endif /* CONFIG_PM */
-
-#if defined(CONFIG_PM) && defined(CONFIG_PPC32)
-
-/* Sleep is broadcast last-to-first */
-static int
-broadcast_sleep(int when, int fallback)
-{
- int ret = PBOOK_SLEEP_OK;
- struct list_head *list;
- struct pmu_sleep_notifier *notifier;
-
- for (list = sleep_notifiers.prev; list != &sleep_notifiers;
- list = list->prev) {
- notifier = list_entry(list, struct pmu_sleep_notifier, list);
- ret = notifier->notifier_call(notifier, when);
- if (ret != PBOOK_SLEEP_OK) {
- printk(KERN_DEBUG "sleep %d rejected by %p (%p)\n",
- when, notifier, notifier->notifier_call);
- for (; list != &sleep_notifiers; list = list->next) {
- notifier = list_entry(list, struct pmu_sleep_notifier, list);
- notifier->notifier_call(notifier, fallback);
- }
- return ret;
- }
- }
- return ret;
-}
-
-/* Wake is broadcast first-to-last */
-static int
-broadcast_wake(void)
-{
- int ret = PBOOK_SLEEP_OK;
- struct list_head *list;
- struct pmu_sleep_notifier *notifier;
-
- for (list = sleep_notifiers.next; list != &sleep_notifiers;
- list = list->next) {
- notifier = list_entry(list, struct pmu_sleep_notifier, list);
- notifier->notifier_call(notifier, PBOOK_WAKE);
- }
- return ret;
-}
-
-/*
- * This struct is used to store config register values for
- * PCI devices which may get powered off when we sleep.
- */
-static struct pci_save {
-#ifndef HACKED_PCI_SAVE
- u16 command;
- u16 cache_lat;
- u16 intr;
- u32 rom_address;
-#else
- u32 config[16];
-#endif
-} *pbook_pci_saves;
-static int pbook_npci_saves;
-
-static void
-pbook_alloc_pci_save(void)
-{
- int npci;
- struct pci_dev *pd = NULL;
-
- npci = 0;
- while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
- ++npci;
- }
- if (npci == 0)
- return;
- pbook_pci_saves = (struct pci_save *)
- kmalloc(npci * sizeof(struct pci_save), GFP_KERNEL);
- pbook_npci_saves = npci;
-}
-
-static void
-pbook_free_pci_save(void)
-{
- if (pbook_pci_saves == NULL)
- return;
- kfree(pbook_pci_saves);
- pbook_pci_saves = NULL;
- pbook_npci_saves = 0;
-}
-
-static void
-pbook_pci_save(void)
-{
- struct pci_save *ps = pbook_pci_saves;
- struct pci_dev *pd = NULL;
- int npci = pbook_npci_saves;
-
- if (ps == NULL)
- return;
-
- while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
- if (npci-- == 0)
- return;
-#ifndef HACKED_PCI_SAVE
- pci_read_config_word(pd, PCI_COMMAND, &ps->command);
- pci_read_config_word(pd, PCI_CACHE_LINE_SIZE, &ps->cache_lat);
- pci_read_config_word(pd, PCI_INTERRUPT_LINE, &ps->intr);
- pci_read_config_dword(pd, PCI_ROM_ADDRESS, &ps->rom_address);
-#else
- int i;
- for (i=1;i<16;i++)
- pci_read_config_dword(pd, i<<4, &ps->config[i]);
-#endif
- ++ps;
- }
-}
-
-/* For this to work, we must take care of a few things: If gmac was enabled
- * during boot, it will be in the pci dev list. If it's disabled at this point
- * (and it will probably be), then you can't access it's config space.
- */
-static void
-pbook_pci_restore(void)
-{
- u16 cmd;
- struct pci_save *ps = pbook_pci_saves - 1;
- struct pci_dev *pd = NULL;
- int npci = pbook_npci_saves;
- int j;
-
- while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
-#ifdef HACKED_PCI_SAVE
- int i;
- if (npci-- == 0)
- return;
- ps++;
- for (i=2;i<16;i++)
- pci_write_config_dword(pd, i<<4, ps->config[i]);
- pci_write_config_dword(pd, 4, ps->config[1]);
-#else
- if (npci-- == 0)
- return;
- ps++;
- if (ps->command == 0)
- continue;
- pci_read_config_word(pd, PCI_COMMAND, &cmd);
- if ((ps->command & ~cmd) == 0)
- continue;
- switch (pd->hdr_type) {
- case PCI_HEADER_TYPE_NORMAL:
- for (j = 0; j < 6; ++j)
- pci_write_config_dword(pd,
- PCI_BASE_ADDRESS_0 + j*4,
- pd->resource[j].start);
- pci_write_config_dword(pd, PCI_ROM_ADDRESS,
- ps->rom_address);
- pci_write_config_word(pd, PCI_CACHE_LINE_SIZE,
- ps->cache_lat);
- pci_write_config_word(pd, PCI_INTERRUPT_LINE,
- ps->intr);
- pci_write_config_word(pd, PCI_COMMAND, ps->command);
- break;
- }
-#endif
- }
-}
-
-#ifdef DEBUG_SLEEP
-/* N.B. This doesn't work on the 3400 */
-void
-pmu_blink(int n)
-{
- struct adb_request req;
-
- memset(&req, 0, sizeof(req));
-
- for (; n > 0; --n) {
- req.nbytes = 4;
- req.done = NULL;
- req.data[0] = 0xee;
- req.data[1] = 4;
- req.data[2] = 0;
- req.data[3] = 1;
- req.reply[0] = ADB_RET_OK;
- req.reply_len = 1;
- req.reply_expected = 0;
- pmu_polled_request(&req);
- mdelay(50);
- req.nbytes = 4;
- req.done = NULL;
- req.data[0] = 0xee;
- req.data[1] = 4;
- req.data[2] = 0;
- req.data[3] = 0;
- req.reply[0] = ADB_RET_OK;
- req.reply_len = 1;
- req.reply_expected = 0;
- pmu_polled_request(&req);
- mdelay(50);
- }
- mdelay(50);
-}
-#endif
-
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
/*
* Put the powerbook to sleep.
*/
@@ -1995,143 +1782,6 @@ restore_via_state(void)
out_8(&via[IER], IER_SET | SR_INT | CB1_INT);
}
-extern void pmu_backlight_set_sleep(int sleep);
-
-static int
-pmac_suspend_devices(void)
-{
- int ret;
-
- pm_prepare_console();
-
- /* Notify old-style device drivers & userland */
- ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, PBOOK_SLEEP_REJECT);
- if (ret != PBOOK_SLEEP_OK) {
- printk(KERN_ERR "Sleep rejected by drivers\n");
- return -EBUSY;
- }
-
- /* Sync the disks. */
- /* XXX It would be nice to have some way to ensure that
- * nobody is dirtying any new buffers while we wait. That
- * could be achieved using the refrigerator for processes
- * that swsusp uses
- */
- sys_sync();
-
- /* Sleep can fail now. May not be very robust but useful for debugging */
- ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE);
- if (ret != PBOOK_SLEEP_OK) {
- printk(KERN_ERR "Driver sleep failed\n");
- return -EBUSY;
- }
-
- /* Send suspend call to devices, hold the device core's dpm_sem */
- ret = device_suspend(PMSG_SUSPEND);
- if (ret) {
- broadcast_wake();
- printk(KERN_ERR "Driver sleep failed\n");
- return -EBUSY;
- }
-
-#ifdef CONFIG_PMAC_BACKLIGHT
- /* Tell backlight code not to muck around with the chip anymore */
- pmu_backlight_set_sleep(1);
-#endif
-
- /* Call platform functions marked "on sleep" */
- pmac_pfunc_i2c_suspend();
- pmac_pfunc_base_suspend();
-
- /* Stop preemption */
- preempt_disable();
-
- /* Make sure the decrementer won't interrupt us */
- asm volatile("mtdec %0" : : "r" (0x7fffffff));
- /* Make sure any pending DEC interrupt occurring while we did
- * the above didn't re-enable the DEC */
- mb();
- asm volatile("mtdec %0" : : "r" (0x7fffffff));
-
- /* We can now disable MSR_EE. This code of course works properly only
- * on UP machines... For SMP, if we ever implement sleep, we'll have to
- * stop the "other" CPUs way before we do all that stuff.
- */
- local_irq_disable();
-
- /* Broadcast power down irq
- * This isn't that useful in most cases (only directly wired devices can
- * use this but still... This will take care of sysdev's as well, so
- * we exit from here with local irqs disabled and PIC off.
- */
- ret = device_power_down(PMSG_SUSPEND);
- if (ret) {
- wakeup_decrementer();
- local_irq_enable();
- preempt_enable();
- device_resume();
- broadcast_wake();
- printk(KERN_ERR "Driver powerdown failed\n");
- return -EBUSY;
- }
-
- /* Wait for completion of async requests */
- while (!batt_req.complete)
- pmu_poll();
-
- /* Giveup the lazy FPU & vec so we don't have to back them
- * up from the low level code
- */
- enable_kernel_fp();
-
-#ifdef CONFIG_ALTIVEC
- if (cpu_has_feature(CPU_FTR_ALTIVEC))
- enable_kernel_altivec();
-#endif /* CONFIG_ALTIVEC */
-
- return 0;
-}
-
-static int
-pmac_wakeup_devices(void)
-{
- mdelay(100);
-
-#ifdef CONFIG_PMAC_BACKLIGHT
- /* Tell backlight code it can use the chip again */
- pmu_backlight_set_sleep(0);
-#endif
-
- /* Power back up system devices (including the PIC) */
- device_power_up();
-
- /* Force a poll of ADB interrupts */
- adb_int_pending = 1;
- via_pmu_interrupt(0, NULL, NULL);
-
- /* Restart jiffies & scheduling */
- wakeup_decrementer();
-
- /* Re-enable local CPU interrupts */
- local_irq_enable();
- mdelay(10);
- preempt_enable();
-
- /* Call platform functions marked "on wake" */
- pmac_pfunc_base_resume();
- pmac_pfunc_i2c_resume();
-
- /* Resume devices */
- device_resume();
-
- /* Notify old style drivers */
- broadcast_wake();
-
- pm_restore_console();
-
- return 0;
-}
-
#define GRACKLE_PM (1<<7)
#define GRACKLE_DOZE (1<<5)
#define GRACKLE_NAP (1<<4)
@@ -2142,19 +1792,12 @@ static int powerbook_sleep_grackle(void)
unsigned long save_l2cr;
unsigned short pmcr1;
struct adb_request req;
- int ret;
struct pci_dev *grackle;
- grackle = pci_find_slot(0, 0);
+ grackle = pci_get_bus_and_slot(0, 0);
if (!grackle)
return -ENODEV;
- ret = pmac_suspend_devices();
- if (ret) {
- printk(KERN_ERR "Sleep rejected by devices\n");
- return ret;
- }
-
/* Turn off various things. Darwin does some retry tests here... */
pmu_request(&req, NULL, 2, PMU_POWER_CTRL0, PMU_POW0_OFF|PMU_POW0_HARD_DRIVE);
pmu_wait_complete(&req);
@@ -2193,6 +1836,8 @@ static int powerbook_sleep_grackle(void)
pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP);
pci_write_config_word(grackle, 0x70, pmcr1);
+ pci_dev_put(grackle);
+
/* Make sure the PMU is idle */
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
restore_via_state();
@@ -2202,7 +1847,7 @@ static int powerbook_sleep_grackle(void)
_set_L2CR(save_l2cr);
/* Restore userland MMU context */
- set_context(current->active_mm->context.id, current->active_mm->pgd);
+ switch_mmu_context(NULL, current->active_mm);
/* Power things up */
pmu_unlock();
@@ -2215,8 +1860,6 @@ static int powerbook_sleep_grackle(void)
PMU_POW_ON|PMU_POW_BACKLIGHT|PMU_POW_CHARGER|PMU_POW_IRLED|PMU_POW_MEDIABAY);
pmu_wait_complete(&req);
- pmac_wakeup_devices();
-
return 0;
}
@@ -2226,7 +1869,6 @@ powerbook_sleep_Core99(void)
unsigned long save_l2cr;
unsigned long save_l3cr;
struct adb_request req;
- int ret;
if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) < 0) {
printk(KERN_ERR "Sleep mode not supported on this machine\n");
@@ -2236,12 +1878,6 @@ powerbook_sleep_Core99(void)
if (num_online_cpus() > 1 || cpu_is_offline(0))
return -EAGAIN;
- ret = pmac_suspend_devices();
- if (ret) {
- printk(KERN_ERR "Sleep rejected by devices\n");
- return ret;
- }
-
/* Stop environment and ADB interrupts */
pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0);
pmu_wait_complete(&req);
@@ -2300,7 +1936,7 @@ powerbook_sleep_Core99(void)
_set_L3CR(save_l3cr);
/* Restore userland MMU context */
- set_context(current->active_mm->context.id, current->active_mm->pgd);
+ switch_mmu_context(NULL, current->active_mm);
/* Tell PMU we are ready */
pmu_unlock();
@@ -2312,44 +1948,33 @@ powerbook_sleep_Core99(void)
/* Restore LPJ, cpufreq will adjust the cpu frequency */
loops_per_jiffy /= 2;
- pmac_wakeup_devices();
-
return 0;
}
#define PB3400_MEM_CTRL 0xf8000000
#define PB3400_MEM_CTRL_SLEEP 0x70
-static int
-powerbook_sleep_3400(void)
+static void __iomem *pb3400_mem_ctrl;
+
+static void powerbook_sleep_init_3400(void)
{
- int ret, i, x;
+ /* map in the memory controller registers */
+ pb3400_mem_ctrl = ioremap(PB3400_MEM_CTRL, 0x100);
+ if (pb3400_mem_ctrl == NULL)
+ printk(KERN_WARNING "ioremap failed: sleep won't be possible");
+}
+
+static int powerbook_sleep_3400(void)
+{
+ int i, x;
unsigned int hid0;
- unsigned long p;
+ unsigned long msr;
struct adb_request sleep_req;
- void __iomem *mem_ctrl;
unsigned int __iomem *mem_ctrl_sleep;
- /* first map in the memory controller registers */
- mem_ctrl = ioremap(PB3400_MEM_CTRL, 0x100);
- if (mem_ctrl == NULL) {
- printk("powerbook_sleep_3400: ioremap failed\n");
+ if (pb3400_mem_ctrl == NULL)
return -ENOMEM;
- }
- mem_ctrl_sleep = mem_ctrl + PB3400_MEM_CTRL_SLEEP;
-
- /* Allocate room for PCI save */
- pbook_alloc_pci_save();
-
- ret = pmac_suspend_devices();
- if (ret) {
- pbook_free_pci_save();
- printk(KERN_ERR "Sleep rejected by devices\n");
- return ret;
- }
-
- /* Save the state of PCI config space for some slots */
- pbook_pci_save();
+ mem_ctrl_sleep = pb3400_mem_ctrl + PB3400_MEM_CTRL_SLEEP;
/* Set the memory controller to keep the memory refreshed
while we're asleep */
@@ -2364,41 +1989,34 @@ powerbook_sleep_3400(void)
/* Ask the PMU to put us to sleep */
pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
- while (!sleep_req.complete)
- mb();
+ pmu_wait_complete(&sleep_req);
+ pmu_unlock();
- pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
+ pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 1);
- /* displacement-flush the L2 cache - necessary? */
- for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000)
- i = *(volatile int *)p;
asleep = 1;
/* Put the CPU into sleep mode */
hid0 = mfspr(SPRN_HID0);
hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP;
mtspr(SPRN_HID0, hid0);
- mtmsr(mfmsr() | MSR_POW | MSR_EE);
- udelay(10);
+ local_irq_enable();
+ msr = mfmsr() | MSR_POW;
+ while (asleep) {
+ mb();
+ mtmsr(msr);
+ isync();
+ }
+ local_irq_disable();
/* OK, we're awake again, start restoring things */
out_be32(mem_ctrl_sleep, 0x3f);
- pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
- pbook_pci_restore();
- pmu_unlock();
-
- /* wait for the PMU interrupt sequence to complete */
- while (asleep)
- mb();
-
- pmac_wakeup_devices();
- pbook_free_pci_save();
- iounmap(mem_ctrl);
+ pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 0);
return 0;
}
-#endif /* CONFIG_PM && CONFIG_PPC32 */
+#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
/*
* Support for /dev/pmu device
@@ -2463,6 +2081,7 @@ pmu_open(struct inode *inode, struct file *file)
pp->rb_get = pp->rb_put = 0;
spin_lock_init(&pp->lock);
init_waitqueue_head(&pp->wait);
+ mutex_lock(&pmu_info_proc_mutex);
spin_lock_irqsave(&all_pvt_lock, flags);
#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
pp->backlight_locker = 0;
@@ -2470,6 +2089,7 @@ pmu_open(struct inode *inode, struct file *file)
list_add(&pp->list, &all_pmu_pvt);
spin_unlock_irqrestore(&all_pvt_lock, flags);
file->private_data = pp;
+ mutex_unlock(&pmu_info_proc_mutex);
return 0;
}
@@ -2555,7 +2175,6 @@ pmu_release(struct inode *inode, struct file *file)
struct pmu_private *pp = file->private_data;
unsigned long flags;
- lock_kernel();
if (pp != 0) {
file->private_data = NULL;
spin_lock_irqsave(&all_pvt_lock, flags);
@@ -2569,47 +2188,112 @@ pmu_release(struct inode *inode, struct file *file)
kfree(pp);
}
- unlock_kernel();
return 0;
}
-static int
-pmu_ioctl(struct inode * inode, struct file *filp,
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
+static void pmac_suspend_disable_irqs(void)
+{
+ /* Call platform functions marked "on sleep" */
+ pmac_pfunc_i2c_suspend();
+ pmac_pfunc_base_suspend();
+}
+
+static int powerbook_sleep(suspend_state_t state)
+{
+ int error = 0;
+
+ /* Wait for completion of async requests */
+ while (!batt_req.complete)
+ pmu_poll();
+
+ /* Giveup the lazy FPU & vec so we don't have to back them
+ * up from the low level code
+ */
+ enable_kernel_fp();
+
+#ifdef CONFIG_ALTIVEC
+ if (cpu_has_feature(CPU_FTR_ALTIVEC))
+ enable_kernel_altivec();
+#endif /* CONFIG_ALTIVEC */
+
+ switch (pmu_kind) {
+ case PMU_OHARE_BASED:
+ error = powerbook_sleep_3400();
+ break;
+ case PMU_HEATHROW_BASED:
+ case PMU_PADDINGTON_BASED:
+ error = powerbook_sleep_grackle();
+ break;
+ case PMU_KEYLARGO_BASED:
+ error = powerbook_sleep_Core99();
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ if (error)
+ return error;
+
+ mdelay(100);
+
+ return 0;
+}
+
+static void pmac_suspend_enable_irqs(void)
+{
+ /* Force a poll of ADB interrupts */
+ adb_int_pending = 1;
+ via_pmu_interrupt(0, NULL);
+
+ mdelay(10);
+
+ /* Call platform functions marked "on wake" */
+ pmac_pfunc_base_resume();
+ pmac_pfunc_i2c_resume();
+}
+
+static int pmu_sleep_valid(suspend_state_t state)
+{
+ return state == PM_SUSPEND_MEM
+ && (pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, -1) >= 0);
+}
+
+static const struct platform_suspend_ops pmu_pm_ops = {
+ .enter = powerbook_sleep,
+ .valid = pmu_sleep_valid,
+};
+
+static int register_pmu_pm_ops(void)
+{
+ if (pmu_kind == PMU_OHARE_BASED)
+ powerbook_sleep_init_3400();
+ ppc_md.suspend_disable_irqs = pmac_suspend_disable_irqs;
+ ppc_md.suspend_enable_irqs = pmac_suspend_enable_irqs;
+ suspend_set_ops(&pmu_pm_ops);
+
+ return 0;
+}
+
+device_initcall(register_pmu_pm_ops);
+#endif
+
+static int pmu_ioctl(struct file *filp,
u_int cmd, u_long arg)
{
__u32 __user *argp = (__u32 __user *)arg;
int error = -EINVAL;
switch (cmd) {
-#if defined(CONFIG_PM) && defined(CONFIG_PPC32)
case PMU_IOC_SLEEP:
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- if (sleep_in_progress)
- return -EBUSY;
- sleep_in_progress = 1;
- switch (pmu_kind) {
- case PMU_OHARE_BASED:
- error = powerbook_sleep_3400();
- break;
- case PMU_HEATHROW_BASED:
- case PMU_PADDINGTON_BASED:
- error = powerbook_sleep_grackle();
- break;
- case PMU_KEYLARGO_BASED:
- error = powerbook_sleep_Core99();
- break;
- default:
- error = -ENOSYS;
- }
- sleep_in_progress = 0;
- break;
+ return pm_suspend(PM_SUSPEND_MEM);
case PMU_IOC_CAN_SLEEP:
- if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) < 0)
+ if (pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, -1) < 0)
return put_user(0, argp);
else
return put_user(1, argp);
-#endif /* CONFIG_PM && CONFIG_PPC32 */
#ifdef CONFIG_PMAC_BACKLIGHT_LEGACY
/* Compatibility ioctl's for backlight */
@@ -2617,9 +2301,6 @@ pmu_ioctl(struct inode * inode, struct file *filp,
{
int brightness;
- if (sleep_in_progress)
- return -EBUSY;
-
brightness = pmac_backlight_get_legacy_brightness();
if (brightness < 0)
return brightness;
@@ -2631,9 +2312,6 @@ pmu_ioctl(struct inode * inode, struct file *filp,
{
int brightness;
- if (sleep_in_progress)
- return -EBUSY;
-
error = get_user(brightness, argp);
if (error)
return error;
@@ -2663,13 +2341,67 @@ pmu_ioctl(struct inode * inode, struct file *filp,
return error;
}
-static struct file_operations pmu_device_fops = {
+static long pmu_unlocked_ioctl(struct file *filp,
+ u_int cmd, u_long arg)
+{
+ int ret;
+
+ mutex_lock(&pmu_info_proc_mutex);
+ ret = pmu_ioctl(filp, cmd, arg);
+ mutex_unlock(&pmu_info_proc_mutex);
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+#define PMU_IOC_GET_BACKLIGHT32 _IOR('B', 1, compat_size_t)
+#define PMU_IOC_SET_BACKLIGHT32 _IOW('B', 2, compat_size_t)
+#define PMU_IOC_GET_MODEL32 _IOR('B', 3, compat_size_t)
+#define PMU_IOC_HAS_ADB32 _IOR('B', 4, compat_size_t)
+#define PMU_IOC_CAN_SLEEP32 _IOR('B', 5, compat_size_t)
+#define PMU_IOC_GRAB_BACKLIGHT32 _IOR('B', 6, compat_size_t)
+
+static long compat_pmu_ioctl (struct file *filp, u_int cmd, u_long arg)
+{
+ switch (cmd) {
+ case PMU_IOC_SLEEP:
+ break;
+ case PMU_IOC_GET_BACKLIGHT32:
+ cmd = PMU_IOC_GET_BACKLIGHT;
+ break;
+ case PMU_IOC_SET_BACKLIGHT32:
+ cmd = PMU_IOC_SET_BACKLIGHT;
+ break;
+ case PMU_IOC_GET_MODEL32:
+ cmd = PMU_IOC_GET_MODEL;
+ break;
+ case PMU_IOC_HAS_ADB32:
+ cmd = PMU_IOC_HAS_ADB;
+ break;
+ case PMU_IOC_CAN_SLEEP32:
+ cmd = PMU_IOC_CAN_SLEEP;
+ break;
+ case PMU_IOC_GRAB_BACKLIGHT32:
+ cmd = PMU_IOC_GRAB_BACKLIGHT;
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return pmu_unlocked_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static const struct file_operations pmu_device_fops = {
.read = pmu_read,
.write = pmu_write,
.poll = pmu_fpoll,
- .ioctl = pmu_ioctl,
+ .unlocked_ioctl = pmu_unlocked_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = compat_pmu_ioctl,
+#endif
.open = pmu_open,
.release = pmu_release,
+ .llseek = noop_llseek,
};
static struct miscdevice pmu_device = {
@@ -2758,89 +2490,92 @@ pmu_polled_request(struct adb_request *req)
local_irq_restore(flags);
return 0;
}
-#endif /* DEBUG_SLEEP */
+/* N.B. This doesn't work on the 3400 */
+void pmu_blink(int n)
+{
+ struct adb_request req;
-/* FIXME: This is a temporary set of callbacks to enable us
- * to do suspend-to-disk.
- */
+ memset(&req, 0, sizeof(req));
-#if defined(CONFIG_PM) && defined(CONFIG_PPC32)
+ for (; n > 0; --n) {
+ req.nbytes = 4;
+ req.done = NULL;
+ req.data[0] = 0xee;
+ req.data[1] = 4;
+ req.data[2] = 0;
+ req.data[3] = 1;
+ req.reply[0] = ADB_RET_OK;
+ req.reply_len = 1;
+ req.reply_expected = 0;
+ pmu_polled_request(&req);
+ mdelay(50);
+ req.nbytes = 4;
+ req.done = NULL;
+ req.data[0] = 0xee;
+ req.data[1] = 4;
+ req.data[2] = 0;
+ req.data[3] = 0;
+ req.reply[0] = ADB_RET_OK;
+ req.reply_len = 1;
+ req.reply_expected = 0;
+ pmu_polled_request(&req);
+ mdelay(50);
+ }
+ mdelay(50);
+}
+#endif /* DEBUG_SLEEP */
-static int pmu_sys_suspended = 0;
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
+int pmu_sys_suspended;
-static int pmu_sys_suspend(struct sys_device *sysdev, pm_message_t state)
+static int pmu_syscore_suspend(void)
{
- if (state.event != PM_EVENT_SUSPEND || pmu_sys_suspended)
- return 0;
-
/* Suspend PMU event interrupts */
pmu_suspend();
-
pmu_sys_suspended = 1;
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+ /* Tell backlight code not to muck around with the chip anymore */
+ pmu_backlight_set_sleep(1);
+#endif
+
return 0;
}
-static int pmu_sys_resume(struct sys_device *sysdev)
+static void pmu_syscore_resume(void)
{
struct adb_request req;
if (!pmu_sys_suspended)
- return 0;
+ return;
/* Tell PMU we are ready */
pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
pmu_wait_complete(&req);
+#ifdef CONFIG_PMAC_BACKLIGHT
+ /* Tell backlight code it can use the chip again */
+ pmu_backlight_set_sleep(0);
+#endif
/* Resume PMU event interrupts */
pmu_resume();
-
pmu_sys_suspended = 0;
-
- return 0;
}
-#endif /* CONFIG_PM && CONFIG_PPC32 */
-
-static struct sysdev_class pmu_sysclass = {
- set_kset_name("pmu"),
-};
-
-static struct sys_device device_pmu = {
- .id = 0,
- .cls = &pmu_sysclass,
+static struct syscore_ops pmu_syscore_ops = {
+ .suspend = pmu_syscore_suspend,
+ .resume = pmu_syscore_resume,
};
-static struct sysdev_driver driver_pmu = {
-#if defined(CONFIG_PM) && defined(CONFIG_PPC32)
- .suspend = &pmu_sys_suspend,
- .resume = &pmu_sys_resume,
-#endif /* CONFIG_PM && CONFIG_PPC32 */
-};
-
-static int __init init_pmu_sysfs(void)
+static int pmu_syscore_register(void)
{
- int rc;
+ register_syscore_ops(&pmu_syscore_ops);
- rc = sysdev_class_register(&pmu_sysclass);
- if (rc) {
- printk(KERN_ERR "Failed registering PMU sys class\n");
- return -ENODEV;
- }
- rc = sysdev_register(&device_pmu);
- if (rc) {
- printk(KERN_ERR "Failed registering PMU sys device\n");
- return -ENODEV;
- }
- rc = sysdev_driver_register(&pmu_sysclass, &driver_pmu);
- if (rc) {
- printk(KERN_ERR "Failed registering PMU sys driver\n");
- return -ENODEV;
- }
return 0;
}
-
-subsys_initcall(init_pmu_sysfs);
+subsys_initcall(pmu_syscore_register);
+#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
EXPORT_SYMBOL(pmu_request);
EXPORT_SYMBOL(pmu_queue_request);
@@ -2850,10 +2585,10 @@ EXPORT_SYMBOL(pmu_wait_complete);
EXPORT_SYMBOL(pmu_suspend);
EXPORT_SYMBOL(pmu_resume);
EXPORT_SYMBOL(pmu_unlock);
-#if defined(CONFIG_PM) && defined(CONFIG_PPC32)
+#if defined(CONFIG_PPC32)
EXPORT_SYMBOL(pmu_enable_irled);
EXPORT_SYMBOL(pmu_battery_count);
EXPORT_SYMBOL(pmu_batteries);
EXPORT_SYMBOL(pmu_power_flags);
-#endif /* CONFIG_PM && CONFIG_PPC32 */
+#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
diff --git a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c
index 35b70323e7e..a00ee41f057 100644
--- a/drivers/macintosh/via-pmu68k.c
+++ b/drivers/macintosh/via-pmu68k.c
@@ -22,11 +22,9 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/delay.h>
-#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/blkdev.h>
#include <linux/pci.h>
-#include <linux/slab.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -36,11 +34,9 @@
#include <asm/macintosh.h>
#include <asm/macints.h>
-#include <asm/machw.h>
#include <asm/mac_via.h>
#include <asm/pgtable.h>
-#include <asm/system.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
@@ -96,30 +92,27 @@ static int data_index;
static int data_len;
static int adb_int_pending;
static int pmu_adb_flags;
-static int adb_dev_map = 0;
+static int adb_dev_map;
static struct adb_request bright_req_1, bright_req_2, bright_req_3;
static int pmu_kind = PMU_UNKNOWN;
-static int pmu_fully_inited = 0;
+static int pmu_fully_inited;
int asleep;
-BLOCKING_NOTIFIER_HEAD(sleep_notifier_list);
static int pmu_probe(void);
static int pmu_init(void);
static void pmu_start(void);
-static irqreturn_t pmu_interrupt(int irq, void *arg, struct pt_regs *regs);
+static irqreturn_t pmu_interrupt(int irq, void *arg);
static int pmu_send_request(struct adb_request *req, int sync);
static int pmu_autopoll(int devs);
void pmu_poll(void);
static int pmu_reset_bus(void);
-static int pmu_queue_request(struct adb_request *req);
static void pmu_start(void);
static void send_byte(int x);
static void recv_byte(void);
static void pmu_done(struct adb_request *req);
-static void pmu_handle_data(unsigned char *data, int len,
- struct pt_regs *regs);
+static void pmu_handle_data(unsigned char *data, int len);
static void set_volume(int level);
static void pmu_enable_backlight(int on);
static void pmu_set_brightness(int level);
@@ -222,7 +215,7 @@ pmu_init(void)
}
if (pmu_state == idle) {
adb_int_pending = 1;
- pmu_interrupt(0, NULL, NULL);
+ pmu_interrupt(0, NULL);
}
pmu_poll();
udelay(10);
@@ -477,7 +470,7 @@ pmu_request(struct adb_request *req, void (*done)(struct adb_request *),
return pmu_queue_request(req);
}
-static int
+int
pmu_queue_request(struct adb_request *req)
{
unsigned long flags;
@@ -563,17 +556,17 @@ pmu_poll(void)
local_irq_save(flags);
if (via1[IFR] & SR_INT) {
via1[IFR] = SR_INT;
- pmu_interrupt(IRQ_MAC_ADB_SR, NULL, NULL);
+ pmu_interrupt(IRQ_MAC_ADB_SR, NULL);
}
if (via1[IFR] & CB1_INT) {
via1[IFR] = CB1_INT;
- pmu_interrupt(IRQ_MAC_ADB_CL, NULL, NULL);
+ pmu_interrupt(IRQ_MAC_ADB_CL, NULL);
}
local_irq_restore(flags);
}
static irqreturn_t
-pmu_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+pmu_interrupt(int irq, void *dev_id)
{
struct adb_request *req;
int timeout, bite = 0; /* to prevent compiler warning */
@@ -657,7 +650,7 @@ pmu_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
if (pmu_state == reading_intr) {
- pmu_handle_data(interrupt_data, data_index, regs);
+ pmu_handle_data(interrupt_data, data_index);
} else {
req = current_req;
current_req = req->next;
@@ -701,7 +694,7 @@ pmu_done(struct adb_request *req)
/* Interrupt data could be the result data from an ADB cmd */
static void
-pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
+pmu_handle_data(unsigned char *data, int len)
{
static int show_pmu_ints = 1;
@@ -726,7 +719,7 @@ pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
}
pmu_done(req);
} else {
- adb_input(data+1, len-1, regs, 1);
+ adb_input(data+1, len-1, 1);
}
} else {
if (data[0] == 0x08 && len == 3) {
@@ -744,8 +737,8 @@ pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
}
}
-int backlight_level = -1;
-int backlight_enabled = 0;
+static int backlight_level = -1;
+static int backlight_enabled = 0;
#define LEVEL_TO_BRIGHT(lev) ((lev) < 1? 0x7f: 0x4a - ((lev) << 1))
@@ -821,243 +814,3 @@ pmu_present(void)
{
return (pmu_kind != PMU_UNKNOWN);
}
-
-#if 0 /* needs some work for 68K */
-
-/*
- * This struct is used to store config register values for
- * PCI devices which may get powered off when we sleep.
- */
-static struct pci_save {
- u16 command;
- u16 cache_lat;
- u16 intr;
-} *pbook_pci_saves;
-static int n_pbook_pci_saves;
-
-static inline void
-pbook_pci_save(void)
-{
- int npci;
- struct pci_dev *pd = NULL;
- struct pci_save *ps;
-
- npci = 0;
- while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL)
- ++npci;
- n_pbook_pci_saves = npci;
- if (npci == 0)
- return;
- ps = (struct pci_save *) kmalloc(npci * sizeof(*ps), GFP_KERNEL);
- pbook_pci_saves = ps;
- if (ps == NULL)
- return;
-
- pd = NULL;
- while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
- pci_read_config_word(pd, PCI_COMMAND, &ps->command);
- pci_read_config_word(pd, PCI_CACHE_LINE_SIZE, &ps->cache_lat);
- pci_read_config_word(pd, PCI_INTERRUPT_LINE, &ps->intr);
- ++ps;
- --npci;
- }
-}
-
-static inline void
-pbook_pci_restore(void)
-{
- u16 cmd;
- struct pci_save *ps = pbook_pci_saves;
- struct pci_dev *pd = NULL;
- int j;
-
- while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
- if (ps->command == 0)
- continue;
- pci_read_config_word(pd, PCI_COMMAND, &cmd);
- if ((ps->command & ~cmd) == 0)
- continue;
- switch (pd->hdr_type) {
- case PCI_HEADER_TYPE_NORMAL:
- for (j = 0; j < 6; ++j)
- pci_write_config_dword(pd,
- PCI_BASE_ADDRESS_0 + j*4,
- pd->resource[j].start);
- pci_write_config_dword(pd, PCI_ROM_ADDRESS,
- pd->resource[PCI_ROM_RESOURCE].start);
- pci_write_config_word(pd, PCI_CACHE_LINE_SIZE,
- ps->cache_lat);
- pci_write_config_word(pd, PCI_INTERRUPT_LINE,
- ps->intr);
- pci_write_config_word(pd, PCI_COMMAND, ps->command);
- break;
- /* other header types not restored at present */
- }
- }
-}
-
-/*
- * Put the powerbook to sleep.
- */
-#define IRQ_ENABLE ((unsigned int *)0xf3000024)
-#define MEM_CTRL ((unsigned int *)0xf8000070)
-
-int powerbook_sleep(void)
-{
- int ret, i, x;
- static int save_backlight;
- static unsigned int save_irqen;
- unsigned long msr;
- unsigned int hid0;
- unsigned long p, wait;
- struct adb_request sleep_req;
-
- /* Notify device drivers */
- ret = blocking_notifier_call_chain(&sleep_notifier_list,
- PBOOK_SLEEP, NULL);
- if (ret & NOTIFY_STOP_MASK)
- return -EBUSY;
-
- /* Sync the disks. */
- /* XXX It would be nice to have some way to ensure that
- * nobody is dirtying any new buffers while we wait. */
- sys_sync();
-
- /* Turn off the display backlight */
- save_backlight = backlight_enabled;
- if (save_backlight)
- pmu_enable_backlight(0);
-
- /* Give the disks a little time to actually finish writing */
- for (wait = jiffies + (HZ/4); time_before(jiffies, wait); )
- mb();
-
- /* Disable all interrupts except pmu */
- save_irqen = in_le32(IRQ_ENABLE);
- for (i = 0; i < 32; ++i)
- if (i != vias->intrs[0].line && (save_irqen & (1 << i)))
- disable_irq(i);
- asm volatile("mtdec %0" : : "r" (0x7fffffff));
-
- /* Save the state of PCI config space for some slots */
- pbook_pci_save();
-
- /* Set the memory controller to keep the memory refreshed
- while we're asleep */
- for (i = 0x403f; i >= 0x4000; --i) {
- out_be32(MEM_CTRL, i);
- do {
- x = (in_be32(MEM_CTRL) >> 16) & 0x3ff;
- } while (x == 0);
- if (x >= 0x100)
- break;
- }
-
- /* Ask the PMU to put us to sleep */
- pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
- while (!sleep_req.complete)
- mb();
- /* displacement-flush the L2 cache - necessary? */
- for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000)
- i = *(volatile int *)p;
- asleep = 1;
-
- /* Put the CPU into sleep mode */
- asm volatile("mfspr %0,1008" : "=r" (hid0) :);
- hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP;
- asm volatile("mtspr 1008,%0" : : "r" (hid0));
- local_save_flags(msr);
- msr |= MSR_POW | MSR_EE;
- local_irq_restore(msr);
- udelay(10);
-
- /* OK, we're awake again, start restoring things */
- out_be32(MEM_CTRL, 0x3f);
- pbook_pci_restore();
-
- /* wait for the PMU interrupt sequence to complete */
- while (asleep)
- mb();
-
- /* reenable interrupts */
- for (i = 0; i < 32; ++i)
- if (i != vias->intrs[0].line && (save_irqen & (1 << i)))
- enable_irq(i);
-
- /* Notify drivers */
- blocking_notifier_call_chain(&sleep_notifier_list, PBOOK_WAKE, NULL);
-
- /* reenable ADB autopoll */
- pmu_adb_autopoll(adb_dev_map);
-
- /* Turn on the screen backlight, if it was on before */
- if (save_backlight)
- pmu_enable_backlight(1);
-
- /* Wait for the hard disk to spin up */
-
- return 0;
-}
-
-/*
- * Support for /dev/pmu device
- */
-static int pmu_open(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static ssize_t pmu_read(struct file *file, char *buf,
- size_t count, loff_t *ppos)
-{
- return 0;
-}
-
-static ssize_t pmu_write(struct file *file, const char *buf,
- size_t count, loff_t *ppos)
-{
- return 0;
-}
-
-static int pmu_ioctl(struct inode * inode, struct file *filp,
- u_int cmd, u_long arg)
-{
- int error;
- __u32 value;
-
- switch (cmd) {
- case PMU_IOC_SLEEP:
- return -ENOSYS;
- case PMU_IOC_GET_BACKLIGHT:
- return put_user(backlight_level, (__u32 *)arg);
- case PMU_IOC_SET_BACKLIGHT:
- error = get_user(value, (__u32 *)arg);
- if (!error)
- pmu_set_brightness(value);
- return error;
- case PMU_IOC_GET_MODEL:
- return put_user(pmu_kind, (__u32 *)arg);
- }
- return -EINVAL;
-}
-
-static struct file_operations pmu_device_fops = {
- .read = pmu_read,
- .write = pmu_write,
- .ioctl = pmu_ioctl,
- .open = pmu_open,
-};
-
-static struct miscdevice pmu_device = {
- PMU_MINOR, "pmu", &pmu_device_fops
-};
-
-void pmu_device_init(void)
-{
- if (!via)
- return;
- if (misc_register(&pmu_device) < 0)
- printk(KERN_ERR "via-pmu68k: cannot register misc device.\n");
-}
-#endif /* CONFIG_PMAC_PBOOK */
-
diff --git a/drivers/macintosh/windfarm.h b/drivers/macintosh/windfarm.h
index 7a2482cc26a..028cdac2d33 100644
--- a/drivers/macintosh/windfarm.h
+++ b/drivers/macintosh/windfarm.h
@@ -17,7 +17,7 @@
#include <linux/device.h>
/* Display a 16.16 fixed point value */
-#define FIX32TOPRINT(f) ((f) >> 16),((((f) & 0xffff) * 1000) >> 16)
+#define FIX32TOPRINT(f) (((s32)(f)) >> 16),(((((s32)(f)) & 0xffff) * 1000) >> 16)
/*
* Control objects
@@ -35,12 +35,13 @@ struct wf_control_ops {
};
struct wf_control {
- struct list_head link;
- struct wf_control_ops *ops;
- char *name;
- int type;
- struct kref ref;
- struct device_attribute attr;
+ struct list_head link;
+ const struct wf_control_ops *ops;
+ const char *name;
+ int type;
+ struct kref ref;
+ struct device_attribute attr;
+ void *priv;
};
#define WF_CONTROL_TYPE_GENERIC 0
@@ -72,6 +73,26 @@ static inline int wf_control_set_min(struct wf_control *ct)
return ct->ops->set_value(ct, vmin);
}
+static inline int wf_control_set(struct wf_control *ct, s32 val)
+{
+ return ct->ops->set_value(ct, val);
+}
+
+static inline int wf_control_get(struct wf_control *ct, s32 *val)
+{
+ return ct->ops->get_value(ct, val);
+}
+
+static inline s32 wf_control_get_min(struct wf_control *ct)
+{
+ return ct->ops->get_min(ct);
+}
+
+static inline s32 wf_control_get_max(struct wf_control *ct)
+{
+ return ct->ops->get_max(ct);
+}
+
/*
* Sensor objects
*/
@@ -85,11 +106,12 @@ struct wf_sensor_ops {
};
struct wf_sensor {
- struct list_head link;
- struct wf_sensor_ops *ops;
- char *name;
- struct kref ref;
- struct device_attribute attr;
+ struct list_head link;
+ const struct wf_sensor_ops *ops;
+ const char *name;
+ struct kref ref;
+ struct device_attribute attr;
+ void *priv;
};
/* Same lifetime rules as controls */
@@ -99,6 +121,11 @@ extern struct wf_sensor * wf_find_sensor(const char *name);
extern int wf_get_sensor(struct wf_sensor *sr);
extern void wf_put_sensor(struct wf_sensor *sr);
+static inline int wf_sensor_get(struct wf_sensor *sr, s32 *val)
+{
+ return sr->ops->get_value(sr, val);
+}
+
/* For use by clients. Note that we are a bit racy here since
* notifier_block doesn't have a module owner field. I may fix
* it one day ...
diff --git a/drivers/macintosh/windfarm_ad7417_sensor.c b/drivers/macintosh/windfarm_ad7417_sensor.c
new file mode 100644
index 00000000000..7c28b71246c
--- /dev/null
+++ b/drivers/macintosh/windfarm_ad7417_sensor.c
@@ -0,0 +1,347 @@
+/*
+ * Windfarm PowerMac thermal control. AD7417 sensors
+ *
+ * Copyright 2012 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Released under the term of the GNU GPL v2.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+
+#include "windfarm.h"
+#include "windfarm_mpu.h"
+
+#define VERSION "1.0"
+
+struct wf_ad7417_priv {
+ struct kref ref;
+ struct i2c_client *i2c;
+ u8 config;
+ u8 cpu;
+ const struct mpu_data *mpu;
+ struct wf_sensor sensors[5];
+ struct mutex lock;
+};
+
+static int wf_ad7417_temp_get(struct wf_sensor *sr, s32 *value)
+{
+ struct wf_ad7417_priv *pv = sr->priv;
+ u8 buf[2];
+ s16 raw;
+ int rc;
+
+ *value = 0;
+ mutex_lock(&pv->lock);
+
+ /* Read temp register */
+ buf[0] = 0;
+ rc = i2c_master_send(pv->i2c, buf, 1);
+ if (rc < 0)
+ goto error;
+ rc = i2c_master_recv(pv->i2c, buf, 2);
+ if (rc < 0)
+ goto error;
+
+ /* Read a a 16-bit signed value */
+ raw = be16_to_cpup((__le16 *)buf);
+
+ /* Convert 8.8-bit to 16.16 fixed point */
+ *value = ((s32)raw) << 8;
+
+ mutex_unlock(&pv->lock);
+ return 0;
+
+error:
+ mutex_unlock(&pv->lock);
+ return -1;
+}
+
+/*
+ * Scaling factors for the AD7417 ADC converters (except
+ * for the CPU diode which is obtained from the EEPROM).
+ * Those values are obtained from the property list of
+ * the darwin driver
+ */
+#define ADC_12V_CURRENT_SCALE 0x0320 /* _AD2 */
+#define ADC_CPU_VOLTAGE_SCALE 0x00a0 /* _AD3 */
+#define ADC_CPU_CURRENT_SCALE 0x1f40 /* _AD4 */
+
+static void wf_ad7417_adc_convert(struct wf_ad7417_priv *pv,
+ int chan, s32 raw, s32 *value)
+{
+ switch(chan) {
+ case 1: /* Diode */
+ *value = (raw * (s32)pv->mpu->mdiode +
+ ((s32)pv->mpu->bdiode << 12)) >> 2;
+ break;
+ case 2: /* 12v current */
+ *value = raw * ADC_12V_CURRENT_SCALE;
+ break;
+ case 3: /* core voltage */
+ *value = raw * ADC_CPU_VOLTAGE_SCALE;
+ break;
+ case 4: /* core current */
+ *value = raw * ADC_CPU_CURRENT_SCALE;
+ break;
+ }
+}
+
+static int wf_ad7417_adc_get(struct wf_sensor *sr, s32 *value)
+{
+ struct wf_ad7417_priv *pv = sr->priv;
+ int chan = sr - pv->sensors;
+ int i, rc;
+ u8 buf[2];
+ u16 raw;
+
+ *value = 0;
+ mutex_lock(&pv->lock);
+ for (i = 0; i < 10; i++) {
+ /* Set channel */
+ buf[0] = 1;
+ buf[1] = (pv->config & 0x1f) | (chan << 5);
+ rc = i2c_master_send(pv->i2c, buf, 2);
+ if (rc < 0)
+ goto error;
+
+ /* Wait for conversion */
+ msleep(1);
+
+ /* Switch to data register */
+ buf[0] = 4;
+ rc = i2c_master_send(pv->i2c, buf, 1);
+ if (rc < 0)
+ goto error;
+
+ /* Read result */
+ rc = i2c_master_recv(pv->i2c, buf, 2);
+ if (rc < 0)
+ goto error;
+
+ /* Read a a 16-bit signed value */
+ raw = be16_to_cpup((__le16 *)buf) >> 6;
+ wf_ad7417_adc_convert(pv, chan, raw, value);
+
+ dev_vdbg(&pv->i2c->dev, "ADC chan %d [%s]"
+ " raw value: 0x%x, conv to: 0x%08x\n",
+ chan, sr->name, raw, *value);
+
+ mutex_unlock(&pv->lock);
+ return 0;
+
+ error:
+ dev_dbg(&pv->i2c->dev,
+ "Error reading ADC, try %d...\n", i);
+ if (i < 9)
+ msleep(10);
+ }
+ mutex_unlock(&pv->lock);
+ return -1;
+}
+
+static void wf_ad7417_release(struct kref *ref)
+{
+ struct wf_ad7417_priv *pv = container_of(ref,
+ struct wf_ad7417_priv, ref);
+ kfree(pv);
+}
+
+static void wf_ad7417_sensor_release(struct wf_sensor *sr)
+{
+ struct wf_ad7417_priv *pv = sr->priv;
+
+ kfree(sr->name);
+ kref_put(&pv->ref, wf_ad7417_release);
+}
+
+static const struct wf_sensor_ops wf_ad7417_temp_ops = {
+ .get_value = wf_ad7417_temp_get,
+ .release = wf_ad7417_sensor_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct wf_sensor_ops wf_ad7417_adc_ops = {
+ .get_value = wf_ad7417_adc_get,
+ .release = wf_ad7417_sensor_release,
+ .owner = THIS_MODULE,
+};
+
+static void wf_ad7417_add_sensor(struct wf_ad7417_priv *pv,
+ int index, const char *name,
+ const struct wf_sensor_ops *ops)
+{
+ pv->sensors[index].name = kasprintf(GFP_KERNEL, "%s-%d", name, pv->cpu);
+ pv->sensors[index].priv = pv;
+ pv->sensors[index].ops = ops;
+ if (!wf_register_sensor(&pv->sensors[index]))
+ kref_get(&pv->ref);
+}
+
+static void wf_ad7417_init_chip(struct wf_ad7417_priv *pv)
+{
+ int rc;
+ u8 buf[2];
+ u8 config = 0;
+
+ /*
+ * Read ADC the configuration register and cache it. We
+ * also make sure Config2 contains proper values, I've seen
+ * cases where we got stale grabage in there, thus preventing
+ * proper reading of conv. values
+ */
+
+ /* Clear Config2 */
+ buf[0] = 5;
+ buf[1] = 0;
+ i2c_master_send(pv->i2c, buf, 2);
+
+ /* Read & cache Config1 */
+ buf[0] = 1;
+ rc = i2c_master_send(pv->i2c, buf, 1);
+ if (rc > 0) {
+ rc = i2c_master_recv(pv->i2c, buf, 1);
+ if (rc > 0) {
+ config = buf[0];
+
+ dev_dbg(&pv->i2c->dev, "ADC config reg: %02x\n",
+ config);
+
+ /* Disable shutdown mode */
+ config &= 0xfe;
+ buf[0] = 1;
+ buf[1] = config;
+ rc = i2c_master_send(pv->i2c, buf, 2);
+ }
+ }
+ if (rc <= 0)
+ dev_err(&pv->i2c->dev, "Error reading ADC config\n");
+
+ pv->config = config;
+}
+
+static int wf_ad7417_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct wf_ad7417_priv *pv;
+ const struct mpu_data *mpu;
+ const char *loc;
+ int cpu_nr;
+
+ loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL);
+ if (!loc) {
+ dev_warn(&client->dev, "Missing hwsensor-location property!\n");
+ return -ENXIO;
+ }
+
+ /*
+ * Identify which CPU we belong to by looking at the first entry
+ * in the hwsensor-location list
+ */
+ if (!strncmp(loc, "CPU A", 5))
+ cpu_nr = 0;
+ else if (!strncmp(loc, "CPU B", 5))
+ cpu_nr = 1;
+ else {
+ pr_err("wf_ad7417: Can't identify location %s\n", loc);
+ return -ENXIO;
+ }
+ mpu = wf_get_mpu(cpu_nr);
+ if (!mpu) {
+ dev_err(&client->dev, "Failed to retrieve MPU data\n");
+ return -ENXIO;
+ }
+
+ pv = kzalloc(sizeof(struct wf_ad7417_priv), GFP_KERNEL);
+ if (pv == NULL)
+ return -ENODEV;
+
+ kref_init(&pv->ref);
+ mutex_init(&pv->lock);
+ pv->i2c = client;
+ pv->cpu = cpu_nr;
+ pv->mpu = mpu;
+ dev_set_drvdata(&client->dev, pv);
+
+ /* Initialize the chip */
+ wf_ad7417_init_chip(pv);
+
+ /*
+ * We cannot rely on Apple device-tree giving us child
+ * node with the names of the individual sensors so we
+ * just hard code what we know about them
+ */
+ wf_ad7417_add_sensor(pv, 0, "cpu-amb-temp", &wf_ad7417_temp_ops);
+ wf_ad7417_add_sensor(pv, 1, "cpu-diode-temp", &wf_ad7417_adc_ops);
+ wf_ad7417_add_sensor(pv, 2, "cpu-12v-current", &wf_ad7417_adc_ops);
+ wf_ad7417_add_sensor(pv, 3, "cpu-voltage", &wf_ad7417_adc_ops);
+ wf_ad7417_add_sensor(pv, 4, "cpu-current", &wf_ad7417_adc_ops);
+
+ return 0;
+}
+
+static int wf_ad7417_remove(struct i2c_client *client)
+{
+ struct wf_ad7417_priv *pv = dev_get_drvdata(&client->dev);
+ int i;
+
+ /* Mark client detached */
+ pv->i2c = NULL;
+
+ /* Release sensor */
+ for (i = 0; i < 5; i++)
+ wf_unregister_sensor(&pv->sensors[i]);
+
+ kref_put(&pv->ref, wf_ad7417_release);
+
+ return 0;
+}
+
+static const struct i2c_device_id wf_ad7417_id[] = {
+ { "MAC,ad7417", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wf_ad7417_id);
+
+static struct i2c_driver wf_ad7417_driver = {
+ .driver = {
+ .name = "wf_ad7417",
+ },
+ .probe = wf_ad7417_probe,
+ .remove = wf_ad7417_remove,
+ .id_table = wf_ad7417_id,
+};
+
+static int wf_ad7417_init(void)
+{
+ /* This is only supported on these machines */
+ if (!of_machine_is_compatible("PowerMac7,2") &&
+ !of_machine_is_compatible("PowerMac7,3") &&
+ !of_machine_is_compatible("RackMac3,1"))
+ return -ENODEV;
+
+ return i2c_add_driver(&wf_ad7417_driver);
+}
+
+static void wf_ad7417_exit(void)
+{
+ i2c_del_driver(&wf_ad7417_driver);
+}
+
+module_init(wf_ad7417_init);
+module_exit(wf_ad7417_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("ad7417 sensor driver for PowerMacs");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c
index ab3faa702d5..3ee198b6584 100644
--- a/drivers/macintosh/windfarm_core.c
+++ b/drivers/macintosh/windfarm_core.c
@@ -25,15 +25,16 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/init.h>
#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
#include <linux/kthread.h>
#include <linux/jiffies.h>
#include <linux/reboot.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
+#include <linux/freezer.h>
#include <asm/prom.h>
@@ -80,7 +81,8 @@ int wf_critical_overtemp(void)
"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
NULL };
- return call_usermodehelper(critical_overtemp_path, argv, envp, 0);
+ return call_usermodehelper(critical_overtemp_path,
+ argv, envp, UMH_WAIT_EXEC);
}
EXPORT_SYMBOL_GPL(wf_critical_overtemp);
@@ -92,7 +94,8 @@ static int wf_thread_func(void *data)
DBG("wf: thread started\n");
- while(!kthread_should_stop()) {
+ set_freezable();
+ while (!kthread_should_stop()) {
try_to_freeze();
if (time_after_eq(jiffies, next)) {
@@ -116,12 +119,6 @@ static int wf_thread_func(void *data)
delay = next - jiffies;
if (delay <= HZ)
schedule_timeout_interruptible(delay);
-
- /* there should be no signal, but oh well */
- if (signal_pending(current)) {
- printk(KERN_WARNING "windfarm: thread got sigl !\n");
- break;
- }
}
DBG("wf: thread stopped\n");
@@ -167,13 +164,27 @@ static ssize_t wf_show_control(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct wf_control *ctrl = container_of(attr, struct wf_control, attr);
+ const char *typestr;
s32 val = 0;
int err;
err = ctrl->ops->get_value(ctrl, &val);
- if (err < 0)
+ if (err < 0) {
+ if (err == -EFAULT)
+ return sprintf(buf, "<HW FAULT>\n");
return err;
- return sprintf(buf, "%d\n", val);
+ }
+ switch(ctrl->type) {
+ case WF_CONTROL_RPM_FAN:
+ typestr = " RPM";
+ break;
+ case WF_CONTROL_PWM_FAN:
+ typestr = " %";
+ break;
+ default:
+ typestr = "";
+ }
+ return sprintf(buf, "%d%s\n", val, typestr);
}
/* This is really only for debugging... */
@@ -213,12 +224,15 @@ int wf_register_control(struct wf_control *new_ct)
kref_init(&new_ct->ref);
list_add(&new_ct->link, &wf_controls);
+ sysfs_attr_init(&new_ct->attr.attr);
new_ct->attr.attr.name = new_ct->name;
- new_ct->attr.attr.owner = THIS_MODULE;
new_ct->attr.attr.mode = 0644;
new_ct->attr.show = wf_show_control;
new_ct->attr.store = wf_store_control;
- device_create_file(&wf_platform_device.dev, &new_ct->attr);
+ if (device_create_file(&wf_platform_device.dev, &new_ct->attr))
+ printk(KERN_WARNING "windfarm: device_create_file failed"
+ " for %s\n", new_ct->name);
+ /* the subsystem still does useful work without the file */
DBG("wf: Registered control %s\n", new_ct->name);
@@ -323,12 +337,15 @@ int wf_register_sensor(struct wf_sensor *new_sr)
kref_init(&new_sr->ref);
list_add(&new_sr->link, &wf_sensors);
+ sysfs_attr_init(&new_sr->attr.attr);
new_sr->attr.attr.name = new_sr->name;
- new_sr->attr.attr.owner = THIS_MODULE;
new_sr->attr.attr.mode = 0444;
new_sr->attr.show = wf_show_sensor;
new_sr->attr.store = NULL;
- device_create_file(&wf_platform_device.dev, &new_sr->attr);
+ if (device_create_file(&wf_platform_device.dev, &new_sr->attr))
+ printk(KERN_WARNING "windfarm: device_create_file failed"
+ " for %s\n", new_sr->name);
+ /* the subsystem still does useful work without the file */
DBG("wf: Registered sensor %s\n", new_sr->name);
@@ -467,11 +484,6 @@ static int __init windfarm_core_init(void)
{
DBG("wf: core loaded\n");
- /* Don't register on old machines that use therm_pm72 for now */
- if (machine_is_compatible("PowerMac7,2") ||
- machine_is_compatible("PowerMac7,3") ||
- machine_is_compatible("RackMac3,1"))
- return -ENODEV;
platform_device_register(&wf_platform_device);
return 0;
}
diff --git a/drivers/macintosh/windfarm_cpufreq_clamp.c b/drivers/macintosh/windfarm_cpufreq_clamp.c
index 900aade0619..72d1fdfe02a 100644
--- a/drivers/macintosh/windfarm_cpufreq_clamp.c
+++ b/drivers/macintosh/windfarm_cpufreq_clamp.c
@@ -75,12 +75,6 @@ static int __init wf_cpufreq_clamp_init(void)
{
struct wf_control *clamp;
- /* Don't register on old machines that use therm_pm72 for now */
- if (machine_is_compatible("PowerMac7,2") ||
- machine_is_compatible("PowerMac7,3") ||
- machine_is_compatible("RackMac3,1"))
- return -ENODEV;
-
clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL);
if (clamp == NULL)
return -ENOMEM;
diff --git a/drivers/macintosh/windfarm_fcu_controls.c b/drivers/macintosh/windfarm_fcu_controls.c
new file mode 100644
index 00000000000..0226b796a21
--- /dev/null
+++ b/drivers/macintosh/windfarm_fcu_controls.c
@@ -0,0 +1,600 @@
+/*
+ * Windfarm PowerMac thermal control. FCU fan control
+ *
+ * Copyright 2012 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Released under the term of the GNU GPL v2.
+ */
+#undef DEBUG
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+
+#include "windfarm.h"
+#include "windfarm_mpu.h"
+
+#define VERSION "1.0"
+
+#ifdef DEBUG
+#define DBG(args...) printk(args)
+#else
+#define DBG(args...) do { } while(0)
+#endif
+
+/*
+ * This option is "weird" :) Basically, if you define this to 1
+ * the control loop for the RPMs fans (not PWMs) will apply the
+ * correction factor obtained from the PID to the actual RPM
+ * speed read from the FCU.
+ *
+ * If you define the below constant to 0, then it will be
+ * applied to the setpoint RPM speed, that is basically the
+ * speed we proviously "asked" for.
+ *
+ * I'm using 0 for now which is what therm_pm72 used to do and
+ * what Darwin -apparently- does based on observed behaviour.
+ */
+#define RPM_PID_USE_ACTUAL_SPEED 0
+
+/* Default min/max for pumps */
+#define CPU_PUMP_OUTPUT_MAX 3200
+#define CPU_PUMP_OUTPUT_MIN 1250
+
+#define FCU_FAN_RPM 0
+#define FCU_FAN_PWM 1
+
+struct wf_fcu_priv {
+ struct kref ref;
+ struct i2c_client *i2c;
+ struct mutex lock;
+ struct list_head fan_list;
+ int rpm_shift;
+};
+
+struct wf_fcu_fan {
+ struct list_head link;
+ int id;
+ s32 min, max, target;
+ struct wf_fcu_priv *fcu_priv;
+ struct wf_control ctrl;
+};
+
+static void wf_fcu_release(struct kref *ref)
+{
+ struct wf_fcu_priv *pv = container_of(ref, struct wf_fcu_priv, ref);
+
+ kfree(pv);
+}
+
+static void wf_fcu_fan_release(struct wf_control *ct)
+{
+ struct wf_fcu_fan *fan = ct->priv;
+
+ kref_put(&fan->fcu_priv->ref, wf_fcu_release);
+ kfree(fan);
+}
+
+static int wf_fcu_read_reg(struct wf_fcu_priv *pv, int reg,
+ unsigned char *buf, int nb)
+{
+ int tries, nr, nw;
+
+ mutex_lock(&pv->lock);
+
+ buf[0] = reg;
+ tries = 0;
+ for (;;) {
+ nw = i2c_master_send(pv->i2c, buf, 1);
+ if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
+ break;
+ msleep(10);
+ ++tries;
+ }
+ if (nw <= 0) {
+ pr_err("Failure writing address to FCU: %d", nw);
+ nr = nw;
+ goto bail;
+ }
+ tries = 0;
+ for (;;) {
+ nr = i2c_master_recv(pv->i2c, buf, nb);
+ if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100)
+ break;
+ msleep(10);
+ ++tries;
+ }
+ if (nr <= 0)
+ pr_err("wf_fcu: Failure reading data from FCU: %d", nw);
+ bail:
+ mutex_unlock(&pv->lock);
+ return nr;
+}
+
+static int wf_fcu_write_reg(struct wf_fcu_priv *pv, int reg,
+ const unsigned char *ptr, int nb)
+{
+ int tries, nw;
+ unsigned char buf[16];
+
+ buf[0] = reg;
+ memcpy(buf+1, ptr, nb);
+ ++nb;
+ tries = 0;
+ for (;;) {
+ nw = i2c_master_send(pv->i2c, buf, nb);
+ if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
+ break;
+ msleep(10);
+ ++tries;
+ }
+ if (nw < 0)
+ pr_err("wf_fcu: Failure writing to FCU: %d", nw);
+ return nw;
+}
+
+static int wf_fcu_fan_set_rpm(struct wf_control *ct, s32 value)
+{
+ struct wf_fcu_fan *fan = ct->priv;
+ struct wf_fcu_priv *pv = fan->fcu_priv;
+ int rc, shift = pv->rpm_shift;
+ unsigned char buf[2];
+
+ if (value < fan->min)
+ value = fan->min;
+ if (value > fan->max)
+ value = fan->max;
+
+ fan->target = value;
+
+ buf[0] = value >> (8 - shift);
+ buf[1] = value << shift;
+ rc = wf_fcu_write_reg(pv, 0x10 + (fan->id * 2), buf, 2);
+ if (rc < 0)
+ return -EIO;
+ return 0;
+}
+
+static int wf_fcu_fan_get_rpm(struct wf_control *ct, s32 *value)
+{
+ struct wf_fcu_fan *fan = ct->priv;
+ struct wf_fcu_priv *pv = fan->fcu_priv;
+ int rc, reg_base, shift = pv->rpm_shift;
+ unsigned char failure;
+ unsigned char active;
+ unsigned char buf[2];
+
+ rc = wf_fcu_read_reg(pv, 0xb, &failure, 1);
+ if (rc != 1)
+ return -EIO;
+ if ((failure & (1 << fan->id)) != 0)
+ return -EFAULT;
+ rc = wf_fcu_read_reg(pv, 0xd, &active, 1);
+ if (rc != 1)
+ return -EIO;
+ if ((active & (1 << fan->id)) == 0)
+ return -ENXIO;
+
+ /* Programmed value or real current speed */
+#if RPM_PID_USE_ACTUAL_SPEED
+ reg_base = 0x11;
+#else
+ reg_base = 0x10;
+#endif
+ rc = wf_fcu_read_reg(pv, reg_base + (fan->id * 2), buf, 2);
+ if (rc != 2)
+ return -EIO;
+
+ *value = (buf[0] << (8 - shift)) | buf[1] >> shift;
+
+ return 0;
+}
+
+static int wf_fcu_fan_set_pwm(struct wf_control *ct, s32 value)
+{
+ struct wf_fcu_fan *fan = ct->priv;
+ struct wf_fcu_priv *pv = fan->fcu_priv;
+ unsigned char buf[2];
+ int rc;
+
+ if (value < fan->min)
+ value = fan->min;
+ if (value > fan->max)
+ value = fan->max;
+
+ fan->target = value;
+
+ value = (value * 2559) / 1000;
+ buf[0] = value;
+ rc = wf_fcu_write_reg(pv, 0x30 + (fan->id * 2), buf, 1);
+ if (rc < 0)
+ return -EIO;
+ return 0;
+}
+
+static int wf_fcu_fan_get_pwm(struct wf_control *ct, s32 *value)
+{
+ struct wf_fcu_fan *fan = ct->priv;
+ struct wf_fcu_priv *pv = fan->fcu_priv;
+ unsigned char failure;
+ unsigned char active;
+ unsigned char buf[2];
+ int rc;
+
+ rc = wf_fcu_read_reg(pv, 0x2b, &failure, 1);
+ if (rc != 1)
+ return -EIO;
+ if ((failure & (1 << fan->id)) != 0)
+ return -EFAULT;
+ rc = wf_fcu_read_reg(pv, 0x2d, &active, 1);
+ if (rc != 1)
+ return -EIO;
+ if ((active & (1 << fan->id)) == 0)
+ return -ENXIO;
+
+ rc = wf_fcu_read_reg(pv, 0x30 + (fan->id * 2), buf, 1);
+ if (rc != 1)
+ return -EIO;
+
+ *value = (((s32)buf[0]) * 1000) / 2559;
+
+ return 0;
+}
+
+static s32 wf_fcu_fan_min(struct wf_control *ct)
+{
+ struct wf_fcu_fan *fan = ct->priv;
+
+ return fan->min;
+}
+
+static s32 wf_fcu_fan_max(struct wf_control *ct)
+{
+ struct wf_fcu_fan *fan = ct->priv;
+
+ return fan->max;
+}
+
+static const struct wf_control_ops wf_fcu_fan_rpm_ops = {
+ .set_value = wf_fcu_fan_set_rpm,
+ .get_value = wf_fcu_fan_get_rpm,
+ .get_min = wf_fcu_fan_min,
+ .get_max = wf_fcu_fan_max,
+ .release = wf_fcu_fan_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct wf_control_ops wf_fcu_fan_pwm_ops = {
+ .set_value = wf_fcu_fan_set_pwm,
+ .get_value = wf_fcu_fan_get_pwm,
+ .get_min = wf_fcu_fan_min,
+ .get_max = wf_fcu_fan_max,
+ .release = wf_fcu_fan_release,
+ .owner = THIS_MODULE,
+};
+
+static void wf_fcu_get_pump_minmax(struct wf_fcu_fan *fan)
+{
+ const struct mpu_data *mpu = wf_get_mpu(0);
+ u16 pump_min = 0, pump_max = 0xffff;
+ u16 tmp[4];
+
+ /* Try to fetch pumps min/max infos from eeprom */
+ if (mpu) {
+ memcpy(&tmp, mpu->processor_part_num, 8);
+ if (tmp[0] != 0xffff && tmp[1] != 0xffff) {
+ pump_min = max(pump_min, tmp[0]);
+ pump_max = min(pump_max, tmp[1]);
+ }
+ if (tmp[2] != 0xffff && tmp[3] != 0xffff) {
+ pump_min = max(pump_min, tmp[2]);
+ pump_max = min(pump_max, tmp[3]);
+ }
+ }
+
+ /* Double check the values, this _IS_ needed as the EEPROM on
+ * some dual 2.5Ghz G5s seem, at least, to have both min & max
+ * same to the same value ... (grrrr)
+ */
+ if (pump_min == pump_max || pump_min == 0 || pump_max == 0xffff) {
+ pump_min = CPU_PUMP_OUTPUT_MIN;
+ pump_max = CPU_PUMP_OUTPUT_MAX;
+ }
+
+ fan->min = pump_min;
+ fan->max = pump_max;
+
+ DBG("wf_fcu: pump min/max for %s set to: [%d..%d] RPM\n",
+ fan->ctrl.name, pump_min, pump_max);
+}
+
+static void wf_fcu_get_rpmfan_minmax(struct wf_fcu_fan *fan)
+{
+ struct wf_fcu_priv *pv = fan->fcu_priv;
+ const struct mpu_data *mpu0 = wf_get_mpu(0);
+ const struct mpu_data *mpu1 = wf_get_mpu(1);
+
+ /* Default */
+ fan->min = 2400 >> pv->rpm_shift;
+ fan->max = 56000 >> pv->rpm_shift;
+
+ /* CPU fans have min/max in MPU */
+ if (mpu0 && !strcmp(fan->ctrl.name, "cpu-front-fan-0")) {
+ fan->min = max(fan->min, (s32)mpu0->rminn_intake_fan);
+ fan->max = min(fan->max, (s32)mpu0->rmaxn_intake_fan);
+ goto bail;
+ }
+ if (mpu1 && !strcmp(fan->ctrl.name, "cpu-front-fan-1")) {
+ fan->min = max(fan->min, (s32)mpu1->rminn_intake_fan);
+ fan->max = min(fan->max, (s32)mpu1->rmaxn_intake_fan);
+ goto bail;
+ }
+ if (mpu0 && !strcmp(fan->ctrl.name, "cpu-rear-fan-0")) {
+ fan->min = max(fan->min, (s32)mpu0->rminn_exhaust_fan);
+ fan->max = min(fan->max, (s32)mpu0->rmaxn_exhaust_fan);
+ goto bail;
+ }
+ if (mpu1 && !strcmp(fan->ctrl.name, "cpu-rear-fan-1")) {
+ fan->min = max(fan->min, (s32)mpu1->rminn_exhaust_fan);
+ fan->max = min(fan->max, (s32)mpu1->rmaxn_exhaust_fan);
+ goto bail;
+ }
+ /* Rackmac variants, we just use mpu0 intake */
+ if (!strncmp(fan->ctrl.name, "cpu-fan", 7)) {
+ fan->min = max(fan->min, (s32)mpu0->rminn_intake_fan);
+ fan->max = min(fan->max, (s32)mpu0->rmaxn_intake_fan);
+ goto bail;
+ }
+ bail:
+ DBG("wf_fcu: fan min/max for %s set to: [%d..%d] RPM\n",
+ fan->ctrl.name, fan->min, fan->max);
+}
+
+static void wf_fcu_add_fan(struct wf_fcu_priv *pv, const char *name,
+ int type, int id)
+{
+ struct wf_fcu_fan *fan;
+
+ fan = kzalloc(sizeof(*fan), GFP_KERNEL);
+ if (!fan)
+ return;
+ fan->fcu_priv = pv;
+ fan->id = id;
+ fan->ctrl.name = name;
+ fan->ctrl.priv = fan;
+
+ /* min/max is oddball but the code comes from
+ * therm_pm72 which seems to work so ...
+ */
+ if (type == FCU_FAN_RPM) {
+ if (!strncmp(name, "cpu-pump", strlen("cpu-pump")))
+ wf_fcu_get_pump_minmax(fan);
+ else
+ wf_fcu_get_rpmfan_minmax(fan);
+ fan->ctrl.type = WF_CONTROL_RPM_FAN;
+ fan->ctrl.ops = &wf_fcu_fan_rpm_ops;
+ } else {
+ fan->min = 10;
+ fan->max = 100;
+ fan->ctrl.type = WF_CONTROL_PWM_FAN;
+ fan->ctrl.ops = &wf_fcu_fan_pwm_ops;
+ }
+
+ if (wf_register_control(&fan->ctrl)) {
+ pr_err("wf_fcu: Failed to register fan %s\n", name);
+ kfree(fan);
+ return;
+ }
+ list_add(&fan->link, &pv->fan_list);
+ kref_get(&pv->ref);
+}
+
+static void wf_fcu_lookup_fans(struct wf_fcu_priv *pv)
+{
+ /* Translation of device-tree location properties to
+ * windfarm fan names
+ */
+ static const struct {
+ const char *dt_name; /* Device-tree name */
+ const char *ct_name; /* Control name */
+ } loc_trans[] = {
+ { "BACKSIDE", "backside-fan", },
+ { "SYS CTRLR FAN", "backside-fan", },
+ { "DRIVE BAY", "drive-bay-fan", },
+ { "SLOT", "slots-fan", },
+ { "PCI FAN", "slots-fan", },
+ { "CPU A INTAKE", "cpu-front-fan-0", },
+ { "CPU A EXHAUST", "cpu-rear-fan-0", },
+ { "CPU B INTAKE", "cpu-front-fan-1", },
+ { "CPU B EXHAUST", "cpu-rear-fan-1", },
+ { "CPU A PUMP", "cpu-pump-0", },
+ { "CPU B PUMP", "cpu-pump-1", },
+ { "CPU A 1", "cpu-fan-a-0", },
+ { "CPU A 2", "cpu-fan-b-0", },
+ { "CPU A 3", "cpu-fan-c-0", },
+ { "CPU B 1", "cpu-fan-a-1", },
+ { "CPU B 2", "cpu-fan-b-1", },
+ { "CPU B 3", "cpu-fan-c-1", },
+ };
+ struct device_node *np = NULL, *fcu = pv->i2c->dev.of_node;
+ int i;
+
+ DBG("Looking up FCU controls in device-tree...\n");
+
+ while ((np = of_get_next_child(fcu, np)) != NULL) {
+ int id, type = -1;
+ const char *loc;
+ const char *name;
+ const u32 *reg;
+
+ DBG(" control: %s, type: %s\n", np->name, np->type);
+
+ /* Detect control type */
+ if (!strcmp(np->type, "fan-rpm-control") ||
+ !strcmp(np->type, "fan-rpm"))
+ type = FCU_FAN_RPM;
+ if (!strcmp(np->type, "fan-pwm-control") ||
+ !strcmp(np->type, "fan-pwm"))
+ type = FCU_FAN_PWM;
+ /* Only care about fans for now */
+ if (type == -1)
+ continue;
+
+ /* Lookup for a matching location */
+ loc = of_get_property(np, "location", NULL);
+ reg = of_get_property(np, "reg", NULL);
+ if (loc == NULL || reg == NULL)
+ continue;
+ DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg);
+
+ for (i = 0; i < ARRAY_SIZE(loc_trans); i++) {
+ if (strncmp(loc, loc_trans[i].dt_name,
+ strlen(loc_trans[i].dt_name)))
+ continue;
+ name = loc_trans[i].ct_name;
+
+ DBG(" location match, name: %s\n", name);
+
+ if (type == FCU_FAN_RPM)
+ id = ((*reg) - 0x10) / 2;
+ else
+ id = ((*reg) - 0x30) / 2;
+ if (id > 7) {
+ pr_warning("wf_fcu: Can't parse "
+ "fan ID in device-tree for %s\n",
+ np->full_name);
+ break;
+ }
+ wf_fcu_add_fan(pv, name, type, id);
+ break;
+ }
+ }
+}
+
+static void wf_fcu_default_fans(struct wf_fcu_priv *pv)
+{
+ /* We only support the default fans for PowerMac7,2 */
+ if (!of_machine_is_compatible("PowerMac7,2"))
+ return;
+
+ wf_fcu_add_fan(pv, "backside-fan", FCU_FAN_PWM, 1);
+ wf_fcu_add_fan(pv, "drive-bay-fan", FCU_FAN_RPM, 2);
+ wf_fcu_add_fan(pv, "slots-fan", FCU_FAN_PWM, 2);
+ wf_fcu_add_fan(pv, "cpu-front-fan-0", FCU_FAN_RPM, 3);
+ wf_fcu_add_fan(pv, "cpu-rear-fan-0", FCU_FAN_RPM, 4);
+ wf_fcu_add_fan(pv, "cpu-front-fan-1", FCU_FAN_RPM, 5);
+ wf_fcu_add_fan(pv, "cpu-rear-fan-1", FCU_FAN_RPM, 6);
+}
+
+static int wf_fcu_init_chip(struct wf_fcu_priv *pv)
+{
+ unsigned char buf = 0xff;
+ int rc;
+
+ rc = wf_fcu_write_reg(pv, 0xe, &buf, 1);
+ if (rc < 0)
+ return -EIO;
+ rc = wf_fcu_write_reg(pv, 0x2e, &buf, 1);
+ if (rc < 0)
+ return -EIO;
+ rc = wf_fcu_read_reg(pv, 0, &buf, 1);
+ if (rc < 0)
+ return -EIO;
+ pv->rpm_shift = (buf == 1) ? 2 : 3;
+
+ pr_debug("wf_fcu: FCU Initialized, RPM fan shift is %d\n",
+ pv->rpm_shift);
+
+ return 0;
+}
+
+static int wf_fcu_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct wf_fcu_priv *pv;
+
+ pv = kzalloc(sizeof(*pv), GFP_KERNEL);
+ if (!pv)
+ return -ENOMEM;
+
+ kref_init(&pv->ref);
+ mutex_init(&pv->lock);
+ INIT_LIST_HEAD(&pv->fan_list);
+ pv->i2c = client;
+
+ /*
+ * First we must start the FCU which will query the
+ * shift value to apply to RPMs
+ */
+ if (wf_fcu_init_chip(pv)) {
+ pr_err("wf_fcu: Initialization failed !\n");
+ kfree(pv);
+ return -ENXIO;
+ }
+
+ /* First lookup fans in the device-tree */
+ wf_fcu_lookup_fans(pv);
+
+ /*
+ * Older machines don't have the device-tree entries
+ * we are looking for, just hard code the list
+ */
+ if (list_empty(&pv->fan_list))
+ wf_fcu_default_fans(pv);
+
+ /* Still no fans ? FAIL */
+ if (list_empty(&pv->fan_list)) {
+ pr_err("wf_fcu: Failed to find fans for your machine\n");
+ kfree(pv);
+ return -ENODEV;
+ }
+
+ dev_set_drvdata(&client->dev, pv);
+
+ return 0;
+}
+
+static int wf_fcu_remove(struct i2c_client *client)
+{
+ struct wf_fcu_priv *pv = dev_get_drvdata(&client->dev);
+ struct wf_fcu_fan *fan;
+
+ while (!list_empty(&pv->fan_list)) {
+ fan = list_first_entry(&pv->fan_list, struct wf_fcu_fan, link);
+ list_del(&fan->link);
+ wf_unregister_control(&fan->ctrl);
+ }
+ kref_put(&pv->ref, wf_fcu_release);
+ return 0;
+}
+
+static const struct i2c_device_id wf_fcu_id[] = {
+ { "MAC,fcu", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wf_fcu_id);
+
+static struct i2c_driver wf_fcu_driver = {
+ .driver = {
+ .name = "wf_fcu",
+ },
+ .probe = wf_fcu_probe,
+ .remove = wf_fcu_remove,
+ .id_table = wf_fcu_id,
+};
+
+module_i2c_driver(wf_fcu_driver);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("FCU control objects for PowerMacs thermal control");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c
index 3f7967feaf5..590214ba736 100644
--- a/drivers/macintosh/windfarm_lm75_sensor.c
+++ b/drivers/macintosh/windfarm_lm75_sensor.c
@@ -18,13 +18,12 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/pmac_low_i2c.h>
#include "windfarm.h"
-#define VERSION "0.2"
+#define VERSION "1.0"
#undef DEBUG
@@ -37,34 +36,22 @@
struct wf_lm75_sensor {
int ds1775 : 1;
int inited : 1;
- struct i2c_client i2c;
- struct wf_sensor sens;
+ struct i2c_client *i2c;
+ struct wf_sensor sens;
};
#define wf_to_lm75(c) container_of(c, struct wf_lm75_sensor, sens)
-#define i2c_to_lm75(c) container_of(c, struct wf_lm75_sensor, i2c)
-
-static int wf_lm75_attach(struct i2c_adapter *adapter);
-static int wf_lm75_detach(struct i2c_client *client);
-
-static struct i2c_driver wf_lm75_driver = {
- .driver = {
- .name = "wf_lm75",
- },
- .attach_adapter = wf_lm75_attach,
- .detach_client = wf_lm75_detach,
-};
static int wf_lm75_get(struct wf_sensor *sr, s32 *value)
{
struct wf_lm75_sensor *lm = wf_to_lm75(sr);
s32 data;
- if (lm->i2c.adapter == NULL)
+ if (lm->i2c == NULL)
return -ENODEV;
/* Init chip if necessary */
if (!lm->inited) {
- u8 cfg_new, cfg = (u8)i2c_smbus_read_byte_data(&lm->i2c, 1);
+ u8 cfg_new, cfg = (u8)i2c_smbus_read_byte_data(lm->i2c, 1);
DBG("wf_lm75: Initializing %s, cfg was: %02x\n",
sr->name, cfg);
@@ -73,7 +60,7 @@ static int wf_lm75_get(struct wf_sensor *sr, s32 *value)
* the firmware for now
*/
cfg_new = cfg & ~0x01;
- i2c_smbus_write_byte_data(&lm->i2c, 1, cfg_new);
+ i2c_smbus_write_byte_data(lm->i2c, 1, cfg_new);
lm->inited = 1;
/* If we just powered it up, let's wait 200 ms */
@@ -81,7 +68,7 @@ static int wf_lm75_get(struct wf_sensor *sr, s32 *value)
}
/* Read temperature register */
- data = (s32)le16_to_cpu(i2c_smbus_read_word_data(&lm->i2c, 0));
+ data = (s32)le16_to_cpu(i2c_smbus_read_word_data(lm->i2c, 0));
data <<= 8;
*value = data;
@@ -92,12 +79,6 @@ static void wf_lm75_release(struct wf_sensor *sr)
{
struct wf_lm75_sensor *lm = wf_to_lm75(sr);
- /* check if client is registered and detach from i2c */
- if (lm->i2c.adapter) {
- i2c_detach_client(&lm->i2c);
- lm->i2c.adapter = NULL;
- }
-
kfree(lm);
}
@@ -107,104 +88,69 @@ static struct wf_sensor_ops wf_lm75_ops = {
.owner = THIS_MODULE,
};
-static struct wf_lm75_sensor *wf_lm75_create(struct i2c_adapter *adapter,
- u8 addr, int ds1775,
- const char *loc)
-{
+static int wf_lm75_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
struct wf_lm75_sensor *lm;
- int rc;
+ int rc, ds1775 = id->driver_data;
+ const char *name, *loc;
DBG("wf_lm75: creating %s device at address 0x%02x\n",
- ds1775 ? "ds1775" : "lm75", addr);
+ ds1775 ? "ds1775" : "lm75", client->addr);
- lm = kmalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL);
- if (lm == NULL)
- return NULL;
- memset(lm, 0, sizeof(struct wf_lm75_sensor));
+ loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL);
+ if (!loc) {
+ dev_warn(&client->dev, "Missing hwsensor-location property!\n");
+ return -ENXIO;
+ }
/* Usual rant about sensor names not beeing very consistent in
* the device-tree, oh well ...
* Add more entries below as you deal with more setups
*/
if (!strcmp(loc, "Hard drive") || !strcmp(loc, "DRIVE BAY"))
- lm->sens.name = "hd-temp";
+ name = "hd-temp";
+ else if (!strcmp(loc, "Incoming Air Temp"))
+ name = "incoming-air-temp";
+ else if (!strcmp(loc, "ODD Temp"))
+ name = "optical-drive-temp";
+ else if (!strcmp(loc, "HD Temp"))
+ name = "hard-drive-temp";
+ else if (!strcmp(loc, "PCI SLOTS"))
+ name = "slots-temp";
+ else if (!strcmp(loc, "CPU A INLET"))
+ name = "cpu-inlet-temp-0";
+ else if (!strcmp(loc, "CPU B INLET"))
+ name = "cpu-inlet-temp-1";
else
- goto fail;
+ return -ENXIO;
+
- lm->inited = 0;
- lm->sens.ops = &wf_lm75_ops;
- lm->ds1775 = ds1775;
- lm->i2c.addr = (addr >> 1) & 0x7f;
- lm->i2c.adapter = adapter;
- lm->i2c.driver = &wf_lm75_driver;
- strncpy(lm->i2c.name, lm->sens.name, I2C_NAME_SIZE-1);
-
- rc = i2c_attach_client(&lm->i2c);
- if (rc) {
- printk(KERN_ERR "windfarm: failed to attach %s %s to i2c,"
- " err %d\n", ds1775 ? "ds1775" : "lm75",
- lm->i2c.name, rc);
- goto fail;
- }
-
- if (wf_register_sensor(&lm->sens)) {
- i2c_detach_client(&lm->i2c);
- goto fail;
- }
-
- return lm;
- fail:
- kfree(lm);
- return NULL;
-}
-
-static int wf_lm75_attach(struct i2c_adapter *adapter)
-{
- struct device_node *busnode, *dev;
- struct pmac_i2c_bus *bus;
-
- DBG("wf_lm75: adapter %s detected\n", adapter->name);
-
- bus = pmac_i2c_adapter_to_bus(adapter);
- if (bus == NULL)
+ lm = kzalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL);
+ if (lm == NULL)
return -ENODEV;
- busnode = pmac_i2c_get_bus_node(bus);
- DBG("wf_lm75: bus found, looking for device...\n");
-
- /* Now look for lm75(s) in there */
- for (dev = NULL;
- (dev = of_get_next_child(busnode, dev)) != NULL;) {
- const char *loc =
- get_property(dev, "hwsensor-location", NULL);
- u8 addr;
+ lm->inited = 0;
+ lm->ds1775 = ds1775;
+ lm->i2c = client;
+ lm->sens.name = name;
+ lm->sens.ops = &wf_lm75_ops;
+ i2c_set_clientdata(client, lm);
- /* We must re-match the adapter in order to properly check
- * the channel on multibus setups
- */
- if (!pmac_i2c_match_adapter(dev, adapter))
- continue;
- addr = pmac_i2c_get_dev_addr(dev);
- if (loc == NULL || addr == 0)
- continue;
- /* real lm75 */
- if (device_is_compatible(dev, "lm75"))
- wf_lm75_create(adapter, addr, 0, loc);
- /* ds1775 (compatible, better resolution */
- else if (device_is_compatible(dev, "ds1775"))
- wf_lm75_create(adapter, addr, 1, loc);
- }
- return 0;
+ rc = wf_register_sensor(&lm->sens);
+ if (rc)
+ kfree(lm);
+ return rc;
}
-static int wf_lm75_detach(struct i2c_client *client)
+static int wf_lm75_remove(struct i2c_client *client)
{
- struct wf_lm75_sensor *lm = i2c_to_lm75(client);
+ struct wf_lm75_sensor *lm = i2c_get_clientdata(client);
DBG("wf_lm75: i2c detatch called for %s\n", lm->sens.name);
/* Mark client detached */
- lm->i2c.adapter = NULL;
+ lm->i2c = NULL;
/* release sensor */
wf_unregister_sensor(&lm->sens);
@@ -212,24 +158,23 @@ static int wf_lm75_detach(struct i2c_client *client)
return 0;
}
-static int __init wf_lm75_sensor_init(void)
-{
- /* Don't register on old machines that use therm_pm72 for now */
- if (machine_is_compatible("PowerMac7,2") ||
- machine_is_compatible("PowerMac7,3") ||
- machine_is_compatible("RackMac3,1"))
- return -ENODEV;
- return i2c_add_driver(&wf_lm75_driver);
-}
-
-static void __exit wf_lm75_sensor_exit(void)
-{
- i2c_del_driver(&wf_lm75_driver);
-}
+static const struct i2c_device_id wf_lm75_id[] = {
+ { "MAC,lm75", 0 },
+ { "MAC,ds1775", 1 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wf_lm75_id);
+static struct i2c_driver wf_lm75_driver = {
+ .driver = {
+ .name = "wf_lm75",
+ },
+ .probe = wf_lm75_probe,
+ .remove = wf_lm75_remove,
+ .id_table = wf_lm75_id,
+};
-module_init(wf_lm75_sensor_init);
-module_exit(wf_lm75_sensor_exit);
+module_i2c_driver(wf_lm75_driver);
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("LM75 sensor objects for PowerMacs thermal control");
diff --git a/drivers/macintosh/windfarm_lm87_sensor.c b/drivers/macintosh/windfarm_lm87_sensor.c
new file mode 100644
index 00000000000..c071aab79dd
--- /dev/null
+++ b/drivers/macintosh/windfarm_lm87_sensor.c
@@ -0,0 +1,201 @@
+/*
+ * Windfarm PowerMac thermal control. LM87 sensor
+ *
+ * Copyright 2012 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Released under the term of the GNU GPL v2.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+#include <asm/pmac_low_i2c.h>
+
+#include "windfarm.h"
+
+#define VERSION "1.0"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...) printk(args)
+#else
+#define DBG(args...) do { } while(0)
+#endif
+
+struct wf_lm87_sensor {
+ struct i2c_client *i2c;
+ struct wf_sensor sens;
+};
+#define wf_to_lm87(c) container_of(c, struct wf_lm87_sensor, sens)
+
+
+static int wf_lm87_read_reg(struct i2c_client *chip, int reg)
+{
+ int rc, tries = 0;
+ u8 buf;
+
+ for (;;) {
+ /* Set address */
+ buf = (u8)reg;
+ rc = i2c_master_send(chip, &buf, 1);
+ if (rc <= 0)
+ goto error;
+ rc = i2c_master_recv(chip, &buf, 1);
+ if (rc <= 0)
+ goto error;
+ return (int)buf;
+ error:
+ DBG("wf_lm87: Error reading LM87, retrying...\n");
+ if (++tries > 10) {
+ printk(KERN_ERR "wf_lm87: Error reading LM87 !\n");
+ return -EIO;
+ }
+ msleep(10);
+ }
+}
+
+static int wf_lm87_get(struct wf_sensor *sr, s32 *value)
+{
+ struct wf_lm87_sensor *lm = sr->priv;
+ s32 temp;
+
+ if (lm->i2c == NULL)
+ return -ENODEV;
+
+#define LM87_INT_TEMP 0x27
+
+ /* Read temperature register */
+ temp = wf_lm87_read_reg(lm->i2c, LM87_INT_TEMP);
+ if (temp < 0)
+ return temp;
+ *value = temp << 16;
+
+ return 0;
+}
+
+static void wf_lm87_release(struct wf_sensor *sr)
+{
+ struct wf_lm87_sensor *lm = wf_to_lm87(sr);
+
+ kfree(lm);
+}
+
+static struct wf_sensor_ops wf_lm87_ops = {
+ .get_value = wf_lm87_get,
+ .release = wf_lm87_release,
+ .owner = THIS_MODULE,
+};
+
+static int wf_lm87_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct wf_lm87_sensor *lm;
+ const char *name = NULL, *loc;
+ struct device_node *np = NULL;
+ int rc;
+
+ /*
+ * The lm87 contains a whole pile of sensors, additionally,
+ * the Xserve G5 has several lm87's. However, for now we only
+ * care about the internal temperature sensor
+ */
+ while ((np = of_get_next_child(client->dev.of_node, np)) != NULL) {
+ if (strcmp(np->name, "int-temp"))
+ continue;
+ loc = of_get_property(np, "location", NULL);
+ if (!loc)
+ continue;
+ if (strstr(loc, "DIMM"))
+ name = "dimms-temp";
+ else if (strstr(loc, "Processors"))
+ name = "between-cpus-temp";
+ if (name) {
+ of_node_put(np);
+ break;
+ }
+ }
+ if (!name) {
+ pr_warning("wf_lm87: Unsupported sensor %s\n",
+ client->dev.of_node->full_name);
+ return -ENODEV;
+ }
+
+ lm = kzalloc(sizeof(struct wf_lm87_sensor), GFP_KERNEL);
+ if (lm == NULL)
+ return -ENODEV;
+
+ lm->i2c = client;
+ lm->sens.name = name;
+ lm->sens.ops = &wf_lm87_ops;
+ lm->sens.priv = lm;
+ i2c_set_clientdata(client, lm);
+
+ rc = wf_register_sensor(&lm->sens);
+ if (rc)
+ kfree(lm);
+ return rc;
+}
+
+static int wf_lm87_remove(struct i2c_client *client)
+{
+ struct wf_lm87_sensor *lm = i2c_get_clientdata(client);
+
+ DBG("wf_lm87: i2c detatch called for %s\n", lm->sens.name);
+
+ /* Mark client detached */
+ lm->i2c = NULL;
+
+ /* release sensor */
+ wf_unregister_sensor(&lm->sens);
+
+ return 0;
+}
+
+static const struct i2c_device_id wf_lm87_id[] = {
+ { "MAC,lm87cimt", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wf_lm87_id);
+
+static struct i2c_driver wf_lm87_driver = {
+ .driver = {
+ .name = "wf_lm87",
+ },
+ .probe = wf_lm87_probe,
+ .remove = wf_lm87_remove,
+ .id_table = wf_lm87_id,
+};
+
+static int __init wf_lm87_sensor_init(void)
+{
+ /* We only support this on the Xserve */
+ if (!of_machine_is_compatible("RackMac3,1"))
+ return -ENODEV;
+
+ return i2c_add_driver(&wf_lm87_driver);
+}
+
+static void __exit wf_lm87_sensor_exit(void)
+{
+ i2c_del_driver(&wf_lm87_driver);
+}
+
+
+module_init(wf_lm87_sensor_init);
+module_exit(wf_lm87_sensor_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("LM87 sensor objects for PowerMacs thermal control");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c
index eae1189d6c4..87e439b1031 100644
--- a/drivers/macintosh/windfarm_max6690_sensor.c
+++ b/drivers/macintosh/windfarm_max6690_sensor.c
@@ -16,7 +16,7 @@
#include "windfarm.h"
-#define VERSION "0.2"
+#define VERSION "1.0"
/* This currently only exports the external temperature sensor,
since that's all the control loops need. */
@@ -26,34 +26,22 @@
#define MAX6690_EXTERNAL_TEMP 1
struct wf_6690_sensor {
- struct i2c_client i2c;
+ struct i2c_client *i2c;
struct wf_sensor sens;
};
#define wf_to_6690(x) container_of((x), struct wf_6690_sensor, sens)
-#define i2c_to_6690(x) container_of((x), struct wf_6690_sensor, i2c)
-
-static int wf_max6690_attach(struct i2c_adapter *adapter);
-static int wf_max6690_detach(struct i2c_client *client);
-
-static struct i2c_driver wf_max6690_driver = {
- .driver = {
- .name = "wf_max6690",
- },
- .attach_adapter = wf_max6690_attach,
- .detach_client = wf_max6690_detach,
-};
static int wf_max6690_get(struct wf_sensor *sr, s32 *value)
{
struct wf_6690_sensor *max = wf_to_6690(sr);
s32 data;
- if (max->i2c.adapter == NULL)
+ if (max->i2c == NULL)
return -ENODEV;
/* chip gets initialized by firmware */
- data = i2c_smbus_read_byte_data(&max->i2c, MAX6690_EXTERNAL_TEMP);
+ data = i2c_smbus_read_byte_data(max->i2c, MAX6690_EXTERNAL_TEMP);
if (data < 0)
return data;
*value = data << 16;
@@ -64,10 +52,6 @@ static void wf_max6690_release(struct wf_sensor *sr)
{
struct wf_6690_sensor *max = wf_to_6690(sr);
- if (max->i2c.adapter) {
- i2c_detach_client(&max->i2c);
- max->i2c.adapter = NULL;
- }
kfree(max);
}
@@ -77,102 +61,76 @@ static struct wf_sensor_ops wf_max6690_ops = {
.owner = THIS_MODULE,
};
-static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr)
+static int wf_max6690_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
+ const char *name, *loc;
struct wf_6690_sensor *max;
- char *name = "backside-temp";
+ int rc;
- max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL);
- if (max == NULL) {
- printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor %s: "
- "no memory\n", name);
- return;
+ loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL);
+ if (!loc) {
+ dev_warn(&client->dev, "Missing hwsensor-location property!\n");
+ return -ENXIO;
}
- max->sens.ops = &wf_max6690_ops;
- max->sens.name = name;
- max->i2c.addr = addr >> 1;
- max->i2c.adapter = adapter;
- max->i2c.driver = &wf_max6690_driver;
- strncpy(max->i2c.name, name, I2C_NAME_SIZE-1);
-
- if (i2c_attach_client(&max->i2c)) {
- printk(KERN_ERR "windfarm: failed to attach MAX6690 sensor\n");
- goto fail;
- }
+ /*
+ * We only expose the external temperature register for
+ * now as this is all we need for our control loops
+ */
+ if (!strcmp(loc, "BACKSIDE") || !strcmp(loc, "SYS CTRLR AMBIENT"))
+ name = "backside-temp";
+ else if (!strcmp(loc, "NB Ambient"))
+ name = "north-bridge-temp";
+ else if (!strcmp(loc, "GPU Ambient"))
+ name = "gpu-temp";
+ else
+ return -ENXIO;
- if (wf_register_sensor(&max->sens)) {
- i2c_detach_client(&max->i2c);
- goto fail;
+ max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL);
+ if (max == NULL) {
+ printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor: "
+ "no memory\n");
+ return -ENOMEM;
}
- return;
-
- fail:
- kfree(max);
-}
-
-static int wf_max6690_attach(struct i2c_adapter *adapter)
-{
- struct device_node *busnode, *dev = NULL;
- struct pmac_i2c_bus *bus;
- const char *loc;
-
- bus = pmac_i2c_adapter_to_bus(adapter);
- if (bus == NULL)
- return -ENODEV;
- busnode = pmac_i2c_get_bus_node(bus);
-
- while ((dev = of_get_next_child(busnode, dev)) != NULL) {
- u8 addr;
-
- /* We must re-match the adapter in order to properly check
- * the channel on multibus setups
- */
- if (!pmac_i2c_match_adapter(dev, adapter))
- continue;
- if (!device_is_compatible(dev, "max6690"))
- continue;
- addr = pmac_i2c_get_dev_addr(dev);
- loc = get_property(dev, "hwsensor-location", NULL);
- if (loc == NULL || addr == 0)
- continue;
- printk("found max6690, loc=%s addr=0x%02x\n", loc, addr);
- if (strcmp(loc, "BACKSIDE"))
- continue;
- wf_max6690_create(adapter, addr);
- }
+ max->i2c = client;
+ max->sens.name = name;
+ max->sens.ops = &wf_max6690_ops;
+ i2c_set_clientdata(client, max);
- return 0;
+ rc = wf_register_sensor(&max->sens);
+ if (rc)
+ kfree(max);
+ return rc;
}
-static int wf_max6690_detach(struct i2c_client *client)
+static int wf_max6690_remove(struct i2c_client *client)
{
- struct wf_6690_sensor *max = i2c_to_6690(client);
+ struct wf_6690_sensor *max = i2c_get_clientdata(client);
- max->i2c.adapter = NULL;
+ max->i2c = NULL;
wf_unregister_sensor(&max->sens);
return 0;
}
-static int __init wf_max6690_sensor_init(void)
-{
- /* Don't register on old machines that use therm_pm72 for now */
- if (machine_is_compatible("PowerMac7,2") ||
- machine_is_compatible("PowerMac7,3") ||
- machine_is_compatible("RackMac3,1"))
- return -ENODEV;
- return i2c_add_driver(&wf_max6690_driver);
-}
+static const struct i2c_device_id wf_max6690_id[] = {
+ { "MAC,max6690", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wf_max6690_id);
-static void __exit wf_max6690_sensor_exit(void)
-{
- i2c_del_driver(&wf_max6690_driver);
-}
+static struct i2c_driver wf_max6690_driver = {
+ .driver = {
+ .name = "wf_max6690",
+ },
+ .probe = wf_max6690_probe,
+ .remove = wf_max6690_remove,
+ .id_table = wf_max6690_id,
+};
-module_init(wf_max6690_sensor_init);
-module_exit(wf_max6690_sensor_exit);
+module_i2c_driver(wf_max6690_driver);
MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
MODULE_DESCRIPTION("MAX6690 sensor objects for PowerMac thermal control");
diff --git a/drivers/macintosh/windfarm_mpu.h b/drivers/macintosh/windfarm_mpu.h
new file mode 100644
index 00000000000..046edc8c2ec
--- /dev/null
+++ b/drivers/macintosh/windfarm_mpu.h
@@ -0,0 +1,105 @@
+/*
+ * Windfarm PowerMac thermal control
+ *
+ * Copyright 2012 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Released under the term of the GNU GPL v2.
+ */
+
+#ifndef __WINDFARM_MPU_H
+#define __WINDFARM_MPU_H
+
+typedef unsigned short fu16;
+typedef int fs32;
+typedef short fs16;
+
+/* Definition of the MPU data structure which contains per CPU
+ * calibration information (among others) for the G5 machines
+ */
+struct mpu_data
+{
+ u8 signature; /* 0x00 - EEPROM sig. */
+ u8 bytes_used; /* 0x01 - Bytes used in eeprom (160 ?) */
+ u8 size; /* 0x02 - EEPROM size (256 ?) */
+ u8 version; /* 0x03 - EEPROM version */
+ u32 data_revision; /* 0x04 - Dataset revision */
+ u8 processor_bin_code[3]; /* 0x08 - Processor BIN code */
+ u8 bin_code_expansion; /* 0x0b - ??? (padding ?) */
+ u8 processor_num; /* 0x0c - Number of CPUs on this MPU */
+ u8 input_mul_bus_div; /* 0x0d - Clock input multiplier/bus divider */
+ u8 reserved1[2]; /* 0x0e - */
+ u32 input_clk_freq_high; /* 0x10 - Input clock frequency high */
+ u8 cpu_nb_target_cycles; /* 0x14 - ??? */
+ u8 cpu_statlat; /* 0x15 - ??? */
+ u8 cpu_snooplat; /* 0x16 - ??? */
+ u8 cpu_snoopacc; /* 0x17 - ??? */
+ u8 nb_paamwin; /* 0x18 - ??? */
+ u8 nb_statlat; /* 0x19 - ??? */
+ u8 nb_snooplat; /* 0x1a - ??? */
+ u8 nb_snoopwin; /* 0x1b - ??? */
+ u8 api_bus_mode; /* 0x1c - ??? */
+ u8 reserved2[3]; /* 0x1d - */
+ u32 input_clk_freq_low; /* 0x20 - Input clock frequency low */
+ u8 processor_card_slot; /* 0x24 - Processor card slot number */
+ u8 reserved3[2]; /* 0x25 - */
+ u8 padjmax; /* 0x27 - Max power adjustment (Not in OF!) */
+ u8 ttarget; /* 0x28 - Target temperature */
+ u8 tmax; /* 0x29 - Max temperature */
+ u8 pmaxh; /* 0x2a - Max power */
+ u8 tguardband; /* 0x2b - Guardband temp ??? Hist. len in OSX */
+ fs32 pid_gp; /* 0x2c - PID proportional gain */
+ fs32 pid_gr; /* 0x30 - PID reset gain */
+ fs32 pid_gd; /* 0x34 - PID derivative gain */
+ fu16 voph; /* 0x38 - Vop High */
+ fu16 vopl; /* 0x3a - Vop Low */
+ fs16 nactual_die; /* 0x3c - nActual Die */
+ fs16 nactual_heatsink; /* 0x3e - nActual Heatsink */
+ fs16 nactual_system; /* 0x40 - nActual System */
+ u16 calibration_flags; /* 0x42 - Calibration flags */
+ fu16 mdiode; /* 0x44 - Diode M value (scaling factor) */
+ fs16 bdiode; /* 0x46 - Diode B value (offset) */
+ fs32 theta_heat_sink; /* 0x48 - Theta heat sink */
+ u16 rminn_intake_fan; /* 0x4c - Intake fan min RPM */
+ u16 rmaxn_intake_fan; /* 0x4e - Intake fan max RPM */
+ u16 rminn_exhaust_fan; /* 0x50 - Exhaust fan min RPM */
+ u16 rmaxn_exhaust_fan; /* 0x52 - Exhaust fan max RPM */
+ u8 processor_part_num[8]; /* 0x54 - Processor part number XX pumps min/max */
+ u32 processor_lot_num; /* 0x5c - Processor lot number */
+ u8 orig_card_sernum[0x10]; /* 0x60 - Card original serial number */
+ u8 curr_card_sernum[0x10]; /* 0x70 - Card current serial number */
+ u8 mlb_sernum[0x18]; /* 0x80 - MLB serial number */
+ u32 checksum1; /* 0x98 - */
+ u32 checksum2; /* 0x9c - */
+}; /* Total size = 0xa0 */
+
+static inline const struct mpu_data *wf_get_mpu(int cpu)
+{
+ struct device_node *np;
+ char nodename[64];
+ const void *data;
+ int len;
+
+ /*
+ * prom.c routine for finding a node by path is a bit brain dead
+ * and requires exact @xxx unit numbers. This is a bit ugly but
+ * will work for these machines
+ */
+ sprintf(nodename, "/u3@0,f8000000/i2c@f8001000/cpuid@a%d", cpu ? 2 : 0);
+ np = of_find_node_by_path(nodename);
+ if (!np)
+ return NULL;
+ data = of_get_property(np, "cpuid", &len);
+ of_node_put(np);
+ if (!data)
+ return NULL;
+
+ /*
+ * We are naughty, we have dropped the reference to the device
+ * node and still return a pointer to the content. We know we
+ * can do that though as this is only ever called on PowerMac
+ * which cannot remove those nodes
+ */
+ return data;
+}
+
+#endif /* __WINDFARM_MPU_H */
diff --git a/drivers/macintosh/windfarm_pm112.c b/drivers/macintosh/windfarm_pm112.c
index ef66bf2778e..3024685e4cc 100644
--- a/drivers/macintosh/windfarm_pm112.c
+++ b/drivers/macintosh/windfarm_pm112.c
@@ -650,46 +650,59 @@ static struct notifier_block pm112_events = {
.notifier_call = pm112_wf_notify,
};
-static int wf_pm112_probe(struct device *dev)
+static int wf_pm112_probe(struct platform_device *dev)
{
wf_register_client(&pm112_events);
return 0;
}
-static int wf_pm112_remove(struct device *dev)
+static int wf_pm112_remove(struct platform_device *dev)
{
wf_unregister_client(&pm112_events);
/* should release all sensors and controls */
return 0;
}
-static struct device_driver wf_pm112_driver = {
- .name = "windfarm",
- .bus = &platform_bus_type,
+static struct platform_driver wf_pm112_driver = {
.probe = wf_pm112_probe,
.remove = wf_pm112_remove,
+ .driver = {
+ .name = "windfarm",
+ .owner = THIS_MODULE,
+ },
};
static int __init wf_pm112_init(void)
{
struct device_node *cpu;
- if (!machine_is_compatible("PowerMac11,2"))
+ if (!of_machine_is_compatible("PowerMac11,2"))
return -ENODEV;
/* Count the number of CPU cores */
nr_cores = 0;
- for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; )
+ for_each_node_by_type(cpu, "cpu")
++nr_cores;
printk(KERN_INFO "windfarm: initializing for dual-core desktop G5\n");
- driver_register(&wf_pm112_driver);
+
+#ifdef MODULE
+ request_module("windfarm_smu_controls");
+ request_module("windfarm_smu_sensors");
+ request_module("windfarm_smu_sat");
+ request_module("windfarm_lm75_sensor");
+ request_module("windfarm_max6690_sensor");
+ request_module("windfarm_cpufreq_clamp");
+
+#endif /* MODULE */
+
+ platform_driver_register(&wf_pm112_driver);
return 0;
}
static void __exit wf_pm112_exit(void)
{
- driver_unregister(&wf_pm112_driver);
+ platform_driver_unregister(&wf_pm112_driver);
}
module_init(wf_pm112_init);
@@ -698,3 +711,4 @@ module_exit(wf_pm112_exit);
MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
MODULE_DESCRIPTION("Thermal control for PowerMac11,2");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:windfarm");
diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c
new file mode 100644
index 00000000000..b350fb86ff0
--- /dev/null
+++ b/drivers/macintosh/windfarm_pm121.c
@@ -0,0 +1,1053 @@
+/*
+ * Windfarm PowerMac thermal control. iMac G5 iSight
+ *
+ * (c) Copyright 2007 Étienne Bersac <bersace@gmail.com>
+ *
+ * Bits & pieces from windfarm_pm81.c by (c) Copyright 2005 Benjamin
+ * Herrenschmidt, IBM Corp. <benh@kernel.crashing.org>
+ *
+ * Released under the term of the GNU GPL v2.
+ *
+ *
+ *
+ * PowerMac12,1
+ * ============
+ *
+ *
+ * The algorithm used is the PID control algorithm, used the same way
+ * the published Darwin code does, using the same values that are
+ * present in the Darwin 8.10 snapshot property lists (note however
+ * that none of the code has been re-used, it's a complete
+ * re-implementation
+ *
+ * There is two models using PowerMac12,1. Model 2 is iMac G5 iSight
+ * 17" while Model 3 is iMac G5 20". They do have both the same
+ * controls with a tiny difference. The control-ids of hard-drive-fan
+ * and cpu-fan is swapped.
+ *
+ *
+ * Target Correction :
+ *
+ * controls have a target correction calculated as :
+ *
+ * new_min = ((((average_power * slope) >> 16) + offset) >> 16) + min_value
+ * new_value = max(new_value, max(new_min, 0))
+ *
+ * OD Fan control correction.
+ *
+ * # model_id: 2
+ * offset : -19563152
+ * slope : 1956315
+ *
+ * # model_id: 3
+ * offset : -15650652
+ * slope : 1565065
+ *
+ * HD Fan control correction.
+ *
+ * # model_id: 2
+ * offset : -15650652
+ * slope : 1565065
+ *
+ * # model_id: 3
+ * offset : -19563152
+ * slope : 1956315
+ *
+ * CPU Fan control correction.
+ *
+ * # model_id: 2
+ * offset : -25431900
+ * slope : 2543190
+ *
+ * # model_id: 3
+ * offset : -15650652
+ * slope : 1565065
+ *
+ *
+ * Target rubber-banding :
+ *
+ * Some controls have a target correction which depends on another
+ * control value. The correction is computed in the following way :
+ *
+ * new_min = ref_value * slope + offset
+ *
+ * ref_value is the value of the reference control. If new_min is
+ * greater than 0, then we correct the target value using :
+ *
+ * new_target = max (new_target, new_min >> 16)
+ *
+ *
+ * # model_id : 2
+ * control : cpu-fan
+ * ref : optical-drive-fan
+ * offset : -15650652
+ * slope : 1565065
+ *
+ * # model_id : 3
+ * control : optical-drive-fan
+ * ref : hard-drive-fan
+ * offset : -32768000
+ * slope : 65536
+ *
+ *
+ * In order to have the moste efficient correction with those
+ * dependencies, we must trigger HD loop before OD loop before CPU
+ * loop.
+ *
+ *
+ * The various control loops found in Darwin config file are:
+ *
+ * HD Fan control loop.
+ *
+ * # model_id: 2
+ * control : hard-drive-fan
+ * sensor : hard-drive-temp
+ * PID params : G_d = 0x00000000
+ * G_p = 0x002D70A3
+ * G_r = 0x00019999
+ * History = 2 entries
+ * Input target = 0x370000
+ * Interval = 5s
+ *
+ * # model_id: 3
+ * control : hard-drive-fan
+ * sensor : hard-drive-temp
+ * PID params : G_d = 0x00000000
+ * G_p = 0x002170A3
+ * G_r = 0x00019999
+ * History = 2 entries
+ * Input target = 0x370000
+ * Interval = 5s
+ *
+ * OD Fan control loop.
+ *
+ * # model_id: 2
+ * control : optical-drive-fan
+ * sensor : optical-drive-temp
+ * PID params : G_d = 0x00000000
+ * G_p = 0x001FAE14
+ * G_r = 0x00019999
+ * History = 2 entries
+ * Input target = 0x320000
+ * Interval = 5s
+ *
+ * # model_id: 3
+ * control : optical-drive-fan
+ * sensor : optical-drive-temp
+ * PID params : G_d = 0x00000000
+ * G_p = 0x001FAE14
+ * G_r = 0x00019999
+ * History = 2 entries
+ * Input target = 0x320000
+ * Interval = 5s
+ *
+ * GPU Fan control loop.
+ *
+ * # model_id: 2
+ * control : hard-drive-fan
+ * sensor : gpu-temp
+ * PID params : G_d = 0x00000000
+ * G_p = 0x002A6666
+ * G_r = 0x00019999
+ * History = 2 entries
+ * Input target = 0x5A0000
+ * Interval = 5s
+ *
+ * # model_id: 3
+ * control : cpu-fan
+ * sensor : gpu-temp
+ * PID params : G_d = 0x00000000
+ * G_p = 0x0010CCCC
+ * G_r = 0x00019999
+ * History = 2 entries
+ * Input target = 0x500000
+ * Interval = 5s
+ *
+ * KODIAK (aka northbridge) Fan control loop.
+ *
+ * # model_id: 2
+ * control : optical-drive-fan
+ * sensor : north-bridge-temp
+ * PID params : G_d = 0x00000000
+ * G_p = 0x003BD70A
+ * G_r = 0x00019999
+ * History = 2 entries
+ * Input target = 0x550000
+ * Interval = 5s
+ *
+ * # model_id: 3
+ * control : hard-drive-fan
+ * sensor : north-bridge-temp
+ * PID params : G_d = 0x00000000
+ * G_p = 0x0030F5C2
+ * G_r = 0x00019999
+ * History = 2 entries
+ * Input target = 0x550000
+ * Interval = 5s
+ *
+ * CPU Fan control loop.
+ *
+ * control : cpu-fan
+ * sensors : cpu-temp, cpu-power
+ * PID params : from SDB partition
+ *
+ *
+ * CPU Slew control loop.
+ *
+ * control : cpufreq-clamp
+ * sensor : cpu-temp
+ *
+ */
+
+#undef DEBUG
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/kmod.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+#include <asm/smu.h>
+
+#include "windfarm.h"
+#include "windfarm_pid.h"
+
+#define VERSION "0.3"
+
+static int pm121_mach_model; /* machine model id */
+
+/* Controls & sensors */
+static struct wf_sensor *sensor_cpu_power;
+static struct wf_sensor *sensor_cpu_temp;
+static struct wf_sensor *sensor_cpu_voltage;
+static struct wf_sensor *sensor_cpu_current;
+static struct wf_sensor *sensor_gpu_temp;
+static struct wf_sensor *sensor_north_bridge_temp;
+static struct wf_sensor *sensor_hard_drive_temp;
+static struct wf_sensor *sensor_optical_drive_temp;
+static struct wf_sensor *sensor_incoming_air_temp; /* unused ! */
+
+enum {
+ FAN_CPU,
+ FAN_HD,
+ FAN_OD,
+ CPUFREQ,
+ N_CONTROLS
+};
+static struct wf_control *controls[N_CONTROLS] = {};
+
+/* Set to kick the control loop into life */
+static int pm121_all_controls_ok, pm121_all_sensors_ok, pm121_started;
+
+enum {
+ FAILURE_FAN = 1 << 0,
+ FAILURE_SENSOR = 1 << 1,
+ FAILURE_OVERTEMP = 1 << 2
+};
+
+/* All sys loops. Note the HD before the OD loop in order to have it
+ run before. */
+enum {
+ LOOP_GPU, /* control = hd or cpu, but luckily,
+ it doesn't matter */
+ LOOP_HD, /* control = hd */
+ LOOP_KODIAK, /* control = hd or od */
+ LOOP_OD, /* control = od */
+ N_LOOPS
+};
+
+static const char *loop_names[N_LOOPS] = {
+ "GPU",
+ "HD",
+ "KODIAK",
+ "OD",
+};
+
+#define PM121_NUM_CONFIGS 2
+
+static unsigned int pm121_failure_state;
+static int pm121_readjust, pm121_skipping;
+static bool pm121_overtemp;
+static s32 average_power;
+
+struct pm121_correction {
+ int offset;
+ int slope;
+};
+
+static struct pm121_correction corrections[N_CONTROLS][PM121_NUM_CONFIGS] = {
+ /* FAN_OD */
+ {
+ /* MODEL 2 */
+ { .offset = -19563152,
+ .slope = 1956315
+ },
+ /* MODEL 3 */
+ { .offset = -15650652,
+ .slope = 1565065
+ },
+ },
+ /* FAN_HD */
+ {
+ /* MODEL 2 */
+ { .offset = -15650652,
+ .slope = 1565065
+ },
+ /* MODEL 3 */
+ { .offset = -19563152,
+ .slope = 1956315
+ },
+ },
+ /* FAN_CPU */
+ {
+ /* MODEL 2 */
+ { .offset = -25431900,
+ .slope = 2543190
+ },
+ /* MODEL 3 */
+ { .offset = -15650652,
+ .slope = 1565065
+ },
+ },
+ /* CPUFREQ has no correction (and is not implemented at all) */
+};
+
+struct pm121_connection {
+ unsigned int control_id;
+ unsigned int ref_id;
+ struct pm121_correction correction;
+};
+
+static struct pm121_connection pm121_connections[] = {
+ /* MODEL 2 */
+ { .control_id = FAN_CPU,
+ .ref_id = FAN_OD,
+ { .offset = -32768000,
+ .slope = 65536
+ }
+ },
+ /* MODEL 3 */
+ { .control_id = FAN_OD,
+ .ref_id = FAN_HD,
+ { .offset = -32768000,
+ .slope = 65536
+ }
+ },
+};
+
+/* pointer to the current model connection */
+static struct pm121_connection *pm121_connection;
+
+/*
+ * ****** System Fans Control Loop ******
+ *
+ */
+
+/* Since each loop handles only one control and we want to avoid
+ * writing virtual control, we store the control correction with the
+ * loop params. Some data are not set, there are common to all loop
+ * and thus, hardcoded.
+ */
+struct pm121_sys_param {
+ /* purely informative since we use mach_model-2 as index */
+ int model_id;
+ struct wf_sensor **sensor; /* use sensor_id instead ? */
+ s32 gp, itarget;
+ unsigned int control_id;
+};
+
+static struct pm121_sys_param
+pm121_sys_all_params[N_LOOPS][PM121_NUM_CONFIGS] = {
+ /* GPU Fan control loop */
+ {
+ { .model_id = 2,
+ .sensor = &sensor_gpu_temp,
+ .gp = 0x002A6666,
+ .itarget = 0x5A0000,
+ .control_id = FAN_HD,
+ },
+ { .model_id = 3,
+ .sensor = &sensor_gpu_temp,
+ .gp = 0x0010CCCC,
+ .itarget = 0x500000,
+ .control_id = FAN_CPU,
+ },
+ },
+ /* HD Fan control loop */
+ {
+ { .model_id = 2,
+ .sensor = &sensor_hard_drive_temp,
+ .gp = 0x002D70A3,
+ .itarget = 0x370000,
+ .control_id = FAN_HD,
+ },
+ { .model_id = 3,
+ .sensor = &sensor_hard_drive_temp,
+ .gp = 0x002170A3,
+ .itarget = 0x370000,
+ .control_id = FAN_HD,
+ },
+ },
+ /* KODIAK Fan control loop */
+ {
+ { .model_id = 2,
+ .sensor = &sensor_north_bridge_temp,
+ .gp = 0x003BD70A,
+ .itarget = 0x550000,
+ .control_id = FAN_OD,
+ },
+ { .model_id = 3,
+ .sensor = &sensor_north_bridge_temp,
+ .gp = 0x0030F5C2,
+ .itarget = 0x550000,
+ .control_id = FAN_HD,
+ },
+ },
+ /* OD Fan control loop */
+ {
+ { .model_id = 2,
+ .sensor = &sensor_optical_drive_temp,
+ .gp = 0x001FAE14,
+ .itarget = 0x320000,
+ .control_id = FAN_OD,
+ },
+ { .model_id = 3,
+ .sensor = &sensor_optical_drive_temp,
+ .gp = 0x001FAE14,
+ .itarget = 0x320000,
+ .control_id = FAN_OD,
+ },
+ },
+};
+
+/* the hardcoded values */
+#define PM121_SYS_GD 0x00000000
+#define PM121_SYS_GR 0x00019999
+#define PM121_SYS_HISTORY_SIZE 2
+#define PM121_SYS_INTERVAL 5
+
+/* State data used by the system fans control loop
+ */
+struct pm121_sys_state {
+ int ticks;
+ s32 setpoint;
+ struct wf_pid_state pid;
+};
+
+struct pm121_sys_state *pm121_sys_state[N_LOOPS] = {};
+
+/*
+ * ****** CPU Fans Control Loop ******
+ *
+ */
+
+#define PM121_CPU_INTERVAL 1
+
+/* State data used by the cpu fans control loop
+ */
+struct pm121_cpu_state {
+ int ticks;
+ s32 setpoint;
+ struct wf_cpu_pid_state pid;
+};
+
+static struct pm121_cpu_state *pm121_cpu_state;
+
+
+
+/*
+ * ***** Implementation *****
+ *
+ */
+
+/* correction the value using the output-low-bound correction algo */
+static s32 pm121_correct(s32 new_setpoint,
+ unsigned int control_id,
+ s32 min)
+{
+ s32 new_min;
+ struct pm121_correction *correction;
+ correction = &corrections[control_id][pm121_mach_model - 2];
+
+ new_min = (average_power * correction->slope) >> 16;
+ new_min += correction->offset;
+ new_min = (new_min >> 16) + min;
+
+ return max3(new_setpoint, new_min, 0);
+}
+
+static s32 pm121_connect(unsigned int control_id, s32 setpoint)
+{
+ s32 new_min, value, new_setpoint;
+
+ if (pm121_connection->control_id == control_id) {
+ controls[control_id]->ops->get_value(controls[control_id],
+ &value);
+ new_min = value * pm121_connection->correction.slope;
+ new_min += pm121_connection->correction.offset;
+ if (new_min > 0) {
+ new_setpoint = max(setpoint, (new_min >> 16));
+ if (new_setpoint != setpoint) {
+ pr_debug("pm121: %s depending on %s, "
+ "corrected from %d to %d RPM\n",
+ controls[control_id]->name,
+ controls[pm121_connection->ref_id]->name,
+ (int) setpoint, (int) new_setpoint);
+ }
+ } else
+ new_setpoint = setpoint;
+ }
+ /* no connection */
+ else
+ new_setpoint = setpoint;
+
+ return new_setpoint;
+}
+
+/* FAN LOOPS */
+static void pm121_create_sys_fans(int loop_id)
+{
+ struct pm121_sys_param *param = NULL;
+ struct wf_pid_param pid_param;
+ struct wf_control *control = NULL;
+ int i;
+
+ /* First, locate the params for this model */
+ for (i = 0; i < PM121_NUM_CONFIGS; i++) {
+ if (pm121_sys_all_params[loop_id][i].model_id == pm121_mach_model) {
+ param = &(pm121_sys_all_params[loop_id][i]);
+ break;
+ }
+ }
+
+ /* No params found, put fans to max */
+ if (param == NULL) {
+ printk(KERN_WARNING "pm121: %s fan config not found "
+ " for this machine model\n",
+ loop_names[loop_id]);
+ goto fail;
+ }
+
+ control = controls[param->control_id];
+
+ /* Alloc & initialize state */
+ pm121_sys_state[loop_id] = kmalloc(sizeof(struct pm121_sys_state),
+ GFP_KERNEL);
+ if (pm121_sys_state[loop_id] == NULL) {
+ printk(KERN_WARNING "pm121: Memory allocation error\n");
+ goto fail;
+ }
+ pm121_sys_state[loop_id]->ticks = 1;
+
+ /* Fill PID params */
+ pid_param.gd = PM121_SYS_GD;
+ pid_param.gp = param->gp;
+ pid_param.gr = PM121_SYS_GR;
+ pid_param.interval = PM121_SYS_INTERVAL;
+ pid_param.history_len = PM121_SYS_HISTORY_SIZE;
+ pid_param.itarget = param->itarget;
+ if(control)
+ {
+ pid_param.min = control->ops->get_min(control);
+ pid_param.max = control->ops->get_max(control);
+ } else {
+ /*
+ * This is probably not the right!?
+ * Perhaps goto fail if control == NULL above?
+ */
+ pid_param.min = 0;
+ pid_param.max = 0;
+ }
+
+ wf_pid_init(&pm121_sys_state[loop_id]->pid, &pid_param);
+
+ pr_debug("pm121: %s Fan control loop initialized.\n"
+ " itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
+ loop_names[loop_id], FIX32TOPRINT(pid_param.itarget),
+ pid_param.min, pid_param.max);
+ return;
+
+ fail:
+ /* note that this is not optimal since another loop may still
+ control the same control */
+ printk(KERN_WARNING "pm121: failed to set up %s loop "
+ "setting \"%s\" to max speed.\n",
+ loop_names[loop_id], control ? control->name : "uninitialized value");
+
+ if (control)
+ wf_control_set_max(control);
+}
+
+static void pm121_sys_fans_tick(int loop_id)
+{
+ struct pm121_sys_param *param;
+ struct pm121_sys_state *st;
+ struct wf_sensor *sensor;
+ struct wf_control *control;
+ s32 temp, new_setpoint;
+ int rc;
+
+ param = &(pm121_sys_all_params[loop_id][pm121_mach_model-2]);
+ st = pm121_sys_state[loop_id];
+ sensor = *(param->sensor);
+ control = controls[param->control_id];
+
+ if (--st->ticks != 0) {
+ if (pm121_readjust)
+ goto readjust;
+ return;
+ }
+ st->ticks = PM121_SYS_INTERVAL;
+
+ rc = sensor->ops->get_value(sensor, &temp);
+ if (rc) {
+ printk(KERN_WARNING "windfarm: %s sensor error %d\n",
+ sensor->name, rc);
+ pm121_failure_state |= FAILURE_SENSOR;
+ return;
+ }
+
+ pr_debug("pm121: %s Fan tick ! %s: %d.%03d\n",
+ loop_names[loop_id], sensor->name,
+ FIX32TOPRINT(temp));
+
+ new_setpoint = wf_pid_run(&st->pid, temp);
+
+ /* correction */
+ new_setpoint = pm121_correct(new_setpoint,
+ param->control_id,
+ st->pid.param.min);
+ /* linked corretion */
+ new_setpoint = pm121_connect(param->control_id, new_setpoint);
+
+ if (new_setpoint == st->setpoint)
+ return;
+ st->setpoint = new_setpoint;
+ pr_debug("pm121: %s corrected setpoint: %d RPM\n",
+ control->name, (int)new_setpoint);
+ readjust:
+ if (control && pm121_failure_state == 0) {
+ rc = control->ops->set_value(control, st->setpoint);
+ if (rc) {
+ printk(KERN_WARNING "windfarm: %s fan error %d\n",
+ control->name, rc);
+ pm121_failure_state |= FAILURE_FAN;
+ }
+ }
+}
+
+
+/* CPU LOOP */
+static void pm121_create_cpu_fans(void)
+{
+ struct wf_cpu_pid_param pid_param;
+ const struct smu_sdbp_header *hdr;
+ struct smu_sdbp_cpupiddata *piddata;
+ struct smu_sdbp_fvt *fvt;
+ struct wf_control *fan_cpu;
+ s32 tmax, tdelta, maxpow, powadj;
+
+ fan_cpu = controls[FAN_CPU];
+
+ /* First, locate the PID params in SMU SBD */
+ hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);
+ if (hdr == 0) {
+ printk(KERN_WARNING "pm121: CPU PID fan config not found.\n");
+ goto fail;
+ }
+ piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
+
+ /* Get the FVT params for operating point 0 (the only supported one
+ * for now) in order to get tmax
+ */
+ hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
+ if (hdr) {
+ fvt = (struct smu_sdbp_fvt *)&hdr[1];
+ tmax = ((s32)fvt->maxtemp) << 16;
+ } else
+ tmax = 0x5e0000; /* 94 degree default */
+
+ /* Alloc & initialize state */
+ pm121_cpu_state = kmalloc(sizeof(struct pm121_cpu_state),
+ GFP_KERNEL);
+ if (pm121_cpu_state == NULL)
+ goto fail;
+ pm121_cpu_state->ticks = 1;
+
+ /* Fill PID params */
+ pid_param.interval = PM121_CPU_INTERVAL;
+ pid_param.history_len = piddata->history_len;
+ if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
+ printk(KERN_WARNING "pm121: History size overflow on "
+ "CPU control loop (%d)\n", piddata->history_len);
+ pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
+ }
+ pid_param.gd = piddata->gd;
+ pid_param.gp = piddata->gp;
+ pid_param.gr = piddata->gr / pid_param.history_len;
+
+ tdelta = ((s32)piddata->target_temp_delta) << 16;
+ maxpow = ((s32)piddata->max_power) << 16;
+ powadj = ((s32)piddata->power_adj) << 16;
+
+ pid_param.tmax = tmax;
+ pid_param.ttarget = tmax - tdelta;
+ pid_param.pmaxadj = maxpow - powadj;
+
+ pid_param.min = fan_cpu->ops->get_min(fan_cpu);
+ pid_param.max = fan_cpu->ops->get_max(fan_cpu);
+
+ wf_cpu_pid_init(&pm121_cpu_state->pid, &pid_param);
+
+ pr_debug("pm121: CPU Fan control initialized.\n");
+ pr_debug(" ttarged=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM,\n",
+ FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),
+ pid_param.min, pid_param.max);
+
+ return;
+
+ fail:
+ printk(KERN_WARNING "pm121: CPU fan config not found, max fan speed\n");
+
+ if (controls[CPUFREQ])
+ wf_control_set_max(controls[CPUFREQ]);
+ if (fan_cpu)
+ wf_control_set_max(fan_cpu);
+}
+
+
+static void pm121_cpu_fans_tick(struct pm121_cpu_state *st)
+{
+ s32 new_setpoint, temp, power;
+ struct wf_control *fan_cpu = NULL;
+ int rc;
+
+ if (--st->ticks != 0) {
+ if (pm121_readjust)
+ goto readjust;
+ return;
+ }
+ st->ticks = PM121_CPU_INTERVAL;
+
+ fan_cpu = controls[FAN_CPU];
+
+ rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);
+ if (rc) {
+ printk(KERN_WARNING "pm121: CPU temp sensor error %d\n",
+ rc);
+ pm121_failure_state |= FAILURE_SENSOR;
+ return;
+ }
+
+ rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);
+ if (rc) {
+ printk(KERN_WARNING "pm121: CPU power sensor error %d\n",
+ rc);
+ pm121_failure_state |= FAILURE_SENSOR;
+ return;
+ }
+
+ pr_debug("pm121: CPU Fans tick ! CPU temp: %d.%03d°C, power: %d.%03d\n",
+ FIX32TOPRINT(temp), FIX32TOPRINT(power));
+
+ if (temp > st->pid.param.tmax)
+ pm121_failure_state |= FAILURE_OVERTEMP;
+
+ new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);
+
+ /* correction */
+ new_setpoint = pm121_correct(new_setpoint,
+ FAN_CPU,
+ st->pid.param.min);
+
+ /* connected correction */
+ new_setpoint = pm121_connect(FAN_CPU, new_setpoint);
+
+ if (st->setpoint == new_setpoint)
+ return;
+ st->setpoint = new_setpoint;
+ pr_debug("pm121: CPU corrected setpoint: %d RPM\n", (int)new_setpoint);
+
+ readjust:
+ if (fan_cpu && pm121_failure_state == 0) {
+ rc = fan_cpu->ops->set_value(fan_cpu, st->setpoint);
+ if (rc) {
+ printk(KERN_WARNING "pm121: %s fan error %d\n",
+ fan_cpu->name, rc);
+ pm121_failure_state |= FAILURE_FAN;
+ }
+ }
+}
+
+/*
+ * ****** Common ******
+ *
+ */
+
+static void pm121_tick(void)
+{
+ unsigned int last_failure = pm121_failure_state;
+ unsigned int new_failure;
+ s32 total_power;
+ int i;
+
+ if (!pm121_started) {
+ pr_debug("pm121: creating control loops !\n");
+ for (i = 0; i < N_LOOPS; i++)
+ pm121_create_sys_fans(i);
+
+ pm121_create_cpu_fans();
+ pm121_started = 1;
+ }
+
+ /* skipping ticks */
+ if (pm121_skipping && --pm121_skipping)
+ return;
+
+ /* compute average power */
+ total_power = 0;
+ for (i = 0; i < pm121_cpu_state->pid.param.history_len; i++)
+ total_power += pm121_cpu_state->pid.powers[i];
+
+ average_power = total_power / pm121_cpu_state->pid.param.history_len;
+
+
+ pm121_failure_state = 0;
+ for (i = 0 ; i < N_LOOPS; i++) {
+ if (pm121_sys_state[i])
+ pm121_sys_fans_tick(i);
+ }
+
+ if (pm121_cpu_state)
+ pm121_cpu_fans_tick(pm121_cpu_state);
+
+ pm121_readjust = 0;
+ new_failure = pm121_failure_state & ~last_failure;
+
+ /* If entering failure mode, clamp cpufreq and ramp all
+ * fans to full speed.
+ */
+ if (pm121_failure_state && !last_failure) {
+ for (i = 0; i < N_CONTROLS; i++) {
+ if (controls[i])
+ wf_control_set_max(controls[i]);
+ }
+ }
+
+ /* If leaving failure mode, unclamp cpufreq and readjust
+ * all fans on next iteration
+ */
+ if (!pm121_failure_state && last_failure) {
+ if (controls[CPUFREQ])
+ wf_control_set_min(controls[CPUFREQ]);
+ pm121_readjust = 1;
+ }
+
+ /* Overtemp condition detected, notify and start skipping a couple
+ * ticks to let the temperature go down
+ */
+ if (new_failure & FAILURE_OVERTEMP) {
+ wf_set_overtemp();
+ pm121_skipping = 2;
+ pm121_overtemp = true;
+ }
+
+ /* We only clear the overtemp condition if overtemp is cleared
+ * _and_ no other failure is present. Since a sensor error will
+ * clear the overtemp condition (can't measure temperature) at
+ * the control loop levels, but we don't want to keep it clear
+ * here in this case
+ */
+ if (!pm121_failure_state && pm121_overtemp) {
+ wf_clear_overtemp();
+ pm121_overtemp = false;
+ }
+}
+
+
+static struct wf_control* pm121_register_control(struct wf_control *ct,
+ const char *match,
+ unsigned int id)
+{
+ if (controls[id] == NULL && !strcmp(ct->name, match)) {
+ if (wf_get_control(ct) == 0)
+ controls[id] = ct;
+ }
+ return controls[id];
+}
+
+static void pm121_new_control(struct wf_control *ct)
+{
+ int all = 1;
+
+ if (pm121_all_controls_ok)
+ return;
+
+ all = pm121_register_control(ct, "optical-drive-fan", FAN_OD) && all;
+ all = pm121_register_control(ct, "hard-drive-fan", FAN_HD) && all;
+ all = pm121_register_control(ct, "cpu-fan", FAN_CPU) && all;
+ all = pm121_register_control(ct, "cpufreq-clamp", CPUFREQ) && all;
+
+ if (all)
+ pm121_all_controls_ok = 1;
+}
+
+
+
+
+static struct wf_sensor* pm121_register_sensor(struct wf_sensor *sensor,
+ const char *match,
+ struct wf_sensor **var)
+{
+ if (*var == NULL && !strcmp(sensor->name, match)) {
+ if (wf_get_sensor(sensor) == 0)
+ *var = sensor;
+ }
+ return *var;
+}
+
+static void pm121_new_sensor(struct wf_sensor *sr)
+{
+ int all = 1;
+
+ if (pm121_all_sensors_ok)
+ return;
+
+ all = pm121_register_sensor(sr, "cpu-temp",
+ &sensor_cpu_temp) && all;
+ all = pm121_register_sensor(sr, "cpu-current",
+ &sensor_cpu_current) && all;
+ all = pm121_register_sensor(sr, "cpu-voltage",
+ &sensor_cpu_voltage) && all;
+ all = pm121_register_sensor(sr, "cpu-power",
+ &sensor_cpu_power) && all;
+ all = pm121_register_sensor(sr, "hard-drive-temp",
+ &sensor_hard_drive_temp) && all;
+ all = pm121_register_sensor(sr, "optical-drive-temp",
+ &sensor_optical_drive_temp) && all;
+ all = pm121_register_sensor(sr, "incoming-air-temp",
+ &sensor_incoming_air_temp) && all;
+ all = pm121_register_sensor(sr, "north-bridge-temp",
+ &sensor_north_bridge_temp) && all;
+ all = pm121_register_sensor(sr, "gpu-temp",
+ &sensor_gpu_temp) && all;
+
+ if (all)
+ pm121_all_sensors_ok = 1;
+}
+
+
+
+static int pm121_notify(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ switch (event) {
+ case WF_EVENT_NEW_CONTROL:
+ pr_debug("pm121: new control %s detected\n",
+ ((struct wf_control *)data)->name);
+ pm121_new_control(data);
+ break;
+ case WF_EVENT_NEW_SENSOR:
+ pr_debug("pm121: new sensor %s detected\n",
+ ((struct wf_sensor *)data)->name);
+ pm121_new_sensor(data);
+ break;
+ case WF_EVENT_TICK:
+ if (pm121_all_controls_ok && pm121_all_sensors_ok)
+ pm121_tick();
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block pm121_events = {
+ .notifier_call = pm121_notify,
+};
+
+static int pm121_init_pm(void)
+{
+ const struct smu_sdbp_header *hdr;
+
+ hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL);
+ if (hdr != 0) {
+ struct smu_sdbp_sensortree *st =
+ (struct smu_sdbp_sensortree *)&hdr[1];
+ pm121_mach_model = st->model_id;
+ }
+
+ pm121_connection = &pm121_connections[pm121_mach_model - 2];
+
+ printk(KERN_INFO "pm121: Initializing for iMac G5 iSight model ID %d\n",
+ pm121_mach_model);
+
+ return 0;
+}
+
+
+static int pm121_probe(struct platform_device *ddev)
+{
+ wf_register_client(&pm121_events);
+
+ return 0;
+}
+
+static int pm121_remove(struct platform_device *ddev)
+{
+ wf_unregister_client(&pm121_events);
+ return 0;
+}
+
+static struct platform_driver pm121_driver = {
+ .probe = pm121_probe,
+ .remove = pm121_remove,
+ .driver = {
+ .name = "windfarm",
+ .bus = &platform_bus_type,
+ },
+};
+
+
+static int __init pm121_init(void)
+{
+ int rc = -ENODEV;
+
+ if (of_machine_is_compatible("PowerMac12,1"))
+ rc = pm121_init_pm();
+
+ if (rc == 0) {
+ request_module("windfarm_smu_controls");
+ request_module("windfarm_smu_sensors");
+ request_module("windfarm_smu_sat");
+ request_module("windfarm_lm75_sensor");
+ request_module("windfarm_max6690_sensor");
+ request_module("windfarm_cpufreq_clamp");
+ platform_driver_register(&pm121_driver);
+ }
+
+ return rc;
+}
+
+static void __exit pm121_exit(void)
+{
+
+ platform_driver_unregister(&pm121_driver);
+}
+
+
+module_init(pm121_init);
+module_exit(pm121_exit);
+
+MODULE_AUTHOR("Étienne Bersac <bersace@gmail.com>");
+MODULE_DESCRIPTION("Thermal control logic for iMac G5 (iSight)");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_pm72.c b/drivers/macintosh/windfarm_pm72.c
new file mode 100644
index 00000000000..2f506b9d5a5
--- /dev/null
+++ b/drivers/macintosh/windfarm_pm72.c
@@ -0,0 +1,847 @@
+/*
+ * Windfarm PowerMac thermal control.
+ * Control loops for PowerMac7,2 and 7,3
+ *
+ * Copyright (C) 2012 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Use and redistribute under the terms of the GNU GPL v2.
+ */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <asm/prom.h>
+#include <asm/smu.h>
+
+#include "windfarm.h"
+#include "windfarm_pid.h"
+#include "windfarm_mpu.h"
+
+#define VERSION "1.0"
+
+#undef DEBUG
+#undef LOTSA_DEBUG
+
+#ifdef DEBUG
+#define DBG(args...) printk(args)
+#else
+#define DBG(args...) do { } while(0)
+#endif
+
+#ifdef LOTSA_DEBUG
+#define DBG_LOTS(args...) printk(args)
+#else
+#define DBG_LOTS(args...) do { } while(0)
+#endif
+
+/* define this to force CPU overtemp to 60 degree, useful for testing
+ * the overtemp code
+ */
+#undef HACKED_OVERTEMP
+
+/* We currently only handle 2 chips */
+#define NR_CHIPS 2
+#define NR_CPU_FANS 3 * NR_CHIPS
+
+/* Controls and sensors */
+static struct wf_sensor *sens_cpu_temp[NR_CHIPS];
+static struct wf_sensor *sens_cpu_volts[NR_CHIPS];
+static struct wf_sensor *sens_cpu_amps[NR_CHIPS];
+static struct wf_sensor *backside_temp;
+static struct wf_sensor *drives_temp;
+
+static struct wf_control *cpu_front_fans[NR_CHIPS];
+static struct wf_control *cpu_rear_fans[NR_CHIPS];
+static struct wf_control *cpu_pumps[NR_CHIPS];
+static struct wf_control *backside_fan;
+static struct wf_control *drives_fan;
+static struct wf_control *slots_fan;
+static struct wf_control *cpufreq_clamp;
+
+/* We keep a temperature history for average calculation of 180s */
+#define CPU_TEMP_HIST_SIZE 180
+
+/* Fixed speed for slot fan */
+#define SLOTS_FAN_DEFAULT_PWM 40
+
+/* Scale value for CPU intake fans */
+#define CPU_INTAKE_SCALE 0x0000f852
+
+/* PID loop state */
+static const struct mpu_data *cpu_mpu_data[NR_CHIPS];
+static struct wf_cpu_pid_state cpu_pid[NR_CHIPS];
+static bool cpu_pid_combined;
+static u32 cpu_thist[CPU_TEMP_HIST_SIZE];
+static int cpu_thist_pt;
+static s64 cpu_thist_total;
+static s32 cpu_all_tmax = 100 << 16;
+static struct wf_pid_state backside_pid;
+static int backside_tick;
+static struct wf_pid_state drives_pid;
+static int drives_tick;
+
+static int nr_chips;
+static bool have_all_controls;
+static bool have_all_sensors;
+static bool started;
+
+static int failure_state;
+#define FAILURE_SENSOR 1
+#define FAILURE_FAN 2
+#define FAILURE_PERM 4
+#define FAILURE_LOW_OVERTEMP 8
+#define FAILURE_HIGH_OVERTEMP 16
+
+/* Overtemp values */
+#define LOW_OVER_AVERAGE 0
+#define LOW_OVER_IMMEDIATE (10 << 16)
+#define LOW_OVER_CLEAR ((-10) << 16)
+#define HIGH_OVER_IMMEDIATE (14 << 16)
+#define HIGH_OVER_AVERAGE (10 << 16)
+#define HIGH_OVER_IMMEDIATE (14 << 16)
+
+
+static void cpu_max_all_fans(void)
+{
+ int i;
+
+ /* We max all CPU fans in case of a sensor error. We also do the
+ * cpufreq clamping now, even if it's supposedly done later by the
+ * generic code anyway, we do it earlier here to react faster
+ */
+ if (cpufreq_clamp)
+ wf_control_set_max(cpufreq_clamp);
+ for (i = 0; i < nr_chips; i++) {
+ if (cpu_front_fans[i])
+ wf_control_set_max(cpu_front_fans[i]);
+ if (cpu_rear_fans[i])
+ wf_control_set_max(cpu_rear_fans[i]);
+ if (cpu_pumps[i])
+ wf_control_set_max(cpu_pumps[i]);
+ }
+}
+
+static int cpu_check_overtemp(s32 temp)
+{
+ int new_state = 0;
+ s32 t_avg, t_old;
+ static bool first = true;
+
+ /* First check for immediate overtemps */
+ if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) {
+ new_state |= FAILURE_LOW_OVERTEMP;
+ if ((failure_state & FAILURE_LOW_OVERTEMP) == 0)
+ printk(KERN_ERR "windfarm: Overtemp due to immediate CPU"
+ " temperature !\n");
+ }
+ if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) {
+ new_state |= FAILURE_HIGH_OVERTEMP;
+ if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0)
+ printk(KERN_ERR "windfarm: Critical overtemp due to"
+ " immediate CPU temperature !\n");
+ }
+
+ /*
+ * The first time around, initialize the array with the first
+ * temperature reading
+ */
+ if (first) {
+ int i;
+
+ cpu_thist_total = 0;
+ for (i = 0; i < CPU_TEMP_HIST_SIZE; i++) {
+ cpu_thist[i] = temp;
+ cpu_thist_total += temp;
+ }
+ first = false;
+ }
+
+ /*
+ * We calculate a history of max temperatures and use that for the
+ * overtemp management
+ */
+ t_old = cpu_thist[cpu_thist_pt];
+ cpu_thist[cpu_thist_pt] = temp;
+ cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE;
+ cpu_thist_total -= t_old;
+ cpu_thist_total += temp;
+ t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE;
+
+ DBG_LOTS(" t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n",
+ FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp));
+
+ /* Now check for average overtemps */
+ if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) {
+ new_state |= FAILURE_LOW_OVERTEMP;
+ if ((failure_state & FAILURE_LOW_OVERTEMP) == 0)
+ printk(KERN_ERR "windfarm: Overtemp due to average CPU"
+ " temperature !\n");
+ }
+ if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) {
+ new_state |= FAILURE_HIGH_OVERTEMP;
+ if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0)
+ printk(KERN_ERR "windfarm: Critical overtemp due to"
+ " average CPU temperature !\n");
+ }
+
+ /* Now handle overtemp conditions. We don't currently use the windfarm
+ * overtemp handling core as it's not fully suited to the needs of those
+ * new machine. This will be fixed later.
+ */
+ if (new_state) {
+ /* High overtemp -> immediate shutdown */
+ if (new_state & FAILURE_HIGH_OVERTEMP)
+ machine_power_off();
+ if ((failure_state & new_state) != new_state)
+ cpu_max_all_fans();
+ failure_state |= new_state;
+ } else if ((failure_state & FAILURE_LOW_OVERTEMP) &&
+ (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) {
+ printk(KERN_ERR "windfarm: Overtemp condition cleared !\n");
+ failure_state &= ~FAILURE_LOW_OVERTEMP;
+ }
+
+ return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP);
+}
+
+static int read_one_cpu_vals(int cpu, s32 *temp, s32 *power)
+{
+ s32 dtemp, volts, amps;
+ int rc;
+
+ /* Get diode temperature */
+ rc = wf_sensor_get(sens_cpu_temp[cpu], &dtemp);
+ if (rc) {
+ DBG(" CPU%d: temp reading error !\n", cpu);
+ return -EIO;
+ }
+ DBG_LOTS(" CPU%d: temp = %d.%03d\n", cpu, FIX32TOPRINT((dtemp)));
+ *temp = dtemp;
+
+ /* Get voltage */
+ rc = wf_sensor_get(sens_cpu_volts[cpu], &volts);
+ if (rc) {
+ DBG(" CPU%d, volts reading error !\n", cpu);
+ return -EIO;
+ }
+ DBG_LOTS(" CPU%d: volts = %d.%03d\n", cpu, FIX32TOPRINT((volts)));
+
+ /* Get current */
+ rc = wf_sensor_get(sens_cpu_amps[cpu], &amps);
+ if (rc) {
+ DBG(" CPU%d, current reading error !\n", cpu);
+ return -EIO;
+ }
+ DBG_LOTS(" CPU%d: amps = %d.%03d\n", cpu, FIX32TOPRINT((amps)));
+
+ /* Calculate power */
+
+ /* Scale voltage and current raw sensor values according to fixed scales
+ * obtained in Darwin and calculate power from I and V
+ */
+ *power = (((u64)volts) * ((u64)amps)) >> 16;
+
+ DBG_LOTS(" CPU%d: power = %d.%03d\n", cpu, FIX32TOPRINT((*power)));
+
+ return 0;
+
+}
+
+static void cpu_fans_tick_split(void)
+{
+ int err, cpu;
+ s32 intake, temp, power, t_max = 0;
+
+ DBG_LOTS("* cpu fans_tick_split()\n");
+
+ for (cpu = 0; cpu < nr_chips; ++cpu) {
+ struct wf_cpu_pid_state *sp = &cpu_pid[cpu];
+
+ /* Read current speed */
+ wf_control_get(cpu_rear_fans[cpu], &sp->target);
+
+ DBG_LOTS(" CPU%d: cur_target = %d RPM\n", cpu, sp->target);
+
+ err = read_one_cpu_vals(cpu, &temp, &power);
+ if (err) {
+ failure_state |= FAILURE_SENSOR;
+ cpu_max_all_fans();
+ return;
+ }
+
+ /* Keep track of highest temp */
+ t_max = max(t_max, temp);
+
+ /* Handle possible overtemps */
+ if (cpu_check_overtemp(t_max))
+ return;
+
+ /* Run PID */
+ wf_cpu_pid_run(sp, power, temp);
+
+ DBG_LOTS(" CPU%d: target = %d RPM\n", cpu, sp->target);
+
+ /* Apply result directly to exhaust fan */
+ err = wf_control_set(cpu_rear_fans[cpu], sp->target);
+ if (err) {
+ pr_warning("wf_pm72: Fan %s reports error %d\n",
+ cpu_rear_fans[cpu]->name, err);
+ failure_state |= FAILURE_FAN;
+ break;
+ }
+
+ /* Scale result for intake fan */
+ intake = (sp->target * CPU_INTAKE_SCALE) >> 16;
+ DBG_LOTS(" CPU%d: intake = %d RPM\n", cpu, intake);
+ err = wf_control_set(cpu_front_fans[cpu], intake);
+ if (err) {
+ pr_warning("wf_pm72: Fan %s reports error %d\n",
+ cpu_front_fans[cpu]->name, err);
+ failure_state |= FAILURE_FAN;
+ break;
+ }
+ }
+}
+
+static void cpu_fans_tick_combined(void)
+{
+ s32 temp0, power0, temp1, power1, t_max = 0;
+ s32 temp, power, intake, pump;
+ struct wf_control *pump0, *pump1;
+ struct wf_cpu_pid_state *sp = &cpu_pid[0];
+ int err, cpu;
+
+ DBG_LOTS("* cpu fans_tick_combined()\n");
+
+ /* Read current speed from cpu 0 */
+ wf_control_get(cpu_rear_fans[0], &sp->target);
+
+ DBG_LOTS(" CPUs: cur_target = %d RPM\n", sp->target);
+
+ /* Read values for both CPUs */
+ err = read_one_cpu_vals(0, &temp0, &power0);
+ if (err) {
+ failure_state |= FAILURE_SENSOR;
+ cpu_max_all_fans();
+ return;
+ }
+ err = read_one_cpu_vals(1, &temp1, &power1);
+ if (err) {
+ failure_state |= FAILURE_SENSOR;
+ cpu_max_all_fans();
+ return;
+ }
+
+ /* Keep track of highest temp */
+ t_max = max(t_max, max(temp0, temp1));
+
+ /* Handle possible overtemps */
+ if (cpu_check_overtemp(t_max))
+ return;
+
+ /* Use the max temp & power of both */
+ temp = max(temp0, temp1);
+ power = max(power0, power1);
+
+ /* Run PID */
+ wf_cpu_pid_run(sp, power, temp);
+
+ /* Scale result for intake fan */
+ intake = (sp->target * CPU_INTAKE_SCALE) >> 16;
+
+ /* Same deal with pump speed */
+ pump0 = cpu_pumps[0];
+ pump1 = cpu_pumps[1];
+ if (!pump0) {
+ pump0 = pump1;
+ pump1 = NULL;
+ }
+ pump = (sp->target * wf_control_get_max(pump0)) /
+ cpu_mpu_data[0]->rmaxn_exhaust_fan;
+
+ DBG_LOTS(" CPUs: target = %d RPM\n", sp->target);
+ DBG_LOTS(" CPUs: intake = %d RPM\n", intake);
+ DBG_LOTS(" CPUs: pump = %d RPM\n", pump);
+
+ for (cpu = 0; cpu < nr_chips; cpu++) {
+ err = wf_control_set(cpu_rear_fans[cpu], sp->target);
+ if (err) {
+ pr_warning("wf_pm72: Fan %s reports error %d\n",
+ cpu_rear_fans[cpu]->name, err);
+ failure_state |= FAILURE_FAN;
+ }
+ err = wf_control_set(cpu_front_fans[cpu], intake);
+ if (err) {
+ pr_warning("wf_pm72: Fan %s reports error %d\n",
+ cpu_front_fans[cpu]->name, err);
+ failure_state |= FAILURE_FAN;
+ }
+ err = 0;
+ if (cpu_pumps[cpu])
+ err = wf_control_set(cpu_pumps[cpu], pump);
+ if (err) {
+ pr_warning("wf_pm72: Pump %s reports error %d\n",
+ cpu_pumps[cpu]->name, err);
+ failure_state |= FAILURE_FAN;
+ }
+ }
+}
+
+/* Implementation... */
+static int cpu_setup_pid(int cpu)
+{
+ struct wf_cpu_pid_param pid;
+ const struct mpu_data *mpu = cpu_mpu_data[cpu];
+ s32 tmax, ttarget, ptarget;
+ int fmin, fmax, hsize;
+
+ /* Get PID params from the appropriate MPU EEPROM */
+ tmax = mpu->tmax << 16;
+ ttarget = mpu->ttarget << 16;
+ ptarget = ((s32)(mpu->pmaxh - mpu->padjmax)) << 16;
+
+ DBG("wf_72: CPU%d ttarget = %d.%03d, tmax = %d.%03d\n",
+ cpu, FIX32TOPRINT(ttarget), FIX32TOPRINT(tmax));
+
+ /* We keep a global tmax for overtemp calculations */
+ if (tmax < cpu_all_tmax)
+ cpu_all_tmax = tmax;
+
+ /* Set PID min/max by using the rear fan min/max */
+ fmin = wf_control_get_min(cpu_rear_fans[cpu]);
+ fmax = wf_control_get_max(cpu_rear_fans[cpu]);
+ DBG("wf_72: CPU%d max RPM range = [%d..%d]\n", cpu, fmin, fmax);
+
+ /* History size */
+ hsize = min_t(int, mpu->tguardband, WF_PID_MAX_HISTORY);
+ DBG("wf_72: CPU%d history size = %d\n", cpu, hsize);
+
+ /* Initialize PID loop */
+ pid.interval = 1; /* seconds */
+ pid.history_len = hsize;
+ pid.gd = mpu->pid_gd;
+ pid.gp = mpu->pid_gp;
+ pid.gr = mpu->pid_gr;
+ pid.tmax = tmax;
+ pid.ttarget = ttarget;
+ pid.pmaxadj = ptarget;
+ pid.min = fmin;
+ pid.max = fmax;
+
+ wf_cpu_pid_init(&cpu_pid[cpu], &pid);
+ cpu_pid[cpu].target = 1000;
+
+ return 0;
+}
+
+/* Backside/U3 fan */
+static struct wf_pid_param backside_u3_param = {
+ .interval = 5,
+ .history_len = 2,
+ .gd = 40 << 20,
+ .gp = 5 << 20,
+ .gr = 0,
+ .itarget = 65 << 16,
+ .additive = 1,
+ .min = 20,
+ .max = 100,
+};
+
+static struct wf_pid_param backside_u3h_param = {
+ .interval = 5,
+ .history_len = 2,
+ .gd = 20 << 20,
+ .gp = 5 << 20,
+ .gr = 0,
+ .itarget = 75 << 16,
+ .additive = 1,
+ .min = 20,
+ .max = 100,
+};
+
+static void backside_fan_tick(void)
+{
+ s32 temp;
+ int speed;
+ int err;
+
+ if (!backside_fan || !backside_temp || !backside_tick)
+ return;
+ if (--backside_tick > 0)
+ return;
+ backside_tick = backside_pid.param.interval;
+
+ DBG_LOTS("* backside fans tick\n");
+
+ /* Update fan speed from actual fans */
+ err = wf_control_get(backside_fan, &speed);
+ if (!err)
+ backside_pid.target = speed;
+
+ err = wf_sensor_get(backside_temp, &temp);
+ if (err) {
+ printk(KERN_WARNING "windfarm: U4 temp sensor error %d\n",
+ err);
+ failure_state |= FAILURE_SENSOR;
+ wf_control_set_max(backside_fan);
+ return;
+ }
+ speed = wf_pid_run(&backside_pid, temp);
+
+ DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n",
+ FIX32TOPRINT(temp), speed);
+
+ err = wf_control_set(backside_fan, speed);
+ if (err) {
+ printk(KERN_WARNING "windfarm: backside fan error %d\n", err);
+ failure_state |= FAILURE_FAN;
+ }
+}
+
+static void backside_setup_pid(void)
+{
+ /* first time initialize things */
+ s32 fmin = wf_control_get_min(backside_fan);
+ s32 fmax = wf_control_get_max(backside_fan);
+ struct wf_pid_param param;
+ struct device_node *u3;
+ int u3h = 1; /* conservative by default */
+
+ u3 = of_find_node_by_path("/u3@0,f8000000");
+ if (u3 != NULL) {
+ const u32 *vers = of_get_property(u3, "device-rev", NULL);
+ if (vers)
+ if (((*vers) & 0x3f) < 0x34)
+ u3h = 0;
+ of_node_put(u3);
+ }
+
+ param = u3h ? backside_u3h_param : backside_u3_param;
+
+ param.min = max(param.min, fmin);
+ param.max = min(param.max, fmax);
+ wf_pid_init(&backside_pid, &param);
+ backside_tick = 1;
+
+ pr_info("wf_pm72: Backside control loop started.\n");
+}
+
+/* Drive bay fan */
+static const struct wf_pid_param drives_param = {
+ .interval = 5,
+ .history_len = 2,
+ .gd = 30 << 20,
+ .gp = 5 << 20,
+ .gr = 0,
+ .itarget = 40 << 16,
+ .additive = 1,
+ .min = 300,
+ .max = 4000,
+};
+
+static void drives_fan_tick(void)
+{
+ s32 temp;
+ int speed;
+ int err;
+
+ if (!drives_fan || !drives_temp || !drives_tick)
+ return;
+ if (--drives_tick > 0)
+ return;
+ drives_tick = drives_pid.param.interval;
+
+ DBG_LOTS("* drives fans tick\n");
+
+ /* Update fan speed from actual fans */
+ err = wf_control_get(drives_fan, &speed);
+ if (!err)
+ drives_pid.target = speed;
+
+ err = wf_sensor_get(drives_temp, &temp);
+ if (err) {
+ pr_warning("wf_pm72: drive bay temp sensor error %d\n", err);
+ failure_state |= FAILURE_SENSOR;
+ wf_control_set_max(drives_fan);
+ return;
+ }
+ speed = wf_pid_run(&drives_pid, temp);
+
+ DBG_LOTS("drives PID temp=%d.%.3d speed=%d\n",
+ FIX32TOPRINT(temp), speed);
+
+ err = wf_control_set(drives_fan, speed);
+ if (err) {
+ printk(KERN_WARNING "windfarm: drive bay fan error %d\n", err);
+ failure_state |= FAILURE_FAN;
+ }
+}
+
+static void drives_setup_pid(void)
+{
+ /* first time initialize things */
+ s32 fmin = wf_control_get_min(drives_fan);
+ s32 fmax = wf_control_get_max(drives_fan);
+ struct wf_pid_param param = drives_param;
+
+ param.min = max(param.min, fmin);
+ param.max = min(param.max, fmax);
+ wf_pid_init(&drives_pid, &param);
+ drives_tick = 1;
+
+ pr_info("wf_pm72: Drive bay control loop started.\n");
+}
+
+static void set_fail_state(void)
+{
+ cpu_max_all_fans();
+
+ if (backside_fan)
+ wf_control_set_max(backside_fan);
+ if (slots_fan)
+ wf_control_set_max(slots_fan);
+ if (drives_fan)
+ wf_control_set_max(drives_fan);
+}
+
+static void pm72_tick(void)
+{
+ int i, last_failure;
+
+ if (!started) {
+ started = 1;
+ printk(KERN_INFO "windfarm: CPUs control loops started.\n");
+ for (i = 0; i < nr_chips; ++i) {
+ if (cpu_setup_pid(i) < 0) {
+ failure_state = FAILURE_PERM;
+ set_fail_state();
+ break;
+ }
+ }
+ DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax));
+
+ backside_setup_pid();
+ drives_setup_pid();
+
+ /*
+ * We don't have the right stuff to drive the PCI fan
+ * so we fix it to a default value
+ */
+ wf_control_set(slots_fan, SLOTS_FAN_DEFAULT_PWM);
+
+#ifdef HACKED_OVERTEMP
+ cpu_all_tmax = 60 << 16;
+#endif
+ }
+
+ /* Permanent failure, bail out */
+ if (failure_state & FAILURE_PERM)
+ return;
+
+ /*
+ * Clear all failure bits except low overtemp which will be eventually
+ * cleared by the control loop itself
+ */
+ last_failure = failure_state;
+ failure_state &= FAILURE_LOW_OVERTEMP;
+ if (cpu_pid_combined)
+ cpu_fans_tick_combined();
+ else
+ cpu_fans_tick_split();
+ backside_fan_tick();
+ drives_fan_tick();
+
+ DBG_LOTS(" last_failure: 0x%x, failure_state: %x\n",
+ last_failure, failure_state);
+
+ /* Check for failures. Any failure causes cpufreq clamping */
+ if (failure_state && last_failure == 0 && cpufreq_clamp)
+ wf_control_set_max(cpufreq_clamp);
+ if (failure_state == 0 && last_failure && cpufreq_clamp)
+ wf_control_set_min(cpufreq_clamp);
+
+ /* That's it for now, we might want to deal with other failures
+ * differently in the future though
+ */
+}
+
+static void pm72_new_control(struct wf_control *ct)
+{
+ bool all_controls;
+ bool had_pump = cpu_pumps[0] || cpu_pumps[1];
+
+ if (!strcmp(ct->name, "cpu-front-fan-0"))
+ cpu_front_fans[0] = ct;
+ else if (!strcmp(ct->name, "cpu-front-fan-1"))
+ cpu_front_fans[1] = ct;
+ else if (!strcmp(ct->name, "cpu-rear-fan-0"))
+ cpu_rear_fans[0] = ct;
+ else if (!strcmp(ct->name, "cpu-rear-fan-1"))
+ cpu_rear_fans[1] = ct;
+ else if (!strcmp(ct->name, "cpu-pump-0"))
+ cpu_pumps[0] = ct;
+ else if (!strcmp(ct->name, "cpu-pump-1"))
+ cpu_pumps[1] = ct;
+ else if (!strcmp(ct->name, "backside-fan"))
+ backside_fan = ct;
+ else if (!strcmp(ct->name, "slots-fan"))
+ slots_fan = ct;
+ else if (!strcmp(ct->name, "drive-bay-fan"))
+ drives_fan = ct;
+ else if (!strcmp(ct->name, "cpufreq-clamp"))
+ cpufreq_clamp = ct;
+
+ all_controls =
+ cpu_front_fans[0] &&
+ cpu_rear_fans[0] &&
+ backside_fan &&
+ slots_fan &&
+ drives_fan;
+ if (nr_chips > 1)
+ all_controls &=
+ cpu_front_fans[1] &&
+ cpu_rear_fans[1];
+ have_all_controls = all_controls;
+
+ if ((cpu_pumps[0] || cpu_pumps[1]) && !had_pump) {
+ pr_info("wf_pm72: Liquid cooling pump(s) detected,"
+ " using new algorithm !\n");
+ cpu_pid_combined = true;
+ }
+}
+
+
+static void pm72_new_sensor(struct wf_sensor *sr)
+{
+ bool all_sensors;
+
+ if (!strcmp(sr->name, "cpu-diode-temp-0"))
+ sens_cpu_temp[0] = sr;
+ else if (!strcmp(sr->name, "cpu-diode-temp-1"))
+ sens_cpu_temp[1] = sr;
+ else if (!strcmp(sr->name, "cpu-voltage-0"))
+ sens_cpu_volts[0] = sr;
+ else if (!strcmp(sr->name, "cpu-voltage-1"))
+ sens_cpu_volts[1] = sr;
+ else if (!strcmp(sr->name, "cpu-current-0"))
+ sens_cpu_amps[0] = sr;
+ else if (!strcmp(sr->name, "cpu-current-1"))
+ sens_cpu_amps[1] = sr;
+ else if (!strcmp(sr->name, "backside-temp"))
+ backside_temp = sr;
+ else if (!strcmp(sr->name, "hd-temp"))
+ drives_temp = sr;
+
+ all_sensors =
+ sens_cpu_temp[0] &&
+ sens_cpu_volts[0] &&
+ sens_cpu_amps[0] &&
+ backside_temp &&
+ drives_temp;
+ if (nr_chips > 1)
+ all_sensors &=
+ sens_cpu_temp[1] &&
+ sens_cpu_volts[1] &&
+ sens_cpu_amps[1];
+
+ have_all_sensors = all_sensors;
+}
+
+static int pm72_wf_notify(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ switch (event) {
+ case WF_EVENT_NEW_SENSOR:
+ pm72_new_sensor(data);
+ break;
+ case WF_EVENT_NEW_CONTROL:
+ pm72_new_control(data);
+ break;
+ case WF_EVENT_TICK:
+ if (have_all_controls && have_all_sensors)
+ pm72_tick();
+ }
+ return 0;
+}
+
+static struct notifier_block pm72_events = {
+ .notifier_call = pm72_wf_notify,
+};
+
+static int wf_pm72_probe(struct platform_device *dev)
+{
+ wf_register_client(&pm72_events);
+ return 0;
+}
+
+static int wf_pm72_remove(struct platform_device *dev)
+{
+ wf_unregister_client(&pm72_events);
+
+ /* should release all sensors and controls */
+ return 0;
+}
+
+static struct platform_driver wf_pm72_driver = {
+ .probe = wf_pm72_probe,
+ .remove = wf_pm72_remove,
+ .driver = {
+ .name = "windfarm",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init wf_pm72_init(void)
+{
+ struct device_node *cpu;
+ int i;
+
+ if (!of_machine_is_compatible("PowerMac7,2") &&
+ !of_machine_is_compatible("PowerMac7,3"))
+ return -ENODEV;
+
+ /* Count the number of CPU cores */
+ nr_chips = 0;
+ for_each_node_by_type(cpu, "cpu")
+ ++nr_chips;
+ if (nr_chips > NR_CHIPS)
+ nr_chips = NR_CHIPS;
+
+ pr_info("windfarm: Initializing for desktop G5 with %d chips\n",
+ nr_chips);
+
+ /* Get MPU data for each CPU */
+ for (i = 0; i < nr_chips; i++) {
+ cpu_mpu_data[i] = wf_get_mpu(i);
+ if (!cpu_mpu_data[i]) {
+ pr_err("wf_pm72: Failed to find MPU data for CPU %d\n", i);
+ return -ENXIO;
+ }
+ }
+
+#ifdef MODULE
+ request_module("windfarm_fcu_controls");
+ request_module("windfarm_lm75_sensor");
+ request_module("windfarm_ad7417_sensor");
+ request_module("windfarm_max6690_sensor");
+ request_module("windfarm_cpufreq_clamp");
+#endif /* MODULE */
+
+ platform_driver_register(&wf_pm72_driver);
+ return 0;
+}
+
+static void __exit wf_pm72_exit(void)
+{
+ platform_driver_unregister(&wf_pm72_driver);
+}
+
+module_init(wf_pm72_init);
+module_exit(wf_pm72_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("Thermal control for AGP PowerMac G5s");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:windfarm");
diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c
index 2ff546e4c92..2a5e1b15b1d 100644
--- a/drivers/macintosh/windfarm_pm81.c
+++ b/drivers/macintosh/windfarm_pm81.c
@@ -107,7 +107,6 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/smu.h>
@@ -131,8 +130,6 @@
static int wf_smu_mach_model; /* machine model id */
-static struct device *wf_smu_dev;
-
/* Controls & sensors */
static struct wf_sensor *sensor_cpu_power;
static struct wf_sensor *sensor_cpu_temp;
@@ -152,6 +149,7 @@ static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok, wf_smu_started;
static unsigned int wf_smu_failure_state;
static int wf_smu_readjust, wf_smu_skipping;
+static bool wf_smu_overtemp;
/*
* ****** System Fans Control Loop ******
@@ -190,7 +188,7 @@ struct wf_smu_sys_fans_state {
};
/*
- * Configs for SMU Sytem Fan control loop
+ * Configs for SMU System Fan control loop
*/
static struct wf_smu_sys_fans_param wf_smu_sys_all_params[] = {
/* Model ID 2 */
@@ -305,13 +303,13 @@ static void wf_smu_create_sys_fans(void)
pid_param.interval = WF_SMU_SYS_FANS_INTERVAL;
pid_param.history_len = WF_SMU_SYS_FANS_HISTORY_SIZE;
pid_param.itarget = param->itarget;
- pid_param.min = fan_system->ops->get_min(fan_system);
- pid_param.max = fan_system->ops->get_max(fan_system);
+ pid_param.min = wf_control_get_min(fan_system);
+ pid_param.max = wf_control_get_max(fan_system);
if (fan_hd) {
pid_param.min =
- max(pid_param.min,fan_hd->ops->get_min(fan_hd));
+ max(pid_param.min, wf_control_get_min(fan_hd));
pid_param.max =
- min(pid_param.max,fan_hd->ops->get_max(fan_hd));
+ min(pid_param.max, wf_control_get_max(fan_hd));
}
wf_pid_init(&wf_smu_sys_fans->pid, &pid_param);
@@ -340,7 +338,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st)
}
st->ticks = WF_SMU_SYS_FANS_INTERVAL;
- rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp);
+ rc = wf_sensor_get(sensor_hd_temp, &temp);
if (rc) {
printk(KERN_WARNING "windfarm: HD temp sensor error %d\n",
rc);
@@ -376,7 +374,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st)
st->hd_setpoint = new_setpoint;
readjust:
if (fan_system && wf_smu_failure_state == 0) {
- rc = fan_system->ops->set_value(fan_system, st->sys_setpoint);
+ rc = wf_control_set(fan_system, st->sys_setpoint);
if (rc) {
printk(KERN_WARNING "windfarm: Sys fan error %d\n",
rc);
@@ -384,7 +382,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st)
}
}
if (fan_hd && wf_smu_failure_state == 0) {
- rc = fan_hd->ops->set_value(fan_hd, st->hd_setpoint);
+ rc = wf_control_set(fan_hd, st->hd_setpoint);
if (rc) {
printk(KERN_WARNING "windfarm: HD fan error %d\n",
rc);
@@ -450,8 +448,8 @@ static void wf_smu_create_cpu_fans(void)
pid_param.ttarget = tmax - tdelta;
pid_param.pmaxadj = maxpow - powadj;
- pid_param.min = fan_cpu_main->ops->get_min(fan_cpu_main);
- pid_param.max = fan_cpu_main->ops->get_max(fan_cpu_main);
+ pid_param.min = wf_control_get_min(fan_cpu_main);
+ pid_param.max = wf_control_get_max(fan_cpu_main);
wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param);
@@ -484,7 +482,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)
}
st->ticks = WF_SMU_CPU_FANS_INTERVAL;
- rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);
+ rc = wf_sensor_get(sensor_cpu_temp, &temp);
if (rc) {
printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n",
rc);
@@ -492,7 +490,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)
return;
}
- rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);
+ rc = wf_sensor_get(sensor_cpu_power, &power);
if (rc) {
printk(KERN_WARNING "windfarm: CPU power sensor error %d\n",
rc);
@@ -528,8 +526,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)
st->cpu_setpoint = new_setpoint;
readjust:
if (fan_cpu_main && wf_smu_failure_state == 0) {
- rc = fan_cpu_main->ops->set_value(fan_cpu_main,
- st->cpu_setpoint);
+ rc = wf_control_set(fan_cpu_main, st->cpu_setpoint);
if (rc) {
printk(KERN_WARNING "windfarm: CPU main fan"
" error %d\n", rc);
@@ -597,6 +594,7 @@ static void wf_smu_tick(void)
if (new_failure & FAILURE_OVERTEMP) {
wf_set_overtemp();
wf_smu_skipping = 2;
+ wf_smu_overtemp = true;
}
/* We only clear the overtemp condition if overtemp is cleared
@@ -605,8 +603,10 @@ static void wf_smu_tick(void)
* the control loop levels, but we don't want to keep it clear
* here in this case
*/
- if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
+ if (!wf_smu_failure_state && wf_smu_overtemp) {
wf_clear_overtemp();
+ wf_smu_overtemp = false;
+ }
}
static void wf_smu_new_control(struct wf_control *ct)
@@ -717,16 +717,14 @@ static int wf_init_pm(void)
return 0;
}
-static int wf_smu_probe(struct device *ddev)
+static int wf_smu_probe(struct platform_device *ddev)
{
- wf_smu_dev = ddev;
-
wf_register_client(&wf_smu_events);
return 0;
}
-static int wf_smu_remove(struct device *ddev)
+static int wf_smu_remove(struct platform_device *ddev)
{
wf_unregister_client(&wf_smu_events);
@@ -761,21 +759,19 @@ static int wf_smu_remove(struct device *ddev)
wf_put_control(cpufreq_clamp);
/* Destroy control loops state structures */
- if (wf_smu_sys_fans)
- kfree(wf_smu_sys_fans);
- if (wf_smu_cpu_fans)
- kfree(wf_smu_cpu_fans);
-
- wf_smu_dev = NULL;
+ kfree(wf_smu_sys_fans);
+ kfree(wf_smu_cpu_fans);
return 0;
}
-static struct device_driver wf_smu_driver = {
- .name = "windfarm",
- .bus = &platform_bus_type,
+static struct platform_driver wf_smu_driver = {
.probe = wf_smu_probe,
.remove = wf_smu_remove,
+ .driver = {
+ .name = "windfarm",
+ .owner = THIS_MODULE,
+ },
};
@@ -783,8 +779,8 @@ static int __init wf_smu_init(void)
{
int rc = -ENODEV;
- if (machine_is_compatible("PowerMac8,1") ||
- machine_is_compatible("PowerMac8,2"))
+ if (of_machine_is_compatible("PowerMac8,1") ||
+ of_machine_is_compatible("PowerMac8,2"))
rc = wf_init_pm();
if (rc == 0) {
@@ -792,9 +788,10 @@ static int __init wf_smu_init(void)
request_module("windfarm_smu_controls");
request_module("windfarm_smu_sensors");
request_module("windfarm_lm75_sensor");
+ request_module("windfarm_cpufreq_clamp");
#endif /* MODULE */
- driver_register(&wf_smu_driver);
+ platform_driver_register(&wf_smu_driver);
}
return rc;
@@ -803,7 +800,7 @@ static int __init wf_smu_init(void)
static void __exit wf_smu_exit(void)
{
- driver_unregister(&wf_smu_driver);
+ platform_driver_unregister(&wf_smu_driver);
}
@@ -813,4 +810,4 @@ module_exit(wf_smu_exit);
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("Thermal control logic for iMac G5");
MODULE_LICENSE("GPL");
-
+MODULE_ALIAS("platform:windfarm");
diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c
index 59e9ffe37c3..a8ac66cd3b1 100644
--- a/drivers/macintosh/windfarm_pm91.c
+++ b/drivers/macintosh/windfarm_pm91.c
@@ -41,7 +41,6 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/smu.h>
@@ -63,8 +62,6 @@
*/
#undef HACKED_OVERTEMP
-static struct device *wf_smu_dev;
-
/* Controls & sensors */
static struct wf_sensor *sensor_cpu_power;
static struct wf_sensor *sensor_cpu_temp;
@@ -79,6 +76,7 @@ static struct wf_control *cpufreq_clamp;
/* Set to kick the control loop into life */
static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok, wf_smu_started;
+static bool wf_smu_overtemp;
/* Failure handling.. could be nicer */
#define FAILURE_FAN 0x01
@@ -195,8 +193,8 @@ static void wf_smu_create_cpu_fans(void)
pid_param.ttarget = tmax - tdelta;
pid_param.pmaxadj = maxpow - powadj;
- pid_param.min = fan_cpu_main->ops->get_min(fan_cpu_main);
- pid_param.max = fan_cpu_main->ops->get_max(fan_cpu_main);
+ pid_param.min = wf_control_get_min(fan_cpu_main);
+ pid_param.max = wf_control_get_max(fan_cpu_main);
wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param);
@@ -229,7 +227,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)
}
st->ticks = WF_SMU_CPU_FANS_INTERVAL;
- rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);
+ rc = wf_sensor_get(sensor_cpu_temp, &temp);
if (rc) {
printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n",
rc);
@@ -237,7 +235,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)
return;
}
- rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);
+ rc = wf_sensor_get(sensor_cpu_power, &power);
if (rc) {
printk(KERN_WARNING "windfarm: CPU power sensor error %d\n",
rc);
@@ -264,8 +262,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)
st->cpu_setpoint = new_setpoint;
readjust:
if (fan_cpu_main && wf_smu_failure_state == 0) {
- rc = fan_cpu_main->ops->set_value(fan_cpu_main,
- st->cpu_setpoint);
+ rc = wf_control_set(fan_cpu_main, st->cpu_setpoint);
if (rc) {
printk(KERN_WARNING "windfarm: CPU main fan"
" error %d\n", rc);
@@ -273,8 +270,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)
}
}
if (fan_cpu_second && wf_smu_failure_state == 0) {
- rc = fan_cpu_second->ops->set_value(fan_cpu_second,
- st->cpu_setpoint);
+ rc = wf_control_set(fan_cpu_second, st->cpu_setpoint);
if (rc) {
printk(KERN_WARNING "windfarm: CPU second fan"
" error %d\n", rc);
@@ -282,8 +278,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)
}
}
if (fan_cpu_third && wf_smu_failure_state == 0) {
- rc = fan_cpu_main->ops->set_value(fan_cpu_third,
- st->cpu_setpoint);
+ rc = wf_control_set(fan_cpu_third, st->cpu_setpoint);
if (rc) {
printk(KERN_WARNING "windfarm: CPU third fan"
" error %d\n", rc);
@@ -315,8 +310,8 @@ static void wf_smu_create_drive_fans(void)
/* Fill PID params */
param.additive = (fan_hd->type == WF_CONTROL_RPM_FAN);
- param.min = fan_hd->ops->get_min(fan_hd);
- param.max = fan_hd->ops->get_max(fan_hd);
+ param.min = wf_control_get_min(fan_hd);
+ param.max = wf_control_get_max(fan_hd);
wf_pid_init(&wf_smu_drive_fans->pid, &param);
DBG("wf: Drive Fan control initialized.\n");
@@ -341,7 +336,7 @@ static void wf_smu_drive_fans_tick(struct wf_smu_drive_fans_state *st)
}
st->ticks = st->pid.param.interval;
- rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp);
+ rc = wf_sensor_get(sensor_hd_temp, &temp);
if (rc) {
printk(KERN_WARNING "windfarm: HD temp sensor error %d\n",
rc);
@@ -364,7 +359,7 @@ static void wf_smu_drive_fans_tick(struct wf_smu_drive_fans_state *st)
st->setpoint = new_setpoint;
readjust:
if (fan_hd && wf_smu_failure_state == 0) {
- rc = fan_hd->ops->set_value(fan_hd, st->setpoint);
+ rc = wf_control_set(fan_hd, st->setpoint);
if (rc) {
printk(KERN_WARNING "windfarm: HD fan error %d\n",
rc);
@@ -396,8 +391,8 @@ static void wf_smu_create_slots_fans(void)
/* Fill PID params */
param.additive = (fan_slots->type == WF_CONTROL_RPM_FAN);
- param.min = fan_slots->ops->get_min(fan_slots);
- param.max = fan_slots->ops->get_max(fan_slots);
+ param.min = wf_control_get_min(fan_slots);
+ param.max = wf_control_get_max(fan_slots);
wf_pid_init(&wf_smu_slots_fans->pid, &param);
DBG("wf: Slots Fan control initialized.\n");
@@ -422,7 +417,7 @@ static void wf_smu_slots_fans_tick(struct wf_smu_slots_fans_state *st)
}
st->ticks = st->pid.param.interval;
- rc = sensor_slots_power->ops->get_value(sensor_slots_power, &power);
+ rc = wf_sensor_get(sensor_slots_power, &power);
if (rc) {
printk(KERN_WARNING "windfarm: Slots power sensor error %d\n",
rc);
@@ -447,7 +442,7 @@ static void wf_smu_slots_fans_tick(struct wf_smu_slots_fans_state *st)
st->setpoint = new_setpoint;
readjust:
if (fan_slots && wf_smu_failure_state == 0) {
- rc = fan_slots->ops->set_value(fan_slots, st->setpoint);
+ rc = wf_control_set(fan_slots, st->setpoint);
if (rc) {
printk(KERN_WARNING "windfarm: Slots fan error %d\n",
rc);
@@ -523,6 +518,7 @@ static void wf_smu_tick(void)
if (new_failure & FAILURE_OVERTEMP) {
wf_set_overtemp();
wf_smu_skipping = 2;
+ wf_smu_overtemp = true;
}
/* We only clear the overtemp condition if overtemp is cleared
@@ -531,8 +527,10 @@ static void wf_smu_tick(void)
* the control loop levels, but we don't want to keep it clear
* here in this case
*/
- if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
+ if (!wf_smu_failure_state && wf_smu_overtemp) {
wf_clear_overtemp();
+ wf_smu_overtemp = false;
+ }
}
@@ -641,16 +639,14 @@ static int wf_init_pm(void)
return 0;
}
-static int wf_smu_probe(struct device *ddev)
+static int wf_smu_probe(struct platform_device *ddev)
{
- wf_smu_dev = ddev;
-
wf_register_client(&wf_smu_events);
return 0;
}
-static int wf_smu_remove(struct device *ddev)
+static int wf_smu_remove(struct platform_device *ddev)
{
wf_unregister_client(&wf_smu_events);
@@ -691,23 +687,20 @@ static int wf_smu_remove(struct device *ddev)
wf_put_control(cpufreq_clamp);
/* Destroy control loops state structures */
- if (wf_smu_slots_fans)
- kfree(wf_smu_cpu_fans);
- if (wf_smu_drive_fans)
- kfree(wf_smu_cpu_fans);
- if (wf_smu_cpu_fans)
- kfree(wf_smu_cpu_fans);
-
- wf_smu_dev = NULL;
+ kfree(wf_smu_slots_fans);
+ kfree(wf_smu_drive_fans);
+ kfree(wf_smu_cpu_fans);
return 0;
}
-static struct device_driver wf_smu_driver = {
- .name = "windfarm",
- .bus = &platform_bus_type,
+static struct platform_driver wf_smu_driver = {
.probe = wf_smu_probe,
.remove = wf_smu_remove,
+ .driver = {
+ .name = "windfarm",
+ .owner = THIS_MODULE,
+ },
};
@@ -715,7 +708,7 @@ static int __init wf_smu_init(void)
{
int rc = -ENODEV;
- if (machine_is_compatible("PowerMac9,1"))
+ if (of_machine_is_compatible("PowerMac9,1"))
rc = wf_init_pm();
if (rc == 0) {
@@ -723,9 +716,10 @@ static int __init wf_smu_init(void)
request_module("windfarm_smu_controls");
request_module("windfarm_smu_sensors");
request_module("windfarm_lm75_sensor");
+ request_module("windfarm_cpufreq_clamp");
#endif /* MODULE */
- driver_register(&wf_smu_driver);
+ platform_driver_register(&wf_smu_driver);
}
return rc;
@@ -734,7 +728,7 @@ static int __init wf_smu_init(void)
static void __exit wf_smu_exit(void)
{
- driver_unregister(&wf_smu_driver);
+ platform_driver_unregister(&wf_smu_driver);
}
@@ -745,3 +739,4 @@ MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("Thermal control logic for PowerMac9,1");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:windfarm");
diff --git a/drivers/macintosh/windfarm_rm31.c b/drivers/macintosh/windfarm_rm31.c
new file mode 100644
index 00000000000..82fc86a90c1
--- /dev/null
+++ b/drivers/macintosh/windfarm_rm31.c
@@ -0,0 +1,740 @@
+/*
+ * Windfarm PowerMac thermal control.
+ * Control loops for RackMack3,1 (Xserve G5)
+ *
+ * Copyright (C) 2012 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Use and redistribute under the terms of the GNU GPL v2.
+ */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <asm/prom.h>
+#include <asm/smu.h>
+
+#include "windfarm.h"
+#include "windfarm_pid.h"
+#include "windfarm_mpu.h"
+
+#define VERSION "1.0"
+
+#undef DEBUG
+#undef LOTSA_DEBUG
+
+#ifdef DEBUG
+#define DBG(args...) printk(args)
+#else
+#define DBG(args...) do { } while(0)
+#endif
+
+#ifdef LOTSA_DEBUG
+#define DBG_LOTS(args...) printk(args)
+#else
+#define DBG_LOTS(args...) do { } while(0)
+#endif
+
+/* define this to force CPU overtemp to 60 degree, useful for testing
+ * the overtemp code
+ */
+#undef HACKED_OVERTEMP
+
+/* We currently only handle 2 chips */
+#define NR_CHIPS 2
+#define NR_CPU_FANS 3 * NR_CHIPS
+
+/* Controls and sensors */
+static struct wf_sensor *sens_cpu_temp[NR_CHIPS];
+static struct wf_sensor *sens_cpu_volts[NR_CHIPS];
+static struct wf_sensor *sens_cpu_amps[NR_CHIPS];
+static struct wf_sensor *backside_temp;
+static struct wf_sensor *slots_temp;
+static struct wf_sensor *dimms_temp;
+
+static struct wf_control *cpu_fans[NR_CHIPS][3];
+static struct wf_control *backside_fan;
+static struct wf_control *slots_fan;
+static struct wf_control *cpufreq_clamp;
+
+/* We keep a temperature history for average calculation of 180s */
+#define CPU_TEMP_HIST_SIZE 180
+
+/* PID loop state */
+static const struct mpu_data *cpu_mpu_data[NR_CHIPS];
+static struct wf_cpu_pid_state cpu_pid[NR_CHIPS];
+static u32 cpu_thist[CPU_TEMP_HIST_SIZE];
+static int cpu_thist_pt;
+static s64 cpu_thist_total;
+static s32 cpu_all_tmax = 100 << 16;
+static struct wf_pid_state backside_pid;
+static int backside_tick;
+static struct wf_pid_state slots_pid;
+static int slots_tick;
+static int slots_speed;
+static struct wf_pid_state dimms_pid;
+static int dimms_output_clamp;
+
+static int nr_chips;
+static bool have_all_controls;
+static bool have_all_sensors;
+static bool started;
+
+static int failure_state;
+#define FAILURE_SENSOR 1
+#define FAILURE_FAN 2
+#define FAILURE_PERM 4
+#define FAILURE_LOW_OVERTEMP 8
+#define FAILURE_HIGH_OVERTEMP 16
+
+/* Overtemp values */
+#define LOW_OVER_AVERAGE 0
+#define LOW_OVER_IMMEDIATE (10 << 16)
+#define LOW_OVER_CLEAR ((-10) << 16)
+#define HIGH_OVER_IMMEDIATE (14 << 16)
+#define HIGH_OVER_AVERAGE (10 << 16)
+#define HIGH_OVER_IMMEDIATE (14 << 16)
+
+
+static void cpu_max_all_fans(void)
+{
+ int i;
+
+ /* We max all CPU fans in case of a sensor error. We also do the
+ * cpufreq clamping now, even if it's supposedly done later by the
+ * generic code anyway, we do it earlier here to react faster
+ */
+ if (cpufreq_clamp)
+ wf_control_set_max(cpufreq_clamp);
+ for (i = 0; i < nr_chips; i++) {
+ if (cpu_fans[i][0])
+ wf_control_set_max(cpu_fans[i][0]);
+ if (cpu_fans[i][1])
+ wf_control_set_max(cpu_fans[i][1]);
+ if (cpu_fans[i][2])
+ wf_control_set_max(cpu_fans[i][2]);
+ }
+}
+
+static int cpu_check_overtemp(s32 temp)
+{
+ int new_state = 0;
+ s32 t_avg, t_old;
+ static bool first = true;
+
+ /* First check for immediate overtemps */
+ if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) {
+ new_state |= FAILURE_LOW_OVERTEMP;
+ if ((failure_state & FAILURE_LOW_OVERTEMP) == 0)
+ printk(KERN_ERR "windfarm: Overtemp due to immediate CPU"
+ " temperature !\n");
+ }
+ if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) {
+ new_state |= FAILURE_HIGH_OVERTEMP;
+ if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0)
+ printk(KERN_ERR "windfarm: Critical overtemp due to"
+ " immediate CPU temperature !\n");
+ }
+
+ /*
+ * The first time around, initialize the array with the first
+ * temperature reading
+ */
+ if (first) {
+ int i;
+
+ cpu_thist_total = 0;
+ for (i = 0; i < CPU_TEMP_HIST_SIZE; i++) {
+ cpu_thist[i] = temp;
+ cpu_thist_total += temp;
+ }
+ first = false;
+ }
+
+ /*
+ * We calculate a history of max temperatures and use that for the
+ * overtemp management
+ */
+ t_old = cpu_thist[cpu_thist_pt];
+ cpu_thist[cpu_thist_pt] = temp;
+ cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE;
+ cpu_thist_total -= t_old;
+ cpu_thist_total += temp;
+ t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE;
+
+ DBG_LOTS(" t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n",
+ FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp));
+
+ /* Now check for average overtemps */
+ if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) {
+ new_state |= FAILURE_LOW_OVERTEMP;
+ if ((failure_state & FAILURE_LOW_OVERTEMP) == 0)
+ printk(KERN_ERR "windfarm: Overtemp due to average CPU"
+ " temperature !\n");
+ }
+ if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) {
+ new_state |= FAILURE_HIGH_OVERTEMP;
+ if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0)
+ printk(KERN_ERR "windfarm: Critical overtemp due to"
+ " average CPU temperature !\n");
+ }
+
+ /* Now handle overtemp conditions. We don't currently use the windfarm
+ * overtemp handling core as it's not fully suited to the needs of those
+ * new machine. This will be fixed later.
+ */
+ if (new_state) {
+ /* High overtemp -> immediate shutdown */
+ if (new_state & FAILURE_HIGH_OVERTEMP)
+ machine_power_off();
+ if ((failure_state & new_state) != new_state)
+ cpu_max_all_fans();
+ failure_state |= new_state;
+ } else if ((failure_state & FAILURE_LOW_OVERTEMP) &&
+ (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) {
+ printk(KERN_ERR "windfarm: Overtemp condition cleared !\n");
+ failure_state &= ~FAILURE_LOW_OVERTEMP;
+ }
+
+ return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP);
+}
+
+static int read_one_cpu_vals(int cpu, s32 *temp, s32 *power)
+{
+ s32 dtemp, volts, amps;
+ int rc;
+
+ /* Get diode temperature */
+ rc = wf_sensor_get(sens_cpu_temp[cpu], &dtemp);
+ if (rc) {
+ DBG(" CPU%d: temp reading error !\n", cpu);
+ return -EIO;
+ }
+ DBG_LOTS(" CPU%d: temp = %d.%03d\n", cpu, FIX32TOPRINT((dtemp)));
+ *temp = dtemp;
+
+ /* Get voltage */
+ rc = wf_sensor_get(sens_cpu_volts[cpu], &volts);
+ if (rc) {
+ DBG(" CPU%d, volts reading error !\n", cpu);
+ return -EIO;
+ }
+ DBG_LOTS(" CPU%d: volts = %d.%03d\n", cpu, FIX32TOPRINT((volts)));
+
+ /* Get current */
+ rc = wf_sensor_get(sens_cpu_amps[cpu], &amps);
+ if (rc) {
+ DBG(" CPU%d, current reading error !\n", cpu);
+ return -EIO;
+ }
+ DBG_LOTS(" CPU%d: amps = %d.%03d\n", cpu, FIX32TOPRINT((amps)));
+
+ /* Calculate power */
+
+ /* Scale voltage and current raw sensor values according to fixed scales
+ * obtained in Darwin and calculate power from I and V
+ */
+ *power = (((u64)volts) * ((u64)amps)) >> 16;
+
+ DBG_LOTS(" CPU%d: power = %d.%03d\n", cpu, FIX32TOPRINT((*power)));
+
+ return 0;
+
+}
+
+static void cpu_fans_tick(void)
+{
+ int err, cpu, i;
+ s32 speed, temp, power, t_max = 0;
+
+ DBG_LOTS("* cpu fans_tick_split()\n");
+
+ for (cpu = 0; cpu < nr_chips; ++cpu) {
+ struct wf_cpu_pid_state *sp = &cpu_pid[cpu];
+
+ /* Read current speed */
+ wf_control_get(cpu_fans[cpu][0], &sp->target);
+
+ err = read_one_cpu_vals(cpu, &temp, &power);
+ if (err) {
+ failure_state |= FAILURE_SENSOR;
+ cpu_max_all_fans();
+ return;
+ }
+
+ /* Keep track of highest temp */
+ t_max = max(t_max, temp);
+
+ /* Handle possible overtemps */
+ if (cpu_check_overtemp(t_max))
+ return;
+
+ /* Run PID */
+ wf_cpu_pid_run(sp, power, temp);
+
+ DBG_LOTS(" CPU%d: target = %d RPM\n", cpu, sp->target);
+
+ /* Apply DIMMs clamp */
+ speed = max(sp->target, dimms_output_clamp);
+
+ /* Apply result to all cpu fans */
+ for (i = 0; i < 3; i++) {
+ err = wf_control_set(cpu_fans[cpu][i], speed);
+ if (err) {
+ pr_warning("wf_rm31: Fan %s reports error %d\n",
+ cpu_fans[cpu][i]->name, err);
+ failure_state |= FAILURE_FAN;
+ }
+ }
+ }
+}
+
+/* Implementation... */
+static int cpu_setup_pid(int cpu)
+{
+ struct wf_cpu_pid_param pid;
+ const struct mpu_data *mpu = cpu_mpu_data[cpu];
+ s32 tmax, ttarget, ptarget;
+ int fmin, fmax, hsize;
+
+ /* Get PID params from the appropriate MPU EEPROM */
+ tmax = mpu->tmax << 16;
+ ttarget = mpu->ttarget << 16;
+ ptarget = ((s32)(mpu->pmaxh - mpu->padjmax)) << 16;
+
+ DBG("wf_72: CPU%d ttarget = %d.%03d, tmax = %d.%03d\n",
+ cpu, FIX32TOPRINT(ttarget), FIX32TOPRINT(tmax));
+
+ /* We keep a global tmax for overtemp calculations */
+ if (tmax < cpu_all_tmax)
+ cpu_all_tmax = tmax;
+
+ /* Set PID min/max by using the rear fan min/max */
+ fmin = wf_control_get_min(cpu_fans[cpu][0]);
+ fmax = wf_control_get_max(cpu_fans[cpu][0]);
+ DBG("wf_72: CPU%d max RPM range = [%d..%d]\n", cpu, fmin, fmax);
+
+ /* History size */
+ hsize = min_t(int, mpu->tguardband, WF_PID_MAX_HISTORY);
+ DBG("wf_72: CPU%d history size = %d\n", cpu, hsize);
+
+ /* Initialize PID loop */
+ pid.interval = 1; /* seconds */
+ pid.history_len = hsize;
+ pid.gd = mpu->pid_gd;
+ pid.gp = mpu->pid_gp;
+ pid.gr = mpu->pid_gr;
+ pid.tmax = tmax;
+ pid.ttarget = ttarget;
+ pid.pmaxadj = ptarget;
+ pid.min = fmin;
+ pid.max = fmax;
+
+ wf_cpu_pid_init(&cpu_pid[cpu], &pid);
+ cpu_pid[cpu].target = 4000;
+
+ return 0;
+}
+
+/* Backside/U3 fan */
+static struct wf_pid_param backside_param = {
+ .interval = 1,
+ .history_len = 2,
+ .gd = 0x00500000,
+ .gp = 0x0004cccc,
+ .gr = 0,
+ .itarget = 70 << 16,
+ .additive = 0,
+ .min = 20,
+ .max = 100,
+};
+
+/* DIMMs temperature (clamp the backside fan) */
+static struct wf_pid_param dimms_param = {
+ .interval = 1,
+ .history_len = 20,
+ .gd = 0,
+ .gp = 0,
+ .gr = 0x06553600,
+ .itarget = 50 << 16,
+ .additive = 0,
+ .min = 4000,
+ .max = 14000,
+};
+
+static void backside_fan_tick(void)
+{
+ s32 temp, dtemp;
+ int speed, dspeed, fan_min;
+ int err;
+
+ if (!backside_fan || !backside_temp || !dimms_temp || !backside_tick)
+ return;
+ if (--backside_tick > 0)
+ return;
+ backside_tick = backside_pid.param.interval;
+
+ DBG_LOTS("* backside fans tick\n");
+
+ /* Update fan speed from actual fans */
+ err = wf_control_get(backside_fan, &speed);
+ if (!err)
+ backside_pid.target = speed;
+
+ err = wf_sensor_get(backside_temp, &temp);
+ if (err) {
+ printk(KERN_WARNING "windfarm: U3 temp sensor error %d\n",
+ err);
+ failure_state |= FAILURE_SENSOR;
+ wf_control_set_max(backside_fan);
+ return;
+ }
+ speed = wf_pid_run(&backside_pid, temp);
+
+ DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n",
+ FIX32TOPRINT(temp), speed);
+
+ err = wf_sensor_get(dimms_temp, &dtemp);
+ if (err) {
+ printk(KERN_WARNING "windfarm: DIMMs temp sensor error %d\n",
+ err);
+ failure_state |= FAILURE_SENSOR;
+ wf_control_set_max(backside_fan);
+ return;
+ }
+ dspeed = wf_pid_run(&dimms_pid, dtemp);
+ dimms_output_clamp = dspeed;
+
+ fan_min = (dspeed * 100) / 14000;
+ fan_min = max(fan_min, backside_param.min);
+ speed = max(speed, fan_min);
+
+ err = wf_control_set(backside_fan, speed);
+ if (err) {
+ printk(KERN_WARNING "windfarm: backside fan error %d\n", err);
+ failure_state |= FAILURE_FAN;
+ }
+}
+
+static void backside_setup_pid(void)
+{
+ /* first time initialize things */
+ s32 fmin = wf_control_get_min(backside_fan);
+ s32 fmax = wf_control_get_max(backside_fan);
+ struct wf_pid_param param;
+
+ param = backside_param;
+ param.min = max(param.min, fmin);
+ param.max = min(param.max, fmax);
+ wf_pid_init(&backside_pid, &param);
+
+ param = dimms_param;
+ wf_pid_init(&dimms_pid, &param);
+
+ backside_tick = 1;
+
+ pr_info("wf_rm31: Backside control loop started.\n");
+}
+
+/* Slots fan */
+static const struct wf_pid_param slots_param = {
+ .interval = 1,
+ .history_len = 20,
+ .gd = 0,
+ .gp = 0,
+ .gr = 0x00100000,
+ .itarget = 3200000,
+ .additive = 0,
+ .min = 20,
+ .max = 100,
+};
+
+static void slots_fan_tick(void)
+{
+ s32 temp;
+ int speed;
+ int err;
+
+ if (!slots_fan || !slots_temp || !slots_tick)
+ return;
+ if (--slots_tick > 0)
+ return;
+ slots_tick = slots_pid.param.interval;
+
+ DBG_LOTS("* slots fans tick\n");
+
+ err = wf_sensor_get(slots_temp, &temp);
+ if (err) {
+ pr_warning("wf_rm31: slots temp sensor error %d\n", err);
+ failure_state |= FAILURE_SENSOR;
+ wf_control_set_max(slots_fan);
+ return;
+ }
+ speed = wf_pid_run(&slots_pid, temp);
+
+ DBG_LOTS("slots PID temp=%d.%.3d speed=%d\n",
+ FIX32TOPRINT(temp), speed);
+
+ slots_speed = speed;
+ err = wf_control_set(slots_fan, speed);
+ if (err) {
+ printk(KERN_WARNING "windfarm: slots bay fan error %d\n", err);
+ failure_state |= FAILURE_FAN;
+ }
+}
+
+static void slots_setup_pid(void)
+{
+ /* first time initialize things */
+ s32 fmin = wf_control_get_min(slots_fan);
+ s32 fmax = wf_control_get_max(slots_fan);
+ struct wf_pid_param param = slots_param;
+
+ param.min = max(param.min, fmin);
+ param.max = min(param.max, fmax);
+ wf_pid_init(&slots_pid, &param);
+ slots_tick = 1;
+
+ pr_info("wf_rm31: Slots control loop started.\n");
+}
+
+static void set_fail_state(void)
+{
+ cpu_max_all_fans();
+
+ if (backside_fan)
+ wf_control_set_max(backside_fan);
+ if (slots_fan)
+ wf_control_set_max(slots_fan);
+}
+
+static void rm31_tick(void)
+{
+ int i, last_failure;
+
+ if (!started) {
+ started = 1;
+ printk(KERN_INFO "windfarm: CPUs control loops started.\n");
+ for (i = 0; i < nr_chips; ++i) {
+ if (cpu_setup_pid(i) < 0) {
+ failure_state = FAILURE_PERM;
+ set_fail_state();
+ break;
+ }
+ }
+ DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax));
+
+ backside_setup_pid();
+ slots_setup_pid();
+
+#ifdef HACKED_OVERTEMP
+ cpu_all_tmax = 60 << 16;
+#endif
+ }
+
+ /* Permanent failure, bail out */
+ if (failure_state & FAILURE_PERM)
+ return;
+
+ /*
+ * Clear all failure bits except low overtemp which will be eventually
+ * cleared by the control loop itself
+ */
+ last_failure = failure_state;
+ failure_state &= FAILURE_LOW_OVERTEMP;
+ backside_fan_tick();
+ slots_fan_tick();
+
+ /* We do CPUs last because they can be clamped high by
+ * DIMM temperature
+ */
+ cpu_fans_tick();
+
+ DBG_LOTS(" last_failure: 0x%x, failure_state: %x\n",
+ last_failure, failure_state);
+
+ /* Check for failures. Any failure causes cpufreq clamping */
+ if (failure_state && last_failure == 0 && cpufreq_clamp)
+ wf_control_set_max(cpufreq_clamp);
+ if (failure_state == 0 && last_failure && cpufreq_clamp)
+ wf_control_set_min(cpufreq_clamp);
+
+ /* That's it for now, we might want to deal with other failures
+ * differently in the future though
+ */
+}
+
+static void rm31_new_control(struct wf_control *ct)
+{
+ bool all_controls;
+
+ if (!strcmp(ct->name, "cpu-fan-a-0"))
+ cpu_fans[0][0] = ct;
+ else if (!strcmp(ct->name, "cpu-fan-b-0"))
+ cpu_fans[0][1] = ct;
+ else if (!strcmp(ct->name, "cpu-fan-c-0"))
+ cpu_fans[0][2] = ct;
+ else if (!strcmp(ct->name, "cpu-fan-a-1"))
+ cpu_fans[1][0] = ct;
+ else if (!strcmp(ct->name, "cpu-fan-b-1"))
+ cpu_fans[1][1] = ct;
+ else if (!strcmp(ct->name, "cpu-fan-c-1"))
+ cpu_fans[1][2] = ct;
+ else if (!strcmp(ct->name, "backside-fan"))
+ backside_fan = ct;
+ else if (!strcmp(ct->name, "slots-fan"))
+ slots_fan = ct;
+ else if (!strcmp(ct->name, "cpufreq-clamp"))
+ cpufreq_clamp = ct;
+
+ all_controls =
+ cpu_fans[0][0] &&
+ cpu_fans[0][1] &&
+ cpu_fans[0][2] &&
+ backside_fan &&
+ slots_fan;
+ if (nr_chips > 1)
+ all_controls &=
+ cpu_fans[1][0] &&
+ cpu_fans[1][1] &&
+ cpu_fans[1][2];
+ have_all_controls = all_controls;
+}
+
+
+static void rm31_new_sensor(struct wf_sensor *sr)
+{
+ bool all_sensors;
+
+ if (!strcmp(sr->name, "cpu-diode-temp-0"))
+ sens_cpu_temp[0] = sr;
+ else if (!strcmp(sr->name, "cpu-diode-temp-1"))
+ sens_cpu_temp[1] = sr;
+ else if (!strcmp(sr->name, "cpu-voltage-0"))
+ sens_cpu_volts[0] = sr;
+ else if (!strcmp(sr->name, "cpu-voltage-1"))
+ sens_cpu_volts[1] = sr;
+ else if (!strcmp(sr->name, "cpu-current-0"))
+ sens_cpu_amps[0] = sr;
+ else if (!strcmp(sr->name, "cpu-current-1"))
+ sens_cpu_amps[1] = sr;
+ else if (!strcmp(sr->name, "backside-temp"))
+ backside_temp = sr;
+ else if (!strcmp(sr->name, "slots-temp"))
+ slots_temp = sr;
+ else if (!strcmp(sr->name, "dimms-temp"))
+ dimms_temp = sr;
+
+ all_sensors =
+ sens_cpu_temp[0] &&
+ sens_cpu_volts[0] &&
+ sens_cpu_amps[0] &&
+ backside_temp &&
+ slots_temp &&
+ dimms_temp;
+ if (nr_chips > 1)
+ all_sensors &=
+ sens_cpu_temp[1] &&
+ sens_cpu_volts[1] &&
+ sens_cpu_amps[1];
+
+ have_all_sensors = all_sensors;
+}
+
+static int rm31_wf_notify(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ switch (event) {
+ case WF_EVENT_NEW_SENSOR:
+ rm31_new_sensor(data);
+ break;
+ case WF_EVENT_NEW_CONTROL:
+ rm31_new_control(data);
+ break;
+ case WF_EVENT_TICK:
+ if (have_all_controls && have_all_sensors)
+ rm31_tick();
+ }
+ return 0;
+}
+
+static struct notifier_block rm31_events = {
+ .notifier_call = rm31_wf_notify,
+};
+
+static int wf_rm31_probe(struct platform_device *dev)
+{
+ wf_register_client(&rm31_events);
+ return 0;
+}
+
+static int wf_rm31_remove(struct platform_device *dev)
+{
+ wf_unregister_client(&rm31_events);
+
+ /* should release all sensors and controls */
+ return 0;
+}
+
+static struct platform_driver wf_rm31_driver = {
+ .probe = wf_rm31_probe,
+ .remove = wf_rm31_remove,
+ .driver = {
+ .name = "windfarm",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init wf_rm31_init(void)
+{
+ struct device_node *cpu;
+ int i;
+
+ if (!of_machine_is_compatible("RackMac3,1"))
+ return -ENODEV;
+
+ /* Count the number of CPU cores */
+ nr_chips = 0;
+ for_each_node_by_type(cpu, "cpu")
+ ++nr_chips;
+ if (nr_chips > NR_CHIPS)
+ nr_chips = NR_CHIPS;
+
+ pr_info("windfarm: Initializing for desktop G5 with %d chips\n",
+ nr_chips);
+
+ /* Get MPU data for each CPU */
+ for (i = 0; i < nr_chips; i++) {
+ cpu_mpu_data[i] = wf_get_mpu(i);
+ if (!cpu_mpu_data[i]) {
+ pr_err("wf_rm31: Failed to find MPU data for CPU %d\n", i);
+ return -ENXIO;
+ }
+ }
+
+#ifdef MODULE
+ request_module("windfarm_fcu_controls");
+ request_module("windfarm_lm75_sensor");
+ request_module("windfarm_lm87_sensor");
+ request_module("windfarm_ad7417_sensor");
+ request_module("windfarm_max6690_sensor");
+ request_module("windfarm_cpufreq_clamp");
+#endif /* MODULE */
+
+ platform_driver_register(&wf_rm31_driver);
+ return 0;
+}
+
+static void __exit wf_rm31_exit(void)
+{
+ platform_driver_unregister(&wf_rm31_driver);
+}
+
+module_init(wf_rm31_init);
+module_exit(wf_rm31_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("Thermal control for Xserve G5");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:windfarm");
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c
index bff1f372f18..c155a54e863 100644
--- a/drivers/macintosh/windfarm_smu_controls.c
+++ b/drivers/macintosh/windfarm_smu_controls.c
@@ -18,7 +18,6 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/smu.h>
@@ -56,7 +55,7 @@ static int smu_set_fan(int pwm, u8 id, u16 value)
{
struct smu_cmd cmd;
u8 buffer[16];
- DECLARE_COMPLETION(comp);
+ DECLARE_COMPLETION_ONSTACK(comp);
int rc;
/* Fill SMU command structure */
@@ -167,7 +166,7 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node,
if (fct == NULL)
return NULL;
fct->ctrl.ops = &smu_fan_ops;
- l = get_property(node, "location", NULL);
+ l = of_get_property(node, "location", NULL);
if (l == NULL)
goto fail;
@@ -202,6 +201,8 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node,
fct->ctrl.name = "cpu-front-fan-1";
else if (!strcmp(l, "CPU A PUMP"))
fct->ctrl.name = "cpu-pump-0";
+ else if (!strcmp(l, "CPU B PUMP"))
+ fct->ctrl.name = "cpu-pump-1";
else if (!strcmp(l, "Slots Fan") || !strcmp(l, "Slots fan") ||
!strcmp(l, "EXPANSION SLOTS INTAKE"))
fct->ctrl.name = "slots-fan";
@@ -218,23 +219,27 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node,
fct->ctrl.name = "cpu-fan";
else if (!strcmp(l, "Hard Drive") || !strcmp(l, "Hard drive"))
fct->ctrl.name = "drive-bay-fan";
+ else if (!strcmp(l, "HDD Fan")) /* seen on iMac G5 iSight */
+ fct->ctrl.name = "hard-drive-fan";
+ else if (!strcmp(l, "ODD Fan")) /* same */
+ fct->ctrl.name = "optical-drive-fan";
/* Unrecognized fan, bail out */
if (fct->ctrl.name == NULL)
goto fail;
/* Get min & max values*/
- v = get_property(node, "min-value", NULL);
+ v = of_get_property(node, "min-value", NULL);
if (v == NULL)
goto fail;
fct->min = *v;
- v = get_property(node, "max-value", NULL);
+ v = of_get_property(node, "max-value", NULL);
if (v == NULL)
goto fail;
fct->max = *v;
/* Get "reg" value */
- reg = get_property(node, "reg", NULL);
+ reg = of_get_property(node, "reg", NULL);
if (reg == NULL)
goto fail;
fct->reg = *reg;
@@ -263,7 +268,7 @@ static int __init smu_controls_init(void)
/* Look for RPM fans */
for (fans = NULL; (fans = of_get_next_child(smu, fans)) != NULL;)
if (!strcmp(fans->name, "rpm-fans") ||
- device_is_compatible(fans, "smu-rpm-fans"))
+ of_device_is_compatible(fans, "smu-rpm-fans"))
break;
for (fan = NULL;
fans && (fan = of_get_next_child(fans, fan)) != NULL;) {
diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c
index aceb61d9fbc..ad6223e8834 100644
--- a/drivers/macintosh/windfarm_smu_sat.c
+++ b/drivers/macintosh/windfarm_smu_sat.c
@@ -13,14 +13,14 @@
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/i2c.h>
-#include <asm/semaphore.h>
+#include <linux/mutex.h>
#include <asm/prom.h>
#include <asm/smu.h>
#include <asm/pmac_low_i2c.h>
#include "windfarm.h"
-#define VERSION "0.2"
+#define VERSION "1.0"
#define DEBUG
@@ -34,58 +34,28 @@
#define MAX_AGE msecs_to_jiffies(800)
struct wf_sat {
+ struct kref ref;
int nr;
- atomic_t refcnt;
- struct semaphore mutex;
+ struct mutex mutex;
unsigned long last_read; /* jiffies when cache last updated */
u8 cache[16];
- struct i2c_client i2c;
+ struct list_head sensors;
+ struct i2c_client *i2c;
struct device_node *node;
};
static struct wf_sat *sats[2];
struct wf_sat_sensor {
- int index;
- int index2; /* used for power sensors */
- int shift;
- struct wf_sat *sat;
- struct wf_sensor sens;
+ struct list_head link;
+ int index;
+ int index2; /* used for power sensors */
+ int shift;
+ struct wf_sat *sat;
+ struct wf_sensor sens;
};
#define wf_to_sat(c) container_of(c, struct wf_sat_sensor, sens)
-#define i2c_to_sat(c) container_of(c, struct wf_sat, i2c)
-
-static int wf_sat_attach(struct i2c_adapter *adapter);
-static int wf_sat_detach(struct i2c_client *client);
-
-static struct i2c_driver wf_sat_driver = {
- .driver = {
- .name = "wf_smu_sat",
- },
- .attach_adapter = wf_sat_attach,
- .detach_client = wf_sat_detach,
-};
-
-/*
- * XXX i2c_smbus_read_i2c_block_data doesn't pass the requested
- * length down to the low-level driver, so we use this, which
- * works well enough with the SMU i2c driver code...
- */
-static int sat_read_block(struct i2c_client *client, u8 command,
- u8 *values, int len)
-{
- union i2c_smbus_data data;
- int err;
-
- data.block[0] = len;
- err = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
- I2C_SMBUS_READ, command, I2C_SMBUS_I2C_BLOCK_DATA,
- &data);
- if (!err)
- memcpy(values, data.block, len);
- return err;
-}
struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
unsigned int *size)
@@ -101,17 +71,18 @@ struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
if (sat_id > 1 || (sat = sats[sat_id]) == NULL)
return NULL;
- err = i2c_smbus_write_word_data(&sat->i2c, 8, id << 8);
+ err = i2c_smbus_write_word_data(sat->i2c, 8, id << 8);
if (err) {
printk(KERN_ERR "smu_sat_get_sdb_part wr error %d\n", err);
return NULL;
}
- len = i2c_smbus_read_word_data(&sat->i2c, 9);
- if (len < 0) {
+ err = i2c_smbus_read_word_data(sat->i2c, 9);
+ if (err < 0) {
printk(KERN_ERR "smu_sat_get_sdb_part rd len error\n");
return NULL;
}
+ len = err;
if (len == 0) {
printk(KERN_ERR "smu_sat_get_sdb_part no partition %x\n", id);
return NULL;
@@ -124,8 +95,8 @@ struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
return NULL;
for (i = 0; i < len; i += 4) {
- err = sat_read_block(&sat->i2c, 0xa, data, 4);
- if (err) {
+ err = i2c_smbus_read_i2c_block_data(sat->i2c, 0xa, 4, data);
+ if (err < 0) {
printk(KERN_ERR "smu_sat_get_sdb_part rd err %d\n",
err);
goto fail;
@@ -157,8 +128,8 @@ static int wf_sat_read_cache(struct wf_sat *sat)
{
int err;
- err = sat_read_block(&sat->i2c, 0x3f, sat->cache, 16);
- if (err)
+ err = i2c_smbus_read_i2c_block_data(sat->i2c, 0x3f, 16, sat->cache);
+ if (err < 0)
return err;
sat->last_read = jiffies;
#ifdef LOTSA_DEBUG
@@ -173,17 +144,17 @@ static int wf_sat_read_cache(struct wf_sat *sat)
return 0;
}
-static int wf_sat_get(struct wf_sensor *sr, s32 *value)
+static int wf_sat_sensor_get(struct wf_sensor *sr, s32 *value)
{
struct wf_sat_sensor *sens = wf_to_sat(sr);
struct wf_sat *sat = sens->sat;
int i, err;
s32 val;
- if (sat->i2c.adapter == NULL)
+ if (sat->i2c == NULL)
return -ENODEV;
- down(&sat->mutex);
+ mutex_lock(&sat->mutex);
if (time_after(jiffies, (sat->last_read + MAX_AGE))) {
err = wf_sat_read_cache(sat);
if (err)
@@ -202,75 +173,66 @@ static int wf_sat_get(struct wf_sensor *sr, s32 *value)
err = 0;
fail:
- up(&sat->mutex);
+ mutex_unlock(&sat->mutex);
return err;
}
-static void wf_sat_release(struct wf_sensor *sr)
+static void wf_sat_release(struct kref *ref)
+{
+ struct wf_sat *sat = container_of(ref, struct wf_sat, ref);
+
+ if (sat->nr >= 0)
+ sats[sat->nr] = NULL;
+ kfree(sat);
+}
+
+static void wf_sat_sensor_release(struct wf_sensor *sr)
{
struct wf_sat_sensor *sens = wf_to_sat(sr);
struct wf_sat *sat = sens->sat;
- if (atomic_dec_and_test(&sat->refcnt)) {
- if (sat->i2c.adapter) {
- i2c_detach_client(&sat->i2c);
- sat->i2c.adapter = NULL;
- }
- if (sat->nr >= 0)
- sats[sat->nr] = NULL;
- kfree(sat);
- }
kfree(sens);
+ kref_put(&sat->ref, wf_sat_release);
}
static struct wf_sensor_ops wf_sat_ops = {
- .get_value = wf_sat_get,
- .release = wf_sat_release,
+ .get_value = wf_sat_sensor_get,
+ .release = wf_sat_sensor_release,
.owner = THIS_MODULE,
};
-static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev)
+static int wf_sat_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
+ struct device_node *dev = client->dev.of_node;
struct wf_sat *sat;
struct wf_sat_sensor *sens;
const u32 *reg;
const char *loc, *type;
- u8 addr, chip, core;
+ u8 chip, core;
struct device_node *child;
int shift, cpu, index;
char *name;
int vsens[2], isens[2];
- reg = get_property(dev, "reg", NULL);
- if (reg == NULL)
- return;
- addr = *reg;
- DBG(KERN_DEBUG "wf_sat: creating sat at address %x\n", addr);
-
sat = kzalloc(sizeof(struct wf_sat), GFP_KERNEL);
if (sat == NULL)
- return;
+ return -ENOMEM;
sat->nr = -1;
sat->node = of_node_get(dev);
- atomic_set(&sat->refcnt, 0);
- init_MUTEX(&sat->mutex);
- sat->i2c.addr = (addr >> 1) & 0x7f;
- sat->i2c.adapter = adapter;
- sat->i2c.driver = &wf_sat_driver;
- strncpy(sat->i2c.name, "smu-sat", I2C_NAME_SIZE-1);
-
- if (i2c_attach_client(&sat->i2c)) {
- printk(KERN_ERR "windfarm: failed to attach smu-sat to i2c\n");
- goto fail;
- }
+ kref_init(&sat->ref);
+ mutex_init(&sat->mutex);
+ sat->i2c = client;
+ INIT_LIST_HEAD(&sat->sensors);
+ i2c_set_clientdata(client, sat);
vsens[0] = vsens[1] = -1;
isens[0] = isens[1] = -1;
child = NULL;
while ((child = of_get_next_child(dev, child)) != NULL) {
- reg = get_property(child, "reg", NULL);
- type = get_property(child, "device_type", NULL);
- loc = get_property(child, "location", NULL);
+ reg = of_get_property(child, "reg", NULL);
+ type = of_get_property(child, "device_type", NULL);
+ loc = of_get_property(child, "location", NULL);
if (reg == NULL || loc == NULL)
continue;
@@ -323,14 +285,15 @@ static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev)
sens->index2 = -1;
sens->shift = shift;
sens->sat = sat;
- atomic_inc(&sat->refcnt);
sens->sens.ops = &wf_sat_ops;
sens->sens.name = (char *) (sens + 1);
- snprintf(sens->sens.name, 16, "%s-%d", name, cpu);
+ snprintf((char *)sens->sens.name, 16, "%s-%d", name, cpu);
- if (wf_register_sensor(&sens->sens)) {
- atomic_dec(&sat->refcnt);
+ if (wf_register_sensor(&sens->sens))
kfree(sens);
+ else {
+ list_add(&sens->link, &sat->sensors);
+ kref_get(&sat->ref);
}
}
@@ -349,69 +312,58 @@ static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev)
sens->index2 = isens[core];
sens->shift = 0;
sens->sat = sat;
- atomic_inc(&sat->refcnt);
sens->sens.ops = &wf_sat_ops;
sens->sens.name = (char *) (sens + 1);
- snprintf(sens->sens.name, 16, "cpu-power-%d", cpu);
+ snprintf((char *)sens->sens.name, 16, "cpu-power-%d", cpu);
- if (wf_register_sensor(&sens->sens)) {
- atomic_dec(&sat->refcnt);
+ if (wf_register_sensor(&sens->sens))
kfree(sens);
+ else {
+ list_add(&sens->link, &sat->sensors);
+ kref_get(&sat->ref);
}
}
if (sat->nr >= 0)
sats[sat->nr] = sat;
- return;
-
- fail:
- kfree(sat);
-}
-
-static int wf_sat_attach(struct i2c_adapter *adapter)
-{
- struct device_node *busnode, *dev = NULL;
- struct pmac_i2c_bus *bus;
-
- bus = pmac_i2c_adapter_to_bus(adapter);
- if (bus == NULL)
- return -ENODEV;
- busnode = pmac_i2c_get_bus_node(bus);
-
- while ((dev = of_get_next_child(busnode, dev)) != NULL)
- if (device_is_compatible(dev, "smu-sat"))
- wf_sat_create(adapter, dev);
return 0;
}
-static int wf_sat_detach(struct i2c_client *client)
+static int wf_sat_remove(struct i2c_client *client)
{
- struct wf_sat *sat = i2c_to_sat(client);
+ struct wf_sat *sat = i2c_get_clientdata(client);
+ struct wf_sat_sensor *sens;
- /* XXX TODO */
+ /* release sensors */
+ while(!list_empty(&sat->sensors)) {
+ sens = list_first_entry(&sat->sensors,
+ struct wf_sat_sensor, link);
+ list_del(&sens->link);
+ wf_unregister_sensor(&sens->sens);
+ }
+ sat->i2c = NULL;
+ kref_put(&sat->ref, wf_sat_release);
- sat->i2c.adapter = NULL;
return 0;
}
-static int __init sat_sensors_init(void)
-{
- int err;
-
- err = i2c_add_driver(&wf_sat_driver);
- if (err < 0)
- return err;
- return 0;
-}
+static const struct i2c_device_id wf_sat_id[] = {
+ { "MAC,smu-sat", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wf_sat_id);
-static void __exit sat_sensors_exit(void)
-{
- i2c_del_driver(&wf_sat_driver);
-}
+static struct i2c_driver wf_sat_driver = {
+ .driver = {
+ .name = "wf_smu_sat",
+ },
+ .probe = wf_sat_probe,
+ .remove = wf_sat_remove,
+ .id_table = wf_sat_id,
+};
-module_init(sat_sensors_init);
-/*module_exit(sat_sensors_exit); Uncomment when cleanup is implemented */
+module_i2c_driver(wf_sat_driver);
MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
MODULE_DESCRIPTION("SMU satellite sensors for PowerMac thermal control");
diff --git a/drivers/macintosh/windfarm_smu_sensors.c b/drivers/macintosh/windfarm_smu_sensors.c
index defe9922ebd..1cc4e4953d8 100644
--- a/drivers/macintosh/windfarm_smu_sensors.c
+++ b/drivers/macintosh/windfarm_smu_sensors.c
@@ -18,7 +18,6 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/smu.h>
@@ -67,7 +66,7 @@ static void smu_ads_release(struct wf_sensor *sr)
static int smu_read_adc(u8 id, s32 *value)
{
struct smu_simple_cmd cmd;
- DECLARE_COMPLETION(comp);
+ DECLARE_COMPLETION_ONSTACK(comp);
int rc;
rc = smu_queue_simple(&cmd, SMU_CMD_READ_ADC, 1,
@@ -204,8 +203,8 @@ static struct smu_ad_sensor *smu_ads_create(struct device_node *node)
ads = kmalloc(sizeof(struct smu_ad_sensor), GFP_KERNEL);
if (ads == NULL)
return NULL;
- c = get_property(node, "device_type", NULL);
- l = get_property(node, "location", NULL);
+ c = of_get_property(node, "device_type", NULL);
+ l = of_get_property(node, "location", NULL);
if (c == NULL || l == NULL)
goto fail;
@@ -255,7 +254,7 @@ static struct smu_ad_sensor *smu_ads_create(struct device_node *node)
} else
goto fail;
- v = get_property(node, "reg", NULL);
+ v = of_get_property(node, "reg", NULL);
if (v == NULL)
goto fail;
ads->reg = *v;
@@ -363,9 +362,9 @@ smu_cpu_power_create(struct wf_sensor *volts, struct wf_sensor *amps)
* I yet have to figure out what's up with 8,2 and will have to
* adjust for later, unless we can 100% trust the SDB partition...
*/
- if ((machine_is_compatible("PowerMac8,1") ||
- machine_is_compatible("PowerMac8,2") ||
- machine_is_compatible("PowerMac9,1")) &&
+ if ((of_machine_is_compatible("PowerMac8,1") ||
+ of_machine_is_compatible("PowerMac8,2") ||
+ of_machine_is_compatible("PowerMac9,1")) &&
cpuvcp_version >= 2) {
pow->quadratic = 1;
DBG("windfarm: CPU Power using quadratic transform\n");