* [PATCH] Performance Monitor/Oprofile support for e500
From: Andy Fleming @ 2004-11-29 22:16 UTC (permalink / raw)
To: linuxppc-embedded
[-- Attachment #1: Type: text/plain, Size: 88 bytes --]
This patch adds support for using the performance monitors on e500, and
for oprofile.
[-- Attachment #2: op_kernel_patch_11292004 --]
[-- Type: application/octet-stream, Size: 23932 bytes --]
diff -Nru a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile
--- a/arch/ppc/kernel/Makefile 2004-11-29 15:04:37 -06:00
+++ b/arch/ppc/kernel/Makefile 2004-11-29 15:04:37 -06:00
@@ -14,7 +14,7 @@
obj-y := entry.o traps.o irq.o idle.o time.o misc.o \
process.o signal.o ptrace.o align.o \
semaphore.o syscalls.o setup.o \
- cputable.o ppc_htab.o
+ cputable.o ppc_htab.o perfmon.o
obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o
obj-$(CONFIG_POWER4) += cpu_setup_power4.o
obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o
@@ -24,6 +24,7 @@
obj-$(CONFIG_SMP) += smp.o smp-tbsync.o
obj-$(CONFIG_TAU) += temp.o
obj-$(CONFIG_ALTIVEC) += vecemu.o vector.o
+obj-$(CONFIG_FSL_BOOKE) += perfmon_fsl_booke.o
ifndef CONFIG_MATH_EMULATION
obj-$(CONFIG_8xx) += softemu8xx.o
diff -Nru a/arch/ppc/kernel/head_e500.S b/arch/ppc/kernel/head_e500.S
--- a/arch/ppc/kernel/head_e500.S 2004-11-29 15:04:37 -06:00
+++ b/arch/ppc/kernel/head_e500.S 2004-11-29 15:04:37 -06:00
@@ -666,7 +666,8 @@
EXCEPTION(0x2050, SPEFloatingPointRound, UnknownException, EXC_XFER_EE)
/* Performance Monitor */
- EXCEPTION(0x2060, PerformanceMonitor, UnknownException, EXC_XFER_EE)
+ EXCEPTION(0x2060, PerformanceMonitor, PerformanceMonitorException, EXC_XFER_STD)
+
/* Debug Interrupt */
DEBUG_EXCEPTION
diff -Nru a/arch/ppc/kernel/perfmon.c b/arch/ppc/kernel/perfmon.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/arch/ppc/kernel/perfmon.c 2004-11-29 15:04:37 -06:00
@@ -0,0 +1,94 @@
+/* kernel/perfmon.c
+ * PPC 32 Performance Monitor Infrastructure
+ *
+ * Author: Andy Fleming
+ * Copyright (c) 2004 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/prctl.h>
+
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/reg.h>
+#include <asm/xmon.h>
+
+/* A lock to regulate grabbing the interrupt */
+spinlock_t perfmon_lock = SPIN_LOCK_UNLOCKED;
+
+#ifdef CONFIG_FSL_BOOKE
+static void dummy_perf(struct pt_regs *regs)
+{
+ unsigned int pmgc0 = mfpmr(PMRN_PMGC0);
+
+ pmgc0 &= ~PMGC0_PMIE;
+ mtpmr(PMRN_PMGC0, pmgc0);
+}
+
+#else
+/* Ensure exceptions are disabled */
+#define MMCR0_PMXE (1UL << (31 - 5))
+
+static void dummy_perf(struct pt_regs *regs)
+{
+ unsigned int mmcr0 = mfspr(SPRN_MMCR0);
+
+ mmcr0 &= ~MMCR0_PMXE;
+ mtspr(SPRN_MMCR0, mmcr0);
+}
+#endif
+
+void (*perf_irq)(struct pt_regs *) = dummy_perf;
+
+/* Grab the interrupt, if it's free.
+ * Returns 0 on success, -1 if the interrupt is taken already */
+int request_perfmon_irq(void (*handler)(struct pt_regs *))
+{
+ int err = 0;
+
+ spin_lock(&perfmon_lock);
+
+ if (perf_irq == dummy_perf)
+ perf_irq = handler;
+ else {
+ pr_info("perfmon irq already handled by %p\n", perf_irq);
+ err = -1;
+ }
+
+ spin_unlock(&perfmon_lock);
+
+ return err;
+}
+
+void free_perfmon_irq(void)
+{
+ spin_lock(&perfmon_lock);
+
+ perf_irq = dummy_perf;
+
+ spin_unlock(&perfmon_lock);
+}
+
+EXPORT_SYMBOL(perf_irq);
+EXPORT_SYMBOL(request_perfmon_irq);
+EXPORT_SYMBOL(free_perfmon_irq);
diff -Nru a/arch/ppc/kernel/perfmon_fsl_booke.c b/arch/ppc/kernel/perfmon_fsl_booke.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/arch/ppc/kernel/perfmon_fsl_booke.c 2004-11-29 15:04:37 -06:00
@@ -0,0 +1,230 @@
+/* kernel/perfmon_fsl_booke.c
+ * Freescale Book-E Performance Monitor code
+ *
+ * Author: Andy Fleming
+ * Copyright (c) 2004 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/prctl.h>
+
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/reg.h>
+#include <asm/xmon.h>
+
+void init_pmc_stop(int ctr);
+void set_pmc_event(int ctr, int event);
+void set_pmc_user_kernel(int ctr, int user, int kernel);
+void set_pmc_marked(int ctr, int mark0, int mark1);
+void pmc_start_ctr(int ctr, int enable);
+void pmc_start_ctrs(int enable);
+void pmc_stop_ctrs(void);
+void dump_pmcs(void);
+
+static inline u32 get_pmlca(int ctr);
+static inline void set_pmlca(int ctr, u32 pmlca);
+
+static inline u32 get_pmlca(int ctr)
+{
+ u32 pmlca;
+
+ switch (ctr) {
+ case 0:
+ pmlca = mfpmr(PMRN_PMLCA0);
+ break;
+ case 1:
+ pmlca = mfpmr(PMRN_PMLCA1);
+ break;
+ case 2:
+ pmlca = mfpmr(PMRN_PMLCA2);
+ break;
+ case 3:
+ pmlca = mfpmr(PMRN_PMLCA3);
+ break;
+ default:
+ panic("Bad ctr number\n");
+ }
+
+ return pmlca;
+}
+
+static inline void set_pmlca(int ctr, u32 pmlca)
+{
+ switch (ctr) {
+ case 0:
+ mtpmr(PMRN_PMLCA0, pmlca);
+ break;
+ case 1:
+ mtpmr(PMRN_PMLCA1, pmlca);
+ break;
+ case 2:
+ mtpmr(PMRN_PMLCA2, pmlca);
+ break;
+ case 3:
+ mtpmr(PMRN_PMLCA3, pmlca);
+ break;
+ default:
+ panic("Bad ctr number\n");
+ }
+}
+
+void init_pmc_stop(int ctr)
+{
+ u32 pmlca = (PMLCA_FC | PMLCA_FCS | PMLCA_FCU |
+ PMLCA_FCM1 | PMLCA_FCM0);
+ u32 pmlcb = 0;
+
+ switch (ctr) {
+ case 0:
+ mtpmr(PMRN_PMLCA0, pmlca);
+ mtpmr(PMRN_PMLCB0, pmlcb);
+ break;
+ case 1:
+ mtpmr(PMRN_PMLCA1, pmlca);
+ mtpmr(PMRN_PMLCB1, pmlcb);
+ break;
+ case 2:
+ mtpmr(PMRN_PMLCA2, pmlca);
+ mtpmr(PMRN_PMLCB2, pmlcb);
+ break;
+ case 3:
+ mtpmr(PMRN_PMLCA3, pmlca);
+ mtpmr(PMRN_PMLCB3, pmlcb);
+ break;
+ default:
+ panic("Bad ctr number!\n");
+ }
+}
+
+void set_pmc_event(int ctr, int event)
+{
+ u32 pmlca;
+
+ pmlca = get_pmlca(ctr);
+
+ pmlca = (pmlca & ~PMLCA_EVENT_MASK) |
+ ((event << PMLCA_EVENT_SHIFT) &
+ PMLCA_EVENT_MASK);
+
+ set_pmlca(ctr, pmlca);
+}
+
+void set_pmc_user_kernel(int ctr, int user, int kernel)
+{
+ u32 pmlca;
+
+ pmlca = get_pmlca(ctr);
+
+ if(user)
+ pmlca &= ~PMLCA_FCU;
+ else
+ pmlca |= PMLCA_FCU;
+
+ if(kernel)
+ pmlca &= ~PMLCA_FCS;
+ else
+ pmlca |= PMLCA_FCS;
+
+ set_pmlca(ctr, pmlca);
+}
+
+void set_pmc_marked(int ctr, int mark0, int mark1)
+{
+ u32 pmlca = get_pmlca(ctr);
+
+ if(mark0)
+ pmlca &= ~PMLCA_FCM0;
+ else
+ pmlca |= PMLCA_FCM0;
+
+ if(mark1)
+ pmlca &= ~PMLCA_FCM1;
+ else
+ pmlca |= PMLCA_FCM1;
+
+ set_pmlca(ctr, pmlca);
+}
+
+void pmc_start_ctr(int ctr, int enable)
+{
+ u32 pmlca = get_pmlca(ctr);
+
+ pmlca &= ~PMLCA_FC;
+
+ if (enable)
+ pmlca |= PMLCA_CE;
+ else
+ pmlca &= ~PMLCA_CE;
+
+ set_pmlca(ctr, pmlca);
+}
+
+void pmc_start_ctrs(int enable)
+{
+ u32 pmgc0 = mfpmr(PMRN_PMGC0);
+
+ pmgc0 &= ~PMGC0_FAC;
+ pmgc0 |= PMGC0_FCECE;
+
+ if (enable)
+ pmgc0 |= PMGC0_PMIE;
+ else
+ pmgc0 &= ~PMGC0_PMIE;
+
+ mtpmr(PMRN_PMGC0, pmgc0);
+}
+
+void pmc_stop_ctrs(void)
+{
+ u32 pmgc0 = mfpmr(PMRN_PMGC0);
+
+ pmgc0 |= PMGC0_FAC;
+
+ pmgc0 &= ~(PMGC0_PMIE | PMGC0_FCECE);
+
+ mtpmr(PMRN_PMGC0, pmgc0);
+}
+
+void dump_pmcs(void)
+{
+ printk("pmgc0: %x\n", mfpmr(PMRN_PMGC0));
+ printk("pmc\t\tpmlca\t\tpmlcb\n");
+ printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC0),
+ mfpmr(PMRN_PMLCA0), mfpmr(PMRN_PMLCB0));
+ printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC1),
+ mfpmr(PMRN_PMLCA1), mfpmr(PMRN_PMLCB1));
+ printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC2),
+ mfpmr(PMRN_PMLCA2), mfpmr(PMRN_PMLCB2));
+ printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC3),
+ mfpmr(PMRN_PMLCA3), mfpmr(PMRN_PMLCB3));
+}
+
+EXPORT_SYMBOL(init_pmc_stop);
+EXPORT_SYMBOL(set_pmc_event);
+EXPORT_SYMBOL(set_pmc_user_kernel);
+EXPORT_SYMBOL(set_pmc_marked);
+EXPORT_SYMBOL(pmc_start_ctr);
+EXPORT_SYMBOL(pmc_start_ctrs);
+EXPORT_SYMBOL(pmc_stop_ctrs);
+EXPORT_SYMBOL(dump_pmcs);
diff -Nru a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c
--- a/arch/ppc/kernel/traps.c 2004-11-29 15:04:37 -06:00
+++ b/arch/ppc/kernel/traps.c 2004-11-29 15:04:37 -06:00
@@ -71,6 +71,7 @@
* Trap & Exception support
*/
+extern void (*perf_irq)(struct pt_regs *);
spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
@@ -725,6 +726,11 @@
}
}
#endif /* CONFIG_ALTIVEC */
+
+void PerformanceMonitorException(struct pt_regs *regs)
+{
+ perf_irq(regs);
+}
#ifdef CONFIG_FSL_BOOKE
void CacheLockingException(struct pt_regs *regs, unsigned long address,
diff -Nru a/arch/ppc/oprofile/Makefile b/arch/ppc/oprofile/Makefile
--- a/arch/ppc/oprofile/Makefile 2004-11-29 15:04:37 -06:00
+++ b/arch/ppc/oprofile/Makefile 2004-11-29 15:04:37 -06:00
@@ -6,4 +6,4 @@
oprofilefs.o oprofile_stats.o \
timer_int.o )
-oprofile-y := $(DRIVER_OBJS) init.o
+oprofile-y := $(DRIVER_OBJS) common.o op_model_fsl_booke.o
diff -Nru a/arch/ppc/oprofile/common.c b/arch/ppc/oprofile/common.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/arch/ppc/oprofile/common.c 2004-11-29 15:04:37 -06:00
@@ -0,0 +1,163 @@
+/*
+ * PPC 32 oprofile support
+ * Based on PPC64 oprofile support
+ * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * Copyright (C) Freescale Semiconductor, Inc 2004
+ *
+ * Author: Andy Fleming
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/oprofile.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/errno.h>
+#include <asm/ptrace.h>
+#include <asm/system.h>
+#include <asm/perfmon.h>
+#include <asm/cputable.h>
+
+#include "op_impl.h"
+
+extern struct op_ppc32_model op_model_fsl_booke;
+static struct op_ppc32_model *model;
+
+static struct op_counter_config ctr[OP_MAX_COUNTER];
+static struct op_system_config sys;
+
+static void op_handle_interrupt(struct pt_regs *regs)
+{
+ model->handle_interrupt(regs, ctr);
+}
+
+static int op_ppc32_setup(void)
+{
+ /* Install our interrupt handler into the existing hook. */
+ if(request_perfmon_irq(&op_handle_interrupt))
+ return -EBUSY;
+
+ mb();
+
+ /* Pre-compute the values to stuff in the hardware registers. */
+ model->reg_setup(ctr, &sys, model->num_counters);
+
+#if 0
+ /* FIXME: Make multi-cpu work */
+ /* Configure the registers on all cpus. */
+ on_each_cpu(model->reg_setup, NULL, 0, 1);
+#endif
+
+ return 0;
+}
+
+static void op_ppc32_shutdown(void)
+{
+ mb();
+
+ /* Remove our interrupt handler. We may be removing this module. */
+ free_perfmon_irq();
+}
+
+static void op_ppc32_cpu_start(void *dummy)
+{
+ model->start(ctr);
+}
+
+static int op_ppc32_start(void)
+{
+ on_each_cpu(op_ppc32_cpu_start, NULL, 0, 1);
+ return 0;
+}
+
+static inline void op_ppc32_cpu_stop(void *dummy)
+{
+ model->stop();
+}
+
+static void op_ppc32_stop(void)
+{
+ on_each_cpu(op_ppc32_cpu_stop, NULL, 0, 1);
+}
+
+static int op_ppc32_create_files(struct super_block *sb, struct dentry *root)
+{
+ int i;
+
+ for (i = 0; i < model->num_counters; ++i) {
+ struct dentry *dir;
+ char buf[3];
+
+ snprintf(buf, sizeof buf, "%d", i);
+ dir = oprofilefs_mkdir(sb, root, buf);
+
+ oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
+ oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
+ oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
+ oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
+ oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
+
+ /* FIXME: Not sure if this is used */
+ oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
+ }
+
+ oprofilefs_create_ulong(sb, root, "enable_kernel", &sys.enable_kernel);
+ oprofilefs_create_ulong(sb, root, "enable_user", &sys.enable_user);
+
+ /* Default to tracing both kernel and user */
+ sys.enable_kernel = 1;
+ sys.enable_user = 1;
+
+ return 0;
+}
+
+static struct oprofile_operations oprof_ppc32_ops = {
+ .create_files = op_ppc32_create_files,
+ .setup = op_ppc32_setup,
+ .shutdown = op_ppc32_shutdown,
+ .start = op_ppc32_start,
+ .stop = op_ppc32_stop,
+ .cpu_type = NULL /* To be filled in below. */
+};
+
+int __init oprofile_arch_init(struct oprofile_operations **ops)
+{
+ char *name;
+ int cpu_id = smp_processor_id();
+
+#ifdef CONFIG_FSL_BOOKE
+ model = &op_model_fsl_booke;
+#else
+ printk(KERN_ERR "oprofile enabled on unsupported processor!\n");
+ return -ENODEV;
+#endif
+
+ name = kmalloc(32, GFP_KERNEL);
+
+ if (NULL == name)
+ return -ENOMEM;
+
+ sprintf(name, "ppc/%s", cur_cpu_spec[cpu_id]->cpu_name);
+
+ oprof_ppc32_ops.cpu_type = name;
+
+ model->num_counters = cur_cpu_spec[cpu_id]->num_pmcs;
+
+ *ops = &oprof_ppc32_ops;
+
+ printk(KERN_INFO "oprofile: using %s performance monitoring.\n",
+ oprof_ppc32_ops.cpu_type);
+
+ return 0;
+}
+
+void oprofile_arch_exit(void)
+{
+ kfree(oprof_ppc32_ops.cpu_type);
+ oprof_ppc32_ops.cpu_type = NULL;
+}
diff -Nru a/arch/ppc/oprofile/init.c b/arch/ppc/oprofile/init.c
--- a/arch/ppc/oprofile/init.c 2004-11-29 15:04:37 -06:00
+++ /dev/null Wed Dec 31 16:00:00 196900
@@ -1,23 +0,0 @@
-/**
- * @file init.c
- *
- * @remark Copyright 2002 OProfile authors
- * @remark Read the file COPYING
- *
- * @author John Levon <levon@movementarian.org>
- */
-
-#include <linux/kernel.h>
-#include <linux/oprofile.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-
-int __init oprofile_arch_init(struct oprofile_operations ** ops)
-{
- return -ENODEV;
-}
-
-
-void oprofile_arch_exit(void)
-{
-}
diff -Nru a/arch/ppc/oprofile/op_impl.h b/arch/ppc/oprofile/op_impl.h
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/arch/ppc/oprofile/op_impl.h 2004-11-29 15:04:37 -06:00
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * Based on alpha version.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef OP_IMPL_H
+#define OP_IMPL_H 1
+
+#define OP_MAX_COUNTER 8
+
+/* Per-counter configuration as set via oprofilefs. */
+struct op_counter_config {
+ unsigned long enabled;
+ unsigned long event;
+ unsigned long count;
+ unsigned long kernel;
+ unsigned long user;
+ unsigned long unit_mask;
+};
+
+/* System-wide configuration as set via oprofilefs. */
+struct op_system_config {
+ unsigned long enable_kernel;
+ unsigned long enable_user;
+};
+
+/* Per-arch configuration */
+struct op_ppc32_model {
+ void (*reg_setup) (struct op_counter_config *,
+ struct op_system_config *,
+ int num_counters);
+ void (*start) (struct op_counter_config *);
+ void (*stop) (void);
+ void (*handle_interrupt) (struct pt_regs *,
+ struct op_counter_config *);
+ int num_counters;
+};
+
+#endif /* OP_IMPL_H */
diff -Nru a/arch/ppc/oprofile/op_model_fsl_booke.c b/arch/ppc/oprofile/op_model_fsl_booke.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/arch/ppc/oprofile/op_model_fsl_booke.c 2004-11-29 15:04:37 -06:00
@@ -0,0 +1,185 @@
+/*
+ * oprofile/op_model_e500.c
+ *
+ * Freescale Book-E oprofile support, based on ppc64 oprofile support
+ * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala <Kumar.Gala@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/oprofile.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <asm/ptrace.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/cputable.h>
+#include <asm/reg_booke.h>
+#include <asm/page.h>
+#include <asm/perfmon.h>
+
+#include "op_impl.h"
+
+static unsigned long reset_value[OP_MAX_COUNTER];
+
+static int num_counters;
+static int oprofile_running;
+
+static inline unsigned int ctr_read(unsigned int i)
+{
+ switch(i) {
+ case 0:
+ return mfpmr(PMRN_PMC0);
+ case 1:
+ return mfpmr(PMRN_PMC1);
+ case 2:
+ return mfpmr(PMRN_PMC2);
+ case 3:
+ return mfpmr(PMRN_PMC3);
+ default:
+ return 0;
+ }
+}
+
+static inline void ctr_write(unsigned int i, unsigned int val)
+{
+ switch(i) {
+ case 0:
+ mtpmr(PMRN_PMC0, val);
+ break;
+ case 1:
+ mtpmr(PMRN_PMC1, val);
+ break;
+ case 2:
+ mtpmr(PMRN_PMC2, val);
+ break;
+ case 3:
+ mtpmr(PMRN_PMC3, val);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void fsl_booke_reg_setup(struct op_counter_config *ctr,
+ struct op_system_config *sys,
+ int num_ctrs)
+{
+ int i;
+
+ num_counters = num_ctrs;
+
+ /* freeze all counters */
+ pmc_stop_ctrs();
+
+ /* Our counters count up, and "count" refers to
+ * how much before the next interrupt, and we interrupt
+ * on overflow. So we calculate the starting value
+ * which will give us "count" until overflow.
+ * Then we set the events on the enabled counters */
+ for (i = 0; i < num_counters; ++i) {
+ reset_value[i] = 0x80000000UL - ctr[i].count;
+
+ init_pmc_stop(i);
+
+ set_pmc_event(i, ctr[i].event);
+
+ set_pmc_user_kernel(i, ctr[i].user, ctr[i].kernel);
+ }
+}
+
+static void fsl_booke_start(struct op_counter_config *ctr)
+{
+ int i;
+
+ mtmsr(mfmsr() | MSR_PMM);
+
+ for (i = 0; i < num_counters; ++i) {
+ if (ctr[i].enabled) {
+ ctr_write(i, reset_value[i]);
+ /* Set Each enabled counterd to only
+ * count when the Mark bit is not set */
+ set_pmc_marked(i, 1, 0);
+ pmc_start_ctr(i, 1);
+ } else {
+ ctr_write(i, 0);
+
+ /* Set the ctr to be stopped */
+ pmc_start_ctr(i, 0);
+ }
+ }
+
+ /* Clear the freeze bit, and enable the interrupt.
+ * The counters won't actually start until the rfi clears
+ * the PMM bit */
+ pmc_start_ctrs(1);
+
+ oprofile_running = 1;
+
+ pr_debug("start on cpu %d, pmgc0 %x\n", smp_processor_id(),
+ mfpmr(PMRN_PMGC0));
+}
+
+static void fsl_booke_stop(void)
+{
+ /* freeze counters */
+ pmc_stop_ctrs();
+
+ oprofile_running = 0;
+
+ pr_debug("stop on cpu %d, pmgc0 %x\n", smp_processor_id(),
+ mfpmr(PMRN_PMGC0));
+
+ mb();
+}
+
+
+static void fsl_booke_handle_interrupt(struct pt_regs *regs,
+ struct op_counter_config *ctr)
+{
+ unsigned long pc;
+ int is_kernel;
+ int val;
+ int i;
+ unsigned int cpu = smp_processor_id();
+
+ /* set the PMM bit (see comment below) */
+ mtmsr(mfmsr() | MSR_PMM);
+
+ pc = regs->nip;
+ is_kernel = (pc >= KERNELBASE);
+
+ for (i = 0; i < num_counters; ++i) {
+ val = ctr_read(i);
+ if (val < 0) {
+ if (oprofile_running && ctr[i].enabled) {
+ oprofile_add_sample(pc, is_kernel, i, cpu);
+ ctr_write(i, reset_value[i]);
+ } else {
+ ctr_write(i, 0);
+ }
+ }
+ }
+
+ /* The freeze bit was set by the interrupt. */
+ /* Clear the freeze bit, and reenable the interrupt.
+ * The counters won't actually start until the rfi clears
+ * the PMM bit */
+ pmc_start_ctrs(1);
+}
+
+struct op_ppc32_model op_model_fsl_booke = {
+ .reg_setup = fsl_booke_reg_setup,
+ .start = fsl_booke_start,
+ .stop = fsl_booke_stop,
+ .handle_interrupt = fsl_booke_handle_interrupt,
+};
diff -Nru a/include/asm-ppc/cputable.h b/include/asm-ppc/cputable.h
--- a/include/asm-ppc/cputable.h 2004-11-29 15:04:37 -06:00
+++ b/include/asm-ppc/cputable.h 2004-11-29 15:04:37 -06:00
@@ -71,7 +71,6 @@
#define CPU_FTR_TAU 0x00000010
#define CPU_FTR_CAN_DOZE 0x00000020
#define CPU_FTR_USE_TB 0x00000040
-#define CPU_FTR_604_PERF_MON 0x00000080
#define CPU_FTR_601 0x00000100
#define CPU_FTR_HPTE_TABLE 0x00000200
#define CPU_FTR_CAN_NAP 0x00000400
diff -Nru a/include/asm-ppc/perfmon.h b/include/asm-ppc/perfmon.h
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/include/asm-ppc/perfmon.h 2004-11-29 15:04:37 -06:00
@@ -0,0 +1,18 @@
+#ifndef __PERFMON_H
+#define __PERFMON_H
+
+int request_perfmon_irq(void (*handler)(struct pt_regs *));
+void free_perfmon_irq(void);
+
+#ifdef CONFIG_FSL_BOOKE
+void init_pmc_stop(int ctr);
+void set_pmc_event(int ctr, int event);
+void set_pmc_user_kernel(int ctr, int user, int kernel);
+void set_pmc_marked(int ctr, int mark0, int mark1);
+void pmc_start_ctr(int ctr, int enable);
+void pmc_start_ctrs(int enable);
+void pmc_stop_ctrs(void);
+void dump_pmcs(void);
+#endif
+
+#endif /* __PERFMON_H */
diff -Nru a/include/asm-ppc/reg_booke.h b/include/asm-ppc/reg_booke.h
--- a/include/asm-ppc/reg_booke.h 2004-11-29 15:04:37 -06:00
+++ b/include/asm-ppc/reg_booke.h 2004-11-29 15:04:37 -06:00
@@ -51,6 +51,59 @@
#define mtpmr(rn, v) asm volatile("mtpmr " __stringify(rn) ",%0" : : "r" (v))
#endif /* __ASSEMBLY__ */
+/* Freescale Book E Performance Monitor APU Registers */
+#define PMRN_PMC0 0x010 /* Performance Monitor Counter 0 */
+#define PMRN_PMC1 0x011 /* Performance Monitor Counter 1 */
+#define PMRN_PMC2 0x012 /* Performance Monitor Counter 1 */
+#define PMRN_PMC3 0x013 /* Performance Monitor Counter 1 */
+#define PMRN_PMLCA0 0x090 /* PM Local Control A0 */
+#define PMRN_PMLCA1 0x091 /* PM Local Control A1 */
+#define PMRN_PMLCA2 0x092 /* PM Local Control A2 */
+#define PMRN_PMLCA3 0x093 /* PM Local Control A3 */
+
+#define PMLCA_FC 0x80000000 /* Freeze Counter */
+#define PMLCA_FCS 0x40000000 /* Freeze in Supervisor */
+#define PMLCA_FCU 0x20000000 /* Freeze in User */
+#define PMLCA_FCM1 0x10000000 /* Freeze when PMM==1 */
+#define PMLCA_FCM0 0x08000000 /* Freeze when PMM==0 */
+#define PMLCA_CE 0x04000000 /* Condition Enable */
+
+#define PMLCA_EVENT_MASK 0x007f0000 /* Event field */
+#define PMLCA_EVENT_SHIFT 16
+
+#define PMRN_PMLCB0 0x110 /* PM Local Control B0 */
+#define PMRN_PMLCB1 0x111 /* PM Local Control B1 */
+#define PMRN_PMLCB2 0x112 /* PM Local Control B2 */
+#define PMRN_PMLCB3 0x113 /* PM Local Control B3 */
+
+#define PMLCB_THRESHMUL_MASK 0x0700 /* Threshhold Multiple Field */
+#define PMLCB_THRESHMUL_SHIFT 8
+
+#define PMLCB_THRESHOLD_MASK 0x003f /* Threshold Field */
+#define PMLCB_THRESHOLD_SHIFT 0
+
+#define PMRN_PMGC0 0x190 /* PM Global Control 0 */
+
+#define PMGC0_FAC 0x80000000 /* Freeze all Counters */
+#define PMGC0_PMIE 0x40000000 /* Interrupt Enable */
+#define PMGC0_FCECE 0x20000000 /* Freeze countes on
+ Enabled Condition or
+ Event */
+
+#define PMRN_UPMC0 0x000 /* User Performance Monitor Counter 0 */
+#define PMRN_UPMC1 0x001 /* User Performance Monitor Counter 1 */
+#define PMRN_UPMC2 0x002 /* User Performance Monitor Counter 1 */
+#define PMRN_UPMC3 0x003 /* User Performance Monitor Counter 1 */
+#define PMRN_UPMLCA0 0x080 /* User PM Local Control A0 */
+#define PMRN_UPMLCA1 0x081 /* User PM Local Control A1 */
+#define PMRN_UPMLCA2 0x082 /* User PM Local Control A2 */
+#define PMRN_UPMLCA3 0x083 /* User PM Local Control A3 */
+#define PMRN_UPMLCB0 0x100 /* User PM Local Control B0 */
+#define PMRN_UPMLCB1 0x101 /* User PM Local Control B1 */
+#define PMRN_UPMLCB2 0x102 /* User PM Local Control B2 */
+#define PMRN_UPMLCB3 0x103 /* User PM Local Control B3 */
+#define PMRN_UPMGC0 0x180 /* User PM Global Control 0 */
+
/* Machine State Register (MSR) Fields */
#define MSR_UCLE (1<<26) /* User-mode cache lock enable */
^ permalink raw reply
* Re: G3 iBook LCD brightness in X under kernel 2.4.25 and 2.6.8
From: Benjamin Herrenschmidt @ 2004-11-29 21:56 UTC (permalink / raw)
To: Frank Murphy; +Cc: debian-x, linuxppc-dev list
In-Reply-To: <1101721355.31317.209633721@webmail.messagingengine.com>
On Mon, 2004-11-29 at 10:42 +0100, Frank Murphy wrote:
> It probably is. But it's strange because I use all the same software
> except the kernel to get this behavior. So there's something with the
> kernel / X interface that has changed, either for 2.6 or the way Debian
> builds 2.6. I have a hard time with this because there are so many
> interactions: is it the PMU? The APM emulation? X driver? Kernel
> driver? I don't have a good idea about how these things interact.
>
> Let me know what you find out.
It has to do with interactions between the X driver and the atyfb driver
I suppose...
Ben.
^ permalink raw reply
* Re: MPC5200 Cache coherency with BestComm issue
From: Sylvain Munaut @ 2004-11-29 18:29 UTC (permalink / raw)
To: roger blofeld; +Cc: linuxppc-embedded
In-Reply-To: <20041129174842.14784.qmail@web53506.mail.yahoo.com>
Hi Roger
>
>Sylvain,
> You are correct. The CPU_FTR_NEED_COHERENT is not required. Perhaps
>the solution is to only turn on powersave_nap if CONFIG_PPC_BESTCOMM is
>not selected.
>
>
Pull the latest tree. I've just commented it out with a note.
I didn't rip it off so that people copying lite5200.c for their own
board know how to activate
it if they need to ...
It can also be controlled from userspace /proc/sys/kernel/powersave-nap
You just need to be aware of the consequences ( like USB not working
either ! ) if you want
to save power.
Sylvain
^ permalink raw reply
* fbmem.c compile problem
From: Frank @ 2004-11-29 17:50 UTC (permalink / raw)
To: linuxppc-embedded
I am trying to get framebuffer support compiled into the kernel
(2.4.28) before I compile and install directfb, but I am having
a problem I can't seem to solve. I am getting the following
error when I try to compile fb support into the kernel:
/drivers/char/mem.c:747: undefined reference to `fbmem_init'
I have what I think i need enabled in my .config file:
#
# Console drivers
#
CONFIG_VGA_CONSOLE=y
#
# Frame-buffer support
#
CONFIG_FB=y
CONFIG_DUMMY_CONSOLE=y
# CONFIG_FB_VOYAGER is not set
# CONFIG_FB_MB86290 is not set
# CONFIG_FB_RIVA is not set
# CONFIG_FB_CLGEN is not set
# CONFIG_FB_PM2 is not set
# CONFIG_FB_PM3 is not set
# CONFIG_FB_CYBER2000 is not set
# CONFIG_FB_CT65550 is not set
# CONFIG_FB_IMSTT is not set
# CONFIG_FB_S3TRIO is not set
CONFIG_FB_VESA=y
# CONFIG_FB_VGA16 is not set
# CONFIG_FB_LYNX is not set
# CONFIG_FB_SM712 is not set
# CONFIG_FB_MATROX is not set
# CONFIG_FB_ATY is not set
CONFIG_FB_RADEON=y
# CONFIG_FB_ATY128 is not set
Can someone shed some light on why, although I have CONFIG_FB
set
to 'y' the makefile won't compile drivers/video/fbmem.c
I am on a very tight schedule and any help would be greatly
appreciated....
__________________________________
Do you Yahoo!?
Yahoo! Mail - You care about security. So do we.
http://promotions.yahoo.com/new_mail
^ permalink raw reply
* MPC5200 Cache coherency with BestComm issue
From: roger blofeld @ 2004-11-29 17:48 UTC (permalink / raw)
To: tnt; +Cc: linuxppc-embedded
>Hi Roger
>
>> By experimenting I have found that the BestComm and FEC work without
>>the cache flush provided CPU_FTR_MAYBE_CAN_NAP is removed from the
>>cputable (nap disables snooping) and that CPU_FTR_NEED_COHERENT is
>>added to the cputable (turns on "M" bit in BAT/PTE so that the XLB
has
>>a chance of seeing a global transaction).
>>
>>
>Thanks for the info.
>
>The nap thing was indeed obvious ... The proper way to deactivate it
>would be in lite5200.c
>I added the powersave_nap = 1; as an example on how to allow it to
nap.
>I'll probably comment it out by default with a note.
>
>For the CPU_FTR_NEED_COHERENT, are you sure it's required ?
>From my understanding of the G2Core manual, the M bit must be set if
>you want the G2Core
>to assert the global signal when it access memory. Here, we don't care
>since only the G2Core is snooping on the bus. So that should not be
required ...
>
>
> Sylvain
Sylvain,
You are correct. The CPU_FTR_NEED_COHERENT is not required. Perhaps
the solution is to only turn on powersave_nap if CONFIG_PPC_BESTCOMM is
not selected.
-rb
__________________________________
Do you Yahoo!?
Meet the all-new My Yahoo! - Try it today!
http://my.yahoo.com
^ permalink raw reply
* Re: G3 iBook LCD brightness in X under kernel 2.4.25 and 2.6.8
From: Christof Petig @ 2004-11-29 17:17 UTC (permalink / raw)
To: Frank Murphy; +Cc: debian-x, linuxppc-dev list
In-Reply-To: <1101721355.31317.209633721@webmail.messagingengine.com>
Frank Murphy schrieb:
>>Well, the backlight control is done by writing to one of the LCD
>>register, you can try to track down the values in there and eventually
>>compare with 2.4
>
>
> I'll try to look at this, but I don't know very much about it.
is it possible that using acceleration in the X server puts the chip in
a state which makes it hard for the kernel framebuffer to set brightness.
>>>I find it interesting that in the console the dimming works. I thought that on
>>>PPC the console used the framebuffer. Another notable thing is that while in
>>>X, the dimming doesn't work, but when I switch to one of the VTs, the screen
>>>brightness is adjusted to what it should have been in X.
>>
>>Ah ? What do you mean by "dimming" ? blanking ? or backlight control ?
>
>
> OK, the F1 and F2 keys on my keyboard have little "brighter"/"dimmer"
> icons on them. When I press them when running X on 2.4.25, the screen
> brightness changes. So for me, that's "dimming." I believe that it's the
> "backlight control" that does this. When I press the "dimmer" button
> enough (8 times, I think), then the backlight turns totally off, and I
> can't see anything on the screen. For me that's "blanking" in this
> context. It's true that something else (X, I think) will kind of blank
> the screen, but the backlight stays lit. I noticed this when I closed
> the lid to make the iBook sleep, but the backlight stayed on, warming
> the keyboard. I then noticed the same problem with the dimmer keys.
>
>
>>Could be some crap done by the X driver ... It's notoriously allergic to
>>fbdev's ... I'll have to double check.
>
> It probably is. But it's strange because I use all the same software
> except the kernel to get this behavior. So there's something with the
> kernel / X interface that has changed, either for 2.6 or the way Debian
> builds 2.6. I have a hard time with this because there are so many
> interactions: is it the PMU? The APM emulation? X driver? Kernel
> driver? I don't have a good idea about how these things interact.
I don't think that this problem is debian related (I use a stock kernel
for my tests). It _might_ theoretically be a strange result of the
debian X server patches, but I don't think so.
If I enter snooze in _2.4_ my screen gets strangely garbaged (blue
patterns which are somewhat related to the X11 screen but with wrong
line offset, perhaps displayed at 640x480x8bpp?), then the hard disc
parks, then backlight is turned off. If I press a key, the cdrom spins
up, the backlight is turned on (screen garbaged) and the screen is
restored. I would say that this is the correct behaviour.
My last experiment with 2.6.9 gave me (IIRC): The console is switched to
a text console, some power management transition messages appear, the
hard disc parks, the text remains readable ... Pressing a screen gives
CD spinup, more messages and the computer switches back to the X console.
I will retry with Linus latest tree.
Christof
^ permalink raw reply
* Re: Kernel 2.6.10-rc1 yet not running on ads8272
From: alebas @ 2004-11-29 13:58 UTC (permalink / raw)
To: linuxppc-embedded
In-Reply-To: <001d01c4d617$0f72f340$0301a8c0@chuck2>
Mark,
I have tried this before and this doesn't work.
Execution flow never arrives to "2:".
All the other values are extra info I have collected.
But problem was isolated using breakpoints to avoid
stepping influence.
Best regards,
Alex
Citando Mark Chambers <markc@mail.com>:
> RFI = Return From Interrupt. In normal usage the current
> address is copied into SRR0 when an exception occurs so
> the processor will return to that point with an RFI. So this
> code is 'faking out' the RFI instruction to return to 2:
>
> Are you maybe trying to trace through this code? If so,
> and depending on your debugger, you may not be able to.
> If your debugger uses trace or some other exception to do
> single step, once you get past mtspr SRR0,r4 the single
> step exception will overwrite the correct SRR0. You
> can see if this is the case by only tracing to mtspr SRR0,r4
> and then putting a breakpoint at 2:
>
> Good luck,
> Mark Chambers
>
^ permalink raw reply
* Re: Kernel 2.6.10-rc1 yet not running on ads8272
From: Mark Chambers @ 2004-11-29 13:26 UTC (permalink / raw)
To: alebas, linuxppc-embedded
In-Reply-To: <1101732712.41ab1b682760f@webmail.televes.com:443>
> Hi all,
>
> After commenting out the BCSR lines on m82xx_board_init as Dan
> suggested, some problem still prevents kernel 2.6.10-rc1 from
> running in ads8272 board.
>
> After running MMU_init, next step in head.S is this
>
> /*
> * Go back to running unmapped so we can load up new values
> * for SDR1 (hash table pointer) and the segment registers
> * and change to using our exception vectors.
> */
> lis r4,2f@h
> ori r4,r4,2f@l
> tophys(r4,r4)
> li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR)
> FIX_SRR1(r3,r5)
> mtspr SRR0,r4
> mtspr SRR1,r3
> SYNC
> RFI
> /* Load up the kernel context */
> 2: bl load_up_mmu
>
> which is not clear for me. Here, the execution flow breaks
> in RFI and never arrives to "bl load_up_mmu". The value of
> SRR0 and SRR1 before RFI is:
>
RFI = Return From Interrupt. In normal usage the current
address is copied into SRR0 when an exception occurs so
the processor will return to that point with an RFI. So this
code is 'faking out' the RFI instruction to return to 2:
Are you maybe trying to trace through this code? If so,
and depending on your debugger, you may not be able to.
If your debugger uses trace or some other exception to do
single step, once you get past mtspr SRR0,r4 the single
step exception will overwrite the correct SRR0. You
can see if this is the case by only tracing to mtspr SRR0,r4
and then putting a breakpoint at 2:
Good luck,
Mark Chambers
^ permalink raw reply
* Kernel 2.6.10-rc1 yet not running on ads8272
From: alebas @ 2004-11-29 12:51 UTC (permalink / raw)
To: linuxppc-embedded
In-Reply-To: <2515D492-3D8C-11D9-B8D4-003065F9B7DC@embeddededge.com>
Hi all,
After commenting out the BCSR lines on m82xx_board_init as Dan
suggested, some problem still prevents kernel 2.6.10-rc1 from
running in ads8272 board.
After running MMU_init, next step in head.S is this
/*
* Go back to running unmapped so we can load up new values
* for SDR1 (hash table pointer) and the segment registers
* and change to using our exception vectors.
*/
lis r4,2f@h
ori r4,r4,2f@l
tophys(r4,r4)
li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR)
FIX_SRR1(r3,r5)
mtspr SRR0,r4
mtspr SRR1,r3
SYNC
RFI
/* Load up the kernel context */
2: bl load_up_mmu
which is not clear for me. Here, the execution flow breaks
in RFI and never arrives to "bl load_up_mmu". The value of
SRR0 and SRR1 before RFI is:
8272>rd srr0
srr0: 0x000035f8 13816
8272>rd srr1
srr1: 0x00001002 4098
SRR0=0x35f8 is the EA where "bl load_up_mmu" instruction is
supposed to be.
So now I have no idea about what or why this is not working.
Any help would be very valuable. My knowledge about this
stuff is actually very limited.
Thanks,
Alex Bastos
Dan Malek <dan@embeddededge.com> wrote
> > When this code is executed, the kernel jumps directly to another
> > address, 0xc000984c,
>
> I suspect the MMU isn't yet set up to map the address of the BCSR.
> Access the address causes a fault, and the "jump" you see is the
> result of executing a bunch of exception handler instructions that
> you won't single step due to the exception priorities.
>
> -- Dan
^ permalink raw reply
* Re: G3 iBook LCD brightness in X under kernel 2.4.25 and 2.6.8
From: Frank Murphy @ 2004-11-29 9:42 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Frank Murphy; +Cc: debian-x, linuxppc-dev list
In-Reply-To: <1101674379.4653.9.camel@gaston>
On Mon, 29 Nov 2004 07:39:39 +1100, "Benjamin Herrenschmidt" said:
> On Sun, 2004-11-28 at 21:14 +0100, Frank Murphy wrote:
> > On Sunday 28 November 2004 2:58, Benjamin Herrenschmidt wrote:
> > > >
> > > > So I've tried this using the stock Debian 2.6.8 and 2.6.9 PPC kernels
> > > > with the same results.
> > > >
> > > > $ grep ATY /boot/config-2.6.9-powerpc
> > > > CONFIG_FB_ATY128=y
> > > > CONFIG_FB_ATY=y
> > > > CONFIG_FB_ATY_CT=y
> > > > CONFIG_FB_ATY_GX=y
> > > > # CONFIG_FB_ATY_XL_INIT is not set
> > > >
> > > > It seems that the atyfb module is compiled in. Is there another way to
> > > > check?
> > >
> > > Check the bits in CONFIG_PMAC_BACKLIGHT in atyfb (like, add some
> > > printk's to see if they are reached etc..)
> >
> > So I added a couple of printk()s like you asked. I added one to
> > aty128fb_blank() in aty128fb.c:2037 and two to aty_init() in
> > atyfb_base.c:1793 and 1802 (inside the 'if (M64_HAS(MOBIL_BUS))'). The one I
> > added to aty128fb didn't print (as expected), but both of them from
> > atyfb_base.c printed. So it seems that CONFIG_PMAC_BACKLIGHT is properly
> > defined. Anything you'd specifically like me to try?
>
> Well, the backlight control is done by writing to one of the LCD
> register, you can try to track down the values in there and eventually
> compare with 2.4
I'll try to look at this, but I don't know very much about it.
> > I find it interesting that in the console the dimming works. I thought that on
> > PPC the console used the framebuffer. Another notable thing is that while in
> > X, the dimming doesn't work, but when I switch to one of the VTs, the screen
> > brightness is adjusted to what it should have been in X.
>
> Ah ? What do you mean by "dimming" ? blanking ? or backlight control ?
OK, the F1 and F2 keys on my keyboard have little "brighter"/"dimmer"
icons on them. When I press them when running X on 2.4.25, the screen
brightness changes. So for me, that's "dimming." I believe that it's the
"backlight control" that does this. When I press the "dimmer" button
enough (8 times, I think), then the backlight turns totally off, and I
can't see anything on the screen. For me that's "blanking" in this
context. It's true that something else (X, I think) will kind of blank
the screen, but the backlight stays lit. I noticed this when I closed
the lid to make the iBook sleep, but the backlight stayed on, warming
the keyboard. I then noticed the same problem with the dimmer keys.
> Could be some crap done by the X driver ... It's notoriously allergic to
> fbdev's ... I'll have to double check.
It probably is. But it's strange because I use all the same software
except the kernel to get this behavior. So there's something with the
kernel / X interface that has changed, either for 2.6 or the way Debian
builds 2.6. I have a hard time with this because there are so many
interactions: is it the PMU? The APM emulation? X driver? Kernel
driver? I don't have a good idea about how these things interact.
Let me know what you find out.
Frank
^ permalink raw reply
* Re: G3 iBook LCD brightness in X under kernel 2.4.25 and 2.6.8
From: Christof Petig @ 2004-11-29 0:45 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev list, Frank Murphy, debian-x
In-Reply-To: <1101517373.4668.2.camel@gaston>
Benjamin Herrenschmidt schrieb:
> What machine model precisely ? If it's an iBook, only the very first one
> had a mach64 based chip and indeed used atyfb-based brightness control,
> that might have been broken...
My machine is an orange iBook (300MHz), lspci:
0000:00:0b.0 Host bridge: Apple Computer Inc. UniNorth AGP
0000:00:10.0 VGA compatible controller: ATI Technologies Inc Rage
Mobility L AGP 2x (rev 64)
0000:10:0b.0 Host bridge: Apple Computer Inc. UniNorth PCI
0000:10:17.0 ff00: Apple Computer Inc. KeyLargo Mac I/O (rev 02)
0000:10:18.0 USB Controller: Apple Computer Inc. KeyLargo USB
0000:10:19.0 USB Controller: Apple Computer Inc. KeyLargo USB
0000:20:0b.0 Host bridge: Apple Computer Inc. UniNorth Internal PCI
0000:20:0f.0 ffff: Apple Computer Inc. UniNorth GMAC (Sun GEM) (rev ff)
> Some earlier models have PMU-controlled brightness, independently on the
> video card...
I don't think this is the case for us.
>>Your mouse pointer might get some noisy spots on brightness change, ever
>>noticed? I would blame the kernel first (since 2.4 works well).
>
>
> Ah ? Haven't noticed that ? what machine ?
I would blame X to leaving the mach64 in a 'strange' state which makes
the kernel unable to set the brightness the way it uses to do. Since 2.4
was able to do it, it must be possible (might be less efficient).
>>>The reason this is important for me is that the LCD is not dimmed when I close
>>>the lid for sleep, which kind of ruins the point of sleep.
>>>
>>>Does anyone have an idea of what else I need to do to get screen dimming to
>>>work under X with Linux 2.6?
>
>
> You may be missing the atyfb kernel driver.
CONFIG_ADB=y
# CONFIG_ADB_CUDA is not set
CONFIG_ADB_PMU=y
CONFIG_PMAC_PBOOK=y
CONFIG_PMAC_APM_EMU=y
CONFIG_PMAC_BACKLIGHT=y
# CONFIG_ADB_MACIO is not set
# CONFIG_FB_ATY128 is not set
CONFIG_FB_ATY=y
CONFIG_FB_ATY_CT=y
# CONFIG_FB_ATY_GX is not set
# CONFIG_FB_ATY_XL_INIT is not set
>>Try to ask on the ppc-kernel list (CCed). I suspect that nobody has
>>reimplemented power management for mach64 in 2.6. The fact that lid
>>closing does not put the display to sleep keeps me locked in 2.4, too
>>(though I experienced that 2.6 is way faster).
>
>
> It is, provided you have the right driver.
I really would like to work this out.
Yours
Christof
^ permalink raw reply
* Re: MPC5200 Cache coherency with BestComm issue
From: Sylvain Munaut @ 2004-11-28 23:22 UTC (permalink / raw)
To: roger blofeld; +Cc: linuxppc-embedded
In-Reply-To: <20041127213940.72317.qmail@web53501.mail.yahoo.com>
Hi Roger
> By experimenting I have found that the BestComm and FEC work without
>the cache flush provided CPU_FTR_MAYBE_CAN_NAP is removed from the
>cputable (nap disables snooping) and that CPU_FTR_NEED_COHERENT is
>added to the cputable (turns on "M" bit in BAT/PTE so that the XLB has
>a chance of seeing a global transaction).
>
>
Thanks for the info.
The nap thing was indeed obvious ... The proper way to deactivate it
would be in lite5200.c
I added the powersave_nap = 1; as an example on how to allow it to nap.
I'll probably
comment it out by default with a note.
For the CPU_FTR_NEED_COHERENT, are you sure it's required ?
From my understanding of the G2Core manual, the M bit must be set if
you want the G2Core
to assert the global signal when it access memory. Here, we don't care
since only
the G2Core is snooping on the bus. So that should not be required ...
Sylvain
^ permalink raw reply
* Re: G3 iBook LCD brightness in X under kernel 2.4.25 and 2.6.8
From: Benjamin Herrenschmidt @ 2004-11-28 20:39 UTC (permalink / raw)
To: Frank Murphy; +Cc: debian-x, linuxppc-dev list
In-Reply-To: <200411282114.48141.murphyf+xfree86@f-m.fm>
On Sun, 2004-11-28 at 21:14 +0100, Frank Murphy wrote:
> On Sunday 28 November 2004 2:58, Benjamin Herrenschmidt wrote:
> > >
> > > So I've tried this using the stock Debian 2.6.8 and 2.6.9 PPC kernels
> > > with the same results.
> > >
> > > $ grep ATY /boot/config-2.6.9-powerpc
> > > CONFIG_FB_ATY128=y
> > > CONFIG_FB_ATY=y
> > > CONFIG_FB_ATY_CT=y
> > > CONFIG_FB_ATY_GX=y
> > > # CONFIG_FB_ATY_XL_INIT is not set
> > >
> > > It seems that the atyfb module is compiled in. Is there another way to
> > > check?
> >
> > Check the bits in CONFIG_PMAC_BACKLIGHT in atyfb (like, add some
> > printk's to see if they are reached etc..)
>
> So I added a couple of printk()s like you asked. I added one to
> aty128fb_blank() in aty128fb.c:2037 and two to aty_init() in
> atyfb_base.c:1793 and 1802 (inside the 'if (M64_HAS(MOBIL_BUS))'). The one I
> added to aty128fb didn't print (as expected), but both of them from
> atyfb_base.c printed. So it seems that CONFIG_PMAC_BACKLIGHT is properly
> defined. Anything you'd specifically like me to try?
Well, the backlight control is done by writing to one of the LCD
register, you can try to track down the values in there and eventually
compare with 2.4
> I find it interesting that in the console the dimming works. I thought that on
> PPC the console used the framebuffer. Another notable thing is that while in
> X, the dimming doesn't work, but when I switch to one of the VTs, the screen
> brightness is adjusted to what it should have been in X.
Ah ? What do you mean by "dimming" ? blanking ? or backlight control ?
Could be some crap done by the X driver ... It's notoriously allergic to
fbdev's ... I'll have to double check.
> Thanks for your help on this,
>
> Frank
--
Benjamin Herrenschmidt <benh@kernel.crashing.org>
^ permalink raw reply
* Re: G3 iBook LCD brightness in X under kernel 2.4.25 and 2.6.8
From: Frank Murphy @ 2004-11-28 20:14 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: debian-x, linuxppc-dev list
In-Reply-To: <1101607094.5176.0.camel@gaston>
On Sunday 28 November 2004 2:58, Benjamin Herrenschmidt wrote:
> >
> > So I've tried this using the stock Debian 2.6.8 and 2.6.9 PPC kernels
> > with the same results.
> >
> > $ grep ATY /boot/config-2.6.9-powerpc
> > CONFIG_FB_ATY128=y
> > CONFIG_FB_ATY=y
> > CONFIG_FB_ATY_CT=y
> > CONFIG_FB_ATY_GX=y
> > # CONFIG_FB_ATY_XL_INIT is not set
> >
> > It seems that the atyfb module is compiled in. Is there another way to
> > check?
>
> Check the bits in CONFIG_PMAC_BACKLIGHT in atyfb (like, add some
> printk's to see if they are reached etc..)
So I added a couple of printk()s like you asked. I added one to
aty128fb_blank() in aty128fb.c:2037 and two to aty_init() in
atyfb_base.c:1793 and 1802 (inside the 'if (M64_HAS(MOBIL_BUS))'). The one I
added to aty128fb didn't print (as expected), but both of them from
atyfb_base.c printed. So it seems that CONFIG_PMAC_BACKLIGHT is properly
defined. Anything you'd specifically like me to try?
I find it interesting that in the console the dimming works. I thought that on
PPC the console used the framebuffer. Another notable thing is that while in
X, the dimming doesn't work, but when I switch to one of the VTs, the screen
brightness is adjusted to what it should have been in X.
Thanks for your help on this,
Frank
^ permalink raw reply
* Re: TEST: Sleep patch #5
From: Richard Hult @ 2004-11-28 12:09 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: linuxppc-dev list, gentoo-ppc-user,
debian-powerpc@lists.debian.org
In-Reply-To: <1101280145.4511.4.camel@gaston>
Benjamin Herrenschmidt wrote:
> Ok, here's the 5th version of the sleep patch for ATI based albooks &
> iBook G4. Other machine users, please test too as it may cause
> regressions (or improvements) as well.
This seems to be working pretty well, thanks!
One slight problem is that there is some flicker when going to and from
level 0 on the backlight is still present. I can reproduce this by
running fblevel 0, with the effect that the backlight temporarily
switches to the maximum value (it seems) and then shuts it off
completely. The same happens when going from 0 to a higher value.
The only other problem I've seen is that the trackpad/fn keys settings
aren't always restored on resume.
Thanks again,
Richard
--
Imendio AB, http://www.imendio.com/
^ permalink raw reply
* Re: G3 iBook LCD brightness in X under kernel 2.4.25 and 2.6.8
From: Benjamin Herrenschmidt @ 2004-11-28 1:58 UTC (permalink / raw)
To: Frank Murphy; +Cc: debian-x, linuxppc-dev list
In-Reply-To: <200411270948.08054.murphyf+xfree86@f-m.fm>
> r.
>
> So I've tried this using the stock Debian 2.6.8 and 2.6.9 PPC kernels with the
> same results.
>
> $ grep ATY /boot/config-2.6.9-powerpc
> CONFIG_FB_ATY128=y
> CONFIG_FB_ATY=y
> CONFIG_FB_ATY_CT=y
> CONFIG_FB_ATY_GX=y
> # CONFIG_FB_ATY_XL_INIT is not set
>
> It seems that the atyfb module is compiled in. Is there another way to check?
Check the bits in CONFIG_PMAC_BACKLIGHT in atyfb (like, add some
printk's to see if they are reached etc..)
Ben
^ permalink raw reply
* Re: MPC5200 Cache coherency with BestComm issue
From: roger blofeld @ 2004-11-27 21:39 UTC (permalink / raw)
To: tnt; +Cc: linuxppc-embedded
In-Reply-To: <20041116201654.GA3088@xyzzy>
Sylvain,
By experimenting I have found that the BestComm and FEC work without
the cache flush provided CPU_FTR_MAYBE_CAN_NAP is removed from the
cputable (nap disables snooping) and that CPU_FTR_NEED_COHERENT is
added to the cputable (turns on "M" bit in BAT/PTE so that the XLB has
a chance of seeing a global transaction).
I don't know if that will work for all G2_LE cores, but it seems
required for the 5200.
-rb
__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com
^ permalink raw reply
* Re: MPC5200 toolchain test
From: Jon Masters @ 2004-11-27 18:42 UTC (permalink / raw)
To: Wolfgang Denk; +Cc: linuxppc-embedded
In-Reply-To: <20041126143701.98123C1430@atlas.denx.de>
On Fri, 26 Nov 2004 15:36:56 +0100, Wolfgang Denk <wd@denx.de> wrote:
> [I hope you don't call "U-Boot" an OS ;-) ]
Just wait until a marketting department gets their hands on it :-).
Jon.
^ permalink raw reply
* Re: TEST: Sleep patch #5
From: Colin Leroy @ 2004-11-27 18:17 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20041127123111.7e2db5bb@jack.colino.net>
On 27 Nov 2004 at 12h11, Colin Leroy wrote:
Hi,
> noticed glxgears down to approx. 200fps. It used to be around
> 1500-1800, xorg didn't change. It is also slower just after boot, no
> need to go to sleep.
Hmmm. Disregard that, it looks like a perfect case of ID-10T error ;)
--
Colin
http://dudusdl.sf.net/ : a free Puzzle Bubble clone
^ permalink raw reply
* [PATCH] test patch, sungem wake-on-lan for 2.6.9+sleep
From: Colin Leroy @ 2004-11-27 16:58 UTC (permalink / raw)
To: debian-powerpc, linuxppc-dev
[-- Attachment #1: Type: text/plain, Size: 156 bytes --]
Hi,
this patch is a port of Ben's sungem WOL patch that applies on top of
2.6.9 + Ben's sleep patch. It also has 2.6.10-rc2's therm_adt746x.
hth
--
Colin
[-- Attachment #2: sleep_and_wol.patch --]
[-- Type: application/octet-stream, Size: 55465 bytes --]
diff -ur a/arch/ppc/platforms/pmac_feature.c b/arch/ppc/platforms/pmac_feature.c
--- a/arch/ppc/platforms/pmac_feature.c 2004-11-26 20:09:44.768528928 +0100
+++ b/arch/ppc/platforms/pmac_feature.c 2004-11-26 17:36:42.000000000 +0100
@@ -1688,8 +1688,11 @@
*/
save_unin_clock_ctl = UN_IN(UNI_N_CLOCK_CNTL);
+ /* Note: do not switch GMAC off, driver does it when necessary, WOL must keep it
+ * enabled !
+ */
UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl &
- ~(UNI_N_CLOCK_CNTL_GMAC|UNI_N_CLOCK_CNTL_FW/*|UNI_N_CLOCK_CNTL_PCI*/));
+ ~(/*UNI_N_CLOCK_CNTL_GMAC|*/UNI_N_CLOCK_CNTL_FW/*|UNI_N_CLOCK_CNTL_PCI*/));
udelay(100);
UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_SLEEPING);
UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_SLEEP);
diff -ur a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c
--- a/drivers/macintosh/therm_adt746x.c 2004-10-18 23:54:54.000000000 +0200
+++ b/drivers/macintosh/therm_adt746x.c 2004-11-19 09:00:35.000000000 +0100
@@ -22,13 +22,16 @@
#include <linux/spinlock.h>
#include <linux/smp_lock.h>
#include <linux/wait.h>
+#include <linux/suspend.h>
+#include <linux/kthread.h>
+#include <linux/moduleparam.h>
+
#include <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 <linux/kthread.h>
#undef DEBUG
@@ -50,21 +53,26 @@
static int fan_speed = -1;
MODULE_AUTHOR("Colin Leroy <colin@colino.net>");
-MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and Powerbook G4 Alu");
+MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and "
+ "Powerbook G4 Alu");
MODULE_LICENSE("GPL");
-MODULE_PARM(limit_adjust,"i");
-MODULE_PARM_DESC(limit_adjust,"Adjust maximum temperatures (50 cpu, 70 gpu) by N degrees.");
-MODULE_PARM(fan_speed,"i");
-MODULE_PARM_DESC(fan_speed,"Specify fan speed (0-255) when lim < temp < lim+8 (default 128)");
+module_param(limit_adjust, int, 0644);
+MODULE_PARM_DESC(limit_adjust,"Adjust maximum temperatures (50 cpu, 70 gpu) "
+ "by N degrees.");
+
+module_param(fan_speed, int, 0644);
+MODULE_PARM_DESC(fan_speed,"Specify starting fan speed (0-255) "
+ "(default 64)");
struct thermostat {
struct i2c_client clt;
+ u8 temps[3];
u8 cached_temp[3];
u8 initial_limits[3];
u8 limits[3];
int last_speed[2];
- int overriding[2];
+ int last_var[2];
};
static enum {ADT7460, ADT7467} therm_type;
@@ -73,7 +81,9 @@
static struct thermostat* thermostat;
static struct task_struct *thread_therm = NULL;
-static int attach_one_thermostat(struct i2c_adapter *adapter, int addr, int busno);
+static 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);
@@ -139,10 +149,11 @@
kthread_stop(thread_therm);
}
- printk(KERN_INFO "adt746x: Putting max temperatures back from %d, %d, %d,"
- " to %d, %d, %d\n",
+ printk(KERN_INFO "adt746x: Putting max temperatures back from "
+ "%d, %d, %d to %d, %d, %d\n",
th->limits[0], th->limits[1], th->limits[2],
- th->initial_limits[0], th->initial_limits[1], th->initial_limits[2]);
+ 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]);
@@ -201,11 +212,11 @@
if (th->last_speed[fan] != speed) {
if (speed == -1)
- printk(KERN_INFO "adt746x: Setting speed to: automatic for %s fan.\n",
- fan?"GPU":"CPU");
+ printk(KERN_DEBUG "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");
+ printk(KERN_DEBUG "adt746x: Setting speed to %d "
+ "for %s fan.\n", speed, fan?"GPU":"CPU");
} else
return;
@@ -216,8 +227,11 @@
} 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]);
+ 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));
@@ -227,97 +241,117 @@
th->last_speed[fan] = speed;
}
-static int monitor_task(void *arg)
+static void read_sensors(struct thermostat *th)
{
- struct thermostat* th = arg;
- u8 temps[3];
- u8 lims[3];
- int i;
+ int i = 0;
+
+ for (i = 0; i < 3; i++)
+ th->temps[i] = read_reg(th, TEMP_REG[i]);
+}
+
#ifdef DEBUG
- int mfan_speed;
+static void display_stats(struct thermostat *th)
+{
+ if (th->temps[0] != th->cached_temp[0]
+ || th->temps[1] != th->cached_temp[1]
+ || th->temps[2] != th->cached_temp[2]) {
+ printk(KERN_INFO "adt746x: Temperature infos:"
+ " thermostats: %d,%d,%d;"
+ " limits: %d,%d,%d;"
+ " fan speed: %d RPM\n",
+ th->temps[0], th->temps[1], th->temps[2],
+ th->limits[0], th->limits[1], th->limits[2],
+ read_fan_speed(th, FAN_SPEED[0]));
+ }
+ th->cached_temp[0] = th->temps[0];
+ th->cached_temp[1] = th->temps[1];
+ th->cached_temp[2] = th->temps[2];
+}
#endif
- while(!kthread_should_stop())
- {
- msleep_interruptible(2000);
- /* 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];
+static void update_fans_speed (struct thermostat *th)
+{
+ int lastvar = 0; /* last variation, for iBook */
+ int i = 0;
+
+ /* we don't care about local sensor, so we start at sensor 1 */
+ for (i = 1; i < 3; i++) {
+ int started = 0;
+ int fan_number = (therm_type == ADT7460 && i == 2);
+ int var = th->temps[i] - th->limits[i];
+
+ if (var > -1) {
+ int step = (255 - fan_speed) / 7;
+ int new_speed = 0;
+
+ /* hysteresis : change fan speed only if variation is
+ * more than two degrees */
+ if (abs(var - th->last_var[fan_number]) < 2)
+ continue;
+
+ started = 1;
+ new_speed = fan_speed + ((var-1)*step);
+
+ if (new_speed < fan_speed)
+ new_speed = fan_speed;
+ if (new_speed > 255)
+ new_speed = 255;
+
+ printk(KERN_DEBUG "adt746x: setting fans speed to %d "
+ "(limit exceeded by %d on %s) \n",
+ new_speed, var,
+ fan_number?"GPU/pwr":"CPU");
+ write_both_fan_speed(th, new_speed);
+ th->last_var[fan_number] = var;
+ } else if (var < -2) {
+ /* don't stop fan if GPU/power is cold and CPU is not
+ * so cold (lastvar >= -1) */
+ if (i == 2 && lastvar < -1) {
+ if (th->last_speed[fan_number] != 0)
+ printk(KERN_DEBUG "adt746x: Stopping "
+ "fans.\n");
+ write_both_fan_speed(th, 0);
}
-#ifndef DEBUG
}
+
+ lastvar = var;
+
+ if (started)
+ return; /* we don't want to re-stop the fan
+ * if CPU is heating and GPU/power is not */
+ }
+}
+
+static int monitor_task(void *arg)
+{
+ struct thermostat* th = arg;
+
+ while(!kthread_should_stop()) {
+ if (current->flags & PF_FREEZE)
+ refrigerator(PF_FREEZE);
+
+ msleep_interruptible(2000);
+
+#ifndef DEBUG
+ if (fan_speed != -1)
+ read_sensors(th);
+#else
+ read_sensors(th);
#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, 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, 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 */
- }
- }
+
+ if (fan_speed != -1)
+ update_fans_speed(th);
+
#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;"
- " limits: %d,%d,%d;"
- " 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
+ display_stats(th);
+#endif
+
}
return 0;
}
-static void
-set_limit(struct thermostat *th, int i)
+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;
@@ -326,9 +360,9 @@
/* 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)
+
+static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,
+ int busno)
{
struct thermostat* th;
int rc;
@@ -336,9 +370,13 @@
if (thermostat)
return 0;
- th = (struct thermostat *)kmalloc(sizeof(struct thermostat), GFP_KERNEL);
+
+ 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;
@@ -348,15 +386,16 @@
rc = read_reg(th, 0);
if (rc < 0) {
- printk(KERN_ERR "adt746x: Thermostat failed to read config from bus %d !\n",
- busno);
+ 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;
+ fan_speed = 64;
if(therm_type == ADT7460) {
printk(KERN_INFO "adt746x: ADT7460 initializing\n");
@@ -371,14 +410,16 @@
}
printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d"
- " to %d, %d, %d\n",
- th->initial_limits[0], th->initial_limits[1], th->initial_limits[2],
- th->limits[0], th->limits[1], th->limits[2]);
+ " to %d, %d, %d\n",
+ th->initial_limits[0], th->initial_limits[1],
+ th->initial_limits[2], th->limits[0], th->limits[1],
+ th->limits[2]);
thermostat = th;
if (i2c_attach_client(&th->clt)) {
- printk(KERN_INFO "adt746x: Thermostat failed to attach client !\n");
+ printk(KERN_INFO "adt746x: Thermostat failed to attach "
+ "client !\n");
thermostat = NULL;
kfree(th);
return -ENODEV;
@@ -387,10 +428,14 @@
/* be sure to really write fan speed the first time */
th->last_speed[0] = -2;
th->last_speed[1] = -2;
-
+ th->last_var[0] = -80;
+ th->last_var[1] = -80;
+
if (fan_speed != -1) {
+ /* manual mode, stop fans */
write_both_fan_speed(th, 0);
} else {
+ /* automatic mode */
write_both_fan_speed(th, -1);
}
@@ -507,8 +552,9 @@
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);
+ 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");
@@ -545,8 +591,11 @@
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);
+ device_remove_file(&of_dev->dev,
+ &dev_attr_gpu_fan_speed);
+
of_device_unregister(of_dev);
}
i2c_del_driver(&thermostat_driver);
diff -ur a/drivers/net/sungem.c b/drivers/net/sungem.c
--- a/drivers/net/sungem.c 2004-11-26 20:09:44.799524216 +0100
+++ b/drivers/net/sungem.c 2004-11-26 19:54:41.000000000 +0100
@@ -10,9 +10,24 @@
* (C) 2004 by Eric Lemoine (eric.lemoine@gmail.com)
*
* TODO:
- * - Get rid of all those nasty mdelay's and replace them
- * with schedule_timeout.
- * - Implement WOL
+ * - Now that the driver was significantly simplified, I need to rework
+ * the locking. I'm sure we don't need _2_ spinlocks, and we probably
+ * can avoid taking most of them for so long period of time (and schedule
+ * instead). The main issues at this point are caused by the netdev layer
+ * though:
+ *
+ * gem_change_mtu() and gem_set_multicast() are called with a read_lock()
+ * help by net/core/dev.c, thus they can't schedule. That means they can't
+ * call netif_poll_disable() neither, thus force gem_poll() to keep a spinlock
+ * where it could have been dropped. change_mtu especially would love also to
+ * be able to msleep instead of horrid locked delays when resetting the HW,
+ * but that read_lock() makes it impossible, unless I defer it's action to
+ * the reset task, which means it'll be asynchronous (won't take effect until
+ * the system schedules a bit).
+ *
+ * Also, it would probably be possible to also remove most of the long-life
+ * locking in open/resume code path (gem_reinit_chip) by beeing more careful
+ * about when we can start taking interrupts or get xmit() called...
*/
#include <linux/module.h>
@@ -196,6 +211,29 @@
writel(GREG_STAT_NAPI | GREG_STAT_TXDONE, gp->regs + GREG_IMASK);
}
+static void gem_get_cell(struct gem *gp)
+{
+ gp->cell_enabled++;
+#ifdef CONFIG_PPC_PMAC
+ if (gp->cell_enabled == 1) {
+ mb();
+ pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 1);
+ udelay(10);
+ }
+#endif /* CONFIG_PPC_PMAC */
+}
+
+/* Turn off the chip's clock */
+static void gem_put_cell(struct gem *gp)
+{
+ gp->cell_enabled--;
+ BUG_ON(gp->cell_enabled < 0);
+#ifdef CONFIG_PPC_PMAC
+ if (gp->cell_enabled == 0)
+ pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 0);
+#endif /* CONFIG_PPC_PMAC */
+}
+
static void gem_handle_mif_event(struct gem *gp, u32 reg_val, u32 changed_bits)
{
if (netif_msg_intr(gp))
@@ -597,7 +635,7 @@
return 0;
do_reset:
- gp->reset_task_pending = 2;
+ gp->reset_task_pending = 1;
schedule_work(&gp->reset_task);
return 1;
@@ -823,6 +861,9 @@
struct gem *gp = dev->priv;
unsigned long flags;
+ /*
+ * NAPI locking nightmare: See comment at head of driver
+ */
spin_lock_irqsave(&gp->lock, flags);
do {
@@ -874,8 +915,11 @@
struct gem *gp = dev->priv;
unsigned long flags;
- /* Swallow interrupts when shutting the chip down */
- if (!gp->hw_running)
+ /* Swallow interrupts when shutting the chip down, though
+ * that shouldn't happen, we should have done free_irq() at
+ * this point...
+ */
+ if (!gp->running)
return IRQ_HANDLED;
spin_lock_irqsave(&gp->lock, flags);
@@ -916,7 +960,7 @@
struct gem *gp = dev->priv;
printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
- if (!gp->hw_running) {
+ if (!gp->running) {
printk("%s: hrm.. hw not running !\n", dev->name);
return;
}
@@ -934,7 +978,7 @@
spin_lock_irq(&gp->lock);
spin_lock(&gp->tx_lock);
- gp->reset_task_pending = 2;
+ gp->reset_task_pending = 1;
schedule_work(&gp->reset_task);
spin_unlock(&gp->tx_lock);
@@ -975,6 +1019,11 @@
local_irq_restore(flags);
return NETDEV_TX_LOCKED;
}
+ /* We raced with gem_do_stop() */
+ if (!gp->running) {
+ spin_unlock_irqrestore(&gp->tx_lock, flags);
+ return NETDEV_TX_BUSY;
+ }
/* This is a hard error, log it. */
if (TX_BUFFS_AVAIL(gp) <= (skb_shinfo(skb)->nr_frags + 1)) {
@@ -1073,46 +1122,10 @@
return NETDEV_TX_OK;
}
-/* Jumbo-grams don't seem to work :-( */
-#define GEM_MIN_MTU 68
-#if 1
-#define GEM_MAX_MTU 1500
-#else
-#define GEM_MAX_MTU 9000
-#endif
-
-static int gem_change_mtu(struct net_device *dev, int new_mtu)
-{
- struct gem *gp = dev->priv;
-
- if (new_mtu < GEM_MIN_MTU || new_mtu > GEM_MAX_MTU)
- return -EINVAL;
-
- if (!netif_running(dev) || !netif_device_present(dev)) {
- /* We'll just catch it later when the
- * device is up'd or resumed.
- */
- dev->mtu = new_mtu;
- return 0;
- }
-
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
- dev->mtu = new_mtu;
- gp->reset_task_pending = 1;
- schedule_work(&gp->reset_task);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
-
- flush_scheduled_work();
-
- return 0;
-}
-
#define STOP_TRIES 32
/* Must be invoked under gp->lock and gp->tx_lock. */
-static void gem_stop(struct gem *gp)
+static void gem_reset(struct gem *gp)
{
int limit;
u32 val;
@@ -1140,7 +1153,7 @@
/* Must be invoked under gp->lock and gp->tx_lock. */
static void gem_start_dma(struct gem *gp)
{
- unsigned long val;
+ u32 val;
/* We are ready to rock, turn everything on. */
val = readl(gp->regs + TXDMA_CFG);
@@ -1155,10 +1168,31 @@
(void) readl(gp->regs + MAC_RXCFG);
udelay(100);
- writel(GREG_STAT_TXDONE, gp->regs + GREG_IMASK);
+ gem_enable_ints(gp);
writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK);
+}
+
+/* Must be invoked under gp->lock and gp->tx_lock. DMA won't be
+ * actually stopped before about 4ms tho ...
+ */
+static void gem_stop_dma(struct gem *gp)
+{
+ u32 val;
+
+ /* We are done rocking, turn everything off. */
+ val = readl(gp->regs + TXDMA_CFG);
+ writel(val & ~TXDMA_CFG_ENABLE, gp->regs + TXDMA_CFG);
+ val = readl(gp->regs + RXDMA_CFG);
+ writel(val & ~RXDMA_CFG_ENABLE, gp->regs + RXDMA_CFG);
+ val = readl(gp->regs + MAC_TXCFG);
+ writel(val & ~MAC_TXCFG_ENAB, gp->regs + MAC_TXCFG);
+ val = readl(gp->regs + MAC_RXCFG);
+ writel(val & ~MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG);
+
+ (void) readl(gp->regs + MAC_RXCFG);
+ /* Need to wait a bit ... done by the caller */
}
@@ -1219,10 +1253,10 @@
if (speed == 0)
speed = SPEED_10;
- /* If HW is down, we don't try to actually setup the PHY, we
+ /* If we are asleep, we don't try to actually setup the PHY, we
* just store the settings
*/
- if (!gp->hw_running) {
+ if (gp->asleep) {
gp->phy_mii.autoneg = gp->want_autoneg = autoneg;
gp->phy_mii.speed = speed;
gp->phy_mii.duplex = duplex;
@@ -1279,6 +1313,9 @@
printk(KERN_INFO "%s: Link is up at %d Mbps, %s-duplex.\n",
gp->dev->name, speed, (full_duplex ? "full" : "half"));
+ if (!gp->running)
+ return 0;
+
val = (MAC_TXCFG_EIPG0 | MAC_TXCFG_NGU);
if (full_duplex) {
val |= (MAC_TXCFG_ICS | MAC_TXCFG_ICOLL);
@@ -1405,48 +1442,19 @@
}
}
-static void gem_init_rings(struct gem *);
-static void gem_init_hw(struct gem *, int);
-
-static void gem_reset_task(void *data)
-{
- struct gem *gp = (struct gem *) data;
-
- netif_poll_disable(gp->dev);
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
-
- if (gp->hw_running && gp->opened) {
- netif_stop_queue(gp->dev);
-
- /* Reset the chip & rings */
- gem_stop(gp);
- gem_init_rings(gp);
-
- gem_init_hw(gp,
- (gp->reset_task_pending == 2));
-
- netif_wake_queue(gp->dev);
- }
- gp->reset_task_pending = 0;
-
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
- netif_poll_enable(gp->dev);
-}
-
static void gem_link_timer(unsigned long data)
{
struct gem *gp = (struct gem *) data;
int restart_aneg = 0;
- if (!gp->hw_running)
+ if (gp->asleep)
return;
spin_lock_irq(&gp->lock);
spin_lock(&gp->tx_lock);
+ gem_get_cell(gp);
- /* If the link of task is still pending, we just
+ /* If the reset task is still pending, we just
* reschedule the link timer
*/
if (gp->reset_task_pending)
@@ -1462,8 +1470,7 @@
if ((val & PCS_MIISTAT_LS) != 0) {
gp->lstate = link_up;
netif_carrier_on(gp->dev);
- if (gp->opened)
- (void)gem_set_link_modes(gp);
+ (void)gem_set_link_modes(gp);
}
goto restart;
}
@@ -1484,7 +1491,7 @@
} else if (gp->lstate != link_up) {
gp->lstate = link_up;
netif_carrier_on(gp->dev);
- if (gp->opened && gem_set_link_modes(gp))
+ if (gem_set_link_modes(gp))
restart_aneg = 1;
}
} else {
@@ -1497,7 +1504,7 @@
printk(KERN_INFO "%s: Link down\n",
gp->dev->name);
netif_carrier_off(gp->dev);
- gp->reset_task_pending = 2;
+ gp->reset_task_pending = 1;
schedule_work(&gp->reset_task);
restart_aneg = 1;
} else if (++gp->timer_ticks > 10) {
@@ -1514,6 +1521,7 @@
restart:
mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
out_unlock:
+ gem_put_cell(gp);
spin_unlock(&gp->tx_lock);
spin_unlock_irq(&gp->lock);
}
@@ -1619,59 +1627,40 @@
wmb();
}
-/* Must be invoked under gp->lock and gp->tx_lock. */
+/* Init PHY interface and start link poll state machine */
static void gem_init_phy(struct gem *gp)
{
u32 mifcfg;
-
+
/* Revert MIF CFG setting done on stop_phy */
mifcfg = readl(gp->regs + MIF_CFG);
mifcfg &= ~MIF_CFG_BBMODE;
writel(mifcfg, gp->regs + MIF_CFG);
-#ifdef CONFIG_PPC_PMAC
if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
- int i, j;
+ int i;
/* Those delay sucks, the HW seem to love them though, I'll
* serisouly consider breaking some locks here to be able
* to schedule instead
*/
- pmac_call_feature(PMAC_FTR_GMAC_PHY_RESET, gp->of_node, 0, 0);
- for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++) {
+#ifdef CONFIG_PPC_PMAC
+ pmac_call_feature(PMAC_FTR_GMAC_PHY_RESET, gp->of_node, 0, 0);
+ msleep(20);
+#endif
/* Some PHYs used by apple have problem getting back to us,
- * we _know_ it's actually at addr 0 or 1, that's a hack, but
- * it helps to do that reset now. I suspect some motherboards
- * don't wire the PHY reset line properly, thus the PHY doesn't
- * come back with the above pmac_call_feature.
+ * we do an additional reset here
*/
- gp->mii_phy_addr = 0;
- phy_write(gp, MII_BMCR, BMCR_RESET);
- gp->mii_phy_addr = 1;
phy_write(gp, MII_BMCR, BMCR_RESET);
- /* We should probably break some locks here and schedule... */
- mdelay(10);
-
- /* On K2, we only probe the internal PHY at address 1, other
- * addresses tend to return garbage.
- */
- if (gp->pdev->device == PCI_DEVICE_ID_APPLE_K2_GMAC)
+ msleep(20);
+ if (phy_read(gp, MII_BMCR) != 0xffff)
break;
-
- for (i = 0; i < 32; i++) {
- gp->mii_phy_addr = i;
- if (phy_read(gp, MII_BMCR) != 0xffff)
- break;
- }
- if (i == 32) {
+ if (i == 2)
printk(KERN_WARNING "%s: GMAC PHY not responding !\n",
gp->dev->name);
- gp->mii_phy_addr = 0;
- } else
- break;
}
}
-#endif /* CONFIG_PPC_PMAC */
if (gp->pdev->vendor == PCI_VENDOR_ID_SUN &&
gp->pdev->device == PCI_DEVICE_ID_SUN_GEM) {
@@ -1755,6 +1744,16 @@
val |= PCS_SCTRL_LOOP;
writel(val, gp->regs + PCS_SCTRL);
}
+
+ /* Default aneg parameters */
+ gp->timer_ticks = 0;
+ gp->lstate = link_down;
+ netif_carrier_off(gp->dev);
+
+ /* Can I advertise gigabit here ? I'd need BCM PHY docs... */
+ spin_lock_irq(&gp->lock);
+ gem_begin_auto_negotiation(gp, NULL);
+ spin_unlock_irq(&gp->lock);
}
/* Must be invoked under gp->lock and gp->tx_lock. */
@@ -1796,8 +1795,7 @@
}
/* Must be invoked under gp->lock and gp->tx_lock. */
-static u32
-gem_setup_multicast(struct gem *gp)
+static u32 gem_setup_multicast(struct gem *gp)
{
u32 rxcfg = 0;
int i;
@@ -1914,6 +1912,10 @@
* make no use of those events other than to record them.
*/
writel(0xffffffff, gp->regs + MAC_MCMASK);
+
+ /* Don't enable GEM's WOL in normal operations
+ */
+ writel(0, gp->regs + WOL_WAKECSR);
}
/* Must be invoked under gp->lock and gp->tx_lock. */
@@ -1975,6 +1977,23 @@
gp->tx_fifo_sz = readl(gp->regs + TXDMA_FSZ) * 64;
gp->rx_fifo_sz = readl(gp->regs + RXDMA_FSZ) * 64;
gp->swrst_base = 0;
+
+ mif_cfg = readl(gp->regs + MIF_CFG);
+ mif_cfg &= ~(MIF_CFG_PSELECT|MIF_CFG_POLL|MIF_CFG_BBMODE|MIF_CFG_MDI1);
+ mif_cfg |= MIF_CFG_MDI0;
+ writel(mif_cfg, gp->regs + MIF_CFG);
+ writel(PCS_DMODE_MGM, gp->regs + PCS_DMODE);
+ writel(MAC_XIFCFG_OE, gp->regs + MAC_XIFCFG);
+
+ /* We hard-code the PHY address so we can properly bring it out of
+ * reset later on, we can't really probe it at this point, though
+ * that isn't an issue.
+ */
+ if (gp->pdev->device == PCI_DEVICE_ID_APPLE_K2_GMAC)
+ gp->mii_phy_addr = 1;
+ else
+ gp->mii_phy_addr = 0;
+
return 0;
}
@@ -2053,71 +2072,30 @@
}
/* Must be invoked under gp->lock and gp->tx_lock. */
-static void gem_init_hw(struct gem *gp, int restart_link)
+static void gem_reinit_chip(struct gem *gp)
{
- /* On Apple's gmac, I initialize the PHY only after
- * setting up the chip. It appears the gigabit PHYs
- * don't quite like beeing talked to on the GII when
- * the chip is not running, I suspect it might not
- * be clocked at that point. --BenH
- */
- if (restart_link)
- gem_init_phy(gp);
- gem_init_pause_thresholds(gp);
- gem_init_dma(gp);
- gem_init_mac(gp);
-
- if (restart_link) {
- /* Default aneg parameters */
- gp->timer_ticks = 0;
- gp->lstate = link_down;
- netif_carrier_off(gp->dev);
+ /* Reset the chip */
+ gem_reset(gp);
- /* Can I advertise gigabit here ? I'd need BCM PHY docs... */
- gem_begin_auto_negotiation(gp, NULL);
- } else {
- if (gp->lstate == link_up) {
- netif_carrier_on(gp->dev);
- gem_set_link_modes(gp);
- }
- }
-}
+ /* Make sure ints are disabled */
+ gem_disable_ints(gp);
-#ifdef CONFIG_PPC_PMAC
-/* Enable the chip's clock and make sure it's config space is
- * setup properly. There appear to be no need to restore the
- * base addresses.
- */
-static void gem_apple_powerup(struct gem *gp)
-{
- u32 mif_cfg;
+ /* Allocate & setup ring buffers */
+ gem_init_rings(gp);
- mb();
- pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 1);
+ /* Configure pause thresholds */
+ gem_init_pause_thresholds(gp);
- udelay(10);
- pci_enable_device(gp->pdev);
- pci_set_master(gp->pdev);
+ /* Configure pause thresholds */
+ gem_init_pause_thresholds(gp);
- mif_cfg = readl(gp->regs + MIF_CFG);
- mif_cfg &= ~(MIF_CFG_PSELECT|MIF_CFG_POLL|MIF_CFG_BBMODE|MIF_CFG_MDI1);
- mif_cfg |= MIF_CFG_MDI0;
- writel(mif_cfg, gp->regs + MIF_CFG);
- writel(PCS_DMODE_MGM, gp->regs + PCS_DMODE);
- writel(MAC_XIFCFG_OE, gp->regs + MAC_XIFCFG);
-}
-
-/* Turn off the chip's clock */
-static void gem_apple_powerdown(struct gem *gp)
-{
- pci_disable_device(gp->pdev);
- pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 0);
+ /* Init DMA & MAC engines */
+ gem_init_dma(gp);
+ gem_init_mac(gp);
}
-#endif /* CONFIG_PPC_PMAC */
-
/* Must be invoked with no lock held. */
-static void gem_stop_phy(struct gem *gp)
+static void gem_stop_phy(struct gem *gp, int wol)
{
u32 mifcfg;
unsigned long flags;
@@ -2134,8 +2112,22 @@
mifcfg &= ~MIF_CFG_POLL;
writel(mifcfg, gp->regs + MIF_CFG);
- if (gp->wake_on_lan) {
- /* Setup wake-on-lan */
+ if (wol) {
+ unsigned char *e = &gp->dev->dev_addr[0];
+ u32 csr;
+
+ /* Setup wake-on-lan for MAGIC packet */
+ writel(MAC_RXCFG_HFE | MAC_RXCFG_SFCS | MAC_RXCFG_ENAB,
+ gp->regs + MAC_RXCFG);
+ writel((e[4] << 8) | e[5], gp->regs + WOL_MATCH0);
+ writel((e[2] << 8) | e[3], gp->regs + WOL_MATCH1);
+ writel((e[0] << 8) | e[1], gp->regs + WOL_MATCH2);
+
+ writel(WOL_MCOUNT_N | WOL_MCOUNT_M, gp->regs + WOL_MCOUNT);
+ csr = WOL_WAKECSR_ENABLE;
+ if ((readl(gp->regs + MAC_XIFCFG) & MAC_XIFCFG_GMII) == 0)
+ csr |= WOL_WAKECSR_MII;
+ writel(csr, gp->regs + WOL_WAKECSR);
} else {
writel(0, gp->regs + MAC_RXCFG);
(void)readl(gp->regs + MAC_RXCFG);
@@ -2151,20 +2143,21 @@
writel(0, gp->regs + TXDMA_CFG);
writel(0, gp->regs + RXDMA_CFG);
- if (!gp->wake_on_lan) {
+ /* No need to take the lock here */
+ if (!wol) {
spin_lock_irqsave(&gp->lock, flags);
spin_lock(&gp->tx_lock);
- gem_stop(gp);
+ gem_reset(gp);
writel(MAC_TXRST_CMD, gp->regs + MAC_TXRST);
writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST);
spin_unlock(&gp->tx_lock);
spin_unlock_irqrestore(&gp->lock, flags);
}
- if (found_mii_phy(gp) && gp->phy_mii.def->ops->suspend)
- gp->phy_mii.def->ops->suspend(&gp->phy_mii, 0 /* wake on lan options */);
+ if (!wol && found_mii_phy(gp) && gp->phy_mii.def->ops->suspend)
+ gp->phy_mii.def->ops->suspend(&gp->phy_mii);
- if (!gp->wake_on_lan) {
+ if (!wol) {
/* According to Apple, we must set the MDIO pins to this begnign
* state or we may 1) eat more current, 2) damage some PHYs
*/
@@ -2177,181 +2170,160 @@
}
}
-/* Shut down the chip, must be called with pm_sem held. */
-static void gem_shutdown(struct gem *gp)
+
+static int gem_do_start(struct net_device *dev)
{
- /* Make us not-running to avoid timers respawning
- * and swallow irqs
- */
- gp->hw_running = 0;
- wmb();
+ struct gem *gp = dev->priv;
+ unsigned long flags;
- /* Stop the link timer */
- del_timer_sync(&gp->link_timer);
+ spin_lock_irqsave(&gp->lock, flags);
+ spin_lock(&gp->tx_lock);
- /* Stop the reset task */
- while (gp->reset_task_pending)
- yield();
-
- /* Actually stop the chip */
- if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
- gem_stop_phy(gp);
+ /* Enable the cell */
+ gem_get_cell(gp);
-#ifdef CONFIG_PPC_PMAC
- /* Power down the chip */
- gem_apple_powerdown(gp);
-#endif /* CONFIG_PPC_PMAC */
- } else{
- unsigned long flags;
+ /* Init & setup chip hardware */
+ gem_reinit_chip(gp);
+
+ gp->running = 1;
+
+ if (gp->lstate == link_up) {
+ netif_carrier_on(gp->dev);
+ gem_set_link_modes(gp);
+ }
+
+ netif_wake_queue(gp->dev);
+
+ spin_unlock(&gp->tx_lock);
+ spin_unlock_irqrestore(&gp->lock, flags);
+
+ if (request_irq(gp->pdev->irq, gem_interrupt,
+ SA_SHIRQ, dev->name, (void *)dev)) {
+ printk(KERN_ERR "%s: failed to request irq !\n", gp->dev->name);
spin_lock_irqsave(&gp->lock, flags);
spin_lock(&gp->tx_lock);
- gem_stop(gp);
+
+ gp->running = 0;
+ gem_reset(gp);
+ gem_clean_rings(gp);
+ gem_put_cell(gp);
+
spin_unlock(&gp->tx_lock);
spin_unlock_irqrestore(&gp->lock, flags);
+
+ return -EAGAIN;
}
+
+ return 0;
}
-static void gem_pm_task(void *data)
+static void gem_do_stop(struct net_device *dev, int wol)
{
- struct gem *gp = (struct gem *) data;
+ struct gem *gp = dev->priv;
+ unsigned long flags;
- /* We assume if we can't lock the pm_sem, then open() was
- * called again (or suspend()), and we can safely ignore
- * the PM request
- */
- if (down_trylock(&gp->pm_sem))
- return;
+ spin_lock_irqsave(&gp->lock, flags);
+ spin_lock(&gp->tx_lock);
- /* Driver was re-opened or already shut down */
- if (gp->opened || !gp->hw_running) {
- up(&gp->pm_sem);
- return;
- }
+ gp->running = 0;
- gem_shutdown(gp);
+ /* Stop netif queue */
+ netif_stop_queue(dev);
- up(&gp->pm_sem);
-}
+ /* Make sure ints are disabled */
+ gem_disable_ints(gp);
-static void gem_pm_timer(unsigned long data)
-{
- struct gem *gp = (struct gem *) data;
+ /* We can drop the lock now */
+ spin_unlock(&gp->tx_lock);
+ spin_unlock_irqrestore(&gp->lock, flags);
+
+ /* If we are going to sleep with WOL */
+ if (wol)
+ gem_stop_dma(gp);
+ else
+ gem_reset(gp);
+ msleep(10);
+
+ /* Get rid of rings */
+ gem_clean_rings(gp);
+
+ /* No irq needed anymore */
+ free_irq(gp->pdev->irq, (void *) dev);
- schedule_work(&gp->pm_task);
+ /* Cell not needed neither if no WOL */
+ if (!wol) {
+ spin_lock_irqsave(&gp->lock, flags);
+ gem_put_cell(gp);
+ spin_unlock_irqrestore(&gp->lock, flags);
+ }
}
-static int gem_open(struct net_device *dev)
+static void gem_reset_task(void *data)
{
- struct gem *gp = dev->priv;
- int hw_was_up;
+ struct gem *gp = (struct gem *) data;
down(&gp->pm_sem);
- hw_was_up = gp->hw_running;
+ netif_poll_disable(gp->dev);
- /* Stop the PM timer/task */
- del_timer(&gp->pm_timer);
- flush_scheduled_work();
+ spin_lock_irq(&gp->lock);
+ spin_lock(&gp->tx_lock);
- /* The power-management semaphore protects the hw_running
- * etc. state so it is safe to do this bit without gp->lock
- */
- if (!gp->hw_running) {
-#ifdef CONFIG_PPC_PMAC
- /* First, we need to bring up the chip */
- if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
- gem_apple_powerup(gp);
- gem_check_invariants(gp);
- }
-#endif /* CONFIG_PPC_PMAC */
+ if (gp->running == 0)
+ goto not_running;
- /* Reset the chip */
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
- gem_stop(gp);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
+ if (gp->running) {
+ netif_stop_queue(gp->dev);
- gp->hw_running = 1;
+ /* Reset the chip & rings */
+ gem_reinit_chip(gp);
+ if (gp->lstate == link_up)
+ gem_set_link_modes(gp);
+ netif_wake_queue(gp->dev);
}
+ not_running:
+ gp->reset_task_pending = 0;
- /* We can now request the interrupt as we know it's masked
- * on the controller
- */
- if (request_irq(gp->pdev->irq, gem_interrupt,
- SA_SHIRQ, dev->name, (void *)dev)) {
- printk(KERN_ERR "%s: failed to request irq !\n", gp->dev->name);
+ spin_unlock(&gp->tx_lock);
+ spin_unlock_irq(&gp->lock);
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
-#ifdef CONFIG_PPC_PMAC
- if (!hw_was_up && gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
- gem_apple_powerdown(gp);
-#endif /* CONFIG_PPC_PMAC */
- /* Fire the PM timer that will shut us down in about 10 seconds */
- gp->pm_timer.expires = jiffies + 10*HZ;
- add_timer(&gp->pm_timer);
- up(&gp->pm_sem);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
-
- return -EAGAIN;
- }
+ netif_poll_enable(gp->dev);
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
+ up(&gp->pm_sem);
+}
- /* Allocate & setup ring buffers */
- gem_init_rings(gp);
- /* Init & setup chip hardware */
- gem_init_hw(gp, !hw_was_up);
+static int gem_open(struct net_device *dev)
+{
+ struct gem *gp = dev->priv;
+ int rc = 0;
- gp->opened = 1;
+ down(&gp->pm_sem);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
+ /* We need the cell enabled */
+ if (!gp->asleep)
+ rc = gem_do_start(dev);
+ gp->opened = (rc == 0);
up(&gp->pm_sem);
- return 0;
+ return rc;
}
static int gem_close(struct net_device *dev)
{
struct gem *gp = dev->priv;
- /* Make sure we don't get distracted by suspend/resume */
- down(&gp->pm_sem);
-
/* Note: we don't need to call netif_poll_disable() here because
* our caller (dev_close) already did it for us
*/
- /* Stop traffic, mark us closed */
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
+ down(&gp->pm_sem);
gp->opened = 0;
-
- netif_stop_queue(dev);
-
- /* Stop chip */
- gem_stop(gp);
-
- /* Get rid of rings */
- gem_clean_rings(gp);
-
- /* Bye, the pm timer will finish the job */
- free_irq(gp->pdev->irq, (void *) dev);
-
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
-
- /* Fire the PM timer that will shut us down in about 10 seconds */
- gp->pm_timer.expires = jiffies + 10*HZ;
- add_timer(&gp->pm_timer);
+ if (!gp->asleep)
+ gem_do_stop(dev, 0);
up(&gp->pm_sem);
@@ -2363,45 +2335,63 @@
{
struct net_device *dev = pci_get_drvdata(pdev);
struct gem *gp = dev->priv;
+ unsigned long flags;
- netif_poll_disable(dev);
-
- /* We hold the PM semaphore during entire driver
- * sleep time
- */
down(&gp->pm_sem);
+ netif_poll_disable(dev);
+
printk(KERN_INFO "%s: suspending, WakeOnLan %s\n",
- dev->name, gp->wake_on_lan ? "enabled" : "disabled");
+ dev->name,
+ (gp->wake_on_lan && gp->opened) ? "enabled" : "disabled");
- /* If the driver is opened, we stop the DMA */
- if (gp->opened) {
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
+ /* Keep the cell enabled during the entire operation */
+ spin_lock_irqsave(&gp->lock, flags);
+ spin_lock(&gp->tx_lock);
+ gem_get_cell(gp);
+ spin_unlock(&gp->tx_lock);
+ spin_unlock_irqrestore(&gp->lock, flags);
+ /* If the driver is opened, we stop the MAC */
+ if (gp->opened) {
/* Stop traffic, mark us closed */
netif_device_detach(dev);
- /* Stop chip */
- gem_stop(gp);
+ /* Switch off MAC, remember WOL setting */
+ gp->asleep_wol = gp->wake_on_lan;
+ gem_do_stop(dev, gp->asleep_wol);
+ msleep(10);
+ } else
+ gp->asleep_wol = 0;
- /* Get rid of ring buffers */
- gem_clean_rings(gp);
+ /* Mark us asleep */
+ gp->asleep = 1;
+ wmb();
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
+ /* Stop the link timer */
+ del_timer_sync(&gp->link_timer);
- if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
- disable_irq(gp->pdev->irq);
- }
+ /* Now we release the semaphore to not block the reset task who
+ * can take it too. We are marked asleep, so there will be no
+ * conflict here
+ */
+ up(&gp->pm_sem);
- if (gp->hw_running) {
- /* Kill PM timer if any */
- del_timer_sync(&gp->pm_timer);
- flush_scheduled_work();
+ /* Wait for a pending reset task to complete */
+ while (gp->reset_task_pending)
+ yield();
+ flush_scheduled_work();
- gem_shutdown(gp);
- }
+ /* Shut the PHY down eventually and setup WOL */
+ gem_stop_phy(gp, gp->asleep_wol);
+
+ /* Make sure bus master is disabled */
+ pci_disable_device(gp->pdev);
+
+ /* Release the cell, no need to take a lock at this point since
+ * nothing else can happen now
+ */
+ gem_put_cell(gp);
return 0;
}
@@ -2410,36 +2400,74 @@
{
struct net_device *dev = pci_get_drvdata(pdev);
struct gem *gp = dev->priv;
+ unsigned long flags;
printk(KERN_INFO "%s: resuming\n", dev->name);
- if (gp->opened) {
-#ifdef CONFIG_PPC_PMAC
- /* First, we need to bring up the chip */
- if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
- gem_apple_powerup(gp);
- gem_check_invariants(gp);
- }
-#endif /* CONFIG_PPC_PMAC */
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
+ down(&gp->pm_sem);
- gem_stop(gp);
- gp->hw_running = 1;
- gem_init_rings(gp);
- gem_init_hw(gp, 1);
+ /* Keep the cell enabled during the entire operation, no need to
+ * take a lock here tho since nothing else can happen while we are
+ * marked asleep
+ */
+ gem_get_cell(gp);
+
+ /* Make sure PCI access and bus master are enabled */
+ if (pci_enable_device(gp->pdev)) {
+ printk(KERN_ERR "%s: Can't re-enable chip !\n",
+ dev->name);
+ /* Put cell and forget it for now, it will be considered as
+ * still asleep, a new sleep cycle may bring it back
+ */
+ gem_put_cell(gp);
+ up(&gp->pm_sem);
+ return 0;
+ }
+ pci_set_master(gp->pdev);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
+ /* Reset everything */
+ gem_reset(gp);
+ /* Mark us woken up */
+ gp->asleep = 0;
+ wmb();
+
+ /* Bring the PHY back. Again, lock is useless at this point as
+ * nothing can be happening until we restart the whole thing
+ */
+ gem_init_phy(gp);
+
+ /* If we were opened, bring everything back */
+ if (gp->opened) {
+ /* Restart MAC */
+ gem_do_start(dev);
+
+ /* Re-attach net device */
netif_device_attach(dev);
- if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
- enable_irq(gp->pdev->irq);
+
}
- up(&gp->pm_sem);
+
+ spin_lock_irqsave(&gp->lock, flags);
+ spin_lock(&gp->tx_lock);
+
+ /* If we had WOL enabled, the cell clock was never turned off during
+ * sleep, so we end up beeing unbalanced. Fix that here
+ */
+ if (gp->asleep_wol)
+ gem_put_cell(gp);
+
+ /* This function doesn't need to hold the cell, it will be held if the
+ * driver is open by gem_do_start().
+ */
+ gem_put_cell(gp);
+
+ spin_unlock(&gp->tx_lock);
+ spin_unlock_irqrestore(&gp->lock, flags);
netif_poll_enable(dev);
+ up(&gp->pm_sem);
+
return 0;
}
#endif /* CONFIG_PM */
@@ -2452,7 +2480,10 @@
spin_lock_irq(&gp->lock);
spin_lock(&gp->tx_lock);
- if (gp->hw_running) {
+ /* I have seen this being called while the PM was in progress,
+ * so we shield against this
+ */
+ if (gp->running) {
stats->rx_crc_errors += readl(gp->regs + MAC_FCSERR);
writel(0, gp->regs + MAC_FCSERR);
@@ -2482,12 +2513,12 @@
u32 rxcfg, rxcfg_new;
int limit = 10000;
- if (!gp->hw_running)
- return;
-
spin_lock_irq(&gp->lock);
spin_lock(&gp->tx_lock);
+ if (!gp->running)
+ goto bail;
+
netif_stop_queue(dev);
rxcfg = readl(gp->regs + MAC_RXCFG);
@@ -2510,9 +2541,46 @@
writel(rxcfg, gp->regs + MAC_RXCFG);
netif_wake_queue(dev);
+ bail:
+ spin_unlock(&gp->tx_lock);
+ spin_unlock_irq(&gp->lock);
+}
+
+/* Jumbo-grams don't seem to work :-( */
+#define GEM_MIN_MTU 68
+#if 1
+#define GEM_MAX_MTU 1500
+#else
+#define GEM_MAX_MTU 9000
+#endif
+
+static int gem_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct gem *gp = dev->priv;
+ if (new_mtu < GEM_MIN_MTU || new_mtu > GEM_MAX_MTU)
+ return -EINVAL;
+
+ if (!netif_running(dev) || !netif_device_present(dev)) {
+ /* We'll just catch it later when the
+ * device is up'd or resumed.
+ */
+ dev->mtu = new_mtu;
+ return 0;
+ }
+
+ spin_lock_irq(&gp->lock);
+ spin_lock(&gp->tx_lock);
+ dev->mtu = new_mtu;
+ if (gp->running) {
+ gem_reinit_chip(gp);
+ if (gp->lstate == link_up)
+ gem_set_link_modes(gp);
+ }
spin_unlock(&gp->tx_lock);
spin_unlock_irq(&gp->lock);
+
+ return 0;
}
static void gem_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
@@ -2543,7 +2611,6 @@
/* Return current PHY settings */
spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
cmd->autoneg = gp->want_autoneg;
cmd->speed = gp->phy_mii.speed;
cmd->duplex = gp->phy_mii.duplex;
@@ -2555,7 +2622,6 @@
*/
if (cmd->advertising == 0)
cmd->advertising = cmd->supported;
- spin_unlock(&gp->tx_lock);
spin_unlock_irq(&gp->lock);
} else { // XXX PCS ?
cmd->supported =
@@ -2595,9 +2661,9 @@
/* Apply settings and restart link process. */
spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
+ gem_get_cell(gp);
gem_begin_auto_negotiation(gp, cmd);
- spin_unlock(&gp->tx_lock);
+ gem_put_cell(gp);
spin_unlock_irq(&gp->lock);
return 0;
@@ -2612,9 +2678,9 @@
/* Restart link process. */
spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
+ gem_get_cell(gp);
gem_begin_auto_negotiation(gp, NULL);
- spin_unlock(&gp->tx_lock);
+ gem_put_cell(gp);
spin_unlock_irq(&gp->lock);
return 0;
@@ -2631,7 +2697,31 @@
struct gem *gp = dev->priv;
gp->msg_enable = value;
}
-
+
+
+/* Add more when I understand how to program the chip */
+/* like WAKE_UCAST | WAKE_MCAST | WAKE_BCAST */
+
+#define WOL_SUPPORTED_MASK (WAKE_MAGIC)
+
+static void gem_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct gem *gp = dev->priv;
+
+ /* Add more when I understand how to program the chip */
+ wol->supported = WOL_SUPPORTED_MASK;
+ wol->wolopts = gp->wake_on_lan;
+}
+
+static int gem_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct gem *gp = dev->priv;
+
+ gp->wake_on_lan = wol->wolopts & WOL_SUPPORTED_MASK;
+
+ return 0;
+}
+
static struct ethtool_ops gem_ethtool_ops = {
.get_drvinfo = gem_get_drvinfo,
.get_link = ethtool_op_get_link,
@@ -2640,6 +2730,8 @@
.nway_reset = gem_nway_reset,
.get_msglevel = gem_get_msglevel,
.set_msglevel = gem_set_msglevel,
+ .get_wol = gem_get_wol,
+ .set_wol = gem_set_wol,
};
static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
@@ -2647,22 +2739,28 @@
struct gem *gp = dev->priv;
struct mii_ioctl_data *data = if_mii(ifr);
int rc = -EOPNOTSUPP;
-
+ unsigned long flags;
+
/* Hold the PM semaphore while doing ioctl's or we may collide
- * with open/close and power management and oops.
+ * with power management.
*/
down(&gp->pm_sem);
-
+
+ spin_lock_irqsave(&gp->lock, flags);
+ gem_get_cell(gp);
+ spin_unlock_irqrestore(&gp->lock, flags);
+
switch (cmd) {
case SIOCGMIIPHY: /* Get address of MII PHY in use. */
data->phy_id = gp->mii_phy_addr;
/* Fallthrough... */
case SIOCGMIIREG: /* Read MII PHY register. */
- if (!gp->hw_running)
- rc = -EIO;
+ if (!gp->running)
+ rc = -EAGAIN;
else {
- data->val_out = __phy_read(gp, data->phy_id & 0x1f, data->reg_num & 0x1f);
+ data->val_out = __phy_read(gp, data->phy_id & 0x1f,
+ data->reg_num & 0x1f);
rc = 0;
}
break;
@@ -2670,14 +2768,19 @@
case SIOCSMIIREG: /* Write MII PHY register. */
if (!capable(CAP_NET_ADMIN))
rc = -EPERM;
- else if (!gp->hw_running)
- rc = -EIO;
+ else if (!gp->running)
+ rc = -EAGAIN;
else {
- __phy_write(gp, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
+ __phy_write(gp, data->phy_id & 0x1f, data->reg_num & 0x1f,
+ data->val_in);
rc = 0;
}
break;
};
+
+ spin_lock_irqsave(&gp->lock, flags);
+ gem_put_cell(gp);
+ spin_unlock_irqrestore(&gp->lock, flags);
up(&gp->pm_sem);
@@ -2782,6 +2885,47 @@
return 0;
}
+static void __devexit gem_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ if (dev) {
+ struct gem *gp = dev->priv;
+
+ unregister_netdev(dev);
+
+ /* Stop the link timer */
+ del_timer_sync(&gp->link_timer);
+
+ /* We shouldn't need any locking here */
+ gem_get_cell(gp);
+
+ /* Wait for a pending reset task to complete */
+ while (gp->reset_task_pending)
+ yield();
+ flush_scheduled_work();
+
+ /* Shut the PHY down */
+ gem_stop_phy(gp, 0);
+
+ gem_put_cell(gp);
+
+ /* Make sure bus master is disabled */
+ pci_disable_device(gp->pdev);
+
+ /* Free resources */
+ pci_free_consistent(pdev,
+ sizeof(struct gem_init_block),
+ gp->init_block,
+ gp->gblock_dvma);
+ iounmap(gp->regs);
+ pci_release_regions(pdev);
+ free_netdev(dev);
+
+ pci_set_drvdata(pdev, NULL);
+ }
+}
+
static int __devinit gem_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -2873,11 +3017,6 @@
gp->link_timer.function = gem_link_timer;
gp->link_timer.data = (unsigned long) gp;
- init_timer(&gp->pm_timer);
- gp->pm_timer.function = gem_pm_timer;
- gp->pm_timer.data = (unsigned long) gp;
-
- INIT_WORK(&gp->pm_task, gem_pm_task, gp);
INIT_WORK(&gp->reset_task, gem_reset_task, gp);
gp->lstate = link_down;
@@ -2893,19 +3032,18 @@
}
/* On Apple, we power the chip up now in order for check
- * invariants to work, but also because the firmware might
- * not have properly shut down the PHY.
+ * invariants to work. We also set some registers that are
+ * normally
*/
#ifdef CONFIG_PPC_PMAC
gp->of_node = pci_device_to_OF_node(pdev);
- if (pdev->vendor == PCI_VENDOR_ID_APPLE)
- gem_apple_powerup(gp);
#endif
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
- gem_stop(gp);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
+
+ /* Make sure cell is enabled */
+ gem_get_cell(gp);
+
+ /* Make sure everything is stopped and in init state */
+ gem_reset(gp);
/* Fill up the mii_phy structure (even if we won't use it) */
gp->phy_mii.dev = dev;
@@ -2914,7 +3052,8 @@
/* By default, we start with autoneg */
gp->want_autoneg = 1;
-
+
+ /* Check fifo sizes, PHY type, etc... */
if (gem_check_invariants(gp)) {
err = -ENODEV;
goto err_out_iounmap;
@@ -2954,6 +3093,19 @@
dev->poll_controller = gem_poll_controller;
#endif
+ /* Set that now, in case PM kicks in now */
+ pci_set_drvdata(pdev, dev);
+
+ /* Detect & init PHY, start autoneg, we release the cell now
+ * too, it will be managed by whoever needs it
+ */
+ gem_init_phy(gp);
+
+ spin_lock_irq(&gp->lock);
+ gem_put_cell(gp);
+ spin_unlock_irq(&gp->lock);
+
+ /* Register with kernel */
if (register_netdev(dev)) {
printk(KERN_ERR PFX "Cannot register net device, "
"aborting.\n");
@@ -2968,48 +3120,22 @@
i == 5 ? ' ' : ':');
printk("\n");
- /* Detect & init PHY, start autoneg */
- spin_lock_irq(&gp->lock);
- spin_lock(&gp->tx_lock);
- gp->hw_running = 1;
- gem_init_phy(gp);
- gem_begin_auto_negotiation(gp, NULL);
- spin_unlock(&gp->tx_lock);
- spin_unlock_irq(&gp->lock);
-
if (gp->phy_type == phy_mii_mdio0 ||
gp->phy_type == phy_mii_mdio1)
printk(KERN_INFO "%s: Found %s PHY\n", dev->name,
gp->phy_mii.def ? gp->phy_mii.def->name : "no");
- pci_set_drvdata(pdev, dev);
-
/* GEM can do it all... */
dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_LLTX;
if (pci_using_dac)
dev->features |= NETIF_F_HIGHDMA;
- /* Fire the PM timer that will shut us down in about 10 seconds */
- gp->pm_timer.expires = jiffies + 10*HZ;
- add_timer(&gp->pm_timer);
-
return 0;
err_out_free_consistent:
- pci_free_consistent(pdev,
- sizeof(struct gem_init_block),
- gp->init_block,
- gp->gblock_dvma);
-
+ gem_remove_one(pdev);
err_out_iounmap:
- down(&gp->pm_sem);
- /* Stop the PM timer & task */
- del_timer_sync(&gp->pm_timer);
- flush_scheduled_work();
- if (gp->hw_running)
- gem_shutdown(gp);
- up(&gp->pm_sem);
-
+ gem_put_cell(gp);
iounmap(gp->regs);
err_out_free_res:
@@ -3023,34 +3149,6 @@
}
-static void __devexit gem_remove_one(struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata(pdev);
-
- if (dev) {
- struct gem *gp = dev->priv;
-
- unregister_netdev(dev);
-
- down(&gp->pm_sem);
- /* Stop the PM timer & task */
- del_timer_sync(&gp->pm_timer);
- flush_scheduled_work();
- if (gp->hw_running)
- gem_shutdown(gp);
- up(&gp->pm_sem);
-
- pci_free_consistent(pdev,
- sizeof(struct gem_init_block),
- gp->init_block,
- gp->gblock_dvma);
- iounmap(gp->regs);
- pci_release_regions(pdev);
- free_netdev(dev);
-
- pci_set_drvdata(pdev, NULL);
- }
-}
static struct pci_driver gem_driver = {
.name = GEM_MODULE_NAME,
diff -ur a/drivers/net/sungem.h b/drivers/net/sungem.h
--- a/drivers/net/sungem.h 2004-10-18 23:54:32.000000000 +0200
+++ b/drivers/net/sungem.h 2004-11-26 17:36:42.000000000 +0100
@@ -170,6 +170,27 @@
* them later. -DaveM
*/
+/* WakeOnLan Registers */
+#define WOL_MATCH0 0x3000UL
+#define WOL_MATCH1 0x3004UL
+#define WOL_MATCH2 0x3008UL
+#define WOL_MCOUNT 0x300CUL
+#define WOL_WAKECSR 0x3010UL
+
+/* WOL Match count register
+ */
+#define WOL_MCOUNT_N 0x00000010
+#define WOL_MCOUNT_M 0x00000000 /* 0 << 8 */
+
+#define WOL_WAKECSR_ENABLE 0x00000001
+#define WOL_WAKECSR_MII 0x00000002
+#define WOL_WAKECSR_SEEN 0x00000004
+#define WOL_WAKECSR_FILT_UCAST 0x00000008
+#define WOL_WAKECSR_FILT_MCAST 0x00000010
+#define WOL_WAKECSR_FILT_BCAST 0x00000020
+#define WOL_WAKECSR_FILT_SEEN 0x00000040
+
+
/* Receive DMA Registers */
#define RXDMA_CFG 0x4000UL /* RX Configuration Register */
#define RXDMA_DBLOW 0x4004UL /* RX Descriptor Base Low */
@@ -276,6 +297,9 @@
*/
/* MAC Registers */
+#define MAC_WOL_MAGIC0 0x3000UL /* six address bytes */
+#define MAC_WOL_MAGIC1 0x3004UL
+#define MAC_WOL_MAGIC2 0x3008UL
#define MAC_TXRST 0x6000UL /* TX MAC Software Reset Command*/
#define MAC_RXRST 0x6004UL /* RX MAC Software Reset Command*/
#define MAC_SNDPAUSE 0x6008UL /* Send Pause Command Register */
@@ -750,6 +774,17 @@
#define PCS_SCTRL_TXZ 0x0000c000 /* PLL input to Serialink */
#define PCS_SCTRL_TXP 0x00030000 /* PLL input to Serialink */
+/* Pattern Match Count register. */
+#define PAT_MATCH_CFG 0x300CUL
+#define PAT_MATCH_N 0x0010UL
+#define PAT_MATCH_M 0x0000UL /* Darwin source notes (0<<8) !?*/
+
+/* Wake on Lan CSR Register */
+#define WOL_CSR_CFG 0x3010UL
+#define WOL_ENABLE 0x0001UL
+#define WOL_MODE_MII 0x0002UL
+
+
/* Shared Output Select Register. For test and debug, allows multiplexing
* test outputs into the PROM address pins. Set to zero for normal
* operation.
@@ -961,11 +996,12 @@
/* Set when chip is actually in operational state
* (ie. not power managed)
*/
- int hw_running;
- int opened;
+ int asleep; /* chip asleep, protected by pm_sem */
+ int asleep_wol; /* was asleep with WOL enabled */
+ int cell_enabled; /* cell enable count, protected by lock */
+ int opened; /* driver opened, protected by pm_sem */
+ int running; /* chip running, protected by lock */
struct semaphore pm_sem;
- struct work_struct pm_task;
- struct timer_list pm_timer;
struct gem_init_block *init_block;
diff -ur a/drivers/net/sungem_phy.c b/drivers/net/sungem_phy.c
--- a/drivers/net/sungem_phy.c 2004-11-26 20:09:44.801523912 +0100
+++ b/drivers/net/sungem_phy.c 2004-11-26 17:49:31.000000000 +0100
@@ -98,25 +98,15 @@
data &= ~MII_BCM5201_MULTIPHY_SUPERISOLATE;
phy_write(phy, MII_BCM5201_MULTIPHY, data);
+ phy_write(phy, MII_BCM5201_INTERRUPT, 0);
+
return 0;
}
-static int bcm5201_suspend(struct mii_phy* phy, int wol_options)
+static int bcm5201_suspend(struct mii_phy* phy)
{
- if (!wol_options)
- phy_write(phy, MII_BCM5201_INTERRUPT, 0);
-
- /* Here's a strange hack used by both MacOS 9 and X */
- phy_write(phy, MII_LPA, phy_read(phy, MII_LPA));
-
- if (!wol_options) {
-#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
- u16 val = phy_read(phy, MII_BCM5201_AUXMODE2)
- phy_write(phy, MII_BCM5201_AUXMODE2,
- val & ~MII_BCM5201_AUXMODE2_LOWPOWER);
-#endif
- phy_write(phy, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE);
- }
+ phy_write(phy, MII_BCM5201_INTERRUPT, 0);
+ phy_write(phy, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE);
return 0;
}
@@ -144,9 +134,10 @@
return 0;
}
-static int bcm5221_suspend(struct mii_phy* phy, int wol_options)
+static int bcm5221_suspend(struct mii_phy* phy)
{
u16 data;
+
data = phy_read(phy, MII_BCM5221_TEST);
phy_write(phy, MII_BCM5221_TEST,
data | MII_BCM5221_TEST_ENABLE_SHADOWS);
@@ -187,7 +178,7 @@
return 0;
}
-static int bcm5400_suspend(struct mii_phy* phy, int wol_options)
+static int bcm5400_suspend(struct mii_phy* phy)
{
#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
phy_write(phy, MII_BMCR, BMCR_PDOWN);
@@ -243,7 +234,7 @@
return 0;
}
-static int bcm5401_suspend(struct mii_phy* phy, int wol_options)
+static int bcm5401_suspend(struct mii_phy* phy)
{
#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
phy_write(phy, MII_BMCR, BMCR_PDOWN);
@@ -280,7 +271,7 @@
return 0;
}
-static int bcm5411_suspend(struct mii_phy* phy, int wol_options)
+static int bcm5411_suspend(struct mii_phy* phy)
{
phy_write(phy, MII_BMCR, BMCR_PDOWN);
diff -ur a/drivers/net/sungem_phy.h b/drivers/net/sungem_phy.h
--- a/drivers/net/sungem_phy.h 2004-11-26 20:09:44.802523760 +0100
+++ b/drivers/net/sungem_phy.h 2004-11-26 17:36:42.000000000 +0100
@@ -7,7 +7,7 @@
struct mii_phy_ops
{
int (*init)(struct mii_phy *phy);
- int (*suspend)(struct mii_phy *phy, int wol_options);
+ int (*suspend)(struct mii_phy *phy);
int (*setup_aneg)(struct mii_phy *phy, u32 advertise);
int (*setup_forced)(struct mii_phy *phy, int speed, int fd);
int (*poll_link)(struct mii_phy *phy);
^ permalink raw reply
* Re: TEST: Sleep patch #5
From: Colin Leroy @ 2004-11-27 11:31 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1101280145.4511.4.camel@gaston>
On 24 Nov 2004 at 18h11, Benjamin Herrenschmidt wrote:
Hi Ben,
> Ok, here's the 5th version of the sleep patch for ATI based albooks &
> iBook G4. Other machine users, please test too as it may cause
> regressions (or improvements) as well.
noticed glxgears down to approx. 200fps. It used to be around 1500-1800,
xorg didn't change. It is also slower just after boot, no need to go to
sleep.
Any idea about that?
--
Colin
"The probability of someone watching you is proportional to the
stupidity of your action."
^ permalink raw reply
* Re: G3 iBook LCD brightness in X under kernel 2.4.25 and 2.6.8
From: Frank Murphy @ 2004-11-27 8:48 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: debian-x, linuxppc-dev list
In-Reply-To: <1101517373.4668.2.camel@gaston>
On Saturday 27 November 2004 2:02, Benjamin Herrenschmidt wrote:
> On Fri, 2004-11-26 at 11:28 +0100, Christof Petig wrote:
> > > I'm running pbbuttonsd 0.6.6-2 with gtkpbbuttons 0.6.4-3 (which are
> > > tools that enable the LCD to be dimmed and brightened with keyboard
> > > pressed). When I'm running the Debian kernel-image 2.4.25-8 and I press
> > > the brightness up or down keys, I get the on-screen indication from
> > > gtkpbbuttons and the LCD dims or brightens as expected in both X and on
> > > the console. However, when I boot into kernel-image 2.6.8-6 or 2.6.9-1
> > > and press the brightness keys, I get the on-screen indication in X, but
> > > the LCD doesn't change brightness. However, if I switch to a VT with a
> > > console, the brightness changes to what I tried to change it to. Also,
> > > pressing the brightness buttons works fine when I press them while in
> > > the console.
>
> What machine model precisely ? If it's an iBook, only the very first one
> had a mach64 based chip and indeed used atyfb-based brightness control,
> that might have been broken...
It's a first generation iBook SE (Special Edition). The SE means that it came
with a 6GB HD, more RAM, and a slightly faster CPU (366MHz), but it does have
a Mach64.
> > > My video card is a Mach64, and I'm using the 'ati' driver for X. One
> > > guess I have is that in 2.6, lots more things are built as kernel
> > > modules. I'd like to try build any needed video stuff in, but I don't
> > > know what it could be. Does anyone here have an idea what controls
> > > screen brightness in X?
>
> Some earlier models have PMU-controlled brightness, independently on the
> video card...
So I don't think it's an "early model" because it came out later than the
fruit-colored iBooks (it's grey and white, not <color>-berry like the real
first gen iBooks).
> > Your mouse pointer might get some noisy spots on brightness change, ever
> > noticed? I would blame the kernel first (since 2.4 works well).
>
> Ah ? Haven't noticed that ? what machine ?
>
> > > The reason this is important for me is that the LCD is not dimmed when
> > > I close the lid for sleep, which kind of ruins the point of sleep.
> > >
> > > Does anyone have an idea of what else I need to do to get screen
> > > dimming to work under X with Linux 2.6?
>
> You may be missing the atyfb kernel driver.
So I've tried this using the stock Debian 2.6.8 and 2.6.9 PPC kernels with the
same results.
$ grep ATY /boot/config-2.6.9-powerpc
CONFIG_FB_ATY128=y
CONFIG_FB_ATY=y
CONFIG_FB_ATY_CT=y
CONFIG_FB_ATY_GX=y
# CONFIG_FB_ATY_XL_INIT is not set
It seems that the atyfb module is compiled in. Is there another way to check?
Frank
^ permalink raw reply
* A new good Linux Kernel Webpage
From: Sam Song @ 2004-11-27 5:41 UTC (permalink / raw)
To: linuxppc-embedded
Hi, all,
I got the nice webpage from a forum. Just CC here for
reference.
http://www.win.tue.nl/~aeb/linux/lk/lk.html
=====
Best regards,
Sam
_________________________________________________________
Do You Yahoo!?
150万曲MP3疯狂搜,带您闯入音乐殿堂
http://music.yisou.com/
美女明星应有尽有,搜遍美图、艳图和酷图
http://image.yisou.com
1G就是1000兆,雅虎电邮自助扩容!
http://cn.rd.yahoo.com/mail_cn/tag/1g/*http://cn.mail.yahoo.com/event/mail_1g/
^ permalink raw reply
* Re: G3 iBook LCD brightness in X under kernel 2.4.25 and 2.6.8
From: Benjamin Herrenschmidt @ 2004-11-27 1:02 UTC (permalink / raw)
To: Christof Petig; +Cc: linuxppc-dev list, Frank Murphy, debian-x
In-Reply-To: <41A70567.8050000@petig-baender.de>
On Fri, 2004-11-26 at 11:28 +0100, Christof Petig wrote:
> > I'm running pbbuttonsd 0.6.6-2 with gtkpbbuttons 0.6.4-3 (which are tools that
> > enable the LCD to be dimmed and brightened with keyboard pressed). When I'm
> > running the Debian kernel-image 2.4.25-8 and I press the brightness up or
> > down keys, I get the on-screen indication from gtkpbbuttons and the LCD dims
> > or brightens as expected in both X and on the console. However, when I boot
> > into kernel-image 2.6.8-6 or 2.6.9-1 and press the brightness keys, I get the
> > on-screen indication in X, but the LCD doesn't change brightness. However, if
> > I switch to a VT with a console, the brightness changes to what I tried to
> > change it to. Also, pressing the brightness buttons works fine when I press
> > them while in the console.
What machine model precisely ? If it's an iBook, only the very first one
had a mach64 based chip and indeed used atyfb-based brightness control,
that might have been broken...
> > My video card is a Mach64, and I'm using the 'ati' driver for X. One guess I
> > have is that in 2.6, lots more things are built as kernel modules. I'd like
> > to try build any needed video stuff in, but I don't know what it could be.
> > Does anyone here have an idea what controls screen brightness in X?
Some earlier models have PMU-controlled brightness, independently on the
video card...
> Your mouse pointer might get some noisy spots on brightness change, ever
> noticed? I would blame the kernel first (since 2.4 works well).
Ah ? Haven't noticed that ? what machine ?
> > The reason this is important for me is that the LCD is not dimmed when I close
> > the lid for sleep, which kind of ruins the point of sleep.
> >
> > Does anyone have an idea of what else I need to do to get screen dimming to
> > work under X with Linux 2.6?
You may be missing the atyfb kernel driver.
> Try to ask on the ppc-kernel list (CCed). I suspect that nobody has
> reimplemented power management for mach64 in 2.6. The fact that lid
> closing does not put the display to sleep keeps me locked in 2.4, too
> (though I experienced that 2.6 is way faster).
It is, provided you have the right driver.
> I played with the thought of fixing it myself (like I plan to fix DRI
> DMA some time) but was unable to allocate ressources (and enough motivation)
>
> So take this as a "I have the problem, too" posting
> Christof
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
--
Benjamin Herrenschmidt <benh@kernel.crashing.org>
^ permalink raw reply
* Re: [PATCH] sungem rework & wake on lan
From: Benjamin Herrenschmidt @ 2004-11-26 22:03 UTC (permalink / raw)
To: Colin Leroy; +Cc: David S. Miller, linuxppc-dev list, Jeff Garzik
In-Reply-To: <20041126200754.1123e8f8@jack.colino.net>
On Fri, 2004-11-26 at 20:07 +0100, Colin Leroy wrote:
> On 26 Nov 2004 at 14h11, Benjamin Herrenschmidt wrote:
>
> Hi Ben,
>
> if (found_mii_phy(gp) && gp->phy_mii.def->ops->suspend)
> - gp->phy_mii.def->ops->suspend(&gp->phy_mii, 0 /* wake on lan options */);
> + gp->phy_mii.def->ops->suspend(&gp->phy_mii);
>
> Shouldn't this if() have an "&& !wol" clause ?
> At least it doesn't work without it, and works with it, here on 5221 PHY.
>
> Thanks for your work. Will post a patch applying to 2.6.9+sleep_patch soon
> for the possibly interested people.
You are right. It happens to work in both case with the 5201 I have
here, but that may be because the superisolate code doesn't quite work
on it ...
Anyway, I do _not_ intend to keep the driver in this state. I dislike
the fact that I have to udelay() between 10 and 100usec (depending on
the machine) at IRQ time with a spinlock held every second when polling
for the link.
I will move the link poll to task level so it can schedule instead. But
the problem is complex. There is a problem with kenrel autoconfig
(ipconfig) which doesn't schedule and should probably be fixed. Also to
do what I want to do (which is to also switch the clock off when the
iface is up, but the link down), I'll have locking issues with set_mtu()
and set_multicast() calls which happen to be called with a spinlock
held.
Ben.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox