summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/block/swim3.c168
-rw-r--r--drivers/char/Makefile4
-rw-r--r--drivers/char/generic_nvram.c145
-rw-r--r--drivers/char/keyboard.c10
-rw-r--r--drivers/i2c/busses/i2c-keywest.c371
-rw-r--r--drivers/i2c/busses/i2c-keywest.h10
-rw-r--r--drivers/ide/ppc/pmac.c250
-rw-r--r--drivers/macintosh/Kconfig156
-rw-r--r--drivers/macintosh/Makefile7
-rw-r--r--drivers/macintosh/adb.c9
-rw-r--r--drivers/macintosh/adbhid.c71
-rw-r--r--drivers/macintosh/macio_asic.c281
-rw-r--r--drivers/macintosh/mediabay.c79
-rw-r--r--drivers/macintosh/therm_adt7467.c562
-rw-r--r--drivers/macintosh/therm_pm72.c1241
-rw-r--r--drivers/macintosh/therm_pm72.h236
-rw-r--r--drivers/macintosh/therm_windtunnel.c456
-rw-r--r--drivers/macintosh/via-pmu.c55
-rw-r--r--drivers/scsi/mac53c94.c503
-rw-r--r--drivers/scsi/mesh.c1760
-rw-r--r--drivers/serial/pmac_zilog.c89
22 files changed, 4909 insertions, 1556 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 28e0da6c8c5d..5da149c6ab4e 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -26,6 +26,8 @@ source "drivers/ieee1394/Kconfig"
source "drivers/message/i2o/Kconfig"
+source "drivers/macintosh/Kconfig"
+
source "net/Kconfig"
source "drivers/isdn/Kconfig"
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
index 4bf309826005..e4cbabf61514 100644
--- a/drivers/block/swim3.c
+++ b/drivers/block/swim3.c
@@ -24,7 +24,10 @@
#include <linux/delay.h>
#include <linux/fd.h>
#include <linux/ioctl.h>
+#include <linux/blkdev.h>
#include <linux/devfs_fs_kernel.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
#include <asm/io.h>
#include <asm/dbdma.h>
#include <asm/prom.h>
@@ -144,7 +147,7 @@ struct swim3 {
#define RELAX 3 /* also eject in progress */
#define READ_DATA_0 4
#define TWOMEG_DRIVE 5
-#define SINGLE_SIDED 6
+#define SINGLE_SIDED 6 /* drive or diskette is 4MB type? */
#define DRIVE_PRESENT 7
#define DISK_IN 8
#define WRITE_PROT 9
@@ -184,6 +187,7 @@ struct floppy_state {
int req_sector; /* sector number ditto */
int scount; /* # sectors we're transferring at present */
int retries;
+ int settle_time;
int secpercyl; /* disk geometry information */
int secpertrack;
int total_secs;
@@ -232,8 +236,9 @@ static void setup_transfer(struct floppy_state *fs);
static void act(struct floppy_state *fs);
static void scan_timeout(unsigned long data);
static void seek_timeout(unsigned long data);
+static void settle_timeout(unsigned long data);
static void xfer_timeout(unsigned long data);
-static void swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
/*static void fd_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs);*/
static int grab_drive(struct floppy_state *fs, enum swim_state state,
int interruptible);
@@ -274,7 +279,6 @@ static void swim3_action(struct floppy_state *fs, int action)
udelay(2);
out_8(&sw->select, sw->select & ~LSTRB);
udelay(1);
- out_8(&sw->select, RELAX);
}
static int swim3_readbit(struct floppy_state *fs, int bit)
@@ -283,9 +287,8 @@ static int swim3_readbit(struct floppy_state *fs, int bit)
int stat;
swim3_select(fs, bit);
- udelay(10);
+ udelay(1);
stat = in_8(&sw->status);
- out_8(&sw->select, RELAX);
return (stat & DATA) == 0;
}
@@ -374,13 +377,13 @@ static void set_timeout(struct floppy_state *fs, int nticks,
static inline void scan_track(struct floppy_state *fs)
{
volatile struct swim3 *sw = fs->swim3;
- int xx;
swim3_select(fs, READ_DATA_0);
- xx = sw->intr; /* clear SEEN_SECTOR bit */
+ in_8(&sw->intr); /* clear SEEN_SECTOR bit */
+ in_8(&sw->error);
+ out_8(&sw->intr_enable, SEEN_SECTOR);
out_8(&sw->control_bis, DO_ACTION);
/* enable intr when track found */
- out_8(&sw->intr_enable, ERROR_INTR | SEEN_SECTOR);
set_timeout(fs, HZ, scan_timeout); /* enable timeout */
}
@@ -395,12 +398,14 @@ static inline void seek_track(struct floppy_state *fs, int n)
swim3_action(fs, SEEK_NEGATIVE);
sw->nseek = -n;
}
- fs->expect_cyl = (fs->cur_cyl > 0)? fs->cur_cyl + n: -1;
+ fs->expect_cyl = (fs->cur_cyl >= 0)? fs->cur_cyl + n: -1;
swim3_select(fs, STEP);
- out_8(&sw->control_bis, DO_SEEK);
+ in_8(&sw->error);
/* enable intr when seek finished */
- out_8(&sw->intr_enable, ERROR_INTR | SEEK_DONE);
- set_timeout(fs, HZ/2, seek_timeout); /* enable timeout */
+ out_8(&sw->intr_enable, SEEK_DONE);
+ out_8(&sw->control_bis, DO_SEEK);
+ set_timeout(fs, 3*HZ, seek_timeout); /* enable timeout */
+ fs->settle_time = 0;
}
static inline void init_dma(struct dbdma_cmd *cp, int cmd,
@@ -448,18 +453,21 @@ static inline void setup_transfer(struct floppy_state *fs)
}
++cp;
out_le16(&cp->command, DBDMA_STOP);
+ out_8(&sw->control_bic, DO_ACTION | WRITE_SECTORS);
+ in_8(&sw->error);
+ out_8(&sw->control_bic, DO_ACTION | WRITE_SECTORS);
+ if (rq_data_dir(fd_req) == WRITE)
+ out_8(&sw->control_bis, WRITE_SECTORS);
+ in_8(&sw->intr);
out_le32(&dr->control, (RUN << 16) | RUN);
- out_8(&sw->control_bis,
- (rq_data_dir(fd_req) == WRITE? WRITE_SECTORS: 0) | DO_ACTION);
/* enable intr when transfer complete */
- out_8(&sw->intr_enable, ERROR_INTR | TRANSFER_DONE);
+ out_8(&sw->intr_enable, TRANSFER_DONE);
+ out_8(&sw->control_bis, DO_ACTION);
set_timeout(fs, 2*HZ, xfer_timeout); /* enable timeout */
}
static void act(struct floppy_state *fs)
{
- volatile struct swim3 *sw = fs->swim3;
-
for (;;) {
switch (fs->state) {
case idle:
@@ -492,20 +500,10 @@ static void act(struct floppy_state *fs)
return;
case settling:
- /* wait for SEEK_COMPLETE to become true */
- swim3_select(fs, SEEK_COMPLETE);
- udelay(10);
- out_8(&sw->intr_enable, ERROR_INTR | DATA_CHANGED);
- in_8(&sw->intr); /* clear DATA_CHANGED */
- if (in_8(&sw->status) & DATA) {
- /* seek_complete is not yet true */
- set_timeout(fs, HZ/2, seek_timeout);
- return;
- }
- out_8(&sw->intr_enable, 0);
- in_8(&sw->intr);
- fs->state = locating;
- break;
+ /* check for SEEK_COMPLETE after 30ms */
+ fs->settle_time = (HZ + 32) / 33;
+ set_timeout(fs, fs->settle_time, settle_timeout);
+ return;
case do_transfer:
if (fs->cur_cyl != fs->req_cyl) {
@@ -537,7 +535,7 @@ static void scan_timeout(unsigned long data)
volatile struct swim3 *sw = fs->swim3;
fs->timeout_pending = 0;
- out_8(&sw->control_bic, DO_ACTION);
+ out_8(&sw->control_bic, DO_ACTION | WRITE_SECTORS);
out_8(&sw->select, RELAX);
out_8(&sw->intr_enable, 0);
fs->cur_cyl = -1;
@@ -557,20 +555,34 @@ static void seek_timeout(unsigned long data)
volatile struct swim3 *sw = fs->swim3;
fs->timeout_pending = 0;
- if (fs->state == settling) {
- printk(KERN_ERR "swim3: MSI sel=%x ctrl=%x stat=%x intr=%x ie=%x\n",
- sw->select, sw->control, sw->status, sw->intr, sw->intr_enable);
- }
out_8(&sw->control_bic, DO_SEEK);
out_8(&sw->select, RELAX);
out_8(&sw->intr_enable, 0);
- if (fs->state == settling && swim3_readbit(fs, SEEK_COMPLETE)) {
- /* printk(KERN_DEBUG "swim3: missed settling interrupt\n"); */
+ printk(KERN_ERR "swim3: seek timeout\n");
+ end_request(fd_req, 0);
+ fs->state = idle;
+ start_request(fs);
+}
+
+static void settle_timeout(unsigned long data)
+{
+ struct floppy_state *fs = (struct floppy_state *) data;
+ volatile struct swim3 *sw = fs->swim3;
+
+ fs->timeout_pending = 0;
+ if (swim3_readbit(fs, SEEK_COMPLETE)) {
+ out_8(&sw->select, RELAX);
fs->state = locating;
act(fs);
return;
}
- printk(KERN_ERR "swim3: seek timeout\n");
+ out_8(&sw->select, RELAX);
+ if (fs->settle_time < 2*HZ) {
+ ++fs->settle_time;
+ set_timeout(fs, 1, settle_timeout);
+ return;
+ }
+ printk(KERN_ERR "swim3: seek settle timeout\n");
end_request(fd_req, 0);
fs->state = idle;
start_request(fs);
@@ -583,9 +595,13 @@ static void xfer_timeout(unsigned long data)
struct dbdma_regs *dr = fs->dma;
struct dbdma_cmd *cp = fs->dma_cmd;
unsigned long s;
+ int n;
fs->timeout_pending = 0;
st_le32(&dr->control, RUN << 16);
+ /* We must wait a bit for dbdma to stop */
+ for (n = 0; (in_le32(&dr->status) & ACTIVE) && n < 1000; n++)
+ udelay(1);
out_8(&sw->intr_enable, 0);
out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION);
out_8(&sw->select, RELAX);
@@ -604,7 +620,7 @@ static void xfer_timeout(unsigned long data)
start_request(fs);
}
-static void swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct floppy_state *fs = (struct floppy_state *) dev_id;
volatile struct swim3 *sw = fs->swim3;
@@ -613,18 +629,15 @@ static void swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
struct dbdma_regs *dr;
struct dbdma_cmd *cp;
- err = in_8(&sw->error);
intr = in_8(&sw->intr);
-#if 0
- printk("swim3 intr state=%d intr=%x err=%x\n", fs->state, intr, err);
-#endif
+ err = (intr & ERROR_INTR)? in_8(&sw->error): 0;
if ((intr & ERROR_INTR) && fs->state != do_transfer)
printk(KERN_ERR "swim3_interrupt, state=%d, dir=%lx, intr=%x, err=%x\n",
fs->state, rq_data_dir(fd_req), intr, err);
switch (fs->state) {
case locating:
if (intr & SEEN_SECTOR) {
- out_8(&sw->control_bic, DO_ACTION);
+ out_8(&sw->control_bic, DO_ACTION | WRITE_SECTORS);
out_8(&sw->select, RELAX);
out_8(&sw->intr_enable, 0);
del_timer(&fs->timeout);
@@ -674,19 +687,33 @@ static void swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
case do_transfer:
if ((intr & (ERROR_INTR | TRANSFER_DONE)) == 0)
break;
- dr = fs->dma;
- cp = fs->dma_cmd;
- /* We must wait a bit for dbdma to complete */
- for (n=0; (in_le32(&dr->status) & ACTIVE) && n < 1000; n++)
- udelay(10);
- DBDMA_DO_STOP(dr);
out_8(&sw->intr_enable, 0);
out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION);
out_8(&sw->select, RELAX);
del_timer(&fs->timeout);
fs->timeout_pending = 0;
+ dr = fs->dma;
+ cp = fs->dma_cmd;
if (rq_data_dir(fd_req) == WRITE)
++cp;
+ /*
+ * Check that the main data transfer has finished.
+ * On writing, the swim3 sometimes doesn't use
+ * up all the bytes of the postamble, so we can still
+ * see DMA active here. That doesn't matter as long
+ * as all the sector data has been transferred.
+ */
+ if ((intr & ERROR_INTR) == 0 && cp->xfer_status == 0) {
+ /* wait a little while for DMA to complete */
+ for (n = 0; n < 100; ++n) {
+ if (cp->xfer_status != 0)
+ break;
+ udelay(1);
+ barrier();
+ }
+ }
+ /* turn off DMA */
+ out_le32(&dr->control, (RUN | PAUSE) << 16);
stat = ld_le16(&cp->xfer_status);
resid = ld_le16(&cp->res_count);
if (intr & ERROR_INTR) {
@@ -742,6 +769,7 @@ static void swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
default:
printk(KERN_ERR "swim3: don't know what to do in state %d\n", fs->state);
}
+ return IRQ_HANDLED;
}
/*
@@ -793,16 +821,19 @@ static int fd_eject(struct floppy_state *fs)
if (err)
return err;
swim3_action(fs, EJECT);
- for (n = 2*HZ; n > 0; --n) {
- if (swim3_readbit(fs, RELAX))
- break;
+ for (n = 20; n > 0; --n) {
if (signal_pending(current)) {
err = -EINTR;
break;
}
+ swim3_select(fs, RELAX);
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
+ if (swim3_readbit(fs, DISK_IN) == 0)
+ break;
}
+ swim3_select(fs, RELAX);
+ udelay(150);
fs->ejected = 1;
release_drive(fs);
return err;
@@ -847,29 +878,31 @@ static int floppy_open(struct inode *inode, struct file *filp)
if (fs->ref_count == 0) {
if (fs->media_bay && check_media_bay(fs->media_bay, MB_FD))
return -ENXIO;
- out_8(&sw->mode, 0x95);
- out_8(&sw->control_bic, 0xff);
out_8(&sw->setup, S_IBM_DRIVE | S_FCLK_DIV2);
+ out_8(&sw->control_bic, 0xff);
+ out_8(&sw->mode, 0x95);
udelay(10);
out_8(&sw->intr_enable, 0);
out_8(&sw->control_bis, DRIVE_ENABLE | INTR_ENABLE);
swim3_action(fs, MOTOR_ON);
fs->write_prot = -1;
fs->cur_cyl = -1;
- for (n = HZ; n > 0; --n) {
- if (swim3_readbit(fs, SEEK_COMPLETE))
+ for (n = 0; n < 2 * HZ; ++n) {
+ if (n >= HZ/30 && swim3_readbit(fs, SEEK_COMPLETE))
break;
if (signal_pending(current)) {
err = -EINTR;
break;
}
+ swim3_select(fs, RELAX);
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
}
if (err == 0 && (swim3_readbit(fs, SEEK_COMPLETE) == 0
|| swim3_readbit(fs, DISK_IN) == 0))
err = -ENXIO;
- swim3_action(fs, 9);
+ swim3_action(fs, SETMFM);
+ swim3_select(fs, RELAX);
} else if (fs->ref_count == -1 || filp->f_flags & O_EXCL)
return -EBUSY;
@@ -892,6 +925,7 @@ static int floppy_open(struct inode *inode, struct file *filp)
if (fs->ref_count == 0) {
swim3_action(fs, MOTOR_OFF);
out_8(&sw->control_bic, DRIVE_ENABLE | INTR_ENABLE);
+ swim3_select(fs, RELAX);
}
return err;
}
@@ -911,6 +945,7 @@ static int floppy_release(struct inode *inode, struct file *filp)
if (fs->ref_count > 0 && --fs->ref_count == 0) {
swim3_action(fs, MOTOR_OFF);
out_8(&sw->control_bic, 0xff);
+ swim3_select(fs, RELAX);
}
return 0;
}
@@ -933,15 +968,17 @@ static int floppy_revalidate(struct gendisk *disk)
sw = fs->swim3;
grab_drive(fs, revalidating, 0);
out_8(&sw->intr_enable, 0);
- out_8(&sw->control_bis, DRIVE_ENABLE | INTR_ENABLE);
- swim3_action(fs, MOTOR_ON);
+ out_8(&sw->control_bis, DRIVE_ENABLE);
+ swim3_action(fs, MOTOR_ON); /* necessary? */
fs->write_prot = -1;
fs->cur_cyl = -1;
+ mdelay(1);
for (n = HZ; n > 0; --n) {
if (swim3_readbit(fs, SEEK_COMPLETE))
break;
if (signal_pending(current))
break;
+ swim3_select(fs, RELAX);
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
}
@@ -951,17 +988,14 @@ static int floppy_revalidate(struct gendisk *disk)
swim3_action(fs, MOTOR_OFF);
else {
fs->ejected = 0;
- swim3_action(fs, 9);
+ swim3_action(fs, SETMFM);
}
+ swim3_select(fs, RELAX);
release_drive(fs);
return ret;
}
-static void floppy_off(unsigned int nr)
-{
-}
-
static struct block_device_operations floppy_fops = {
.open = floppy_open,
.release = floppy_release,
@@ -1104,3 +1138,5 @@ static int swim3_add_device(struct device_node *swim)
return 0;
}
+
+module_init(swim3_init)
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index ca8aa47ef43e..302afc35f077 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -57,7 +57,9 @@ obj-$(CONFIG_SONYPI) += sonypi.o
obj-$(CONFIG_RTC) += rtc.o
obj-$(CONFIG_GEN_RTC) += genrtc.o
obj-$(CONFIG_EFI_RTC) += efirtc.o
-ifeq ($(CONFIG_PPC),)
+ifeq ($(CONFIG_GENERIC_NVRAM),y)
+ obj-$(CONFIG_NVRAM) += generic_nvram.o
+else
obj-$(CONFIG_NVRAM) += nvram.o
endif
obj-$(CONFIG_TOSHIBA) += toshiba.o
diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c
new file mode 100644
index 000000000000..b4d657d02d44
--- /dev/null
+++ b/drivers/char/generic_nvram.c
@@ -0,0 +1,145 @@
+/*
+ * Generic /dev/nvram driver for architectures providing some
+ * "generic" hooks, that is :
+ *
+ * nvram_read_byte, nvram_write_byte, nvram_sync
+ *
+ * Note that an additional hook is supported for PowerMac only
+ * for getting the nvram "partition" informations
+ *
+ */
+
+#define NVRAM_VERSION "1.1"
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+#include <asm/nvram.h>
+
+#define NVRAM_SIZE 8192
+
+static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
+{
+ lock_kernel();
+ switch (origin) {
+ case 1:
+ offset += file->f_pos;
+ break;
+ case 2:
+ offset += NVRAM_SIZE;
+ break;
+ }
+ if (offset < 0) {
+ unlock_kernel();
+ return -EINVAL;
+ }
+ file->f_pos = offset;
+ unlock_kernel();
+ return file->f_pos;
+}
+
+static ssize_t read_nvram(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int i;
+ char __user *p = buf;
+
+ if (verify_area(VERIFY_WRITE, buf, count))
+ return -EFAULT;
+ if (*ppos >= NVRAM_SIZE)
+ return 0;
+ for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count)
+ if (__put_user(nvram_read_byte(i), p))
+ return -EFAULT;
+ *ppos = i;
+ return p - buf;
+}
+
+static ssize_t write_nvram(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int i;
+ const char __user *p = buf;
+ char c;
+
+ if (verify_area(VERIFY_READ, buf, count))
+ return -EFAULT;
+ if (*ppos >= NVRAM_SIZE)
+ return 0;
+ for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) {
+ if (__get_user(c, p))
+ return -EFAULT;
+ nvram_write_byte(c, i);
+ }
+ *ppos = i;
+ return p - buf;
+}
+
+static int nvram_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ switch(cmd) {
+#ifdef CONFIG_PPC_PMAC
+ case OBSOLETE_PMAC_NVRAM_GET_OFFSET:
+ printk(KERN_WARNING "nvram: Using obsolete PMAC_NVRAM_GET_OFFSET ioctl\n");
+ case IOC_NVRAM_GET_OFFSET: {
+ int part, offset;
+
+ if (_machine != _MACH_Pmac)
+ return -EINVAL;
+ if (copy_from_user(&part, (void __user*)arg, sizeof(part)) != 0)
+ return -EFAULT;
+ if (part < pmac_nvram_OF || part > pmac_nvram_NR)
+ return -EINVAL;
+ offset = pmac_get_partition(part);
+ if (copy_to_user((void __user*)arg, &offset, sizeof(offset)) != 0)
+ return -EFAULT;
+ break;
+ }
+#endif /* CONFIG_PPC_PMAC */
+ case IOC_NVRAM_SYNC:
+ nvram_sync();
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct file_operations nvram_fops = {
+ .owner = THIS_MODULE,
+ .llseek = nvram_llseek,
+ .read = read_nvram,
+ .write = write_nvram,
+ .ioctl = nvram_ioctl,
+};
+
+static struct miscdevice nvram_dev = {
+ NVRAM_MINOR,
+ "nvram",
+ &nvram_fops
+};
+
+int __init nvram_init(void)
+{
+ printk(KERN_INFO "Macintosh non-volatile memory driver v%s\n",
+ NVRAM_VERSION);
+ return misc_register(&nvram_dev);
+}
+
+void __exit nvram_cleanup(void)
+{
+ misc_deregister( &nvram_dev );
+}
+
+module_init(nvram_init);
+module_exit(nvram_cleanup);
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index dc03b1a99956..643014bf8c4c 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -972,11 +972,6 @@ extern void sun_do_break(void);
static int emulate_raw(struct vc_data *vc, unsigned int keycode,
unsigned char up_flag)
{
-#ifdef CONFIG_MAC_EMUMOUSEBTN
- if (mac_hid_mouse_emulate_buttons(1, keycode, !up_flag))
- return 0;
-#endif /* CONFIG_MAC_EMUMOUSEBTN */
-
if (keycode > 255 || !x86_keycodes[keycode])
return -1;
@@ -1055,6 +1050,11 @@ void kbd_keycode(unsigned int keycode, int down, struct pt_regs *regs)
rep = (down == 2);
+#ifdef CONFIG_MAC_EMUMOUSEBTN
+ if (mac_hid_mouse_emulate_buttons(1, keycode, down))
+ return;
+#endif /* CONFIG_MAC_EMUMOUSEBTN */
+
if ((raw_mode = (kbd->kbdmode == VC_RAW)))
if (emulate_raw(vc, keycode, !down << 7))
if (keycode < BTN_MISC)
diff --git a/drivers/i2c/busses/i2c-keywest.c b/drivers/i2c/busses/i2c-keywest.c
index 64819889dd45..9d9aa44fefeb 100644
--- a/drivers/i2c/busses/i2c-keywest.c
+++ b/drivers/i2c/busses/i2c-keywest.c
@@ -26,6 +26,9 @@
2001/12/13 BenH New implementation
2001/12/15 BenH Add support for "byte" and "quick"
transfers. Add i2c_xfer routine.
+ 2003/09/21 BenH Rework state machine with Paulus help
+ 2004/01/21 BenH Merge in Greg KH changes, polled mode is back
+ 2004/02/05 BenH Merge 64 bits fixes from the g5 ppc64 tree
My understanding of the various modes supported by keywest are:
@@ -67,9 +70,35 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
+#include <asm/pmac_low_i2c.h>
#include "i2c-keywest.h"
+/* Currently, we don't deal with the weird interrupt cascade of the G5
+ * machines with the ppc64 kernel, so use Polled mode on these
+ */
+#ifdef CONFIG_PPC64
+#define POLLED_MODE
+#else
+#undef POLLED_MODE
+#endif
+
+/* Some debug macros */
+#define WRONG_STATE(name) do {\
+ pr_debug("KW: wrong state. Got %s, state: %s (isr: %02x)\n", \
+ name, __kw_state_names[iface->state], isr); \
+ } while(0)
+
+#ifdef DEBUG
+static const char *__kw_state_names[] = {
+ "state_idle",
+ "state_addr",
+ "state_read",
+ "state_write",
+ "state_stop",
+ "state_dead"
+};
+#endif /* DEBUG */
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("I2C driver for Apple's Keywest");
@@ -78,121 +107,154 @@ MODULE_PARM(probe, "i");
static int probe = 0;
+#ifdef POLLED_MODE
+/* Don't schedule, the g5 fan controller is too
+ * timing sensitive
+ */
+static u8
+wait_interrupt(struct keywest_iface* iface)
+{
+ int i;
+ u8 isr;
+
+ for (i = 0; i < 200000; i++) {
+ isr = read_reg(reg_isr) & KW_I2C_IRQ_MASK;
+ if (isr != 0)
+ return isr;
+ udelay(10);
+ }
+ return isr;
+}
+#endif /* POLLED_MODE */
+
static void
do_stop(struct keywest_iface* iface, int result)
{
- write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_STOP);
+ write_reg(reg_control, KW_I2C_CTL_STOP);
iface->state = state_stop;
iface->result = result;
}
/* Main state machine for standard & standard sub mode */
-static int
+static void
handle_interrupt(struct keywest_iface *iface, u8 isr)
{
int ack;
- int rearm_timer = 1;
- pr_debug("handle_interrupt(), got: %x, status: %x, state: %d\n",
- isr, read_reg(reg_status), iface->state);
- if (isr == 0 && iface->state != state_stop) {
- do_stop(iface, -1);
- return rearm_timer;
- }
- if (isr & KW_I2C_IRQ_STOP && iface->state != state_stop) {
- iface->result = -1;
- iface->state = state_stop;
- }
- switch(iface->state) {
- case state_addr:
- if (!(isr & KW_I2C_IRQ_ADDR)) {
- do_stop(iface, -1);
- break;
- }
- ack = read_reg(reg_status);
- pr_debug("ack on set address: %x\n", ack);
- if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
- do_stop(iface, -1);
- break;
+ if (isr == 0) {
+ if (iface->state != state_stop) {
+ pr_debug("KW: Timeout !\n");
+ do_stop(iface, -EIO);
}
- /* Handle rw "quick" mode */
- if (iface->datalen == 0)
- do_stop(iface, 0);
- else if (iface->read_write == I2C_SMBUS_READ) {
- iface->state = state_read;
- if (iface->datalen > 1)
- write_reg(reg_control, read_reg(reg_control)
- | KW_I2C_CTL_AAK);
- } else {
- iface->state = state_write;
- pr_debug("write byte: %x\n", *(iface->data));
- write_reg(reg_data, *(iface->data++));
- iface->datalen--;
- }
-
- break;
- case state_read:
- if (!(isr & KW_I2C_IRQ_DATA)) {
- do_stop(iface, -1);
- break;
- }
- *(iface->data++) = read_reg(reg_data);
- pr_debug("read byte: %x\n", *(iface->data-1));
- iface->datalen--;
- if (iface->datalen == 0)
- iface->state = state_stop;
- else
- write_reg(reg_control, 0);
- break;
- case state_write:
- if (!(isr & KW_I2C_IRQ_DATA)) {
- do_stop(iface, -1);
- break;
+ if (iface->state == state_stop) {
+ ack = read_reg(reg_status);
+ if (!(ack & KW_I2C_STAT_BUSY)) {
+ iface->state = state_idle;
+ write_reg(reg_ier, 0x00);
+#ifndef POLLED_MODE
+ complete(&iface->complete);
+#endif /* POLLED_MODE */
+ }
}
- /* Check ack status */
+ return;
+ }
+
+ if (isr & KW_I2C_IRQ_ADDR) {
ack = read_reg(reg_status);
- pr_debug("ack on data write: %x\n", ack);
+ if (iface->state != state_addr) {
+ write_reg(reg_isr, KW_I2C_IRQ_ADDR);
+ WRONG_STATE("KW_I2C_IRQ_ADDR");
+ do_stop(iface, -EIO);
+ return;
+ }
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
- do_stop(iface, -1);
- break;
+ iface->state = state_stop;
+ iface->result = -ENODEV;
+ pr_debug("KW: NAK on address\n");
+ } else {
+ /* Handle rw "quick" mode */
+ if (iface->datalen == 0) {
+ do_stop(iface, 0);
+ } else if (iface->read_write == I2C_SMBUS_READ) {
+ iface->state = state_read;
+ if (iface->datalen > 1)
+ write_reg(reg_control, KW_I2C_CTL_AAK);
+ } else {
+ iface->state = state_write;
+ write_reg(reg_data, *(iface->data++));
+ iface->datalen--;
+ }
}
- if (iface->datalen) {
- pr_debug("write byte: %x\n", *(iface->data));
- write_reg(reg_data, *(iface->data++));
+ write_reg(reg_isr, KW_I2C_IRQ_ADDR);
+ }
+
+ if (isr & KW_I2C_IRQ_DATA) {
+ if (iface->state == state_read) {
+ *(iface->data++) = read_reg(reg_data);
+ write_reg(reg_isr, KW_I2C_IRQ_DATA);
iface->datalen--;
- } else
- do_stop(iface, 0);
- break;
-
- case state_stop:
- if (!(isr & KW_I2C_IRQ_STOP) && (++iface->stopretry) < 10)
- do_stop(iface, -1);
- else {
- rearm_timer = 0;
- iface->state = state_idle;
- write_reg(reg_control, 0x00);
- write_reg(reg_ier, 0x00);
- complete(&iface->complete);
+ if (iface->datalen == 0)
+ iface->state = state_stop;
+ else if (iface->datalen == 1)
+ write_reg(reg_control, 0);
+ } else if (iface->state == state_write) {
+ /* Check ack status */
+ ack = read_reg(reg_status);
+ if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
+ pr_debug("KW: nack on data write (%x): %x\n",
+ iface->data[-1], ack);
+ do_stop(iface, -EIO);
+ } else if (iface->datalen) {
+ write_reg(reg_data, *(iface->data++));
+ iface->datalen--;
+ } else {
+ write_reg(reg_control, KW_I2C_CTL_STOP);
+ iface->state = state_stop;
+ iface->result = 0;
+ }
+ write_reg(reg_isr, KW_I2C_IRQ_DATA);
+ } else {
+ write_reg(reg_isr, KW_I2C_IRQ_DATA);
+ WRONG_STATE("KW_I2C_IRQ_DATA");
+ if (iface->state != state_stop)
+ do_stop(iface, -EIO);
}
- break;
}
-
- write_reg(reg_isr, isr);
- return rearm_timer;
+ if (isr & KW_I2C_IRQ_STOP) {
+ write_reg(reg_isr, KW_I2C_IRQ_STOP);
+ if (iface->state != state_stop) {
+ WRONG_STATE("KW_I2C_IRQ_STOP");
+ iface->result = -EIO;
+ }
+ iface->state = state_idle;
+ write_reg(reg_ier, 0x00);
+#ifndef POLLED_MODE
+ complete(&iface->complete);
+#endif /* POLLED_MODE */
+ }
+
+ if (isr & KW_I2C_IRQ_START)
+ write_reg(reg_isr, KW_I2C_IRQ_START);
}
+#ifndef POLLED_MODE
+
/* Interrupt handler */
static irqreturn_t
keywest_irq(int irq, void *dev_id, struct pt_regs *regs)
{
struct keywest_iface *iface = (struct keywest_iface *)dev_id;
+ unsigned long flags;
- spin_lock(&iface->lock);
+ spin_lock_irqsave(&iface->lock, flags);
del_timer(&iface->timeout_timer);
- if (handle_interrupt(iface, read_reg(reg_isr)))
- mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT);
- spin_unlock(&iface->lock);
+ handle_interrupt(iface, read_reg(reg_isr));
+ if (iface->state != state_idle) {
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
+ }
+ spin_unlock_irqrestore(&iface->lock, flags);
return IRQ_HANDLED;
}
@@ -200,14 +262,20 @@ static void
keywest_timeout(unsigned long data)
{
struct keywest_iface *iface = (struct keywest_iface *)data;
+ unsigned long flags;
pr_debug("timeout !\n");
- spin_lock_irq(&iface->lock);
- if (handle_interrupt(iface, read_reg(reg_isr)))
- mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT);
- spin_unlock(&iface->lock);
+ spin_lock_irqsave(&iface->lock, flags);
+ handle_interrupt(iface, read_reg(reg_isr));
+ if (iface->state != state_idle) {
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
+ }
+ spin_unlock_irqrestore(&iface->lock, flags);
}
+#endif /* POLLED_MODE */
+
/*
* SMBUS-type transfer entrypoint
*/
@@ -228,46 +296,54 @@ keywest_smbus_xfer( struct i2c_adapter* adap,
int rc = 0;
if (iface->state == state_dead)
- return -1;
+ return -ENXIO;
/* Prepare datas & select mode */
iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
switch (size) {
- case I2C_SMBUS_QUICK:
+ case I2C_SMBUS_QUICK:
len = 0;
buffer = NULL;
iface->cur_mode |= KW_I2C_MODE_STANDARD;
break;
- case I2C_SMBUS_BYTE:
+ case I2C_SMBUS_BYTE:
len = 1;
buffer = &data->byte;
iface->cur_mode |= KW_I2C_MODE_STANDARD;
break;
- case I2C_SMBUS_BYTE_DATA:
+ case I2C_SMBUS_BYTE_DATA:
len = 1;
buffer = &data->byte;
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
break;
- case I2C_SMBUS_WORD_DATA:
+ case I2C_SMBUS_WORD_DATA:
len = 2;
cur_word = cpu_to_le16(data->word);
buffer = (u8 *)&cur_word;
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
break;
- case I2C_SMBUS_BLOCK_DATA:
+ case I2C_SMBUS_BLOCK_DATA:
len = data->block[0];
buffer = &data->block[1];
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
break;
- default:
+ default:
return -1;
}
+ /* Turn a standardsub read into a combined mode access */
+ if (read_write == I2C_SMBUS_READ
+ && (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB) {
+ iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
+ iface->cur_mode |= KW_I2C_MODE_COMBINED;
+ }
+
/* Original driver had this limitation */
if (len > 32)
len = 32;
- down(&iface->sem);
+ if (pmac_low_i2c_lock(iface->node))
+ return -ENXIO;
pr_debug("chan: %d, addr: 0x%x, transfer len: %d, read: %d\n",
chan->chan_no, addr, len, read_write == I2C_SMBUS_READ);
@@ -276,12 +352,11 @@ keywest_smbus_xfer( struct i2c_adapter* adap,
iface->datalen = len;
iface->state = state_addr;
iface->result = 0;
- iface->stopretry = 0;
iface->read_write = read_write;
/* Setup channel & clear pending irqs */
- write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
write_reg(reg_isr, read_reg(reg_isr));
+ write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
write_reg(reg_status, 0);
/* Set up address and r/w bit */
@@ -293,15 +368,31 @@ keywest_smbus_xfer( struct i2c_adapter* adap,
|| (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED)
write_reg(reg_subaddr, command);
+#ifndef POLLED_MODE
/* Arm timeout */
- mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT);
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
+#endif
/* Start sending address & enable interrupt*/
- write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
+ write_reg(reg_control, KW_I2C_CTL_XADDR);
write_reg(reg_ier, KW_I2C_IRQ_MASK);
- /* Wait interrupt operations completion */
+#ifdef POLLED_MODE
+ pr_debug("using polled mode...\n");
+ /* State machine, to turn into an interrupt handler */
+ while(iface->state != state_idle) {
+ unsigned long flags;
+
+ u8 isr = wait_interrupt(iface);
+ spin_lock_irqsave(&iface->lock, flags);
+ handle_interrupt(iface, isr);
+ spin_unlock_irqrestore(&iface->lock, flags);
+ }
+#else /* POLLED_MODE */
+ pr_debug("using interrupt mode...\n");
wait_for_completion(&iface->complete);
+#endif /* POLLED_MODE */
rc = iface->result;
pr_debug("transfer done, result: %d\n", rc);
@@ -310,7 +401,7 @@ keywest_smbus_xfer( struct i2c_adapter* adap,
data->word = le16_to_cpu(cur_word);
/* Release sem */
- up(&iface->sem);
+ pmac_low_i2c_unlock(iface->node);
return rc;
}
@@ -329,7 +420,11 @@ keywest_xfer( struct i2c_adapter *adap,
int i, completed;
int rc = 0;
- down(&iface->sem);
+ if (iface->state == state_dead)
+ return -ENXIO;
+
+ if (pmac_low_i2c_lock(iface->node))
+ return -ENXIO;
/* Set adapter to standard mode */
iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
@@ -360,7 +455,6 @@ keywest_xfer( struct i2c_adapter *adap,
iface->datalen = pmsg->len;
iface->state = state_addr;
iface->result = 0;
- iface->stopretry = 0;
if (pmsg->flags & I2C_M_RD)
iface->read_write = I2C_SMBUS_READ;
else
@@ -373,15 +467,27 @@ keywest_xfer( struct i2c_adapter *adap,
(addr << 1) |
((iface->read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
+#ifndef POLLED_MODE
/* Arm timeout */
- mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT);
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
+#endif
/* Start sending address & enable interrupt*/
- write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
write_reg(reg_ier, KW_I2C_IRQ_MASK);
-
- /* Wait interrupt operations completion */
+ write_reg(reg_control, KW_I2C_CTL_XADDR);
+
+#ifdef POLLED_MODE
+ pr_debug("using polled mode...\n");
+ /* State machine, to turn into an interrupt handler */
+ while(iface->state != state_idle) {
+ u8 isr = wait_interrupt(iface);
+ handle_interrupt(iface, isr);
+ }
+#else /* POLLED_MODE */
+ pr_debug("using interrupt mode...\n");
wait_for_completion(&iface->complete);
+#endif /* POLLED_MODE */
rc = iface->result;
if (rc == 0)
@@ -390,7 +496,7 @@ keywest_xfer( struct i2c_adapter *adap,
}
/* Release sem */
- up(&iface->sem);
+ pmac_low_i2c_unlock(iface->node);
return completed;
}
@@ -416,19 +522,23 @@ static struct i2c_algorithm keywest_algorithm = {
static int
create_iface(struct device_node *np, struct device *dev)
{
- unsigned long steps, *psteps, *prate;
+ unsigned long steps;
unsigned bsteps, tsize, i, nchan, addroffset;
struct keywest_iface* iface;
+ u32 *psteps, *prate;
int rc;
- psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL);
+ if (pmac_low_i2c_lock(np))
+ return -ENODEV;
+
+ psteps = (u32 *)get_property(np, "AAPL,address-step", NULL);
steps = psteps ? (*psteps) : 0x10;
/* Hrm... maybe we can be smarter here */
for (bsteps = 0; (steps & 0x01) == 0; bsteps++)
steps >>= 1;
- if (!strcmp(np->parent->name, "uni-n")) {
+ if (np->parent->name[0] == 'u') {
nchan = 2;
addroffset = 3;
} else {
@@ -441,12 +551,13 @@ create_iface(struct device_node *np, struct device *dev)
iface = (struct keywest_iface *) kmalloc(tsize, GFP_KERNEL);
if (iface == NULL) {
printk(KERN_ERR "i2c-keywest: can't allocate inteface !\n");
+ pmac_low_i2c_unlock(np);
return -ENOMEM;
}
memset(iface, 0, tsize);
- init_MUTEX(&iface->sem);
spin_lock_init(&iface->lock);
init_completion(&iface->complete);
+ iface->node = of_node_get(np);
iface->bsteps = bsteps;
iface->chan_count = nchan;
iface->state = state_idle;
@@ -458,16 +569,19 @@ create_iface(struct device_node *np, struct device *dev)
if (iface->base == 0) {
printk(KERN_ERR "i2c-keywest: can't map inteface !\n");
kfree(iface);
+ pmac_low_i2c_unlock(np);
return -ENOMEM;
}
+#ifndef POLLED_MODE
init_timer(&iface->timeout_timer);
iface->timeout_timer.function = keywest_timeout;
iface->timeout_timer.data = (unsigned long)iface;
+#endif
/* Select interface rate */
iface->cur_mode = KW_I2C_MODE_100KHZ;
- prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL);
+ prate = (u32 *)get_property(np, "AAPL,i2c-rate", NULL);
if (prate) switch(*prate) {
case 100:
iface->cur_mode = KW_I2C_MODE_100KHZ;
@@ -480,11 +594,11 @@ create_iface(struct device_node *np, struct device *dev)
break;
default:
printk(KERN_WARNING "i2c-keywest: unknown rate %ldKhz, using 100KHz\n",
- *prate);
+ (long)*prate);
}
- /* Select standard sub mode */
- iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+ /* Select standard mode by default */
+ iface->cur_mode |= KW_I2C_MODE_STANDARD;
/* Write mode */
write_reg(reg_mode, iface->cur_mode);
@@ -493,14 +607,17 @@ create_iface(struct device_node *np, struct device *dev)
write_reg(reg_ier, 0x00);
write_reg(reg_isr, KW_I2C_IRQ_MASK);
+#ifndef POLLED_MODE
/* Request chip interrupt */
- rc = request_irq(iface->irq, keywest_irq, 0, "keywest i2c", iface);
+ rc = request_irq(iface->irq, keywest_irq, SA_INTERRUPT, "keywest i2c", iface);
if (rc) {
printk(KERN_ERR "i2c-keywest: can't get IRQ %d !\n", iface->irq);
iounmap((void *)iface->base);
kfree(iface);
+ pmac_low_i2c_unlock(np);
return -ENODEV;
}
+#endif /* POLLED_MODE */
dev_set_drvdata(dev, iface);
@@ -539,6 +656,7 @@ create_iface(struct device_node *np, struct device *dev)
printk(KERN_INFO "Found KeyWest i2c on \"%s\", %d channel%s, stepping: %d bits\n",
np->parent->name, nchan, nchan > 1 ? "s" : "", bsteps);
+ pmac_low_i2c_unlock(np);
return 0;
}
@@ -549,8 +667,10 @@ dispose_iface(struct device *dev)
int i, rc;
/* Make sure we stop all activity */
- down(&iface->sem);
+ if (pmac_low_i2c_lock(iface->node))
+ return -ENODEV;
+#ifndef POLLED_MODE
spin_lock_irq(&iface->lock);
while (iface->state != state_idle) {
spin_unlock_irq(&iface->lock);
@@ -558,10 +678,14 @@ dispose_iface(struct device *dev)
schedule_timeout(HZ/10);
spin_lock_irq(&iface->lock);
}
+#endif /* POLLED_MODE */
iface->state = state_dead;
+#ifndef POLLED_MODE
spin_unlock_irq(&iface->lock);
free_irq(iface->irq, iface);
- up(&iface->sem);
+#endif /* POLLED_MODE */
+
+ pmac_low_i2c_unlock(iface->node);
/* Release all channels */
for (i=0; i<iface->chan_count; i++) {
@@ -576,6 +700,7 @@ dispose_iface(struct device *dev)
}
iounmap((void *)iface->base);
dev_set_drvdata(dev, NULL);
+ of_node_put(iface->node);
kfree(iface);
return 0;
@@ -634,8 +759,8 @@ static struct of_platform_driver i2c_keywest_of_platform_driver =
static int __init
i2c_keywest_init(void)
{
- macio_register_driver(&i2c_keywest_macio_driver);
of_register_driver(&i2c_keywest_of_platform_driver);
+ macio_register_driver(&i2c_keywest_macio_driver);
return 0;
}
@@ -643,8 +768,8 @@ i2c_keywest_init(void)
static void __exit
i2c_keywest_cleanup(void)
{
- macio_unregister_driver(&i2c_keywest_macio_driver);
of_unregister_driver(&i2c_keywest_of_platform_driver);
+ macio_unregister_driver(&i2c_keywest_macio_driver);
}
module_init(i2c_keywest_init);
diff --git a/drivers/i2c/busses/i2c-keywest.h b/drivers/i2c/busses/i2c-keywest.h
index 8f6e975dc27b..ecc279d5ccae 100644
--- a/drivers/i2c/busses/i2c-keywest.h
+++ b/drivers/i2c/busses/i2c-keywest.h
@@ -51,20 +51,19 @@ typedef enum {
/* Physical interface */
struct keywest_iface
{
+ struct device_node *node;
unsigned long base;
unsigned bsteps;
int irq;
- struct semaphore sem;
spinlock_t lock;
- struct keywest_chan* channels;
+ struct keywest_chan *channels;
unsigned chan_count;
u8 cur_mode;
char read_write;
- u8* data;
+ u8 *data;
unsigned datalen;
int state;
int result;
- int stopretry;
struct timer_list timeout_timer;
struct completion complete;
};
@@ -98,8 +97,7 @@ static inline void __write_reg(struct keywest_iface *iface, reg_t reg, u8 val)
{
out_8(((volatile u8 *)iface->base)
+ (((unsigned)reg) << iface->bsteps), val);
- (void)__read_reg(iface, reg);
- udelay(10);
+ (void)__read_reg(iface, reg_subaddr);
}
#define write_reg(reg, val) __write_reg(iface, reg, val)
diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c
index 8612c8d2bc04..a97f5a164a25 100644
--- a/drivers/ide/ppc/pmac.c
+++ b/drivers/ide/ppc/pmac.c
@@ -69,7 +69,7 @@ typedef struct pmac_ide_hwif {
#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
/* Those fields are duplicating what is in hwif. We currently
* can't use the hwif ones because of some assumptions that are
- * being done by the generic code about the kind of dma controller
+ * beeing done by the generic code about the kind of dma controller
* and format of the dma table. This will have to be fixed though.
*/
volatile struct dbdma_regs* dma_regs;
@@ -90,15 +90,17 @@ enum {
controller_heathrow, /* Heathrow/Paddington */
controller_kl_ata3, /* KeyLargo ATA-3 */
controller_kl_ata4, /* KeyLargo ATA-4 */
- controller_un_ata6 /* UniNorth2 ATA-6 */
+ controller_un_ata6, /* UniNorth2 ATA-6 */
+ controller_k2_ata6 /* K2 ATA-6 */
};
static const char* model_name[] = {
"OHare ATA", /* OHare based */
"Heathrow ATA", /* Heathrow/Paddington */
- "KeyLargo ATA-3", /* KeyLargo ATA-3 */
- "KeyLargo ATA-4", /* KeyLargo ATA-4 */
- "UniNorth ATA-6" /* UniNorth2 ATA-6 */
+ "KeyLargo ATA-3", /* KeyLargo ATA-3 (MDMA only) */
+ "KeyLargo ATA-4", /* KeyLargo ATA-4 (UDMA/66) */
+ "UniNorth ATA-6", /* UniNorth2 ATA-6 (UDMA/100) */
+ "K2 ATA-6", /* K2 ATA-6 (UDMA/100) */
};
/*
@@ -336,16 +338,19 @@ kauai_lookup_timing(struct kauai_timing* table, int cycle_time)
/* allow up to 256 DBDMA commands per xfer */
#define MAX_DCMDS 256
-/* Wait 2s for disk to answer on IDE bus after
- * enable operation.
- * NOTE: There is at least one case I know of a disk that needs about 10sec
- * before anwering on the bus. I beleive we could add a kernel command
- * line arg to override this delay for such cases.
- *
- * NOTE2: This has to be fixed with a BSY wait loop. I'm working on adding
- * that to the generic probe code.
+/*
+ * Wait 1s for disk to answer on IDE bus after a hard reset
+ * of the device (via GPIO/FCR).
+ *
+ * Some devices seem to "pollute" the bus even after dropping
+ * the BSY bit (typically some combo drives slave on the UDMA
+ * bus) after a hard reset. Since we hard reset all drives on
+ * KeyLargo ATA66, we have to keep that delay around. I may end
+ * up not hard resetting anymore on these and keep the delay only
+ * for older interfaces instead (we have to reset when coming
+ * from MacOS...) --BenH.
*/
-#define IDE_WAKEUP_DELAY_MS 2000
+#define IDE_WAKEUP_DELAY (1*HZ)
static void pmac_ide_setup_dma(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif);
static int pmac_ide_build_dmatable(ide_drive_t *drive, struct request *rq);
@@ -357,9 +362,16 @@ static int pmac_ide_dma_begin (ide_drive_t *drive);
#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+/*
+ * Below is the code for blinking the laptop LED along with hard
+ * disk activity.
+ */
+
#ifdef CONFIG_BLK_DEV_IDE_PMAC_BLINK
-/* Set to 50ms */
+/* Set to 50ms minimum led-on time (also used to limit frequency
+ * of requests sent to the PMU
+ */
#define PMU_HD_BLINK_TIME (HZ/50)
static struct adb_request pmu_blink_on, pmu_blink_off;
@@ -402,6 +414,7 @@ pmu_hd_kick_blink(void *data, int rw)
pmu_blink_stoptime = jiffies + PMU_HD_BLINK_TIME;
wmb();
mod_timer(&pmu_blink_timer, pmu_blink_stoptime);
+ /* Fast path when LED is already ON */
if (pmu_blink_ledstate == 1)
return;
spin_lock_irqsave(&pmu_blink_lock, flags);
@@ -418,6 +431,11 @@ pmu_hd_blink_init(void)
struct device_node *dt;
const char *model;
+ /* Currently, I only enable this feature on KeyLargo based laptops,
+ * older laptops may support it (at least heathrow/paddington) but
+ * I don't feel like loading those venerable old machines with so
+ * much additional interrupt & PMU activity...
+ */
if (pmu_get_model() != PMU_KEYLARGO_BASED)
return 0;
@@ -476,9 +494,11 @@ pmac_ide_init_hwif_ports(hw_regs_t *hw,
*irq = pmac_ide[ix].irq;
}
-/* Setup timings for the selected drive (master/slave). I still need to verify if this
- * is enough, I beleive selectproc will be called whenever an IDE command is started,
- * but... */
+/*
+ * Apply the timings of the proper unit (master/slave) to the shared
+ * timing register when selecting that unit. This version is for
+ * ASICs with a single timing register
+ */
static void __pmac
pmac_ide_selectproc(ide_drive_t *drive)
{
@@ -496,6 +516,11 @@ pmac_ide_selectproc(ide_drive_t *drive)
(void)readl((unsigned *)(IDE_DATA_REG+IDE_TIMING_CONFIG));
}
+/*
+ * Apply the timings of the proper unit (master/slave) to the shared
+ * timing register when selecting that unit. This version is for
+ * ASICs with a dual timing register (Kauai)
+ */
static void __pmac
pmac_ide_kauai_selectproc(ide_drive_t *drive)
{
@@ -518,6 +543,9 @@ pmac_ide_kauai_selectproc(ide_drive_t *drive)
(void)readl((unsigned *)(IDE_DATA_REG + IDE_KAUAI_PIO_CONFIG));
}
+/*
+ * Force an update of controller timing values for a given drive
+ */
static void __pmac
pmac_ide_do_update_timings(ide_drive_t *drive)
{
@@ -526,12 +554,29 @@ pmac_ide_do_update_timings(ide_drive_t *drive)
if (pmif == NULL)
return;
- if (pmif->kind == controller_un_ata6)
+ if (pmif->kind == controller_un_ata6 || pmif->kind == controller_k2_ata6)
pmac_ide_kauai_selectproc(drive);
else
pmac_ide_selectproc(drive);
}
+static void
+pmac_outbsync(ide_drive_t *drive, u8 value, unsigned long port)
+{
+ u32 tmp;
+
+ writeb(value, port);
+ tmp = readl((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG));
+}
+
+/*
+ * Send the SET_FEATURE IDE command to the drive and update drive->id with
+ * the new state. We currently don't use the generic routine as it used to
+ * cause various trouble, especially with older mediabays.
+ * This code is sometimes triggering a spurrious interrupt though, I need
+ * to sort that out sooner or later and see if I can finally get the
+ * common version to work properly in all cases
+ */
static int __pmac
pmac_ide_do_setfeature(ide_drive_t *drive, u8 command)
{
@@ -606,7 +651,9 @@ out:
return result;
}
-/* Calculate PIO timings */
+/*
+ * Old tuning functions (called on hdparm -p), sets up drive PIO timings
+ */
static void __pmac
pmac_ide_tuneproc(ide_drive_t *drive, u8 pio)
{
@@ -625,7 +672,8 @@ pmac_ide_tuneproc(ide_drive_t *drive, u8 pio)
pio = ide_get_best_pio_mode(drive, pio, 4, &d);
switch (pmif->kind) {
- case controller_un_ata6: {
+ case controller_un_ata6:
+ case controller_k2_ata6: {
/* 100Mhz cell */
u32 tr = kauai_lookup_timing(kauai_pio_timings, d.cycle_time);
if (tr == 0)
@@ -685,6 +733,10 @@ pmac_ide_tuneproc(ide_drive_t *drive, u8 pio)
}
#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+
+/*
+ * Calculate KeyLargo ATA/66 UDMA timings
+ */
static int __pmac
set_timings_udma_ata4(u32 *timings, u8 speed)
{
@@ -710,6 +762,9 @@ set_timings_udma_ata4(u32 *timings, u8 speed)
return 0;
}
+/*
+ * Calculate Kauai ATA/100 UDMA timings
+ */
static int __pmac
set_timings_udma_ata6(u32 *pio_timings, u32 *ultra_timings, u8 speed)
{
@@ -727,6 +782,9 @@ set_timings_udma_ata6(u32 *pio_timings, u32 *ultra_timings, u8 speed)
return 0;
}
+/*
+ * Calculate MDMA timings for all cells
+ */
static int __pmac
set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2,
u8 speed, int drive_cycle_time)
@@ -753,6 +811,7 @@ set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2,
/* Get the proper timing array for this controller */
switch(intf_type) {
case controller_un_ata6:
+ case controller_k2_ata6:
break;
case controller_kl_ata4:
tm = mdma_timings_66;
@@ -784,7 +843,8 @@ set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2,
#endif
}
switch(intf_type) {
- case controller_un_ata6: {
+ case controller_un_ata6:
+ case controller_k2_ata6: {
/* 100Mhz cell */
u32 tr = kauai_lookup_timing(kauai_mdma_timings, cycleTime);
if (tr == 0)
@@ -854,8 +914,12 @@ set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2,
}
#endif /* #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC */
-/* You may notice we don't use this function on normal operation,
- * our, normal mdma function is supposed to be more precise
+/*
+ * Speedproc. This function is called by the core to set any of the standard
+ * timing (PIO, MDMA or UDMA) to both the drive and the controller.
+ * You may notice we don't use this function on normal "dma check" operation,
+ * our dedicated function is more precise as it uses the drive provided
+ * cycle time value. We should probably fix this one to deal with that too...
*/
static int __pmac
pmac_ide_tune_chipset (ide_drive_t *drive, byte speed)
@@ -874,7 +938,8 @@ pmac_ide_tune_chipset (ide_drive_t *drive, byte speed)
switch(speed) {
#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
case XFER_UDMA_5:
- if (pmif->kind != controller_un_ata6)
+ if (pmif->kind != controller_un_ata6 &&
+ pmif->kind != controller_k2_ata6)
return 1;
case XFER_UDMA_4:
case XFER_UDMA_3:
@@ -885,7 +950,8 @@ pmac_ide_tune_chipset (ide_drive_t *drive, byte speed)
case XFER_UDMA_0:
if (pmif->kind == controller_kl_ata4)
ret = set_timings_udma_ata4(timings, speed);
- else if (pmif->kind == controller_un_ata6)
+ else if (pmif->kind == controller_un_ata6
+ || pmif->kind == controller_k2_ata6)
ret = set_timings_udma_ata6(timings, timings2, speed);
else
ret = 1;
@@ -923,6 +989,10 @@ pmac_ide_tune_chipset (ide_drive_t *drive, byte speed)
return 0;
}
+/*
+ * Blast some well known "safe" values to the timing registers at init or
+ * wakeup from sleep time, before we do real calculation
+ */
static void __pmac
sanitize_timings(pmac_ide_hwif_t *pmif)
{
@@ -930,6 +1000,7 @@ sanitize_timings(pmac_ide_hwif_t *pmif)
switch(pmif->kind) {
case controller_un_ata6:
+ case controller_k2_ata6:
value = 0x08618a92;
value2 = 0x00002921;
break;
@@ -1052,9 +1123,11 @@ pmac_ide_do_resume(ide_hwif_t *hwif)
if (!pmif->mediabay) {
ppc_md.feature_call(PMAC_FTR_IDE_RESET, pmif->node, pmif->aapl_bus_id, 1);
ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, pmif->node, pmif->aapl_bus_id, 1);
- mdelay(10);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/100);
ppc_md.feature_call(PMAC_FTR_IDE_RESET, pmif->node, pmif->aapl_bus_id, 0);
- mdelay(100);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(IDE_WAKEUP_DELAY);
}
/* Sanitize drive timings */
@@ -1063,6 +1136,13 @@ pmac_ide_do_resume(ide_hwif_t *hwif)
return 0;
}
+/*
+ * Setup, register & probe an IDE channel driven by this driver, this is
+ * called by one of the 2 probe functions (macio or PCI). Note that a channel
+ * that ends up beeing free of any device is not kept around by this driver
+ * (it is kept in 2.4). This introduce an interface numbering change on some
+ * rare machines unfortunately, but it's better this way.
+ */
static int
pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif)
{
@@ -1073,6 +1153,8 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif)
pmif->broken_dma = pmif->broken_dma_warn = 0;
if (device_is_compatible(np, "kauai-ata"))
pmif->kind = controller_un_ata6;
+ else if (device_is_compatible(np, "K2-UATA"))
+ pmif->kind = controller_k2_ata6;
else if (device_is_compatible(np, "keylargo-ata")) {
if (strcmp(np->name, "ata-4") == 0)
pmif->kind = controller_kl_ata4;
@@ -1089,7 +1171,8 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif)
pmif->aapl_bus_id = bidp ? *bidp : 0;
/* Get cable type from device-tree */
- if (pmif->kind == controller_kl_ata4 || pmif->kind == controller_un_ata6) {
+ if (pmif->kind == controller_kl_ata4 || pmif->kind == controller_un_ata6
+ || pmif->kind == controller_k2_ata6) {
char* cable = get_property(np, "cable-type", NULL);
if (cable && !strncmp(cable, "80-", 3))
pmif->cable_80 = 1;
@@ -1119,13 +1202,16 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif)
/* This is necessary to enable IDE when net-booting */
ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmif->aapl_bus_id, 1);
ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmif->aapl_bus_id, 1);
- mdelay(10);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/100);
ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmif->aapl_bus_id, 0);
- mdelay(100);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(IDE_WAKEUP_DELAY);
}
/* Setup MMIO ops */
default_hwif_mmiops(hwif);
+ hwif->OUTBSYNC = pmac_outbsync;
/* Tell common code _not_ to mess with resources */
hwif->mmio = 2;
@@ -1139,7 +1225,7 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif)
hwif->drives[0].unmask = 1;
hwif->drives[1].unmask = 1;
hwif->tuneproc = pmac_ide_tuneproc;
- if (pmif->kind == controller_un_ata6)
+ if (pmif->kind == controller_un_ata6 || pmif->kind == controller_k2_ata6)
hwif->selectproc = pmac_ide_kauai_selectproc;
else
hwif->selectproc = pmac_ide_selectproc;
@@ -1187,6 +1273,9 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif)
return 0;
}
+/*
+ * Attach to a macio probed interface
+ */
static int __devinit
pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_match *match)
{
@@ -1215,17 +1304,8 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_match *match)
return -ENXIO;
}
- /*
- * Some older OFs have bogus sizes, causing request_OF_resource
- * to fail. We fix them up here
- */
- if (mdev->ofdev.node->addrs[0].size > 0x1000)
- mdev->ofdev.node->addrs[0].size = 0x1000;
- if (mdev->ofdev.node->n_addrs > 1 && mdev->ofdev.node->addrs[1].size > 0x100)
- mdev->ofdev.node->addrs[1].size = 0x100;
-
/* Request memory resource for IO ports */
- if (request_OF_resource(mdev->ofdev.node, 0, " (mac-io ata ports)") == NULL) {
+ if (macio_request_resource(mdev, 0, "ide-pmac (ports)")) {
printk(KERN_ERR "ide%d: can't request mmio resource !\n", i);
return -EBUSY;
}
@@ -1235,14 +1315,14 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_match *match)
* fixes in irq.c. That works well enough for the single case
* where that happens though...
*/
- if (mdev->ofdev.node->n_intrs == 0) {
+ if (macio_irq_count(mdev) == 0) {
printk(KERN_WARNING "ide%d: no intrs for device %s, using 13\n",
i, mdev->ofdev.node->full_name);
irq = 13;
} else
- irq = mdev->ofdev.node->intrs[0].line;
+ irq = macio_irq(mdev, 0);
- base = (unsigned long) ioremap(mdev->ofdev.node->addrs[0].address, 0x400);
+ base = (unsigned long)ioremap(macio_resource_start(mdev, 0), 0x400);
regbase = base;
hwif->pci_dev = mdev->bus->pdev;
@@ -1253,10 +1333,13 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_match *match)
pmif->regbase = regbase;
pmif->irq = irq;
#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
- if (mdev->ofdev.node->n_addrs >= 2)
- pmif->dma_regs = (volatile struct dbdma_regs*)
- ioremap(mdev->ofdev.node->addrs[1].address, 0x1000);
- else
+ if (macio_resource_count(mdev) >= 2) {
+ if (macio_request_resource(mdev, 1, "ide-pmac (dma)"))
+ printk(KERN_WARNING "ide%d: can't request DMA resource !\n", i);
+ else
+ pmif->dma_regs = (volatile struct dbdma_regs*)
+ ioremap(macio_resource_start(mdev, 1), 0x1000);
+ } else
pmif->dma_regs = NULL;
#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
dev_set_drvdata(&mdev->ofdev.dev, hwif);
@@ -1269,7 +1352,9 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_match *match)
if (pmif->dma_regs)
iounmap((void *)pmif->dma_regs);
memset(pmif, 0, sizeof(*pmif));
- release_OF_resource(mdev->ofdev.node, 0);
+ macio_release_resource(mdev, 0);
+ if (pmif->dma_regs)
+ macio_release_resource(mdev, 1);
}
return rc;
@@ -1305,6 +1390,9 @@ pmac_ide_macio_resume(struct macio_dev *mdev)
return rc;
}
+/*
+ * Attach to a PCI probed interface
+ */
static int __devinit
pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id)
{
@@ -1439,8 +1527,10 @@ static struct macio_driver pmac_ide_macio_driver =
.resume = pmac_ide_macio_resume,
};
-static struct pci_device_id pmac_ide_pci_match[] __devinitdata = {
- { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_KAUAI_ATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+static struct pci_device_id pmac_ide_pci_match[] = {
+ { PCI_VENDOR_ID_APPLE, PCI_DEVIEC_ID_APPLE_UNI_N_ATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_IPID_ATA100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_K2_ATA100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
};
static struct pci_driver pmac_ide_pci_driver = {
@@ -1468,6 +1558,11 @@ pmac_ide_probe(void)
#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+/*
+ * This is very close to the generic ide-dma version of the function except
+ * that we don't use the fields in the hwif but our own copies for sg_table
+ * and friends. We build & map the sglist for a given request
+ */
static int __pmac
pmac_ide_build_sglist(ide_drive_t *drive, struct request *rq)
{
@@ -1489,6 +1584,9 @@ pmac_ide_build_sglist(ide_drive_t *drive, struct request *rq)
return pci_map_sg(hwif->pci_dev, sg, nents, pmif->sg_dma_direction);
}
+/*
+ * Same as above but for a "raw" taskfile request
+ */
static int __pmac
pmac_ide_raw_build_sglist(ide_drive_t *drive, struct request *rq)
{
@@ -1630,7 +1728,9 @@ pmac_ide_destroy_dmatable (ide_drive_t *drive)
}
}
-/* Calculate MultiWord DMA timings */
+/*
+ * Pick up best MDMA timing for the drive and apply it
+ */
static int __pmac
pmac_ide_mdma_enable(ide_drive_t *drive, u16 mode)
{
@@ -1685,7 +1785,9 @@ pmac_ide_mdma_enable(ide_drive_t *drive, u16 mode)
return 1;
}
-/* Calculate Ultra DMA timings */
+/*
+ * Pick up best UDMA timing for the drive and apply it
+ */
static int __pmac
pmac_ide_udma_enable(ide_drive_t *drive, u16 mode)
{
@@ -1704,7 +1806,7 @@ pmac_ide_udma_enable(ide_drive_t *drive, u16 mode)
timing_local[1] = *timings2;
/* Calculate timings for interface */
- if (pmif->kind == controller_un_ata6)
+ if (pmif->kind == controller_un_ata6 || pmif->kind == controller_k2_ata6)
ret = set_timings_udma_ata6( &timing_local[0],
&timing_local[1],
mode);
@@ -1733,6 +1835,10 @@ pmac_ide_udma_enable(ide_drive_t *drive, u16 mode)
return 1;
}
+/*
+ * Check what is the best DMA timing setting for the drive and
+ * call appropriate functions to apply it.
+ */
static int __pmac
pmac_ide_dma_check(ide_drive_t *drive)
{
@@ -1754,11 +1860,13 @@ pmac_ide_dma_check(ide_drive_t *drive)
short mode;
map = XFER_MWDMA;
- if (pmif->kind == controller_kl_ata4 || pmif->kind == controller_un_ata6) {
+ if (pmif->kind == controller_kl_ata4 || pmif->kind == controller_un_ata6
+ || pmif->kind == controller_k2_ata6) {
map |= XFER_UDMA;
if (pmif->cable_80) {
map |= XFER_UDMA_66;
- if (pmif->kind == controller_un_ata6)
+ if (pmif->kind == controller_un_ata6 ||
+ pmif->kind == controller_k2_ata6)
map |= XFER_UDMA_100;
}
}
@@ -1774,6 +1882,10 @@ pmac_ide_dma_check(ide_drive_t *drive)
return 0;
}
+/*
+ * Prepare a DMA transfer. We build the DMA table, adjust the timings for
+ * a read on KeyLargo ATA/66 and mark us as waiting for DMA completion
+ */
static int __pmac
pmac_ide_dma_start(ide_drive_t *drive, int reading)
{
@@ -1802,6 +1914,9 @@ pmac_ide_dma_start(ide_drive_t *drive, int reading)
return 0;
}
+/*
+ * Start a DMA READ command
+ */
static int __pmac
pmac_ide_dma_read(ide_drive_t *drive)
{
@@ -1831,6 +1946,9 @@ pmac_ide_dma_read(ide_drive_t *drive)
return pmac_ide_dma_begin(drive);
}
+/*
+ * Start a DMA WRITE command
+ */
static int __pmac
pmac_ide_dma_write (ide_drive_t *drive)
{
@@ -1865,6 +1983,10 @@ pmac_ide_dma_count (ide_drive_t *drive)
return HWIF(drive)->ide_dma_begin(drive);
}
+/*
+ * Kick the DMA controller into life after the DMA command has been issued
+ * to the drive.
+ */
static int __pmac
pmac_ide_dma_begin (ide_drive_t *drive)
{
@@ -1881,6 +2003,9 @@ pmac_ide_dma_begin (ide_drive_t *drive)
return 0;
}
+/*
+ * After a DMA transfer, make sure the controller is stopped
+ */
static int __pmac
pmac_ide_dma_end (ide_drive_t *drive)
{
@@ -1900,6 +2025,12 @@ pmac_ide_dma_end (ide_drive_t *drive)
return (dstat & (RUN|DEAD|ACTIVE)) != RUN;
}
+/*
+ * Check out that the interrupt we got was for us. We can't always know this
+ * for sure with those Apple interfaces (well, we could on the recent ones but
+ * that's not implemented yet), on the other hand, we don't have shared interrupts
+ * so it's not really a problem
+ */
static int __pmac
pmac_ide_dma_test_irq (ide_drive_t *drive)
{
@@ -1982,6 +2113,10 @@ pmac_ide_dma_lostirq (ide_drive_t *drive)
return 0;
}
+/*
+ * Allocate the data structures needed for using DMA with an interface
+ * and fill the proper list of functions pointers
+ */
static void __init
pmac_ide_setup_dma(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif)
{
@@ -2041,6 +2176,7 @@ pmac_ide_setup_dma(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif)
hwif->atapi_dma = 1;
switch(pmif->kind) {
case controller_un_ata6:
+ case controller_k2_ata6:
hwif->ultra_mask = pmif->cable_80 ? 0x3f : 0x07;
hwif->mwdma_mask = 0x07;
hwif->swdma_mask = 0x00;
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
new file mode 100644
index 000000000000..6501e3a9ede2
--- /dev/null
+++ b/drivers/macintosh/Kconfig
@@ -0,0 +1,156 @@
+
+menu "Macintosh device drivers"
+
+# we want to change this to something like CONFIG_SYSCTRL_CUDA/PMU
+config ADB_CUDA
+ bool "Support for CUDA based PowerMacs"
+ depends on PPC_PMAC && !POWER4
+ help
+ This provides support for CUDA based Power Macintosh systems. This
+ includes most OldWorld PowerMacs, the first generation iMacs, the
+ Blue&White G3 and the "Yikes" G4 (PCI Graphics). All later models
+ should use CONFIG_ADB_PMU instead. It is safe to say Y here even if
+ your machine doesn't have a CUDA.
+
+ If unsure say Y.
+
+config ADB_PMU
+ bool "Support for PMU based PowerMacs"
+ depends on PPC_PMAC
+ help
+ On PowerBooks, iBooks, and recent iMacs and Power Macintoshes, the
+ PMU is an embedded microprocessor whose primary function is to
+ control system power, and battery charging on the portable models.
+ The PMU also controls the ADB (Apple Desktop Bus) which connects to
+ the keyboard and mouse on some machines, as well as the non-volatile
+ RAM and the RTC (real time clock) chip. Say Y to enable support for
+ this device; you should do so if your machine is one of those
+ mentioned above.
+
+config PMAC_PBOOK
+ bool "Power management support for PowerBooks"
+ depends on ADB_PMU
+ ---help---
+ This provides support for putting a PowerBook to sleep; it also
+ enables media bay support. Power management works on the
+ PB2400/3400/3500, Wallstreet, Lombard, and Bronze PowerBook G3 and
+ the Titanium Powerbook G4, as well as the iBooks. You should get
+ the power management daemon, pmud, to make it work and you must have
+ the /dev/pmu device (see the pmud README).
+
+ Get pmud from <ftp://ftp.samba.org/pub/ppclinux/pmud/>.
+
+ If you have a PowerBook, you should say Y here.
+
+ You may also want to compile the dma sound driver as a module and
+ have it autoloaded. The act of removing the module shuts down the
+ sound hardware for more power savings.
+
+config PM
+ bool
+ depends on PPC_PMAC && ADB_PMU && PMAC_PBOOK
+ default y
+
+config PMAC_APM_EMU
+ tristate "APM emulation"
+ depends on PMAC_PBOOK
+
+# made a separate option since backlight may end up beeing used
+# on non-powerbook machines (but only on PMU based ones AFAIK)
+config PMAC_BACKLIGHT
+ bool "Backlight control for LCD screens"
+ depends on ADB_PMU
+ help
+ Say Y here to build in code to manage the LCD backlight on a
+ Macintosh PowerBook. With this code, the backlight will be turned
+ on and off appropriately on power-management and lid-open/lid-closed
+ events; also, the PowerBook button device will be enabled so you can
+ change the screen brightness.
+
+config MAC_FLOPPY
+ bool "Support for PowerMac floppy"
+ depends on PPC_PMAC && !POWER4
+ help
+ If you have a SWIM-3 (Super Woz Integrated Machine 3; from Apple)
+ floppy controller, say Y here. Most commonly found in PowerMacs.
+
+config MAC_SERIAL
+ tristate "Support for PowerMac serial ports (OBSOLETE DRIVER)"
+ depends on PPC_PMAC
+ help
+ This driver is obsolete. Use CONFIG_SERIAL_PMACZILOG in
+ "Character devices --> Serial drivers --> PowerMac z85c30" option.
+
+config ADB
+ bool "Apple Desktop Bus (ADB) support"
+ depends on PPC_PMAC
+ help
+ Apple Desktop Bus (ADB) support is for support of devices which
+ are connected to an ADB port. ADB devices tend to have 4 pins.
+ If you have an Apple Macintosh prior to the iMac, an iBook or
+ PowerBook, or a "Blue and White G3", you probably want to say Y
+ here. Otherwise say N.
+
+config ADB_MACIO
+ bool "Include MacIO (CHRP) ADB driver"
+ depends on ADB && !POWER4
+ help
+ Say Y here to include direct support for the ADB controller in the
+ Hydra chip used on PowerPC Macintoshes of the CHRP type. (The Hydra
+ also includes a MESH II SCSI controller, DBDMA controller, VIA chip,
+ OpenPIC controller and two RS422/Geoports.)
+
+config INPUT_ADBHID
+ bool "Support for ADB input devices (keyboard, mice, ...)"
+ depends on ADB && INPUT=y
+ help
+ Say Y here if you want to have ADB (Apple Desktop Bus) HID devices
+ such as keyboards, mice, joysticks, trackpads or graphic tablets
+ handled by the input layer. If you say Y here, make sure to say Y to
+ the corresponding drivers "Keyboard support" (CONFIG_INPUT_KEYBDEV),
+ "Mouse Support" (CONFIG_INPUT_MOUSEDEV) and "Event interface
+ support" (CONFIG_INPUT_EVDEV) as well.
+
+ If unsure, say Y.
+
+config MAC_EMUMOUSEBTN
+ bool "Support for mouse button 2+3 emulation"
+ depends on INPUT_ADBHID
+ help
+ This provides generic support for emulating the 2nd and 3rd mouse
+ button with keypresses. If you say Y here, the emulation is still
+ disabled by default. The emulation is controlled by these sysctl
+ entries:
+ /proc/sys/dev/mac_hid/mouse_button_emulation
+ /proc/sys/dev/mac_hid/mouse_button2_keycode
+ /proc/sys/dev/mac_hid/mouse_button3_keycode
+
+ If you have an Apple machine with a 1-button mouse, say Y here.
+
+config THERM_WINDTUNNEL
+ tristate "Support for thermal management on Windtunnel G4s"
+ depends on I2C && I2C_KEYWEST && !POWER4
+ help
+ This driver provides some thermostat and fan control for the desktop
+ G4 "Windtunnel"
+
+config THERM_ADT7467
+ tristate "Support for thermal mgmnt on laptops with ADT 7467 chipset"
+ depends on I2C && I2C_KEYWEST && !POWER4
+ help
+ This driver provides some thermostat and fan control for the
+ iBook G4, and the ATI based aluminium PowerBooks, allowing slighlty
+ better fan behaviour by default, and some manual control.
+
+config THERM_PM72
+ tristate "Support for thermal management on PowerMac G5"
+ depends on I2C && I2C_KEYWEST && POWER4
+ help
+ This driver provides thermostat and fan control for the desktop
+ G5 machines.
+
+config ANSLCD
+ bool "Support for ANS LCD display"
+ depends on ADB_CUDA
+
+endmenu
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile
index da5aadbd07da..7cecab08df21 100644
--- a/drivers/macintosh/Makefile
+++ b/drivers/macintosh/Makefile
@@ -8,9 +8,6 @@ obj-$(CONFIG_PPC_PMAC) += macio_asic.o
obj-$(CONFIG_PMAC_PBOOK) += mediabay.o
obj-$(CONFIG_MAC_SERIAL) += macserial.o
-ifneq ($(CONFIG_MAC),y)
- obj-$(CONFIG_NVRAM) += nvram.o
-endif
obj-$(CONFIG_MAC_EMUMOUSEBTN) += mac_hid.o
obj-$(CONFIG_INPUT_ADBHID) += adbhid.o
obj-$(CONFIG_ANSLCD) += ans-lcd.o
@@ -25,3 +22,7 @@ obj-$(CONFIG_ADB_MACIISI) += via-maciisi.o
obj-$(CONFIG_ADB_IOP) += adb-iop.o
obj-$(CONFIG_ADB_PMU68K) += via-pmu68k.o
obj-$(CONFIG_ADB_MACIO) += macio-adb.o
+
+obj-$(CONFIG_THERM_PM72) += therm_pm72.o
+obj-$(CONFIG_THERM_WINDTUNNEL) += therm_windtunnel.o
+obj-$(CONFIG_THERM_ADT7467) += therm_adt7467.o
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
index a20a9e899ef4..f1d34776308e 100644
--- a/drivers/macintosh/adb.c
+++ b/drivers/macintosh/adb.c
@@ -83,6 +83,7 @@ static pid_t adb_probe_task_pid;
static DECLARE_MUTEX(adb_probe_mutex);
static struct completion adb_probe_task_comp;
static int sleepy_trackpad;
+static int autopoll_devs;
int __adb_probe_sync;
#ifdef CONFIG_PMAC_PBOOK
@@ -379,7 +380,7 @@ adb_notify_sleep(struct pmu_sleep_notifier *self, int when)
static int
do_adb_reset_bus(void)
{
- int ret, nret, devs;
+ int ret, nret;
if (adb_controller == NULL)
return -ENXIO;
@@ -390,7 +391,7 @@ do_adb_reset_bus(void)
nret = notifier_call_chain(&adb_client_list, ADB_MSG_PRE_RESET, NULL);
if (nret & NOTIFY_STOP_MASK) {
if (adb_controller->autopoll)
- adb_controller->autopoll(devs);
+ adb_controller->autopoll(autopoll_devs);
return -EBUSY;
}
@@ -416,9 +417,9 @@ do_adb_reset_bus(void)
}
if (!ret) {
- devs = adb_scan_bus();
+ autopoll_devs = adb_scan_bus();
if (adb_controller->autopoll)
- adb_controller->autopoll(devs);
+ adb_controller->autopoll(autopoll_devs);
}
up(&adb_handler_sem);
diff --git a/drivers/macintosh/adbhid.c b/drivers/macintosh/adbhid.c
index 01f95a2687d3..4fa4065f18cc 100644
--- a/drivers/macintosh/adbhid.c
+++ b/drivers/macintosh/adbhid.c
@@ -30,6 +30,8 @@
* To do:
*
* Improve Kensington support.
+ * Split mouse/kbd
+ * Move to syfs
*/
#include <linux/config.h>
@@ -63,6 +65,15 @@ static struct notifier_block adbhid_adb_notifier = {
.notifier_call = adb_message_handler,
};
+/* Some special keys */
+#define ADB_KEY_DEL 0x33
+#define ADB_KEY_CMD 0x37
+#define ADB_KEY_CAPSLOCK 0x39
+#define ADB_KEY_FN 0x3f
+#define ADB_KEY_FWDEL 0x75
+#define ADB_KEY_POWER_OLD 0x7e
+#define ADB_KEY_POWER 0x7f
+
unsigned char adb_to_linux_keycodes[128] = {
30, 31, 32, 33, 35, 34, 44, 45, 46, 47, 86, 48, 16, 17, 18, 19,
21, 20, 2, 3, 4, 5, 7, 6, 13, 10, 8, 12, 9, 11, 27, 24,
@@ -84,8 +95,13 @@ struct adbhid {
unsigned char *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
+
static struct adbhid *adbhid[16] = { 0 };
static void adbhid_probe(void);
@@ -148,28 +164,64 @@ adbhid_keyboard_input(unsigned char *data, int nb, struct pt_regs *regs, int apo
static void
adbhid_input_keycode(int id, int keycode, int repeat, struct pt_regs *regs)
{
+ struct adbhid *ahid = adbhid[id];
int up_flag;
up_flag = (keycode & 0x80);
keycode &= 0x7f;
switch (keycode) {
- case 0x39: /* Generate down/up events for CapsLock everytime. */
- input_regs(&adbhid[id]->input, regs);
- input_report_key(&adbhid[id]->input, KEY_CAPSLOCK, 1);
- input_report_key(&adbhid[id]->input, KEY_CAPSLOCK, 0);
- input_sync(&adbhid[id]->input);
- return;
- case 0x3f: /* ignore Powerbook Fn key */
+ 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;
#ifdef CONFIG_PPC_PMAC
- case 0x7e: /* Power key on PBook 3400 needs remapping */
+ case ADB_KEY_POWER_OLD: /* Power key on PBook 3400 needs remapping */
switch(pmac_call_feature(PMAC_FTR_GET_MB_INFO,
NULL, PMAC_MB_INFO_MODEL, 0)) {
case PMAC_TYPE_COMET:
case PMAC_TYPE_HOOPER:
case PMAC_TYPE_KANGA:
- keycode = 0x7f;
+ keycode = ADB_KEY_POWER;
+ }
+ break;
+ case ADB_KEY_POWER:
+ /* Fn + Command will produce a bogus "power" keycode */
+ if (ahid->flags & FLAG_FN_KEY_PRESSED) {
+ keycode = ADB_KEY_CMD;
+ if (up_flag)
+ ahid->flags &= ~FLAG_POWER_FROM_FN;
+ else
+ ahid->flags |= FLAG_POWER_FROM_FN;
+ } else if (ahid->flags & FLAG_POWER_FROM_FN) {
+ keycode = ADB_KEY_CMD;
+ ahid->flags &= ~FLAG_POWER_FROM_FN;
+ }
+ break;
+ case ADB_KEY_FN:
+ /* Keep track of the Fn key state */
+ if (up_flag) {
+ ahid->flags &= ~FLAG_FN_KEY_PRESSED;
+ /* Emulate Fn+delete = forward delete */
+ if (ahid->flags & FLAG_EMU_FWDEL_DOWN) {
+ ahid->flags &= ~FLAG_EMU_FWDEL_DOWN;
+ keycode = ADB_KEY_FWDEL;
+ break;
+ }
+ } else
+ ahid->flags |= FLAG_FN_KEY_PRESSED;
+ /* Swallow the key press */
+ return;
+ case ADB_KEY_DEL:
+ /* Emulate Fn+delete = forward delete */
+ if (ahid->flags & FLAG_FN_KEY_PRESSED) {
+ keycode = ADB_KEY_FWDEL;
+ if (up_flag)
+ ahid->flags &= ~FLAG_EMU_FWDEL_DOWN;
+ else
+ ahid->flags |= FLAG_EMU_FWDEL_DOWN;
}
break;
#endif /* CONFIG_PPC_PMAC */
@@ -500,6 +552,7 @@ adbhid_input_register(int id, int default_id, int original_handler_id,
adbhid[id]->original_handler_id = original_handler_id;
adbhid[id]->current_handler_id = current_handler_id;
adbhid[id]->mouse_kind = mouse_kind;
+ adbhid[id]->flags = 0;
adbhid[id]->input.private = adbhid[id];
adbhid[id]->input.name = adbhid[id]->name;
adbhid[id]->input.phys = adbhid[id]->phys;
diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c
index c8d7d791aaec..a35d44c77f30 100644
--- a/drivers/macintosh/macio_asic.c
+++ b/drivers/macintosh/macio_asic.c
@@ -23,10 +23,13 @@
#include <asm/prom.h>
#include <asm/pci-bridge.h>
+#define DEBUG
+
+#define MAX_NODE_NAME_SIZE (BUS_ID_SIZE - 12)
+
static struct macio_chip *macio_on_hold;
-static int
-macio_bus_match(struct device *dev, struct device_driver *drv)
+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);
@@ -85,41 +88,42 @@ static int macio_device_probe(struct device *dev)
static int macio_device_remove(struct device *dev)
{
struct macio_dev * macio_dev = to_macio_device(dev);
- struct macio_driver * drv = to_macio_driver(macio_dev->ofdev.dev.driver);
+ struct macio_driver * drv = to_macio_driver(dev->driver);
- if (drv && drv->remove)
+ if (dev->driver && drv->remove)
drv->remove(macio_dev);
macio_dev_put(macio_dev);
return 0;
}
+static void macio_device_shutdown(struct device *dev)
+{
+ struct macio_dev * macio_dev = to_macio_device(dev);
+ struct macio_driver * drv = to_macio_driver(dev->driver);
+
+ if (dev->driver && drv->shutdown)
+ drv->shutdown(macio_dev);
+}
+
static int macio_device_suspend(struct device *dev, u32 state)
{
struct macio_dev * macio_dev = to_macio_device(dev);
- struct macio_driver * drv;
- int error = 0;
+ struct macio_driver * drv = to_macio_driver(dev->driver);
- if (macio_dev->ofdev.dev.driver == NULL)
- return 0;
- drv = to_macio_driver(macio_dev->ofdev.dev.driver);
- if (drv->suspend)
- error = drv->suspend(macio_dev, state);
- return error;
+ if (dev->driver && drv->suspend)
+ return drv->suspend(macio_dev, state);
+ return 0;
}
static int macio_device_resume(struct device * dev)
{
struct macio_dev * macio_dev = to_macio_device(dev);
- struct macio_driver * drv;
- int error = 0;
+ struct macio_driver * drv = to_macio_driver(dev->driver);
- if (macio_dev->ofdev.dev.driver == NULL)
- return 0;
- drv = to_macio_driver(macio_dev->ofdev.dev.driver);
- if (drv->resume)
- error = drv->resume(macio_dev);
- return error;
+ if (dev->driver && drv->resume)
+ return drv->resume(macio_dev);
+ return 0;
}
struct bus_type macio_bus_type = {
@@ -129,8 +133,7 @@ struct bus_type macio_bus_type = {
.resume = macio_device_resume,
};
-static int __init
-macio_bus_driver_init(void)
+static int __init macio_bus_driver_init(void)
{
return bus_register(&macio_bus_type);
}
@@ -155,6 +158,58 @@ static void macio_release_dev(struct device *dev)
}
/**
+ * macio_resource_quirks - tweak or skip some resources for a device
+ * @np: pointer to the device node
+ * @res: resulting resource
+ * @index: index of resource in node
+ *
+ * If this routine returns non-null, then the resource is completely
+ * skipped.
+ */
+static int macio_resource_quirks(struct device_node *np, struct resource *res, int index)
+{
+ if (res->flags & IORESOURCE_MEM) {
+ /* Grand Central has too large resource 0 on some machines */
+ if (index == 0 && !strcmp(np->name, "gc")) {
+ np->addrs[0].size = 0x20000;
+ res->end = res->start + 0x1ffff;
+ }
+ /* Airport has bogus resource 2 */
+ if (index >= 2 && !strcmp(np->name, "radio"))
+ return 1;
+ /* DBDMAs may have bogus sizes */
+ if ((res->start & 0x0001f000) == 0x00008000) {
+ np->addrs[index].size = 0x100;
+ res->end = res->start + 0xff;
+ }
+ /* ESCC parent eats child resources. We could have added a level of hierarchy,
+ * but I don't really feel the need for it */
+ if (!strcmp(np->name, "escc"))
+ return 1;
+ /* ESCC has bogus resources >= 3 */
+ if (index >= 3 && !(strcmp(np->name, "ch-a") && strcmp(np->name, "ch-b")))
+ return 1;
+ /* Media bay has too many resources, keep only first one */
+ if (index > 0 && !strcmp(np->name, "media-bay"))
+ return 1;
+ /* Some older IDE resources have bogus sizes */
+ if (!(strcmp(np->name, "IDE") && strcmp(np->name, "ATA") &&
+ strcmp(np->type, "ide") && strcmp(np->type, "ata"))) {
+ if (index == 0 && np->addrs[0].size > 0x1000) {
+ np->addrs[0].size = 0x1000;
+ res->end = res->start + 0xfff;
+ }
+ if (index == 1 && np->addrs[1].size > 0x100) {
+ np->addrs[1].size = 0x100;
+ res->end = res->start + 0xff;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/**
* macio_add_one_device - Add one device from OF node to the device tree
* @chip: pointer to the macio_chip holding the device
* @np: pointer to the device node in the OF tree
@@ -164,9 +219,11 @@ static void macio_release_dev(struct device *dev)
* be exposed to the bay driver some way...
*/
static struct macio_dev * macio_add_one_device(struct macio_chip *chip, struct device *parent,
- struct device_node *np, struct macio_dev *in_bay)
+ struct device_node *np, struct macio_dev *in_bay,
+ struct resource *parent_res)
{
struct macio_dev *dev;
+ int i, j;
u32 *reg;
if (np == NULL)
@@ -186,22 +243,76 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip, struct d
dev->ofdev.dev.bus = &macio_bus_type;
dev->ofdev.dev.release = macio_release_dev;
+#ifdef DEBUG
+ printk("preparing mdev @%p, ofdev @%p, dev @%p, kobj @%p\n",
+ dev, &dev->ofdev, &dev->ofdev.dev, &dev->ofdev.dev.kobj);
+#endif
+
/* MacIO itself has a different reg, we use it's PCI base */
if (np == chip->of_node) {
- sprintf(dev->ofdev.dev.bus_id, "%1d.%08lx:%.8s", chip->lbus.index,
+ sprintf(dev->ofdev.dev.bus_id, "%1d.%08lx:%.*s", chip->lbus.index,
#ifdef CONFIG_PCI
pci_resource_start(chip->lbus.pdev, 0),
#else
0, /* NuBus may want to do something better here */
#endif
- np->name);
+ MAX_NODE_NAME_SIZE, np->name);
} else {
reg = (u32 *)get_property(np, "reg", NULL);
- sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.8s", chip->lbus.index,
- reg ? *reg : 0, np->name);
+ sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s", chip->lbus.index,
+ reg ? *reg : 0, MAX_NODE_NAME_SIZE, np->name);
}
+ /* For now, we use pre-parsed entries in the device-tree for
+ * interrupt routing and addresses, but we should change that
+ * to dynamically parsed entries and so get rid of most of the
+ * clutter in struct device_node
+ */
+ for (i = j = 0; i < np->n_intrs; i++) {
+ struct resource *res = &dev->interrupt[j];
+
+ if (j >= MACIO_DEV_COUNT_IRQS)
+ break;
+ res->start = np->intrs[i].line;
+ res->flags = IORESOURCE_IO;
+ if (np->intrs[j].sense)
+ res->flags |= IORESOURCE_IRQ_LOWLEVEL;
+ else
+ res->flags |= IORESOURCE_IRQ_HIGHEDGE;
+ res->name = dev->ofdev.dev.bus_id;
+ if (macio_resource_quirks(np, res, i))
+ memset(res, 0, sizeof(struct resource));
+ else
+ j++;
+ }
+ dev->n_interrupts = j;
+ for (i = j = 0; i < np->n_addrs; i++) {
+ struct resource *res = &dev->resource[j];
+
+ if (j >= MACIO_DEV_COUNT_RESOURCES)
+ break;
+ res->start = np->addrs[i].address;
+ res->end = np->addrs[i].address + np->addrs[i].size - 1;
+ res->flags = IORESOURCE_MEM;
+ res->name = dev->ofdev.dev.bus_id;
+ if (macio_resource_quirks(np, res, i))
+ memset(res, 0, sizeof(struct resource));
+ else {
+ j++;
+ /* Currently, we consider failure as harmless, this may
+ * change in the future, once I've found all the device
+ * tree bugs in older machines & worked around them
+ */
+ if (insert_resource(parent_res, res))
+ printk(KERN_WARNING "Can't request resource %d for MacIO"
+ " device %s\n", i, dev->ofdev.dev.bus_id);
+ }
+ }
+ dev->n_resources = j;
+
if (of_device_register(&dev->ofdev) != 0) {
+ printk(KERN_DEBUG"macio: device registration error for %s!\n",
+ dev->ofdev.dev.bus_id);
kfree(dev);
return NULL;
}
@@ -234,25 +345,30 @@ static void macio_pci_add_devices(struct macio_chip *chip)
struct device_node *np, *pnode;
struct macio_dev *rdev, *mdev, *mbdev = NULL, *sdev = NULL;
struct device *parent = NULL;
+ struct resource *root_res = &iomem_resource;
/* Add a node for the macio bus itself */
#ifdef CONFIG_PCI
- if (chip->lbus.pdev)
+ if (chip->lbus.pdev) {
parent = &chip->lbus.pdev->dev;
+ root_res = &chip->lbus.pdev->resource[0];
+ }
#endif
pnode = of_node_get(chip->of_node);
if (pnode == NULL)
return;
- rdev = macio_add_one_device(chip, parent, pnode, NULL);
+ /* Add macio itself to hierarchy */
+ rdev = macio_add_one_device(chip, parent, pnode, NULL, root_res);
if (rdev == NULL)
return;
+ root_res = &rdev->resource[0];
/* First scan 1st level */
for (np = NULL; (np = of_get_next_child(pnode, np)) != NULL;) {
if (!macio_skip_device(np)) {
of_node_get(np);
- mdev = macio_add_one_device(chip, &rdev->ofdev.dev, np, NULL);
+ mdev = macio_add_one_device(chip, &rdev->ofdev.dev, np, NULL, root_res);
if (mdev == NULL)
of_node_put(np);
else if (strncmp(np->name, "media-bay", 9) == 0)
@@ -267,17 +383,20 @@ static void macio_pci_add_devices(struct macio_chip *chip)
for (np = NULL; (np = of_get_next_child(mbdev->ofdev.node, np)) != NULL;)
if (!macio_skip_device(np)) {
of_node_get(np);
- if (macio_add_one_device(chip, &mbdev->ofdev.dev, np, mbdev) == NULL)
+ if (macio_add_one_device(chip, &mbdev->ofdev.dev, np, mbdev,
+ root_res) == NULL)
of_node_put(np);
}
/* Add serial ports if any */
- if (sdev)
+ if (sdev) {
for (np = NULL; (np = of_get_next_child(sdev->ofdev.node, np)) != NULL;)
if (!macio_skip_device(np)) {
of_node_get(np);
- if (macio_add_one_device(chip, &sdev->ofdev.dev, np, NULL) == NULL)
+ if (macio_add_one_device(chip, &sdev->ofdev.dev, np, NULL,
+ root_res) == NULL)
of_node_put(np);
}
+ }
}
@@ -294,6 +413,7 @@ int macio_register_driver(struct macio_driver *drv)
drv->driver.bus = &macio_bus_type;
drv->driver.probe = macio_device_probe;
drv->driver.remove = macio_device_remove;
+ drv->driver.shutdown = macio_device_shutdown;
/* register with core */
count = driver_register(&drv->driver);
@@ -309,6 +429,97 @@ void macio_unregister_driver(struct macio_driver *drv)
driver_unregister(&drv->driver);
}
+/**
+ * macio_request_resource - Request an MMIO resource
+ * @dev: pointer to the device holding the resource
+ * @resource_no: resource number to request
+ * @name: resource name
+ *
+ * Mark memory region number @resource_no associated with MacIO
+ * device @dev as being reserved by owner @name. Do not access
+ * any address inside the memory regions unless this call returns
+ * successfully.
+ *
+ * Returns 0 on success, or %EBUSY on error. A warning
+ * message is also printed on failure.
+ */
+int macio_request_resource(struct macio_dev *dev, int resource_no, const char *name)
+{
+ if (macio_resource_len(dev, resource_no) == 0)
+ return 0;
+
+ if (!request_mem_region(macio_resource_start(dev, resource_no),
+ macio_resource_len(dev, resource_no),
+ name))
+ goto err_out;
+
+ return 0;
+
+err_out:
+ printk (KERN_WARNING "MacIO: Unable to reserve resource #%d:%lx@%lx"
+ " for device %s\n",
+ resource_no,
+ macio_resource_len(dev, resource_no),
+ macio_resource_start(dev, resource_no),
+ dev->ofdev.dev.bus_id);
+ return -EBUSY;
+}
+
+/**
+ * macio_release_resource - Release an MMIO resource
+ * @dev: pointer to the device holding the resource
+ * @resource_no: resource number to release
+ */
+void macio_release_resource(struct macio_dev *dev, int resource_no)
+{
+ if (macio_resource_len(dev, resource_no) == 0)
+ return;
+ release_mem_region(macio_resource_start(dev, resource_no),
+ macio_resource_len(dev, resource_no));
+}
+
+/**
+ * macio_request_resources - Reserve all memory resources
+ * @dev: MacIO device whose resources are to be reserved
+ * @name: Name to be associated with resource.
+ *
+ * Mark all memory regions associated with MacIO device @dev as
+ * being reserved by owner @name. Do not access any address inside
+ * the memory regions unless this call returns successfully.
+ *
+ * Returns 0 on success, or %EBUSY on error. A warning
+ * message is also printed on failure.
+ */
+int macio_request_resources(struct macio_dev *dev, const char *name)
+{
+ int i;
+
+ for (i = 0; i < dev->n_resources; i++)
+ if (macio_request_resource(dev, i, name))
+ goto err_out;
+ return 0;
+
+err_out:
+ while(--i >= 0)
+ macio_release_resource(dev, i);
+
+ return -EBUSY;
+}
+
+/**
+ * macio_release_resources - Release reserved memory resources
+ * @dev: MacIO device whose resources were previously reserved
+ */
+
+void macio_release_resources(struct macio_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < dev->n_resources; i++)
+ macio_release_resource(dev, i);
+}
+
+
#ifdef CONFIG_PCI
static int __devinit macio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -416,3 +627,7 @@ EXPORT_SYMBOL(macio_register_driver);
EXPORT_SYMBOL(macio_unregister_driver);
EXPORT_SYMBOL(macio_dev_get);
EXPORT_SYMBOL(macio_dev_put);
+EXPORT_SYMBOL(macio_request_resource);
+EXPORT_SYMBOL(macio_release_resource);
+EXPORT_SYMBOL(macio_request_resources);
+EXPORT_SYMBOL(macio_release_resources);
diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c
index 89d4aaaf622f..507a419d2666 100644
--- a/drivers/macintosh/mediabay.c
+++ b/drivers/macintosh/mediabay.c
@@ -107,6 +107,11 @@ int media_bay_count = 0;
#define MS_TO_HZ(ms) ((ms * HZ + 999) / 1000)
/*
+ * Wait that number of ms between each step in normal polling mode
+ */
+#define MB_POLL_DELAY 25
+
+/*
* Consider the media-bay ID value stable if it is the same for
* this number of milliseconds
*/
@@ -121,7 +126,7 @@ int media_bay_count = 0;
* Hold the media-bay reset signal true for this many ticks
* after a device is inserted before releasing it.
*/
-#define MB_RESET_DELAY 40
+#define MB_RESET_DELAY 50
/*
* Wait this long after the reset signal is released and before doing
@@ -390,24 +395,28 @@ static void __pmac 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 >= MS_TO_HZ(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) {
+ if (id != bay->content_id) {
+ bay->value_count += MS_TO_HZ(MB_POLL_DELAY);
+ if (bay->value_count >= MS_TO_HZ(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;
+ bay->cd_retry = 0;
#endif
- printk(KERN_INFO "media bay %d is empty\n", bay->index);
+ printk(KERN_INFO "media bay %d is empty\n", bay->index);
+ }
+ }
}
- }
} else {
bay->last_value = id;
bay->value_count = 0;
@@ -496,8 +505,12 @@ static void __pmac media_bay_step(int i)
poll_media_bay(bay);
/* If timer expired or polling IDE busy, run state machine */
- if ((bay->state != mb_ide_waiting) && (bay->timer != 0) && ((--bay->timer) != 0))
- return;
+ if ((bay->state != mb_ide_waiting) && (bay->timer != 0)) {
+ bay->timer -= MS_TO_HZ(MB_POLL_DELAY);
+ if (bay->timer > 0)
+ return;
+ bay->timer = 0;
+ }
switch(bay->state) {
case mb_powering_up:
@@ -572,12 +585,13 @@ static void __pmac media_bay_step(int i)
}
break;
} else if (bay->timer > 0)
- bay->timer--;
- if (bay->timer == 0) {
+ bay->timer -= MS_TO_HZ(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 */
@@ -630,7 +644,7 @@ static int __pmac media_bay_task(void *x)
}
current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(MS_TO_HZ(10));
+ schedule_timeout(MS_TO_HZ(MB_POLL_DELAY));
if (signal_pending(current))
return 0;
}
@@ -645,17 +659,16 @@ static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_ma
ofnode = mdev->ofdev.node;
- if (!request_OF_resource(ofnode, 0, NULL))
- return -ENXIO;
-
+ if (macio_resource_count(mdev) < 1)
+ return -ENODEV;
+ if (macio_request_resources(mdev, "media-bay"))
+ return -EBUSY;
/* Media bay registers are located at the beginning of the
* mac-io chip, we get the parent address for now (hrm...)
*/
- if (ofnode->parent->n_addrs == 0)
- return -ENODEV;
regbase = (volatile u32 *)ioremap(ofnode->parent->addrs[0].address, 0x100);
if (regbase == NULL) {
- release_OF_resource(ofnode, 0);
+ macio_release_resources(mdev);
return -ENOMEM;
}
@@ -684,13 +697,13 @@ static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_ma
bay->state = mb_empty;
do {
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(MS_TO_HZ(10));
+ schedule_timeout(MS_TO_HZ(MB_POLL_DELAY));
media_bay_step(i);
} while((bay->state != mb_empty) &&
(bay->state != mb_up));
/* Mark us ready by filling our mdev data */
- dev_set_drvdata(&mdev->ofdev.dev, bay);
+ macio_set_drvdata(mdev, bay);
/* Startup kernel thread */
if (i == 0)
@@ -702,7 +715,7 @@ static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_ma
static int __pmac media_bay_suspend(struct macio_dev *mdev, u32 state)
{
- struct media_bay_info *bay = dev_get_drvdata(&mdev->ofdev.dev);
+ struct media_bay_info *bay = macio_get_drvdata(mdev);
if (state != mdev->ofdev.dev.power_state && state >= 2) {
down(&bay->lock);
@@ -710,7 +723,7 @@ static int __pmac media_bay_suspend(struct macio_dev *mdev, u32 state)
set_mb_power(bay, 0);
up(&bay->lock);
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(MS_TO_HZ(10));
+ schedule_timeout(MS_TO_HZ(MB_POLL_DELAY));
mdev->ofdev.dev.power_state = state;
}
return 0;
@@ -718,7 +731,7 @@ static int __pmac media_bay_suspend(struct macio_dev *mdev, u32 state)
static int __pmac media_bay_resume(struct macio_dev *mdev)
{
- struct media_bay_info *bay = dev_get_drvdata(&mdev->ofdev.dev);
+ struct media_bay_info *bay = macio_get_drvdata(mdev);
if (mdev->ofdev.dev.power_state != 0) {
mdev->ofdev.dev.power_state = 0;
@@ -746,7 +759,7 @@ static int __pmac media_bay_resume(struct macio_dev *mdev)
#endif
do {
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(MS_TO_HZ(10));
+ schedule_timeout(MS_TO_HZ(MB_POLL_DELAY));
media_bay_step(bay->index);
} while((bay->state != mb_empty) &&
(bay->state != mb_up));
diff --git a/drivers/macintosh/therm_adt7467.c b/drivers/macintosh/therm_adt7467.c
new file mode 100644
index 000000000000..5de422f0a5fe
--- /dev/null
+++ b/drivers/macintosh/therm_adt7467.c
@@ -0,0 +1,562 @@
+/*
+ * Device driver for the i2c thermostat found on the iBook G4, Albook G4
+ *
+ * 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
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/wait.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
+
+static u8 TEMP_REG[3] = {0x26, 0x25, 0x27}; /* local, cpu, gpu */
+static u8 LIMIT_REG[3] = {0x6b, 0x6a, 0x6c}; /* local, cpu, gpu */
+static u8 MANUAL_MODE[2] = {0x5c, 0x5d};
+static u8 REM_CONTROL[2] = {0x00, 0x40};
+static u8 FAN_SPEED[2] = {0x28, 0x2a};
+static u8 FAN_SPD_SET[2] = {0x30, 0x31};
+
+static u8 default_limits_local[3] = {70, 50, 70}; /* local, cpu, gpu */
+static u8 default_limits_chip[3] = {80, 65, 80}; /* local, cpu, gpu */
+
+static int limit_adjust = 0;
+static int fan_speed = -1;
+
+MODULE_AUTHOR("Colin Leroy <colin@colino.net>");
+MODULE_DESCRIPTION("Driver for ADT7467 thermostat in iBook G4");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(limit_adjust,"i");
+MODULE_PARM_DESC(limit_adjust,"Adjust maximum temperatures (50°C cpu, 70°C gpu) by N °C.");
+MODULE_PARM(fan_speed,"i");
+MODULE_PARM_DESC(fan_speed,"Specify fan speed (0-255) when lim < temp < lim+8 (default 128)");
+
+struct thermostat {
+ struct i2c_client clt;
+ u8 cached_temp[3];
+ u8 initial_limits[3];
+ u8 limits[3];
+ int last_speed[2];
+ int overriding[2];
+};
+
+static enum {ADT7460, ADT7467} therm_type;
+static int therm_bus, therm_address;
+static struct of_device * of_dev;
+static struct thermostat* thermostat;
+static pid_t monitor_thread_id;
+static int monitor_running;
+static struct completion monitor_task_compl;
+
+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);
+
+static int
+write_reg(struct thermostat* th, int reg, u8 data)
+{
+ u8 tmp[2];
+ int rc;
+
+ tmp[0] = reg;
+ tmp[1] = data;
+ rc = i2c_master_send(&th->clt, (const char *)tmp, 2);
+ if (rc < 0)
+ return rc;
+ if (rc != 2)
+ return -ENODEV;
+ return 0;
+}
+
+static int
+read_reg(struct thermostat* th, int reg)
+{
+ u8 reg_addr, data;
+ int rc;
+
+ reg_addr = (u8)reg;
+ 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);
+ 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 (monitor_running) {
+ monitor_running = 0;
+ wait_for_completion(&monitor_task_compl);
+ }
+
+ printk(KERN_INFO "adt746x: Putting max temperatures back from %d, %d, %d,"
+ " to %d, %d, %d, (°C)\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 = {
+ .name ="Apple Thermostat ADT7467",
+ .id =0xDEAD7467,
+ .flags =I2C_DF_NOTIFY,
+ .attach_adapter =&attach_thermostat,
+ .detach_adapter =&detach_thermostat,
+};
+
+static int read_fan_speed(struct thermostat *th, u8 addr)
+{
+ u8 tmp[2];
+ u16 res;
+
+ /* should start with low byte */
+ tmp[1] = read_reg(th, addr);
+ tmp[0] = read_reg(th, addr + 1);
+
+ res = tmp[1] + (tmp[0] << 8);
+ return (90000*60)/res;
+}
+
+static void write_both_fan_speed(struct thermostat *th, int speed)
+{
+ write_fan_speed(th, speed, 0);
+ if (therm_type == ADT7460)
+ write_fan_speed(th, speed, 1);
+}
+
+static void write_fan_speed(struct thermostat *th, int speed, int fan)
+{
+ u8 manual;
+
+ if (speed > 0xff)
+ speed = 0xff;
+ else if (speed < -1)
+ speed = 0;
+
+ if (therm_type == ADT7467 && fan == 1)
+ return;
+
+ if (th->last_speed[fan] != speed) {
+ if (speed == -1)
+ printk(KERN_INFO "adt746x: Setting speed to: automatic for %s fan.\n",
+ fan?"GPU":"CPU");
+ else
+ printk(KERN_INFO "adt746x: Setting speed to: %d for %s fan.\n",
+ speed, fan?"GPU":"CPU");
+ } else
+ return;
+
+ if (speed >= 0) {
+ manual = read_reg(th, MANUAL_MODE[fan]);
+ write_reg(th, MANUAL_MODE[fan], manual|MANUAL_MASK);
+ write_reg(th, FAN_SPD_SET[fan], speed);
+ } else {
+ /* back to automatic */
+ if(therm_type == ADT7460) {
+ manual = read_reg(th, MANUAL_MODE[fan]) & (~MANUAL_MASK);
+ write_reg(th, MANUAL_MODE[fan], manual|REM_CONTROL[fan]);
+ } else {
+ manual = read_reg(th, MANUAL_MODE[fan]);
+ write_reg(th, MANUAL_MODE[fan], manual&(~AUTO_MASK));
+ }
+ }
+
+ th->last_speed[fan] = speed;
+}
+
+static int monitor_task(void *arg)
+{
+ struct thermostat* th = arg;
+ u8 temps[3];
+ u8 lims[3];
+ int i;
+#ifdef DEBUG
+ int mfan_speed;
+#endif
+
+ lock_kernel();
+ daemonize("kfand");
+ unlock_kernel();
+ strcpy(current->comm, "thermostat");
+ monitor_running = 1;
+
+ while(monitor_running)
+ {
+ set_task_state(current, TASK_UNINTERRUPTIBLE);
+ schedule_timeout(2*HZ);
+
+ /* Check status */
+ /* local : chip */
+ /* remote 1: CPU ?*/
+ /* remote 2: GPU ?*/
+#ifndef DEBUG
+ if (fan_speed != -1) {
+#endif
+ for (i = 0; i < 3; i++) {
+ temps[i] = read_reg(th, TEMP_REG[i]);
+ lims[i] = th->limits[i];
+ }
+#ifndef DEBUG
+ }
+#endif
+ if (fan_speed != -1) {
+ int lastvar = 0; /* for iBook */
+ for (i = 1; i < 3; i++) { /* we don't care about local sensor */
+ int started = 0;
+ int fan_number = (therm_type == ADT7460 && i == 2);
+ int var = temps[i] - lims[i];
+ if (var > 8) {
+ if (th->overriding[fan_number] == 0)
+ printk(KERN_INFO "adt746x: Limit exceeded by %d°C, overriding specified fan speed for %s.\n",
+ var, fan_number?"GPU":"CPU");
+ th->overriding[fan_number] = 1;
+ write_fan_speed(th, 255, fan_number);
+ started = 1;
+ } else if ((!th->overriding[fan_number] || var < 6) && var > 0) {
+ if (th->overriding[fan_number] == 1)
+ printk(KERN_INFO "adt746x: Limit exceeded by %d°C, setting speed to specified for %s.\n",
+ var, fan_number?"GPU":"CPU");
+ th->overriding[fan_number] = 0;
+ write_fan_speed(th, fan_speed, fan_number);
+ started = 1;
+ } else if (var < -1) {
+ /* don't stop iBook fan if GPU is cold and CPU is not
+ * so cold (lastvar >= -1) */
+ if (therm_type == ADT7460 || lastvar < -1 || i == 1) {
+ if (th->last_speed[fan_number] != 0)
+ printk(KERN_INFO "adt746x: Stopping %s fan.\n",
+ fan_number?"GPU":"CPU");
+ write_fan_speed(th, 0, fan_number);
+ }
+ }
+
+ lastvar = var;
+
+ if (started && therm_type == ADT7467)
+ break; /* we don't want to re-stop the fan
+ * if CPU is heating and GPU is not */
+ }
+ }
+#ifdef DEBUG
+ mfan_speed = read_fan_speed(th, FAN_SPEED[0]);
+ /* only one fan in the iBook G4 */
+
+ if (temps[0] != th->cached_temp[0]
+ || temps[1] != th->cached_temp[1]
+ || temps[2] != th->cached_temp[2]) {
+ printk(KERN_INFO "adt746x: Temperature infos:"
+ " thermostats: %d,%d,%d °C;"
+ " limits: %d,%d,%d °C;"
+ " fan speed: %d RPM\n",
+ temps[0], temps[1], temps[2],
+ lims[0], lims[1], lims[2],
+ mfan_speed);
+ }
+ th->cached_temp[0] = temps[0];
+ th->cached_temp[1] = temps[1];
+ th->cached_temp[2] = temps[2];
+#endif
+ }
+
+ complete_and_exit(&monitor_task_compl, 0);
+ return 0;
+}
+
+static void
+set_limit(struct thermostat *th, int i)
+{
+ /* Set CPU 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;
+}
+
+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;
+ th->clt.id = 0xDEAD7467;
+ 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=128;
+
+ 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 (°C)\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("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;
+
+ if (fan_speed != -1) {
+ write_both_fan_speed(th, 0);
+ } else {
+ write_both_fan_speed(th, -1);
+ }
+
+ init_completion(&monitor_task_compl);
+
+ monitor_thread_id = kernel_thread(monitor_task, th,
+ SIGCHLD | CLONE_KERNEL);
+
+ return 0;
+}
+
+/*
+ * 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...
+ *
+ */
+#define BUILD_SHOW_FUNC_DEG(name, data) \
+static ssize_t show_##name(struct device *dev, char *buf) \
+{ \
+ return sprintf(buf, "%d°C\n", data); \
+}
+#define BUILD_SHOW_FUNC_INT(name, data) \
+static ssize_t show_##name(struct device *dev, char *buf) \
+{ \
+ return sprintf(buf, "%d\n", data); \
+}
+
+#define BUILD_STORE_FUNC_DEG(name, data) \
+static ssize_t store_##name(struct device *dev, const char *buf, size_t n) \
+{ \
+ int val; \
+ int i; \
+ val = simple_strtol(buf, NULL, 10); \
+ printk(KERN_INFO "Adjusting limits by %d°C\n", val); \
+ limit_adjust = val; \
+ for (i=0; i < 3; i++) \
+ set_limit(thermostat, i); \
+ return n; \
+}
+
+#define BUILD_STORE_FUNC_INT(name, data) \
+static ssize_t store_##name(struct device *dev, const char *buf, size_t n) \
+{ \
+ u32 val; \
+ val = simple_strtoul(buf, NULL, 10); \
+ if (val < 0 || val > 255) \
+ return -EINVAL; \
+ printk(KERN_INFO "Setting fan speed to %d\n", val); \
+ data = val; \
+ return n; \
+}
+
+BUILD_SHOW_FUNC_DEG(cpu_temperature, (read_reg(thermostat, TEMP_REG[1])))
+BUILD_SHOW_FUNC_DEG(gpu_temperature, (read_reg(thermostat, TEMP_REG[2])))
+BUILD_SHOW_FUNC_DEG(cpu_limit, thermostat->limits[1])
+BUILD_SHOW_FUNC_DEG(gpu_limit, thermostat->limits[2])
+
+BUILD_SHOW_FUNC_INT(specified_fan_speed, fan_speed)
+BUILD_SHOW_FUNC_INT(cpu_fan_speed, (read_fan_speed(thermostat, FAN_SPEED[0])))
+BUILD_SHOW_FUNC_INT(gpu_fan_speed, (read_fan_speed(thermostat, 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)
+
+static DEVICE_ATTR(cpu_temperature, S_IRUGO,
+ show_cpu_temperature,NULL);
+static DEVICE_ATTR(gpu_temperature, S_IRUGO,
+ show_gpu_temperature,NULL);
+static DEVICE_ATTR(cpu_limit, S_IRUGO,
+ show_cpu_limit, NULL);
+static DEVICE_ATTR(gpu_limit, S_IRUGO,
+ show_gpu_limit, NULL);
+
+static DEVICE_ATTR(specified_fan_speed, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
+ show_specified_fan_speed,store_specified_fan_speed);
+
+static DEVICE_ATTR(cpu_fan_speed, S_IRUGO,
+ show_cpu_fan_speed, NULL);
+static DEVICE_ATTR(gpu_fan_speed, S_IRUGO,
+ show_gpu_fan_speed, NULL);
+
+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)
+{
+ struct device_node* np;
+ u32 *prop;
+
+ /* Currently, we only deal with the iBook G4, we will support
+ * all "2003" powerbooks later on
+ */
+ 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 = (u32 *)get_property(np, "reg", NULL);
+ if (!prop)
+ return -ENODEV;
+ therm_bus = ((*prop) >> 8) & 0x0f;
+ 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);
+
+ of_dev = of_platform_device_create(np, "temperatures");
+
+ if (of_dev == NULL) {
+ printk(KERN_ERR "Can't register temperatures device !\n");
+ return -ENODEV;
+ }
+
+ device_create_file(&of_dev->dev, &dev_attr_cpu_temperature);
+ device_create_file(&of_dev->dev, &dev_attr_gpu_temperature);
+ device_create_file(&of_dev->dev, &dev_attr_cpu_limit);
+ device_create_file(&of_dev->dev, &dev_attr_gpu_limit);
+ 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_cpu_fan_speed);
+ if(therm_type == ADT7460)
+ device_create_file(&of_dev->dev, &dev_attr_gpu_fan_speed);
+
+#ifndef CONFIG_I2C_KEYWEST
+ request_module("i2c-keywest");
+#endif
+
+ return i2c_add_driver(&thermostat_driver);
+}
+
+static void __exit
+thermostat_exit(void)
+{
+ if (of_dev) {
+ device_remove_file(&of_dev->dev, &dev_attr_cpu_temperature);
+ device_remove_file(&of_dev->dev, &dev_attr_gpu_temperature);
+ device_remove_file(&of_dev->dev, &dev_attr_cpu_limit);
+ device_remove_file(&of_dev->dev, &dev_attr_gpu_limit);
+ 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_cpu_fan_speed);
+ if(therm_type == ADT7460)
+ device_remove_file(&of_dev->dev, &dev_attr_gpu_fan_speed);
+ of_device_unregister(of_dev);
+ }
+ i2c_del_driver(&thermostat_driver);
+}
+
+module_init(thermostat_init);
+module_exit(thermostat_exit);
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c
new file mode 100644
index 000000000000..c6af16a9e3eb
--- /dev/null
+++ b/drivers/macintosh/therm_pm72.c
@@ -0,0 +1,1241 @@
+/*
+ * Device driver for the thermostats & fan controller of the
+ * Apple G5 "PowerMac7,2" desktop machines.
+ *
+ * (c) Copyright IBM Corp. 2003
+ *
+ * Maintained by: Benjamin Herrenschmidt
+ * <benh@kernel.crashing.org>
+ *
+ *
+ * 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 7.0 snapshot property lists.
+ *
+ * As far as the CPUs control loops are concerned, I use the
+ * calibration & PID constants provided by the EEPROM,
+ * I do _not_ embed any value from the property lists, as the ones
+ * provided by Darwin 7.0 seem to always have an older version that
+ * what I've seen on the actual computers.
+ * It would be interesting to verify that though. Darwin has a
+ * version code of 1.0.0d11 for all control loops it seems, while
+ * so far, the machines EEPROMs contain a dataset versioned 1.0.0f
+ *
+ * Darwin doesn't provide source to all parts, some missing
+ * bits like the AppleFCU driver or the actual scale of some
+ * of the values returned by sensors had to be "guessed" some
+ * way... or based on what Open Firmware does.
+ *
+ * I didn't yet figure out how to get the slots power consumption
+ * out of the FCU, so that part has not been implemented yet and
+ * the slots fan is set to a fixed 50% PWM, hoping this value is
+ * safe enough ...
+ *
+ * Note: I have observed strange oscillations of the CPU control
+ * loop on a dual G5 here. When idle, the CPU exhaust fan tend to
+ * oscillates slowly (over several minutes) between the minimum
+ * of 300RPMs and approx. 1000 RPMs. I don't know what is causing
+ * this, it could be some incorrect constant or an error in the
+ * way I ported the algorithm, or it could be just normal. I
+ * don't have full understanding on the way Apple tweaked the PID
+ * algorithm for the CPU control, it is definitely not a standard
+ * implementation...
+ *
+ * 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
+ * - Deal with fan failures
+ *
+ * History:
+ *
+ * Nov. 13, 2003 : 0.5
+ * - First release
+ *
+ * Nov. 14, 2003 : 0.6
+ * - Read fan speed from FCU, low level fan routines now deal
+ * with errors & check fan status, though higher level don't
+ * do much.
+ * - Move a bunch of definitions to .h file
+ *
+ * Nov. 18, 2003 : 0.7
+ * - Fix build on ppc64 kernel
+ * - Move back statics definitions to .c file
+ * - Avoid calling schedule_timeout with a negative number
+ *
+ * Dev. 18, 2003 : 0.8
+ * - Fix typo when reading back fan speed on 2 CPU machines
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/i2c.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/i2c-dev.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 "therm_pm72.h"
+
+#define VERSION "0.8"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...) printk(args)
+#else
+#define DBG(args...)
+#endif
+
+
+/*
+ * Driver statics
+ */
+
+static struct of_device * of_dev;
+static struct i2c_adapter * u3_0;
+static struct i2c_adapter * u3_1;
+static struct i2c_client * fcu;
+static struct cpu_pid_state cpu_state[2];
+static struct backside_pid_state backside_state;
+static struct drives_pid_state drives_state;
+static int state;
+static int cpu_count;
+static pid_t ctrl_task;
+static struct completion ctrl_complete;
+static int critical_state;
+static DECLARE_MUTEX(driver_lock);
+
+/*
+ * 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 =
+{
+ .name = "therm_pm72",
+ .id = 0xDEADBEEF,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = therm_pm72_attach,
+ .detach_adapter = therm_pm72_detach,
+};
+
+
+static inline void wait_ms(unsigned int ms)
+{
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1 + (ms * HZ + 999) / 1000);
+}
+
+/*
+ * Utility function to create an i2c_client structure and
+ * attach it to one of u3 adapters
+ */
+static struct i2c_client *attach_i2c_chip(int id, const char *name)
+{
+ struct i2c_client *clt;
+ struct i2c_adapter *adap;
+
+ if (id & 0x100)
+ adap = u3_1;
+ else
+ adap = u3_0;
+ 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;
+ clt->id = 0xDEADBEEF;
+ strncpy(clt->name, name, I2C_NAME_SIZE-1);
+
+ if (i2c_attach_client(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);
+}
+
+/*
+ * Here are the i2c chip access wrappers
+ */
+static int read_smon_adc(struct i2c_client *chip, int chan)
+{
+ int ctrl;
+
+ ctrl = i2c_smbus_read_byte_data(chip, 1);
+ i2c_smbus_write_byte_data(chip, 1, (ctrl & 0x1f) | (chan << 5));
+ wait_ms(1);
+ return le16_to_cpu(i2c_smbus_read_word_data(chip, 4)) >> 6;
+}
+
+static int fan_read_reg(int reg, unsigned char *buf, int nb)
+{
+ int tries, nr, nw;
+
+ buf[0] = reg;
+ tries = 0;
+ for (;;) {
+ nw = i2c_master_send(fcu, buf, 1);
+ if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
+ break;
+ wait_ms(10);
+ ++tries;
+ }
+ if (nw <= 0) {
+ printk(KERN_ERR "Failure writing address to FCU: %d", nw);
+ return -EIO;
+ }
+ tries = 0;
+ for (;;) {
+ nr = i2c_master_recv(fcu, buf, nb);
+ if (nr > 0 || (nr < 0 && nr != ENODEV) || tries >= 100)
+ break;
+ wait_ms(10);
+ ++tries;
+ }
+ if (nr <= 0)
+ printk(KERN_ERR "Failure reading data from FCU: %d", nw);
+ return nr;
+}
+
+static int fan_write_reg(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(fcu, buf, nb);
+ if (nw > 0 || (nw < 0 && nw != EIO) || tries >= 100)
+ break;
+ wait_ms(10);
+ ++tries;
+ }
+ if (nw < 0)
+ printk(KERN_ERR "Failure writing to FCU: %d", nw);
+ return nw;
+}
+
+static int set_rpm_fan(int fan, int rpm)
+{
+ unsigned char buf[2];
+ int rc;
+
+ if (rpm < 300)
+ rpm = 300;
+ else if (rpm > 8191)
+ rpm = 8191;
+ buf[0] = rpm >> 5;
+ buf[1] = rpm << 3;
+ rc = fan_write_reg(0x10 + (fan * 2), buf, 2);
+ if (rc < 0)
+ return -EIO;
+ return 0;
+}
+
+static int get_rpm_fan(int fan, int programmed)
+{
+ unsigned char failure;
+ unsigned char active;
+ unsigned char buf[2];
+ int rc, reg_base;
+
+ rc = fan_read_reg(0xb, &failure, 1);
+ if (rc != 1)
+ return -EIO;
+ if ((failure & (1 << fan)) != 0)
+ return -EFAULT;
+ rc = fan_read_reg(0xd, &active, 1);
+ if (rc != 1)
+ return -EIO;
+ if ((active & (1 << fan)) == 0)
+ return -ENXIO;
+
+ /* Programmed value or real current speed */
+ reg_base = programmed ? 0x10 : 0x11;
+ rc = fan_read_reg(reg_base + (fan * 2), buf, 2);
+ if (rc != 2)
+ return -EIO;
+
+ return (buf[0] << 5) | buf[1] >> 3;
+}
+
+static int set_pwm_fan(int fan, int pwm)
+{
+ unsigned char buf[2];
+ int rc;
+
+ if (pwm < 10)
+ pwm = 10;
+ else if (pwm > 100)
+ pwm = 100;
+ pwm = (pwm * 2559) / 1000;
+ buf[0] = pwm;
+ rc = fan_write_reg(0x30 + (fan * 2), buf, 1);
+ if (rc < 0)
+ return rc;
+ return 0;
+}
+
+static int get_pwm_fan(int fan)
+{
+ unsigned char failure;
+ unsigned char active;
+ unsigned char buf[2];
+ int rc;
+
+ rc = fan_read_reg(0x2b, &failure, 1);
+ if (rc != 1)
+ return -EIO;
+ if ((failure & (1 << fan)) != 0)
+ return -EFAULT;
+ rc = fan_read_reg(0x2d, &active, 1);
+ if (rc != 1)
+ return -EIO;
+ if ((active & (1 << fan)) == 0)
+ return -ENXIO;
+
+ /* Programmed value or real current speed */
+ rc = fan_read_reg(0x30 + (fan * 2), buf, 1);
+ if (rc != 1)
+ return -EIO;
+
+ return (buf[0] * 1000) / 2559;
+}
+
+/*
+ * Utility routine to read the CPU calibration EEPROM data
+ * from the device-tree
+ */
+static int read_eeprom(int cpu, struct mpu_data *out)
+{
+ struct device_node *np;
+ char nodename[64];
+ u8 *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 == NULL) {
+ printk(KERN_ERR "therm_pm72: Failed to retreive cpuid node from device-tree\n");
+ return -ENODEV;
+ }
+ data = (u8 *)get_property(np, "cpuid", &len);
+ if (data == NULL) {
+ printk(KERN_ERR "therm_pm72: Failed to retreive cpuid property from device-tree\n");
+ of_node_put(np);
+ return -ENODEV;
+ }
+ memcpy(out, data, sizeof(struct mpu_data));
+ of_node_put(np);
+
+ return 0;
+}
+
+/*
+ * 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...
+ *
+ * That sucks a bit, we take the lock because FIX32TOPRINT evaluates
+ * the input twice... I accept patches :)
+ */
+#define BUILD_SHOW_FUNC_FIX(name, data) \
+static ssize_t show_##name(struct device *dev, char *buf) \
+{ \
+ ssize_t r; \
+ down(&driver_lock); \
+ r = sprintf(buf, "%d.%03d", FIX32TOPRINT(data)); \
+ up(&driver_lock); \
+ return r; \
+}
+#define BUILD_SHOW_FUNC_INT(name, data) \
+static ssize_t show_##name(struct device *dev, char *buf) \
+{ \
+ 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(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(backside_temperature, backside_state.last_temp)
+BUILD_SHOW_FUNC_INT(backside_fan_pwm, backside_state.pwm)
+
+BUILD_SHOW_FUNC_FIX(drives_temperature, drives_state.last_temp)
+BUILD_SHOW_FUNC_INT(drives_fan_rpm, drives_state.rpm)
+
+static DEVICE_ATTR(cpu0_temperature,S_IRUGO,show_cpu0_temperature,NULL);
+static DEVICE_ATTR(cpu0_voltage,S_IRUGO,show_cpu0_voltage,NULL);
+static DEVICE_ATTR(cpu0_current,S_IRUGO,show_cpu0_current,NULL);
+static DEVICE_ATTR(cpu0_exhaust_fan_rpm,S_IRUGO,show_cpu0_exhaust_fan_rpm,NULL);
+static DEVICE_ATTR(cpu0_intake_fan_rpm,S_IRUGO,show_cpu0_intake_fan_rpm,NULL);
+
+static DEVICE_ATTR(cpu1_temperature,S_IRUGO,show_cpu1_temperature,NULL);
+static DEVICE_ATTR(cpu1_voltage,S_IRUGO,show_cpu1_voltage,NULL);
+static DEVICE_ATTR(cpu1_current,S_IRUGO,show_cpu1_current,NULL);
+static DEVICE_ATTR(cpu1_exhaust_fan_rpm,S_IRUGO,show_cpu1_exhaust_fan_rpm,NULL);
+static DEVICE_ATTR(cpu1_intake_fan_rpm,S_IRUGO,show_cpu1_intake_fan_rpm,NULL);
+
+static DEVICE_ATTR(backside_temperature,S_IRUGO,show_backside_temperature,NULL);
+static DEVICE_ATTR(backside_fan_pwm,S_IRUGO,show_backside_fan_pwm,NULL);
+
+static DEVICE_ATTR(drives_temperature,S_IRUGO,show_drives_temperature,NULL);
+static DEVICE_ATTR(drives_fan_rpm,S_IRUGO,show_drives_fan_rpm,NULL);
+
+/*
+ * CPUs fans control loop
+ */
+static void do_monitor_cpu(struct cpu_pid_state *state)
+{
+ s32 temp, voltage, current_a, power, power_target;
+ s32 integral, derivative, proportional, adj_in_target, sval;
+ s64 integ_p, deriv_p, prop_p, sum;
+ int i, intake, rc;
+
+ DBG("cpu %d:\n", state->index);
+
+ /* Read current fan status */
+ if (state->index == 0)
+ rc = get_rpm_fan(CPUA_EXHAUST_FAN_RPM_ID, !RPM_PID_USE_ACTUAL_SPEED);
+ else
+ rc = get_rpm_fan(CPUB_EXHAUST_FAN_RPM_ID, !RPM_PID_USE_ACTUAL_SPEED);
+ if (rc < 0) {
+ printk(KERN_WARNING "Error %d reading CPU %d exhaust fan !\n",
+ rc, state->index);
+ /* XXX What do we do now ? */
+ } else
+ state->rpm = rc;
+ DBG(" current rpm: %d\n", state->rpm);
+
+ /* Get some sensor readings and scale it */
+ temp = read_smon_adc(state->monitor, 1);
+ voltage = read_smon_adc(state->monitor, 3);
+ current_a = read_smon_adc(state->monitor, 4);
+
+ /* Fixup temperature according to diode calibration
+ */
+ DBG(" temp raw: %04x, m_diode: %04x, b_diode: %04x\n",
+ temp, state->mpu.mdiode, state->mpu.bdiode);
+ temp = ((s32)temp * (s32)state->mpu.mdiode + ((s32)state->mpu.bdiode << 12)) >> 2;
+ state->last_temp = temp;
+ DBG(" temp: %d.%03d\n", FIX32TOPRINT(temp));
+
+ /* Check tmax, increment overtemp if we are there. At tmax+8, we go
+ * full blown immediately and try to trigger a shutdown
+ */
+ if (temp >= ((state->mpu.tmax + 8) << 16)) {
+ printk(KERN_WARNING "Warning ! CPU %d temperature way above maximum (%d) !\n",
+ state->index, temp >> 16);
+ state->overtemp = CPU_MAX_OVERTEMP;
+ } else if (temp > (state->mpu.tmax << 16))
+ state->overtemp++;
+ else
+ state->overtemp = 0;
+ if (state->overtemp >= CPU_MAX_OVERTEMP)
+ critical_state = 1;
+ if (state->overtemp > 0) {
+ state->rpm = state->mpu.rmaxn_exhaust_fan;
+ state->intake_rpm = intake = state->mpu.rmaxn_intake_fan;
+ goto do_set_fans;
+ }
+
+ /* Scale other sensor values according to fixed scales
+ * obtained in Darwin and calculate power from I and V
+ */
+ state->voltage = voltage *= ADC_CPU_VOLTAGE_SCALE;
+ state->current_a = current_a *= ADC_CPU_CURRENT_SCALE;
+ power = (((u64)current_a) * ((u64)voltage)) >> 16;
+
+ /* Calculate power target value (could be done once for all)
+ * and convert to a 16.16 fp number
+ */
+ power_target = ((u32)(state->mpu.pmaxh - state->mpu.padjmax)) << 16;
+
+ DBG(" current: %d.%03d, voltage: %d.%03d\n",
+ FIX32TOPRINT(current_a), FIX32TOPRINT(voltage));
+ DBG(" power: %d.%03d W, target: %d.%03d, error: %d.%03d\n", FIX32TOPRINT(power),
+ FIX32TOPRINT(power_target), FIX32TOPRINT(power_target - power));
+
+ /* Store temperature and power in history array */
+ state->cur_temp = (state->cur_temp + 1) % CPU_TEMP_HISTORY_SIZE;
+ state->temp_history[state->cur_temp] = temp;
+ state->cur_power = (state->cur_power + 1) % state->count_power;
+ state->power_history[state->cur_power] = power;
+ state->error_history[state->cur_power] = power_target - power;
+
+ /* If first loop, fill the history table */
+ if (state->first) {
+ for (i = 0; i < (state->count_power - 1); i++) {
+ state->cur_power = (state->cur_power + 1) % state->count_power;
+ state->power_history[state->cur_power] = power;
+ state->error_history[state->cur_power] = power_target - power;
+ }
+ for (i = 0; i < (CPU_TEMP_HISTORY_SIZE - 1); i++) {
+ state->cur_temp = (state->cur_temp + 1) % CPU_TEMP_HISTORY_SIZE;
+ state->temp_history[state->cur_temp] = temp;
+ }
+ state->first = 0;
+ }
+
+ /* Calculate the integral term normally based on the "power" values */
+ sum = 0;
+ integral = 0;
+ for (i = 0; i < state->count_power; i++)
+ integral += state->error_history[i];
+ integral *= CPU_PID_INTERVAL;
+ DBG(" integral: %08x\n", integral);
+
+ /* Calculate the adjusted input (sense value).
+ * G_r is 12.20
+ * integ is 16.16
+ * so the result is 28.36
+ *
+ * input target is mpu.ttarget, input max is mpu.tmax
+ */
+ integ_p = ((s64)state->mpu.pid_gr) * (s64)integral;
+ DBG(" integ_p: %d\n", (int)(deriv_p >> 36));
+ sval = (state->mpu.tmax << 16) - ((integ_p >> 20) & 0xffffffff);
+ adj_in_target = (state->mpu.ttarget << 16);
+ if (adj_in_target > sval)
+ adj_in_target = sval;
+ DBG(" adj_in_target: %d.%03d, ttarget: %d\n", FIX32TOPRINT(adj_in_target),
+ state->mpu.ttarget);
+
+ /* Calculate the derivative term */
+ derivative = state->temp_history[state->cur_temp] -
+ state->temp_history[(state->cur_temp + CPU_TEMP_HISTORY_SIZE - 1)
+ % CPU_TEMP_HISTORY_SIZE];
+ derivative /= CPU_PID_INTERVAL;
+ deriv_p = ((s64)state->mpu.pid_gd) * (s64)derivative;
+ DBG(" deriv_p: %d\n", (int)(deriv_p >> 36));
+ sum += deriv_p;
+
+ /* Calculate the proportional term */
+ proportional = temp - adj_in_target;
+ prop_p = ((s64)state->mpu.pid_gp) * (s64)proportional;
+ DBG(" prop_p: %d\n", (int)(prop_p >> 36));
+ sum += prop_p;
+
+ /* Scale sum */
+ sum >>= 36;
+
+ DBG(" sum: %d\n", (int)sum);
+ state->rpm += (s32)sum;
+
+ if (state->rpm < state->mpu.rminn_exhaust_fan)
+ state->rpm = state->mpu.rminn_exhaust_fan;
+ if (state->rpm > state->mpu.rmaxn_exhaust_fan)
+ state->rpm = state->mpu.rmaxn_exhaust_fan;
+
+ intake = (state->rpm * CPU_INTAKE_SCALE) >> 16;
+ if (intake < state->mpu.rminn_intake_fan)
+ intake = state->mpu.rminn_intake_fan;
+ if (intake > state->mpu.rmaxn_intake_fan)
+ intake = state->mpu.rmaxn_intake_fan;
+ state->intake_rpm = intake;
+
+ do_set_fans:
+ DBG("** CPU %d RPM: %d Ex, %d In, overtemp: %d\n",
+ state->index, (int)state->rpm, intake, state->overtemp);
+
+ /* We should check for errors, shouldn't we ? But then, what
+ * do we do once the error occurs ? For FCU notified fan
+ * failures (-EFAULT) we probably want to notify userland
+ * some way...
+ */
+ if (state->index == 0) {
+ set_rpm_fan(CPUA_INTAKE_FAN_RPM_ID, intake);
+ set_rpm_fan(CPUA_EXHAUST_FAN_RPM_ID, state->rpm);
+ } else {
+ set_rpm_fan(CPUB_INTAKE_FAN_RPM_ID, intake);
+ set_rpm_fan(CPUB_EXHAUST_FAN_RPM_ID, state->rpm);
+ }
+}
+
+/*
+ * Initialize the state structure for one CPU control loop
+ */
+static int init_cpu_state(struct cpu_pid_state *state, int index)
+{
+ state->index = index;
+ state->first = 1;
+ state->rpm = 1000;
+ state->overtemp = 0;
+
+ if (index == 0)
+ state->monitor = attach_i2c_chip(SUPPLY_MONITOR_ID, "CPU0_monitor");
+ else if (index == 1)
+ state->monitor = attach_i2c_chip(SUPPLY_MONITORB_ID, "CPU1_monitor");
+ if (state->monitor == NULL)
+ goto fail;
+
+ if (read_eeprom(index, &state->mpu))
+ goto fail;
+
+ state->count_power = state->mpu.tguardband;
+ if (state->count_power > CPU_POWER_HISTORY_SIZE) {
+ printk(KERN_WARNING "Warning ! too many power history slots\n");
+ state->count_power = CPU_POWER_HISTORY_SIZE;
+ }
+ 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);
+ } 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);
+ }
+
+ return 0;
+ fail:
+ if (state->monitor)
+ detach_i2c_chip(state->monitor);
+ state->monitor = NULL;
+
+ return -ENODEV;
+}
+
+/*
+ * Dispose of the state data for one CPU control loop
+ */
+static void dispose_cpu_state(struct cpu_pid_state *state)
+{
+ if (state->monitor == NULL)
+ return;
+
+ if (state->index == 0) {
+ device_remove_file(&of_dev->dev, &dev_attr_cpu0_temperature);
+ device_remove_file(&of_dev->dev, &dev_attr_cpu0_voltage);
+ device_remove_file(&of_dev->dev, &dev_attr_cpu0_current);
+ device_remove_file(&of_dev->dev, &dev_attr_cpu0_exhaust_fan_rpm);
+ device_remove_file(&of_dev->dev, &dev_attr_cpu0_intake_fan_rpm);
+ } else {
+ device_remove_file(&of_dev->dev, &dev_attr_cpu1_temperature);
+ device_remove_file(&of_dev->dev, &dev_attr_cpu1_voltage);
+ device_remove_file(&of_dev->dev, &dev_attr_cpu1_current);
+ device_remove_file(&of_dev->dev, &dev_attr_cpu1_exhaust_fan_rpm);
+ device_remove_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
+ }
+
+ detach_i2c_chip(state->monitor);
+ state->monitor = NULL;
+}
+
+/*
+ * Motherboard backside & U3 heatsink fan control loop
+ */
+static void do_monitor_backside(struct backside_pid_state *state)
+{
+ s32 temp, integral, derivative;
+ s64 integ_p, deriv_p, prop_p, sum;
+ int i, rc;
+
+ if (--state->ticks != 0)
+ return;
+ state->ticks = BACKSIDE_PID_INTERVAL;
+
+ DBG("backside:\n");
+
+ /* Check fan status */
+ rc = get_pwm_fan(BACKSIDE_FAN_PWM_ID);
+ if (rc < 0) {
+ printk(KERN_WARNING "Error %d reading backside fan !\n", rc);
+ /* XXX What do we do now ? */
+ } else
+ state->pwm = rc;
+ DBG(" current pwm: %d\n", state->pwm);
+
+ /* Get some sensor readings */
+ temp = i2c_smbus_read_byte_data(state->monitor, MAX6690_EXT_TEMP) << 16;
+ state->last_temp = temp;
+ DBG(" temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
+ FIX32TOPRINT(BACKSIDE_PID_INPUT_TARGET));
+
+ /* Store temperature and error in history array */
+ state->cur_sample = (state->cur_sample + 1) % BACKSIDE_PID_HISTORY_SIZE;
+ state->sample_history[state->cur_sample] = temp;
+ state->error_history[state->cur_sample] = temp - BACKSIDE_PID_INPUT_TARGET;
+
+ /* If first loop, fill the history table */
+ if (state->first) {
+ for (i = 0; i < (BACKSIDE_PID_HISTORY_SIZE - 1); i++) {
+ state->cur_sample = (state->cur_sample + 1) %
+ BACKSIDE_PID_HISTORY_SIZE;
+ state->sample_history[state->cur_sample] = temp;
+ state->error_history[state->cur_sample] =
+ temp - BACKSIDE_PID_INPUT_TARGET;
+ }
+ state->first = 0;
+ }
+
+ /* Calculate the integral term */
+ sum = 0;
+ integral = 0;
+ for (i = 0; i < BACKSIDE_PID_HISTORY_SIZE; i++)
+ integral += state->error_history[i];
+ integral *= BACKSIDE_PID_INTERVAL;
+ DBG(" integral: %08x\n", integral);
+ integ_p = ((s64)BACKSIDE_PID_G_r) * (s64)integral;
+ DBG(" integ_p: %d\n", (int)(integ_p >> 36));
+ sum += integ_p;
+
+ /* Calculate the derivative term */
+ derivative = state->error_history[state->cur_sample] -
+ state->error_history[(state->cur_sample + BACKSIDE_PID_HISTORY_SIZE - 1)
+ % BACKSIDE_PID_HISTORY_SIZE];
+ derivative /= BACKSIDE_PID_INTERVAL;
+ deriv_p = ((s64)BACKSIDE_PID_G_d) * (s64)derivative;
+ DBG(" deriv_p: %d\n", (int)(deriv_p >> 36));
+ sum += deriv_p;
+
+ /* Calculate the proportional term */
+ prop_p = ((s64)BACKSIDE_PID_G_p) * (s64)(state->error_history[state->cur_sample]);
+ DBG(" prop_p: %d\n", (int)(prop_p >> 36));
+ sum += prop_p;
+
+ /* Scale sum */
+ sum >>= 36;
+
+ DBG(" sum: %d\n", (int)sum);
+ state->pwm += (s32)sum;
+ if (state->pwm < BACKSIDE_PID_OUTPUT_MIN)
+ state->pwm = BACKSIDE_PID_OUTPUT_MIN;
+ if (state->pwm > BACKSIDE_PID_OUTPUT_MAX)
+ state->pwm = BACKSIDE_PID_OUTPUT_MAX;
+
+ DBG("** BACKSIDE PWM: %d\n", (int)state->pwm);
+ set_pwm_fan(BACKSIDE_FAN_PWM_ID, state->pwm);
+}
+
+/*
+ * Initialize the state structure for the backside fan control loop
+ */
+static int init_backside_state(struct backside_pid_state *state)
+{
+ state->ticks = 1;
+ state->first = 1;
+ state->pwm = 50;
+
+ state->monitor = attach_i2c_chip(BACKSIDE_MAX_ID, "backside_temp");
+ 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);
+
+ return 0;
+}
+
+/*
+ * Dispose of the state data for the backside control loop
+ */
+static void dispose_backside_state(struct backside_pid_state *state)
+{
+ if (state->monitor == NULL)
+ return;
+
+ 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;
+}
+
+/*
+ * Drives bay fan control loop
+ */
+static void do_monitor_drives(struct drives_pid_state *state)
+{
+ s32 temp, integral, derivative;
+ s64 integ_p, deriv_p, prop_p, sum;
+ int i, rc;
+
+ if (--state->ticks != 0)
+ return;
+ state->ticks = DRIVES_PID_INTERVAL;
+
+ DBG("drives:\n");
+
+ /* Check fan status */
+ rc = get_rpm_fan(DRIVES_FAN_RPM_ID, !RPM_PID_USE_ACTUAL_SPEED);
+ if (rc < 0) {
+ printk(KERN_WARNING "Error %d reading drives fan !\n", rc);
+ /* XXX What do we do now ? */
+ } else
+ state->rpm = rc;
+ DBG(" current rpm: %d\n", state->rpm);
+
+ /* Get some sensor readings */
+ temp = le16_to_cpu(i2c_smbus_read_word_data(state->monitor, DS1775_TEMP)) << 8;
+ state->last_temp = temp;
+ DBG(" temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
+ FIX32TOPRINT(DRIVES_PID_INPUT_TARGET));
+
+ /* Store temperature and error in history array */
+ state->cur_sample = (state->cur_sample + 1) % DRIVES_PID_HISTORY_SIZE;
+ state->sample_history[state->cur_sample] = temp;
+ state->error_history[state->cur_sample] = temp - DRIVES_PID_INPUT_TARGET;
+
+ /* If first loop, fill the history table */
+ if (state->first) {
+ for (i = 0; i < (DRIVES_PID_HISTORY_SIZE - 1); i++) {
+ state->cur_sample = (state->cur_sample + 1) %
+ DRIVES_PID_HISTORY_SIZE;
+ state->sample_history[state->cur_sample] = temp;
+ state->error_history[state->cur_sample] =
+ temp - DRIVES_PID_INPUT_TARGET;
+ }
+ state->first = 0;
+ }
+
+ /* Calculate the integral term */
+ sum = 0;
+ integral = 0;
+ for (i = 0; i < DRIVES_PID_HISTORY_SIZE; i++)
+ integral += state->error_history[i];
+ integral *= DRIVES_PID_INTERVAL;
+ DBG(" integral: %08x\n", integral);
+ integ_p = ((s64)DRIVES_PID_G_r) * (s64)integral;
+ DBG(" integ_p: %d\n", (int)(integ_p >> 36));
+ sum += integ_p;
+
+ /* Calculate the derivative term */
+ derivative = state->error_history[state->cur_sample] -
+ state->error_history[(state->cur_sample + DRIVES_PID_HISTORY_SIZE - 1)
+ % DRIVES_PID_HISTORY_SIZE];
+ derivative /= DRIVES_PID_INTERVAL;
+ deriv_p = ((s64)DRIVES_PID_G_d) * (s64)derivative;
+ DBG(" deriv_p: %d\n", (int)(deriv_p >> 36));
+ sum += deriv_p;
+
+ /* Calculate the proportional term */
+ prop_p = ((s64)DRIVES_PID_G_p) * (s64)(state->error_history[state->cur_sample]);
+ DBG(" prop_p: %d\n", (int)(prop_p >> 36));
+ sum += prop_p;
+
+ /* Scale sum */
+ sum >>= 36;
+
+ DBG(" sum: %d\n", (int)sum);
+ state->rpm += (s32)sum;
+ if (state->rpm < DRIVES_PID_OUTPUT_MIN)
+ state->rpm = DRIVES_PID_OUTPUT_MIN;
+ if (state->rpm > DRIVES_PID_OUTPUT_MAX)
+ state->rpm = DRIVES_PID_OUTPUT_MAX;
+
+ DBG("** DRIVES RPM: %d\n", (int)state->rpm);
+ set_rpm_fan(DRIVES_FAN_RPM_ID, state->rpm);
+}
+
+/*
+ * Initialize the state structure for the drives bay fan control loop
+ */
+static int init_drives_state(struct drives_pid_state *state)
+{
+ state->ticks = 1;
+ state->first = 1;
+ state->rpm = 1000;
+
+ state->monitor = attach_i2c_chip(DRIVES_DALLAS_ID, "drives_temp");
+ 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);
+
+ return 0;
+}
+
+/*
+ * Dispose of the state data for the drives control loop
+ */
+static void dispose_drives_state(struct drives_pid_state *state)
+{
+ if (state->monitor == NULL)
+ return;
+
+ 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;
+}
+
+static int call_critical_overtemp(void)
+{
+ char *argv[] = { critical_overtemp_path, NULL };
+ static char *envp[] = { "HOME=/",
+ "TERM=linux",
+ "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
+ NULL };
+
+ return call_usermodehelper(critical_overtemp_path, argv, envp, 0);
+}
+
+
+/*
+ * Here's the kernel thread that calls the various control loops
+ */
+static int main_control_loop(void *x)
+{
+ daemonize("kfand");
+
+ DBG("main_control_loop started\n");
+
+ /* Set the PCI fan once for now */
+ set_pwm_fan(SLOTS_FAN_PWM_ID, SLOTS_FAN_DEFAULT_PWM);
+
+ while (state == state_attached) {
+ unsigned long elapsed, start;
+
+ start = jiffies;
+
+ down(&driver_lock);
+ do_monitor_cpu(&cpu_state[0]);
+ if (cpu_state[1].monitor != NULL)
+ do_monitor_cpu(&cpu_state[1]);
+ do_monitor_backside(&backside_state);
+ do_monitor_drives(&drives_state);
+ up(&driver_lock);
+
+ if (critical_state == 1) {
+ printk(KERN_WARNING "Temperature control detected a critical condition\n");
+ printk(KERN_WARNING "Attempting to shut down...\n");
+ if (call_critical_overtemp()) {
+ printk(KERN_WARNING "Can't call %s, power off now!\n",
+ critical_overtemp_path);
+ machine_power_off();
+ }
+ }
+ if (critical_state > 0)
+ critical_state++;
+ if (critical_state > MAX_CRITICAL_STATE) {
+ printk(KERN_WARNING "Shutdown timed out, power off now !\n");
+ machine_power_off();
+ }
+
+ // FIXME: Deal with signals
+ set_current_state(TASK_INTERRUPTIBLE);
+ elapsed = jiffies - start;
+ if (elapsed < HZ)
+ schedule_timeout(HZ - elapsed);
+ }
+
+ DBG("main_control_loop ended\n");
+
+ ctrl_task = 0;
+ complete_and_exit(&ctrl_complete, 0);
+}
+
+/*
+ * Dispose the control loops when tearing down
+ */
+static void dispose_control_loops(void)
+{
+ dispose_cpu_state(&cpu_state[0]);
+ dispose_cpu_state(&cpu_state[1]);
+
+ dispose_backside_state(&backside_state);
+ dispose_drives_state(&drives_state);
+}
+
+/*
+ * Create the control loops. U3-0 i2c bus is up, so we can now
+ * get to the various sensors
+ */
+static int create_control_loops(void)
+{
+ struct device_node *np;
+
+ /* Count CPUs from the device-tree, we don't care how many are
+ * actually used by Linux
+ */
+ cpu_count = 0;
+ for (np = NULL; NULL != (np = of_find_node_by_type(np, "cpu"));)
+ cpu_count++;
+
+ DBG("counted %d CPUs in the device-tree\n", cpu_count);
+
+ /* Create control loops for everything. If any fail, everything
+ * fails
+ */
+ if (init_cpu_state(&cpu_state[0], 0))
+ goto fail;
+ if (cpu_count > 1 && init_cpu_state(&cpu_state[1], 1))
+ goto fail;
+ if (init_backside_state(&backside_state))
+ goto fail;
+ if (init_drives_state(&drives_state))
+ goto fail;
+
+ DBG("all control loops up !\n");
+
+ return 0;
+
+ fail:
+ DBG("failure creating control loops, disposing\n");
+
+ dispose_control_loops();
+
+ return -ENODEV;
+}
+
+/*
+ * Start the control loops after everything is up, that is create
+ * the thread that will make them run
+ */
+static void start_control_loops(void)
+{
+ init_completion(&ctrl_complete);
+
+ ctrl_task = kernel_thread(main_control_loop, NULL, SIGCHLD | CLONE_KERNEL);
+}
+
+/*
+ * Stop the control loops when tearing down
+ */
+static void stop_control_loops(void)
+{
+ if (ctrl_task != 0)
+ wait_for_completion(&ctrl_complete);
+}
+
+/*
+ * Attach to the i2c FCU after detecting U3-1 bus
+ */
+static int attach_fcu(void)
+{
+ fcu = attach_i2c_chip(FAN_CTRLER_ID, "fcu");
+ if (fcu == NULL)
+ return -ENODEV;
+
+ DBG("FCU attached\n");
+
+ return 0;
+}
+
+/*
+ * Detach from the i2c FCU when tearing down
+ */
+static void detach_fcu(void)
+{
+ if (fcu)
+ detach_i2c_chip(fcu);
+ fcu = NULL;
+}
+
+/*
+ * Attach to the i2c controller. We probe the various chips based
+ * on the device-tree nodes and build everything for the driver to
+ * run, we then kick the driver monitoring thread
+ */
+static int therm_pm72_attach(struct i2c_adapter *adapter)
+{
+ down(&driver_lock);
+
+ /* Check state */
+ if (state == state_detached)
+ state = state_attaching;
+ if (state != state_attaching) {
+ up(&driver_lock);
+ return 0;
+ }
+
+ /* Check if we are looking for one of these */
+ if (u3_0 == NULL && !strcmp(adapter->name, "u3 0")) {
+ u3_0 = adapter;
+ DBG("found U3-0, creating control loops\n");
+ if (create_control_loops())
+ u3_0 = NULL;
+ } else if (u3_1 == NULL && !strcmp(adapter->name, "u3 1")) {
+ u3_1 = adapter;
+ DBG("found U3-1, attaching FCU\n");
+ if (attach_fcu())
+ u3_1 = NULL;
+ }
+ /* We got all we need, start control loops */
+ if (u3_0 != NULL && u3_1 != NULL) {
+ DBG("everything up, starting control loops\n");
+ state = state_attached;
+ start_control_loops();
+ }
+ up(&driver_lock);
+
+ return 0;
+}
+
+/*
+ * Called on every adapter when the driver or the i2c controller
+ * is going away.
+ */
+static int therm_pm72_detach(struct i2c_adapter *adapter)
+{
+ down(&driver_lock);
+
+ if (state != state_detached)
+ state = state_detaching;
+
+ /* Stop control loops if any */
+ DBG("stopping control loops\n");
+ up(&driver_lock);
+ stop_control_loops();
+ down(&driver_lock);
+
+ if (u3_0 != NULL && !strcmp(adapter->name, "u3 0")) {
+ DBG("lost U3-0, disposing control loops\n");
+ dispose_control_loops();
+ u3_0 = NULL;
+ }
+
+ if (u3_1 != NULL && !strcmp(adapter->name, "u3 1")) {
+ DBG("lost U3-1, detaching FCU\n");
+ detach_fcu();
+ u3_1 = NULL;
+ }
+ if (u3_0 == NULL && u3_1 == NULL)
+ state = state_detached;
+
+ up(&driver_lock);
+
+ return 0;
+}
+
+static int fcu_of_probe(struct of_device* dev, const struct of_match *match)
+{
+ int rc;
+
+ state = state_detached;
+
+ rc = i2c_add_driver(&therm_pm72_driver);
+ if (rc < 0)
+ return rc;
+ return 0;
+}
+
+static int fcu_of_remove(struct of_device* dev)
+{
+ i2c_del_driver(&therm_pm72_driver);
+
+ return 0;
+}
+
+static struct of_match fcu_of_match[] =
+{
+ {
+ .name = OF_ANY_MATCH,
+ .type = "fcu",
+ .compatible = OF_ANY_MATCH
+ },
+ {},
+};
+
+static struct of_platform_driver fcu_of_platform_driver =
+{
+ .name = "temperature",
+ .match_table = fcu_of_match,
+ .probe = fcu_of_probe,
+ .remove = fcu_of_remove
+};
+
+/*
+ * Check machine type, attach to i2c controller
+ */
+static int __init therm_pm72_init(void)
+{
+ struct device_node *np;
+
+ if (!machine_is_compatible("PowerMac7,2"))
+ return -ENODEV;
+
+ printk(KERN_INFO "PowerMac G5 Thermal control driver %s\n", VERSION);
+
+ np = of_find_node_by_type(NULL, "fcu");
+ if (np == NULL) {
+ printk(KERN_ERR "Can't find FCU in device-tree !\n");
+ return -ENODEV;
+ }
+ of_dev = of_platform_device_create(np, "temperature");
+ 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;
+}
+
+static void __exit therm_pm72_exit(void)
+{
+ of_unregister_driver(&fcu_of_platform_driver);
+
+ if (of_dev)
+ of_device_unregister(of_dev);
+}
+
+module_init(therm_pm72_init);
+module_exit(therm_pm72_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("Driver for Apple's PowerMac7,2 G5 thermal control");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/therm_pm72.h b/drivers/macintosh/therm_pm72.h
new file mode 100644
index 000000000000..3a566fff51d7
--- /dev/null
+++ b/drivers/macintosh/therm_pm72.h
@@ -0,0 +1,236 @@
+#ifndef __THERM_PMAC_7_2_H__
+#define __THERM_PMAC_7_2_H__
+
+typedef unsigned short fu16;
+typedef int fs32;
+typedef short fs16;
+
+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 */
+ 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 */
+
+/* Display a 16.16 fixed point value */
+#define FIX32TOPRINT(f) ((f) >> 16),((((f) & 0xffff) * 1000) >> 16)
+
+/*
+ * Maximum number of seconds to be in critical state (after a
+ * normal shutdown attempt). If the machine isn't down after
+ * this counter elapses, we force an immediate machine power
+ * off.
+ */
+#define MAX_CRITICAL_STATE 30
+static char * critical_overtemp_path = "/sbin/critical_overtemp";
+
+/*
+ * 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 not sure which of these Apple's algorithm is supposed
+ * to use
+ */
+#define RPM_PID_USE_ACTUAL_SPEED 1
+
+/*
+ * i2c IDs. Currently, we hard code those and assume that
+ * the FCU is on U3 bus 1 while all sensors are on U3 bus
+ * 0. This appear to be safe enough for this first version
+ * of the driver, though I would accept any clean patch
+ * doing a better use of the device-tree without turning the
+ * while i2c registration mecanism into a racy mess
+ */
+#define FAN_CTRLER_ID 0x15e
+#define SUPPLY_MONITOR_ID 0x58
+#define SUPPLY_MONITORB_ID 0x5a
+#define DRIVES_DALLAS_ID 0x94
+#define BACKSIDE_MAX_ID 0x98
+
+/*
+ * Some MAX6690 & DS1775 register definitions
+ */
+#define MAX6690_INT_TEMP 0
+#define MAX6690_EXT_TEMP 1
+#define DS1775_TEMP 0
+
+/*
+ * 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 */
+
+/*
+ * PID factors for the U3/Backside fan control loop
+ */
+#define BACKSIDE_FAN_PWM_ID 1
+#define BACKSIDE_PID_G_d 0x02800000
+#define BACKSIDE_PID_G_p 0x00500000
+#define BACKSIDE_PID_G_r 0x00000000
+#define BACKSIDE_PID_INPUT_TARGET 0x00410000
+#define BACKSIDE_PID_INTERVAL 5
+#define BACKSIDE_PID_OUTPUT_MAX 100
+#define BACKSIDE_PID_OUTPUT_MIN 20
+#define BACKSIDE_PID_HISTORY_SIZE 2
+
+struct backside_pid_state
+{
+ int ticks;
+ struct i2c_client * monitor;
+ s32 sample_history[BACKSIDE_PID_HISTORY_SIZE];
+ s32 error_history[BACKSIDE_PID_HISTORY_SIZE];
+ int cur_sample;
+ s32 last_temp;
+ int pwm;
+ int first;
+};
+
+/*
+ * PID factors for the Drive Bay fan control loop
+ */
+#define DRIVES_FAN_RPM_ID 2
+#define DRIVES_PID_G_d 0x01e00000
+#define DRIVES_PID_G_p 0x00500000
+#define DRIVES_PID_G_r 0x00000000
+#define DRIVES_PID_INPUT_TARGET 0x00280000
+#define DRIVES_PID_INTERVAL 5
+#define DRIVES_PID_OUTPUT_MAX 4000
+#define DRIVES_PID_OUTPUT_MIN 300
+#define DRIVES_PID_HISTORY_SIZE 2
+
+struct drives_pid_state
+{
+ int ticks;
+ struct i2c_client * monitor;
+ s32 sample_history[BACKSIDE_PID_HISTORY_SIZE];
+ s32 error_history[BACKSIDE_PID_HISTORY_SIZE];
+ int cur_sample;
+ s32 last_temp;
+ int rpm;
+ int first;
+};
+
+#define SLOTS_FAN_PWM_ID 2
+#define SLOTS_FAN_DEFAULT_PWM 50 /* Do better here ! */
+
+/*
+ * IDs in Darwin for the sensors & fans
+ *
+ * CPU A AD7417_TEMP 10 (CPU A ambient temperature)
+ * CPU A AD7417_AD1 11 (CPU A diode temperature)
+ * CPU A AD7417_AD2 12 (CPU A 12V current)
+ * CPU A AD7417_AD3 13 (CPU A voltage)
+ * CPU A AD7417_AD4 14 (CPU A current)
+ *
+ * CPU A FAKE POWER 48 (I_V_inputs: 13, 14)
+ *
+ * CPU B AD7417_TEMP 15 (CPU B ambient temperature)
+ * CPU B AD7417_AD1 16 (CPU B diode temperature)
+ * CPU B AD7417_AD2 17 (CPU B 12V current)
+ * CPU B AD7417_AD3 18 (CPU B voltage)
+ * CPU B AD7417_AD4 19 (CPU B current)
+ *
+ * CPU B FAKE POWER 49 (I_V_inputs: 18, 19)
+ */
+
+#define CPUA_INTAKE_FAN_RPM_ID 3
+#define CPUA_EXHAUST_FAN_RPM_ID 4
+#define CPUB_INTAKE_FAN_RPM_ID 5
+#define CPUB_EXHAUST_FAN_RPM_ID 6
+
+#define CPU_INTAKE_SCALE 0x0000f852
+#define CPU_TEMP_HISTORY_SIZE 2
+#define CPU_POWER_HISTORY_SIZE 10
+#define CPU_PID_INTERVAL 1
+#define CPU_MAX_OVERTEMP 30
+
+struct cpu_pid_state
+{
+ int index;
+ struct i2c_client * monitor;
+ struct mpu_data mpu;
+ int overtemp;
+ s32 temp_history[CPU_TEMP_HISTORY_SIZE];
+ int cur_temp;
+ s32 power_history[CPU_POWER_HISTORY_SIZE];
+ s32 error_history[CPU_POWER_HISTORY_SIZE];
+ int cur_power;
+ int count_power;
+ int rpm;
+ int intake_rpm;
+ s32 voltage;
+ s32 current_a;
+ s32 last_temp;
+ int first;
+};
+
+/*
+ * Driver state
+ */
+enum {
+ state_detached,
+ state_attaching,
+ state_attached,
+ state_detaching,
+};
+
+
+#endif /* __THERM_PMAC_7_2_H__ */
diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c
new file mode 100644
index 000000000000..1836aff984d8
--- /dev/null
+++ b/drivers/macintosh/therm_windtunnel.c
@@ -0,0 +1,456 @@
+/*
+ * Creation Date: <2003/03/14 20:54:13 samuel>
+ * Time-stamp: <2003/03/15 18:55:53 samuel>
+ *
+ * <therm_windtunnel.c>
+ *
+ * The G4 "windtunnel" has a single fan controlled by a
+ * DS1775 fan controller and an ADM1030 thermostat.
+ *
+ * The fan controller is equipped with a temperature sensor
+ * which measures the case temperature. The ADM censor
+ * measures the CPU temperature. This driver tunes the
+ * behavior of the fan. It is based upon empirical observations
+ * of the 'AppleFan' driver under OSX.
+ *
+ * WARNING: This driver has only been testen on Apple's
+ * 1.25 MHz Dual G4 (March 03). Other machines might have
+ * a different thermal design. It is tuned for a CPU
+ * temperatur around 57 C.
+ *
+ * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se)
+ *
+ * Loosely based upon 'thermostat.c' written by Benjamin Herrenschmidt
+ *
+ * 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
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+
+MODULE_AUTHOR("Samuel Rydh <samuel@ibrium.se>");
+MODULE_DESCRIPTION("Apple G4 (windtunnel) fan driver");
+MODULE_LICENSE("GPL");
+
+#define LOG_TEMP 0 /* continously log temperature */
+
+/* scan 0x48-0x4f (DS1775) and 0x2c-2x2f (ADM1030) */
+static unsigned short normal_i2c[] = { 0x49, 0x2c, I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { 0x48, 0x4f, 0x2c, 0x2f, I2C_CLIENT_END };
+static struct work_struct poll_work;
+
+I2C_CLIENT_INSMOD;
+
+#define I2C_DRIVERID_G4FAN 0x9001 /* fixme */
+
+#define THERMOSTAT_CLIENT_ID 1
+#define FAN_CLIENT_ID 2
+
+struct temp_range {
+ u8 high; /* start the fan */
+ u8 low; /* stop the fan */
+};
+struct apple_thermal_info {
+ u8 id; /* implementation ID */
+ u8 fan_count; /* number of fans */
+ u8 thermostat_count; /* number of thermostats */
+ u8 unused[5];
+ struct temp_range ranges[4]; /* temperature ranges (may be [])*/
+};
+
+static int do_detect( struct i2c_adapter *adapter, int addr, int kind);
+
+static struct {
+ struct i2c_client *thermostat;
+ struct i2c_client *fan;
+ int error;
+ struct timer_list timer;
+
+ int overheat_temp; /* 100% fan at this temp */
+ int overheat_hyst;
+ int temp;
+ int casetemp;
+ int fan_level; /* active fan_table setting */
+
+ int downind;
+ int upind;
+
+ int r0, r1, r20, r23, r25; /* saved register */
+} x;
+
+static struct {
+ int temp;
+ int fan_setting;
+} fan_up_table[] = {
+ { 0x0000, 11 }, /* min fan */
+ { 0x3900, 8 }, /* 57.0 C */
+ { 0x3a4a, 7 }, /* 58.3 C */
+ { 0x3ad3, 6 }, /* 58.8 C */
+ { 0x3b3c, 5 }, /* 59.2 C */
+ { 0x3b94, 4 }, /* 59.6 C */
+ { 0x3be3, 3 }, /* 58.9 C */
+ { 0x3c29, 2 }, /* 59.2 C */
+ { 0xffff, 1 } /* on fire */
+};
+static struct {
+ int temp;
+ int fan_setting;
+} fan_down_table[] = {
+ { 0x3700, 11 }, /* 55.0 C */
+ { 0x374a, 6 },
+ { 0x3800, 7 }, /* 56.0 C */
+ { 0x3900, 8 }, /* 57.0 C */
+ { 0x3a4a, 7 }, /* 58.3 C */
+ { 0x3ad3, 6 }, /* 58.8 C */
+ { 0x3b3c, 5 }, /* 59.2 C */
+ { 0x3b94, 4 }, /* 58.9 C */
+ { 0x3be3, 3 }, /* 58.9 C */
+ { 0x3c29, 2 }, /* 59.2 C */
+ { 0xffff, 1 }
+};
+
+static int
+write_reg( struct i2c_client *cl, int reg, int data, int len )
+{
+ u8 tmp[3];
+
+ if( len < 1 || len > 2 || data < 0 )
+ return -EINVAL;
+
+ tmp[0] = reg;
+ tmp[1] = (len == 1) ? data : (data >> 8);
+ tmp[2] = data;
+ len++;
+
+ if( i2c_master_send(cl, tmp, len) != len )
+ return -ENODEV;
+ return 0;
+}
+
+static int
+read_reg( struct i2c_client *cl, int reg, int len )
+{
+ u8 buf[2];
+
+ if( len != 1 && len != 2 )
+ return -EINVAL;
+ buf[0] = reg;
+ if( i2c_master_send(cl, buf, 1) != 1 )
+ return -ENODEV;
+ if( i2c_master_recv(cl, buf, len) != len )
+ return -ENODEV;
+ return (len == 2)? ((unsigned int)buf[0] << 8) | buf[1] : buf[0];
+}
+
+
+static void
+print_temp( const char *s, int temp )
+{
+ printk("%s%d.%d C", s ? s : "", temp>>8, (temp & 255)*10/256 );
+}
+
+static void
+tune_fan( int fan_setting )
+{
+ int val = (fan_setting << 3) | 7;
+ x.fan_level = fan_setting;
+
+ //write_reg( x.fan, 0x24, val, 1 );
+ write_reg( x.fan, 0x25, val, 1 );
+ write_reg( x.fan, 0x20, 0, 1 );
+ print_temp("CPU-temp: ", x.temp );
+ if( x.casetemp )
+ print_temp(", Case: ", x.casetemp );
+ printk(" Tuning fan: %d (%02x)\n", fan_setting, val );
+}
+
+static void
+poll_temp( void *param )
+{
+ int temp = read_reg( x.thermostat, 0, 2 );
+ int i, level, casetemp;
+
+ /* this actually occurs when the computer is loaded */
+ if( temp < 0 )
+ goto out;
+
+ casetemp = read_reg(x.fan, 0x0b, 1) << 8;
+ casetemp |= (read_reg(x.fan, 0x06, 1) & 0x7) << 5;
+
+ if( LOG_TEMP && x.temp != temp ) {
+ print_temp("CPU-temp: ", temp );
+ print_temp(", Case: ", casetemp );
+ printk(", Fan: %d\n", x.fan_level );
+ }
+ x.temp = temp;
+ x.casetemp = casetemp;
+
+ level = -1;
+ for( i=0; (temp & 0xffff) > fan_down_table[i].temp ; i++ )
+ ;
+ if( i < x.downind )
+ level = fan_down_table[i].fan_setting;
+ x.downind = i;
+
+ for( i=0; (temp & 0xfffe) >= fan_up_table[i+1].temp ; i++ )
+ ;
+ if( x.upind < i )
+ level = fan_up_table[i].fan_setting;
+ x.upind = i;
+
+ if( level >= 0 )
+ tune_fan( level );
+ out:
+ x.timer.expires = jiffies + 8*HZ;
+ add_timer( &x.timer );
+}
+
+static void
+schedule_poll( unsigned long t )
+{
+ schedule_work(&poll_work);
+}
+
+/************************************************************************/
+/* i2c probing and setup */
+/************************************************************************/
+
+static int
+do_attach( struct i2c_adapter *adapter )
+{
+ return i2c_probe( adapter, &addr_data, &do_detect );
+}
+
+static int
+do_detach( struct i2c_client *client )
+{
+ int err;
+
+ printk("do_detach: id %d\n", client->id );
+ if( (err=i2c_detach_client(client)) ) {
+ printk("failed to detach thermostat client\n");
+ return err;
+ }
+ kfree( client );
+ return 0;
+}
+
+static struct i2c_driver g4fan_driver = {
+ .name = "Apple G4 Thermostat/Fan",
+ .id = I2C_DRIVERID_G4FAN,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = &do_attach,
+ .detach_client = &do_detach,
+ .command = NULL,
+};
+
+static int
+detect_fan( struct i2c_client *cl )
+{
+ /* check that this is an ADM1030 */
+ if( read_reg(cl, 0x3d, 1) != 0x30 || read_reg(cl, 0x3e, 1) != 0x41 )
+ goto out;
+ printk("ADM1030 fan controller detected at %02x\n", cl->addr );
+
+ if( x.fan ) {
+ x.error |= 2;
+ goto out;
+ }
+ x.fan = cl;
+ cl->id = FAN_CLIENT_ID;
+ strncpy( cl->name, "ADM1030 fan controller", sizeof(cl->name) );
+
+ if( i2c_attach_client( cl ) )
+ goto out;
+ return 0;
+ out:
+ if( cl != x.fan )
+ kfree( cl );
+ return 0;
+}
+
+static int
+detect_thermostat( struct i2c_client *cl )
+{
+ int hyst_temp, os_temp, temp;
+
+ if( (temp=read_reg(cl, 0, 2)) < 0 )
+ goto out;
+
+ /* temperature sanity check */
+ if( temp < 0x1600 || temp > 0x3c00 )
+ goto out;
+ hyst_temp = read_reg(cl, 2, 2);
+ os_temp = read_reg(cl, 3, 2);
+ if( hyst_temp < 0 || os_temp < 0 )
+ goto out;
+
+ printk("DS1775 digital thermometer detected at %02x\n", cl->addr );
+ print_temp("Temp: ", temp );
+ print_temp(" Hyst: ", hyst_temp );
+ print_temp(" OS: ", os_temp );
+ printk("\n");
+
+ if( x.thermostat ) {
+ x.error |= 1;
+ goto out;
+ }
+ x.temp = temp;
+ x.thermostat = cl;
+ x.overheat_temp = os_temp;
+ x.overheat_hyst = hyst_temp;
+
+ cl->id = THERMOSTAT_CLIENT_ID;
+ strncpy( cl->name, "DS1775 thermostat", sizeof(cl->name) );
+
+ if( i2c_attach_client( cl ) )
+ goto out;
+ return 0;
+out:
+ kfree( cl );
+ return 0;
+}
+
+static int
+do_detect( struct i2c_adapter *adapter, int addr, int kind )
+{
+ struct i2c_client *cl;
+
+ if( strncmp(adapter->name, "uni-n", 5) )
+ return 0;
+ if( !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA
+ | I2C_FUNC_SMBUS_WRITE_BYTE) )
+ return 0;
+
+ if( !(cl=kmalloc( sizeof(struct i2c_client), 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 )
+ return detect_fan( cl );
+ return detect_thermostat( cl );
+}
+
+#define PRINT_REG( r ) printk("reg %02x = %02x\n", r, read_reg(x.fan, r, 1) )
+
+static int __init
+g4fan_init( void )
+{
+ struct apple_thermal_info *info;
+ struct device_node *np;
+ int ret, val;
+
+ np = of_find_node_by_name(NULL, "power-mgt");
+ if (np == NULL)
+ return -ENODEV;
+ info = (struct apple_thermal_info*)get_property(np, "thermal-info", NULL);
+ of_node_put(np);
+ if (info == NULL)
+ return -ENODEV;
+
+ /* check for G4 "Windtunnel" SMP */
+ if( machine_is_compatible("PowerMac3,6") ) {
+ if( info->id != 3 ) {
+ printk(KERN_ERR "g4fan: design id %d unknown\n", info->id);
+ return -ENODEV;
+ }
+ } else {
+ printk(KERN_ERR "g4fan: unsupported machine type\n");
+ return -ENODEV;
+ }
+ if( (ret=i2c_add_driver(&g4fan_driver)) )
+ return ret;
+
+ if( !x.thermostat || !x.fan ) {
+ i2c_del_driver(&g4fan_driver );
+ return -ENODEV;
+ }
+
+ /* save registers (if we unload the module) */
+ x.r0 = read_reg( x.fan, 0x00, 1 );
+ x.r1 = read_reg( x.fan, 0x01, 1 );
+ x.r20 = read_reg( x.fan, 0x20, 1 );
+ x.r23 = read_reg( x.fan, 0x23, 1 );
+ x.r25 = read_reg( x.fan, 0x25, 1 );
+
+ /* improve measurement resolution (convergence time 1.5s) */
+ if( (val=read_reg( x.thermostat, 1, 1 )) >= 0 ) {
+ val |= 0x60;
+ if( write_reg( x.thermostat, 1, val, 1 ) )
+ printk("Failed writing config register\n");
+ }
+ /* disable interrupts and TAC input */
+ write_reg( x.fan, 0x01, 0x01, 1 );
+ /* enable filter */
+ write_reg( x.fan, 0x23, 0x91, 1 );
+ /* remote temp. controls fan */
+ write_reg( x.fan, 0x00, 0x95, 1 );
+
+ /* The thermostat (which besides measureing temperature controls
+ * has a THERM output which puts the fan on 100%) is usually
+ * set to kick in at 80 C (chip default). We reduce this a bit
+ * 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;
+ write_reg( x.thermostat, 2, x.overheat_hyst, 2 );
+ write_reg( x.thermostat, 3, x.overheat_temp, 2 );
+
+ print_temp("Reducing overheating limit to ", x.overheat_temp );
+ print_temp(" (Hyst: ", x.overheat_hyst );
+ printk(")\n");
+ }
+
+ /* set an initial fan setting */
+ x.upind = x.downind = 1;
+ tune_fan( fan_up_table[x.upind].fan_setting );
+
+ INIT_WORK(&poll_work, poll_temp, NULL);
+
+ init_timer( &x.timer );
+ x.timer.expires = jiffies + 8*HZ;
+ x.timer.function = schedule_poll;
+ add_timer( &x.timer );
+ return 0;
+}
+
+static void __exit
+g4fan_exit( void )
+{
+ del_timer( &x.timer );
+
+ write_reg( x.fan, 0x01, x.r1, 1 );
+ write_reg( x.fan, 0x20, x.r20, 1 );
+ write_reg( x.fan, 0x23, x.r23, 1 );
+ write_reg( x.fan, 0x25, x.r25, 1 );
+ write_reg( x.fan, 0x00, x.r0, 1 );
+
+ i2c_del_driver( &g4fan_driver );
+}
+
+module_init(g4fan_init);
+module_exit(g4fan_exit);
+
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index 99007e8a3981..46dd331673c2 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -168,6 +168,7 @@ static struct proc_dir_entry *proc_pmu_root;
static struct proc_dir_entry *proc_pmu_info;
static struct proc_dir_entry *proc_pmu_irqstats;
static struct proc_dir_entry *proc_pmu_options;
+static int option_server_mode;
#ifdef CONFIG_PMAC_PBOOK
int pmu_battery_count;
@@ -334,7 +335,8 @@ find_via_pmu(void)
pmu_kind = PMU_PADDINGTON_BASED;
else if (device_is_compatible(vias->parent, "heathrow"))
pmu_kind = PMU_HEATHROW_BASED;
- else if (device_is_compatible(vias->parent, "Keylargo")) {
+ else if (device_is_compatible(vias->parent, "Keylargo")
+ || device_is_compatible(vias->parent, "K2-Keylargo")) {
struct device_node *gpio, *gpiop;
pmu_kind = PMU_KEYLARGO_BASED;
@@ -349,6 +351,8 @@ find_via_pmu(void)
if (gpiop && gpiop->n_addrs) {
gpio_reg = ioremap(gpiop->addrs->address, 0x10);
gpio = find_devices("extint-gpio1");
+ if (gpio == NULL)
+ gpio = find_devices("pmu-interrupt");
if (gpio && gpio->parent == gpiop && gpio->n_intrs)
gpio_irq = gpio->intrs[0].line;
}
@@ -564,7 +568,19 @@ init_pmu(void)
pmu_wait_complete(&req);
if (req.reply_len > 0)
pmu_version = req.reply[0];
-
+
+ /* Read server mode setting */
+ if (pmu_kind == PMU_KEYLARGO_BASED) {
+ pmu_request(&req, NULL, 2, PMU_POWER_EVENTS,
+ PMU_PWR_GET_POWERUP_EVENTS);
+ pmu_wait_complete(&req);
+ if (req.reply_len == 2) {
+ if (req.reply[1] & PMU_PWR_WAKEUP_AC_INSERT)
+ option_server_mode = 1;
+ printk(KERN_INFO "via-pmu: Server Mode is %s\n",
+ option_server_mode ? "enabled" : "disabled");
+ }
+ }
return 1;
}
@@ -583,6 +599,28 @@ static inline void wakeup_decrementer(void)
last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
}
+static void pmu_set_server_mode(int server_mode)
+{
+ struct adb_request req;
+
+ if (pmu_kind != PMU_KEYLARGO_BASED)
+ return;
+
+ option_server_mode = server_mode;
+ pmu_request(&req, NULL, 2, PMU_POWER_EVENTS, PMU_PWR_GET_POWERUP_EVENTS);
+ pmu_wait_complete(&req);
+ if (req.reply_len < 2)
+ return;
+ if (server_mode)
+ pmu_request(&req, NULL, 4, PMU_POWER_EVENTS,
+ PMU_PWR_SET_POWERUP_EVENTS,
+ req.reply[0], PMU_PWR_WAKEUP_AC_INSERT);
+ else
+ pmu_request(&req, NULL, 4, PMU_POWER_EVENTS,
+ PMU_PWR_CLR_POWERUP_EVENTS,
+ req.reply[0], PMU_PWR_WAKEUP_AC_INSERT);
+ pmu_wait_complete(&req);
+}
#ifdef CONFIG_PMAC_PBOOK
@@ -845,6 +883,8 @@ proc_read_options(char *page, char **start, off_t off,
if (pmu_kind == PMU_KEYLARGO_BASED && can_sleep)
p += sprintf(p, "lid_wakeup=%d\n", option_lid_wakeup);
#endif /* CONFIG_PMAC_PBOOK */
+ if (pmu_kind == PMU_KEYLARGO_BASED)
+ p += sprintf(p, "server_mode=%d\n", option_server_mode);
return p - page;
}
@@ -884,6 +924,12 @@ proc_write_options(struct file *file, const char __user *buffer,
if (!strcmp(label, "lid_wakeup"))
option_lid_wakeup = ((*val) == '1');
#endif /* CONFIG_PMAC_PBOOK */
+ if (pmu_kind == PMU_KEYLARGO_BASED && !strcmp(label, "server_mode")) {
+ int new_value;
+ new_value = ((*val) == '1');
+ if (new_value != option_server_mode)
+ pmu_set_server_mode(new_value);
+ }
return fcount;
}
@@ -1758,6 +1804,11 @@ pmu_shutdown(void)
pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB |
PMU_INT_TICK );
pmu_wait_complete(&req);
+ } else {
+ /* Disable server mode on shutdown or we'll just
+ * wake up again
+ */
+ pmu_set_server_mode(0);
}
pmu_request(&req, NULL, 5, PMU_SHUTDOWN,
diff --git a/drivers/scsi/mac53c94.c b/drivers/scsi/mac53c94.c
index 62a146b53b2d..9415c2bbb2e0 100644
--- a/drivers/scsi/mac53c94.c
+++ b/drivers/scsi/mac53c94.c
@@ -23,6 +23,7 @@
#include <asm/prom.h>
#include <asm/system.h>
#include <asm/pci-bridge.h>
+#include <asm/macio.h>
#include "scsi.h"
#include "hosts.h"
@@ -37,13 +38,12 @@ enum fsc_phase {
};
struct fsc_state {
- volatile struct mac53c94_regs *regs;
+ struct mac53c94_regs *regs;
int intr;
- volatile struct dbdma_regs *dma;
+ struct dbdma_regs *dma;
int dmaintr;
int clk_freq;
struct Scsi_Host *host;
- struct fsc_state *next;
Scsi_Cmnd *request_q;
Scsi_Cmnd *request_qtail;
Scsi_Cmnd *current_req; /* req we're currently working on */
@@ -52,151 +52,23 @@ struct fsc_state {
void *dma_cmd_space;
struct pci_dev *pdev;
dma_addr_t dma_addr;
+ struct macio_dev *mdev;
};
-static struct fsc_state *all_53c94s;
-
static void mac53c94_init(struct fsc_state *);
static void mac53c94_start(struct fsc_state *);
static void mac53c94_interrupt(int, void *, struct pt_regs *);
static irqreturn_t do_mac53c94_interrupt(int, void *, struct pt_regs *);
static void cmd_done(struct fsc_state *, int result);
static void set_dma_cmds(struct fsc_state *, Scsi_Cmnd *);
-static int data_goes_out(Scsi_Cmnd *);
-
-int
-mac53c94_detect(Scsi_Host_Template *tp)
-{
- struct device_node *node;
- int nfscs;
- struct fsc_state *state, **prev_statep;
- struct Scsi_Host *host;
- void *dma_cmd_space;
- unsigned char *clkprop;
- int proplen;
- struct pci_dev *pdev;
- u8 pbus, devfn;
-
- nfscs = 0;
- prev_statep = &all_53c94s;
- for (node = find_devices("53c94"); node != 0; node = node->next) {
- if (node->n_addrs != 2 || node->n_intrs != 2) {
- printk(KERN_ERR "mac53c94: expected 2 addrs and intrs"
- " (got %d/%d) for node %s\n",
- node->n_addrs, node->n_intrs, node->full_name);
- continue;
- }
-
- pdev = NULL;
- if (node->parent != NULL
- && !pci_device_from_OF_node(node->parent, &pbus, &devfn))
- pdev = pci_find_slot(pbus, devfn);
- if (pdev == NULL) {
- printk(KERN_ERR "mac53c94: can't find PCI device "
- "for %s\n", node->full_name);
- continue;
- }
-
- host = scsi_register(tp, sizeof(struct fsc_state));
- if (host == NULL)
- break;
- host->unique_id = nfscs;
-
- state = (struct fsc_state *) host->hostdata;
- if (state == 0) {
- /* "can't happen" */
- printk(KERN_ERR "mac53c94: no state for %s?!\n",
- node->full_name);
- scsi_unregister(host);
- break;
- }
- state->host = host;
- state->pdev = pdev;
-
- state->regs = (volatile struct mac53c94_regs *)
- ioremap(node->addrs[0].address, 0x1000);
- state->intr = node->intrs[0].line;
- state->dma = (volatile struct dbdma_regs *)
- ioremap(node->addrs[1].address, 0x1000);
- state->dmaintr = node->intrs[1].line;
- if (state->regs == NULL || state->dma == NULL) {
- printk(KERN_ERR "mac53c94: ioremap failed for %s\n",
- node->full_name);
- if (state->dma != NULL)
- iounmap(state->dma);
- if (state->regs != NULL)
- iounmap(state->regs);
- scsi_unregister(host);
- break;
- }
-
- clkprop = get_property(node, "clock-frequency", &proplen);
- if (clkprop == NULL || proplen != sizeof(int)) {
- printk(KERN_ERR "%s: can't get clock frequency, "
- "assuming 25MHz\n", node->full_name);
- state->clk_freq = 25000000;
- } else
- state->clk_freq = *(int *)clkprop;
-
- /* Space for dma command list: +1 for stop command,
- +1 to allow for aligning. */
- dma_cmd_space = kmalloc((host->sg_tablesize + 2) *
- sizeof(struct dbdma_cmd), GFP_KERNEL);
- if (dma_cmd_space == 0) {
- printk(KERN_ERR "mac53c94: couldn't allocate dma "
- "command space for %s\n", node->full_name);
- goto err_cleanup;
- }
- state->dma_cmds = (struct dbdma_cmd *)
- DBDMA_ALIGN(dma_cmd_space);
- memset(state->dma_cmds, 0, (host->sg_tablesize + 1)
- * sizeof(struct dbdma_cmd));
- state->dma_cmd_space = dma_cmd_space;
-
- *prev_statep = state;
- prev_statep = &state->next;
-
- if (request_irq(state->intr, do_mac53c94_interrupt, 0,
- "53C94", state)) {
- printk(KERN_ERR "mac53C94: can't get irq %d for %s\n",
- state->intr, node->full_name);
- err_cleanup:
- iounmap(state->dma);
- iounmap(state->regs);
- scsi_unregister(host);
- break;
- }
- mac53c94_init(state);
- ++nfscs;
- }
- return nfscs;
-}
-
-int
-mac53c94_release(struct Scsi_Host *host)
-{
- struct fsc_state *fp = (struct fsc_state *) host->hostdata;
-
- if (fp == 0)
- return 0;
- if (fp->regs)
- iounmap((void *) fp->regs);
- if (fp->dma)
- iounmap((void *) fp->dma);
- kfree(fp->dma_cmd_space);
- free_irq(fp->intr, fp);
- return 0;
-}
-
-int
-mac53c94_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+static int mac53c94_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
{
struct fsc_state *state;
#if 0
- if (data_goes_out(cmd)) {
+ if (cmd->sc_data_direction == SCSI_DATA_WRITE) {
int i;
printk(KERN_DEBUG "mac53c94_queue %p: command is", cmd);
for (i = 0; i < cmd->cmd_len; ++i)
@@ -223,60 +95,52 @@ mac53c94_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
return 0;
}
-int
-mac53c94_abort(Scsi_Cmnd *cmd)
+static int mac53c94_abort(Scsi_Cmnd *cmd)
{
return SCSI_ABORT_SNOOZE;
}
-int
-mac53c94_host_reset(Scsi_Cmnd *cmd)
+static int mac53c94_host_reset(Scsi_Cmnd *cmd)
{
struct fsc_state *state = (struct fsc_state *) cmd->device->host->hostdata;
- volatile struct mac53c94_regs *regs = state->regs;
- volatile struct dbdma_regs *dma = state->dma;
+ struct mac53c94_regs *regs = state->regs;
+ struct dbdma_regs *dma = state->dma;
st_le32(&dma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
- regs->command = CMD_SCSI_RESET; /* assert RST */
- eieio();
+ writeb(CMD_SCSI_RESET, &regs->command); /* assert RST */
udelay(100); /* leave it on for a while (>= 25us) */
- regs->command = CMD_RESET;
- eieio();
+ writeb(CMD_RESET, &regs->command);
udelay(20);
mac53c94_init(state);
- regs->command = CMD_NOP;
- eieio();
+ writeb(CMD_NOP, &regs->command);
return SUCCESS;
}
-static void
-mac53c94_init(struct fsc_state *state)
+static void mac53c94_init(struct fsc_state *state)
{
- volatile struct mac53c94_regs *regs = state->regs;
- volatile struct dbdma_regs *dma = state->dma;
+ struct mac53c94_regs *regs = state->regs;
+ struct dbdma_regs *dma = state->dma;
int x;
- regs->config1 = state->host->this_id | CF1_PAR_ENABLE;
- regs->sel_timeout = TIMO_VAL(250); /* 250ms */
- regs->clk_factor = CLKF_VAL(state->clk_freq);
- regs->config2 = CF2_FEATURE_EN;
- regs->config3 = 0;
- regs->sync_period = 0;
- regs->sync_offset = 0;
- eieio();
- x = regs->interrupt;
- st_le32(&dma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+ writeb(state->host->this_id | CF1_PAR_ENABLE, &regs->config1);
+ writeb(TIMO_VAL(250), &regs->sel_timeout); /* 250ms */
+ writeb(CLKF_VAL(state->clk_freq), &regs->clk_factor);
+ writeb(CF2_FEATURE_EN, &regs->config2);
+ writeb(0, &regs->config3);
+ writeb(0, &regs->sync_period);
+ writeb(0, &regs->sync_offset);
+ x = readb(&regs->interrupt);
+ writel((RUN|PAUSE|FLUSH|WAKE) << 16, &dma->control);
}
/*
* Start the next command for a 53C94.
* Should be called with interrupts disabled.
*/
-static void
-mac53c94_start(struct fsc_state *state)
+static void mac53c94_start(struct fsc_state *state)
{
Scsi_Cmnd *cmd;
- volatile struct mac53c94_regs *regs = state->regs;
+ struct mac53c94_regs *regs = state->regs;
int i;
if (state->phase != idle || state->current_req != NULL)
@@ -287,37 +151,30 @@ mac53c94_start(struct fsc_state *state)
state->request_q = (Scsi_Cmnd *) cmd->host_scribble;
/* Off we go */
- regs->count_lo = 0;
- regs->count_mid = 0;
- regs->count_hi = 0;
- eieio();
- regs->command = CMD_NOP + CMD_DMA_MODE;
+ writeb(0, &regs->count_lo);
+ writeb(0, &regs->count_mid);
+ writeb(0, &regs->count_hi);
+ writeb(CMD_NOP + CMD_DMA_MODE, &regs->command);
udelay(1);
- eieio();
- regs->command = CMD_FLUSH;
+ writeb(CMD_FLUSH, &regs->command);
udelay(1);
- eieio();
- regs->dest_id = cmd->device->id;
- regs->sync_period = 0;
- regs->sync_offset = 0;
- eieio();
+ writeb(cmd->device->id, &regs->dest_id);
+ writeb(0, &regs->sync_period);
+ writeb(0, &regs->sync_offset);
/* load the command into the FIFO */
- for (i = 0; i < cmd->cmd_len; ++i) {
- regs->fifo = cmd->cmnd[i];
- eieio();
- }
+ for (i = 0; i < cmd->cmd_len; ++i)
+ writeb(cmd->cmnd[i], &regs->fifo);
/* do select without ATN XXX */
- regs->command = CMD_SELECT;
+ writeb(CMD_SELECT, &regs->command);
state->phase = selecting;
if (cmd->use_sg > 0 || cmd->request_bufflen != 0)
set_dma_cmds(state, cmd);
}
-static irqreturn_t
-do_mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
+static irqreturn_t do_mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
{
unsigned long flags;
struct Scsi_Host *dev = ((struct fsc_state *) dev_id)->current_req->device->host;
@@ -328,12 +185,11 @@ do_mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
return IRQ_HANDLED;
}
-static void
-mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
+static void mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
{
struct fsc_state *state = (struct fsc_state *) dev_id;
- volatile struct mac53c94_regs *regs = state->regs;
- volatile struct dbdma_regs *dma = state->dma;
+ struct mac53c94_regs *regs = state->regs;
+ struct dbdma_regs *dma = state->dma;
Scsi_Cmnd *cmd = state->current_req;
int nb, stat, seq, intr;
static int mac53c94_errors;
@@ -343,9 +199,9 @@ mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
* Apparently, reading the interrupt register unlatches
* the status and sequence step registers.
*/
- seq = regs->seqstep;
- stat = regs->status;
- intr = regs->interrupt;
+ seq = readb(&regs->seqstep);
+ stat = readb(&regs->status);
+ intr = readb(&regs->interrupt);
#if 0
printk(KERN_DEBUG "mac53c94_intr, intr=%x stat=%x seq=%x phase=%d\n",
@@ -355,8 +211,8 @@ mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
if (intr & INTR_RESET) {
/* SCSI bus was reset */
printk(KERN_INFO "external SCSI bus reset detected\n");
- regs->command = CMD_NOP;
- st_le32(&dma->control, RUN << 16); /* stop dma */
+ writeb(CMD_NOP, &regs->command);
+ writel(RUN << 16, &dma->control); /* stop dma */
cmd_done(state, DID_RESET << 16);
return;
}
@@ -373,8 +229,7 @@ mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
intr, stat, seq, state->phase);
#endif
++mac53c94_errors;
- regs->command = CMD_NOP + CMD_DMA_MODE;
- eieio();
+ writeb(CMD_NOP + CMD_DMA_MODE, &regs->command);
}
if (cmd == 0) {
printk(KERN_DEBUG "53c94: interrupt with no command active?\n");
@@ -402,7 +257,7 @@ mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
cmd_done(state, DID_ERROR << 16);
return;
}
- regs->command = CMD_NOP;
+ writeb(CMD_NOP, &regs->command);
/* set DMA controller going if any data to transfer */
if ((stat & (STAT_MSG|STAT_CD)) == 0
&& (cmd->use_sg > 0 || cmd->request_bufflen != 0)) {
@@ -410,20 +265,17 @@ mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
if (nb > 0xfff0)
nb = 0xfff0;
cmd->SCp.this_residual -= nb;
- regs->count_lo = nb;
- regs->count_mid = nb >> 8;
- eieio();
- regs->command = CMD_DMA_MODE + CMD_NOP;
- eieio();
- st_le32(&dma->cmdptr, virt_to_phys(state->dma_cmds));
- st_le32(&dma->control, (RUN << 16) | RUN);
- eieio();
- regs->command = CMD_DMA_MODE + CMD_XFER_DATA;
+ writeb(nb, &regs->count_lo);
+ writeb(nb >> 8, &regs->count_mid);
+ writeb(CMD_DMA_MODE + CMD_NOP, &regs->command);
+ writel(virt_to_phys(state->dma_cmds), &dma->cmdptr);
+ writel((RUN << 16) | RUN, &dma->control);
+ writeb(CMD_DMA_MODE + CMD_XFER_DATA, &regs->command);
state->phase = dataing;
break;
} else if ((stat & STAT_PHASE) == STAT_CD + STAT_IO) {
/* up to status phase already */
- regs->command = CMD_I_COMPLETE;
+ writeb(CMD_I_COMPLETE, &regs->command);
state->phase = completing;
} else {
printk(KERN_DEBUG "in unexpected phase %x after cmd\n",
@@ -446,18 +298,16 @@ mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
if (nb > 0xfff0)
nb = 0xfff0;
cmd->SCp.this_residual -= nb;
- regs->count_lo = nb;
- regs->count_mid = nb >> 8;
- eieio();
- regs->command = CMD_DMA_MODE + CMD_NOP;
- eieio();
- regs->command = CMD_DMA_MODE + CMD_XFER_DATA;
+ writeb(nb, &regs->count_lo);
+ writeb(nb >> 8, &regs->count_mid);
+ writeb(CMD_DMA_MODE + CMD_NOP, &regs->command);
+ writeb(CMD_DMA_MODE + CMD_XFER_DATA, &regs->command);
break;
}
if ((stat & STAT_PHASE) != STAT_CD + STAT_IO) {
printk(KERN_DEBUG "intr %x before data xfer complete\n", intr);
}
- out_le32(&dma->control, RUN << 16); /* stop dma */
+ writel(RUN << 16, &dma->control); /* stop dma */
dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
if (cmd->use_sg != 0) {
pci_unmap_sg(state->pdev,
@@ -468,7 +318,7 @@ mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
cmd->request_bufflen, dma_dir);
}
/* should check dma status */
- regs->command = CMD_I_COMPLETE;
+ writeb(CMD_I_COMPLETE, &regs->command);
state->phase = completing;
break;
case completing:
@@ -477,10 +327,10 @@ mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
cmd_done(state, DID_ERROR << 16);
return;
}
- cmd->SCp.Status = regs->fifo; eieio();
- cmd->SCp.Message = regs->fifo; eieio();
- cmd->result =
- regs->command = CMD_ACCEPT_MSG;
+ cmd->SCp.Status = readb(&regs->fifo);
+ cmd->SCp.Message = readb(&regs->fifo);
+ cmd->result = CMD_ACCEPT_MSG;
+ writeb(CMD_ACCEPT_MSG, &regs->command);
state->phase = busfreeing;
break;
case busfreeing:
@@ -495,8 +345,7 @@ mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
}
}
-static void
-cmd_done(struct fsc_state *state, int result)
+static void cmd_done(struct fsc_state *state, int result)
{
Scsi_Cmnd *cmd;
@@ -513,8 +362,7 @@ cmd_done(struct fsc_state *state, int result)
/*
* Set up DMA commands for transferring data.
*/
-static void
-set_dma_cmds(struct fsc_state *state, Scsi_Cmnd *cmd)
+static void set_dma_cmds(struct fsc_state *state, Scsi_Cmnd *cmd)
{
int i, dma_cmd, total;
struct scatterlist *scl;
@@ -523,7 +371,8 @@ set_dma_cmds(struct fsc_state *state, Scsi_Cmnd *cmd)
u32 dma_len;
int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
- dma_cmd = data_goes_out(cmd)? OUTPUT_MORE: INPUT_MORE;
+ dma_cmd = cmd->sc_data_direction == SCSI_DATA_WRITE? OUTPUT_MORE:
+ INPUT_MORE;
dcmds = state->dma_cmds;
if (cmd->use_sg > 0) {
int nseg;
@@ -562,63 +411,9 @@ set_dma_cmds(struct fsc_state *state, Scsi_Cmnd *cmd)
cmd->SCp.this_residual = total;
}
-/*
- * Work out whether data will be going out from the host adaptor or into it.
- */
-static int
-data_goes_out(Scsi_Cmnd *cmd)
-{
- switch (cmd->sc_data_direction) {
- case SCSI_DATA_WRITE:
- return 1;
- case SCSI_DATA_READ:
- return 0;
- }
-
- /* for SCSI_DATA_UNKNOWN or SCSI_DATA_NONE, fall back on the
- old method for now... */
- switch (cmd->cmnd[0]) {
- case CHANGE_DEFINITION:
- case COMPARE:
- case COPY:
- case COPY_VERIFY:
- case FORMAT_UNIT:
- case LOG_SELECT:
- case MEDIUM_SCAN:
- case MODE_SELECT:
- case MODE_SELECT_10:
- case REASSIGN_BLOCKS:
- case RESERVE:
- case SEARCH_EQUAL:
- case SEARCH_EQUAL_12:
- case SEARCH_HIGH:
- case SEARCH_HIGH_12:
- case SEARCH_LOW:
- case SEARCH_LOW_12:
- case SEND_DIAGNOSTIC:
- case SEND_VOLUME_TAG:
- case SET_WINDOW:
- case UPDATE_BLOCK:
- case WRITE_BUFFER:
- case WRITE_6:
- case WRITE_10:
- case WRITE_12:
- case WRITE_LONG:
- case WRITE_LONG_2: /* alternate code for WRITE_LONG */
- case WRITE_SAME:
- case WRITE_VERIFY:
- case WRITE_VERIFY_12:
- return 1;
- default:
- return 0;
- }
-}
-
-static Scsi_Host_Template driver_template = {
+static Scsi_Host_Template mac53c94_template = {
.proc_name = "53c94",
.name = "53C94",
- .detect = mac53c94_detect,
- .release = mac53c94_release,
.queuecommand = mac53c94_queue,
.eh_abort_handler = mac53c94_abort,
.eh_host_reset_handler = mac53c94_host_reset,
@@ -629,4 +424,158 @@ static Scsi_Host_Template driver_template = {
.use_clustering = DISABLE_CLUSTERING,
};
-#include "scsi_module.c"
+static int mac53c94_probe(struct macio_dev *mdev, const struct of_match *match)
+{
+ struct device_node *node = macio_get_of_node(mdev);
+ struct pci_dev *pdev = macio_get_pci_dev(mdev);
+ struct fsc_state *state;
+ struct Scsi_Host *host;
+ void *dma_cmd_space;
+ unsigned char *clkprop;
+ int proplen;
+
+ if (macio_resource_count(mdev) != 2 || macio_irq_count(mdev) != 2) {
+ printk(KERN_ERR "mac53c94: expected 2 addrs and intrs (got %d/%d)\n",
+ node->n_addrs, node->n_intrs);
+ return -ENODEV;
+ }
+
+ if (macio_request_resources(mdev, "mac53c94") != 0) {
+ printk(KERN_ERR "mac53c94: unable to request memory resources");
+ return -EBUSY;
+ }
+
+ host = scsi_host_alloc(&mac53c94_template, sizeof(struct fsc_state));
+ if (host == NULL) {
+ printk(KERN_ERR "mac53c94: couldn't register host");
+ goto out_release;
+ }
+
+ state = (struct fsc_state *) host->hostdata;
+ macio_set_drvdata(mdev, state);
+ state->host = host;
+ state->pdev = pdev;
+ state->mdev = mdev;
+
+ state->regs = (struct mac53c94_regs *)
+ ioremap(macio_resource_start(mdev, 0), 0x1000);
+ state->intr = macio_irq(mdev, 0);
+ state->dma = (struct dbdma_regs *)
+ ioremap(macio_resource_start(mdev, 1), 0x1000);
+ state->dmaintr = macio_irq(mdev, 1);
+ if (state->regs == NULL || state->dma == NULL) {
+ printk(KERN_ERR "mac53c94: ioremap failed for %s\n",
+ node->full_name);
+ goto out_free;
+ }
+
+ clkprop = get_property(node, "clock-frequency", &proplen);
+ if (clkprop == NULL || proplen != sizeof(int)) {
+ printk(KERN_ERR "%s: can't get clock frequency, "
+ "assuming 25MHz\n", node->full_name);
+ state->clk_freq = 25000000;
+ } else
+ state->clk_freq = *(int *)clkprop;
+
+ /* Space for dma command list: +1 for stop command,
+ * +1 to allow for aligning.
+ * XXX FIXME: Use DMA consistent routines
+ */
+ dma_cmd_space = kmalloc((host->sg_tablesize + 2) *
+ sizeof(struct dbdma_cmd), GFP_KERNEL);
+ if (dma_cmd_space == 0) {
+ printk(KERN_ERR "mac53c94: couldn't allocate dma "
+ "command space for %s\n", node->full_name);
+ goto out_free;
+ }
+ state->dma_cmds = (struct dbdma_cmd *)DBDMA_ALIGN(dma_cmd_space);
+ memset(state->dma_cmds, 0, (host->sg_tablesize + 1)
+ * sizeof(struct dbdma_cmd));
+ state->dma_cmd_space = dma_cmd_space;
+
+ mac53c94_init(state);
+
+ if (request_irq(state->intr, do_mac53c94_interrupt, 0, "53C94", state)) {
+ printk(KERN_ERR "mac53C94: can't get irq %d for %s\n",
+ state->intr, node->full_name);
+ goto out_free_dma;
+ }
+
+ /* XXX FIXME: handle failure */
+ scsi_add_host(host, &mdev->ofdev.dev);
+ scsi_scan_host(host);
+
+ return 0;
+
+ out_free_dma:
+ kfree(state->dma_cmd_space);
+ out_free:
+ if (state->dma != NULL)
+ iounmap(state->dma);
+ if (state->regs != NULL)
+ iounmap(state->regs);
+ scsi_host_put(host);
+ out_release:
+ macio_release_resources(mdev);
+
+ return -ENODEV;
+}
+
+static int mac53c94_remove(struct macio_dev *mdev)
+{
+ struct fsc_state *fp = (struct fsc_state *)macio_get_drvdata(mdev);
+ struct Scsi_Host *host = fp->host;
+
+ scsi_remove_host(host);
+
+ free_irq(fp->intr, fp);
+
+ if (fp->regs)
+ iounmap((void *) fp->regs);
+ if (fp->dma)
+ iounmap((void *) fp->dma);
+ kfree(fp->dma_cmd_space);
+
+ scsi_host_put(host);
+
+ macio_release_resources(mdev);
+
+ return 0;
+}
+
+
+static struct of_match mac53c94_match[] =
+{
+ {
+ .name = "53c94",
+ .type = OF_ANY_MATCH,
+ .compatible = OF_ANY_MATCH
+ },
+ {},
+};
+
+static struct macio_driver mac53c94_driver =
+{
+ .name = "mac53c94",
+ .match_table = mac53c94_match,
+ .probe = mac53c94_probe,
+ .remove = mac53c94_remove,
+};
+
+
+static int __init init_mac53c94(void)
+{
+ return macio_register_driver(&mac53c94_driver);
+}
+
+static void __exit exit_mac53c94(void)
+{
+ return macio_unregister_driver(&mac53c94_driver);
+}
+
+module_init(init_mac53c94);
+module_exit(exit_mac53c94);
+
+MODULE_DESCRIPTION("PowerMac 53c94 SCSI driver");
+MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c
index e9dae68a11f0..ec0f629c280a 100644
--- a/drivers/scsi/mesh.c
+++ b/drivers/scsi/mesh.c
@@ -10,9 +10,13 @@
* Apr. 21 2002 - BenH Rework bus reset code for new error handler
* Add delay after initial bus reset
* Add module parameters
+ *
+ * Sep. 27 2003 - BenH Move to new driver model, fix some write posting
+ * issues
* To do:
* - handle aborts correctly
* - retry arbitration if lost (unless higher levels do this for us)
+ * - power down the chip when no device is detected
*/
#include <linux/config.h>
#include <linux/module.h>
@@ -38,10 +42,7 @@
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/pci-bridge.h>
-#ifdef CONFIG_PMAC_PBOOK
-#include <linux/adb.h>
-#include <linux/pmu.h>
-#endif
+#include <asm/macio.h>
#include "scsi.h"
#include "hosts.h"
@@ -164,10 +165,12 @@ struct mesh_state {
int last_n_msgout;
u8 msgout[16];
struct dbdma_cmd *dma_cmds; /* space for dbdma commands, aligned */
+ dma_addr_t dma_cmd_bus;
+ void *dma_cmd_space;
+ int dma_cmd_size;
int clk_freq;
struct mesh_target tgts[8];
- void *dma_cmd_space;
- struct device_node *ofnode;
+ struct macio_dev *mdev;
struct pci_dev* pdev;
#ifdef MESH_DBG
int log_ix;
@@ -176,324 +179,124 @@ struct mesh_state {
#endif
};
-#ifdef MESH_DBG
-
-static void dlog(struct mesh_state *ms, char *fmt, int a);
-static void dumplog(struct mesh_state *ms, int tgt);
-static void dumpslog(struct mesh_state *ms);
-
-#else
-static inline void dlog(struct mesh_state *ms, char *fmt, int a)
-{}
-static inline void dumplog(struct mesh_state *ms, int tgt)
-{}
-static inline void dumpslog(struct mesh_state *ms)
-{}
+/*
+ * Driver is too messy, we need a few prototypes...
+ */
+static void mesh_done(struct mesh_state *ms, int start_next);
+static void mesh_interrupt(int irq, void *dev_id, struct pt_regs *ptregs);
+static void cmd_complete(struct mesh_state *ms);
+static void set_dma_cmds(struct mesh_state *ms, Scsi_Cmnd *cmd);
+static void halt_dma(struct mesh_state *ms);
+static void phase_mismatch(struct mesh_state *ms);
-#endif /* MESH_DBG */
-#define MKWORD(a, b, c, d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
-static struct mesh_state *all_meshes;
-
-static void mesh_init(struct mesh_state *);
-static int mesh_notify_reboot(struct notifier_block *, unsigned long, void *);
-static void mesh_dump_regs(struct mesh_state *);
-static void mesh_start(struct mesh_state *);
-static void mesh_start_cmd(struct mesh_state *, Scsi_Cmnd *);
-static void add_sdtr_msg(struct mesh_state *);
-static void set_sdtr(struct mesh_state *, int, int);
-static void start_phase(struct mesh_state *);
-static void get_msgin(struct mesh_state *);
-static int msgin_length(struct mesh_state *);
-static void cmd_complete(struct mesh_state *);
-static void phase_mismatch(struct mesh_state *);
-static void reselected(struct mesh_state *);
-static void handle_reset(struct mesh_state *);
-static void handle_error(struct mesh_state *);
-static void handle_exception(struct mesh_state *);
-static void mesh_interrupt(int, void *, struct pt_regs *);
-static irqreturn_t do_mesh_interrupt(int, void *, struct pt_regs *);
-static void handle_msgin(struct mesh_state *);
-static void mesh_done(struct mesh_state *, int);
-static void mesh_completed(struct mesh_state *, Scsi_Cmnd *);
-static void set_dma_cmds(struct mesh_state *, Scsi_Cmnd *);
-static void halt_dma(struct mesh_state *);
-static int data_goes_out(Scsi_Cmnd *);
-static void do_abort(struct mesh_state *ms);
-static void set_mesh_power(struct mesh_state *ms, int state);
-
-#ifdef CONFIG_PMAC_PBOOK
-static int mesh_notify_sleep(struct pmu_sleep_notifier *self, int when);
-static struct pmu_sleep_notifier mesh_sleep_notifier = {
- mesh_notify_sleep,
- SLEEP_LEVEL_BLOCK,
-};
-#endif
+/*
+ * Some debugging & logging routines
+ */
-static struct notifier_block mesh_notifier = {
- mesh_notify_reboot,
- NULL,
- 0
-};
+#ifdef MESH_DBG
-int
-mesh_detect(Scsi_Host_Template *tp)
+static inline u32 readtb(void)
{
- struct device_node *mesh;
- int nmeshes, tgt, *cfp, minper;
- struct mesh_state *ms, **prev_statep;
- struct Scsi_Host *mesh_host;
- void *dma_cmd_space;
-
- if (_machine == _MACH_Pmac) {
- use_active_neg = (find_devices("mac-io") ? 0 : SEQ_ACTIVE_NEG);
- } else {
- /* CHRP mac-io */
- use_active_neg = SEQ_ACTIVE_NEG;
- }
-
- /* Calculate sync rate from module parameters */
- if (sync_rate > 10)
- sync_rate = 10;
- if (sync_rate > 0) {
- printk(KERN_INFO "mesh: configured for synchronous %d MB/s\n", sync_rate);
- mesh_sync_period = 1000 / sync_rate; /* ns */
- mesh_sync_offset = 15;
- } else
- printk(KERN_INFO "mesh: configured for asynchronous\n");
-
- nmeshes = 0;
- prev_statep = &all_meshes;
- /*
- * On powermacs, the MESH node has device_type "mesh".
- * On chrp machines, its device_type is "scsi" with
- * "chrp,mesh0" as its `compatible' property.
- */
- mesh = find_devices("mesh");
- if (mesh == 0)
- mesh = find_compatible_devices("scsi", "chrp,mesh0");
- for (; mesh != 0; mesh = mesh->next) {
- u8 pci_bus, pci_devfn;
- struct pci_dev* pdev = NULL;
-
- if (mesh->n_addrs != 2 || mesh->n_intrs != 2) {
- printk(KERN_ERR "mesh: expected 2 addrs and 2 intrs"
- " (got %d,%d)\n", mesh->n_addrs, mesh->n_intrs);
- continue;
- }
- if (mesh->parent != NULL
- && pci_device_from_OF_node(mesh->parent, &pci_bus,
- &pci_devfn) == 0)
- pdev = pci_find_slot(pci_bus, pci_devfn);
- if (pdev == NULL) {
- printk(KERN_ERR "mesh: Can't locate PCI entry\n");
- continue;
- }
+ u32 tb;
- mesh_host = scsi_register(tp, sizeof(struct mesh_state));
- if (mesh_host == 0) {
- printk(KERN_ERR "mesh: couldn't register host");
- continue;
- }
- mesh_host->unique_id = nmeshes;
-#if !defined(MODULE)
- note_scsi_host(mesh, mesh_host);
+#ifdef DBG_USE_TB
+ /* Beware: if you enable this, it will crash on 601s. */
+ asm ("mftb %0" : "=r" (tb) : );
+#else
+ tb = 0;
#endif
-
- ms = (struct mesh_state *) mesh_host->hostdata;
- if (ms == 0)
- panic("no mesh state");
- memset(ms, 0, sizeof(*ms));
- ms->host = mesh_host;
- ms->ofnode = mesh;
- ms->pdev = pdev;
- ms->mesh = (volatile struct mesh_regs *)
- ioremap(mesh->addrs[0].address, 0x1000);
- ms->dma = (volatile struct dbdma_regs *)
- ioremap(mesh->addrs[1].address, 0x1000);
- ms->meshintr = mesh->intrs[0].line;
- ms->dmaintr = mesh->intrs[1].line;
-
- /* Space for dma command list: +1 for stop command,
- +1 to allow for aligning. */
- dma_cmd_space = kmalloc((mesh_host->sg_tablesize + 2) *
- sizeof(struct dbdma_cmd), GFP_KERNEL);
- if (dma_cmd_space == 0)
- panic("mesh: couldn't allocate dma command space");
- ms->dma_cmds = (struct dbdma_cmd *) DBDMA_ALIGN(dma_cmd_space);
- memset(ms->dma_cmds, 0, (mesh_host->sg_tablesize + 1)
- * sizeof(struct dbdma_cmd));
- ms->dma_cmd_space = dma_cmd_space;
-
- ms->current_req = 0;
- for (tgt = 0; tgt < 8; ++tgt) {
- ms->tgts[tgt].sdtr_state = do_sdtr;
- ms->tgts[tgt].sync_params = ASYNC_PARAMS;
- ms->tgts[tgt].current_req = 0;
- }
- *prev_statep = ms;
- prev_statep = &ms->next;
-
- if ((cfp = (int *) get_property(mesh, "clock-frequency",
- NULL))) {
- ms->clk_freq = *cfp;
- } else {
- printk(KERN_INFO "mesh: assuming 50MHz clock frequency\n");
- ms->clk_freq = 50000000;
- }
- /* The maximum sync rate is clock / 5; increase
- mesh_sync_period if necessary. */
- minper = 1000000000 / (ms->clk_freq / 5); /* ns */
- if (mesh_sync_period < minper)
- mesh_sync_period = minper;
-
- set_mesh_power(ms, 1);
-
- mesh_init(ms);
-
- if (request_irq(ms->meshintr, do_mesh_interrupt, 0, "MESH", ms)) {
- printk(KERN_ERR "MESH: can't get irq %d\n", ms->meshintr);
- }
-
- ++nmeshes;
- }
-
- if ((_machine == _MACH_Pmac) && (nmeshes > 0)) {
-#ifdef CONFIG_PMAC_PBOOK
- pmu_register_sleep_notifier(&mesh_sleep_notifier);
-#endif /* CONFIG_PMAC_PBOOK */
- register_reboot_notifier(&mesh_notifier);
- }
-
- return nmeshes;
+ return tb;
}
-int
-mesh_release(struct Scsi_Host *host)
+static void dlog(struct mesh_state *ms, char *fmt, int a)
{
- struct mesh_state *ms = (struct mesh_state *) host->hostdata;
+ struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
+ struct dbglog *tlp, *slp;
- if (ms == 0)
- return 0;
- if (ms->mesh)
- iounmap((void *) ms->mesh);
- if (ms->dma)
- iounmap((void *) ms->dma);
- kfree(ms->dma_cmd_space);
- free_irq(ms->meshintr, ms);
- pmac_call_feature(PMAC_FTR_MESH_ENABLE, ms->ofnode, 0, 0);
- return 0;
+ tlp = &tp->log[tp->log_ix];
+ slp = &ms->log[ms->log_ix];
+ tlp->fmt = fmt;
+ tlp->tb = readtb();
+ tlp->phase = (ms->msgphase << 4) + ms->phase;
+ tlp->bs0 = ms->mesh->bus_status0;
+ tlp->bs1 = ms->mesh->bus_status1;
+ tlp->tgt = ms->conn_tgt;
+ tlp->d = a;
+ *slp = *tlp;
+ if (++tp->log_ix >= N_DBG_LOG)
+ tp->log_ix = 0;
+ if (tp->n_log < N_DBG_LOG)
+ ++tp->n_log;
+ if (++ms->log_ix >= N_DBG_SLOG)
+ ms->log_ix = 0;
+ if (ms->n_log < N_DBG_SLOG)
+ ++ms->n_log;
}
-static void
-set_mesh_power(struct mesh_state *ms, int state)
+static void dumplog(struct mesh_state *ms, int t)
{
- if (_machine != _MACH_Pmac)
- return;
- if (state) {
- pmac_call_feature(PMAC_FTR_MESH_ENABLE, ms->ofnode, 0, 1);
- mdelay(200);
- } else {
- pmac_call_feature(PMAC_FTR_MESH_ENABLE, ms->ofnode, 0, 0);
- mdelay(10);
- }
-}
+ struct mesh_target *tp = &ms->tgts[t];
+ struct dbglog *lp;
+ int i;
-#ifdef CONFIG_PMAC_PBOOK
-/*
- * notify clients before sleep and reset bus afterwards
- */
-int
-mesh_notify_sleep(struct pmu_sleep_notifier *self, int when)
-{
- struct mesh_state *ms;
-
- switch (when) {
- case PBOOK_SLEEP_REQUEST:
- /* XXX We should wait for current transactions and queue
- * new ones that would be posted beyond this point
- */
- break;
- case PBOOK_SLEEP_REJECT:
- break;
-
- case PBOOK_SLEEP_NOW:
- for (ms = all_meshes; ms != 0; ms = ms->next) {
- unsigned long flags;
-
- scsi_block_requests(ms->host);
- spin_lock_irqsave(ms->host->host_lock, flags);
- while(ms->phase != idle) {
- spin_unlock_irqrestore(ms->host->host_lock, flags);
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout(1);
- spin_lock_irqsave(ms->host->host_lock, flags);
- }
- ms->phase = sleeping;
- spin_unlock_irqrestore(ms->host->host_lock, flags);
- disable_irq(ms->meshintr);
- set_mesh_power(ms, 0);
- }
- break;
- case PBOOK_WAKE:
- for (ms = all_meshes; ms != 0; ms = ms->next) {
- unsigned long flags;
-
- set_mesh_power(ms, 1);
- mesh_init(ms);
- spin_lock_irqsave(ms->host->host_lock, flags);
- mesh_start(ms);
- spin_unlock_irqrestore(ms->host->host_lock, flags);
- enable_irq(ms->meshintr);
- scsi_unblock_requests(ms->host);
- }
- break;
- }
- return PBOOK_SLEEP_OK;
+ if (tp->n_log == 0)
+ return;
+ i = tp->log_ix - tp->n_log;
+ if (i < 0)
+ i += N_DBG_LOG;
+ tp->n_log = 0;
+ do {
+ lp = &tp->log[i];
+ printk(KERN_DEBUG "mesh log %d: bs=%.2x%.2x ph=%.2x ",
+ t, lp->bs1, lp->bs0, lp->phase);
+#ifdef DBG_USE_TB
+ printk("tb=%10u ", lp->tb);
+#endif
+ printk(lp->fmt, lp->d);
+ printk("\n");
+ if (++i >= N_DBG_LOG)
+ i = 0;
+ } while (i != tp->log_ix);
}
-#endif /* CONFIG_PMAC_PBOOK */
-/*
- * Called by midlayer with host locked to queue a new
- * request
- */
-int
-mesh_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+static void dumpslog(struct mesh_state *ms)
{
- struct mesh_state *ms;
-
- cmd->scsi_done = done;
- cmd->host_scribble = NULL;
-
- ms = (struct mesh_state *) cmd->device->host->hostdata;
+ struct dbglog *lp;
+ int i;
- if (ms->request_q == NULL)
- ms->request_q = cmd;
- else
- ms->request_qtail->host_scribble = (void *) cmd;
- ms->request_qtail = cmd;
+ if (ms->n_log == 0)
+ return;
+ i = ms->log_ix - ms->n_log;
+ if (i < 0)
+ i += N_DBG_SLOG;
+ ms->n_log = 0;
+ do {
+ lp = &ms->log[i];
+ printk(KERN_DEBUG "mesh log: bs=%.2x%.2x ph=%.2x t%d ",
+ lp->bs1, lp->bs0, lp->phase, lp->tgt);
+#ifdef DBG_USE_TB
+ printk("tb=%10u ", lp->tb);
+#endif
+ printk(lp->fmt, lp->d);
+ printk("\n");
+ if (++i >= N_DBG_SLOG)
+ i = 0;
+ } while (i != ms->log_ix);
+}
- if (ms->phase == idle)
- mesh_start(ms);
+#else
- return 0;
-}
+static inline void dlog(struct mesh_state *ms, char *fmt, int a)
+{}
+static inline void dumplog(struct mesh_state *ms, int tgt)
+{}
+static inline void dumpslog(struct mesh_state *ms)
+{}
-/* Todo: here we can at least try to remove the command from the
- * queue if it isn't connected yet, and for pending command, assert
- * ATN until the bus gets freed.
- */
-int
-mesh_abort(Scsi_Cmnd *cmd)
-{
- struct mesh_state *ms = (struct mesh_state *) cmd->device->host->hostdata;
+#endif /* MESH_DBG */
- printk(KERN_DEBUG "mesh_abort(%p)\n", cmd);
- mesh_dump_regs(ms);
- dumplog(ms, cmd->device->id);
- dumpslog(ms);
- return SCSI_ABORT_SNOOZE;
-}
+#define MKWORD(a, b, c, d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
static void
mesh_dump_regs(struct mesh_state *ms)
@@ -528,79 +331,35 @@ mesh_dump_regs(struct mesh_state *ms)
}
}
+
/*
- * Called by the midlayer with the lock held to reset the
- * SCSI host and bus.
- * The midlayer will wait for devices to come back, we don't need
- * to do that ourselves
+ * Flush write buffers on the bus path to the mesh
*/
-int
-mesh_host_reset(Scsi_Cmnd *cmd)
+static inline void mesh_flush_io(volatile struct mesh_regs *mr)
{
- struct mesh_state *ms = (struct mesh_state *) cmd->device->host->hostdata;
- volatile struct mesh_regs *mr = ms->mesh;
- volatile struct dbdma_regs *md = ms->dma;
-
- printk(KERN_DEBUG "mesh_host_reset\n");
-
- /* Reset the controller & dbdma channel */
- out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* stop dma */
- out_8(&mr->exception, 0xff); /* clear all exception bits */
- out_8(&mr->error, 0xff); /* clear all error bits */
- out_8(&mr->sequence, SEQ_RESETMESH);
- udelay(1);
- out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
- out_8(&mr->source_id, ms->host->this_id);
- out_8(&mr->sel_timeout, 25); /* 250ms */
- out_8(&mr->sync_params, ASYNC_PARAMS);
-
- /* Reset the bus */
- out_8(&mr->bus_status1, BS1_RST); /* assert RST */
- udelay(30); /* leave it on for >= 25us */
- out_8(&mr->bus_status1, 0); /* negate RST */
-
- /* Complete pending commands */
- handle_reset(ms);
-
- return SUCCESS;
+ (void)in_8(&mr->mesh_id);
}
+
/*
- * If we leave drives set for synchronous transfers (especially
- * CDROMs), and reboot to MacOS, it gets confused, poor thing.
- * So, on reboot we reset the SCSI bus.
+ * Complete a SCSI command
*/
-static int
-mesh_notify_reboot(struct notifier_block *this, unsigned long code, void *x)
+static void mesh_completed(struct mesh_state *ms, Scsi_Cmnd *cmd)
{
- struct mesh_state *ms;
- volatile struct mesh_regs *mr;
-
- if (code == SYS_DOWN) {
- printk(KERN_INFO "resetting MESH scsi bus(es)\n");
- for (ms = all_meshes; ms != 0; ms = ms->next) {
- mr = ms->mesh;
- out_8(&mr->intr_mask, 0);
- out_8(&mr->interrupt,
- INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
- out_8(&mr->bus_status1, BS1_RST);
- udelay(30);
- out_8(&mr->bus_status1, 0);
- }
- }
- return NOTIFY_DONE;
+ (*cmd->scsi_done)(cmd);
}
+
/* Called with meshinterrupt disabled, initialize the chipset
* and eventually do the initial bus reset. The lock must not be
* held since we can schedule.
*/
-static void
-mesh_init(struct mesh_state *ms)
+static void mesh_init(struct mesh_state *ms)
{
volatile struct mesh_regs *mr = ms->mesh;
volatile struct dbdma_regs *md = ms->dma;
+ mesh_flush_io(mr);
udelay(100);
/* Reset controller */
@@ -608,6 +367,7 @@ mesh_init(struct mesh_state *ms)
out_8(&mr->exception, 0xff); /* clear all exception bits */
out_8(&mr->error, 0xff); /* clear all error bits */
out_8(&mr->sequence, SEQ_RESETMESH);
+ mesh_flush_io(mr);
udelay(10);
out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
out_8(&mr->source_id, ms->host->this_id);
@@ -619,8 +379,10 @@ mesh_init(struct mesh_state *ms)
/* Reset bus */
out_8(&mr->bus_status1, BS1_RST); /* assert RST */
+ mesh_flush_io(mr);
udelay(30); /* leave it on for >= 25us */
out_8(&mr->bus_status1, 0); /* negate RST */
+ mesh_flush_io(mr);
/* Wait for bus to come back */
current->state = TASK_UNINTERRUPTIBLE;
@@ -630,6 +392,7 @@ mesh_init(struct mesh_state *ms)
/* Reconfigure controller */
out_8(&mr->interrupt, 0xff); /* clear all interrupt bits */
out_8(&mr->sequence, SEQ_FLUSHFIFO);
+ mesh_flush_io(mr);
udelay(1);
out_8(&mr->sync_params, ASYNC_PARAMS);
out_8(&mr->sequence, SEQ_ENBRESEL);
@@ -638,51 +401,15 @@ mesh_init(struct mesh_state *ms)
ms->msgphase = msg_none;
}
-/*
- * Start the next command for a MESH.
- * Should be called with interrupts disabled.
- */
-static void
-mesh_start(struct mesh_state *ms)
-{
- Scsi_Cmnd *cmd, *prev, *next;
- if (ms->phase != idle || ms->current_req != NULL) {
- printk(KERN_ERR "inappropriate mesh_start (phase=%d, ms=%p)",
- ms->phase, ms);
- return;
- }
-
- while (ms->phase == idle) {
- prev = NULL;
- for (cmd = ms->request_q; ; cmd = (Scsi_Cmnd *) cmd->host_scribble) {
- if (cmd == NULL)
- return;
- if (ms->tgts[cmd->device->id].current_req == NULL)
- break;
- prev = cmd;
- }
- next = (Scsi_Cmnd *) cmd->host_scribble;
- if (prev == NULL)
- ms->request_q = next;
- else
- prev->host_scribble = (void *) next;
- if (next == NULL)
- ms->request_qtail = prev;
-
- mesh_start_cmd(ms, cmd);
- }
-}
-
-static void
-mesh_start_cmd(struct mesh_state *ms, Scsi_Cmnd *cmd)
+static void mesh_start_cmd(struct mesh_state *ms, Scsi_Cmnd *cmd)
{
volatile struct mesh_regs *mr = ms->mesh;
int t, id;
id = cmd->device->id;
ms->current_req = cmd;
- ms->tgts[id].data_goes_out = data_goes_out(cmd);
+ ms->tgts[id].data_goes_out = cmd->sc_data_direction == SCSI_DATA_WRITE;
ms->tgts[id].current_req = cmd;
#if 1
@@ -720,9 +447,10 @@ mesh_start_cmd(struct mesh_state *ms, Scsi_Cmnd *cmd)
MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
out_8(&mr->interrupt, INT_CMDDONE);
out_8(&mr->sequence, SEQ_ENBRESEL);
+ mesh_flush_io(mr);
udelay(1);
- if (mr->bus_status1 & (BS1_BSY | BS1_SEL)) {
+ if (in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) {
/*
* Some other device has the bus or is arbitrating for it -
* probably a target which is about to reselect us.
@@ -731,7 +459,7 @@ mesh_start_cmd(struct mesh_state *ms, Scsi_Cmnd *cmd)
MKWORD(mr->interrupt, mr->exception,
mr->error, mr->fifo_count));
for (t = 100; t > 0; --t) {
- if ((mr->bus_status1 & (BS1_BSY | BS1_SEL)) == 0)
+ if ((in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) == 0)
break;
if (in_8(&mr->interrupt) != 0) {
dlog(ms, "intr b4 arb, intr/exc/err/fc=%.8x",
@@ -743,7 +471,7 @@ mesh_start_cmd(struct mesh_state *ms, Scsi_Cmnd *cmd)
}
udelay(1);
}
- if (mr->bus_status1 & (BS1_BSY | BS1_SEL)) {
+ if (in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) {
/* XXX should try again in a little while */
ms->stat = DID_BUS_BUSY;
ms->phase = idle;
@@ -792,23 +520,25 @@ mesh_start_cmd(struct mesh_state *ms, Scsi_Cmnd *cmd)
}
dlog(ms, "after arb, intr/exc/err/fc=%.8x",
MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
- if (mr->interrupt == 0 && (mr->bus_status1 & BS1_SEL)
- && (mr->bus_status0 & BS0_IO)) {
+ if (in_8(&mr->interrupt) == 0 && (in_8(&mr->bus_status1) & BS1_SEL)
+ && (in_8(&mr->bus_status0) & BS0_IO)) {
/* looks like a reselection - try resetting the mesh */
dlog(ms, "resel? after arb, intr/exc/err/fc=%.8x",
MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
out_8(&mr->sequence, SEQ_RESETMESH);
+ mesh_flush_io(mr);
udelay(10);
out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
out_8(&mr->sequence, SEQ_ENBRESEL);
- for (t = 10; t > 0 && mr->interrupt == 0; --t)
+ mesh_flush_io(mr);
+ for (t = 10; t > 0 && in_8(&mr->interrupt) == 0; --t)
udelay(1);
dlog(ms, "tried reset after arb, intr/exc/err/fc=%.8x",
MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
#ifndef MESH_MULTIPLE_HOSTS
- if (mr->interrupt == 0 && (mr->bus_status1 & BS1_SEL)
- && (mr->bus_status0 & BS0_IO)) {
+ if (in_8(&mr->interrupt) == 0 && (in_8(&mr->bus_status1) & BS1_SEL)
+ && (in_8(&mr->bus_status0) & BS0_IO)) {
printk(KERN_ERR "mesh: controller not responding"
" to reselection!\n");
/*
@@ -822,8 +552,76 @@ mesh_start_cmd(struct mesh_state *ms, Scsi_Cmnd *cmd)
}
}
-static inline void
-add_sdtr_msg(struct mesh_state *ms)
+/*
+ * Start the next command for a MESH.
+ * Should be called with interrupts disabled.
+ */
+static void mesh_start(struct mesh_state *ms)
+{
+ Scsi_Cmnd *cmd, *prev, *next;
+
+ if (ms->phase != idle || ms->current_req != NULL) {
+ printk(KERN_ERR "inappropriate mesh_start (phase=%d, ms=%p)",
+ ms->phase, ms);
+ return;
+ }
+
+ while (ms->phase == idle) {
+ prev = NULL;
+ for (cmd = ms->request_q; ; cmd = (Scsi_Cmnd *) cmd->host_scribble) {
+ if (cmd == NULL)
+ return;
+ if (ms->tgts[cmd->device->id].current_req == NULL)
+ break;
+ prev = cmd;
+ }
+ next = (Scsi_Cmnd *) cmd->host_scribble;
+ if (prev == NULL)
+ ms->request_q = next;
+ else
+ prev->host_scribble = (void *) next;
+ if (next == NULL)
+ ms->request_qtail = prev;
+
+ mesh_start_cmd(ms, cmd);
+ }
+}
+
+static void mesh_done(struct mesh_state *ms, int start_next)
+{
+ Scsi_Cmnd *cmd;
+ struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
+
+ cmd = ms->current_req;
+ ms->current_req = 0;
+ tp->current_req = 0;
+ if (cmd) {
+ cmd->result = (ms->stat << 16) + cmd->SCp.Status;
+ if (ms->stat == DID_OK)
+ cmd->result += (cmd->SCp.Message << 8);
+ if (DEBUG_TARGET(cmd)) {
+ printk(KERN_DEBUG "mesh_done: result = %x, data_ptr=%d, buflen=%d\n",
+ cmd->result, ms->data_ptr, cmd->request_bufflen);
+ if ((cmd->cmnd[0] == 0 || cmd->cmnd[0] == 0x12 || cmd->cmnd[0] == 3)
+ && cmd->request_buffer != 0) {
+ unsigned char *b = cmd->request_buffer;
+ printk(KERN_DEBUG "buffer = %x %x %x %x %x %x %x %x\n",
+ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+ }
+ }
+ cmd->SCp.this_residual -= ms->data_ptr;
+ mesh_completed(ms, cmd);
+ }
+ if (start_next) {
+ out_8(&ms->mesh->sequence, SEQ_ENBRESEL);
+ mesh_flush_io(ms->mesh);
+ udelay(1);
+ ms->phase = idle;
+ mesh_start(ms);
+ }
+}
+
+static inline void add_sdtr_msg(struct mesh_state *ms)
{
int i = ms->n_msgout;
@@ -835,8 +633,7 @@ add_sdtr_msg(struct mesh_state *ms)
ms->n_msgout = i + 5;
}
-static void
-set_sdtr(struct mesh_state *ms, int period, int offset)
+static void set_sdtr(struct mesh_state *ms, int period, int offset)
{
struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
volatile struct mesh_regs *mr = ms->mesh;
@@ -877,8 +674,7 @@ set_sdtr(struct mesh_state *ms, int period, int offset)
ms->conn_tgt, tr/10, tr%10);
}
-static void
-start_phase(struct mesh_state *ms)
+static void start_phase(struct mesh_state *ms)
{
int i, seq, nb;
volatile struct mesh_regs *mr = ms->mesh;
@@ -925,14 +721,16 @@ start_phase(struct mesh_state *ms)
ms->msgout[1], ms->msgout[2]));
out_8(&mr->count_hi, 0);
out_8(&mr->sequence, SEQ_FLUSHFIFO);
+ mesh_flush_io(mr);
udelay(1);
/*
* If ATN is not already asserted, we assert it, then
* issue a SEQ_MSGOUT to get the mesh to drop ACK.
*/
- if ((mr->bus_status0 & BS0_ATN) == 0) {
+ if ((in_8(&mr->bus_status0) & BS0_ATN) == 0) {
dlog(ms, "bus0 was %.2x explictly asserting ATN", mr->bus_status0);
out_8(&mr->bus_status0, BS0_ATN); /* explicit ATN */
+ mesh_flush_io(mr);
udelay(1);
out_8(&mr->count_lo, 1);
out_8(&mr->sequence, SEQ_MSGOUT + seq);
@@ -1006,6 +804,7 @@ start_phase(struct mesh_state *ms)
case busfreeing:
case disconnecting:
out_8(&mr->sequence, SEQ_ENBRESEL);
+ mesh_flush_io(mr);
udelay(1);
dlog(ms, "enbresel intr/exc/err/fc=%.8x",
MKWORD(mr->interrupt, mr->exception, mr->error,
@@ -1020,8 +819,7 @@ start_phase(struct mesh_state *ms)
}
-static inline void
-get_msgin(struct mesh_state *ms)
+static inline void get_msgin(struct mesh_state *ms)
{
volatile struct mesh_regs *mr = ms->mesh;
int i, n;
@@ -1035,8 +833,7 @@ get_msgin(struct mesh_state *ms)
}
}
-static inline int
-msgin_length(struct mesh_state *ms)
+static inline int msgin_length(struct mesh_state *ms)
{
int b, n;
@@ -1054,265 +851,7 @@ msgin_length(struct mesh_state *ms)
return n;
}
-static void
-cmd_complete(struct mesh_state *ms)
-{
- volatile struct mesh_regs *mr = ms->mesh;
- Scsi_Cmnd *cmd = ms->current_req;
- struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
- int seq, n, t;
-
- dlog(ms, "cmd_complete fc=%x", mr->fifo_count);
- seq = use_active_neg + (ms->n_msgout? SEQ_ATN: 0);
- switch (ms->msgphase) {
- case msg_out_xxx:
- /* huh? we expected a phase mismatch */
- ms->n_msgin = 0;
- ms->msgphase = msg_in;
- /* fall through */
-
- case msg_in:
- /* should have some message bytes in fifo */
- get_msgin(ms);
- n = msgin_length(ms);
- if (ms->n_msgin < n) {
- out_8(&mr->count_lo, n - ms->n_msgin);
- out_8(&mr->sequence, SEQ_MSGIN + seq);
- } else {
- ms->msgphase = msg_none;
- handle_msgin(ms);
- start_phase(ms);
- }
- break;
-
- case msg_in_bad:
- out_8(&mr->sequence, SEQ_FLUSHFIFO);
- udelay(1);
- out_8(&mr->count_lo, 1);
- out_8(&mr->sequence, SEQ_MSGIN + SEQ_ATN + use_active_neg);
- break;
-
- case msg_out:
- /*
- * To get the right timing on ATN wrt ACK, we have
- * to get the MESH to drop ACK, wait until REQ gets
- * asserted, then drop ATN. To do this we first
- * issue a SEQ_MSGOUT with ATN and wait for REQ,
- * then change the command to a SEQ_MSGOUT w/o ATN.
- * If we don't see REQ in a reasonable time, we
- * change the command to SEQ_MSGIN with ATN,
- * wait for the phase mismatch interrupt, then
- * issue the SEQ_MSGOUT without ATN.
- */
- out_8(&mr->count_lo, 1);
- out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg + SEQ_ATN);
- t = 30; /* wait up to 30us */
- while ((mr->bus_status0 & BS0_REQ) == 0 && --t >= 0)
- udelay(1);
- dlog(ms, "last_mbyte err/exc/fc/cl=%.8x",
- MKWORD(mr->error, mr->exception,
- mr->fifo_count, mr->count_lo));
- if (in_8(&mr->interrupt) & (INT_ERROR | INT_EXCEPTION)) {
- /* whoops, target didn't do what we expected */
- ms->last_n_msgout = ms->n_msgout;
- ms->n_msgout = 0;
- if (in_8(&mr->interrupt) & INT_ERROR) {
- printk(KERN_ERR "mesh: error %x in msg_out\n",
- in_8(&mr->error));
- handle_error(ms);
- return;
- }
- if (in_8(&mr->exception) != EXC_PHASEMM)
- printk(KERN_ERR "mesh: exc %x in msg_out\n",
- in_8(&mr->exception));
- else
- printk(KERN_DEBUG "mesh: bs0=%x in msg_out\n",
- in_8(&mr->bus_status0));
- handle_exception(ms);
- return;
- }
- if (mr->bus_status0 & BS0_REQ) {
- out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg);
- udelay(1);
- out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);
- ms->msgphase = msg_out_last;
- } else {
- out_8(&mr->sequence, SEQ_MSGIN + use_active_neg + SEQ_ATN);
- ms->msgphase = msg_out_xxx;
- }
- break;
-
- case msg_out_last:
- ms->last_n_msgout = ms->n_msgout;
- ms->n_msgout = 0;
- ms->msgphase = ms->expect_reply? msg_in: msg_none;
- start_phase(ms);
- break;
-
- case msg_none:
- switch (ms->phase) {
- case idle:
- printk(KERN_ERR "mesh: interrupt in idle phase?\n");
- dumpslog(ms);
- return;
- case selecting:
- dlog(ms, "Selecting phase at command completion",0);
- ms->msgout[0] = IDENTIFY(ALLOW_RESEL(ms->conn_tgt),
- (cmd? cmd->device->lun: 0));
- ms->n_msgout = 1;
- ms->expect_reply = 0;
- if (ms->aborting) {
- ms->msgout[0] = ABORT;
- ms->n_msgout++;
- } else if (tp->sdtr_state == do_sdtr) {
- /* add SDTR message */
- add_sdtr_msg(ms);
- ms->expect_reply = 1;
- tp->sdtr_state = sdtr_sent;
- }
- ms->msgphase = msg_out;
- /*
- * We need to wait for REQ before dropping ATN.
- * We wait for at most 30us, then fall back to
- * a scheme where we issue a SEQ_COMMAND with ATN,
- * which will give us a phase mismatch interrupt
- * when REQ does come, and then we send the message.
- */
- t = 230; /* wait up to 230us */
- while ((mr->bus_status0 & BS0_REQ) == 0) {
- if (--t < 0) {
- dlog(ms, "impatient for req", ms->n_msgout);
- ms->msgphase = msg_none;
- break;
- }
- udelay(1);
- }
- break;
- case dataing:
- if (ms->dma_count != 0) {
- start_phase(ms);
- return;
- }
- /*
- * We can get a phase mismatch here if the target
- * changes to the status phase, even though we have
- * had a command complete interrupt. Then, if we
- * issue the SEQ_STATUS command, we'll get a sequence
- * error interrupt. Which isn't so bad except that
- * occasionally the mesh actually executes the
- * SEQ_STATUS *as well as* giving us the sequence
- * error and phase mismatch exception.
- */
- out_8(&mr->sequence, 0);
- out_8(&mr->interrupt,
- INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
- halt_dma(ms);
- break;
- case statusing:
- if (cmd) {
- cmd->SCp.Status = mr->fifo;
- if (DEBUG_TARGET(cmd))
- printk(KERN_DEBUG "mesh: status is %x\n",
- cmd->SCp.Status);
- }
- ms->msgphase = msg_in;
- break;
- case busfreeing:
- mesh_done(ms, 1);
- return;
- case disconnecting:
- ms->current_req = 0;
- ms->phase = idle;
- mesh_start(ms);
- return;
- default:
- break;
- }
- ++ms->phase;
- start_phase(ms);
- break;
- }
-}
-
-static void phase_mismatch(struct mesh_state *ms)
-{
- volatile struct mesh_regs *mr = ms->mesh;
- int phase;
-
- dlog(ms, "phasemm ch/cl/seq/fc=%.8x",
- MKWORD(mr->count_hi, mr->count_lo, mr->sequence, mr->fifo_count));
- phase = mr->bus_status0 & BS0_PHASE;
- if (ms->msgphase == msg_out_xxx && phase == BP_MSGOUT) {
- /* output the last byte of the message, without ATN */
- out_8(&mr->count_lo, 1);
- out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg);
- udelay(1);
- out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);
- ms->msgphase = msg_out_last;
- return;
- }
-
- if (ms->msgphase == msg_in) {
- get_msgin(ms);
- if (ms->n_msgin)
- handle_msgin(ms);
- }
-
- if (ms->dma_started)
- halt_dma(ms);
- if (mr->fifo_count) {
- out_8(&mr->sequence, SEQ_FLUSHFIFO);
- udelay(1);
- }
-
- ms->msgphase = msg_none;
- switch (phase) {
- case BP_DATAIN:
- ms->tgts[ms->conn_tgt].data_goes_out = 0;
- ms->phase = dataing;
- break;
- case BP_DATAOUT:
- ms->tgts[ms->conn_tgt].data_goes_out = 1;
- ms->phase = dataing;
- break;
- case BP_COMMAND:
- ms->phase = commanding;
- break;
- case BP_STATUS:
- ms->phase = statusing;
- break;
- case BP_MSGIN:
- ms->msgphase = msg_in;
- ms->n_msgin = 0;
- break;
- case BP_MSGOUT:
- ms->msgphase = msg_out;
- if (ms->n_msgout == 0) {
- if (ms->aborting) {
- do_abort(ms);
- } else {
- if (ms->last_n_msgout == 0) {
- printk(KERN_DEBUG
- "mesh: no msg to repeat\n");
- ms->msgout[0] = NOP;
- ms->last_n_msgout = 1;
- }
- ms->n_msgout = ms->last_n_msgout;
- }
- }
- break;
- default:
- printk(KERN_DEBUG "mesh: unknown scsi phase %x\n", phase);
- ms->stat = DID_ERROR;
- mesh_done(ms, 1);
- return;
- }
-
- start_phase(ms);
-}
-
-static void
-reselected(struct mesh_state *ms)
+static void reselected(struct mesh_state *ms)
{
volatile struct mesh_regs *mr = ms->mesh;
Scsi_Cmnd *cmd;
@@ -1360,26 +899,30 @@ reselected(struct mesh_state *ms)
/*
* We seem to get abortive reselections sometimes.
*/
- while ((mr->bus_status1 & BS1_BSY) == 0) {
+ while ((in_8(&mr->bus_status1) & BS1_BSY) == 0) {
static int mesh_aborted_resels;
mesh_aborted_resels++;
out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ mesh_flush_io(mr);
udelay(1);
out_8(&mr->sequence, SEQ_ENBRESEL);
+ mesh_flush_io(mr);
udelay(5);
dlog(ms, "extra resel err/exc/fc = %.6x",
MKWORD(0, mr->error, mr->exception, mr->fifo_count));
}
out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ mesh_flush_io(mr);
udelay(1);
out_8(&mr->sequence, SEQ_ENBRESEL);
+ mesh_flush_io(mr);
udelay(1);
out_8(&mr->sync_params, ASYNC_PARAMS);
/*
* Find out who reselected us.
*/
- if (mr->fifo_count == 0) {
+ if (in_8(&mr->fifo_count) == 0) {
printk(KERN_ERR "mesh: reselection but nothing in fifo?\n");
ms->conn_tgt = ms->host->this_id;
goto bogus;
@@ -1438,8 +981,7 @@ static void do_abort(struct mesh_state *ms)
dlog(ms, "abort", 0);
}
-static void
-handle_reset(struct mesh_state *ms)
+static void handle_reset(struct mesh_state *ms)
{
int tgt;
struct mesh_target *tp;
@@ -1466,13 +1008,13 @@ handle_reset(struct mesh_state *ms)
ms->msgphase = msg_none;
out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
out_8(&mr->sequence, SEQ_FLUSHFIFO);
+ mesh_flush_io(mr);
udelay(1);
out_8(&mr->sync_params, ASYNC_PARAMS);
out_8(&mr->sequence, SEQ_ENBRESEL);
}
-static irqreturn_t
-do_mesh_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
+static irqreturn_t do_mesh_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
{
unsigned long flags;
struct Scsi_Host *dev = ((struct mesh_state *)dev_id)->host;
@@ -1497,7 +1039,7 @@ static void handle_error(struct mesh_state *ms)
/* SCSI bus was reset */
printk(KERN_INFO "mesh: SCSI bus reset detected: "
"waiting for end...");
- while ((mr->bus_status1 & BS1_RST) != 0)
+ while ((in_8(&mr->bus_status1) & BS1_RST) != 0)
udelay(1);
printk("done\n");
handle_reset(ms);
@@ -1567,7 +1109,7 @@ static void handle_error(struct mesh_state *ms)
}
mesh_dump_regs(ms);
dumplog(ms, ms->conn_tgt);
- if (ms->phase > selecting && (mr->bus_status1 & BS1_BSY)) {
+ if (ms->phase > selecting && (in_8(&mr->bus_status1) & BS1_BSY)) {
/* try to do what the target wants */
do_abort(ms);
phase_mismatch(ms);
@@ -1609,36 +1151,7 @@ static void handle_exception(struct mesh_state *ms)
}
}
-static void
-mesh_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
-{
- struct mesh_state *ms = (struct mesh_state *) dev_id;
- volatile struct mesh_regs *mr = ms->mesh;
- int intr;
-
-#if 0
- if (ALLOW_DEBUG(ms->conn_tgt))
- printk(KERN_DEBUG "mesh_intr, bs0=%x int=%x exc=%x err=%x "
- "phase=%d msgphase=%d\n", mr->bus_status0,
- mr->interrupt, mr->exception, mr->error,
- ms->phase, ms->msgphase);
-#endif
- while ((intr = in_8(&mr->interrupt)) != 0) {
- dlog(ms, "interrupt intr/err/exc/seq=%.8x",
- MKWORD(intr, mr->error, mr->exception, mr->sequence));
- if (intr & INT_ERROR) {
- handle_error(ms);
- } else if (intr & INT_EXCEPTION) {
- handle_exception(ms);
- } else if (intr & INT_CMDDONE) {
- out_8(&mr->interrupt, INT_CMDDONE);
- cmd_complete(ms);
- }
- }
-}
-
-static void
-handle_msgin(struct mesh_state *ms)
+static void handle_msgin(struct mesh_state *ms)
{
int i, code;
Scsi_Cmnd *cmd = ms->current_req;
@@ -1736,51 +1249,10 @@ handle_msgin(struct mesh_state *ms)
ms->msgphase = msg_out;
}
-static void
-mesh_done(struct mesh_state *ms, int start_next)
-{
- Scsi_Cmnd *cmd;
- struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
-
- cmd = ms->current_req;
- ms->current_req = 0;
- tp->current_req = 0;
- if (cmd) {
- cmd->result = (ms->stat << 16) + cmd->SCp.Status;
- if (ms->stat == DID_OK)
- cmd->result += (cmd->SCp.Message << 8);
- if (DEBUG_TARGET(cmd)) {
- printk(KERN_DEBUG "mesh_done: result = %x, data_ptr=%d, buflen=%d\n",
- cmd->result, ms->data_ptr, cmd->request_bufflen);
- if ((cmd->cmnd[0] == 0 || cmd->cmnd[0] == 0x12 || cmd->cmnd[0] == 3)
- && cmd->request_buffer != 0) {
- unsigned char *b = cmd->request_buffer;
- printk(KERN_DEBUG "buffer = %x %x %x %x %x %x %x %x\n",
- b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
- }
- }
- cmd->SCp.this_residual -= ms->data_ptr;
- mesh_completed(ms, cmd);
- }
- if (start_next) {
- out_8(&ms->mesh->sequence, SEQ_ENBRESEL);
- udelay(1);
- ms->phase = idle;
- mesh_start(ms);
- }
-}
-
-static void
-mesh_completed(struct mesh_state *ms, Scsi_Cmnd *cmd)
-{
- (*cmd->scsi_done)(cmd);
-}
-
/*
* Set up DMA commands for transferring data.
*/
-static void
-set_dma_cmds(struct mesh_state *ms, Scsi_Cmnd *cmd)
+static void set_dma_cmds(struct mesh_state *ms, Scsi_Cmnd *cmd)
{
int i, dma_cmd, total, off, dtot;
struct scatterlist *scl;
@@ -1848,8 +1320,7 @@ set_dma_cmds(struct mesh_state *ms, Scsi_Cmnd *cmd)
ms->dma_count = dtot;
}
-static void
-halt_dma(struct mesh_state *ms)
+static void halt_dma(struct mesh_state *ms)
{
volatile struct dbdma_regs *md = ms->dma;
volatile struct mesh_regs *mr = ms->mesh;
@@ -1859,7 +1330,7 @@ halt_dma(struct mesh_state *ms)
if (!ms->tgts[ms->conn_tgt].data_goes_out) {
/* wait a little while until the fifo drains */
t = 50;
- while (t > 0 && mr->fifo_count != 0
+ while (t > 0 && in_8(&mr->fifo_count) != 0
&& (in_le32(&md->status) & ACTIVE) != 0) {
--t;
udelay(1);
@@ -1899,154 +1370,471 @@ halt_dma(struct mesh_state *ms)
ms->dma_started = 0;
}
-/*
- * Work out whether we expect data to go out from the host adaptor or into it.
- */
-static int
-data_goes_out(Scsi_Cmnd *cmd)
+static void phase_mismatch(struct mesh_state *ms)
{
- switch (cmd->sc_data_direction) {
- case SCSI_DATA_WRITE:
- return 1;
- case SCSI_DATA_READ:
- return 0;
+ volatile struct mesh_regs *mr = ms->mesh;
+ int phase;
+
+ dlog(ms, "phasemm ch/cl/seq/fc=%.8x",
+ MKWORD(mr->count_hi, mr->count_lo, mr->sequence, mr->fifo_count));
+ phase = in_8(&mr->bus_status0) & BS0_PHASE;
+ if (ms->msgphase == msg_out_xxx && phase == BP_MSGOUT) {
+ /* output the last byte of the message, without ATN */
+ out_8(&mr->count_lo, 1);
+ out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg);
+ mesh_flush_io(mr);
+ udelay(1);
+ out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);
+ ms->msgphase = msg_out_last;
+ return;
+ }
+
+ if (ms->msgphase == msg_in) {
+ get_msgin(ms);
+ if (ms->n_msgin)
+ handle_msgin(ms);
}
- /* for SCSI_DATA_UNKNOWN or SCSI_DATA_NONE, fall back on the
- old method for now... */
- switch (cmd->cmnd[0]) {
- case CHANGE_DEFINITION:
- case COMPARE:
- case COPY:
- case COPY_VERIFY:
- case FORMAT_UNIT:
- case LOG_SELECT:
- case MEDIUM_SCAN:
- case MODE_SELECT:
- case MODE_SELECT_10:
- case REASSIGN_BLOCKS:
- case RESERVE:
- case SEARCH_EQUAL:
- case SEARCH_EQUAL_12:
- case SEARCH_HIGH:
- case SEARCH_HIGH_12:
- case SEARCH_LOW:
- case SEARCH_LOW_12:
- case SEND_DIAGNOSTIC:
- case SEND_VOLUME_TAG:
- case SET_WINDOW:
- case UPDATE_BLOCK:
- case WRITE_BUFFER:
- case WRITE_6:
- case WRITE_10:
- case WRITE_12:
- case WRITE_LONG:
- case WRITE_LONG_2: /* alternate code for WRITE_LONG */
- case WRITE_SAME:
- case WRITE_VERIFY:
- case WRITE_VERIFY_12:
- return 1;
+ if (ms->dma_started)
+ halt_dma(ms);
+ if (mr->fifo_count) {
+ out_8(&mr->sequence, SEQ_FLUSHFIFO);
+ mesh_flush_io(mr);
+ udelay(1);
+ }
+
+ ms->msgphase = msg_none;
+ switch (phase) {
+ case BP_DATAIN:
+ ms->tgts[ms->conn_tgt].data_goes_out = 0;
+ ms->phase = dataing;
+ break;
+ case BP_DATAOUT:
+ ms->tgts[ms->conn_tgt].data_goes_out = 1;
+ ms->phase = dataing;
+ break;
+ case BP_COMMAND:
+ ms->phase = commanding;
+ break;
+ case BP_STATUS:
+ ms->phase = statusing;
+ break;
+ case BP_MSGIN:
+ ms->msgphase = msg_in;
+ ms->n_msgin = 0;
+ break;
+ case BP_MSGOUT:
+ ms->msgphase = msg_out;
+ if (ms->n_msgout == 0) {
+ if (ms->aborting) {
+ do_abort(ms);
+ } else {
+ if (ms->last_n_msgout == 0) {
+ printk(KERN_DEBUG
+ "mesh: no msg to repeat\n");
+ ms->msgout[0] = NOP;
+ ms->last_n_msgout = 1;
+ }
+ ms->n_msgout = ms->last_n_msgout;
+ }
+ }
+ break;
default:
- return 0;
+ printk(KERN_DEBUG "mesh: unknown scsi phase %x\n", phase);
+ ms->stat = DID_ERROR;
+ mesh_done(ms, 1);
+ return;
}
+
+ start_phase(ms);
}
-#ifdef MESH_DBG
-static inline u32 readtb(void)
+static void cmd_complete(struct mesh_state *ms)
{
- u32 tb;
+ volatile struct mesh_regs *mr = ms->mesh;
+ Scsi_Cmnd *cmd = ms->current_req;
+ struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
+ int seq, n, t;
-#ifdef DBG_USE_TB
- /* Beware: if you enable this, it will crash on 601s. */
- asm ("mftb %0" : "=r" (tb) : );
-#else
- tb = 0;
-#endif
- return tb;
+ dlog(ms, "cmd_complete fc=%x", mr->fifo_count);
+ seq = use_active_neg + (ms->n_msgout? SEQ_ATN: 0);
+ switch (ms->msgphase) {
+ case msg_out_xxx:
+ /* huh? we expected a phase mismatch */
+ ms->n_msgin = 0;
+ ms->msgphase = msg_in;
+ /* fall through */
+
+ case msg_in:
+ /* should have some message bytes in fifo */
+ get_msgin(ms);
+ n = msgin_length(ms);
+ if (ms->n_msgin < n) {
+ out_8(&mr->count_lo, n - ms->n_msgin);
+ out_8(&mr->sequence, SEQ_MSGIN + seq);
+ } else {
+ ms->msgphase = msg_none;
+ handle_msgin(ms);
+ start_phase(ms);
+ }
+ break;
+
+ case msg_in_bad:
+ out_8(&mr->sequence, SEQ_FLUSHFIFO);
+ mesh_flush_io(mr);
+ udelay(1);
+ out_8(&mr->count_lo, 1);
+ out_8(&mr->sequence, SEQ_MSGIN + SEQ_ATN + use_active_neg);
+ break;
+
+ case msg_out:
+ /*
+ * To get the right timing on ATN wrt ACK, we have
+ * to get the MESH to drop ACK, wait until REQ gets
+ * asserted, then drop ATN. To do this we first
+ * issue a SEQ_MSGOUT with ATN and wait for REQ,
+ * then change the command to a SEQ_MSGOUT w/o ATN.
+ * If we don't see REQ in a reasonable time, we
+ * change the command to SEQ_MSGIN with ATN,
+ * wait for the phase mismatch interrupt, then
+ * issue the SEQ_MSGOUT without ATN.
+ */
+ out_8(&mr->count_lo, 1);
+ out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg + SEQ_ATN);
+ t = 30; /* wait up to 30us */
+ while ((in_8(&mr->bus_status0) & BS0_REQ) == 0 && --t >= 0)
+ udelay(1);
+ dlog(ms, "last_mbyte err/exc/fc/cl=%.8x",
+ MKWORD(mr->error, mr->exception,
+ mr->fifo_count, mr->count_lo));
+ if (in_8(&mr->interrupt) & (INT_ERROR | INT_EXCEPTION)) {
+ /* whoops, target didn't do what we expected */
+ ms->last_n_msgout = ms->n_msgout;
+ ms->n_msgout = 0;
+ if (in_8(&mr->interrupt) & INT_ERROR) {
+ printk(KERN_ERR "mesh: error %x in msg_out\n",
+ in_8(&mr->error));
+ handle_error(ms);
+ return;
+ }
+ if (in_8(&mr->exception) != EXC_PHASEMM)
+ printk(KERN_ERR "mesh: exc %x in msg_out\n",
+ in_8(&mr->exception));
+ else
+ printk(KERN_DEBUG "mesh: bs0=%x in msg_out\n",
+ in_8(&mr->bus_status0));
+ handle_exception(ms);
+ return;
+ }
+ if (in_8(&mr->bus_status0) & BS0_REQ) {
+ out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg);
+ mesh_flush_io(mr);
+ udelay(1);
+ out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);
+ ms->msgphase = msg_out_last;
+ } else {
+ out_8(&mr->sequence, SEQ_MSGIN + use_active_neg + SEQ_ATN);
+ ms->msgphase = msg_out_xxx;
+ }
+ break;
+
+ case msg_out_last:
+ ms->last_n_msgout = ms->n_msgout;
+ ms->n_msgout = 0;
+ ms->msgphase = ms->expect_reply? msg_in: msg_none;
+ start_phase(ms);
+ break;
+
+ case msg_none:
+ switch (ms->phase) {
+ case idle:
+ printk(KERN_ERR "mesh: interrupt in idle phase?\n");
+ dumpslog(ms);
+ return;
+ case selecting:
+ dlog(ms, "Selecting phase at command completion",0);
+ ms->msgout[0] = IDENTIFY(ALLOW_RESEL(ms->conn_tgt),
+ (cmd? cmd->device->lun: 0));
+ ms->n_msgout = 1;
+ ms->expect_reply = 0;
+ if (ms->aborting) {
+ ms->msgout[0] = ABORT;
+ ms->n_msgout++;
+ } else if (tp->sdtr_state == do_sdtr) {
+ /* add SDTR message */
+ add_sdtr_msg(ms);
+ ms->expect_reply = 1;
+ tp->sdtr_state = sdtr_sent;
+ }
+ ms->msgphase = msg_out;
+ /*
+ * We need to wait for REQ before dropping ATN.
+ * We wait for at most 30us, then fall back to
+ * a scheme where we issue a SEQ_COMMAND with ATN,
+ * which will give us a phase mismatch interrupt
+ * when REQ does come, and then we send the message.
+ */
+ t = 230; /* wait up to 230us */
+ while ((in_8(&mr->bus_status0) & BS0_REQ) == 0) {
+ if (--t < 0) {
+ dlog(ms, "impatient for req", ms->n_msgout);
+ ms->msgphase = msg_none;
+ break;
+ }
+ udelay(1);
+ }
+ break;
+ case dataing:
+ if (ms->dma_count != 0) {
+ start_phase(ms);
+ return;
+ }
+ /*
+ * We can get a phase mismatch here if the target
+ * changes to the status phase, even though we have
+ * had a command complete interrupt. Then, if we
+ * issue the SEQ_STATUS command, we'll get a sequence
+ * error interrupt. Which isn't so bad except that
+ * occasionally the mesh actually executes the
+ * SEQ_STATUS *as well as* giving us the sequence
+ * error and phase mismatch exception.
+ */
+ out_8(&mr->sequence, 0);
+ out_8(&mr->interrupt,
+ INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ halt_dma(ms);
+ break;
+ case statusing:
+ if (cmd) {
+ cmd->SCp.Status = mr->fifo;
+ if (DEBUG_TARGET(cmd))
+ printk(KERN_DEBUG "mesh: status is %x\n",
+ cmd->SCp.Status);
+ }
+ ms->msgphase = msg_in;
+ break;
+ case busfreeing:
+ mesh_done(ms, 1);
+ return;
+ case disconnecting:
+ ms->current_req = 0;
+ ms->phase = idle;
+ mesh_start(ms);
+ return;
+ default:
+ break;
+ }
+ ++ms->phase;
+ start_phase(ms);
+ break;
+ }
}
-static void dlog(struct mesh_state *ms, char *fmt, int a)
+
+/*
+ * Called by midlayer with host locked to queue a new
+ * request
+ */
+static int mesh_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
{
- struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
- struct dbglog *tlp, *slp;
+ struct mesh_state *ms;
- tlp = &tp->log[tp->log_ix];
- slp = &ms->log[ms->log_ix];
- tlp->fmt = fmt;
- tlp->tb = readtb();
- tlp->phase = (ms->msgphase << 4) + ms->phase;
- tlp->bs0 = ms->mesh->bus_status0;
- tlp->bs1 = ms->mesh->bus_status1;
- tlp->tgt = ms->conn_tgt;
- tlp->d = a;
- *slp = *tlp;
- if (++tp->log_ix >= N_DBG_LOG)
- tp->log_ix = 0;
- if (tp->n_log < N_DBG_LOG)
- ++tp->n_log;
- if (++ms->log_ix >= N_DBG_SLOG)
- ms->log_ix = 0;
- if (ms->n_log < N_DBG_SLOG)
- ++ms->n_log;
+ cmd->scsi_done = done;
+ cmd->host_scribble = NULL;
+
+ ms = (struct mesh_state *) cmd->device->host->hostdata;
+
+ if (ms->request_q == NULL)
+ ms->request_q = cmd;
+ else
+ ms->request_qtail->host_scribble = (void *) cmd;
+ ms->request_qtail = cmd;
+
+ if (ms->phase == idle)
+ mesh_start(ms);
+
+ return 0;
}
-static void dumplog(struct mesh_state *ms, int t)
+/*
+ * Called to handle interrupts, either call by the interrupt
+ * handler (do_mesh_interrupt) or by other functions in
+ * exceptional circumstances
+ */
+static void mesh_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
{
- struct mesh_target *tp = &ms->tgts[t];
- struct dbglog *lp;
- int i;
+ struct mesh_state *ms = (struct mesh_state *) dev_id;
+ volatile struct mesh_regs *mr = ms->mesh;
+ int intr;
- if (tp->n_log == 0)
- return;
- i = tp->log_ix - tp->n_log;
- if (i < 0)
- i += N_DBG_LOG;
- tp->n_log = 0;
- do {
- lp = &tp->log[i];
- printk(KERN_DEBUG "mesh log %d: bs=%.2x%.2x ph=%.2x ",
- t, lp->bs1, lp->bs0, lp->phase);
-#ifdef DBG_USE_TB
- printk("tb=%10u ", lp->tb);
+#if 0
+ if (ALLOW_DEBUG(ms->conn_tgt))
+ printk(KERN_DEBUG "mesh_intr, bs0=%x int=%x exc=%x err=%x "
+ "phase=%d msgphase=%d\n", mr->bus_status0,
+ mr->interrupt, mr->exception, mr->error,
+ ms->phase, ms->msgphase);
#endif
- printk(lp->fmt, lp->d);
- printk("\n");
- if (++i >= N_DBG_LOG)
- i = 0;
- } while (i != tp->log_ix);
+ while ((intr = in_8(&mr->interrupt)) != 0) {
+ dlog(ms, "interrupt intr/err/exc/seq=%.8x",
+ MKWORD(intr, mr->error, mr->exception, mr->sequence));
+ if (intr & INT_ERROR) {
+ handle_error(ms);
+ } else if (intr & INT_EXCEPTION) {
+ handle_exception(ms);
+ } else if (intr & INT_CMDDONE) {
+ out_8(&mr->interrupt, INT_CMDDONE);
+ cmd_complete(ms);
+ }
+ }
}
-static void dumpslog(struct mesh_state *ms)
+/* Todo: here we can at least try to remove the command from the
+ * queue if it isn't connected yet, and for pending command, assert
+ * ATN until the bus gets freed.
+ */
+static int mesh_abort(Scsi_Cmnd *cmd)
{
- struct dbglog *lp;
- int i;
+ struct mesh_state *ms = (struct mesh_state *) cmd->device->host->hostdata;
- if (ms->n_log == 0)
+ printk(KERN_DEBUG "mesh_abort(%p)\n", cmd);
+ mesh_dump_regs(ms);
+ dumplog(ms, cmd->device->id);
+ dumpslog(ms);
+ return SCSI_ABORT_SNOOZE;
+}
+
+/*
+ * Called by the midlayer with the lock held to reset the
+ * SCSI host and bus.
+ * The midlayer will wait for devices to come back, we don't need
+ * to do that ourselves
+ */
+static int mesh_host_reset(Scsi_Cmnd *cmd)
+{
+ struct mesh_state *ms = (struct mesh_state *) cmd->device->host->hostdata;
+ volatile struct mesh_regs *mr = ms->mesh;
+ volatile struct dbdma_regs *md = ms->dma;
+
+ printk(KERN_DEBUG "mesh_host_reset\n");
+
+ /* Reset the controller & dbdma channel */
+ out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* stop dma */
+ out_8(&mr->exception, 0xff); /* clear all exception bits */
+ out_8(&mr->error, 0xff); /* clear all error bits */
+ out_8(&mr->sequence, SEQ_RESETMESH);
+ mesh_flush_io(mr);
+ udelay(1);
+ out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ out_8(&mr->source_id, ms->host->this_id);
+ out_8(&mr->sel_timeout, 25); /* 250ms */
+ out_8(&mr->sync_params, ASYNC_PARAMS);
+
+ /* Reset the bus */
+ out_8(&mr->bus_status1, BS1_RST); /* assert RST */
+ mesh_flush_io(mr);
+ udelay(30); /* leave it on for >= 25us */
+ out_8(&mr->bus_status1, 0); /* negate RST */
+
+ /* Complete pending commands */
+ handle_reset(ms);
+
+ return SUCCESS;
+}
+
+static void set_mesh_power(struct mesh_state *ms, int state)
+{
+ if (_machine != _MACH_Pmac)
return;
- i = ms->log_ix - ms->n_log;
- if (i < 0)
- i += N_DBG_SLOG;
- ms->n_log = 0;
- do {
- lp = &ms->log[i];
- printk(KERN_DEBUG "mesh log: bs=%.2x%.2x ph=%.2x t%d ",
- lp->bs1, lp->bs0, lp->phase, lp->tgt);
-#ifdef DBG_USE_TB
- printk("tb=%10u ", lp->tb);
-#endif
- printk(lp->fmt, lp->d);
- printk("\n");
- if (++i >= N_DBG_SLOG)
- i = 0;
- } while (i != ms->log_ix);
+ if (state) {
+ pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 1);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/5);
+ } else {
+ pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 0);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/100);
+ }
+}
+
+
+#ifdef CONFIG_PM
+static int mesh_suspend(struct macio_dev *mdev, u32 state)
+{
+ struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
+ unsigned long flags;
+
+ if (state == mdev->ofdev.dev.power_state || state < 2)
+ return 0;
+
+ scsi_block_requests(ms->host);
+ spin_lock_irqsave(ms->host->host_lock, flags);
+ while(ms->phase != idle) {
+ spin_unlock_irqrestore(ms->host->host_lock, flags);
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(HZ/100);
+ spin_lock_irqsave(ms->host->host_lock, flags);
+ }
+ ms->phase = sleeping;
+ spin_unlock_irqrestore(ms->host->host_lock, flags);
+ disable_irq(ms->meshintr);
+ set_mesh_power(ms, 0);
+
+ mdev->ofdev.dev.power_state = state;
+
+ return 0;
+}
+
+static int mesh_resume(struct macio_dev *mdev)
+{
+ struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
+ unsigned long flags;
+
+ if (mdev->ofdev.dev.power_state == 0)
+ return 0;
+
+ set_mesh_power(ms, 1);
+ mesh_init(ms);
+ spin_lock_irqsave(ms->host->host_lock, flags);
+ mesh_start(ms);
+ spin_unlock_irqrestore(ms->host->host_lock, flags);
+ enable_irq(ms->meshintr);
+ scsi_unblock_requests(ms->host);
+
+ mdev->ofdev.dev.power_state = 0;
+
+ return 0;
}
-#endif /* MESH_DBG */
-static Scsi_Host_Template driver_template = {
+#endif /* CONFIG_PM */
+
+/*
+ * If we leave drives set for synchronous transfers (especially
+ * CDROMs), and reboot to MacOS, it gets confused, poor thing.
+ * So, on reboot we reset the SCSI bus.
+ */
+static int mesh_shutdown(struct macio_dev *mdev)
+{
+ struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
+ volatile struct mesh_regs *mr;
+ unsigned long flags;
+
+ printk(KERN_INFO "resetting MESH scsi bus(es)\n");
+ spin_lock_irqsave(ms->host->host_lock, flags);
+ mr = ms->mesh;
+ out_8(&mr->intr_mask, 0);
+ out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ out_8(&mr->bus_status1, BS1_RST);
+ mesh_flush_io(mr);
+ udelay(30);
+ out_8(&mr->bus_status1, 0);
+ spin_unlock_irqrestore(ms->host->host_lock, flags);
+
+ return 0;
+}
+
+static Scsi_Host_Template mesh_template = {
.proc_name = "mesh",
.name = "MESH",
- .detect = mesh_detect,
- .release = mesh_release,
.queuecommand = mesh_queue,
.eh_abort_handler = mesh_abort,
.eh_host_reset_handler = mesh_host_reset,
@@ -2057,4 +1845,222 @@ static Scsi_Host_Template driver_template = {
.use_clustering = DISABLE_CLUSTERING,
};
-#include "scsi_module.c"
+static int mesh_probe(struct macio_dev *mdev, const struct of_match *match)
+{
+ struct device_node *mesh = macio_get_of_node(mdev);
+ struct pci_dev* pdev = macio_get_pci_dev(mdev);
+ int tgt, *cfp, minper;
+ struct mesh_state *ms;
+ struct Scsi_Host *mesh_host;
+ void *dma_cmd_space;
+ dma_addr_t dma_cmd_bus;
+
+ switch (mdev->bus->chip->type) {
+ case macio_heathrow:
+ case macio_gatwick:
+ case macio_paddington:
+ use_active_neg = 0;
+ break;
+ default:
+ use_active_neg = SEQ_ACTIVE_NEG;
+ }
+
+ if (macio_resource_count(mdev) != 2 || macio_irq_count(mdev) != 2) {
+ printk(KERN_ERR "mesh: expected 2 addrs and 2 intrs"
+ " (got %d,%d)\n", mesh->n_addrs, mesh->n_intrs);
+ return -ENODEV;
+ }
+
+ if (macio_request_resources(mdev, "mesh") != 0) {
+ printk(KERN_ERR "mesh: unable to request memory resources");
+ return -EBUSY;
+ }
+ mesh_host = scsi_host_alloc(&mesh_template, sizeof(struct mesh_state));
+ if (mesh_host == NULL) {
+ printk(KERN_ERR "mesh: couldn't register host");
+ goto out_release;
+ }
+
+ /* Old junk for root discovery, that will die ultimately */
+#if !defined(MODULE)
+ note_scsi_host(mesh, mesh_host);
+#endif
+
+ mesh_host->base = macio_resource_start(mdev, 0);
+ mesh_host->irq = macio_irq(mdev, 0);
+ ms = (struct mesh_state *) mesh_host->hostdata;
+ macio_set_drvdata(mdev, ms);
+ ms->host = mesh_host;
+ ms->mdev = mdev;
+ ms->pdev = pdev;
+
+ ms->mesh = (volatile struct mesh_regs *)
+ ioremap(macio_resource_start(mdev, 0), 0x1000);
+ if (ms->mesh == NULL) {
+ printk(KERN_ERR "mesh: can't map registers\n");
+ goto out_free;
+ }
+ ms->dma = (volatile struct dbdma_regs *)
+ ioremap(macio_resource_start(mdev, 1), 0x1000);
+ if (ms->dma == NULL) {
+ printk(KERN_ERR "mesh: can't map registers\n");
+ iounmap((void *)ms->mesh);
+ goto out_free;
+ }
+
+ ms->meshintr = macio_irq(mdev, 0);
+ ms->dmaintr = macio_irq(mdev, 1);
+
+ /* Space for dma command list: +1 for stop command,
+ * +1 to allow for aligning.
+ */
+ ms->dma_cmd_size = (mesh_host->sg_tablesize + 2) * sizeof(struct dbdma_cmd);
+
+ /* We use the PCI APIs for now until the generic one gets fixed
+ * enough or until we get some macio-specific versions
+ */
+ dma_cmd_space = pci_alloc_consistent(macio_get_pci_dev(mdev),
+ ms->dma_cmd_size,
+ &dma_cmd_bus);
+ if (dma_cmd_space == NULL) {
+ printk(KERN_ERR "mesh: can't allocate DMA table\n");
+ goto out_unmap;
+ }
+ memset(dma_cmd_space, 0, ms->dma_cmd_size);
+
+ ms->dma_cmds = (struct dbdma_cmd *) DBDMA_ALIGN(dma_cmd_space);
+ ms->dma_cmd_space = dma_cmd_space;
+ ms->dma_cmd_bus = dma_cmd_bus + ((unsigned long)ms->dma_cmds)
+ - (unsigned long)dma_cmd_space;
+ ms->current_req = NULL;
+ for (tgt = 0; tgt < 8; ++tgt) {
+ ms->tgts[tgt].sdtr_state = do_sdtr;
+ ms->tgts[tgt].sync_params = ASYNC_PARAMS;
+ ms->tgts[tgt].current_req = 0;
+ }
+
+ if ((cfp = (int *) get_property(mesh, "clock-frequency", NULL)))
+ ms->clk_freq = *cfp;
+ else {
+ printk(KERN_INFO "mesh: assuming 50MHz clock frequency\n");
+ ms->clk_freq = 50000000;
+ }
+
+ /* The maximum sync rate is clock / 5; increase
+ * mesh_sync_period if necessary.
+ */
+ minper = 1000000000 / (ms->clk_freq / 5); /* ns */
+ if (mesh_sync_period < minper)
+ mesh_sync_period = minper;
+
+ /* Power up the chip */
+ set_mesh_power(ms, 1);
+
+ /* Set it up */
+ mesh_init(ms);
+
+ /* XXX FIXME: error should be fatal */
+ if (request_irq(ms->meshintr, do_mesh_interrupt, 0, "MESH", ms))
+ printk(KERN_ERR "MESH: can't get irq %d\n", ms->meshintr);
+
+ /* XXX FIXME: handle failure */
+ scsi_add_host(mesh_host, &mdev->ofdev.dev);
+ scsi_scan_host(mesh_host);
+
+ return 0;
+
+out_unmap:
+ iounmap((void *)ms->dma);
+ iounmap((void *)ms->mesh);
+out_free:
+ scsi_host_put(mesh_host);
+out_release:
+ macio_release_resources(mdev);
+
+ return -ENODEV;
+}
+
+static int mesh_remove(struct macio_dev *mdev)
+{
+ struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
+ struct Scsi_Host *mesh_host = ms->host;
+
+ scsi_remove_host(mesh_host);
+
+ free_irq(ms->meshintr, ms);
+
+ /* Reset scsi bus */
+ mesh_shutdown(mdev);
+
+ /* Shut down chip & termination */
+ set_mesh_power(ms, 0);
+
+ /* Unmap registers & dma controller */
+ iounmap((void *) ms->mesh);
+ iounmap((void *) ms->dma);
+
+ /* Free DMA commands memory */
+ pci_free_consistent(macio_get_pci_dev(mdev), ms->dma_cmd_size,
+ ms->dma_cmd_space, ms->dma_cmd_bus);
+
+ /* Release memory resources */
+ macio_release_resources(mdev);
+
+ scsi_host_put(mesh_host);
+
+ return 0;
+}
+
+
+static struct of_match mesh_match[] =
+{
+ {
+ .name = "mesh",
+ .type = OF_ANY_MATCH,
+ .compatible = OF_ANY_MATCH
+ },
+ {
+ .name = OF_ANY_MATCH,
+ .type = "scsi",
+ .compatible = "chrp,mesh0"
+ },
+ {},
+};
+
+static struct macio_driver mesh_driver =
+{
+ .name = "mesh",
+ .match_table = mesh_match,
+ .probe = mesh_probe,
+ .remove = mesh_remove,
+ .shutdown = mesh_shutdown,
+#ifdef CONFIG_PM
+ .suspend = mesh_suspend,
+ .resume = mesh_resume,
+#endif
+};
+
+
+static int __init init_mesh(void)
+{
+
+ /* Calculate sync rate from module parameters */
+ if (sync_rate > 10)
+ sync_rate = 10;
+ if (sync_rate > 0) {
+ printk(KERN_INFO "mesh: configured for synchronous %d MB/s\n", sync_rate);
+ mesh_sync_period = 1000 / sync_rate; /* ns */
+ mesh_sync_offset = 15;
+ } else
+ printk(KERN_INFO "mesh: configured for asynchronous\n");
+
+ return macio_register_driver(&mesh_driver);
+}
+
+static void __exit exit_mesh(void)
+{
+ return macio_unregister_driver(&mesh_driver);
+}
+
+module_init(init_mesh);
+module_exit(exit_mesh);
diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c
index a53e837aba21..43c13e34dcd5 100644
--- a/drivers/serial/pmac_zilog.c
+++ b/drivers/serial/pmac_zilog.c
@@ -1120,7 +1120,7 @@ static struct uart_ops pmz_pops = {
* Unlike sunzilog, we don't need to pre-init the spinlock as we don't
* register our console before uart_add_one_port() is called
*/
-static int __init pmz_setup_port(struct uart_pmac_port *up, int early)
+static int __init pmz_setup_port(struct uart_pmac_port *up)
{
struct device_node *np = up->node;
char *conn;
@@ -1133,11 +1133,6 @@ static int __init pmz_setup_port(struct uart_pmac_port *up, int early)
/*
* Request & map chip registers
*/
- if (!early && request_OF_resource(np, 0, NULL) == NULL) {
- printk("pmac_zilog: failed to request resources for %s\n",
- np->full_name);
- return -EBUSY;
- }
up->port.mapbase = np->addrs[0].address;
up->port.membase = ioremap(up->port.mapbase, 0x1000);
@@ -1152,27 +1147,23 @@ static int __init pmz_setup_port(struct uart_pmac_port *up, int early)
up->flags |= PMACZILOG_FLAG_HAS_DMA;
#endif
if (ZS_HAS_DMA(up)) {
- if (!early && request_OF_resource(np, np->n_addrs - 2, " (tx dma)") == NULL) {
- printk(KERN_ERR "pmac_zilog: can't request TX DMA resource !\n");
+ up->tx_dma_regs = (volatile struct dbdma_regs *)
+ ioremap(np->addrs[np->n_addrs - 2].address, 0x1000);
+ if (up->tx_dma_regs == NULL) {
up->flags &= ~PMACZILOG_FLAG_HAS_DMA;
goto no_dma;
}
- if (!early && request_OF_resource(np, np->n_addrs - 1, " (rx dma)") == NULL) {
- release_OF_resource(np, np->n_addrs - 2);
- printk(KERN_ERR "pmac_zilog: can't request RX DMA resource !\n");
+ up->rx_dma_regs = (volatile struct dbdma_regs *)
+ ioremap(np->addrs[np->n_addrs - 1].address, 0x1000);
+ if (up->rx_dma_regs == NULL) {
+ iounmap((void *)up->tx_dma_regs);
up->flags &= ~PMACZILOG_FLAG_HAS_DMA;
goto no_dma;
}
- up->tx_dma_regs = (volatile struct dbdma_regs *)
- ioremap(np->addrs[np->n_addrs - 2].address, 0x1000);
- up->rx_dma_regs = (volatile struct dbdma_regs *)
- ioremap(np->addrs[np->n_addrs - 1].address, 0x1000);
up->tx_dma_irq = np->intrs[1].line;
up->rx_dma_irq = np->intrs[2].line;
}
no_dma:
- if (!early)
- up->flags |= PMACZILOG_FLAG_RSRC_REQUESTED;
/*
* Detect port type
@@ -1258,8 +1249,15 @@ static int pmz_attach(struct macio_dev *mdev, const struct of_match *match)
*/
for (i = 0; i < MAX_ZS_PORTS; i++)
if (pmz_ports[i].node == mdev->ofdev.node) {
- pmz_ports[i].dev = mdev;
- dev_set_drvdata(&mdev->ofdev.dev, &pmz_ports[i]);
+ struct uart_pmac_port *up = &pmz_ports[i];
+
+ up->dev = mdev;
+ dev_set_drvdata(&mdev->ofdev.dev, up);
+ if (macio_request_resources(up->dev, "pmac_zilog"))
+ printk(KERN_WARNING "%s: Failed to request resource, port still active\n",
+ up->node->name);
+ else
+ up->flags |= PMACZILOG_FLAG_RSRC_REQUESTED;
return 0;
}
return -ENODEV;
@@ -1271,13 +1269,17 @@ static int pmz_attach(struct macio_dev *mdev, const struct of_match *match)
*/
static int pmz_detach(struct macio_dev *mdev)
{
- struct uart_pmac_port *port = dev_get_drvdata(&mdev->ofdev.dev);
+ struct uart_pmac_port *up = dev_get_drvdata(&mdev->ofdev.dev);
- if (!port)
+ if (!up)
return -ENODEV;
+ if (up->flags & PMACZILOG_FLAG_RSRC_REQUESTED) {
+ macio_release_resources(up->dev);
+ up->flags &= ~PMACZILOG_FLAG_RSRC_REQUESTED;
+ }
dev_set_drvdata(&mdev->ofdev.dev, NULL);
- port->dev = NULL;
+ up->dev = NULL;
return 0;
}
@@ -1288,7 +1290,7 @@ static int pmz_detach(struct macio_dev *mdev)
* used later to "attach" to the sysfs tree so we get power management
* events
*/
-static int __init pmz_probe(int early)
+static int __init pmz_probe(void)
{
struct device_node *node_p, *node_a, *node_b, *np;
int count = 0;
@@ -1333,9 +1335,9 @@ static int __init pmz_probe(int early)
/*
* Setup the ports for real
*/
- rc = pmz_setup_port(&pmz_ports[count], early);
+ rc = pmz_setup_port(&pmz_ports[count]);
if (rc == 0)
- rc = pmz_setup_port(&pmz_ports[count+1], early);
+ rc = pmz_setup_port(&pmz_ports[count+1]);
if (rc != 0) {
of_node_put(node_a);
of_node_put(node_b);
@@ -1436,43 +1438,10 @@ static struct macio_driver pmz_driver =
// .resume = pmz_resume, *** NYI
};
-static void pmz_fixup_resources(void)
-{
- int i;
- for (i=0; i<pmz_ports_count; i++) {
- struct uart_pmac_port *up = &pmz_ports[i];
-
- if (up->node == NULL)
- continue;
- if (up->flags & PMACZILOG_FLAG_RSRC_REQUESTED)
- continue;
- if (request_OF_resource(up->node, 0, NULL) == NULL)
- printk(KERN_WARNING "%s: Failed to do late IO resource request, port still active\n",
- up->node->name);
- up->flags |= PMACZILOG_FLAG_RSRC_REQUESTED;
- if (!ZS_HAS_DMA(up))
- continue;
- if (request_OF_resource(up->node, up->node->n_addrs - 2, NULL) == NULL)
- printk(KERN_WARNING "%s: Failed to do late DMA resource request, port still active\n",
- up->node->name);
- if (request_OF_resource(up->node, up->node->n_addrs - 1, NULL) == NULL)
- printk(KERN_WARNING "%s: Failed to do late DMA resource request, port still active\n",
- up->node->name);
- }
-
-}
-
static int __init init_pmz(void)
{
printk(KERN_DEBUG "%s\n", version);
- /*
- * If we had serial console, then we didn't request
- * resources yet. We fix that up now
- */
- if (pmz_ports_count > 0)
- pmz_fixup_resources();
-
/*
* First, we need to do a direct OF-based probe pass. We
* do that because we want serial console up before the
@@ -1481,7 +1450,7 @@ static int __init init_pmz(void)
* uart_register_driver()
*/
if (pmz_ports_count == 0)
- pmz_probe(0);
+ pmz_probe();
/*
* Bail early if no port found
@@ -1610,7 +1579,7 @@ static int __init pmz_console_setup(struct console *co, char *options)
static int __init pmz_console_init(void)
{
/* Probe ports */
- pmz_probe(1);
+ pmz_probe();
/* TODO: Autoprobe console based on OF */
/* pmz_console.index = i; */