* [RFC/PATCH] envctrl framework and envctrltwo (UE250) driver
@ 2005-02-22 18:07 Eric Brower
2005-02-22 22:27 ` Eric Brower
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Eric Brower @ 2005-02-22 18:07 UTC (permalink / raw)
To: ultralinux
[-- Attachment #1: Type: text/plain, Size: 2882 bytes --]
Attached is a patch against a somewhat dated BK 2.6.10 that provides
the following:
1) a basic framework for supporting SUNW,envctrl environmental
monitoring flavors
2) an implementation of the framework for basic SUNW,envctrltwo
(UE250) support
I am using the term "framework" very lightly here.
This demonstrates use of the in-kernel I2C subsystem rather than the
current roll-your-own support found in the envctrl and bbc_envctrl
drivers. There are a few shortcomings with using the in-kernel I2C
but these can perhaps be ironed-out over time. As well, it addresses
issue brought up many months ago related to how the monitoring kernel
thread is started/stopped and how the shutdown functions are invoked--
basically a freshening. I believe it would be easy to move the
existing envctrl driver (should be renamed envctrl-i2c or somesuch) to
this framework as well and I might be convinced to do so if there is
any interest.
As for the in-kernel I2C shortcomings (as I understand them; correct
me if you know better):
First, the UE250 is a multi-master I2C bus; this is unlike (so far as
I know) other envctrl implementations. The in-kernel I2C subsystem
doesn't handle multimaster, but I've included an I2C patch to support
detection of lost arbitration and I've passed this on to the lmsensors
folks (still awaiting a response). This lost arbitration detection
and recovery could be tightened up a bit, but seems relatively stable.
Second, and related to the first, there does not seem to be a way
within the I2C subsystem to send multiple I2C messages with dyamic
content in a bus-atomic fashion, which is required for proper
multimaster operation. I have no workaround for this, though the I2C
API could be easily modified to accomodate such an extension, I
believe. For the moment, our heavy locking (see below) and collision
detection give us a level of protection but it doesn't make me sleep
well at night.
Second, the in-kernel I2C isn't written to support use while
in_interrupt() (it uses semaphores for restricting access), which is a
requirement of the SUNW,envctrltwo (if we use interrupts...). As a
workaround, I've wrapped all I2C bus access calls with spinlocks.
Fourth, I don't see any easy way to leverage the use of the
already-written chip drivers. Implementing support for them was
trivial, and it is not clear to me how a kernel module could probe
existing chip drivers and retain a handle to them for in-kernel
access.
This driver is relatively rough, but has been running on my E250 for
quite a while under a bit of I2C bus stress. I'd like to see reports
from folks with dual CPUs, though. Please note that I needed to
upgrade my BK 2.6 to include SYM53C8XX version 2.1.18n, but that may
just be due to my setup. I've excessively abused sysfs for access to
sensors data in this driver. I'd appreciate any feedback.
Thanks,
E
[-- Attachment #2: 26_envctrltwo.patch --]
[-- Type: application/octet-stream, Size: 47680 bytes --]
===== arch/sparc64/Kconfig 1.61 vs edited =====
--- 1.61/arch/sparc64/Kconfig 2005-01-10 13:11:03 -08:00
+++ edited/arch/sparc64/Kconfig 2005-02-02 18:20:46 -08:00
@@ -418,7 +418,7 @@
macro in lp.c and the PARPORT_MAX macro in parport.h.
config ENVCTRL
- tristate "SUNW, envctrl support"
+ tristate "SUNW,envctrl support"
depends on PCI
help
Kernel support for temperature and fan monitoring on Sun SME
@@ -426,6 +426,13 @@
To compile this driver as a module, choose M here: the
module will be called envctrl.
+
+config ENVCTRLTWO
+ tristate "SUNW,envctrltwo environmental monitoring support"
+ depends on I2C_ENVCTRL
+ help
+ This driver provides environmental monitoring and front status
+ panel support for systems with SUNW,envctrltwo (Ultra Enterprise 250)
config DISPLAY7SEG
tristate "7-Segment Display support"
===== arch/sparc64/defconfig 1.155 vs edited =====
--- 1.155/arch/sparc64/defconfig 2005-01-21 20:03:35 -08:00
+++ edited/arch/sparc64/defconfig 2005-02-02 18:21:04 -08:00
@@ -115,6 +115,7 @@
CONFIG_PARPORT_1284=y
CONFIG_PRINTER=m
CONFIG_ENVCTRL=m
+CONFIG_ENVCTRLTWO=m
CONFIG_DISPLAY7SEG=m
# CONFIG_CMDLINE_BOOL is not set
===== arch/sparc64/kernel/Makefile 1.30 vs edited =====
--- 1.30/arch/sparc64/kernel/Makefile 2004-09-01 17:52:53 -07:00
+++ edited/arch/sparc64/kernel/Makefile 2005-02-15 08:45:23 -08:00
@@ -23,6 +23,9 @@
obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o
obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o
obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_ENVCTRLTWO) += envctrltwo.o
+
+envctrltwo-objs := env_lib.o env_envctrltwo.o
ifdef CONFIG_SUNOS_EMUL
obj-y += sys_sunos32.o sunos_ioctl32.o
===== arch/sparc64/kernel/env_envctrltwo.c 1.1 vs edited =====
--- 1.1/arch/sparc64/kernel/env_envctrltwo.c 2005-02-03 10:12:38 -08:00
+++ edited/arch/sparc64/kernel/env_envctrltwo.c 2005-02-18 19:15:38 -08:00
@@ -0,0 +1,1012 @@
+/* $Id$
+ * env_envctrltwo.c: Environmental monitoring and front status panel
+ * support for SUNW,envctrltwo (E250)
+ *
+ * Copyright (C) 2004-2005 Eric Brower (ebrower@gmail.com)
+ *
+ * TODOs: move overtemp handling to library code
+ * implement diagnostic return values for all I2C-access functions
+ * (i2c) i2c-core cannot be used in_interrupt() due to semaphores
+ * (i2c) bus-atomicity for sending multiple messages (read,xor,write)
+ * better IRQ cause reporting/handling (see IDEAs below)
+ *
+ * IDEAs: monitor keyswich-- secure == sysctl kernel.stop-a = 0
+ * diag == sysctl kernel.printk increased loglevel
+ * override sysctl kernel.scons-poweroff if overtemp_action == poweroff
+ * decrease on-demand bus traffic with sensor periodic polling
+ * periodic polling of ps and disk sensors (and interrupts) to set leds
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/kmod.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/reboot.h>
+#include <linux/sysfs.h>
+
+#include <asm/ebus.h>
+#include <asm/uaccess.h>
+#include <asm/auxio.h>
+
+#include <asm/i2c-envctrl.h>
+#include "env_envctrltwo.h"
+#include "env_lib.h"
+
+#define ENV2_DEVNAME "envctrltwo"
+#define ENV2_VERSION "0.2.0"
+
+#define ENV2_DEBUG_THERMS (0) /* dump thermisters found and values probed */
+#define ENV2_DEBUG_FAN (0) /* user fanspeed control via sysfs - DANGER! */
+#define ENV2_DEBUG_TEMP (0) /* user CPU temp control via sysfs */
+
+static struct pcf_envctrl_nexus *nex;
+
+/* these are referenced by env_lib-- perhaps an init() func would be better */
+struct i2c_adapter *envctrl_adap;
+spinlock_t envctrl_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * OBP "thermisters" property
+ * Note: "name" is actually variable length,
+ * but null-padded to at least 4 characters
+ */
+struct env2_thermister_prop {
+ u32 addr;
+ u32 port;
+ u32 min_temp;
+ u32 warn_temp;
+ u32 shutdown_temp;
+ u32 num;
+ u32 den;
+ u8 name[4];
+}__attribute((packed));
+
+typedef enum env2_therm_type {
+ therm_type_cpu = 0,
+ therm_type_noncpu,
+} thermtype_t;
+
+struct therm {
+ struct env2_thermister_prop *cfg;
+ thermtype_t type;
+ unsigned long last_warn;
+ struct list_head list;
+};
+
+static struct list_head env2_therms = LIST_HEAD_INIT(env2_therms);
+static u8 env2_cpu_temp_table[256];
+static u8 env2_cpu_fanspeed_table[256];
+static u8 *env2_therms_data;
+
+static struct timer_list env2_fsp_act_timer;
+static unsigned int env2_fsp_irq;
+
+static struct task_struct *kenvctrld_task;
+
+static void env2_do_poweroff(void);
+static void env2_do_shutdown(void);
+static void (*env2_poweroff_pfn)(void) = NULL /* env2_do_poweroff */;
+static int env2_fanspeed_forced = 0;
+
+/* sysfs sh!te goes here... */
+static struct platform_device *env2_device;
+
+static ssize_t env2_show_fanspeed(struct device *d, char *buf);
+#if ENV2_DEBUG_FAN
+ static ssize_t env2_store_fanspeed(
+ struct device *, const char *, size_t);
+ static DEVICE_ATTR(fanspeed, \
+ S_IRUGO|S_IWUSR, env2_show_fanspeed, env2_store_fanspeed);
+#else
+ static DEVICE_ATTR(fanspeed, S_IRUGO, env2_show_fanspeed, NULL);
+#endif
+#if ENV2_DEBUG_TEMP
+ static u8 env2_forced_temp;
+ static ssize_t env2_store_temp(struct device *, const char *, size_t);
+ static ssize_t env2_show_temp (struct device *, char *buf);
+ static DEVICE_ATTR(forced_temp, \
+ S_IRUGO | S_IWUSR, env2_show_temp, env2_store_temp);
+#endif
+static ssize_t env2_show_thermisters(struct device *d, char *buf);
+static DEVICE_ATTR(thermisters, S_IRUGO, env2_show_thermisters, NULL);
+/* TODO: store_power for PS status override ??? */
+static ssize_t env2_show_power(struct device *d, char *buf);
+static DEVICE_ATTR(power, S_IRUGO, env2_show_power, NULL);
+/* TODO: store_disks for disk status override ??? */
+static ssize_t env2_show_disks(struct device *d, char *buf);
+static ssize_t env2_store_disks(struct device *d, const char *buf, size_t cnt);
+static DEVICE_ATTR(disks, S_IRUGO|S_IWUSR, env2_show_disks, env2_store_disks);
+static ssize_t env2_show_keyswitch(struct device *d, char *buf);
+static DEVICE_ATTR(keyswitch, S_IRUGO, env2_show_keyswitch, NULL);
+/* we can set LEDs, but until we have bus-atomicity it is racy */
+static ssize_t env2_show_fspleds(struct device *d, char *buf);
+static ssize_t env2_store_fspleds(struct device *d, const char *b, size_t c);
+static DEVICE_ATTR(fspleds, \
+ S_IRUGO|S_IWUSR, env2_show_fspleds, env2_store_fspleds);
+
+/* specify "envctrl_overtemp=" to override the default over-temperature
+ * action. Allowable values are poweroff, shutdown and none (dangerous!).
+ * The default is shutdown to be idiomatic with existing Linux/SPARC envctrl
+ * drivers.
+ * They all ought to be "poweroff" for safety-- though this requires
+ * tweaking the scons_poweroff sysctl on kernels that support it,
+ * otherwise serial console based systems will not poweroff properly.
+ */
+static int __init env2_parse_overtemp_option(const char *str)
+{
+ if (NULL == str) {
+ return 1;
+ }
+
+ if (!strncmp(str, "poweroff", strlen("poweroff"))) {
+ env2_poweroff_pfn = env2_do_poweroff;
+ return 1;
+ }
+ else if (!strncmp(str, "shutdown", strlen("shutdown"))) {
+ env2_poweroff_pfn = env2_do_shutdown;
+ return 1;
+ }
+#if ENV2_DEBUG_TEMP
+ else if (!strncmp(str, "none", strlen("none"))) {
+ env2_poweroff_pfn = NULL;
+ printk(KERN_INFO
+ "%s: WARNING: thermal shutdown/power-off disabled!\n",
+ ENV2_DEVNAME);
+ return 1;
+ }
+#endif
+ else {
+ printk(KERN_ERR
+ "%s: unrecognized envctrl_overtemp value \"%s\" ignored\n",
+ ENV2_DEVNAME, str);
+ }
+ return 0;
+}
+
+static const char *envctrl_overtemp;
+MODULE_PARM (envctrl_overtemp, "s");
+MODULE_PARM_DESC(envctrl_overtemp,
+ "over-temperature action: poweroff or shutdown (default)");
+
+static int __init env2_init_thermisters(struct linux_ebus_device *edev)
+{
+ int i;
+ int ntherms = 0;
+ struct env2_thermister_prop *t_itr;
+ u8 *start, *end;
+
+ struct therm *therm;
+ struct list_head *tmp1, *tmp2;
+
+ /*
+ * The lookup table length is short by two entries that
+ * represent the coolest thermister raw values
+ * (indecies 254, 255). These lower values are reasonably
+ * expected, so we allocate a bigger table and shift the table
+ * values down by two. We'll duplicate the value at the top of
+ * the table for the two new slots (though these are cooking
+ * temperatures we'd not expect to see).
+ */
+ i = prom_getproplen(edev->prom_node, "cpu-temp-factors");
+ if (i != 254) {
+ printk(KERN_ERR "%s: unexpected cpu-temp-factors table size " \
+ "(is %u, expected %u)\n", ENV2_DEVNAME, i, 254);
+ goto out_err;
+ }
+
+ i = prom_getproperty(edev->prom_node, "cpu-temp-factors",
+ (u8*)(&env2_cpu_temp_table) + 2, i);
+ if (i == -1) {
+ printk(KERN_ERR "%s: unable to read OBP cpu temp table\n",
+ ENV2_DEVNAME);
+ goto out_err;
+ }
+ env2_cpu_temp_table[0] = \
+ env2_cpu_temp_table[1] = env2_cpu_temp_table[2];
+
+ /* Same drill with cpu-fan-speeds table...
+ *
+ * Observed behavior differs from OBP cpu-fan-speeds table;
+ * it seems that 162 is the minimum speed at which the fan can run,
+ * so don't be confused by the discrepancy between table and readings
+ * at the low end of the scale.
+ */
+ i = prom_getproplen(edev->prom_node, "cpu-fan-speeds");
+ if (i != 254) {
+ printk(KERN_ERR "%s: unexpected cpu-fan-speeds table size " \
+ "(is %u expected %u)\n", ENV2_DEVNAME, i, 254);
+ goto out_err;
+ }
+
+ i = prom_getproperty(edev->prom_node, "cpu-fan-speeds",
+ (u8*)(&env2_cpu_fanspeed_table) + 2, i);
+ if (i == -1) {
+ printk(KERN_ERR "%s: unable to read OBP cpu-fan-speeds table\n",
+ ENV2_DEVNAME);
+ goto out_err;
+ }
+ env2_cpu_fanspeed_table[0] = \
+ env2_cpu_fanspeed_table[1] = env2_cpu_fanspeed_table[2];
+
+ i = prom_getproplen(edev->prom_node, "thermisters");
+ if (!i) {
+ printk(KERN_ERR "%s: no thermister data found in OBP\n",
+ ENV2_DEVNAME);
+ goto out_err;
+ }
+ env2_therms_data = (u8*) kmalloc(i, GFP_KERNEL);
+ if (!env2_therms_data) {
+ printk(KERN_ERR "%s: failed to malloc thermister table\n",
+ ENV2_DEVNAME);
+ goto out_err;
+ }
+ if (! prom_getproperty(edev->prom_node, "thermisters",
+ env2_therms_data, i)) {
+ printk(KERN_ERR "%s: unable to collect thermister data\n",
+ ENV2_DEVNAME);
+ goto out_err;
+ }
+
+ start = env2_therms_data;
+ end = start + i - 1;
+
+ while (start < end &&
+ ((end - start) >= sizeof(struct env2_thermister_prop))) {
+ t_itr = (struct env2_thermister_prop *) start;
+ start = t_itr->name + strlen(t_itr->name) + 1;
+
+ therm = kmalloc(sizeof(struct therm), GFP_KERNEL);
+ if (!therm) {
+ goto out_err;
+ }
+
+ /* init one thermister */
+ therm->cfg = t_itr;
+ therm->last_warn = 0;
+ therm->type =
+ (!strncmp(therm->cfg->name, "CPU", strlen("CPU"))) ?
+ therm_type_cpu : therm_type_noncpu;
+ list_add_tail(&(therm->list), &env2_therms);
+ ntherms++;
+ }
+ return ntherms;
+
+out_err:
+ if (env2_therms_data) {
+ kfree(env2_therms_data);
+ }
+ list_for_each_safe(tmp1, tmp2, &env2_therms) {
+ therm = list_entry(tmp1, struct therm, list);
+ list_del(tmp1);
+ kfree(therm);
+ }
+ return 0;
+}
+
+static irqreturn_t env2_fsp_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ /* TODO:
+ * known interrupt sources by inverted bit:
+ * 0xBF - PS
+ * 0xDF - Keyswitch
+ * 0xEF - Fan
+ * 0xFB - Disk (0 & 1 tested)
+ */
+ u8 srcs = envctrl_i2c_read_8574(ENV2_FSP_CTRL_ADDR);
+ envctrl_i2c_write_8574(ENV2_FSP_CTRL_ADDR, 0xFF); /* Ack All... */
+ printk(KERN_DEBUG "envctrl: received FSP interrupt (ctrl 0x%02x)\n",
+ srcs);
+
+ return IRQ_HANDLED;
+}
+
+static void env2_fsp_activity(unsigned long unused)
+{
+ u8 rd;
+ unsigned long flags;
+
+ /* TODO: this must be made i2c-atomic-- not just spinlocked */
+ spin_lock_irqsave(&envctrl_lock, flags);
+
+ __envctrl_i2c_read_8574(ENV2_FSP_LED_ADDR);
+ /* always write keyswitch mask-- key bits can be unset, but not set! */
+ rd = (__envctrl_i2c_read_8574(ENV2_FSP_LED_ADDR) ^ ENV2_LED_ACTIVE)
+ | ENV2_FSP_KEYMSK;
+ __envctrl_i2c_write_8574(ENV2_FSP_LED_ADDR, rd);
+
+ spin_unlock_irqrestore(&envctrl_lock, flags);
+
+ mod_timer(&env2_fsp_act_timer, jiffies + (HZ/2));
+}
+
+static int __init env2_init_fsp(struct linux_ebus_device *edev)
+{
+
+ u8 leds;
+
+ if (edev->num_irqs != 2) {
+ printk(KERN_ERR "%s: unexpected number of interrupts "
+ "(is %i expected %i)\n",
+ ENV2_DEVNAME, edev->num_irqs, 2);
+ return 0;
+ }
+
+ env2_fsp_irq = edev->irqs[1];
+ if (request_irq(env2_fsp_irq, &env2_fsp_intr,
+ SA_SHIRQ, "SUNW,envctrltwo FSP",
+ (void *)&env2_fsp_act_timer)) {
+ printk(KERN_ERR "%s: unable to register FSP irq\n",
+ ENV2_DEVNAME);
+ env2_fsp_irq = 0;
+ return 0;
+ }
+
+ /*
+ * FSP GEN_ERR LED is plumbed OR'ed with auxio LED bit.
+ * Extinguish it, otherwise system appears in error
+ */
+ auxio_set_led(AUXIO_LED_OFF);
+
+ leds = envctrl_i2c_read_8574(ENV2_FSP_LED_ADDR);
+ envctrl_i2c_write_8574(ENV2_FSP_LED_ADDR, leds | ENV2_FSP_LEDMSK);
+
+ init_timer(&env2_fsp_act_timer);
+ env2_fsp_act_timer.expires = jiffies + (HZ/2);
+ env2_fsp_act_timer.function = env2_fsp_activity;
+ add_timer(&env2_fsp_act_timer);
+
+ /* Enable FSP interrupts */
+ envctrl_i2c_write_8574(ENV2_FSP_CTRL_ADDR, ENV2_FSP_DFLOP_INIT0);
+ envctrl_i2c_write_8574(ENV2_FSP_CTRL_ADDR, ENV2_FSP_DFLOP_INIT1);
+ envctrl_i2c_write_8574(ENV2_FSP_CTRL_ADDR, ENV2_FSP_DEVINTR_INIT0);
+ envctrl_i2c_write_8574(ENV2_FSP_CTRL_ADDR, ENV2_FSP_DEVINTR_INIT1);
+
+ return 1;
+}
+
+/* env2_read_thermister: reads a thermister and returns a translated value
+ * if "raw" is non-null, the raw thermister reading will be stored into it
+ */
+static u8 env2_read_thermister(struct therm *t, u8 *raw)
+{
+ u8 val;
+ int ret;
+
+ switch (t->cfg->addr & 0xF0) {
+ case 0x90:
+ ret = envctrl_i2c_read_8591(
+ t->cfg->addr, t->cfg->port, &val);
+ break;
+ default:
+ /* BUG() */
+ return 0;
+ }
+
+ if (ret) {
+ printk(KERN_ERR "%s: unable to read thermister %s%s\n",
+ ENV2_DEVNAME, t->cfg->name,
+ (ret == -EINTR)?" (lost arbitration-- transient)":"");
+ return 0;
+ }
+ if (raw) {
+ *raw = val;
+ }
+#if ENV2_DEBUG_THERMS
+ printk("%s: %s raw: %u ", __FUNCTION__, t->cfg->name, val);
+#endif
+ if (t->type == therm_type_cpu) {
+ val = env2_cpu_temp_table[val];
+ }
+ else if (t->cfg->num && t->cfg->den) {
+ val = (val * t->cfg->num) / t->cfg->den;
+ }
+ else {
+ /* perhaps raw? */
+ printk(KERN_WARNING
+ "%s: no thermister translation for %s (val %i)\n",
+ ENV2_DEVNAME, t->cfg->name, val);
+ }
+#if ENV2_DEBUG_THERMS
+ printk("cooked: %u\n", val);
+#endif
+ return val;
+}
+
+/* TODO: change to include return value */
+static void env2_set_fanspeed(u8 fanspeed, u8 forced)
+{
+ if (!forced && env2_fanspeed_forced) {
+ return;
+ }
+ envctrl_i2c_write_8591(ENV2_FAN_ADDR, fanspeed);
+}
+
+/* TODO: change for meaningful return value */
+static u8 env2_get_fanspeed(void)
+{
+ u8 rv;
+ int val;
+ /* TODO: raw values read from the fan do not correspond to
+ * the values written (even after suitable settling time).
+ * This is likely due to approximations in the D2A, so we might
+ * as well artificially inflate the read values here...
+ *
+ * Ugh-- consider wraparound at high speeds...
+ */
+ if (! envctrl_i2c_read_8591(ENV2_FAN_ADDR, ENV2_FAN_PORT, &rv)) {
+ val = rv * 107 / 100;
+ if (val > 255 /* UCHAR_MAX */) {
+ return 255; /* UCHAR_MAX or FAN_MAX */
+ }
+ return (u8) val;
+ }
+ return 0;
+}
+
+static ssize_t env2_show_fanspeed(struct device *dev, char *buf)
+{
+ return sprintf(buf, "FAN: [%u]%s\n",
+ env2_get_fanspeed(), env2_fanspeed_forced ? " (forced)" : "");
+}
+
+#if ENV2_DEBUG_FAN
+static ssize_t env2_store_fanspeed(struct device *dev, const char *buf, size_t count)
+{
+ u8 val = (u8) simple_strtoul(buf, NULL, 0);
+ if (!val) {
+ /* writing 0 un-forces fan speed */
+ env2_fanspeed_forced = 0;
+ return count;
+ }
+
+ env2_fanspeed_forced = 1;
+ env2_set_fanspeed(val, 1);
+
+ return count;
+}
+#endif /* ENV2_DEBUG_FAN */
+
+#if ENV2_DEBUG_TEMP
+static ssize_t env2_store_temp(struct device *d, const char *buf, size_t count)
+{
+ u8 val = (u8) simple_strtoul(buf, NULL, 0);
+ env2_forced_temp = val;
+ return count;
+}
+
+static ssize_t env2_show_temp (struct device *d, char *buf)
+{
+ return sprintf(buf, "%i\n", env2_forced_temp);
+}
+#endif
+
+static ssize_t env2_show_thermisters(struct device *dev, char *buf)
+{
+ ssize_t ret = 0;
+ struct therm *t;
+ struct list_head *tmp1, *tmp2;
+
+ list_for_each_safe(tmp1, tmp2, &env2_therms) {
+ t = list_entry(tmp1, struct therm, list);
+#if ENV2_DEBUG_THERMS
+ ret += sprintf(buf + ret,
+ "%s:\t%u C (0x%02x[%u] min %i, warn %i, shut %i, "
+ "num %i, den %i, type %i)\n",
+ t->cfg->name, env2_read_thermister(t, NULL),
+ t->cfg->addr, t->cfg->port, t->cfg->min_temp,
+ t->cfg->warn_temp, t->cfg->shutdown_temp,
+ t->cfg->num, t->cfg->den, t->type);
+#else
+ ret += sprintf(buf + ret,
+ "%s:\t[%u C] (min %i, warn %i, shut %i)\n",
+ t->cfg->name, env2_read_thermister(t, NULL),
+ t->cfg->min_temp, t->cfg->warn_temp,
+ t->cfg->shutdown_temp);
+#endif
+ }
+ return ret;
+}
+
+static u8 env2_get_power(void)
+{
+ return envctrl_i2c_read_8574(ENV2_PS_ADDR);
+}
+
+static ssize_t env2_show_power(struct device *dev, char *buf)
+{
+ ssize_t ret = 0;
+ u8 stat = env2_get_power();
+
+ ret += sprintf(buf+ret, "PS1: [%s]\n",
+ (ENV2_PS_PRES(stat,1) ?
+ (ENV2_PS_ERR(stat,1) ? "FAIL" : "OK") : "ABSENT"));
+ ret += sprintf(buf+ret, "PS2: [%s]\n",
+ (ENV2_PS_PRES(stat,2) ?
+ (ENV2_PS_ERR(stat,2) ? "FAIL" : "OK") : "ABSENT"));
+ return ret;
+}
+
+static u8 env2_get_diskpres(void)
+{
+ return envctrl_i2c_read_8574(ENV2_DISKPRES_ADDR);
+}
+
+static u8 env2_get_diskstat(void)
+{
+ return envctrl_i2c_read_8574(ENV2_DISKSTAT_ADDR);
+}
+
+static ssize_t env2_show_disks(struct device *dev, char *buf)
+{
+ ssize_t ret = 0;
+ u8 pres = env2_get_diskpres();
+ u8 stat = env2_get_diskstat();
+ int i;
+ for (i = 1; i <= ENV2_DISK_MAX; i++) {
+ ret += sprintf(buf+ret, "DISK%i: [%s]\n", i,
+ (!(pres & (1<<(i-1))) ?
+ ((!(stat & (1<<(i-1)))) ? "FAIL" : "OK") :
+ ((!(stat & (1<<(i-1)))) ? "ABSENT/ATTN" :
+ "ABSENT")));
+ }
+
+ return ret;
+}
+
+static ssize_t env2_store_disks(struct device *dev, const char *buf, size_t count)
+{
+ u8 stat;
+
+ /* We don't first check the disk is present-- we could, though */
+ u8 diskno = (u8) simple_strtoul(buf, NULL, 0);
+ if (diskno && diskno <= ENV2_DISK_MAX) {
+ char *val = strchr(buf, '=');
+ if (val) {
+ val++;
+ if (!strnicmp(val, "fail", strlen("fail"))) {
+ /* TODO: this must be made bus-atomic */
+ spin_lock(&envctrl_lock);
+ stat =
+ __envctrl_i2c_read_8574(ENV2_DISKSTAT_ADDR);
+ stat &= ~(1 << (diskno - 1));
+ __envctrl_i2c_write_8574(
+ ENV2_DISKSTAT_ADDR, stat);
+ spin_unlock(&envctrl_lock);
+ }
+ else if (!strnicmp(val, "ok", strlen("ok"))) {
+ /* TODO: this must be made bus-atomic */
+ spin_lock(&envctrl_lock);
+ stat =
+ __envctrl_i2c_read_8574(ENV2_DISKSTAT_ADDR);
+ stat |= (1 << (diskno - 1));
+ __envctrl_i2c_write_8574(
+ ENV2_DISKSTAT_ADDR, stat);
+ spin_unlock(&envctrl_lock);
+ }
+ }
+ }
+ return count;
+}
+
+static u8 env2_get_keyswitch(void)
+{
+ return envctrl_i2c_read_8574(ENV2_FSP_LED_ADDR) & ENV2_FSP_KEYMSK;
+}
+
+static ssize_t env2_show_keyswitch(struct device *dev, char *buf)
+{
+ ssize_t ret = 0;
+ u8 stat = env2_get_keyswitch();
+ char *output;
+
+ switch (stat) {
+ case ENV2_FSP_KEY_OFF:
+ output = "OFF";
+ break;
+ case ENV2_FSP_KEY_DIAG:
+ output = "DIAG";
+ break;
+ case ENV2_FSP_KEY_ON:
+ output = "ON";
+ break;
+ case ENV2_FSP_KEY_LOCK:
+ output = "LOCKED";
+ break;
+ default:
+ output = "UNKNOWN";
+ break;
+ }
+
+ ret += sprintf(buf + ret, "KEYSWITCH: [%s]\n", output);
+
+ return ret;
+}
+
+static u8 env2_get_fspleds(void)
+{
+ return envctrl_i2c_read_8574(ENV2_FSP_LED_ADDR) & ENV2_FSP_LEDMSK;
+}
+
+/* TODO: diagnostic return value */
+static void env2_set_fspleds(u8 leds_on, u8 leds_off)
+{
+ u8 rd;
+ unsigned long flags;
+
+ /* don't stomp on keyswitch bits */
+ if ((leds_on | leds_off) & ENV2_FSP_KEYMSK) {
+ return;
+ }
+
+ /* TODO: this must be made i2c-atomic-- not just spinlocked */
+ spin_lock_irqsave(&envctrl_lock, flags);
+
+ __envctrl_i2c_read_8574(ENV2_FSP_LED_ADDR);
+ /* always write keyswitch mask-- key bits can be unset, but not set! */
+ rd = (__envctrl_i2c_read_8574(ENV2_FSP_LED_ADDR) |
+ ENV2_FSP_KEYMSK | leds_off) & ~leds_on;
+ __envctrl_i2c_write_8574(ENV2_FSP_LED_ADDR, rd);
+
+ spin_unlock_irqrestore(&envctrl_lock, flags);
+}
+
+static ssize_t env2_show_fspleds(struct device *dev, char *buf)
+{
+ ssize_t ret = 0;
+ u8 stat = env2_get_fspleds();
+
+ ret += sprintf(buf + ret, "POWER: [%s]\n", "ON");
+ ret += sprintf(buf + ret, "GEN ERROR: [%s]\n",
+ stat & ENV2_LED_GENERR ? "OFF" : "ON");
+ ret += sprintf(buf + ret, "ACTIVITY: [%s]\n",
+ stat & ENV2_LED_ACTIVE ? "OFF" : "ON");
+ ret += sprintf(buf + ret, "DISK ERROR: [%s]\n",
+ stat & ENV2_LED_DISKERR ? "OFF" : "ON");
+ ret += sprintf(buf + ret, "TEMP ERROR: [%s]\n",
+ stat & ENV2_LED_DISKERR ? "OFF" : "ON");
+ ret += sprintf(buf + ret, "PS ERROR: [%s]\n",
+ stat & ENV2_LED_PSERR ? "OFF" : "ON");
+
+ return ret;
+}
+
+static ssize_t env2_store_fspleds(struct device *dev, const char *buf, size_t cnt)
+{
+ char on = -1;
+ char led= -1;
+
+ char *val = strchr(buf, '=');
+ if (!val) {
+ return cnt;
+ }
+ val++;
+
+ if (!strnicmp(val, "on", strlen("on"))) {
+ on = 1;
+ }
+ else if (!strnicmp(val, "off", strlen("off"))) {
+ on = 0;
+ }
+ else {
+ return cnt;
+ }
+
+ if (!strnicmp(buf, "temp=", strlen("temp="))) {
+ led = ENV2_LED_TEMPERR;
+ }
+ else if (!strnicmp(buf, "disk=", strlen("disk="))) {
+ led = ENV2_LED_DISKERR;
+ }
+ else if (!strnicmp(buf, "ps=", strlen("ps="))) {
+ led = ENV2_LED_PSERR;
+ }
+ else if (!strnicmp(buf, "all=", strlen("all="))) {
+ led = ENV2_LED_PSERR | ENV2_LED_DISKERR | ENV2_LED_TEMPERR;
+ }
+ else {
+ printk("%s: unknown led specified\n", __FUNCTION__);
+ return cnt;
+ }
+
+ env2_set_fspleds((on ? led : 0), on ? 0 : led);
+
+ return cnt;
+}
+
+
+static void env2_do_poweroff(void)
+{
+ static int po_inprog = 0;
+
+ if (po_inprog) {
+ return;
+ }
+
+ po_inprog = 1;
+ printk(KERN_CRIT "%s: WARNING: Powering-off the system now!\n",
+ current->comm);
+
+ /* TODO: machine_power_off will go to OBP instead if this system
+ * is serial-console based and scons_poweroff sysctl is not set
+ */
+ machine_power_off();
+}
+
+static void env2_do_shutdown(void)
+{
+ static int sd_inprog = 0;
+ static char *envp[] = {
+ "HOME=/", "TERM=linux",
+ "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+ char *argv[] = { "/sbin/shutdown", "-h", "now", NULL };
+
+ if (sd_inprog != 0)
+ return;
+
+ sd_inprog = 1;
+
+ printk(KERN_CRIT "%s: WARNING: Shutting down the system now.\n",
+ current->comm);
+#if 0
+ if (0 > execve("/sbin/shutdown", argv, envp)) {
+#else
+ if (call_usermodehelper("/sbin/shutdown", argv, envp, 0)) {
+#endif
+ printk(KERN_CRIT "%s: WARNING: system shutdown failed!\n",
+ current->comm);
+ /* unlikely to succeed, but we could try again */
+ sd_inprog = 0;
+ }
+}
+
+#define WARN_INTERVAL (30 * HZ)
+static void env2_check_therms(void)
+{
+ u8 tempbuf;
+ u8 tempmax = 0;
+ u8 rawbuf;
+ u8 tempmax_raw = 0;
+
+ struct list_head *tmp;
+ struct therm *t;
+
+ list_for_each(tmp, &env2_therms) {
+ t = list_entry(tmp, struct therm, list);
+ tempbuf = env2_read_thermister(t, &rawbuf);
+ if (t->type == therm_type_cpu) {
+#if ENV2_DEBUG_TEMP
+ if (env2_forced_temp) {
+ tempbuf = env2_forced_temp;
+ }
+#endif
+ if (tempbuf > tempmax) {
+ tempmax = tempbuf;
+ tempmax_raw = rawbuf;
+ }
+ }
+ if (t->cfg->shutdown_temp && tempbuf >= t->cfg->shutdown_temp) {
+ printk(KERN_CRIT
+ "%s: WARNING: %s temperature %u C meets or "
+ "exceeds shutdown threshold %u C\n",
+ ENV2_DEVNAME, t->cfg->name,
+ tempbuf, t->cfg->shutdown_temp);
+
+ env2_set_fspleds(ENV2_LED_GENERR, 0);
+ env2_set_fanspeed(ENV2_FAN_MAX, 0);
+
+ if (env2_poweroff_pfn) {
+ env2_poweroff_pfn();
+ }
+ return;
+ }
+ else if (t->cfg->warn_temp && tempbuf >= t->cfg->warn_temp) {
+ /* TODO: set thermal warning LED or gen_err */
+ if (time_after(jiffies, t->last_warn + WARN_INTERVAL)) {
+ t->last_warn = jiffies;
+ printk(KERN_CRIT
+ "%s: WARNING: %s temperature %u C "
+ "meets or exceeds warning threshold "
+ "%u C\n",
+ ENV2_DEVNAME, t->cfg->name,
+ tempbuf, t->cfg->warn_temp);
+ printk(KERN_CRIT
+ "%s: WARNING: system will shut down "
+ "at %s temperature %u C\n",
+ ENV2_DEVNAME, t->cfg->name,
+ t->cfg->shutdown_temp);
+ }
+ }
+ else {
+ /* Use last_warn as a flag to reduce I2C traffic--
+ * because there is no hysteresis, this could cause
+ * unlimited warn messages if we hover around warn_temp
+ */
+ if (t->last_warn) {
+ t->last_warn = 0;
+ // TODO: env2_set_led functionality
+ //env2_set_led(ENVX_LED_TYPE_GEN_ERR, ENVX_LED_OFF);
+ env2_set_fspleds(0, ENV2_LED_GENERR);
+ }
+ }
+ }
+ env2_set_fanspeed(env2_cpu_fanspeed_table[tempmax_raw], 0);
+}
+
+static int kenvctrld(void *__unused)
+{
+ int poll_interval = 5 * HZ;
+
+ daemonize("kenvctrld");
+ allow_signal(SIGKILL);
+
+ printk(KERN_INFO "%s: %s starting\n", ENV2_DEVNAME, current->comm);
+ while (!kthread_should_stop()) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(poll_interval);
+ current->state = TASK_RUNNING;
+
+ if (signal_pending(current)) {
+ break;
+ }
+
+ env2_check_therms();
+ }
+ printk(KERN_INFO "%s: %s exiting\n", ENV2_DEVNAME, current->comm);
+
+ return 0;
+}
+
+static void __exit env2_cleanup(void)
+{
+ /* XXX: env2_cleanup_thermisters(), env2_cleanup_fsp(), etc */
+ struct therm *therm;
+ struct list_head *tmp1, *tmp2;
+
+ if (env2_device) {
+ device_remove_file(&(env2_device->dev), &dev_attr_fanspeed);
+ device_remove_file(&(env2_device->dev), &dev_attr_thermisters);
+ device_remove_file(&(env2_device->dev), &dev_attr_power);
+ device_remove_file(&(env2_device->dev), &dev_attr_disks);
+ device_remove_file(&(env2_device->dev), &dev_attr_keyswitch);
+ device_remove_file(&(env2_device->dev), &dev_attr_fspleds);
+#if ENV2_DEBUG_TEMP
+ device_remove_file(&(env2_device->dev), &dev_attr_forced_temp);
+#endif
+ platform_device_unregister(env2_device);
+ env2_device = NULL;
+ }
+
+ if (kenvctrld_task) {
+ (void) kthread_stop(kenvctrld_task);
+ kenvctrld_task = NULL;
+ }
+
+ if (env2_fsp_irq) {
+ /* disable FSP interrupts */
+ envctrl_i2c_write_8574(
+ ENV2_FSP_CTRL_ADDR, ENV2_FSP_INTR_LATCH_CLR);
+ free_irq(env2_fsp_irq, (void *) &env2_fsp_act_timer);
+ env2_fsp_irq = 0;
+ }
+ del_timer_sync(&env2_fsp_act_timer);
+
+ if (env2_therms_data) {
+ kfree(env2_therms_data);
+ env2_therms_data = NULL;
+ }
+ list_for_each_safe(tmp1, tmp2, &env2_therms) {
+ therm = list_entry(tmp1, struct therm, list);
+ list_del(tmp1);
+ kfree(therm);
+ }
+
+ if (envctrl_adap) {
+ i2c_put_adapter(envctrl_adap);
+ envctrl_adap = NULL;
+ }
+
+ return;
+}
+
+#if ENV2_DEBUG_THERMS
+static void dump_therms(void)
+{
+ struct therm *t;
+ struct list_head *tmp1, *tmp2;
+
+ list_for_each_safe(tmp1, tmp2, &env2_therms) {
+ t = list_entry(tmp1, struct therm, list);
+ printk("0x%02x[%u] min %i, warn %i, shut %i, "
+ "num %i, den %i, %s\n",
+ t->cfg->addr, t->cfg->port, t->cfg->min_temp,
+ t->cfg->warn_temp, t->cfg->shutdown_temp,
+ t->cfg->num, t->cfg->den, t->cfg->name);
+ }
+}
+#endif
+
+static int __init env2_init(void)
+{
+ int err = 0;
+
+ env2_parse_overtemp_option(envctrl_overtemp);
+
+ nex = pcf_envctrl_get_nexus();
+ if (nex->type != TYPE_ENVCTRLTWO) {
+ printk("%s: nexus type %i is not ENVCTRLTWO\n",
+ __FUNCTION__, nex->type);
+ return -ENODEV;
+ }
+
+ envctrl_adap = i2c_get_adapter(nex->i2c_id);
+ if (!envctrl_adap) {
+ printk("%s: i2c_get_adapter_id(%i) returned NULL\n",
+ __FUNCTION__, nex->i2c_id);
+ return -ENODEV;
+ }
+
+ if (! env2_init_thermisters(nex->edev)) {
+ err = -EINVAL;
+ goto out_err;
+ }
+
+#if ENV2_DEBUG_THERMS
+ dump_therms();
+#endif
+
+ if (! env2_init_fsp(nex->edev)) {
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ kenvctrld_task = kthread_run(kenvctrld, NULL, "kenvctrld");
+ if (IS_ERR(kenvctrld_task)) {
+ err = PTR_ERR(kenvctrld_task);
+ kenvctrld_task = NULL;
+ goto out_err;
+ }
+
+ env2_device = platform_device_register_simple(
+ ENV2_DEVNAME "-", 0, NULL, 0);
+ if (IS_ERR(env2_device)) {
+ err = PTR_ERR(env2_device);
+ env2_device = NULL;
+ goto out_err;
+ }
+ device_create_file(&(env2_device->dev), &dev_attr_fanspeed);
+ device_create_file(&(env2_device->dev), &dev_attr_thermisters);
+ device_create_file(&(env2_device->dev), &dev_attr_power);
+ device_create_file(&(env2_device->dev), &dev_attr_disks);
+ device_create_file(&(env2_device->dev), &dev_attr_keyswitch);
+ device_create_file(&(env2_device->dev), &dev_attr_fspleds);
+#if ENV2_DEBUG_TEMP
+ device_create_file(&(env2_device->dev), &dev_attr_forced_temp);
+#endif
+
+ printk(KERN_INFO "%s: driver version %s initialized\n",
+ ENV2_DEVNAME, ENV2_VERSION);
+
+ return 0;
+
+out_err:
+ env2_cleanup();
+ return err;
+}
+
+
+module_init(env2_init);
+module_exit(env2_cleanup);
+MODULE_LICENSE("GPL");
===== arch/sparc64/kernel/env_envctrltwo.h 1.1 vs edited =====
--- 1.1/arch/sparc64/kernel/env_envctrltwo.h 2005-02-03 10:12:42 -08:00
+++ edited/arch/sparc64/kernel/env_envctrltwo.h 2005-02-15 08:45:01 -08:00
@@ -0,0 +1,54 @@
+
+/* $Id$
+ * env_envctrltwo.h - SUNW,envctrltwo private definitions
+ * Copyright (C) 2004-2005 Eric Brower (ebrower@gmail.com)
+ */
+
+#ifndef _SPARC64_ENV_ENVCTRLTWO_H
+#define _SPARC64_ENV_ENVCTRLTWO_H
+
+/*
+ * envctrltwo definitions
+ */
+#define ENV2_FSP_LED_ADDR 0x7C
+#define ENV2_LED_DISKERR (1<<0)
+#define ENV2_LED_PSERR (1<<1)
+#define ENV2_LED_TEMPERR (1<<2)
+#define ENV2_LED_GENERR (1<<3)
+#define ENV2_LED_ACTIVE (1<<4)
+#define ENV2_LED_POWER (1<<5)
+#define ENV2_FSP_LEDMSK 0x3F
+
+#define ENV2_FSP_KEY_OFF 0x00
+#define ENV2_FSP_KEY_DIAG 0x40
+#define ENV2_FSP_KEY_ON 0x80
+#define ENV2_FSP_KEY_LOCK 0xC0
+#define ENV2_FSP_KEYMSK 0xC0
+
+#define ENV2_FSP_CTRL_ADDR 0x70
+#define ENV2_FSP_DFLOP_INIT0 0x77
+#define ENV2_FSP_DFLOP_INIT1 0x7F
+#define ENV2_FSP_DEVINTR_INIT0 0xF7
+#define ENV2_FSP_DEVINTR_INIT1 0xFF
+#define ENV2_FSP_INTR_LATCH_CLR 0xFE
+
+#define ENV2_FAN_ADDR 0x94
+#define ENV2_FAN_PORT 0x01
+#define ENV2_FAN_MAX 0xFF
+
+#define ENV2_DISKPRES_ADDR 0x7A
+#define ENV2_DISKSTAT_ADDR 0x7E
+#define ENV2_DISK_MAX 6 /* 1-based */
+
+#define ENV2_PS_ADDR 0x72
+#define ENV2_PS_PRES_MASK 0x03
+#define ENV2_PS_PRES_SHIFT 0
+#define ENV2_PS_PRES(stat, ps) \
+ (!(((stat & ENV2_PS_PRES_MASK) >> (ENV2_PS_PRES_SHIFT + ps - 1)) & 0x01))
+#define ENV2_PS_ERR_MASK 0x30
+#define ENV2_PS_ERR(stat, ps) \
+ (!(((stat & ENV2_PS_ERR_MASK) >> (ENV2_PS_ERR_SHIFT + ps - 1)) & 0x01))
+#define ENV2_PS_ERR_SHIFT 4
+#define ENV2_PS_MAX 2 /* 1-based */
+
+#endif /* #ifndef _SPARC64_ENV_ENVCTRLTWO_H */
===== arch/sparc64/kernel/env_lib.c 1.1 vs edited =====
--- 1.1/arch/sparc64/kernel/env_lib.c 2005-02-14 23:55:51 -08:00
+++ edited/arch/sparc64/kernel/env_lib.c 2005-02-15 08:45:01 -08:00
@@ -0,0 +1,181 @@
+/* $Id$
+ * env_lib.c: Environmental monitoring library code for
+ * accessing I2C clients (chips) via i2c-envctrl
+ * PCF8584 bus nexus interface
+ *
+ * Copyright (C) 2004-2005 Eric Brower (ebrower@gmail.com)
+ *
+ * TODO: functions should all return explicit error codes
+ */
+
+#include <linux/spinlock.h>
+#include "env_lib.h"
+
+/* TODO-- get references from an env_lib_init() routine
+ */
+extern struct i2c_adapter *envctrl_adap;
+extern spinlock_t envctrl_lock;
+
+/* returns 0 on success, error code on failure */
+int __env2_i2c_readb(u8 addr, u8 *rv)
+{
+ int ret;
+ struct i2c_msg msg = {
+ .addr = (addr>>1),
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = rv,
+ };
+
+ ret = i2c_transfer(envctrl_adap, &msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ }
+ return (ret == 1) ? 0 : ret;
+}
+
+int env2_i2c_readb(u8 addr, u8 *rv)
+{
+ int ret;
+ unsigned long flags;
+ spin_lock_irqsave(&envctrl_lock, flags);
+
+ ret = __env2_i2c_readb(addr, rv);
+ spin_unlock_irqrestore(&envctrl_lock, flags);
+ return ret;
+}
+
+int __env2_i2c_writeb(u8 addr, u8 val)
+{
+ u8 valbuf = val;
+ int ret;
+
+ struct i2c_msg msg = {
+ .addr = (addr>>1),
+ .flags = 0,
+ .len = 1,
+ .buf = &valbuf,
+ };
+
+ if (!envctrl_adap) {
+ BUG();
+ }
+
+ ret = i2c_transfer(envctrl_adap, &msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ }
+ return ret;
+}
+
+int env2_i2c_writeb(u8 addr, u8 val)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&envctrl_lock, flags);
+ ret = __env2_i2c_writeb(addr, val);
+ spin_unlock_irqrestore(&envctrl_lock, flags);
+ return ret;
+}
+
+u8 __envctrl_i2c_read_8574(u8 addr)
+{
+ u8 buf;
+ (void) __env2_i2c_readb(addr, &buf);
+ return buf;
+}
+u8 envctrl_i2c_read_8574(u8 addr)
+{
+ u8 buf;
+ (void) env2_i2c_readb(addr, &buf);
+ return buf;
+}
+
+int __envctrl_i2c_read_8591(u8 addr, u8 port, u8 *rv)
+{
+#define PCF8591_AOE (0x40)
+ int ret;
+ u8 tmp = port | PCF8591_AOE;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = (addr>>1),
+ .flags = 0,
+ .len = 1,
+ .buf = &tmp,
+ },
+ {
+ /* dummy read-- result of previous conversion cycle */
+ .addr = (addr>>1),
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = &tmp,
+ },
+ {
+ .addr = (addr>>1),
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = rv,
+ },
+
+ };
+
+ int msgcnt = sizeof(msg) / sizeof(struct i2c_msg);
+ if (!envctrl_adap) {
+ BUG();
+ }
+ ret = i2c_transfer(envctrl_adap, msg, msgcnt);
+ if (ret == msgcnt) {
+ ret = 0;
+ }
+ return ret;
+}
+
+int envctrl_i2c_read_8591(u8 addr, u8 port, u8 *rv)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&envctrl_lock, flags);
+ ret = __envctrl_i2c_read_8591(addr, port, rv);
+ spin_unlock_irqrestore(&envctrl_lock, flags);
+ return ret;
+}
+
+u8 __envctrl_i2c_write_8591(u8 addr, u8 data)
+{
+ int ret;
+ u8 tmp[] = { PCF8591_AOE, data };
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = (addr>>1),
+ .flags = 0,
+ .len = 2,
+ .buf = tmp,
+ }
+ };
+
+ int msgcnt = sizeof(msg) / sizeof(struct i2c_msg);
+
+ if (!envctrl_adap) {
+ BUG();
+ }
+ ret = i2c_transfer(envctrl_adap, msg, msgcnt);
+ if (ret == msgcnt) {
+ ret = 0;
+ }
+ return ret;
+}
+
+u8 envctrl_i2c_write_8591(u8 addr, u8 data)
+{
+ u8 ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&envctrl_lock, flags);
+ ret = __envctrl_i2c_write_8591(addr, data);
+ spin_unlock_irqrestore(&envctrl_lock, flags);
+ return ret;
+}
===== arch/sparc64/kernel/env_lib.h 1.1 vs edited =====
--- 1.1/arch/sparc64/kernel/env_lib.h 2005-02-14 23:56:01 -08:00
+++ edited/arch/sparc64/kernel/env_lib.h 2005-02-15 08:45:01 -08:00
@@ -0,0 +1,54 @@
+/* $Id$
+ * env_lib.c: Environmental monitoring library code for
+ * accessing I2C clients (chips) via i2c-envctrl
+ * PCF8584 bus nexus interface
+ *
+ * Copyright (C) 2004-2005 Eric Brower (ebrower@gmail.com)
+ *
+ * TODO: functions should all return explicit error codes
+ */
+
+#ifndef _SPARC64_ENV_LIB_H
+#define _SPARC64_ENV_LIB_H
+
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+
+/*
+ * "chip" access functions--
+ * We use spinlocks for two reasons:
+ * 1) because the i2c core doesn't support use while in_interrupt()
+ * so we "wrap" their semaphores with spinlocks (ugh!)
+ * 2) to prevent concurrent access to bus from interrupts or timers
+ *
+ * Unlocked routines are provided for when multiple I2C message
+ * atomicity is required-- the i2c core does not currently allow us
+ * to issue multiple messages with dynamic content (i.e. read,xor,write).
+ * When using unlocked functions (__xxx() funcs) you must acquire and
+ * release the spinlock yourself-- this gives us a level of atomicity
+ * but does not address multi-master concerns because all such transactions
+ * are done as separate START/STOP sequences (FIXME in i2c core).
+ */
+
+/* returns 0 on success, error code on failure */
+int __env2_i2c_readb(u8 addr, u8 *rv);
+int env2_i2c_readb(u8 addr, u8 *rv);
+
+int __env2_i2c_writeb(u8 addr, u8 val);
+int env2_i2c_writeb(u8 addr, u8 val);
+
+/* XXX Temporary-- should be lib code or somesuch */
+#define __envctrl_i2c_write_8574(addr,data) __env2_i2c_writeb(addr,data);
+#define envctrl_i2c_write_8574(addr,data) env2_i2c_writeb(addr,data);
+
+u8 __envctrl_i2c_read_8574(u8 addr);
+u8 envctrl_i2c_read_8574(u8 addr);
+
+int __envctrl_i2c_read_8591(u8 addr, u8 port, u8 *rv);
+int envctrl_i2c_read_8591(u8 addr, u8 port, u8 *rv);
+
+u8 __envctrl_i2c_write_8591(u8 addr, u8 data);
+u8 envctrl_i2c_write_8591(u8 addr, u8 data);
+
+#endif /* #ifndef _SPARC64_ENV_LIB_H */
===== drivers/i2c/algos/i2c-algo-pcf.c 1.16 vs edited =====
--- 1.16/drivers/i2c/algos/i2c-algo-pcf.c 2004-12-07 02:14:58 -08:00
+++ edited/drivers/i2c/algos/i2c-algo-pcf.c 2005-02-11 17:50:20 -08:00
@@ -78,7 +78,6 @@
set_pcf(adap, 1, I2C_PCF_STOP);
}
-
static int wait_for_bb(struct i2c_algo_pcf_data *adap) {
int timeout = DEF_TIMEOUT;
@@ -109,6 +108,26 @@
adap->waitforpin();
*status = get_pcf(adap, 1);
}
+ if (*status & I2C_PCF_LAB) {
+ DEB2(printk(KERN_INFO
+ "i2c-algo-pcf.o: lost arbitration (CSR 0x%02x)\n",
+ *status));
+ /* Cleanup from LAB-- reset and enable ESO.
+ * This resets the PCF8584; since we've lost the bus, no
+ * further attempts should be made by callers to clean up
+ * (no i2c_stop() etc.)
+ */
+ set_pcf(adap, 1, I2C_PCF_PIN);
+ set_pcf(adap, 1, I2C_PCF_ESO);
+ /* TODO: we should pause for a time period sufficient for any
+ * running I2C transaction to complete-- the arbitration
+ * logic won't work properly until the next START is seen.
+ */
+ DEB2(printk(KERN_INFO
+ "i2c-algo-pcf.o: reset LAB condition (CSR 0x%02x)\n",
+ get_pcf(adap,1)));
+ return(-EINTR);
+ }
#endif
if (timeout <= 0)
return(-1);
@@ -188,16 +207,22 @@
unsigned char addr, int retries)
{
int i, status, ret = -1;
+ int wfp;
for (i=0;i<retries;i++) {
i2c_outb(adap, addr);
i2c_start(adap);
status = get_pcf(adap, 1);
- if (wait_for_pin(adap, &status) >= 0) {
+ if ((wfp = wait_for_pin(adap, &status)) >= 0) {
if ((status & I2C_PCF_LRB) == 0) {
i2c_stop(adap);
break; /* success! */
}
}
+ if (wfp == -EINTR) {
+ /* arbitration lost */
+ udelay(adap->udelay);
+ return -EINTR;
+ }
i2c_stop(adap);
udelay(adap->udelay);
}
@@ -219,6 +244,10 @@
i2c_outb(adap, buf[wrcount]);
timeout = wait_for_pin(adap, &status);
if (timeout) {
+ if (timeout == -EINTR) {
+ /* arbitration lost */
+ return -EINTR;
+ }
i2c_stop(adap);
dev_err(&i2c_adap->dev, "i2c_write: error - timeout.\n");
return -EREMOTEIO; /* got a better one ?? */
@@ -247,11 +276,16 @@
{
int i, status;
struct i2c_algo_pcf_data *adap = i2c_adap->algo_data;
+ int wfp;
/* increment number of bytes to read by one -- read dummy byte */
for (i = 0; i <= count; i++) {
- if (wait_for_pin(adap, &status)) {
+ if ((wfp = wait_for_pin(adap, &status))) {
+ if (wfp == -EINTR) {
+ /* arbitration lost */
+ return -EINTR;
+ }
i2c_stop(adap);
dev_err(&i2c_adap->dev, "pcf_readbytes timed out.\n");
return (-1);
@@ -366,6 +400,10 @@
/* Wait for PIN (pending interrupt NOT) */
timeout = wait_for_pin(adap, &status);
if (timeout) {
+ if (timeout == -EINTR) {
+ /* arbitration lost */
+ return (-EINTR);
+ }
i2c_stop(adap);
DEB2(printk(KERN_ERR "i2c-algo-pcf.o: Timeout waiting "
"for PIN(1) in pcf_xfer\n");)
===== drivers/i2c/busses/Kconfig 1.67 vs edited =====
--- 1.67/drivers/i2c/busses/Kconfig 2005-01-17 12:30:41 -08:00
+++ edited/drivers/i2c/busses/Kconfig 2005-02-02 18:16:43 -08:00
@@ -476,4 +476,16 @@
This driver can also be built as a module. If so, the module
will be called i2c-pca-isa.
+config I2C_ENVCTRL
+ tristate "Sun Microsystems envctrl I2C bus nexus driver"
+ depends on SPARC64 && PCI
+ select I2C_ALGOPCF
+ help
+ This driver provides access to the PCF8584 I2C bus nexus
+ used for environmental monitoring on certain Sun Microsystems
+ platforms.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-envctrl.
+
endmenu
===== drivers/i2c/busses/Makefile 1.40 vs edited =====
--- 1.40/drivers/i2c/busses/Makefile 2004-11-18 20:39:29 -08:00
+++ edited/drivers/i2c/busses/Makefile 2005-02-02 18:13:43 -08:00
@@ -9,6 +9,7 @@
obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o
obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
+obj-$(CONFIG_I2C_ENVCTRL) += i2c-envctrl.o
obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o
obj-$(CONFIG_I2C_I801) += i2c-i801.o
obj-$(CONFIG_I2C_I810) += i2c-i810.o
===== drivers/i2c/busses/i2c-envctrl.c 1.1 vs edited =====
--- 1.1/drivers/i2c/busses/i2c-envctrl.c 2005-02-03 10:06:36 -08:00
+++ edited/drivers/i2c/busses/i2c-envctrl.c 2005-02-03 10:06:58 -08:00
@@ -0,0 +1,254 @@
+/* i2c-envctrl - PCF8584 I2C bus nexus driver for supported
+ * Sun Microsystems systems
+ *
+ * Copyright (c) 2004-2005 Eric Brower <ebrower@gmail.com>
+ *
+ * Adapted from i2c-elektor.c - reference that file for copyright information
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/wait.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-pcf.h>
+
+#include <asm/ebus.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/i2c-envctrl.h>
+
+#include "../algos/i2c-algo-pcf.h"
+
+struct pcf8584_regs {
+ volatile u8 data;
+ volatile u8 csr;
+};
+
+static struct __iomem pcf8584_regs *i2c;
+static int irq;
+static int clock = 0x1c;
+static int own = 0x55;
+
+static wait_queue_head_t pcf_wait;
+static int pcf_pending;
+static spinlock_t lock;
+
+static struct pcf_envctrl_nexus nexus = {
+ .type = TYPE_UNKNOWN,
+ .i2c_id = -1,
+ .edev = NULL
+};
+
+/* ----- local functions ---------------------------------------------- */
+
+static void pcf_envctrl_setbyte(void *data, int ctl, int val)
+{
+ /* enable irq if any specified for serial operation */
+ if (ctl && irq && (val & I2C_PCF_ESO)) {
+ val |= I2C_PCF_ENI;
+ }
+
+ pr_debug("i2c-envctrl: Write 0x%X 0x%02X\n", address, val & 255);
+
+ if (ctl) {
+ writeb(val, &i2c->csr);
+ }
+ else {
+ writeb(val, &i2c->data);
+ }
+}
+
+static int pcf_envctrl_getbyte(void *data, int ctl)
+{
+ int val;
+
+ if (ctl) {
+ val = readb(&i2c->csr);
+ }
+ else {
+ val = readb(&i2c->data);
+ }
+
+ pr_debug("i2c-envctrl: Read 0x%X 0x%02X\n", address, val);
+
+ return (val);
+}
+
+static int pcf_envctrl_getown(void *data)
+{
+ return (own);
+}
+
+
+static int pcf_envctrl_getclock(void *data)
+{
+ return (clock);
+}
+
+static void pcf_envctrl_waitforpin(void) {
+
+ int timeout = 2;
+ long flags;
+
+ if (irq > 0) {
+ spin_lock_irqsave(&lock, flags);
+ if (pcf_pending == 0) {
+ spin_unlock_irqrestore(&lock, flags);
+ if (interruptible_sleep_on_timeout(&pcf_wait,
+ timeout*HZ)) {
+ spin_lock_irqsave(&lock, flags);
+ if (pcf_pending == 1) {
+ pcf_pending = 0;
+ }
+ spin_unlock_irqrestore(&lock, flags);
+ }
+ } else {
+ pcf_pending = 0;
+ spin_unlock_irqrestore(&lock, flags);
+ }
+ } else {
+ udelay(100);
+ }
+}
+
+
+static irqreturn_t pcf_envctrl_handler(int this_irq, void *dev_id, struct pt_regs *regs) {
+ spin_lock(&lock);
+ pcf_pending = 1;
+ spin_unlock(&lock);
+ wake_up_interruptible(&pcf_wait);
+ return IRQ_HANDLED;
+}
+
+
+static int pcf_envctrl_init(void)
+{
+ spin_lock_init(&lock);
+ if (irq > 0) {
+ if (request_irq(irq, pcf_envctrl_handler, 0, "PCF8584", NULL) < 0) {
+ printk(KERN_ERR "i2c-envctrl: Request irq%d failed\n", irq);
+ irq = 0;
+ }
+ else {
+ enable_irq(irq);
+ }
+ }
+ return 0;
+}
+
+static struct i2c_algo_pcf_data pcf_envctrl_data = {
+ .setpcf = pcf_envctrl_setbyte,
+ .getpcf = pcf_envctrl_getbyte,
+ .getown = pcf_envctrl_getown,
+ .getclock = pcf_envctrl_getclock,
+ .waitforpin = pcf_envctrl_waitforpin,
+ .udelay = 10,
+ .mdelay = 10,
+ .timeout = 100,
+};
+
+static struct i2c_adapter pcf_envctrl_ops = {
+ .owner = THIS_MODULE,
+ .id = I2C_HW_P_ELEK,
+ .algo_data = &pcf_envctrl_data,
+ .name = "PCF8584 bus nexus adapter",
+};
+
+static int __init i2c_envctrl_init(void)
+{
+ struct linux_ebus *ebus = NULL;
+ struct linux_ebus_device *edev = NULL;
+
+ for_each_ebus(ebus) {
+ for_each_ebusdev(edev, ebus) {
+ /* TODO: change this to operate off array */
+ if (!strcmp(edev->prom_name, "SUNW,envctrltwo")) {
+ nexus.type = TYPE_ENVCTRLTWO;
+ goto done;
+ }
+ else if (!strcmp(edev->prom_name, "SUNW,envctrl")) {
+ nexus.type = TYPE_ENVCTRL;
+ goto done;
+ }
+ else if (!strcmp(edev->prom_name, "SUNW,rasctrl")) {
+ nexus.type = TYPE_RASCTRL;
+ goto done;
+ }
+ else if (!strcmp(edev->prom_name, "i2c")) {
+ nexus.type = TYPE_I2C;
+ goto done;
+ }
+ }
+ }
+done:
+ if (!edev) {
+ return -ENODEV;
+ }
+ nexus.edev = edev;
+
+ i2c = ioremap(edev->resource[0].start, sizeof(struct pcf8584_regs));
+ if (!i2c) {
+ return -EINVAL;
+ }
+
+ init_waitqueue_head(&pcf_wait);
+
+ if (pcf_envctrl_init())
+ return -ENODEV;
+
+ if ((nexus.i2c_id = i2c_pcf_add_bus(&pcf_envctrl_ops)) < 0)
+ goto fail;
+
+ printk(KERN_INFO "i2c-envctrl: %s PCF8584 bus nexus at 0x%lx\n",
+ edev->prom_name, (unsigned long)i2c);
+
+ return 0;
+
+ fail:
+ if (irq > 0) {
+ disable_irq(irq);
+ free_irq(irq, NULL);
+ }
+
+ return -ENODEV;
+}
+
+static void i2c_envctrl_exit(void)
+{
+ i2c_pcf_del_bus(&pcf_envctrl_ops);
+
+ if (irq > 0) {
+ disable_irq(irq);
+ free_irq(irq, NULL);
+ }
+
+ if (i2c) {
+ iounmap(i2c);
+ }
+}
+
+struct pcf_envctrl_nexus * pcf_envctrl_get_nexus(void)
+{
+ return &nexus;
+}
+
+EXPORT_SYMBOL(pcf_envctrl_get_nexus);
+
+MODULE_AUTHOR("Eric Brower <ebrower@gmail.com>");
+MODULE_DESCRIPTION("PCF8584 I2C bus nexus driver for Sun Microsystems envctrl");
+MODULE_LICENSE("GPL");
+
+module_param(irq, int, 0);
+module_param(clock, int, 0);
+module_param(own, int, 0);
+
+module_init(i2c_envctrl_init);
+module_exit(i2c_envctrl_exit);
===== include/asm-sparc64/i2c-envctrl.h 1.1 vs edited =====
--- 1.1/include/asm-sparc64/i2c-envctrl.h 2005-02-03 10:10:38 -08:00
+++ edited/include/asm-sparc64/i2c-envctrl.h 2005-02-03 10:10:56 -08:00
@@ -0,0 +1,21 @@
+#ifndef _SPARC64_I2C_ENVCTRL
+#define _SPARC64_I2C_ENVCTRL
+
+enum pcf_envctrl_types {
+ TYPE_UNKNOWN = 0,
+ TYPE_ENVCTRL = 1,
+ TYPE_ENVCTRLTWO,
+ TYPE_RASCTRL,
+ TYPE_I2C,
+};
+typedef enum pcf_envctrl_types pcf_envctrl_type_t;
+
+struct pcf_envctrl_nexus {
+ pcf_envctrl_type_t type;
+ int i2c_id;
+ struct linux_ebus_device *edev;
+};
+
+extern struct pcf_envctrl_nexus * pcf_envctrl_get_nexus(void);
+
+#endif /* _SPARC64_I2C_ENVCTRL */
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC/PATCH] envctrl framework and envctrltwo (UE250) driver
2005-02-22 18:07 [RFC/PATCH] envctrl framework and envctrltwo (UE250) driver Eric Brower
@ 2005-02-22 22:27 ` Eric Brower
2005-02-23 3:46 ` David S. Miller
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Eric Brower @ 2005-02-22 22:27 UTC (permalink / raw)
To: ultralinux
DaveM, et. al,
It occurred to me that we had spoken a long while back about
coalescing the logic for system LED blinking into one location (one
timer, I suppose). This driver does not do that, but we could speak
about how you'd like that done were this driver at some point
considered for inclusion in the kernel.
E
On Tue, 22 Feb 2005 10:07:55 -0800, Eric Brower <ebrower@gmail.com> wrote:
> Attached is a patch against a somewhat dated BK 2.6.10 that provides
> the following:
>
> 1) a basic framework for supporting SUNW,envctrl environmental
> monitoring flavors
> 2) an implementation of the framework for basic SUNW,envctrltwo
> (UE250) support
>
> I am using the term "framework" very lightly here.
>
> This demonstrates use of the in-kernel I2C subsystem rather than the
> current roll-your-own support found in the envctrl and bbc_envctrl
> drivers. There are a few shortcomings with using the in-kernel I2C
> but these can perhaps be ironed-out over time. As well, it addresses
> issue brought up many months ago related to how the monitoring kernel
> thread is started/stopped and how the shutdown functions are invoked--
> basically a freshening. I believe it would be easy to move the
> existing envctrl driver (should be renamed envctrl-i2c or somesuch) to
> this framework as well and I might be convinced to do so if there is
> any interest.
>
> As for the in-kernel I2C shortcomings (as I understand them; correct
> me if you know better):
>
> First, the UE250 is a multi-master I2C bus; this is unlike (so far as
> I know) other envctrl implementations. The in-kernel I2C subsystem
> doesn't handle multimaster, but I've included an I2C patch to support
> detection of lost arbitration and I've passed this on to the lmsensors
> folks (still awaiting a response). This lost arbitration detection
> and recovery could be tightened up a bit, but seems relatively stable.
>
> Second, and related to the first, there does not seem to be a way
> within the I2C subsystem to send multiple I2C messages with dyamic
> content in a bus-atomic fashion, which is required for proper
> multimaster operation. I have no workaround for this, though the I2C
> API could be easily modified to accomodate such an extension, I
> believe. For the moment, our heavy locking (see below) and collision
> detection give us a level of protection but it doesn't make me sleep
> well at night.
>
> Second, the in-kernel I2C isn't written to support use while
> in_interrupt() (it uses semaphores for restricting access), which is a
> requirement of the SUNW,envctrltwo (if we use interrupts...). As a
> workaround, I've wrapped all I2C bus access calls with spinlocks.
>
> Fourth, I don't see any easy way to leverage the use of the
> already-written chip drivers. Implementing support for them was
> trivial, and it is not clear to me how a kernel module could probe
> existing chip drivers and retain a handle to them for in-kernel
> access.
>
> This driver is relatively rough, but has been running on my E250 for
> quite a while under a bit of I2C bus stress. I'd like to see reports
> from folks with dual CPUs, though. Please note that I needed to
> upgrade my BK 2.6 to include SYM53C8XX version 2.1.18n, but that may
> just be due to my setup. I've excessively abused sysfs for access to
> sensors data in this driver. I'd appreciate any feedback.
>
> Thanks,
> E
>
>
>
--
E
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC/PATCH] envctrl framework and envctrltwo (UE250) driver
2005-02-22 18:07 [RFC/PATCH] envctrl framework and envctrltwo (UE250) driver Eric Brower
2005-02-22 22:27 ` Eric Brower
@ 2005-02-23 3:46 ` David S. Miller
2005-02-24 4:10 ` Eric Brower
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: David S. Miller @ 2005-02-23 3:46 UTC (permalink / raw)
To: ultralinux
On Tue, 22 Feb 2005 14:27:58 -0800
Eric Brower <ebrower@gmail.com> wrote:
> DaveM, et. al,
>
> It occurred to me that we had spoken a long while back about
> coalescing the logic for system LED blinking into one location (one
> timer, I suppose). This driver does not do that, but we could speak
> about how you'd like that done were this driver at some point
> considered for inclusion in the kernel.
Don't worry about it for the time being.
I'll review your envctrl stuff soon, it looks really nice.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC/PATCH] envctrl framework and envctrltwo (UE250) driver
2005-02-22 18:07 [RFC/PATCH] envctrl framework and envctrltwo (UE250) driver Eric Brower
2005-02-22 22:27 ` Eric Brower
2005-02-23 3:46 ` David S. Miller
@ 2005-02-24 4:10 ` Eric Brower
2005-02-24 4:42 ` David S. Miller
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Eric Brower @ 2005-02-24 4:10 UTC (permalink / raw)
To: ultralinux
[-- Attachment #1: Type: text/plain, Size: 890 bytes --]
I'm hesitant to provide a patch to a patch, but there was a tiny
buglet in how the Thermal Error LED was represented in sysfs. A
patchlet is attached, but I can resend an entire new patch if
necessary. There are probably a few others lurking in there... :)
E
On Tue, 22 Feb 2005 19:46:02 -0800, David S. Miller <davem@davemloft.net> wrote:
> On Tue, 22 Feb 2005 14:27:58 -0800
> Eric Brower <ebrower@gmail.com> wrote:
>
> > DaveM, et. al,
> >
> > It occurred to me that we had spoken a long while back about
> > coalescing the logic for system LED blinking into one location (one
> > timer, I suppose). This driver does not do that, but we could speak
> > about how you'd like that done were this driver at some point
> > considered for inclusion in the kernel.
>
> Don't worry about it for the time being.
>
> I'll review your envctrl stuff soon, it looks really nice.
>
--
E
[-- Attachment #2: 26_envctrl_templed.patch --]
[-- Type: application/octet-stream, Size: 810 bytes --]
--- arch/sparc64/kernel/env_envctrltwo.c.old 2005-02-23 19:12:10.552473226 -0800
+++ arch/sparc64/kernel/env_envctrltwo.c 2005-02-23 19:12:28.074809426 -0800
@@ -46,7 +46,7 @@
#include "env_lib.h"
#define ENV2_DEVNAME "envctrltwo"
-#define ENV2_VERSION "0.2.0"
+#define ENV2_VERSION "0.3.0"
#define ENV2_DEBUG_THERMS (0) /* dump thermisters found and values probed */
#define ENV2_DEBUG_FAN (0) /* user fanspeed control via sysfs - DANGER! */
@@ -676,7 +676,7 @@
ret += sprintf(buf + ret, "DISK ERROR: [%s]\n",
stat & ENV2_LED_DISKERR ? "OFF" : "ON");
ret += sprintf(buf + ret, "TEMP ERROR: [%s]\n",
- stat & ENV2_LED_DISKERR ? "OFF" : "ON");
+ stat & ENV2_LED_TEMPERR ? "OFF" : "ON");
ret += sprintf(buf + ret, "PS ERROR: [%s]\n",
stat & ENV2_LED_PSERR ? "OFF" : "ON");
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC/PATCH] envctrl framework and envctrltwo (UE250) driver
2005-02-22 18:07 [RFC/PATCH] envctrl framework and envctrltwo (UE250) driver Eric Brower
` (2 preceding siblings ...)
2005-02-24 4:10 ` Eric Brower
@ 2005-02-24 4:42 ` David S. Miller
2005-02-24 19:54 ` David S. Miller
2005-02-24 20:10 ` Eric Brower
5 siblings, 0 replies; 7+ messages in thread
From: David S. Miller @ 2005-02-24 4:42 UTC (permalink / raw)
To: ultralinux
On Wed, 23 Feb 2005 20:10:30 -0800
Eric Brower <ebrower@gmail.com> wrote:
> I'm hesitant to provide a patch to a patch, but there was a tiny
> buglet in how the Thermal Error LED was represented in sysfs. A
> patchlet is attached, but I can resend an entire new patch if
> necessary. There are probably a few others lurking in there... :)
This relative patch is fine, you don't need to resend the rest.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC/PATCH] envctrl framework and envctrltwo (UE250) driver
2005-02-22 18:07 [RFC/PATCH] envctrl framework and envctrltwo (UE250) driver Eric Brower
` (3 preceding siblings ...)
2005-02-24 4:42 ` David S. Miller
@ 2005-02-24 19:54 ` David S. Miller
2005-02-24 20:10 ` Eric Brower
5 siblings, 0 replies; 7+ messages in thread
From: David S. Miller @ 2005-02-24 19:54 UTC (permalink / raw)
To: ultralinux
On Tue, 22 Feb 2005 10:07:55 -0800
Eric Brower <ebrower@gmail.com> wrote:
> Attached is a patch against a somewhat dated BK 2.6.10 that provides
> the following:
>
> 1) a basic framework for supporting SUNW,envctrl environmental
> monitoring flavors
> 2) an implementation of the framework for basic SUNW,envctrltwo
> (UE250) support
I like this a lot. But I'd like to see the I2C layer maintainer
take your lost-arbitration changes in before I merge in your
envctrl driver. Ok?
It looks like greg@kroah.com is the most active in this area, perhaps
contacting him might get a quicker response than the lm-sensors folks.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC/PATCH] envctrl framework and envctrltwo (UE250) driver
2005-02-22 18:07 [RFC/PATCH] envctrl framework and envctrltwo (UE250) driver Eric Brower
` (4 preceding siblings ...)
2005-02-24 19:54 ` David S. Miller
@ 2005-02-24 20:10 ` Eric Brower
5 siblings, 0 replies; 7+ messages in thread
From: Eric Brower @ 2005-02-24 20:10 UTC (permalink / raw)
To: ultralinux
Will do-- I'll keep you posted. The last copyright holder didn't have
any issues with the patch; I've not heard back from the lm78 folks.
Thanks,
E
On Thu, 24 Feb 2005 11:54:30 -0800, David S. Miller <davem@davemloft.net> wrote:
> On Tue, 22 Feb 2005 10:07:55 -0800
> Eric Brower <ebrower@gmail.com> wrote:
>
> > Attached is a patch against a somewhat dated BK 2.6.10 that provides
> > the following:
> >
> > 1) a basic framework for supporting SUNW,envctrl environmental
> > monitoring flavors
> > 2) an implementation of the framework for basic SUNW,envctrltwo
> > (UE250) support
>
> I like this a lot. But I'd like to see the I2C layer maintainer
> take your lost-arbitration changes in before I merge in your
> envctrl driver. Ok?
>
> It looks like greg@kroah.com is the most active in this area, perhaps
> contacting him might get a quicker response than the lm-sensors folks.
>
--
E
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2005-02-24 20:10 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-02-22 18:07 [RFC/PATCH] envctrl framework and envctrltwo (UE250) driver Eric Brower
2005-02-22 22:27 ` Eric Brower
2005-02-23 3:46 ` David S. Miller
2005-02-24 4:10 ` Eric Brower
2005-02-24 4:42 ` David S. Miller
2005-02-24 19:54 ` David S. Miller
2005-02-24 20:10 ` Eric Brower
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox