Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

101
drivers/sbus/char/Kconfig Normal file
View File

@@ -0,0 +1,101 @@
menu "Misc Linux/SPARC drivers"
config SUN_OPENPROMIO
tristate "/dev/openprom device support"
help
This driver provides user programs with an interface to the SPARC
PROM device tree. The driver implements a SunOS-compatible
interface and a NetBSD-compatible interface.
To compile this driver as a module, choose M here: the
module will be called openprom.
If unsure, say Y.
config SUN_MOSTEK_RTC
tristate "Mostek real time clock support"
help
The Mostek RTC chip is used on all known Sun computers except
some JavaStations. For a JavaStation you need to say Y both here
and to "Enhanced Real Time Clock Support".
Say Y here unless you are building a special purpose kernel.
config OBP_FLASH
tristate "OBP Flash Device support"
depends on SPARC64
help
The OpenBoot PROM on Ultra systems is flashable. If you want to be
able to upgrade the OBP firmware, say Y here.
config SUN_BPP
tristate "Bidirectional parallel port support (OBSOLETE)"
depends on EXPERIMENTAL
help
Say Y here to support Sun's obsolete variant of IEEE1284
bidirectional parallel port protocol as /dev/bppX. Can be built on
x86 machines.
config SUN_VIDEOPIX
tristate "Videopix Frame Grabber (EXPERIMENTAL)"
depends on EXPERIMENTAL && (BROKEN || !64BIT)
help
Say Y here to support the Videopix Frame Grabber from Sun
Microsystems, commonly found on SPARCstations. This card, which is
based on the Phillips SAA9051, can handle NTSC and PAL/SECAM and
SVIDEO signals.
config TADPOLE_TS102_UCTRL
tristate "Tadpole TS102 Microcontroller support (EXPERIMENTAL)"
depends on EXPERIMENTAL && SPARC32
help
Say Y here to directly support the TS102 Microcontroller interface
on the Tadpole Sparcbook 3. This device handles power-management
events, and can also notice the attachment/detachment of external
monitors and mice.
config SUN_JSFLASH
tristate "JavaStation OS Flash SIMM (EXPERIMENTAL)"
depends on EXPERIMENTAL && SPARC32
help
If you say Y here, you will be able to boot from your JavaStation's
Flash memory.
config BBC_I2C
tristate "UltraSPARC-III bootbus i2c controller driver"
depends on PCI && SPARC64
help
The BBC devices on the UltraSPARC III have two I2C controllers. The
first I2C controller connects mainly to configuration PROMs (NVRAM,
CPU configuration, DIMM types, etc.). The second I2C controller
connects to environmental control devices such as fans and
temperature sensors. The second controller also connects to the
smartcard reader, if present. Say Y to enable support for these.
config ENVCTRL
tristate "SUNW, envctrl support"
depends on PCI && SPARC64
help
Kernel support for temperature and fan monitoring on Sun SME
machines.
To compile this driver as a module, choose M here: the
module will be called envctrl.
config DISPLAY7SEG
tristate "7-Segment Display support"
depends on PCI && SPARC64
---help---
This is the driver for the 7-segment display and LED present on
Sun Microsystems CompactPCI models CP1400 and CP1500.
To compile this driver as a module, choose M here: the
module will be called display7seg.
If you do not have a CompactPCI model CP1400 or CP1500, or
another UltraSPARC-IIi-cEngine boardset with a 7-segment display,
you should say N to this option.
endmenu

View File

@@ -0,0 +1,24 @@
#
# Makefile for the kernel miscellaneous SPARC device drivers.
#
# Dave Redman Frame Buffer tuning support.
#
# 7 October 2000, Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
# Rewritten to use lists instead of if-statements.
#
vfc-objs := vfc_dev.o vfc_i2c.o
bbc-objs := bbc_i2c.o bbc_envctrl.o
obj-$(CONFIG_ENVCTRL) += envctrl.o
obj-$(CONFIG_DISPLAY7SEG) += display7seg.o
obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwatchdog.o
obj-$(CONFIG_WATCHDOG_RIO) += riowatchdog.o
obj-$(CONFIG_OBP_FLASH) += flash.o
obj-$(CONFIG_SUN_OPENPROMIO) += openprom.o
obj-$(CONFIG_SUN_MOSTEK_RTC) += rtc.o
obj-$(CONFIG_SUN_BPP) += bpp.o
obj-$(CONFIG_SUN_VIDEOPIX) += vfc.o
obj-$(CONFIG_TADPOLE_TS102_UCTRL) += uctrl.o
obj-$(CONFIG_SUN_JSFLASH) += jsflash.o
obj-$(CONFIG_BBC_I2C) += bbc.o

View File

@@ -0,0 +1,621 @@
/* $Id: bbc_envctrl.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
* bbc_envctrl.c: UltraSPARC-III environment control driver.
*
* Copyright (C) 2001 David S. Miller (davem@redhat.com)
*/
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/kmod.h>
#include <asm/oplib.h>
#include <asm/ebus.h>
#include "bbc_i2c.h"
#include "max1617.h"
#undef ENVCTRL_TRACE
/* WARNING: Making changes to this driver is very dangerous.
* If you misprogram the sensor chips they can
* cut the power on you instantly.
*/
/* Two temperature sensors exist in the SunBLADE-1000 enclosure.
* Both are implemented using max1617 i2c devices. Each max1617
* monitors 2 temperatures, one for one of the cpu dies and the other
* for the ambient temperature.
*
* The max1617 is capable of being programmed with power-off
* temperature values, one low limit and one high limit. These
* can be controlled independently for the cpu or ambient temperature.
* If a limit is violated, the power is simply shut off. The frequency
* with which the max1617 does temperature sampling can be controlled
* as well.
*
* Three fans exist inside the machine, all three are controlled with
* an i2c digital to analog converter. There is a fan directed at the
* two processor slots, another for the rest of the enclosure, and the
* third is for the power supply. The first two fans may be speed
* controlled by changing the voltage fed to them. The third fan may
* only be completely off or on. The third fan is meant to only be
* disabled/enabled when entering/exiting the lowest power-saving
* mode of the machine.
*
* An environmental control kernel thread periodically monitors all
* temperature sensors. Based upon the samples it will adjust the
* fan speeds to try and keep the system within a certain temperature
* range (the goal being to make the fans as quiet as possible without
* allowing the system to get too hot).
*
* If the temperature begins to rise/fall outside of the acceptable
* operating range, a periodic warning will be sent to the kernel log.
* The fans will be put on full blast to attempt to deal with this
* situation. After exceeding the acceptable operating range by a
* certain threshold, the kernel thread will shut down the system.
* Here, the thread is attempting to shut the machine down cleanly
* before the hardware based power-off event is triggered.
*/
/* These settings are in Celsius. We use these defaults only
* if we cannot interrogate the cpu-fru SEEPROM.
*/
struct temp_limits {
s8 high_pwroff, high_shutdown, high_warn;
s8 low_warn, low_shutdown, low_pwroff;
};
static struct temp_limits cpu_temp_limits[2] = {
{ 100, 85, 80, 5, -5, -10 },
{ 100, 85, 80, 5, -5, -10 },
};
static struct temp_limits amb_temp_limits[2] = {
{ 65, 55, 40, 5, -5, -10 },
{ 65, 55, 40, 5, -5, -10 },
};
enum fan_action { FAN_SLOWER, FAN_SAME, FAN_FASTER, FAN_FULLBLAST, FAN_STATE_MAX };
struct bbc_cpu_temperature {
struct bbc_cpu_temperature *next;
struct bbc_i2c_client *client;
int index;
/* Current readings, and history. */
s8 curr_cpu_temp;
s8 curr_amb_temp;
s8 prev_cpu_temp;
s8 prev_amb_temp;
s8 avg_cpu_temp;
s8 avg_amb_temp;
int sample_tick;
enum fan_action fan_todo[2];
#define FAN_AMBIENT 0
#define FAN_CPU 1
};
struct bbc_cpu_temperature *all_bbc_temps;
struct bbc_fan_control {
struct bbc_fan_control *next;
struct bbc_i2c_client *client;
int index;
int psupply_fan_on;
int cpu_fan_speed;
int system_fan_speed;
};
struct bbc_fan_control *all_bbc_fans;
#define CPU_FAN_REG 0xf0
#define SYS_FAN_REG 0xf2
#define PSUPPLY_FAN_REG 0xf4
#define FAN_SPEED_MIN 0x0c
#define FAN_SPEED_MAX 0x3f
#define PSUPPLY_FAN_ON 0x1f
#define PSUPPLY_FAN_OFF 0x00
static void set_fan_speeds(struct bbc_fan_control *fp)
{
/* Put temperatures into range so we don't mis-program
* the hardware.
*/
if (fp->cpu_fan_speed < FAN_SPEED_MIN)
fp->cpu_fan_speed = FAN_SPEED_MIN;
if (fp->cpu_fan_speed > FAN_SPEED_MAX)
fp->cpu_fan_speed = FAN_SPEED_MAX;
if (fp->system_fan_speed < FAN_SPEED_MIN)
fp->system_fan_speed = FAN_SPEED_MIN;
if (fp->system_fan_speed > FAN_SPEED_MAX)
fp->system_fan_speed = FAN_SPEED_MAX;
#ifdef ENVCTRL_TRACE
printk("fan%d: Changed fan speed to cpu(%02x) sys(%02x)\n",
fp->index,
fp->cpu_fan_speed, fp->system_fan_speed);
#endif
bbc_i2c_writeb(fp->client, fp->cpu_fan_speed, CPU_FAN_REG);
bbc_i2c_writeb(fp->client, fp->system_fan_speed, SYS_FAN_REG);
bbc_i2c_writeb(fp->client,
(fp->psupply_fan_on ?
PSUPPLY_FAN_ON : PSUPPLY_FAN_OFF),
PSUPPLY_FAN_REG);
}
static void get_current_temps(struct bbc_cpu_temperature *tp)
{
tp->prev_amb_temp = tp->curr_amb_temp;
bbc_i2c_readb(tp->client,
(unsigned char *) &tp->curr_amb_temp,
MAX1617_AMB_TEMP);
tp->prev_cpu_temp = tp->curr_cpu_temp;
bbc_i2c_readb(tp->client,
(unsigned char *) &tp->curr_cpu_temp,
MAX1617_CPU_TEMP);
#ifdef ENVCTRL_TRACE
printk("temp%d: cpu(%d C) amb(%d C)\n",
tp->index,
(int) tp->curr_cpu_temp, (int) tp->curr_amb_temp);
#endif
}
static void do_envctrl_shutdown(struct bbc_cpu_temperature *tp)
{
static int shutting_down = 0;
static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
char *argv[] = { "/sbin/shutdown", "-h", "now", NULL };
char *type = "???";
s8 val = -1;
if (shutting_down != 0)
return;
if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_shutdown ||
tp->curr_amb_temp < amb_temp_limits[tp->index].low_shutdown) {
type = "ambient";
val = tp->curr_amb_temp;
} else if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_shutdown ||
tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_shutdown) {
type = "CPU";
val = tp->curr_cpu_temp;
}
printk(KERN_CRIT "temp%d: Outside of safe %s "
"operating temperature, %d C.\n",
tp->index, type, val);
printk(KERN_CRIT "kenvctrld: Shutting down the system now.\n");
shutting_down = 1;
if (call_usermodehelper("/sbin/shutdown", argv, envp, 0) < 0)
printk(KERN_CRIT "envctrl: shutdown execution failed\n");
}
#define WARN_INTERVAL (30 * HZ)
static void analyze_ambient_temp(struct bbc_cpu_temperature *tp, unsigned long *last_warn, int tick)
{
int ret = 0;
if (time_after(jiffies, (*last_warn + WARN_INTERVAL))) {
if (tp->curr_amb_temp >=
amb_temp_limits[tp->index].high_warn) {
printk(KERN_WARNING "temp%d: "
"Above safe ambient operating temperature, %d C.\n",
tp->index, (int) tp->curr_amb_temp);
ret = 1;
} else if (tp->curr_amb_temp <
amb_temp_limits[tp->index].low_warn) {
printk(KERN_WARNING "temp%d: "
"Below safe ambient operating temperature, %d C.\n",
tp->index, (int) tp->curr_amb_temp);
ret = 1;
}
if (ret)
*last_warn = jiffies;
} else if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_warn ||
tp->curr_amb_temp < amb_temp_limits[tp->index].low_warn)
ret = 1;
/* Now check the shutdown limits. */
if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_shutdown ||
tp->curr_amb_temp < amb_temp_limits[tp->index].low_shutdown) {
do_envctrl_shutdown(tp);
ret = 1;
}
if (ret) {
tp->fan_todo[FAN_AMBIENT] = FAN_FULLBLAST;
} else if ((tick & (8 - 1)) == 0) {
s8 amb_goal_hi = amb_temp_limits[tp->index].high_warn - 10;
s8 amb_goal_lo;
amb_goal_lo = amb_goal_hi - 3;
/* We do not try to avoid 'too cold' events. Basically we
* only try to deal with over-heating and fan noise reduction.
*/
if (tp->avg_amb_temp < amb_goal_hi) {
if (tp->avg_amb_temp >= amb_goal_lo)
tp->fan_todo[FAN_AMBIENT] = FAN_SAME;
else
tp->fan_todo[FAN_AMBIENT] = FAN_SLOWER;
} else {
tp->fan_todo[FAN_AMBIENT] = FAN_FASTER;
}
} else {
tp->fan_todo[FAN_AMBIENT] = FAN_SAME;
}
}
static void analyze_cpu_temp(struct bbc_cpu_temperature *tp, unsigned long *last_warn, int tick)
{
int ret = 0;
if (time_after(jiffies, (*last_warn + WARN_INTERVAL))) {
if (tp->curr_cpu_temp >=
cpu_temp_limits[tp->index].high_warn) {
printk(KERN_WARNING "temp%d: "
"Above safe CPU operating temperature, %d C.\n",
tp->index, (int) tp->curr_cpu_temp);
ret = 1;
} else if (tp->curr_cpu_temp <
cpu_temp_limits[tp->index].low_warn) {
printk(KERN_WARNING "temp%d: "
"Below safe CPU operating temperature, %d C.\n",
tp->index, (int) tp->curr_cpu_temp);
ret = 1;
}
if (ret)
*last_warn = jiffies;
} else if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_warn ||
tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_warn)
ret = 1;
/* Now check the shutdown limits. */
if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_shutdown ||
tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_shutdown) {
do_envctrl_shutdown(tp);
ret = 1;
}
if (ret) {
tp->fan_todo[FAN_CPU] = FAN_FULLBLAST;
} else if ((tick & (8 - 1)) == 0) {
s8 cpu_goal_hi = cpu_temp_limits[tp->index].high_warn - 10;
s8 cpu_goal_lo;
cpu_goal_lo = cpu_goal_hi - 3;
/* We do not try to avoid 'too cold' events. Basically we
* only try to deal with over-heating and fan noise reduction.
*/
if (tp->avg_cpu_temp < cpu_goal_hi) {
if (tp->avg_cpu_temp >= cpu_goal_lo)
tp->fan_todo[FAN_CPU] = FAN_SAME;
else
tp->fan_todo[FAN_CPU] = FAN_SLOWER;
} else {
tp->fan_todo[FAN_CPU] = FAN_FASTER;
}
} else {
tp->fan_todo[FAN_CPU] = FAN_SAME;
}
}
static void analyze_temps(struct bbc_cpu_temperature *tp, unsigned long *last_warn)
{
tp->avg_amb_temp = (s8)((int)((int)tp->avg_amb_temp + (int)tp->curr_amb_temp) / 2);
tp->avg_cpu_temp = (s8)((int)((int)tp->avg_cpu_temp + (int)tp->curr_cpu_temp) / 2);
analyze_ambient_temp(tp, last_warn, tp->sample_tick);
analyze_cpu_temp(tp, last_warn, tp->sample_tick);
tp->sample_tick++;
}
static enum fan_action prioritize_fan_action(int which_fan)
{
struct bbc_cpu_temperature *tp;
enum fan_action decision = FAN_STATE_MAX;
/* Basically, prioritize what the temperature sensors
* recommend we do, and perform that action on all the
* fans.
*/
for (tp = all_bbc_temps; tp; tp = tp->next) {
if (tp->fan_todo[which_fan] == FAN_FULLBLAST) {
decision = FAN_FULLBLAST;
break;
}
if (tp->fan_todo[which_fan] == FAN_SAME &&
decision != FAN_FASTER)
decision = FAN_SAME;
else if (tp->fan_todo[which_fan] == FAN_FASTER)
decision = FAN_FASTER;
else if (decision != FAN_FASTER &&
decision != FAN_SAME &&
tp->fan_todo[which_fan] == FAN_SLOWER)
decision = FAN_SLOWER;
}
if (decision == FAN_STATE_MAX)
decision = FAN_SAME;
return decision;
}
static int maybe_new_ambient_fan_speed(struct bbc_fan_control *fp)
{
enum fan_action decision = prioritize_fan_action(FAN_AMBIENT);
int ret;
if (decision == FAN_SAME)
return 0;
ret = 1;
if (decision == FAN_FULLBLAST) {
if (fp->system_fan_speed >= FAN_SPEED_MAX)
ret = 0;
else
fp->system_fan_speed = FAN_SPEED_MAX;
} else {
if (decision == FAN_FASTER) {
if (fp->system_fan_speed >= FAN_SPEED_MAX)
ret = 0;
else
fp->system_fan_speed += 2;
} else {
int orig_speed = fp->system_fan_speed;
if (orig_speed <= FAN_SPEED_MIN ||
orig_speed <= (fp->cpu_fan_speed - 3))
ret = 0;
else
fp->system_fan_speed -= 1;
}
}
return ret;
}
static int maybe_new_cpu_fan_speed(struct bbc_fan_control *fp)
{
enum fan_action decision = prioritize_fan_action(FAN_CPU);
int ret;
if (decision == FAN_SAME)
return 0;
ret = 1;
if (decision == FAN_FULLBLAST) {
if (fp->cpu_fan_speed >= FAN_SPEED_MAX)
ret = 0;
else
fp->cpu_fan_speed = FAN_SPEED_MAX;
} else {
if (decision == FAN_FASTER) {
if (fp->cpu_fan_speed >= FAN_SPEED_MAX)
ret = 0;
else {
fp->cpu_fan_speed += 2;
if (fp->system_fan_speed <
(fp->cpu_fan_speed - 3))
fp->system_fan_speed =
fp->cpu_fan_speed - 3;
}
} else {
if (fp->cpu_fan_speed <= FAN_SPEED_MIN)
ret = 0;
else
fp->cpu_fan_speed -= 1;
}
}
return ret;
}
static void maybe_new_fan_speeds(struct bbc_fan_control *fp)
{
int new;
new = maybe_new_ambient_fan_speed(fp);
new |= maybe_new_cpu_fan_speed(fp);
if (new)
set_fan_speeds(fp);
}
static void fans_full_blast(void)
{
struct bbc_fan_control *fp;
/* Since we will not be monitoring things anymore, put
* the fans on full blast.
*/
for (fp = all_bbc_fans; fp; fp = fp->next) {
fp->cpu_fan_speed = FAN_SPEED_MAX;
fp->system_fan_speed = FAN_SPEED_MAX;
fp->psupply_fan_on = 1;
set_fan_speeds(fp);
}
}
#define POLL_INTERVAL (5 * 1000)
static unsigned long last_warning_jiffies;
static struct task_struct *kenvctrld_task;
static int kenvctrld(void *__unused)
{
printk(KERN_INFO "bbc_envctrl: kenvctrld starting...\n");
last_warning_jiffies = jiffies - WARN_INTERVAL;
for (;;) {
struct bbc_cpu_temperature *tp;
struct bbc_fan_control *fp;
msleep_interruptible(POLL_INTERVAL);
if (kthread_should_stop())
break;
for (tp = all_bbc_temps; tp; tp = tp->next) {
get_current_temps(tp);
analyze_temps(tp, &last_warning_jiffies);
}
for (fp = all_bbc_fans; fp; fp = fp->next)
maybe_new_fan_speeds(fp);
}
printk(KERN_INFO "bbc_envctrl: kenvctrld exiting...\n");
fans_full_blast();
return 0;
}
static void attach_one_temp(struct linux_ebus_child *echild, int temp_idx)
{
struct bbc_cpu_temperature *tp = kmalloc(sizeof(*tp), GFP_KERNEL);
if (!tp)
return;
memset(tp, 0, sizeof(*tp));
tp->client = bbc_i2c_attach(echild);
if (!tp->client) {
kfree(tp);
return;
}
tp->index = temp_idx;
{
struct bbc_cpu_temperature **tpp = &all_bbc_temps;
while (*tpp)
tpp = &((*tpp)->next);
tp->next = NULL;
*tpp = tp;
}
/* Tell it to convert once every 5 seconds, clear all cfg
* bits.
*/
bbc_i2c_writeb(tp->client, 0x00, MAX1617_WR_CFG_BYTE);
bbc_i2c_writeb(tp->client, 0x02, MAX1617_WR_CVRATE_BYTE);
/* Program the hard temperature limits into the chip. */
bbc_i2c_writeb(tp->client, amb_temp_limits[tp->index].high_pwroff,
MAX1617_WR_AMB_HIGHLIM);
bbc_i2c_writeb(tp->client, amb_temp_limits[tp->index].low_pwroff,
MAX1617_WR_AMB_LOWLIM);
bbc_i2c_writeb(tp->client, cpu_temp_limits[tp->index].high_pwroff,
MAX1617_WR_CPU_HIGHLIM);
bbc_i2c_writeb(tp->client, cpu_temp_limits[tp->index].low_pwroff,
MAX1617_WR_CPU_LOWLIM);
get_current_temps(tp);
tp->prev_cpu_temp = tp->avg_cpu_temp = tp->curr_cpu_temp;
tp->prev_amb_temp = tp->avg_amb_temp = tp->curr_amb_temp;
tp->fan_todo[FAN_AMBIENT] = FAN_SAME;
tp->fan_todo[FAN_CPU] = FAN_SAME;
}
static void attach_one_fan(struct linux_ebus_child *echild, int fan_idx)
{
struct bbc_fan_control *fp = kmalloc(sizeof(*fp), GFP_KERNEL);
if (!fp)
return;
memset(fp, 0, sizeof(*fp));
fp->client = bbc_i2c_attach(echild);
if (!fp->client) {
kfree(fp);
return;
}
fp->index = fan_idx;
{
struct bbc_fan_control **fpp = &all_bbc_fans;
while (*fpp)
fpp = &((*fpp)->next);
fp->next = NULL;
*fpp = fp;
}
/* The i2c device controlling the fans is write-only.
* So the only way to keep track of the current power
* level fed to the fans is via software. Choose half
* power for cpu/system and 'on' fo the powersupply fan
* and set it now.
*/
fp->psupply_fan_on = 1;
fp->cpu_fan_speed = (FAN_SPEED_MAX - FAN_SPEED_MIN) / 2;
fp->cpu_fan_speed += FAN_SPEED_MIN;
fp->system_fan_speed = (FAN_SPEED_MAX - FAN_SPEED_MIN) / 2;
fp->system_fan_speed += FAN_SPEED_MIN;
set_fan_speeds(fp);
}
int bbc_envctrl_init(void)
{
struct linux_ebus_child *echild;
int temp_index = 0;
int fan_index = 0;
int devidx = 0;
while ((echild = bbc_i2c_getdev(devidx++)) != NULL) {
if (!strcmp(echild->prom_node->name, "temperature"))
attach_one_temp(echild, temp_index++);
if (!strcmp(echild->prom_node->name, "fan-control"))
attach_one_fan(echild, fan_index++);
}
if (temp_index != 0 && fan_index != 0) {
kenvctrld_task = kthread_run(kenvctrld, NULL, "kenvctrld");
if (IS_ERR(kenvctrld_task))
return PTR_ERR(kenvctrld_task);
}
return 0;
}
static void destroy_one_temp(struct bbc_cpu_temperature *tp)
{
bbc_i2c_detach(tp->client);
kfree(tp);
}
static void destroy_one_fan(struct bbc_fan_control *fp)
{
bbc_i2c_detach(fp->client);
kfree(fp);
}
void bbc_envctrl_cleanup(void)
{
struct bbc_cpu_temperature *tp;
struct bbc_fan_control *fp;
kthread_stop(kenvctrld_task);
tp = all_bbc_temps;
while (tp != NULL) {
struct bbc_cpu_temperature *next = tp->next;
destroy_one_temp(tp);
tp = next;
}
all_bbc_temps = NULL;
fp = all_bbc_fans;
while (fp != NULL) {
struct bbc_fan_control *next = fp->next;
destroy_one_fan(fp);
fp = next;
}
all_bbc_fans = NULL;
}

491
drivers/sbus/char/bbc_i2c.c Normal file
View File

@@ -0,0 +1,491 @@
/* $Id: bbc_i2c.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
* bbc_i2c.c: I2C low-level driver for BBC device on UltraSPARC-III
* platforms.
*
* Copyright (C) 2001 David S. Miller (davem@redhat.com)
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <asm/oplib.h>
#include <asm/ebus.h>
#include <asm/spitfire.h>
#include <asm/bbc.h>
#include "bbc_i2c.h"
/* Convert this driver to use i2c bus layer someday... */
#define I2C_PCF_PIN 0x80
#define I2C_PCF_ESO 0x40
#define I2C_PCF_ES1 0x20
#define I2C_PCF_ES2 0x10
#define I2C_PCF_ENI 0x08
#define I2C_PCF_STA 0x04
#define I2C_PCF_STO 0x02
#define I2C_PCF_ACK 0x01
#define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ENI | I2C_PCF_STA | I2C_PCF_ACK)
#define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK)
#define I2C_PCF_REPSTART ( I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK)
#define I2C_PCF_IDLE (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ACK)
#define I2C_PCF_INI 0x40 /* 1 if not initialized */
#define I2C_PCF_STS 0x20
#define I2C_PCF_BER 0x10
#define I2C_PCF_AD0 0x08
#define I2C_PCF_LRB 0x08
#define I2C_PCF_AAS 0x04
#define I2C_PCF_LAB 0x02
#define I2C_PCF_BB 0x01
/* The BBC devices have two I2C controllers. The first I2C controller
* connects mainly to configuration proms (NVRAM, cpu configuration,
* dimm types, etc.). Whereas the second I2C controller connects to
* environmental control devices such as fans and temperature sensors.
* The second controller also connects to the smartcard reader, if present.
*/
#define NUM_CHILDREN 8
struct bbc_i2c_bus {
struct bbc_i2c_bus *next;
int index;
spinlock_t lock;
void __iomem *i2c_bussel_reg;
void __iomem *i2c_control_regs;
unsigned char own, clock;
wait_queue_head_t wq;
volatile int waiting;
struct linux_ebus_device *bus_edev;
struct {
struct linux_ebus_child *device;
int client_claimed;
} devs[NUM_CHILDREN];
};
static struct bbc_i2c_bus *all_bbc_i2c;
struct bbc_i2c_client {
struct bbc_i2c_bus *bp;
struct linux_ebus_child *echild;
int bus;
int address;
};
static int find_device(struct bbc_i2c_bus *bp, struct linux_ebus_child *echild)
{
int i;
for (i = 0; i < NUM_CHILDREN; i++) {
if (bp->devs[i].device == echild) {
if (bp->devs[i].client_claimed)
return 0;
return 1;
}
}
return 0;
}
static void set_device_claimage(struct bbc_i2c_bus *bp, struct linux_ebus_child *echild, int val)
{
int i;
for (i = 0; i < NUM_CHILDREN; i++) {
if (bp->devs[i].device == echild) {
bp->devs[i].client_claimed = val;
return;
}
}
}
#define claim_device(BP,ECHILD) set_device_claimage(BP,ECHILD,1)
#define release_device(BP,ECHILD) set_device_claimage(BP,ECHILD,0)
static struct bbc_i2c_bus *find_bus_for_device(struct linux_ebus_child *echild)
{
struct bbc_i2c_bus *bp = all_bbc_i2c;
while (bp != NULL) {
if (find_device(bp, echild) != 0)
break;
bp = bp->next;
}
return bp;
}
struct linux_ebus_child *bbc_i2c_getdev(int index)
{
struct bbc_i2c_bus *bp = all_bbc_i2c;
struct linux_ebus_child *echild = NULL;
int curidx = 0;
while (bp != NULL) {
struct bbc_i2c_bus *next = bp->next;
int i;
for (i = 0; i < NUM_CHILDREN; i++) {
if (!(echild = bp->devs[i].device))
break;
if (curidx == index)
goto out;
echild = NULL;
curidx++;
}
bp = next;
}
out:
if (curidx == index)
return echild;
return NULL;
}
struct bbc_i2c_client *bbc_i2c_attach(struct linux_ebus_child *echild)
{
struct bbc_i2c_bus *bp = find_bus_for_device(echild);
struct bbc_i2c_client *client;
if (!bp)
return NULL;
client = kmalloc(sizeof(*client), GFP_KERNEL);
if (!client)
return NULL;
memset(client, 0, sizeof(*client));
client->bp = bp;
client->echild = echild;
client->bus = echild->resource[0].start;
client->address = echild->resource[1].start;
claim_device(bp, echild);
return client;
}
void bbc_i2c_detach(struct bbc_i2c_client *client)
{
struct bbc_i2c_bus *bp = client->bp;
struct linux_ebus_child *echild = client->echild;
release_device(bp, echild);
kfree(client);
}
static int wait_for_pin(struct bbc_i2c_bus *bp, u8 *status)
{
DECLARE_WAITQUEUE(wait, current);
int limit = 32;
int ret = 1;
bp->waiting = 1;
add_wait_queue(&bp->wq, &wait);
while (limit-- > 0) {
unsigned long val;
val = wait_event_interruptible_timeout(
bp->wq,
(((*status = readb(bp->i2c_control_regs + 0))
& I2C_PCF_PIN) == 0),
msecs_to_jiffies(250));
if (val > 0) {
ret = 0;
break;
}
}
remove_wait_queue(&bp->wq, &wait);
bp->waiting = 0;
return ret;
}
int bbc_i2c_writeb(struct bbc_i2c_client *client, unsigned char val, int off)
{
struct bbc_i2c_bus *bp = client->bp;
int address = client->address;
u8 status;
int ret = -1;
if (bp->i2c_bussel_reg != NULL)
writeb(client->bus, bp->i2c_bussel_reg);
writeb(address, bp->i2c_control_regs + 0x1);
writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0);
if (wait_for_pin(bp, &status))
goto out;
writeb(off, bp->i2c_control_regs + 0x1);
if (wait_for_pin(bp, &status) ||
(status & I2C_PCF_LRB) != 0)
goto out;
writeb(val, bp->i2c_control_regs + 0x1);
if (wait_for_pin(bp, &status))
goto out;
ret = 0;
out:
writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0);
return ret;
}
int bbc_i2c_readb(struct bbc_i2c_client *client, unsigned char *byte, int off)
{
struct bbc_i2c_bus *bp = client->bp;
unsigned char address = client->address, status;
int ret = -1;
if (bp->i2c_bussel_reg != NULL)
writeb(client->bus, bp->i2c_bussel_reg);
writeb(address, bp->i2c_control_regs + 0x1);
writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0);
if (wait_for_pin(bp, &status))
goto out;
writeb(off, bp->i2c_control_regs + 0x1);
if (wait_for_pin(bp, &status) ||
(status & I2C_PCF_LRB) != 0)
goto out;
writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0);
address |= 0x1; /* READ */
writeb(address, bp->i2c_control_regs + 0x1);
writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0);
if (wait_for_pin(bp, &status))
goto out;
/* Set PIN back to one so the device sends the first
* byte.
*/
(void) readb(bp->i2c_control_regs + 0x1);
if (wait_for_pin(bp, &status))
goto out;
writeb(I2C_PCF_ESO | I2C_PCF_ENI, bp->i2c_control_regs + 0x0);
*byte = readb(bp->i2c_control_regs + 0x1);
if (wait_for_pin(bp, &status))
goto out;
ret = 0;
out:
writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0);
(void) readb(bp->i2c_control_regs + 0x1);
return ret;
}
int bbc_i2c_write_buf(struct bbc_i2c_client *client,
char *buf, int len, int off)
{
int ret = 0;
while (len > 0) {
int err = bbc_i2c_writeb(client, *buf, off);
if (err < 0) {
ret = err;
break;
}
len--;
buf++;
off++;
}
return ret;
}
int bbc_i2c_read_buf(struct bbc_i2c_client *client,
char *buf, int len, int off)
{
int ret = 0;
while (len > 0) {
int err = bbc_i2c_readb(client, buf, off);
if (err < 0) {
ret = err;
break;
}
len--;
buf++;
off++;
}
return ret;
}
EXPORT_SYMBOL(bbc_i2c_getdev);
EXPORT_SYMBOL(bbc_i2c_attach);
EXPORT_SYMBOL(bbc_i2c_detach);
EXPORT_SYMBOL(bbc_i2c_writeb);
EXPORT_SYMBOL(bbc_i2c_readb);
EXPORT_SYMBOL(bbc_i2c_write_buf);
EXPORT_SYMBOL(bbc_i2c_read_buf);
static irqreturn_t bbc_i2c_interrupt(int irq, void *dev_id)
{
struct bbc_i2c_bus *bp = dev_id;
/* PIN going from set to clear is the only event which
* makes the i2c assert an interrupt.
*/
if (bp->waiting &&
!(readb(bp->i2c_control_regs + 0x0) & I2C_PCF_PIN))
wake_up_interruptible(&bp->wq);
return IRQ_HANDLED;
}
static void __init reset_one_i2c(struct bbc_i2c_bus *bp)
{
writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0);
writeb(bp->own, bp->i2c_control_regs + 0x1);
writeb(I2C_PCF_PIN | I2C_PCF_ES1, bp->i2c_control_regs + 0x0);
writeb(bp->clock, bp->i2c_control_regs + 0x1);
writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0);
}
static int __init attach_one_i2c(struct linux_ebus_device *edev, int index)
{
struct bbc_i2c_bus *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
struct linux_ebus_child *echild;
int entry;
if (!bp)
return -ENOMEM;
memset(bp, 0, sizeof(*bp));
bp->i2c_control_regs = ioremap(edev->resource[0].start, 0x2);
if (!bp->i2c_control_regs)
goto fail;
if (edev->num_addrs == 2) {
bp->i2c_bussel_reg = ioremap(edev->resource[1].start, 0x1);
if (!bp->i2c_bussel_reg)
goto fail;
}
bp->waiting = 0;
init_waitqueue_head(&bp->wq);
if (request_irq(edev->irqs[0], bbc_i2c_interrupt,
IRQF_SHARED, "bbc_i2c", bp))
goto fail;
bp->index = index;
bp->bus_edev = edev;
spin_lock_init(&bp->lock);
bp->next = all_bbc_i2c;
all_bbc_i2c = bp;
entry = 0;
for (echild = edev->children;
echild && entry < 8;
echild = echild->next, entry++) {
bp->devs[entry].device = echild;
bp->devs[entry].client_claimed = 0;
}
writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0);
bp->own = readb(bp->i2c_control_regs + 0x01);
writeb(I2C_PCF_PIN | I2C_PCF_ES1, bp->i2c_control_regs + 0x0);
bp->clock = readb(bp->i2c_control_regs + 0x01);
printk(KERN_INFO "i2c-%d: Regs at %p, %d devices, own %02x, clock %02x.\n",
bp->index, bp->i2c_control_regs, entry, bp->own, bp->clock);
reset_one_i2c(bp);
return 0;
fail:
if (bp->i2c_bussel_reg)
iounmap(bp->i2c_bussel_reg);
if (bp->i2c_control_regs)
iounmap(bp->i2c_control_regs);
kfree(bp);
return -EINVAL;
}
static int __init bbc_present(void)
{
struct linux_ebus *ebus = NULL;
struct linux_ebus_device *edev = NULL;
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
if (!strcmp(edev->prom_node->name, "bbc"))
return 1;
}
}
return 0;
}
extern int bbc_envctrl_init(void);
extern void bbc_envctrl_cleanup(void);
static void bbc_i2c_cleanup(void);
static int __init bbc_i2c_init(void)
{
struct linux_ebus *ebus = NULL;
struct linux_ebus_device *edev = NULL;
int err, index = 0;
if ((tlb_type != cheetah && tlb_type != cheetah_plus) ||
!bbc_present())
return -ENODEV;
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
if (!strcmp(edev->prom_node->name, "i2c")) {
if (!attach_one_i2c(edev, index))
index++;
}
}
}
if (!index)
return -ENODEV;
err = bbc_envctrl_init();
if (err)
bbc_i2c_cleanup();
return err;
}
static void bbc_i2c_cleanup(void)
{
struct bbc_i2c_bus *bp = all_bbc_i2c;
bbc_envctrl_cleanup();
while (bp != NULL) {
struct bbc_i2c_bus *next = bp->next;
free_irq(bp->bus_edev->irqs[0], bp);
if (bp->i2c_bussel_reg)
iounmap(bp->i2c_bussel_reg);
if (bp->i2c_control_regs)
iounmap(bp->i2c_control_regs);
kfree(bp);
bp = next;
}
all_bbc_i2c = NULL;
}
module_init(bbc_i2c_init);
module_exit(bbc_i2c_cleanup);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,20 @@
/* $Id: bbc_i2c.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $ */
#ifndef _BBC_I2C_H
#define _BBC_I2C_H
#include <asm/ebus.h>
struct bbc_i2c_client;
/* Probing and attachment. */
extern struct linux_ebus_child *bbc_i2c_getdev(int);
extern struct bbc_i2c_client *bbc_i2c_attach(struct linux_ebus_child *);
extern void bbc_i2c_detach(struct bbc_i2c_client *);
/* Register read/write. NOTE: Blocking! */
extern int bbc_i2c_writeb(struct bbc_i2c_client *, unsigned char val, int off);
extern int bbc_i2c_readb(struct bbc_i2c_client *, unsigned char *byte, int off);
extern int bbc_i2c_write_buf(struct bbc_i2c_client *, char *buf, int len, int off);
extern int bbc_i2c_read_buf(struct bbc_i2c_client *, char *buf, int len, int off);
#endif /* _BBC_I2C_H */

1053
drivers/sbus/char/bpp.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,853 @@
/* cpwatchdog.c - driver implementation for hardware watchdog
* timers found on Sun Microsystems CP1400 and CP1500 boards.
*
* This device supports both the generic Linux watchdog
* interface and Solaris-compatible ioctls as best it is
* able.
*
* NOTE: CP1400 systems appear to have a defective intr_mask
* register on the PLD, preventing the disabling of
* timer interrupts. We use a timer to periodically
* reset 'stopped' watchdogs on affected platforms.
*
* Copyright (c) 2000 Eric Brower (ebrower@usa.net)
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/major.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/timer.h>
#include <linux/smp_lock.h>
#include <asm/irq.h>
#include <asm/ebus.h>
#include <asm/oplib.h>
#include <asm/uaccess.h>
#include <asm/watchdog.h>
#define WD_OBPNAME "watchdog"
#define WD_BADMODEL "SUNW,501-5336"
#define WD_BTIMEOUT (jiffies + (HZ * 1000))
#define WD_BLIMIT 0xFFFF
#define WD0_DEVNAME "watchdog0"
#define WD1_DEVNAME "watchdog1"
#define WD2_DEVNAME "watchdog2"
#define WD0_MINOR 212
#define WD1_MINOR 213
#define WD2_MINOR 214
/* Internal driver definitions
*/
#define WD0_ID 0 /* Watchdog0 */
#define WD1_ID 1 /* Watchdog1 */
#define WD2_ID 2 /* Watchdog2 */
#define WD_NUMDEVS 3 /* Device contains 3 timers */
#define WD_INTR_OFF 0 /* Interrupt disable value */
#define WD_INTR_ON 1 /* Interrupt enable value */
#define WD_STAT_INIT 0x01 /* Watchdog timer is initialized */
#define WD_STAT_BSTOP 0x02 /* Watchdog timer is brokenstopped */
#define WD_STAT_SVCD 0x04 /* Watchdog interrupt occurred */
/* Register value definitions
*/
#define WD0_INTR_MASK 0x01 /* Watchdog device interrupt masks */
#define WD1_INTR_MASK 0x02
#define WD2_INTR_MASK 0x04
#define WD_S_RUNNING 0x01 /* Watchdog device status running */
#define WD_S_EXPIRED 0x02 /* Watchdog device status expired */
/* Sun uses Altera PLD EPF8820ATC144-4
* providing three hardware watchdogs:
*
* 1) RIC - sends an interrupt when triggered
* 2) XIR - asserts XIR_B_RESET when triggered, resets CPU
* 3) POR - asserts POR_B_RESET when triggered, resets CPU, backplane, board
*
*** Timer register block definition (struct wd_timer_regblk)
*
* dcntr and limit registers (halfword access):
* -------------------
* | 15 | ...| 1 | 0 |
* -------------------
* |- counter val -|
* -------------------
* dcntr - Current 16-bit downcounter value.
* When downcounter reaches '0' watchdog expires.
* Reading this register resets downcounter with 'limit' value.
* limit - 16-bit countdown value in 1/10th second increments.
* Writing this register begins countdown with input value.
* Reading from this register does not affect counter.
* NOTES: After watchdog reset, dcntr and limit contain '1'
*
* status register (byte access):
* ---------------------------
* | 7 | ... | 2 | 1 | 0 |
* --------------+------------
* |- UNUSED -| EXP | RUN |
* ---------------------------
* status- Bit 0 - Watchdog is running
* Bit 1 - Watchdog has expired
*
*** PLD register block definition (struct wd_pld_regblk)
*
* intr_mask register (byte access):
* ---------------------------------
* | 7 | ... | 3 | 2 | 1 | 0 |
* +-------------+------------------
* |- UNUSED -| WD3 | WD2 | WD1 |
* ---------------------------------
* WD3 - 1 == Interrupt disabled for watchdog 3
* WD2 - 1 == Interrupt disabled for watchdog 2
* WD1 - 1 == Interrupt disabled for watchdog 1
*
* pld_status register (byte access):
* UNKNOWN, MAGICAL MYSTERY REGISTER
*
*/
#define WD_TIMER_REGSZ 16
#define WD0_OFF 0
#define WD1_OFF (WD_TIMER_REGSZ * 1)
#define WD2_OFF (WD_TIMER_REGSZ * 2)
#define PLD_OFF (WD_TIMER_REGSZ * 3)
#define WD_DCNTR 0x00
#define WD_LIMIT 0x04
#define WD_STATUS 0x08
#define PLD_IMASK (PLD_OFF + 0x00)
#define PLD_STATUS (PLD_OFF + 0x04)
/* Individual timer structure
*/
struct wd_timer {
__u16 timeout;
__u8 intr_mask;
unsigned char runstatus;
void __iomem *regs;
};
/* Device structure
*/
struct wd_device {
int irq;
spinlock_t lock;
unsigned char isbaddoggie; /* defective PLD */
unsigned char opt_enable;
unsigned char opt_reboot;
unsigned short opt_timeout;
unsigned char initialized;
struct wd_timer watchdog[WD_NUMDEVS];
void __iomem *regs;
};
static struct wd_device wd_dev = {
0, SPIN_LOCK_UNLOCKED, 0, 0, 0, 0,
};
static struct timer_list wd_timer;
static int wd0_timeout = 0;
static int wd1_timeout = 0;
static int wd2_timeout = 0;
#ifdef MODULE
module_param (wd0_timeout, int, 0);
MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs");
module_param (wd1_timeout, int, 0);
MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs");
module_param (wd2_timeout, int, 0);
MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs");
MODULE_AUTHOR
("Eric Brower <ebrower@usa.net>");
MODULE_DESCRIPTION
("Hardware watchdog driver for Sun Microsystems CP1400/1500");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE
("watchdog");
#endif /* ifdef MODULE */
/* Forward declarations of internal methods
*/
#ifdef WD_DEBUG
static void wd_dumpregs(void);
#endif
static irqreturn_t wd_interrupt(int irq, void *dev_id);
static void wd_toggleintr(struct wd_timer* pTimer, int enable);
static void wd_pingtimer(struct wd_timer* pTimer);
static void wd_starttimer(struct wd_timer* pTimer);
static void wd_resetbrokentimer(struct wd_timer* pTimer);
static void wd_stoptimer(struct wd_timer* pTimer);
static void wd_brokentimer(unsigned long data);
static int wd_getstatus(struct wd_timer* pTimer);
/* PLD expects words to be written in LSB format,
* so we must flip all words prior to writing them to regs
*/
static inline unsigned short flip_word(unsigned short word)
{
return ((word & 0xff) << 8) | ((word >> 8) & 0xff);
}
#define wd_writew(val, addr) (writew(flip_word(val), addr))
#define wd_readw(addr) (flip_word(readw(addr)))
#define wd_writeb(val, addr) (writeb(val, addr))
#define wd_readb(addr) (readb(addr))
/* CP1400s seem to have broken PLD implementations--
* the interrupt_mask register cannot be written, so
* no timer interrupts can be masked within the PLD.
*/
static inline int wd_isbroken(void)
{
/* we could test this by read/write/read/restore
* on the interrupt mask register only if OBP
* 'watchdog-enable?' == FALSE, but it seems
* ubiquitous on CP1400s
*/
char val[32];
prom_getproperty(prom_root_node, "model", val, sizeof(val));
return((!strcmp(val, WD_BADMODEL)) ? 1 : 0);
}
/* Retrieve watchdog-enable? option from OBP
* Returns 0 if false, 1 if true
*/
static inline int wd_opt_enable(void)
{
int opt_node;
opt_node = prom_getchild(prom_root_node);
opt_node = prom_searchsiblings(opt_node, "options");
return((-1 == prom_getint(opt_node, "watchdog-enable?")) ? 0 : 1);
}
/* Retrieve watchdog-reboot? option from OBP
* Returns 0 if false, 1 if true
*/
static inline int wd_opt_reboot(void)
{
int opt_node;
opt_node = prom_getchild(prom_root_node);
opt_node = prom_searchsiblings(opt_node, "options");
return((-1 == prom_getint(opt_node, "watchdog-reboot?")) ? 0 : 1);
}
/* Retrieve watchdog-timeout option from OBP
* Returns OBP value, or 0 if not located
*/
static inline int wd_opt_timeout(void)
{
int opt_node;
char value[32];
char *p = value;
opt_node = prom_getchild(prom_root_node);
opt_node = prom_searchsiblings(opt_node, "options");
opt_node = prom_getproperty(opt_node,
"watchdog-timeout",
value,
sizeof(value));
if(-1 != opt_node) {
/* atoi implementation */
for(opt_node = 0; /* nop */; p++) {
if(*p >= '0' && *p <= '9') {
opt_node = (10*opt_node)+(*p-'0');
}
else {
break;
}
}
}
return((-1 == opt_node) ? (0) : (opt_node));
}
static int wd_open(struct inode *inode, struct file *f)
{
switch(iminor(inode))
{
case WD0_MINOR:
f->private_data = &wd_dev.watchdog[WD0_ID];
break;
case WD1_MINOR:
f->private_data = &wd_dev.watchdog[WD1_ID];
break;
case WD2_MINOR:
f->private_data = &wd_dev.watchdog[WD2_ID];
break;
default:
return(-ENODEV);
}
/* Register IRQ on first open of device */
if(0 == wd_dev.initialized)
{
if (request_irq(wd_dev.irq,
&wd_interrupt,
IRQF_SHARED,
WD_OBPNAME,
(void *)wd_dev.regs)) {
printk("%s: Cannot register IRQ %d\n",
WD_OBPNAME, wd_dev.irq);
return(-EBUSY);
}
wd_dev.initialized = 1;
}
return(nonseekable_open(inode, f));
}
static int wd_release(struct inode *inode, struct file *file)
{
return 0;
}
static int wd_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int setopt = 0;
struct wd_timer* pTimer = (struct wd_timer*)file->private_data;
void __user *argp = (void __user *)arg;
struct watchdog_info info = {
0,
0,
"Altera EPF8820ATC144-4"
};
if(NULL == pTimer) {
return(-EINVAL);
}
switch(cmd)
{
/* Generic Linux IOCTLs */
case WDIOC_GETSUPPORT:
if(copy_to_user(argp, &info, sizeof(struct watchdog_info))) {
return(-EFAULT);
}
break;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
if (put_user(0, (int __user *)argp))
return -EFAULT;
break;
case WDIOC_KEEPALIVE:
wd_pingtimer(pTimer);
break;
case WDIOC_SETOPTIONS:
if(copy_from_user(&setopt, argp, sizeof(unsigned int))) {
return -EFAULT;
}
if(setopt & WDIOS_DISABLECARD) {
if(wd_dev.opt_enable) {
printk(
"%s: cannot disable watchdog in ENABLED mode\n",
WD_OBPNAME);
return(-EINVAL);
}
wd_stoptimer(pTimer);
}
else if(setopt & WDIOS_ENABLECARD) {
wd_starttimer(pTimer);
}
else {
return(-EINVAL);
}
break;
/* Solaris-compatible IOCTLs */
case WIOCGSTAT:
setopt = wd_getstatus(pTimer);
if(copy_to_user(argp, &setopt, sizeof(unsigned int))) {
return(-EFAULT);
}
break;
case WIOCSTART:
wd_starttimer(pTimer);
break;
case WIOCSTOP:
if(wd_dev.opt_enable) {
printk("%s: cannot disable watchdog in ENABLED mode\n",
WD_OBPNAME);
return(-EINVAL);
}
wd_stoptimer(pTimer);
break;
default:
return(-EINVAL);
}
return(0);
}
static long wd_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int rval = -ENOIOCTLCMD;
switch (cmd) {
/* solaris ioctls are specific to this driver */
case WIOCSTART:
case WIOCSTOP:
case WIOCGSTAT:
lock_kernel();
rval = wd_ioctl(file->f_path.dentry->d_inode, file, cmd, arg);
unlock_kernel();
break;
/* everything else is handled by the generic compat layer */
default:
break;
}
return rval;
}
static ssize_t wd_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
struct wd_timer* pTimer = (struct wd_timer*)file->private_data;
if(NULL == pTimer) {
return(-EINVAL);
}
if (count) {
wd_pingtimer(pTimer);
return 1;
}
return 0;
}
static ssize_t wd_read(struct file * file, char __user *buffer,
size_t count, loff_t *ppos)
{
#ifdef WD_DEBUG
wd_dumpregs();
return(0);
#else
return(-EINVAL);
#endif /* ifdef WD_DEBUG */
}
static irqreturn_t wd_interrupt(int irq, void *dev_id)
{
/* Only WD0 will interrupt-- others are NMI and we won't
* see them here....
*/
spin_lock_irq(&wd_dev.lock);
if((unsigned long)wd_dev.regs == (unsigned long)dev_id)
{
wd_stoptimer(&wd_dev.watchdog[WD0_ID]);
wd_dev.watchdog[WD0_ID].runstatus |= WD_STAT_SVCD;
}
spin_unlock_irq(&wd_dev.lock);
return IRQ_HANDLED;
}
static const struct file_operations wd_fops = {
.owner = THIS_MODULE,
.ioctl = wd_ioctl,
.compat_ioctl = wd_compat_ioctl,
.open = wd_open,
.write = wd_write,
.read = wd_read,
.release = wd_release,
};
static struct miscdevice wd0_miscdev = { WD0_MINOR, WD0_DEVNAME, &wd_fops };
static struct miscdevice wd1_miscdev = { WD1_MINOR, WD1_DEVNAME, &wd_fops };
static struct miscdevice wd2_miscdev = { WD2_MINOR, WD2_DEVNAME, &wd_fops };
#ifdef WD_DEBUG
static void wd_dumpregs(void)
{
/* Reading from downcounters initiates watchdog countdown--
* Example is included below for illustration purposes.
*/
int i;
printk("%s: dumping register values\n", WD_OBPNAME);
for(i = WD0_ID; i < WD_NUMDEVS; ++i) {
/* printk("\t%s%i: dcntr at 0x%lx: 0x%x\n",
* WD_OBPNAME,
* i,
* (unsigned long)(&wd_dev.watchdog[i].regs->dcntr),
* readw(&wd_dev.watchdog[i].regs->dcntr));
*/
printk("\t%s%i: limit at 0x%lx: 0x%x\n",
WD_OBPNAME,
i,
(unsigned long)(&wd_dev.watchdog[i].regs->limit),
readw(&wd_dev.watchdog[i].regs->limit));
printk("\t%s%i: status at 0x%lx: 0x%x\n",
WD_OBPNAME,
i,
(unsigned long)(&wd_dev.watchdog[i].regs->status),
readb(&wd_dev.watchdog[i].regs->status));
printk("\t%s%i: driver status: 0x%x\n",
WD_OBPNAME,
i,
wd_getstatus(&wd_dev.watchdog[i]));
}
printk("\tintr_mask at %p: 0x%x\n",
wd_dev.regs + PLD_IMASK,
readb(wd_dev.regs + PLD_IMASK));
printk("\tpld_status at %p: 0x%x\n",
wd_dev.regs + PLD_STATUS,
readb(wd_dev.regs + PLD_STATUS));
}
#endif
/* Enable or disable watchdog interrupts
* Because of the CP1400 defect this should only be
* called during initialzation or by wd_[start|stop]timer()
*
* pTimer - pointer to timer device, or NULL to indicate all timers
* enable - non-zero to enable interrupts, zero to disable
*/
static void wd_toggleintr(struct wd_timer* pTimer, int enable)
{
unsigned char curregs = wd_readb(wd_dev.regs + PLD_IMASK);
unsigned char setregs =
(NULL == pTimer) ?
(WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) :
(pTimer->intr_mask);
(WD_INTR_ON == enable) ?
(curregs &= ~setregs):
(curregs |= setregs);
wd_writeb(curregs, wd_dev.regs + PLD_IMASK);
return;
}
/* Reset countdown timer with 'limit' value and continue countdown.
* This will not start a stopped timer.
*
* pTimer - pointer to timer device
*/
static void wd_pingtimer(struct wd_timer* pTimer)
{
if (wd_readb(pTimer->regs + WD_STATUS) & WD_S_RUNNING) {
wd_readw(pTimer->regs + WD_DCNTR);
}
}
/* Stop a running watchdog timer-- the timer actually keeps
* running, but the interrupt is masked so that no action is
* taken upon expiration.
*
* pTimer - pointer to timer device
*/
static void wd_stoptimer(struct wd_timer* pTimer)
{
if(wd_readb(pTimer->regs + WD_STATUS) & WD_S_RUNNING) {
wd_toggleintr(pTimer, WD_INTR_OFF);
if(wd_dev.isbaddoggie) {
pTimer->runstatus |= WD_STAT_BSTOP;
wd_brokentimer((unsigned long)&wd_dev);
}
}
}
/* Start a watchdog timer with the specified limit value
* If the watchdog is running, it will be restarted with
* the provided limit value.
*
* This function will enable interrupts on the specified
* watchdog.
*
* pTimer - pointer to timer device
* limit - limit (countdown) value in 1/10th seconds
*/
static void wd_starttimer(struct wd_timer* pTimer)
{
if(wd_dev.isbaddoggie) {
pTimer->runstatus &= ~WD_STAT_BSTOP;
}
pTimer->runstatus &= ~WD_STAT_SVCD;
wd_writew(pTimer->timeout, pTimer->regs + WD_LIMIT);
wd_toggleintr(pTimer, WD_INTR_ON);
}
/* Restarts timer with maximum limit value and
* does not unset 'brokenstop' value.
*/
static void wd_resetbrokentimer(struct wd_timer* pTimer)
{
wd_toggleintr(pTimer, WD_INTR_ON);
wd_writew(WD_BLIMIT, pTimer->regs + WD_LIMIT);
}
/* Timer device initialization helper.
* Returns 0 on success, other on failure
*/
static int wd_inittimer(int whichdog)
{
struct miscdevice *whichmisc;
void __iomem *whichregs;
char whichident[8];
int whichmask;
__u16 whichlimit;
switch(whichdog)
{
case WD0_ID:
whichmisc = &wd0_miscdev;
strcpy(whichident, "RIC");
whichregs = wd_dev.regs + WD0_OFF;
whichmask = WD0_INTR_MASK;
whichlimit= (0 == wd0_timeout) ?
(wd_dev.opt_timeout):
(wd0_timeout);
break;
case WD1_ID:
whichmisc = &wd1_miscdev;
strcpy(whichident, "XIR");
whichregs = wd_dev.regs + WD1_OFF;
whichmask = WD1_INTR_MASK;
whichlimit= (0 == wd1_timeout) ?
(wd_dev.opt_timeout):
(wd1_timeout);
break;
case WD2_ID:
whichmisc = &wd2_miscdev;
strcpy(whichident, "POR");
whichregs = wd_dev.regs + WD2_OFF;
whichmask = WD2_INTR_MASK;
whichlimit= (0 == wd2_timeout) ?
(wd_dev.opt_timeout):
(wd2_timeout);
break;
default:
printk("%s: %s: invalid watchdog id: %i\n",
WD_OBPNAME, __FUNCTION__, whichdog);
return(1);
}
if(0 != misc_register(whichmisc))
{
return(1);
}
wd_dev.watchdog[whichdog].regs = whichregs;
wd_dev.watchdog[whichdog].timeout = whichlimit;
wd_dev.watchdog[whichdog].intr_mask = whichmask;
wd_dev.watchdog[whichdog].runstatus &= ~WD_STAT_BSTOP;
wd_dev.watchdog[whichdog].runstatus |= WD_STAT_INIT;
printk("%s%i: %s hardware watchdog [%01i.%i sec] %s\n",
WD_OBPNAME,
whichdog,
whichident,
wd_dev.watchdog[whichdog].timeout / 10,
wd_dev.watchdog[whichdog].timeout % 10,
(0 != wd_dev.opt_enable) ? "in ENABLED mode" : "");
return(0);
}
/* Timer method called to reset stopped watchdogs--
* because of the PLD bug on CP1400, we cannot mask
* interrupts within the PLD so me must continually
* reset the timers ad infinitum.
*/
static void wd_brokentimer(unsigned long data)
{
struct wd_device* pDev = (struct wd_device*)data;
int id, tripped = 0;
/* kill a running timer instance, in case we
* were called directly instead of by kernel timer
*/
if(timer_pending(&wd_timer)) {
del_timer(&wd_timer);
}
for(id = WD0_ID; id < WD_NUMDEVS; ++id) {
if(pDev->watchdog[id].runstatus & WD_STAT_BSTOP) {
++tripped;
wd_resetbrokentimer(&pDev->watchdog[id]);
}
}
if(tripped) {
/* there is at least one timer brokenstopped-- reschedule */
init_timer(&wd_timer);
wd_timer.expires = WD_BTIMEOUT;
add_timer(&wd_timer);
}
}
static int wd_getstatus(struct wd_timer* pTimer)
{
unsigned char stat = wd_readb(pTimer->regs + WD_STATUS);
unsigned char intr = wd_readb(wd_dev.regs + PLD_IMASK);
unsigned char ret = WD_STOPPED;
/* determine STOPPED */
if(0 == stat ) {
return(ret);
}
/* determine EXPIRED vs FREERUN vs RUNNING */
else if(WD_S_EXPIRED & stat) {
ret = WD_EXPIRED;
}
else if(WD_S_RUNNING & stat) {
if(intr & pTimer->intr_mask) {
ret = WD_FREERUN;
}
else {
/* Fudge WD_EXPIRED status for defective CP1400--
* IF timer is running
* AND brokenstop is set
* AND an interrupt has been serviced
* we are WD_EXPIRED.
*
* IF timer is running
* AND brokenstop is set
* AND no interrupt has been serviced
* we are WD_FREERUN.
*/
if(wd_dev.isbaddoggie && (pTimer->runstatus & WD_STAT_BSTOP)) {
if(pTimer->runstatus & WD_STAT_SVCD) {
ret = WD_EXPIRED;
}
else {
/* we could as well pretend we are expired */
ret = WD_FREERUN;
}
}
else {
ret = WD_RUNNING;
}
}
}
/* determine SERVICED */
if(pTimer->runstatus & WD_STAT_SVCD) {
ret |= WD_SERVICED;
}
return(ret);
}
static int __init wd_init(void)
{
int id;
struct linux_ebus *ebus = NULL;
struct linux_ebus_device *edev = NULL;
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
if (!strcmp(edev->ofdev.node->name, WD_OBPNAME))
goto ebus_done;
}
}
ebus_done:
if(!edev) {
printk("%s: unable to locate device\n", WD_OBPNAME);
return -ENODEV;
}
wd_dev.regs =
ioremap(edev->resource[0].start, 4 * WD_TIMER_REGSZ); /* ? */
if(NULL == wd_dev.regs) {
printk("%s: unable to map registers\n", WD_OBPNAME);
return(-ENODEV);
}
/* initialize device structure from OBP parameters */
wd_dev.irq = edev->irqs[0];
wd_dev.opt_enable = wd_opt_enable();
wd_dev.opt_reboot = wd_opt_reboot();
wd_dev.opt_timeout = wd_opt_timeout();
wd_dev.isbaddoggie = wd_isbroken();
/* disable all interrupts unless watchdog-enabled? == true */
if(! wd_dev.opt_enable) {
wd_toggleintr(NULL, WD_INTR_OFF);
}
/* register miscellaneous devices */
for(id = WD0_ID; id < WD_NUMDEVS; ++id) {
if(0 != wd_inittimer(id)) {
printk("%s%i: unable to initialize\n", WD_OBPNAME, id);
}
}
/* warn about possible defective PLD */
if(wd_dev.isbaddoggie) {
init_timer(&wd_timer);
wd_timer.function = wd_brokentimer;
wd_timer.data = (unsigned long)&wd_dev;
wd_timer.expires = WD_BTIMEOUT;
printk("%s: PLD defect workaround enabled for model %s\n",
WD_OBPNAME, WD_BADMODEL);
}
return(0);
}
static void __exit wd_cleanup(void)
{
int id;
/* if 'watchdog-enable?' == TRUE, timers are not stopped
* when module is unloaded. All brokenstopped timers will
* also now eventually trip.
*/
for(id = WD0_ID; id < WD_NUMDEVS; ++id) {
if(WD_S_RUNNING == wd_readb(wd_dev.watchdog[id].regs + WD_STATUS)) {
if(wd_dev.opt_enable) {
printk(KERN_WARNING "%s%i: timer not stopped at release\n",
WD_OBPNAME, id);
}
else {
wd_stoptimer(&wd_dev.watchdog[id]);
if(wd_dev.watchdog[id].runstatus & WD_STAT_BSTOP) {
wd_resetbrokentimer(&wd_dev.watchdog[id]);
printk(KERN_WARNING
"%s%i: defect workaround disabled at release, "\
"timer expires in ~%01i sec\n",
WD_OBPNAME, id,
wd_readw(wd_dev.watchdog[id].regs + WD_LIMIT) / 10);
}
}
}
}
if(wd_dev.isbaddoggie && timer_pending(&wd_timer)) {
del_timer(&wd_timer);
}
if(0 != (wd_dev.watchdog[WD0_ID].runstatus & WD_STAT_INIT)) {
misc_deregister(&wd0_miscdev);
}
if(0 != (wd_dev.watchdog[WD1_ID].runstatus & WD_STAT_INIT)) {
misc_deregister(&wd1_miscdev);
}
if(0 != (wd_dev.watchdog[WD2_ID].runstatus & WD_STAT_INIT)) {
misc_deregister(&wd2_miscdev);
}
if(0 != wd_dev.initialized) {
free_irq(wd_dev.irq, (void *)wd_dev.regs);
}
iounmap(wd_dev.regs);
}
module_init(wd_init);
module_exit(wd_cleanup);

View File

@@ -0,0 +1,242 @@
/* $Id: display7seg.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
*
* display7seg - Driver implementation for the 7-segment display
* present on Sun Microsystems CP1400 and CP1500
*
* Copyright (c) 2000 Eric Brower (ebrower@usa.net)
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/major.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/ioport.h> /* request_region */
#include <linux/smp_lock.h>
#include <asm/atomic.h>
#include <asm/ebus.h> /* EBus device */
#include <asm/oplib.h> /* OpenProm Library */
#include <asm/uaccess.h> /* put_/get_user */
#include <asm/display7seg.h>
#define D7S_MINOR 193
#define D7S_OBPNAME "display7seg"
#define D7S_DEVNAME "d7s"
static int sol_compat = 0; /* Solaris compatibility mode */
#ifdef MODULE
/* Solaris compatibility flag -
* The Solaris implementation omits support for several
* documented driver features (ref Sun doc 806-0180-03).
* By default, this module supports the documented driver
* abilities, rather than the Solaris implementation:
*
* 1) Device ALWAYS reverts to OBP-specified FLIPPED mode
* upon closure of device or module unload.
* 2) Device ioctls D7SIOCRD/D7SIOCWR honor toggling of
* FLIP bit
*
* If you wish the device to operate as under Solaris,
* omitting above features, set this parameter to non-zero.
*/
module_param
(sol_compat, int, 0);
MODULE_PARM_DESC
(sol_compat,
"Disables documented functionality omitted from Solaris driver");
MODULE_AUTHOR
("Eric Brower <ebrower@usa.net>");
MODULE_DESCRIPTION
("7-Segment Display driver for Sun Microsystems CP1400/1500");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE
("d7s");
#endif /* ifdef MODULE */
/*
* Register block address- see header for details
* -----------------------------------------
* | DP | ALARM | FLIP | 4 | 3 | 2 | 1 | 0 |
* -----------------------------------------
*
* DP - Toggles decimal point on/off
* ALARM - Toggles "Alarm" LED green/red
* FLIP - Inverts display for upside-down mounted board
* bits 0-4 - 7-segment display contents
*/
static void __iomem* d7s_regs;
static inline void d7s_free(void)
{
iounmap(d7s_regs);
}
static inline int d7s_obpflipped(void)
{
int opt_node;
opt_node = prom_getchild(prom_root_node);
opt_node = prom_searchsiblings(opt_node, "options");
return ((-1 != prom_getintdefault(opt_node, "d7s-flipped?", -1)) ? 0 : 1);
}
static atomic_t d7s_users = ATOMIC_INIT(0);
static int d7s_open(struct inode *inode, struct file *f)
{
if (D7S_MINOR != iminor(inode))
return -ENODEV;
atomic_inc(&d7s_users);
return 0;
}
static int d7s_release(struct inode *inode, struct file *f)
{
/* Reset flipped state to OBP default only if
* no other users have the device open and we
* are not operating in solaris-compat mode
*/
if (atomic_dec_and_test(&d7s_users) && !sol_compat) {
int regval = 0;
regval = readb(d7s_regs);
(0 == d7s_obpflipped()) ?
writeb(regval |= D7S_FLIP, d7s_regs):
writeb(regval &= ~D7S_FLIP, d7s_regs);
}
return 0;
}
static long d7s_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
__u8 regs = readb(d7s_regs);
__u8 ireg = 0;
int error = 0;
if (D7S_MINOR != iminor(file->f_path.dentry->d_inode))
return -ENODEV;
lock_kernel();
switch (cmd) {
case D7SIOCWR:
/* assign device register values
* we mask-out D7S_FLIP if in sol_compat mode
*/
if (get_user(ireg, (int __user *) arg)) {
error = -EFAULT;
break;
}
if (0 != sol_compat) {
(regs & D7S_FLIP) ?
(ireg |= D7S_FLIP) : (ireg &= ~D7S_FLIP);
}
writeb(ireg, d7s_regs);
break;
case D7SIOCRD:
/* retrieve device register values
* NOTE: Solaris implementation returns D7S_FLIP bit
* as toggled by user, even though it does not honor it.
* This driver will not misinform you about the state
* of your hardware while in sol_compat mode
*/
if (put_user(regs, (int __user *) arg)) {
error = -EFAULT;
break;
}
break;
case D7SIOCTM:
/* toggle device mode-- flip display orientation */
(regs & D7S_FLIP) ?
(regs &= ~D7S_FLIP) : (regs |= D7S_FLIP);
writeb(regs, d7s_regs);
break;
};
unlock_kernel();
return error;
}
static const struct file_operations d7s_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = d7s_ioctl,
.compat_ioctl = d7s_ioctl,
.open = d7s_open,
.release = d7s_release,
};
static struct miscdevice d7s_miscdev = { D7S_MINOR, D7S_DEVNAME, &d7s_fops };
static int __init d7s_init(void)
{
struct linux_ebus *ebus = NULL;
struct linux_ebus_device *edev = NULL;
int iTmp = 0, regs = 0;
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
if (!strcmp(edev->prom_node->name, D7S_OBPNAME))
goto ebus_done;
}
}
ebus_done:
if(!edev) {
printk("%s: unable to locate device\n", D7S_DEVNAME);
return -ENODEV;
}
d7s_regs = ioremap(edev->resource[0].start, sizeof(__u8));
iTmp = misc_register(&d7s_miscdev);
if (0 != iTmp) {
printk("%s: unable to acquire miscdevice minor %i\n",
D7S_DEVNAME, D7S_MINOR);
iounmap(d7s_regs);
return iTmp;
}
/* OBP option "d7s-flipped?" is honored as default
* for the device, and reset default when detached
*/
regs = readb(d7s_regs);
iTmp = d7s_obpflipped();
(0 == iTmp) ?
writeb(regs |= D7S_FLIP, d7s_regs):
writeb(regs &= ~D7S_FLIP, d7s_regs);
printk("%s: 7-Segment Display%s at 0x%lx %s\n",
D7S_DEVNAME,
(0 == iTmp) ? (" (FLIPPED)") : (""),
edev->resource[0].start,
(0 != sol_compat) ? ("in sol_compat mode") : (""));
return 0;
}
static void __exit d7s_cleanup(void)
{
int regs = readb(d7s_regs);
/* Honor OBP d7s-flipped? unless operating in solaris-compat mode */
if (0 == sol_compat) {
(0 == d7s_obpflipped()) ?
writeb(regs |= D7S_FLIP, d7s_regs):
writeb(regs &= ~D7S_FLIP, d7s_regs);
}
misc_deregister(&d7s_miscdev);
d7s_free();
}
module_init(d7s_init);
module_exit(d7s_cleanup);

1140
drivers/sbus/char/envctrl.c Normal file

File diff suppressed because it is too large Load Diff

254
drivers/sbus/char/flash.c Normal file
View File

@@ -0,0 +1,254 @@
/* $Id: flash.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
* flash.c: Allow mmap access to the OBP Flash, for OBP updates.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/sbus.h>
#include <asm/ebus.h>
#include <asm/upa.h>
static DEFINE_SPINLOCK(flash_lock);
static struct {
unsigned long read_base; /* Physical read address */
unsigned long write_base; /* Physical write address */
unsigned long read_size; /* Size of read area */
unsigned long write_size; /* Size of write area */
unsigned long busy; /* In use? */
} flash;
#define FLASH_MINOR 152
static int
flash_mmap(struct file *file, struct vm_area_struct *vma)
{
unsigned long addr;
unsigned long size;
spin_lock(&flash_lock);
if (flash.read_base == flash.write_base) {
addr = flash.read_base;
size = flash.read_size;
} else {
if ((vma->vm_flags & VM_READ) &&
(vma->vm_flags & VM_WRITE)) {
spin_unlock(&flash_lock);
return -EINVAL;
}
if (vma->vm_flags & VM_READ) {
addr = flash.read_base;
size = flash.read_size;
} else if (vma->vm_flags & VM_WRITE) {
addr = flash.write_base;
size = flash.write_size;
} else {
spin_unlock(&flash_lock);
return -ENXIO;
}
}
spin_unlock(&flash_lock);
if ((vma->vm_pgoff << PAGE_SHIFT) > size)
return -ENXIO;
addr = vma->vm_pgoff + (addr >> PAGE_SHIFT);
if (vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT)) > size)
size = vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT));
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
if (io_remap_pfn_range(vma, vma->vm_start, addr, size, vma->vm_page_prot))
return -EAGAIN;
return 0;
}
static long long
flash_llseek(struct file *file, long long offset, int origin)
{
lock_kernel();
switch (origin) {
case 0:
file->f_pos = offset;
break;
case 1:
file->f_pos += offset;
if (file->f_pos > flash.read_size)
file->f_pos = flash.read_size;
break;
case 2:
file->f_pos = flash.read_size;
break;
default:
unlock_kernel();
return -EINVAL;
}
unlock_kernel();
return file->f_pos;
}
static ssize_t
flash_read(struct file * file, char __user * buf,
size_t count, loff_t *ppos)
{
unsigned long p = file->f_pos;
int i;
if (count > flash.read_size - p)
count = flash.read_size - p;
for (i = 0; i < count; i++) {
u8 data = upa_readb(flash.read_base + p + i);
if (put_user(data, buf))
return -EFAULT;
buf++;
}
file->f_pos += count;
return count;
}
static int
flash_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(0, (void *)&flash.busy) != 0)
return -EBUSY;
return 0;
}
static int
flash_release(struct inode *inode, struct file *file)
{
spin_lock(&flash_lock);
flash.busy = 0;
spin_unlock(&flash_lock);
return 0;
}
static const struct file_operations flash_fops = {
/* no write to the Flash, use mmap
* and play flash dependent tricks.
*/
.owner = THIS_MODULE,
.llseek = flash_llseek,
.read = flash_read,
.mmap = flash_mmap,
.open = flash_open,
.release = flash_release,
};
static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops };
static int __init flash_init(void)
{
struct sbus_bus *sbus;
struct sbus_dev *sdev = NULL;
#ifdef CONFIG_PCI
struct linux_ebus *ebus;
struct linux_ebus_device *edev = NULL;
struct linux_prom_registers regs[2];
int len, nregs;
#endif
int err;
for_all_sbusdev(sdev, sbus) {
if (!strcmp(sdev->prom_name, "flashprom")) {
if (sdev->reg_addrs[0].phys_addr == sdev->reg_addrs[1].phys_addr) {
flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) |
(((unsigned long)sdev->reg_addrs[0].which_io)<<32UL);
flash.read_size = sdev->reg_addrs[0].reg_size;
flash.write_base = flash.read_base;
flash.write_size = flash.read_size;
} else {
flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) |
(((unsigned long)sdev->reg_addrs[0].which_io)<<32UL);
flash.read_size = sdev->reg_addrs[0].reg_size;
flash.write_base = ((unsigned long)sdev->reg_addrs[1].phys_addr) |
(((unsigned long)sdev->reg_addrs[1].which_io)<<32UL);
flash.write_size = sdev->reg_addrs[1].reg_size;
}
flash.busy = 0;
break;
}
}
if (!sdev) {
#ifdef CONFIG_PCI
struct linux_prom_registers *ebus_regs;
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
if (!strcmp(edev->prom_node->name, "flashprom"))
goto ebus_done;
}
}
ebus_done:
if (!edev)
return -ENODEV;
ebus_regs = of_get_property(edev->prom_node, "reg", &len);
if (!ebus_regs || (len % sizeof(regs[0])) != 0) {
printk("flash: Strange reg property size %d\n", len);
return -ENODEV;
}
nregs = len / sizeof(ebus_regs[0]);
flash.read_base = edev->resource[0].start;
flash.read_size = ebus_regs[0].reg_size;
if (nregs == 1) {
flash.write_base = edev->resource[0].start;
flash.write_size = ebus_regs[0].reg_size;
} else if (nregs == 2) {
flash.write_base = edev->resource[1].start;
flash.write_size = ebus_regs[1].reg_size;
} else {
printk("flash: Strange number of regs %d\n", nregs);
return -ENODEV;
}
flash.busy = 0;
#else
return -ENODEV;
#endif
}
printk("OBP Flash: RD %lx[%lx] WR %lx[%lx]\n",
flash.read_base, flash.read_size,
flash.write_base, flash.write_size);
err = misc_register(&flash_dev);
if (err) {
printk(KERN_ERR "flash: unable to get misc minor\n");
return err;
}
return 0;
}
static void __exit flash_cleanup(void)
{
misc_deregister(&flash_dev);
}
module_init(flash_init);
module_exit(flash_cleanup);
MODULE_LICENSE("GPL");

628
drivers/sbus/char/jsflash.c Normal file
View File

@@ -0,0 +1,628 @@
/*
* drivers/sbus/char/jsflash.c
*
* Copyright (C) 1991, 1992 Linus Torvalds (drivers/char/mem.c)
* Copyright (C) 1997 Eddie C. Dost (drivers/sbus/char/flash.c)
* Copyright (C) 1997-2000 Pavel Machek <pavel@ucw.cz> (drivers/block/nbd.c)
* Copyright (C) 1999-2000 Pete Zaitcev
*
* This driver is used to program OS into a Flash SIMM on
* Krups and Espresso platforms.
*
* TODO: do not allow erase/programming if file systems are mounted.
* TODO: Erase/program both banks of a 8MB SIMM.
*
* It is anticipated that programming an OS Flash will be a routine
* procedure. In the same time it is exeedingly dangerous because
* a user can program its OBP flash with OS image and effectively
* kill the machine.
*
* This driver uses an interface different from Eddie's flash.c
* as a silly safeguard.
*
* XXX The flash.c manipulates page caching characteristics in a certain
* dubious way; also it assumes that remap_pfn_range() can remap
* PCI bus locations, which may be false. ioremap() must be used
* instead. We should discuss this.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/smp_lock.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#define MAJOR_NR JSFD_MAJOR
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/pcic.h>
#include <asm/oplib.h>
#include <asm/jsflash.h> /* ioctl arguments. <linux/> ?? */
#define JSFIDSZ (sizeof(struct jsflash_ident_arg))
#define JSFPRGSZ (sizeof(struct jsflash_program_arg))
/*
* Our device numbers have no business in system headers.
* The only thing a user knows is the device name /dev/jsflash.
*
* Block devices are laid out like this:
* minor+0 - Bootstrap, for 8MB SIMM 0x20400000[0x800000]
* minor+1 - Filesystem to mount, normally 0x20400400[0x7ffc00]
* minor+2 - Whole flash area for any case... 0x20000000[0x01000000]
* Total 3 minors per flash device.
*
* It is easier to have static size vectors, so we define
* a total minor range JSF_MAX, which must cover all minors.
*/
/* character device */
#define JSF_MINOR 178 /* 178 is registered with hpa */
/* block device */
#define JSF_MAX 3 /* 3 minors wasted total so far. */
#define JSF_NPART 3 /* 3 minors per flash device */
#define JSF_PART_BITS 2 /* 2 bits of minors to cover JSF_NPART */
#define JSF_PART_MASK 0x3 /* 2 bits mask */
/*
* Access functions.
* We could ioremap(), but it's easier this way.
*/
static unsigned int jsf_inl(unsigned long addr)
{
unsigned long retval;
__asm__ __volatile__("lda [%1] %2, %0\n\t" :
"=r" (retval) :
"r" (addr), "i" (ASI_M_BYPASS));
return retval;
}
static void jsf_outl(unsigned long addr, __u32 data)
{
__asm__ __volatile__("sta %0, [%1] %2\n\t" : :
"r" (data), "r" (addr), "i" (ASI_M_BYPASS) :
"memory");
}
/*
* soft carrier
*/
struct jsfd_part {
unsigned long dbase;
unsigned long dsize;
};
struct jsflash {
unsigned long base;
unsigned long size;
unsigned long busy; /* In use? */
struct jsflash_ident_arg id;
/* int mbase; */ /* Minor base, typically zero */
struct jsfd_part dv[JSF_NPART];
};
/*
* We do not map normal memory or obio as a safety precaution.
* But offsets are real, for ease of userland programming.
*/
#define JSF_BASE_TOP 0x30000000
#define JSF_BASE_ALL 0x20000000
#define JSF_BASE_JK 0x20400000
/*
*/
static struct gendisk *jsfd_disk[JSF_MAX];
/*
* Let's pretend we may have several of these...
*/
static struct jsflash jsf0;
/*
* Wait for AMD to finish its embedded algorithm.
* We use the Toggle bit DQ6 (0x40) because it does not
* depend on the data value as /DATA bit DQ7 does.
*
* XXX Do we need any timeout here? So far it never hanged, beware broken hw.
*/
static void jsf_wait(unsigned long p) {
unsigned int x1, x2;
for (;;) {
x1 = jsf_inl(p);
x2 = jsf_inl(p);
if ((x1 & 0x40404040) == (x2 & 0x40404040)) return;
}
}
/*
* Programming will only work if Flash is clean,
* we leave it to the programmer application.
*
* AMD must be programmed one byte at a time;
* thus, Simple Tech SIMM must be written 4 bytes at a time.
*
* Write waits for the chip to become ready after the write
* was finished. This is done so that application would read
* consistent data after the write is done.
*/
static void jsf_write4(unsigned long fa, u32 data) {
jsf_outl(fa, 0xAAAAAAAA); /* Unlock 1 Write 1 */
jsf_outl(fa, 0x55555555); /* Unlock 1 Write 2 */
jsf_outl(fa, 0xA0A0A0A0); /* Byte Program */
jsf_outl(fa, data);
jsf_wait(fa);
}
/*
*/
static void jsfd_read(char *buf, unsigned long p, size_t togo) {
union byte4 {
char s[4];
unsigned int n;
} b;
while (togo >= 4) {
togo -= 4;
b.n = jsf_inl(p);
memcpy(buf, b.s, 4);
p += 4;
buf += 4;
}
}
static void jsfd_do_request(request_queue_t *q)
{
struct request *req;
while ((req = elv_next_request(q)) != NULL) {
struct jsfd_part *jdp = req->rq_disk->private_data;
unsigned long offset = req->sector << 9;
size_t len = req->current_nr_sectors << 9;
if ((offset + len) > jdp->dsize) {
end_request(req, 0);
continue;
}
if (rq_data_dir(req) != READ) {
printk(KERN_ERR "jsfd: write\n");
end_request(req, 0);
continue;
}
if ((jdp->dbase & 0xff000000) != 0x20000000) {
printk(KERN_ERR "jsfd: bad base %x\n", (int)jdp->dbase);
end_request(req, 0);
continue;
}
jsfd_read(req->buffer, jdp->dbase + offset, len);
end_request(req, 1);
}
}
/*
* The memory devices use the full 32/64 bits of the offset, and so we cannot
* check against negative addresses: they are ok. The return value is weird,
* though, in that case (0).
*
* also note that seeking relative to the "end of file" isn't supported:
* it has no meaning, so it returns -EINVAL.
*/
static loff_t jsf_lseek(struct file * file, loff_t offset, int orig)
{
loff_t ret;
lock_kernel();
switch (orig) {
case 0:
file->f_pos = offset;
ret = file->f_pos;
break;
case 1:
file->f_pos += offset;
ret = file->f_pos;
break;
default:
ret = -EINVAL;
}
unlock_kernel();
return ret;
}
/*
* OS SIMM Cannot be read in other size but a 32bits word.
*/
static ssize_t jsf_read(struct file * file, char __user * buf,
size_t togo, loff_t *ppos)
{
unsigned long p = *ppos;
char __user *tmp = buf;
union byte4 {
char s[4];
unsigned int n;
} b;
if (p < JSF_BASE_ALL || p >= JSF_BASE_TOP) {
return 0;
}
if ((p + togo) < p /* wrap */
|| (p + togo) >= JSF_BASE_TOP) {
togo = JSF_BASE_TOP - p;
}
if (p < JSF_BASE_ALL && togo != 0) {
#if 0 /* __bzero XXX */
size_t x = JSF_BASE_ALL - p;
if (x > togo) x = togo;
clear_user(tmp, x);
tmp += x;
p += x;
togo -= x;
#else
/*
* Implementation of clear_user() calls __bzero
* without regard to modversions,
* so we cannot build a module.
*/
return 0;
#endif
}
while (togo >= 4) {
togo -= 4;
b.n = jsf_inl(p);
if (copy_to_user(tmp, b.s, 4))
return -EFAULT;
tmp += 4;
p += 4;
}
/*
* XXX Small togo may remain if 1 byte is ordered.
* It would be nice if we did a word size read and unpacked it.
*/
*ppos = p;
return tmp-buf;
}
static ssize_t jsf_write(struct file * file, const char __user * buf,
size_t count, loff_t *ppos)
{
return -ENOSPC;
}
/*
*/
static int jsf_ioctl_erase(unsigned long arg)
{
unsigned long p;
/* p = jsf0.base; hits wrong bank */
p = 0x20400000;
jsf_outl(p, 0xAAAAAAAA); /* Unlock 1 Write 1 */
jsf_outl(p, 0x55555555); /* Unlock 1 Write 2 */
jsf_outl(p, 0x80808080); /* Erase setup */
jsf_outl(p, 0xAAAAAAAA); /* Unlock 2 Write 1 */
jsf_outl(p, 0x55555555); /* Unlock 2 Write 2 */
jsf_outl(p, 0x10101010); /* Chip erase */
#if 0
/*
* This code is ok, except that counter based timeout
* has no place in this world. Let's just drop timeouts...
*/
{
int i;
__u32 x;
for (i = 0; i < 1000000; i++) {
x = jsf_inl(p);
if ((x & 0x80808080) == 0x80808080) break;
}
if ((x & 0x80808080) != 0x80808080) {
printk("jsf0: erase timeout with 0x%08x\n", x);
} else {
printk("jsf0: erase done with 0x%08x\n", x);
}
}
#else
jsf_wait(p);
#endif
return 0;
}
/*
* Program a block of flash.
* Very simple because we can do it byte by byte anyway.
*/
static int jsf_ioctl_program(void __user *arg)
{
struct jsflash_program_arg abuf;
char __user *uptr;
unsigned long p;
unsigned int togo;
union {
unsigned int n;
char s[4];
} b;
if (copy_from_user(&abuf, arg, JSFPRGSZ))
return -EFAULT;
p = abuf.off;
togo = abuf.size;
if ((togo & 3) || (p & 3)) return -EINVAL;
uptr = (char __user *) (unsigned long) abuf.data;
while (togo != 0) {
togo -= 4;
if (copy_from_user(&b.s[0], uptr, 4))
return -EFAULT;
jsf_write4(p, b.n);
p += 4;
uptr += 4;
}
return 0;
}
static int jsf_ioctl(struct inode *inode, struct file *f, unsigned int cmd,
unsigned long arg)
{
int error = -ENOTTY;
void __user *argp = (void __user *)arg;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
switch (cmd) {
case JSFLASH_IDENT:
if (copy_to_user(argp, &jsf0.id, JSFIDSZ))
return -EFAULT;
break;
case JSFLASH_ERASE:
error = jsf_ioctl_erase(arg);
break;
case JSFLASH_PROGRAM:
error = jsf_ioctl_program(argp);
break;
}
return error;
}
static int jsf_mmap(struct file * file, struct vm_area_struct * vma)
{
return -ENXIO;
}
static int jsf_open(struct inode * inode, struct file * filp)
{
if (jsf0.base == 0) return -ENXIO;
if (test_and_set_bit(0, (void *)&jsf0.busy) != 0)
return -EBUSY;
return 0; /* XXX What security? */
}
static int jsf_release(struct inode *inode, struct file *file)
{
jsf0.busy = 0;
return 0;
}
static const struct file_operations jsf_fops = {
.owner = THIS_MODULE,
.llseek = jsf_lseek,
.read = jsf_read,
.write = jsf_write,
.ioctl = jsf_ioctl,
.mmap = jsf_mmap,
.open = jsf_open,
.release = jsf_release,
};
static struct miscdevice jsf_dev = { JSF_MINOR, "jsflash", &jsf_fops };
static struct block_device_operations jsfd_fops = {
.owner = THIS_MODULE,
};
static int jsflash_init(void)
{
int rc;
struct jsflash *jsf;
int node;
char banner[128];
struct linux_prom_registers reg0;
node = prom_getchild(prom_root_node);
node = prom_searchsiblings(node, "flash-memory");
if (node != 0 && node != -1) {
if (prom_getproperty(node, "reg",
(char *)&reg0, sizeof(reg0)) == -1) {
printk("jsflash: no \"reg\" property\n");
return -ENXIO;
}
if (reg0.which_io != 0) {
printk("jsflash: bus number nonzero: 0x%x:%x\n",
reg0.which_io, reg0.phys_addr);
return -ENXIO;
}
/*
* Flash may be somewhere else, for instance on Ebus.
* So, don't do the following check for IIep flash space.
*/
#if 0
if ((reg0.phys_addr >> 24) != 0x20) {
printk("jsflash: suspicious address: 0x%x:%x\n",
reg0.which_io, reg0.phys_addr);
return -ENXIO;
}
#endif
if ((int)reg0.reg_size <= 0) {
printk("jsflash: bad size 0x%x\n", (int)reg0.reg_size);
return -ENXIO;
}
} else {
/* XXX Remove this code once PROLL ID12 got widespread */
printk("jsflash: no /flash-memory node, use PROLL >= 12\n");
prom_getproperty(prom_root_node, "banner-name", banner, 128);
if (strcmp (banner, "JavaStation-NC") != 0 &&
strcmp (banner, "JavaStation-E") != 0) {
return -ENXIO;
}
reg0.which_io = 0;
reg0.phys_addr = 0x20400000;
reg0.reg_size = 0x00800000;
}
/* Let us be really paranoid for modifications to probing code. */
/* extern enum sparc_cpu sparc_cpu_model; */ /* in <asm/system.h> */
if (sparc_cpu_model != sun4m) {
/* We must be on sun4m because we use MMU Bypass ASI. */
return -ENXIO;
}
if (jsf0.base == 0) {
jsf = &jsf0;
jsf->base = reg0.phys_addr;
jsf->size = reg0.reg_size;
/* XXX Redo the userland interface. */
jsf->id.off = JSF_BASE_ALL;
jsf->id.size = 0x01000000; /* 16M - all segments */
strcpy(jsf->id.name, "Krups_all");
jsf->dv[0].dbase = jsf->base;
jsf->dv[0].dsize = jsf->size;
jsf->dv[1].dbase = jsf->base + 1024;
jsf->dv[1].dsize = jsf->size - 1024;
jsf->dv[2].dbase = JSF_BASE_ALL;
jsf->dv[2].dsize = 0x01000000;
printk("Espresso Flash @0x%lx [%d MB]\n", jsf->base,
(int) (jsf->size / (1024*1024)));
}
if ((rc = misc_register(&jsf_dev)) != 0) {
printk(KERN_ERR "jsf: unable to get misc minor %d\n",
JSF_MINOR);
jsf0.base = 0;
return rc;
}
return 0;
}
static struct request_queue *jsf_queue;
static int jsfd_init(void)
{
static DEFINE_SPINLOCK(lock);
struct jsflash *jsf;
struct jsfd_part *jdp;
int err;
int i;
if (jsf0.base == 0)
return -ENXIO;
err = -ENOMEM;
for (i = 0; i < JSF_MAX; i++) {
struct gendisk *disk = alloc_disk(1);
if (!disk)
goto out;
jsfd_disk[i] = disk;
}
if (register_blkdev(JSFD_MAJOR, "jsfd")) {
err = -EIO;
goto out;
}
jsf_queue = blk_init_queue(jsfd_do_request, &lock);
if (!jsf_queue) {
err = -ENOMEM;
unregister_blkdev(JSFD_MAJOR, "jsfd");
goto out;
}
for (i = 0; i < JSF_MAX; i++) {
struct gendisk *disk = jsfd_disk[i];
if ((i & JSF_PART_MASK) >= JSF_NPART) continue;
jsf = &jsf0; /* actually, &jsfv[i >> JSF_PART_BITS] */
jdp = &jsf->dv[i&JSF_PART_MASK];
disk->major = JSFD_MAJOR;
disk->first_minor = i;
sprintf(disk->disk_name, "jsfd%d", i);
disk->fops = &jsfd_fops;
set_capacity(disk, jdp->dsize >> 9);
disk->private_data = jdp;
disk->queue = jsf_queue;
add_disk(disk);
set_disk_ro(disk, 1);
}
return 0;
out:
while (i--)
put_disk(jsfd_disk[i]);
return err;
}
MODULE_LICENSE("GPL");
static int __init jsflash_init_module(void) {
int rc;
if ((rc = jsflash_init()) == 0) {
jsfd_init();
return 0;
}
return rc;
}
static void __exit jsflash_cleanup_module(void)
{
int i;
for (i = 0; i < JSF_MAX; i++) {
if ((i & JSF_PART_MASK) >= JSF_NPART) continue;
del_gendisk(jsfd_disk[i]);
put_disk(jsfd_disk[i]);
}
if (jsf0.busy)
printk("jsf0: cleaning busy unit\n");
jsf0.base = 0;
jsf0.busy = 0;
misc_deregister(&jsf_dev);
if (unregister_blkdev(JSFD_MAJOR, "jsfd") != 0)
printk("jsfd: cleanup_module failed\n");
blk_cleanup_queue(jsf_queue);
}
module_init(jsflash_init_module);
module_exit(jsflash_cleanup_module);

View File

@@ -0,0 +1,27 @@
/* $Id: max1617.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $ */
#ifndef _MAX1617_H
#define _MAX1617_H
#define MAX1617_AMB_TEMP 0x00 /* Ambient temp in C */
#define MAX1617_CPU_TEMP 0x01 /* Processor die temp in C */
#define MAX1617_STATUS 0x02 /* Chip status bits */
/* Read-only versions of changable registers. */
#define MAX1617_RD_CFG_BYTE 0x03 /* Config register */
#define MAX1617_RD_CVRATE_BYTE 0x04 /* Temp conversion rate */
#define MAX1617_RD_AMB_HIGHLIM 0x05 /* Ambient high limit */
#define MAX1617_RD_AMB_LOWLIM 0x06 /* Ambient low limit */
#define MAX1617_RD_CPU_HIGHLIM 0x07 /* Processor high limit */
#define MAX1617_RD_CPU_LOWLIM 0x08 /* Processor low limit */
/* Write-only versions of the same. */
#define MAX1617_WR_CFG_BYTE 0x09
#define MAX1617_WR_CVRATE_BYTE 0x0a
#define MAX1617_WR_AMB_HIGHLIM 0x0b
#define MAX1617_WR_AMB_LOWLIM 0x0c
#define MAX1617_WR_CPU_HIGHLIM 0x0d
#define MAX1617_WR_CPU_LOWLIM 0x0e
#define MAX1617_ONESHOT 0x0f
#endif /* _MAX1617_H */

View File

@@ -0,0 +1,754 @@
/*
* Linux/SPARC PROM Configuration Driver
* Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
* Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
*
* This character device driver allows user programs to access the
* PROM device tree. It is compatible with the SunOS /dev/openprom
* driver and the NetBSD /dev/openprom driver. The SunOS eeprom
* utility works without any modifications.
*
* The driver uses a minor number under the misc device major. The
* file read/write mode determines the type of access to the PROM.
* Interrupts are disabled whenever the driver calls into the PROM for
* sanity's sake.
*/
/* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/oplib.h>
#include <asm/prom.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/openpromio.h>
#ifdef CONFIG_PCI
#include <linux/pci.h>
#include <asm/pbm.h>
#endif
MODULE_AUTHOR("Thomas K. Dyas (tdyas@noc.rutgers.edu) and Eddie C. Dost (ecd@skynet.be)");
MODULE_DESCRIPTION("OPENPROM Configuration Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0");
/* Private data kept by the driver for each descriptor. */
typedef struct openprom_private_data
{
struct device_node *current_node; /* Current node for SunOS ioctls. */
struct device_node *lastnode; /* Last valid node used by BSD ioctls. */
} DATA;
/* ID of the PROM node containing all of the EEPROM options. */
static struct device_node *options_node;
/*
* Copy an openpromio structure into kernel space from user space.
* This routine does error checking to make sure that all memory
* accesses are within bounds. A pointer to the allocated openpromio
* structure will be placed in "*opp_p". Return value is the length
* of the user supplied buffer.
*/
static int copyin(struct openpromio __user *info, struct openpromio **opp_p)
{
unsigned int bufsize;
if (!info || !opp_p)
return -EFAULT;
if (get_user(bufsize, &info->oprom_size))
return -EFAULT;
if (bufsize == 0)
return -EINVAL;
/* If the bufsize is too large, just limit it.
* Fix from Jason Rappleye.
*/
if (bufsize > OPROMMAXPARAM)
bufsize = OPROMMAXPARAM;
if (!(*opp_p = kzalloc(sizeof(int) + bufsize + 1, GFP_KERNEL)))
return -ENOMEM;
if (copy_from_user(&(*opp_p)->oprom_array,
&info->oprom_array, bufsize)) {
kfree(*opp_p);
return -EFAULT;
}
return bufsize;
}
static int getstrings(struct openpromio __user *info, struct openpromio **opp_p)
{
int n, bufsize;
char c;
if (!info || !opp_p)
return -EFAULT;
if (!(*opp_p = kzalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL)))
return -ENOMEM;
(*opp_p)->oprom_size = 0;
n = bufsize = 0;
while ((n < 2) && (bufsize < OPROMMAXPARAM)) {
if (get_user(c, &info->oprom_array[bufsize])) {
kfree(*opp_p);
return -EFAULT;
}
if (c == '\0')
n++;
(*opp_p)->oprom_array[bufsize++] = c;
}
if (!n) {
kfree(*opp_p);
return -EINVAL;
}
return bufsize;
}
/*
* Copy an openpromio structure in kernel space back to user space.
*/
static int copyout(void __user *info, struct openpromio *opp, int len)
{
if (copy_to_user(info, opp, len))
return -EFAULT;
return 0;
}
static int opromgetprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize)
{
void *pval;
int len;
if (!dp ||
!(pval = of_get_property(dp, op->oprom_array, &len)) ||
len <= 0 || len > bufsize)
return copyout(argp, op, sizeof(int));
memcpy(op->oprom_array, pval, len);
op->oprom_array[len] = '\0';
op->oprom_size = len;
return copyout(argp, op, sizeof(int) + bufsize);
}
static int opromnxtprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize)
{
struct property *prop;
int len;
if (!dp)
return copyout(argp, op, sizeof(int));
if (op->oprom_array[0] == '\0') {
prop = dp->properties;
if (!prop)
return copyout(argp, op, sizeof(int));
len = strlen(prop->name);
} else {
prop = of_find_property(dp, op->oprom_array, NULL);
if (!prop ||
!prop->next ||
(len = strlen(prop->next->name)) + 1 > bufsize)
return copyout(argp, op, sizeof(int));
prop = prop->next;
}
memcpy(op->oprom_array, prop->name, len);
op->oprom_array[len] = '\0';
op->oprom_size = ++len;
return copyout(argp, op, sizeof(int) + bufsize);
}
static int opromsetopt(struct device_node *dp, struct openpromio *op, int bufsize)
{
char *buf = op->oprom_array + strlen(op->oprom_array) + 1;
int len = op->oprom_array + bufsize - buf;
return of_set_property(options_node, op->oprom_array, buf, len);
}
static int opromnext(void __user *argp, unsigned int cmd, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
{
phandle ph;
BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
if (bufsize < sizeof(phandle))
return -EINVAL;
ph = *((int *) op->oprom_array);
if (ph) {
dp = of_find_node_by_phandle(ph);
if (!dp)
return -EINVAL;
switch (cmd) {
case OPROMNEXT:
dp = dp->sibling;
break;
case OPROMCHILD:
dp = dp->child;
break;
case OPROMSETCUR:
default:
break;
};
} else {
/* Sibling of node zero is the root node. */
if (cmd != OPROMNEXT)
return -EINVAL;
dp = of_find_node_by_path("/");
}
ph = 0;
if (dp)
ph = dp->node;
data->current_node = dp;
*((int *) op->oprom_array) = ph;
op->oprom_size = sizeof(phandle);
return copyout(argp, op, bufsize + sizeof(int));
}
static int oprompci2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
{
int err = -EINVAL;
if (bufsize >= 2*sizeof(int)) {
#ifdef CONFIG_PCI
struct pci_dev *pdev;
struct pcidev_cookie *pcp;
pdev = pci_get_bus_and_slot (((int *) op->oprom_array)[0],
((int *) op->oprom_array)[1]);
pcp = pdev->sysdata;
if (pcp != NULL) {
dp = pcp->prom_node;
data->current_node = dp;
*((int *)op->oprom_array) = dp->node;
op->oprom_size = sizeof(int);
err = copyout(argp, op, bufsize + sizeof(int));
}
pci_dev_put(pdev);
#endif
}
return err;
}
static int oprompath2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
{
phandle ph = 0;
dp = of_find_node_by_path(op->oprom_array);
if (dp)
ph = dp->node;
data->current_node = dp;
*((int *)op->oprom_array) = ph;
op->oprom_size = sizeof(int);
return copyout(argp, op, bufsize + sizeof(int));
}
static int opromgetbootargs(void __user *argp, struct openpromio *op, int bufsize)
{
char *buf = saved_command_line;
int len = strlen(buf);
if (len > bufsize)
return -EINVAL;
strcpy(op->oprom_array, buf);
op->oprom_size = len;
return copyout(argp, op, bufsize + sizeof(int));
}
/*
* SunOS and Solaris /dev/openprom ioctl calls.
*/
static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg,
struct device_node *dp)
{
DATA *data = file->private_data;
struct openpromio *opp;
int bufsize, error = 0;
static int cnt;
void __user *argp = (void __user *)arg;
if (cmd == OPROMSETOPT)
bufsize = getstrings(argp, &opp);
else
bufsize = copyin(argp, &opp);
if (bufsize < 0)
return bufsize;
switch (cmd) {
case OPROMGETOPT:
case OPROMGETPROP:
error = opromgetprop(argp, dp, opp, bufsize);
break;
case OPROMNXTOPT:
case OPROMNXTPROP:
error = opromnxtprop(argp, dp, opp, bufsize);
break;
case OPROMSETOPT:
case OPROMSETOPT2:
error = opromsetopt(dp, opp, bufsize);
break;
case OPROMNEXT:
case OPROMCHILD:
case OPROMSETCUR:
error = opromnext(argp, cmd, dp, opp, bufsize, data);
break;
case OPROMPCI2NODE:
error = oprompci2node(argp, dp, opp, bufsize, data);
break;
case OPROMPATH2NODE:
error = oprompath2node(argp, dp, opp, bufsize, data);
break;
case OPROMGETBOOTARGS:
error = opromgetbootargs(argp, opp, bufsize);
break;
case OPROMU2P:
case OPROMGETCONS:
case OPROMGETFBNAME:
if (cnt++ < 10)
printk(KERN_INFO "openprom_sunos_ioctl: unimplemented ioctl\n");
error = -EINVAL;
break;
default:
if (cnt++ < 10)
printk(KERN_INFO "openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
error = -EINVAL;
break;
}
kfree(opp);
return error;
}
static struct device_node *get_node(phandle n, DATA *data)
{
struct device_node *dp = of_find_node_by_phandle(n);
if (dp)
data->lastnode = dp;
return dp;
}
/* Copy in a whole string from userspace into kernelspace. */
static int copyin_string(char __user *user, size_t len, char **ptr)
{
char *tmp;
if ((ssize_t)len < 0 || (ssize_t)(len + 1) < 0)
return -EINVAL;
tmp = kmalloc(len + 1, GFP_KERNEL);
if (!tmp)
return -ENOMEM;
if (copy_from_user(tmp, user, len)) {
kfree(tmp);
return -EFAULT;
}
tmp[len] = '\0';
*ptr = tmp;
return 0;
}
/*
* NetBSD /dev/openprom ioctl calls.
*/
static int opiocget(void __user *argp, DATA *data)
{
struct opiocdesc op;
struct device_node *dp;
char *str;
void *pval;
int err, len;
if (copy_from_user(&op, argp, sizeof(op)))
return -EFAULT;
dp = get_node(op.op_nodeid, data);
err = copyin_string(op.op_name, op.op_namelen, &str);
if (err)
return err;
pval = of_get_property(dp, str, &len);
err = 0;
if (!pval || len > op.op_buflen) {
err = -EINVAL;
} else {
op.op_buflen = len;
if (copy_to_user(argp, &op, sizeof(op)) ||
copy_to_user(op.op_buf, pval, len))
err = -EFAULT;
}
kfree(str);
return err;
}
static int opiocnextprop(void __user *argp, DATA *data)
{
struct opiocdesc op;
struct device_node *dp;
struct property *prop;
char *str;
int err, len;
if (copy_from_user(&op, argp, sizeof(op)))
return -EFAULT;
dp = get_node(op.op_nodeid, data);
if (!dp)
return -EINVAL;
err = copyin_string(op.op_name, op.op_namelen, &str);
if (err)
return err;
if (str[0] == '\0') {
prop = dp->properties;
} else {
prop = of_find_property(dp, str, NULL);
if (prop)
prop = prop->next;
}
kfree(str);
if (!prop)
len = 0;
else
len = prop->length;
if (len > op.op_buflen)
len = op.op_buflen;
if (copy_to_user(argp, &op, sizeof(op)))
return -EFAULT;
if (len &&
copy_to_user(op.op_buf, prop->value, len))
return -EFAULT;
return 0;
}
static int opiocset(void __user *argp, DATA *data)
{
struct opiocdesc op;
struct device_node *dp;
char *str, *tmp;
int err;
if (copy_from_user(&op, argp, sizeof(op)))
return -EFAULT;
dp = get_node(op.op_nodeid, data);
if (!dp)
return -EINVAL;
err = copyin_string(op.op_name, op.op_namelen, &str);
if (err)
return err;
err = copyin_string(op.op_buf, op.op_buflen, &tmp);
if (err) {
kfree(str);
return err;
}
err = of_set_property(dp, str, tmp, op.op_buflen);
kfree(str);
kfree(tmp);
return err;
}
static int opiocgetnext(unsigned int cmd, void __user *argp)
{
struct device_node *dp;
phandle nd;
BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
if (copy_from_user(&nd, argp, sizeof(phandle)))
return -EFAULT;
if (nd == 0) {
if (cmd != OPIOCGETNEXT)
return -EINVAL;
dp = of_find_node_by_path("/");
} else {
dp = of_find_node_by_phandle(nd);
nd = 0;
if (dp) {
if (cmd == OPIOCGETNEXT)
dp = dp->sibling;
else
dp = dp->child;
}
}
if (dp)
nd = dp->node;
if (copy_to_user(argp, &nd, sizeof(phandle)))
return -EFAULT;
return 0;
}
static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
DATA *data = (DATA *) file->private_data;
void __user *argp = (void __user *)arg;
int err;
switch (cmd) {
case OPIOCGET:
err = opiocget(argp, data);
break;
case OPIOCNEXTPROP:
err = opiocnextprop(argp, data);
break;
case OPIOCSET:
err = opiocset(argp, data);
break;
case OPIOCGETOPTNODE:
BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
if (copy_to_user(argp, &options_node->node, sizeof(phandle)))
return -EFAULT;
return 0;
case OPIOCGETNEXT:
case OPIOCGETCHILD:
err = opiocgetnext(cmd, argp);
break;
default:
return -EINVAL;
};
return err;
}
/*
* Handoff control to the correct ioctl handler.
*/
static int openprom_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
DATA *data = (DATA *) file->private_data;
switch (cmd) {
case OPROMGETOPT:
case OPROMNXTOPT:
if ((file->f_mode & FMODE_READ) == 0)
return -EPERM;
return openprom_sunos_ioctl(inode, file, cmd, arg,
options_node);
case OPROMSETOPT:
case OPROMSETOPT2:
if ((file->f_mode & FMODE_WRITE) == 0)
return -EPERM;
return openprom_sunos_ioctl(inode, file, cmd, arg,
options_node);
case OPROMNEXT:
case OPROMCHILD:
case OPROMGETPROP:
case OPROMNXTPROP:
if ((file->f_mode & FMODE_READ) == 0)
return -EPERM;
return openprom_sunos_ioctl(inode, file, cmd, arg,
data->current_node);
case OPROMU2P:
case OPROMGETCONS:
case OPROMGETFBNAME:
case OPROMGETBOOTARGS:
case OPROMSETCUR:
case OPROMPCI2NODE:
case OPROMPATH2NODE:
if ((file->f_mode & FMODE_READ) == 0)
return -EPERM;
return openprom_sunos_ioctl(inode, file, cmd, arg, NULL);
case OPIOCGET:
case OPIOCNEXTPROP:
case OPIOCGETOPTNODE:
case OPIOCGETNEXT:
case OPIOCGETCHILD:
if ((file->f_mode & FMODE_READ) == 0)
return -EBADF;
return openprom_bsd_ioctl(inode,file,cmd,arg);
case OPIOCSET:
if ((file->f_mode & FMODE_WRITE) == 0)
return -EBADF;
return openprom_bsd_ioctl(inode,file,cmd,arg);
default:
return -EINVAL;
};
}
static long openprom_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
long rval = -ENOTTY;
/*
* SunOS/Solaris only, the NetBSD one's have embedded pointers in
* the arg which we'd need to clean up...
*/
switch (cmd) {
case OPROMGETOPT:
case OPROMSETOPT:
case OPROMNXTOPT:
case OPROMSETOPT2:
case OPROMNEXT:
case OPROMCHILD:
case OPROMGETPROP:
case OPROMNXTPROP:
case OPROMU2P:
case OPROMGETCONS:
case OPROMGETFBNAME:
case OPROMGETBOOTARGS:
case OPROMSETCUR:
case OPROMPCI2NODE:
case OPROMPATH2NODE:
rval = openprom_ioctl(file->f_path.dentry->d_inode, file, cmd, arg);
break;
}
return rval;
}
static int openprom_open(struct inode * inode, struct file * file)
{
DATA *data;
data = kmalloc(sizeof(DATA), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->current_node = of_find_node_by_path("/");
data->lastnode = data->current_node;
file->private_data = (void *) data;
return 0;
}
static int openprom_release(struct inode * inode, struct file * file)
{
kfree(file->private_data);
return 0;
}
static const struct file_operations openprom_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.ioctl = openprom_ioctl,
.compat_ioctl = openprom_compat_ioctl,
.open = openprom_open,
.release = openprom_release,
};
static struct miscdevice openprom_dev = {
.minor = SUN_OPENPROM_MINOR,
.name = "openprom",
.fops = &openprom_fops,
};
static int __init openprom_init(void)
{
struct device_node *dp;
int err;
err = misc_register(&openprom_dev);
if (err)
return err;
dp = of_find_node_by_path("/");
dp = dp->child;
while (dp) {
if (!strcmp(dp->name, "options"))
break;
dp = dp->sibling;
}
options_node = dp;
if (!options_node) {
misc_deregister(&openprom_dev);
return -EIO;
}
return 0;
}
static void __exit openprom_cleanup(void)
{
misc_deregister(&openprom_dev);
}
module_init(openprom_init);
module_exit(openprom_cleanup);

View File

@@ -0,0 +1,293 @@
/* $Id: riowatchdog.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
* riowatchdog.c - driver for hw watchdog inside Super I/O of RIO
*
* Copyright (C) 2001 David S. Miller (davem@redhat.com)
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#include <asm/ebus.h>
#include <asm/bbc.h>
#include <asm/oplib.h>
#include <asm/uaccess.h>
#include <asm/watchdog.h>
/* RIO uses the NatSemi Super I/O power management logical device
* as its' watchdog.
*
* When the watchdog triggers, it asserts a line to the BBC (Boot Bus
* Controller) of the machine. The BBC can only be configured to
* trigger a power-on reset when the signal is asserted. The BBC
* can be configured to ignore the signal entirely as well.
*
* The only Super I/O device register we care about is at index
* 0x05 (WDTO_INDEX) which is the watchdog time-out in minutes (1-255).
* If set to zero, this disables the watchdog. When set, the system
* must periodically (before watchdog expires) clear (set to zero) and
* re-set the watchdog else it will trigger.
*
* There are two other indexed watchdog registers inside this Super I/O
* logical device, but they are unused. The first, at index 0x06 is
* the watchdog control and can be used to make the watchdog timer re-set
* when the PS/2 mouse or serial lines show activity. The second, at
* index 0x07 is merely a sampling of the line from the watchdog to the
* BBC.
*
* The watchdog device generates no interrupts.
*/
MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
MODULE_DESCRIPTION("Hardware watchdog driver for Sun RIO");
MODULE_SUPPORTED_DEVICE("watchdog");
MODULE_LICENSE("GPL");
#define RIOWD_NAME "pmc"
#define RIOWD_MINOR 215
static DEFINE_SPINLOCK(riowd_lock);
static void __iomem *bbc_regs;
static void __iomem *riowd_regs;
#define WDTO_INDEX 0x05
static int riowd_timeout = 1; /* in minutes */
module_param(riowd_timeout, int, 0);
MODULE_PARM_DESC(riowd_timeout, "Watchdog timeout in minutes");
#if 0 /* Currently unused. */
static u8 riowd_readreg(int index)
{
unsigned long flags;
u8 ret;
spin_lock_irqsave(&riowd_lock, flags);
writeb(index, riowd_regs + 0);
ret = readb(riowd_regs + 1);
spin_unlock_irqrestore(&riowd_lock, flags);
return ret;
}
#endif
static void riowd_writereg(u8 val, int index)
{
unsigned long flags;
spin_lock_irqsave(&riowd_lock, flags);
writeb(index, riowd_regs + 0);
writeb(val, riowd_regs + 1);
spin_unlock_irqrestore(&riowd_lock, flags);
}
static void riowd_pingtimer(void)
{
riowd_writereg(riowd_timeout, WDTO_INDEX);
}
static void riowd_stoptimer(void)
{
u8 val;
riowd_writereg(0, WDTO_INDEX);
val = readb(bbc_regs + BBC_WDACTION);
val &= ~BBC_WDACTION_RST;
writeb(val, bbc_regs + BBC_WDACTION);
}
static void riowd_starttimer(void)
{
u8 val;
riowd_writereg(riowd_timeout, WDTO_INDEX);
val = readb(bbc_regs + BBC_WDACTION);
val |= BBC_WDACTION_RST;
writeb(val, bbc_regs + BBC_WDACTION);
}
static int riowd_open(struct inode *inode, struct file *filp)
{
nonseekable_open(inode, filp);
return 0;
}
static int riowd_release(struct inode *inode, struct file *filp)
{
return 0;
}
static int riowd_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
static struct watchdog_info info = {
WDIOF_SETTIMEOUT, 0, "Natl. Semiconductor PC97317"
};
void __user *argp = (void __user *)arg;
unsigned int options;
int new_margin;
switch (cmd) {
case WDIOC_GETSUPPORT:
if (copy_to_user(argp, &info, sizeof(info)))
return -EFAULT;
break;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
if (put_user(0, (int __user *)argp))
return -EFAULT;
break;
case WDIOC_KEEPALIVE:
riowd_pingtimer();
break;
case WDIOC_SETOPTIONS:
if (copy_from_user(&options, argp, sizeof(options)))
return -EFAULT;
if (options & WDIOS_DISABLECARD)
riowd_stoptimer();
else if (options & WDIOS_ENABLECARD)
riowd_starttimer();
else
return -EINVAL;
break;
case WDIOC_SETTIMEOUT:
if (get_user(new_margin, (int __user *)argp))
return -EFAULT;
if ((new_margin < 60) || (new_margin > (255 * 60)))
return -EINVAL;
riowd_timeout = (new_margin + 59) / 60;
riowd_pingtimer();
/* Fall */
case WDIOC_GETTIMEOUT:
return put_user(riowd_timeout * 60, (int __user *)argp);
default:
return -EINVAL;
};
return 0;
}
static ssize_t riowd_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
if (count) {
riowd_pingtimer();
return 1;
}
return 0;
}
static const struct file_operations riowd_fops = {
.owner = THIS_MODULE,
.ioctl = riowd_ioctl,
.open = riowd_open,
.write = riowd_write,
.release = riowd_release,
};
static struct miscdevice riowd_miscdev = { RIOWD_MINOR, RIOWD_NAME, &riowd_fops };
static int __init riowd_bbc_init(void)
{
struct linux_ebus *ebus = NULL;
struct linux_ebus_device *edev = NULL;
u8 val;
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
if (!strcmp(edev->ofdev.node->name, "bbc"))
goto found_bbc;
}
}
found_bbc:
if (!edev)
return -ENODEV;
bbc_regs = ioremap(edev->resource[0].start, BBC_REGS_SIZE);
if (!bbc_regs)
return -ENODEV;
/* Turn it off. */
val = readb(bbc_regs + BBC_WDACTION);
val &= ~BBC_WDACTION_RST;
writeb(val, bbc_regs + BBC_WDACTION);
return 0;
}
static int __init riowd_init(void)
{
struct linux_ebus *ebus = NULL;
struct linux_ebus_device *edev = NULL;
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
if (!strcmp(edev->ofdev.node->name, RIOWD_NAME))
goto ebus_done;
}
}
ebus_done:
if (!edev)
goto fail;
riowd_regs = ioremap(edev->resource[0].start, 2);
if (riowd_regs == NULL) {
printk(KERN_ERR "pmc: Cannot map registers.\n");
return -ENODEV;
}
if (riowd_bbc_init()) {
printk(KERN_ERR "pmc: Failure initializing BBC config.\n");
goto fail;
}
if (misc_register(&riowd_miscdev)) {
printk(KERN_ERR "pmc: Cannot register watchdog misc device.\n");
goto fail;
}
printk(KERN_INFO "pmc: Hardware watchdog [%i minutes], "
"regs at %p\n", riowd_timeout, riowd_regs);
return 0;
fail:
if (riowd_regs) {
iounmap(riowd_regs);
riowd_regs = NULL;
}
if (bbc_regs) {
iounmap(bbc_regs);
bbc_regs = NULL;
}
return -ENODEV;
}
static void __exit riowd_cleanup(void)
{
misc_deregister(&riowd_miscdev);
iounmap(riowd_regs);
riowd_regs = NULL;
iounmap(bbc_regs);
bbc_regs = NULL;
}
module_init(riowd_init);
module_exit(riowd_cleanup);

273
drivers/sbus/char/rtc.c Normal file
View File

@@ -0,0 +1,273 @@
/* $Id: rtc.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
*
* Linux/SPARC Real Time Clock Driver
* Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
*
* This is a little driver that lets a user-level program access
* the SPARC Mostek real time clock chip. It is no use unless you
* use the modified clock utility.
*
* Get the modified clock utility from:
* ftp://vger.kernel.org/pub/linux/Sparc/userland/clock.c
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <asm/io.h>
#include <asm/mostek.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/rtc.h>
static int rtc_busy = 0;
/* This is the structure layout used by drivers/char/rtc.c, we
* support that driver's ioctls so that things are less messy in
* userspace.
*/
struct rtc_time_generic {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
#define RTC_AIE_ON _IO('p', 0x01) /* Alarm int. enable on */
#define RTC_AIE_OFF _IO('p', 0x02) /* ... off */
#define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */
#define RTC_UIE_OFF _IO('p', 0x04) /* ... off */
#define RTC_PIE_ON _IO('p', 0x05) /* Periodic int. enable on */
#define RTC_PIE_OFF _IO('p', 0x06) /* ... off */
#define RTC_WIE_ON _IO('p', 0x0f) /* Watchdog int. enable on */
#define RTC_WIE_OFF _IO('p', 0x10) /* ... off */
#define RTC_RD_TIME _IOR('p', 0x09, struct rtc_time_generic) /* Read RTC time */
#define RTC_SET_TIME _IOW('p', 0x0a, struct rtc_time_generic) /* Set RTC time */
#define RTC_ALM_SET _IOW('p', 0x07, struct rtc_time) /* Set alarm time */
#define RTC_ALM_READ _IOR('p', 0x08, struct rtc_time) /* Read alarm time */
#define RTC_IRQP_READ _IOR('p', 0x0b, unsigned long) /* Read IRQ rate */
#define RTC_IRQP_SET _IOW('p', 0x0c, unsigned long) /* Set IRQ rate */
#define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */
#define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */
#define RTC_WKALM_SET _IOW('p', 0x0f, struct rtc_wkalrm)/* Set wakeup alarm*/
#define RTC_WKALM_RD _IOR('p', 0x10, struct rtc_wkalrm)/* Get wakeup alarm*/
#define RTC_PLL_GET _IOR('p', 0x11, struct rtc_pll_info) /* Get PLL correction */
#define RTC_PLL_SET _IOW('p', 0x12, struct rtc_pll_info) /* Set PLL correction */
/* Retrieve the current date and time from the real time clock. */
static void get_rtc_time(struct rtc_time *t)
{
void __iomem *regs = mstk48t02_regs;
u8 tmp;
spin_lock_irq(&mostek_lock);
tmp = mostek_read(regs + MOSTEK_CREG);
tmp |= MSTK_CREG_READ;
mostek_write(regs + MOSTEK_CREG, tmp);
t->sec = MSTK_REG_SEC(regs);
t->min = MSTK_REG_MIN(regs);
t->hour = MSTK_REG_HOUR(regs);
t->dow = MSTK_REG_DOW(regs);
t->dom = MSTK_REG_DOM(regs);
t->month = MSTK_REG_MONTH(regs);
t->year = MSTK_CVT_YEAR( MSTK_REG_YEAR(regs) );
tmp = mostek_read(regs + MOSTEK_CREG);
tmp &= ~MSTK_CREG_READ;
mostek_write(regs + MOSTEK_CREG, tmp);
spin_unlock_irq(&mostek_lock);
}
/* Set the current date and time inthe real time clock. */
void set_rtc_time(struct rtc_time *t)
{
void __iomem *regs = mstk48t02_regs;
u8 tmp;
spin_lock_irq(&mostek_lock);
tmp = mostek_read(regs + MOSTEK_CREG);
tmp |= MSTK_CREG_WRITE;
mostek_write(regs + MOSTEK_CREG, tmp);
MSTK_SET_REG_SEC(regs,t->sec);
MSTK_SET_REG_MIN(regs,t->min);
MSTK_SET_REG_HOUR(regs,t->hour);
MSTK_SET_REG_DOW(regs,t->dow);
MSTK_SET_REG_DOM(regs,t->dom);
MSTK_SET_REG_MONTH(regs,t->month);
MSTK_SET_REG_YEAR(regs,t->year - MSTK_YEAR_ZERO);
tmp = mostek_read(regs + MOSTEK_CREG);
tmp &= ~MSTK_CREG_WRITE;
mostek_write(regs + MOSTEK_CREG, tmp);
spin_unlock_irq(&mostek_lock);
}
static int put_rtc_time_generic(void __user *argp, struct rtc_time *tm)
{
struct rtc_time_generic __user *utm = argp;
if (__put_user(tm->sec, &utm->tm_sec) ||
__put_user(tm->min, &utm->tm_min) ||
__put_user(tm->hour, &utm->tm_hour) ||
__put_user(tm->dom, &utm->tm_mday) ||
__put_user(tm->month, &utm->tm_mon) ||
__put_user(tm->year, &utm->tm_year) ||
__put_user(tm->dow, &utm->tm_wday) ||
__put_user(0, &utm->tm_yday) ||
__put_user(0, &utm->tm_isdst))
return -EFAULT;
return 0;
}
static int get_rtc_time_generic(struct rtc_time *tm, void __user *argp)
{
struct rtc_time_generic __user *utm = argp;
if (__get_user(tm->sec, &utm->tm_sec) ||
__get_user(tm->min, &utm->tm_min) ||
__get_user(tm->hour, &utm->tm_hour) ||
__get_user(tm->dom, &utm->tm_mday) ||
__get_user(tm->month, &utm->tm_mon) ||
__get_user(tm->year, &utm->tm_year) ||
__get_user(tm->dow, &utm->tm_wday))
return -EFAULT;
return 0;
}
static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
struct rtc_time rtc_tm;
void __user *argp = (void __user *)arg;
switch (cmd) {
/* No interrupt support, return an error
* compatible with drivers/char/rtc.c
*/
case RTC_AIE_OFF:
case RTC_AIE_ON:
case RTC_PIE_OFF:
case RTC_PIE_ON:
case RTC_UIE_OFF:
case RTC_UIE_ON:
case RTC_IRQP_READ:
case RTC_IRQP_SET:
case RTC_EPOCH_SET:
case RTC_EPOCH_READ:
return -EINVAL;
case RTCGET:
case RTC_RD_TIME:
memset(&rtc_tm, 0, sizeof(struct rtc_time));
get_rtc_time(&rtc_tm);
if (cmd == RTCGET) {
if (copy_to_user(argp, &rtc_tm,
sizeof(struct rtc_time)))
return -EFAULT;
} else if (put_rtc_time_generic(argp, &rtc_tm))
return -EFAULT;
return 0;
case RTCSET:
case RTC_SET_TIME:
if (!capable(CAP_SYS_TIME))
return -EPERM;
if (cmd == RTCSET) {
if (copy_from_user(&rtc_tm, argp,
sizeof(struct rtc_time)))
return -EFAULT;
} else if (get_rtc_time_generic(&rtc_tm, argp))
return -EFAULT;
set_rtc_time(&rtc_tm);
return 0;
default:
return -EINVAL;
}
}
static int rtc_open(struct inode *inode, struct file *file)
{
int ret;
spin_lock_irq(&mostek_lock);
if (rtc_busy) {
ret = -EBUSY;
} else {
rtc_busy = 1;
ret = 0;
}
spin_unlock_irq(&mostek_lock);
return ret;
}
static int rtc_release(struct inode *inode, struct file *file)
{
rtc_busy = 0;
return 0;
}
static const struct file_operations rtc_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.ioctl = rtc_ioctl,
.open = rtc_open,
.release = rtc_release,
};
static struct miscdevice rtc_dev = { RTC_MINOR, "rtc", &rtc_fops };
static int __init rtc_sun_init(void)
{
int error;
/* It is possible we are being driven by some other RTC chip
* and thus another RTC driver is handling things.
*/
if (!mstk48t02_regs)
return -ENODEV;
error = misc_register(&rtc_dev);
if (error) {
printk(KERN_ERR "rtc: unable to get misc minor for Mostek\n");
return error;
}
printk("rtc_sun_init: Registered Mostek RTC driver.\n");
return 0;
}
static void __exit rtc_sun_cleanup(void)
{
misc_deregister(&rtc_dev);
}
module_init(rtc_sun_init);
module_exit(rtc_sun_cleanup);
MODULE_LICENSE("GPL");

427
drivers/sbus/char/uctrl.c Normal file
View File

@@ -0,0 +1,427 @@
/* $Id: uctrl.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
* uctrl.c: TS102 Microcontroller interface on Tadpole Sparcbook 3
*
* Copyright 1999 Derrick J Brashear (shadow@dementia.org)
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/sbus.h>
#define UCTRL_MINOR 174
#define DEBUG 1
#ifdef DEBUG
#define dprintk(x) printk x
#else
#define dprintk(x)
#endif
struct uctrl_regs {
volatile u32 uctrl_intr;
volatile u32 uctrl_data;
volatile u32 uctrl_stat;
volatile u32 uctrl_xxx[5];
};
struct ts102_regs {
volatile u32 card_a_intr;
volatile u32 card_a_stat;
volatile u32 card_a_ctrl;
volatile u32 card_a_xxx;
volatile u32 card_b_intr;
volatile u32 card_b_stat;
volatile u32 card_b_ctrl;
volatile u32 card_b_xxx;
volatile u32 uctrl_intr;
volatile u32 uctrl_data;
volatile u32 uctrl_stat;
volatile u32 uctrl_xxx;
volatile u32 ts102_xxx[4];
};
/* Bits for uctrl_intr register */
#define UCTRL_INTR_TXE_REQ 0x01 /* transmit FIFO empty int req */
#define UCTRL_INTR_TXNF_REQ 0x02 /* transmit FIFO not full int req */
#define UCTRL_INTR_RXNE_REQ 0x04 /* receive FIFO not empty int req */
#define UCTRL_INTR_RXO_REQ 0x08 /* receive FIFO overflow int req */
#define UCTRL_INTR_TXE_MSK 0x10 /* transmit FIFO empty mask */
#define UCTRL_INTR_TXNF_MSK 0x20 /* transmit FIFO not full mask */
#define UCTRL_INTR_RXNE_MSK 0x40 /* receive FIFO not empty mask */
#define UCTRL_INTR_RXO_MSK 0x80 /* receive FIFO overflow mask */
/* Bits for uctrl_stat register */
#define UCTRL_STAT_TXE_STA 0x01 /* transmit FIFO empty status */
#define UCTRL_STAT_TXNF_STA 0x02 /* transmit FIFO not full status */
#define UCTRL_STAT_RXNE_STA 0x04 /* receive FIFO not empty status */
#define UCTRL_STAT_RXO_STA 0x08 /* receive FIFO overflow status */
static const char *uctrl_extstatus[16] = {
"main power available",
"internal battery attached",
"external battery attached",
"external VGA attached",
"external keyboard attached",
"external mouse attached",
"lid down",
"internal battery currently charging",
"external battery currently charging",
"internal battery currently discharging",
"external battery currently discharging",
};
/* Everything required for one transaction with the uctrl */
struct uctrl_txn {
u8 opcode;
u8 inbits;
u8 outbits;
u8 *inbuf;
u8 *outbuf;
};
struct uctrl_status {
u8 current_temp; /* 0x07 */
u8 reset_status; /* 0x0b */
u16 event_status; /* 0x0c */
u16 error_status; /* 0x10 */
u16 external_status; /* 0x11, 0x1b */
u8 internal_charge; /* 0x18 */
u8 external_charge; /* 0x19 */
u16 control_lcd; /* 0x20 */
u8 control_bitport; /* 0x21 */
u8 speaker_volume; /* 0x23 */
u8 control_tft_brightness; /* 0x24 */
u8 control_kbd_repeat_delay; /* 0x28 */
u8 control_kbd_repeat_period; /* 0x29 */
u8 control_screen_contrast; /* 0x2F */
};
enum uctrl_opcode {
READ_SERIAL_NUMBER=0x1,
READ_ETHERNET_ADDRESS=0x2,
READ_HARDWARE_VERSION=0x3,
READ_MICROCONTROLLER_VERSION=0x4,
READ_MAX_TEMPERATURE=0x5,
READ_MIN_TEMPERATURE=0x6,
READ_CURRENT_TEMPERATURE=0x7,
READ_SYSTEM_VARIANT=0x8,
READ_POWERON_CYCLES=0x9,
READ_POWERON_SECONDS=0xA,
READ_RESET_STATUS=0xB,
READ_EVENT_STATUS=0xC,
READ_REAL_TIME_CLOCK=0xD,
READ_EXTERNAL_VGA_PORT=0xE,
READ_MICROCONTROLLER_ROM_CHECKSUM=0xF,
READ_ERROR_STATUS=0x10,
READ_EXTERNAL_STATUS=0x11,
READ_USER_CONFIGURATION_AREA=0x12,
READ_MICROCONTROLLER_VOLTAGE=0x13,
READ_INTERNAL_BATTERY_VOLTAGE=0x14,
READ_DCIN_VOLTAGE=0x15,
READ_HORIZONTAL_POINTER_VOLTAGE=0x16,
READ_VERTICAL_POINTER_VOLTAGE=0x17,
READ_INTERNAL_BATTERY_CHARGE_LEVEL=0x18,
READ_EXTERNAL_BATTERY_CHARGE_LEVEL=0x19,
READ_REAL_TIME_CLOCK_ALARM=0x1A,
READ_EVENT_STATUS_NO_RESET=0x1B,
READ_INTERNAL_KEYBOARD_LAYOUT=0x1C,
READ_EXTERNAL_KEYBOARD_LAYOUT=0x1D,
READ_EEPROM_STATUS=0x1E,
CONTROL_LCD=0x20,
CONTROL_BITPORT=0x21,
SPEAKER_VOLUME=0x23,
CONTROL_TFT_BRIGHTNESS=0x24,
CONTROL_WATCHDOG=0x25,
CONTROL_FACTORY_EEPROM_AREA=0x26,
CONTROL_KBD_TIME_UNTIL_REPEAT=0x28,
CONTROL_KBD_TIME_BETWEEN_REPEATS=0x29,
CONTROL_TIMEZONE=0x2A,
CONTROL_MARK_SPACE_RATIO=0x2B,
CONTROL_DIAGNOSTIC_MODE=0x2E,
CONTROL_SCREEN_CONTRAST=0x2F,
RING_BELL=0x30,
SET_DIAGNOSTIC_STATUS=0x32,
CLEAR_KEY_COMBINATION_TABLE=0x33,
PERFORM_SOFTWARE_RESET=0x34,
SET_REAL_TIME_CLOCK=0x35,
RECALIBRATE_POINTING_STICK=0x36,
SET_BELL_FREQUENCY=0x37,
SET_INTERNAL_BATTERY_CHARGE_RATE=0x39,
SET_EXTERNAL_BATTERY_CHARGE_RATE=0x3A,
SET_REAL_TIME_CLOCK_ALARM=0x3B,
READ_EEPROM=0x40,
WRITE_EEPROM=0x41,
WRITE_TO_STATUS_DISPLAY=0x42,
DEFINE_SPECIAL_CHARACTER=0x43,
DEFINE_KEY_COMBINATION_ENTRY=0x50,
DEFINE_STRING_TABLE_ENTRY=0x51,
DEFINE_STATUS_SCREEN_DISPLAY=0x52,
PERFORM_EMU_COMMANDS=0x64,
READ_EMU_REGISTER=0x65,
WRITE_EMU_REGISTER=0x66,
READ_EMU_RAM=0x67,
WRITE_EMU_RAM=0x68,
READ_BQ_REGISTER=0x69,
WRITE_BQ_REGISTER=0x6A,
SET_USER_PASSWORD=0x70,
VERIFY_USER_PASSWORD=0x71,
GET_SYSTEM_PASSWORD_KEY=0x72,
VERIFY_SYSTEM_PASSWORD=0x73,
POWER_OFF=0x82,
POWER_RESTART=0x83,
};
struct uctrl_driver {
struct uctrl_regs *regs;
int irq;
int pending;
struct uctrl_status status;
};
static struct uctrl_driver drv;
void uctrl_get_event_status(void);
void uctrl_get_external_status(void);
static int
uctrl_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
switch (cmd) {
default:
return -EINVAL;
}
return 0;
}
static int
uctrl_open(struct inode *inode, struct file *file)
{
uctrl_get_event_status();
uctrl_get_external_status();
return 0;
}
static irqreturn_t uctrl_interrupt(int irq, void *dev_id)
{
struct uctrl_driver *driver = (struct uctrl_driver *)dev_id;
printk("in uctrl_interrupt\n");
return IRQ_HANDLED;
}
static const struct file_operations uctrl_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.ioctl = uctrl_ioctl,
.open = uctrl_open,
};
static struct miscdevice uctrl_dev = {
UCTRL_MINOR,
"uctrl",
&uctrl_fops
};
/* Wait for space to write, then write to it */
#define WRITEUCTLDATA(value) \
{ \
unsigned int i; \
for (i = 0; i < 10000; i++) { \
if (UCTRL_STAT_TXNF_STA & driver->regs->uctrl_stat) \
break; \
} \
dprintk(("write data 0x%02x\n", value)); \
driver->regs->uctrl_data = value; \
}
/* Wait for something to read, read it, then clear the bit */
#define READUCTLDATA(value) \
{ \
unsigned int i; \
value = 0; \
for (i = 0; i < 10000; i++) { \
if ((UCTRL_STAT_RXNE_STA & driver->regs->uctrl_stat) == 0) \
break; \
udelay(1); \
} \
value = driver->regs->uctrl_data; \
dprintk(("read data 0x%02x\n", value)); \
driver->regs->uctrl_stat = UCTRL_STAT_RXNE_STA; \
}
void uctrl_set_video(int status)
{
struct uctrl_driver *driver = &drv;
}
static void uctrl_do_txn(struct uctrl_txn *txn)
{
struct uctrl_driver *driver = &drv;
int stat, incnt, outcnt, bytecnt, intr;
u32 byte;
stat = driver->regs->uctrl_stat;
intr = driver->regs->uctrl_intr;
driver->regs->uctrl_stat = stat;
dprintk(("interrupt stat 0x%x int 0x%x\n", stat, intr));
incnt = txn->inbits;
outcnt = txn->outbits;
byte = (txn->opcode << 8);
WRITEUCTLDATA(byte);
bytecnt = 0;
while (incnt > 0) {
byte = (txn->inbuf[bytecnt] << 8);
WRITEUCTLDATA(byte);
incnt--;
bytecnt++;
}
/* Get the ack */
READUCTLDATA(byte);
dprintk(("ack was %x\n", (byte >> 8)));
bytecnt = 0;
while (outcnt > 0) {
READUCTLDATA(byte);
txn->outbuf[bytecnt] = (byte >> 8);
dprintk(("set byte to %02x\n", byte));
outcnt--;
bytecnt++;
}
}
void uctrl_get_event_status(void)
{
struct uctrl_driver *driver = &drv;
struct uctrl_txn txn;
u8 outbits[2];
txn.opcode = READ_EVENT_STATUS;
txn.inbits = 0;
txn.outbits = 2;
txn.inbuf = NULL;
txn.outbuf = outbits;
uctrl_do_txn(&txn);
dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff)));
driver->status.event_status =
((outbits[0] & 0xff) << 8) | (outbits[1] & 0xff);
dprintk(("ev is %x\n", driver->status.event_status));
}
void uctrl_get_external_status(void)
{
struct uctrl_driver *driver = &drv;
struct uctrl_txn txn;
u8 outbits[2];
int i, v;
txn.opcode = READ_EXTERNAL_STATUS;
txn.inbits = 0;
txn.outbits = 2;
txn.inbuf = NULL;
txn.outbuf = outbits;
uctrl_do_txn(&txn);
dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff)));
driver->status.external_status =
((outbits[0] * 256) + (outbits[1]));
dprintk(("ex is %x\n", driver->status.external_status));
v = driver->status.external_status;
for (i = 0; v != 0; i++, v >>= 1) {
if (v & 1) {
dprintk(("%s%s", " ", uctrl_extstatus[i]));
}
}
dprintk(("\n"));
}
static int __init ts102_uctrl_init(void)
{
struct uctrl_driver *driver = &drv;
int len, i;
struct linux_prom_irqs tmp_irq[2];
unsigned int vaddr[2] = { 0, 0 };
int tmpnode, uctrlnode = prom_getchild(prom_root_node);
int err;
tmpnode = prom_searchsiblings(uctrlnode, "obio");
if (tmpnode)
uctrlnode = prom_getchild(tmpnode);
uctrlnode = prom_searchsiblings(uctrlnode, "uctrl");
if (!uctrlnode)
return -ENODEV;
/* the prom mapped it for us */
len = prom_getproperty(uctrlnode, "address", (void *) vaddr,
sizeof(vaddr));
driver->regs = (struct uctrl_regs *)vaddr[0];
len = prom_getproperty(uctrlnode, "intr", (char *) tmp_irq,
sizeof(tmp_irq));
/* Flush device */
READUCTLDATA(len);
if(!driver->irq)
driver->irq = tmp_irq[0].pri;
err = request_irq(driver->irq, uctrl_interrupt, 0, "uctrl", driver);
if (err) {
printk("%s: unable to register irq %d\n",
__FUNCTION__, driver->irq);
return err;
}
if (misc_register(&uctrl_dev)) {
printk("%s: unable to get misc minor %d\n",
__FUNCTION__, uctrl_dev.minor);
free_irq(driver->irq, driver);
return -ENODEV;
}
driver->regs->uctrl_intr = UCTRL_INTR_RXNE_REQ|UCTRL_INTR_RXNE_MSK;
printk("uctrl: 0x%p (irq %d)\n", driver->regs, driver->irq);
uctrl_get_event_status();
uctrl_get_external_status();
return 0;
}
static void __exit ts102_uctrl_cleanup(void)
{
struct uctrl_driver *driver = &drv;
misc_deregister(&uctrl_dev);
if (driver->irq)
free_irq(driver->irq, driver);
if (driver->regs)
driver->regs = NULL;
}
module_init(ts102_uctrl_init);
module_exit(ts102_uctrl_cleanup);
MODULE_LICENSE("GPL");

175
drivers/sbus/char/vfc.h Normal file
View File

@@ -0,0 +1,175 @@
#ifndef _LINUX_VFC_H_
#define _LINUX_VFC_H_
/*
* The control register for the vfc is at offset 0x4000
* The first field ram bank is located at offset 0x5000
* The second field ram bank is at offset 0x7000
* i2c_reg address the Phillips PCF8584(see notes in vfc_i2c.c)
* data and transmit register.
* i2c_s1 controls register s1 of the PCF8584
* i2c_write seems to be similar to i2c_write but I am not
* quite sure why sun uses it
*
* I am also not sure whether or not you can read the fram bank as a
* whole or whether you must read each word individually from offset
* 0x5000 as soon as I figure it out I will update this file */
struct vfc_regs {
char pad1[0x4000];
unsigned int control; /* Offset 0x4000 */
char pad2[0xffb]; /* from offset 0x4004 to 0x5000 */
unsigned int fram_bank1; /* Offset 0x5000 */
char pad3[0xffb]; /* from offset 0x5004 to 0x6000 */
unsigned int i2c_reg; /* Offset 0x6000 */
unsigned int i2c_magic2; /* Offset 0x6004 */
unsigned int i2c_s1; /* Offset 0x6008 */
unsigned int i2c_write; /* Offset 0x600c */
char pad4[0xff0]; /* from offset 0x6010 to 0x7000 */
unsigned int fram_bank2; /* Offset 0x7000 */
char pad5[0x1000];
};
#define VFC_SAA9051_NR (13)
#define VFC_SAA9051_ADDR (0x8a)
/* The saa9051 returns the following for its status
* bit 0 - 0
* bit 1 - SECAM color detected (1=found,0=not found)
* bit 2 - COLOR detected (1=found,0=not found)
* bit 3 - 0
* bit 4 - Field frequency bit (1=60Hz (NTSC), 0=50Hz (PAL))
* bit 5 - 1
* bit 6 - horizontal frequency lock (1=transmitter found,
* 0=no transmitter)
* bit 7 - Power on reset bit (1=reset,0=at least one successful
* read of the status byte)
*/
#define VFC_SAA9051_PONRES (0x80)
#define VFC_SAA9051_HLOCK (0x40)
#define VFC_SAA9051_FD (0x10)
#define VFC_SAA9051_CD (0x04)
#define VFC_SAA9051_CS (0x02)
/* The various saa9051 sub addresses */
#define VFC_SAA9051_IDEL (0)
#define VFC_SAA9051_HSY_START (1)
#define VFC_SAA9051_HSY_STOP (2)
#define VFC_SAA9051_HC_START (3)
#define VFC_SAA9051_HC_STOP (4)
#define VFC_SAA9051_HS_START (5)
#define VFC_SAA9051_HORIZ_PEAK (6)
#define VFC_SAA9051_HUE (7)
#define VFC_SAA9051_C1 (8)
#define VFC_SAA9051_C2 (9)
#define VFC_SAA9051_C3 (0xa)
#define VFC_SAA9051_SECAM_DELAY (0xb)
/* Bit settings for saa9051 sub address 0x06 */
#define VFC_SAA9051_AP1 (0x01)
#define VFC_SAA9051_AP2 (0x02)
#define VFC_SAA9051_COR1 (0x04)
#define VFC_SAA9051_COR2 (0x08)
#define VFC_SAA9051_BP1 (0x10)
#define VFC_SAA9051_BP2 (0x20)
#define VFC_SAA9051_PF (0x40)
#define VFC_SAA9051_BY (0x80)
/* Bit settings for saa9051 sub address 0x08 */
#define VFC_SAA9051_CCFR0 (0x01)
#define VFC_SAA9051_CCFR1 (0x02)
#define VFC_SAA9051_YPN (0x04)
#define VFC_SAA9051_ALT (0x08)
#define VFC_SAA9051_CO (0x10)
#define VFC_SAA9051_VTR (0x20)
#define VFC_SAA9051_FS (0x40)
#define VFC_SAA9051_HPLL (0x80)
/* Bit settings for saa9051 sub address 9 */
#define VFC_SAA9051_SS0 (0x01)
#define VFC_SAA9051_SS1 (0x02)
#define VFC_SAA9051_AFCC (0x04)
#define VFC_SAA9051_CI (0x08)
#define VFC_SAA9051_SA9D4 (0x10) /* Don't care bit */
#define VFC_SAA9051_OEC (0x20)
#define VFC_SAA9051_OEY (0x40)
#define VFC_SAA9051_VNL (0x80)
/* Bit settings for saa9051 sub address 0x0A */
#define VFC_SAA9051_YDL0 (0x01)
#define VFC_SAA9051_YDL1 (0x02)
#define VFC_SAA9051_YDL2 (0x04)
#define VFC_SAA9051_SS2 (0x08)
#define VFC_SAA9051_SS3 (0x10)
#define VFC_SAA9051_YC (0x20)
#define VFC_SAA9051_CT (0x40)
#define VFC_SAA9051_SYC (0x80)
#define VFC_SAA9051_SA(a,b) ((a)->saa9051_state_array[(b)+1])
#define vfc_update_saa9051(a) (vfc_i2c_sendbuf((a),VFC_SAA9051_ADDR,\
(a)->saa9051_state_array,\
VFC_SAA9051_NR))
struct vfc_dev {
volatile struct vfc_regs __iomem *regs;
struct vfc_regs *phys_regs;
unsigned int control_reg;
struct semaphore device_lock_sem;
int instance;
int busy;
unsigned long which_io;
unsigned char saa9051_state_array[VFC_SAA9051_NR];
};
extern struct vfc_dev **vfc_dev_lst;
void captstat_reset(struct vfc_dev *);
void memptr_reset(struct vfc_dev *);
int vfc_pcf8584_init(struct vfc_dev *);
void vfc_i2c_delay_no_busy(struct vfc_dev *, unsigned long);
void vfc_i2c_delay(struct vfc_dev *);
int vfc_i2c_sendbuf(struct vfc_dev *, unsigned char, char *, int) ;
int vfc_i2c_recvbuf(struct vfc_dev *, unsigned char, char *, int) ;
int vfc_i2c_reset_bus(struct vfc_dev *);
int vfc_init_i2c_bus(struct vfc_dev *);
void vfc_lock_device(struct vfc_dev *);
void vfc_unlock_device(struct vfc_dev *);
#define VFC_CONTROL_DIAGMODE 0x10000000
#define VFC_CONTROL_MEMPTR 0x20000000
#define VFC_CONTROL_CAPTURE 0x02000000
#define VFC_CONTROL_CAPTRESET 0x04000000
#define VFC_STATUS_CAPTURE 0x08000000
#ifdef VFC_IOCTL_DEBUG
#define VFC_IOCTL_DEBUG_PRINTK(a) printk a
#else
#define VFC_IOCTL_DEBUG_PRINTK(a)
#endif
#ifdef VFC_I2C_DEBUG
#define VFC_I2C_DEBUG_PRINTK(a) printk a
#else
#define VFC_I2C_DEBUG_PRINTK(a)
#endif
#endif /* _LINUX_VFC_H_ */

732
drivers/sbus/char/vfc_dev.c Normal file
View File

@@ -0,0 +1,732 @@
/*
* drivers/sbus/char/vfc_dev.c
*
* Driver for the Videopix Frame Grabber.
*
* In order to use the VFC you need to program the video controller
* chip. This chip is the Phillips SAA9051. You need to call their
* documentation ordering line to get the docs.
*
* There is very little documentation on the VFC itself. There is
* some useful info that can be found in the manuals that come with
* the card. I will hopefully write some better docs at a later date.
*
* Copyright (C) 1996 Manish Vachharajani (mvachhar@noc.rutgers.edu)
* */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/smp_lock.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/sbus.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
#define VFC_MAJOR (60)
#if 0
#define VFC_IOCTL_DEBUG
#endif
#include "vfc.h"
#include <asm/vfc_ioctls.h>
static const struct file_operations vfc_fops;
struct vfc_dev **vfc_dev_lst;
static char vfcstr[]="vfc";
static unsigned char saa9051_init_array[VFC_SAA9051_NR] = {
0x00, 0x64, 0x72, 0x52,
0x36, 0x18, 0xff, 0x20,
0xfc, 0x77, 0xe3, 0x50,
0x3e
};
void vfc_lock_device(struct vfc_dev *dev)
{
down(&dev->device_lock_sem);
}
void vfc_unlock_device(struct vfc_dev *dev)
{
up(&dev->device_lock_sem);
}
void vfc_captstat_reset(struct vfc_dev *dev)
{
dev->control_reg |= VFC_CONTROL_CAPTRESET;
sbus_writel(dev->control_reg, &dev->regs->control);
dev->control_reg &= ~VFC_CONTROL_CAPTRESET;
sbus_writel(dev->control_reg, &dev->regs->control);
dev->control_reg |= VFC_CONTROL_CAPTRESET;
sbus_writel(dev->control_reg, &dev->regs->control);
}
void vfc_memptr_reset(struct vfc_dev *dev)
{
dev->control_reg |= VFC_CONTROL_MEMPTR;
sbus_writel(dev->control_reg, &dev->regs->control);
dev->control_reg &= ~VFC_CONTROL_MEMPTR;
sbus_writel(dev->control_reg, &dev->regs->control);
dev->control_reg |= VFC_CONTROL_MEMPTR;
sbus_writel(dev->control_reg, &dev->regs->control);
}
int vfc_csr_init(struct vfc_dev *dev)
{
dev->control_reg = 0x80000000;
sbus_writel(dev->control_reg, &dev->regs->control);
udelay(200);
dev->control_reg &= ~0x80000000;
sbus_writel(dev->control_reg, &dev->regs->control);
udelay(100);
sbus_writel(0x0f000000, &dev->regs->i2c_magic2);
vfc_memptr_reset(dev);
dev->control_reg &= ~VFC_CONTROL_DIAGMODE;
dev->control_reg &= ~VFC_CONTROL_CAPTURE;
dev->control_reg |= 0x40000000;
sbus_writel(dev->control_reg, &dev->regs->control);
vfc_captstat_reset(dev);
return 0;
}
int vfc_saa9051_init(struct vfc_dev *dev)
{
int i;
for (i = 0; i < VFC_SAA9051_NR; i++)
dev->saa9051_state_array[i] = saa9051_init_array[i];
vfc_i2c_sendbuf(dev,VFC_SAA9051_ADDR,
dev->saa9051_state_array, VFC_SAA9051_NR);
return 0;
}
int init_vfc_hw(struct vfc_dev *dev)
{
vfc_lock_device(dev);
vfc_csr_init(dev);
vfc_pcf8584_init(dev);
vfc_init_i2c_bus(dev); /* hopefully this doesn't undo the magic
sun code above*/
vfc_saa9051_init(dev);
vfc_unlock_device(dev);
return 0;
}
int init_vfc_devstruct(struct vfc_dev *dev, int instance)
{
dev->instance=instance;
init_MUTEX(&dev->device_lock_sem);
dev->control_reg=0;
dev->busy=0;
return 0;
}
int init_vfc_device(struct sbus_dev *sdev,struct vfc_dev *dev, int instance)
{
if(dev == NULL) {
printk(KERN_ERR "VFC: Bogus pointer passed\n");
return -ENOMEM;
}
printk("Initializing vfc%d\n",instance);
dev->regs = NULL;
dev->regs = (volatile struct vfc_regs __iomem *)
sbus_ioremap(&sdev->resource[0], 0,
sizeof(struct vfc_regs), vfcstr);
dev->which_io = sdev->reg_addrs[0].which_io;
dev->phys_regs = (struct vfc_regs *) sdev->reg_addrs[0].phys_addr;
if (dev->regs == NULL)
return -EIO;
printk("vfc%d: registers mapped at phys_addr: 0x%lx\n virt_addr: 0x%lx\n",
instance,(unsigned long)sdev->reg_addrs[0].phys_addr,(unsigned long)dev->regs);
if (init_vfc_devstruct(dev, instance))
return -EINVAL;
if (init_vfc_hw(dev))
return -EIO;
return 0;
}
struct vfc_dev *vfc_get_dev_ptr(int instance)
{
return vfc_dev_lst[instance];
}
static DEFINE_SPINLOCK(vfc_dev_lock);
static int vfc_open(struct inode *inode, struct file *file)
{
struct vfc_dev *dev;
spin_lock(&vfc_dev_lock);
dev = vfc_get_dev_ptr(iminor(inode));
if (dev == NULL) {
spin_unlock(&vfc_dev_lock);
return -ENODEV;
}
if (dev->busy) {
spin_unlock(&vfc_dev_lock);
return -EBUSY;
}
dev->busy = 1;
spin_unlock(&vfc_dev_lock);
vfc_lock_device(dev);
vfc_csr_init(dev);
vfc_pcf8584_init(dev);
vfc_init_i2c_bus(dev);
vfc_saa9051_init(dev);
vfc_memptr_reset(dev);
vfc_captstat_reset(dev);
vfc_unlock_device(dev);
return 0;
}
static int vfc_release(struct inode *inode,struct file *file)
{
struct vfc_dev *dev;
spin_lock(&vfc_dev_lock);
dev = vfc_get_dev_ptr(iminor(inode));
if (!dev || !dev->busy) {
spin_unlock(&vfc_dev_lock);
return -EINVAL;
}
dev->busy = 0;
spin_unlock(&vfc_dev_lock);
return 0;
}
static int vfc_debug(struct vfc_dev *dev, int cmd, void __user *argp)
{
struct vfc_debug_inout inout;
unsigned char *buffer;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
switch(cmd) {
case VFC_I2C_SEND:
if(copy_from_user(&inout, argp, sizeof(inout)))
return -EFAULT;
buffer = kmalloc(inout.len, GFP_KERNEL);
if (buffer == NULL)
return -ENOMEM;
if(copy_from_user(buffer, inout.buffer, inout.len)) {
kfree(buffer);
return -EFAULT;
}
vfc_lock_device(dev);
inout.ret=
vfc_i2c_sendbuf(dev,inout.addr & 0xff,
buffer,inout.len);
if (copy_to_user(argp,&inout,sizeof(inout))) {
kfree(buffer);
return -EFAULT;
}
vfc_unlock_device(dev);
break;
case VFC_I2C_RECV:
if (copy_from_user(&inout, argp, sizeof(inout)))
return -EFAULT;
buffer = kzalloc(inout.len, GFP_KERNEL);
if (buffer == NULL)
return -ENOMEM;
vfc_lock_device(dev);
inout.ret=
vfc_i2c_recvbuf(dev,inout.addr & 0xff
,buffer,inout.len);
vfc_unlock_device(dev);
if (copy_to_user(inout.buffer, buffer, inout.len)) {
kfree(buffer);
return -EFAULT;
}
if (copy_to_user(argp,&inout,sizeof(inout))) {
kfree(buffer);
return -EFAULT;
}
kfree(buffer);
break;
default:
return -EINVAL;
};
return 0;
}
int vfc_capture_start(struct vfc_dev *dev)
{
vfc_captstat_reset(dev);
dev->control_reg = sbus_readl(&dev->regs->control);
if((dev->control_reg & VFC_STATUS_CAPTURE)) {
printk(KERN_ERR "vfc%d: vfc capture status not reset\n",
dev->instance);
return -EIO;
}
vfc_lock_device(dev);
dev->control_reg &= ~VFC_CONTROL_CAPTURE;
sbus_writel(dev->control_reg, &dev->regs->control);
dev->control_reg |= VFC_CONTROL_CAPTURE;
sbus_writel(dev->control_reg, &dev->regs->control);
dev->control_reg &= ~VFC_CONTROL_CAPTURE;
sbus_writel(dev->control_reg, &dev->regs->control);
vfc_unlock_device(dev);
return 0;
}
int vfc_capture_poll(struct vfc_dev *dev)
{
int timeout = 1000;
while (!timeout--) {
if (sbus_readl(&dev->regs->control) & VFC_STATUS_CAPTURE)
break;
vfc_i2c_delay_no_busy(dev, 100);
}
if(!timeout) {
printk(KERN_WARNING "vfc%d: capture timed out\n",
dev->instance);
return -ETIMEDOUT;
}
return 0;
}
static int vfc_set_control_ioctl(struct inode *inode, struct file *file,
struct vfc_dev *dev, unsigned long arg)
{
int setcmd, ret = 0;
if (copy_from_user(&setcmd,(void __user *)arg,sizeof(unsigned int)))
return -EFAULT;
VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCSCTRL) arg=0x%x\n",
dev->instance,setcmd));
switch(setcmd) {
case MEMPRST:
vfc_lock_device(dev);
vfc_memptr_reset(dev);
vfc_unlock_device(dev);
ret=0;
break;
case CAPTRCMD:
vfc_capture_start(dev);
vfc_capture_poll(dev);
break;
case DIAGMODE:
if(capable(CAP_SYS_ADMIN)) {
vfc_lock_device(dev);
dev->control_reg |= VFC_CONTROL_DIAGMODE;
sbus_writel(dev->control_reg, &dev->regs->control);
vfc_unlock_device(dev);
ret = 0;
} else {
ret = -EPERM;
}
break;
case NORMMODE:
vfc_lock_device(dev);
dev->control_reg &= ~VFC_CONTROL_DIAGMODE;
sbus_writel(dev->control_reg, &dev->regs->control);
vfc_unlock_device(dev);
ret = 0;
break;
case CAPTRSTR:
vfc_capture_start(dev);
ret = 0;
break;
case CAPTRWAIT:
vfc_capture_poll(dev);
ret = 0;
break;
default:
ret = -EINVAL;
break;
};
return ret;
}
int vfc_port_change_ioctl(struct inode *inode, struct file *file,
struct vfc_dev *dev, unsigned long arg)
{
int ret = 0;
int cmd;
if(copy_from_user(&cmd, (void __user *)arg, sizeof(unsigned int))) {
VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to "
"vfc_port_change_ioctl\n",
dev->instance));
return -EFAULT;
}
VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCPORTCHG) arg=0x%x\n",
dev->instance, cmd));
switch(cmd) {
case 1:
case 2:
VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_START) = 0x72;
VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_STOP) = 0x52;
VFC_SAA9051_SA(dev,VFC_SAA9051_HC_START) = 0x36;
VFC_SAA9051_SA(dev,VFC_SAA9051_HC_STOP) = 0x18;
VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) = VFC_SAA9051_BP2;
VFC_SAA9051_SA(dev,VFC_SAA9051_C3) = VFC_SAA9051_CT | VFC_SAA9051_SS3;
VFC_SAA9051_SA(dev,VFC_SAA9051_SECAM_DELAY) = 0x3e;
break;
case 3:
VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_START) = 0x3a;
VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_STOP) = 0x17;
VFC_SAA9051_SA(dev,VFC_SAA9051_HC_START) = 0xfa;
VFC_SAA9051_SA(dev,VFC_SAA9051_HC_STOP) = 0xde;
VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) =
VFC_SAA9051_BY | VFC_SAA9051_PF | VFC_SAA9051_BP2;
VFC_SAA9051_SA(dev,VFC_SAA9051_C3) = VFC_SAA9051_YC;
VFC_SAA9051_SA(dev,VFC_SAA9051_SECAM_DELAY) = 0;
VFC_SAA9051_SA(dev,VFC_SAA9051_C2) &=
~(VFC_SAA9051_SS0 | VFC_SAA9051_SS1);
break;
default:
ret = -EINVAL;
return ret;
break;
}
switch(cmd) {
case 1:
VFC_SAA9051_SA(dev,VFC_SAA9051_C2) |=
(VFC_SAA9051_SS0 | VFC_SAA9051_SS1);
break;
case 2:
VFC_SAA9051_SA(dev,VFC_SAA9051_C2) &=
~(VFC_SAA9051_SS0 | VFC_SAA9051_SS1);
VFC_SAA9051_SA(dev,VFC_SAA9051_C2) |= VFC_SAA9051_SS0;
break;
case 3:
break;
default:
ret = -EINVAL;
return ret;
break;
}
VFC_SAA9051_SA(dev,VFC_SAA9051_C3) &= ~(VFC_SAA9051_SS2);
ret=vfc_update_saa9051(dev);
udelay(500);
VFC_SAA9051_SA(dev,VFC_SAA9051_C3) |= (VFC_SAA9051_SS2);
ret=vfc_update_saa9051(dev);
return ret;
}
int vfc_set_video_ioctl(struct inode *inode, struct file *file,
struct vfc_dev *dev, unsigned long arg)
{
int ret = 0;
int cmd;
if(copy_from_user(&cmd, (void __user *)arg, sizeof(unsigned int))) {
VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to "
"vfc_set_video_ioctl\n",
dev->instance));
return ret;
}
VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCSVID) arg=0x%x\n",
dev->instance, cmd));
switch(cmd) {
case STD_NTSC:
VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~VFC_SAA9051_ALT;
VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_YPN |
VFC_SAA9051_CCFR0 | VFC_SAA9051_CCFR1 | VFC_SAA9051_FS;
ret = vfc_update_saa9051(dev);
break;
case STD_PAL:
VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~(VFC_SAA9051_YPN |
VFC_SAA9051_CCFR1 |
VFC_SAA9051_CCFR0 |
VFC_SAA9051_FS);
VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_ALT;
ret = vfc_update_saa9051(dev);
break;
case COLOR_ON:
VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_CO;
VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) &=
~(VFC_SAA9051_BY | VFC_SAA9051_PF);
ret = vfc_update_saa9051(dev);
break;
case MONO:
VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~(VFC_SAA9051_CO);
VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) |=
(VFC_SAA9051_BY | VFC_SAA9051_PF);
ret = vfc_update_saa9051(dev);
break;
default:
ret = -EINVAL;
break;
};
return ret;
}
int vfc_get_video_ioctl(struct inode *inode, struct file *file,
struct vfc_dev *dev, unsigned long arg)
{
int ret = 0;
unsigned int status = NO_LOCK;
unsigned char buf[1];
if(vfc_i2c_recvbuf(dev, VFC_SAA9051_ADDR, buf, 1)) {
printk(KERN_ERR "vfc%d: Unable to get status\n",
dev->instance);
return -EIO;
}
if(buf[0] & VFC_SAA9051_HLOCK) {
status = NO_LOCK;
} else if(buf[0] & VFC_SAA9051_FD) {
if(buf[0] & VFC_SAA9051_CD)
status = NTSC_COLOR;
else
status = NTSC_NOCOLOR;
} else {
if(buf[0] & VFC_SAA9051_CD)
status = PAL_COLOR;
else
status = PAL_NOCOLOR;
}
VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCGVID) returning status 0x%x; "
"buf[0]=%x\n", dev->instance, status, buf[0]));
if (copy_to_user((void __user *)arg,&status,sizeof(unsigned int))) {
VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to "
"vfc_get_video_ioctl\n",
dev->instance));
return ret;
}
return ret;
}
static int vfc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
unsigned int tmp;
struct vfc_dev *dev;
void __user *argp = (void __user *)arg;
dev = vfc_get_dev_ptr(iminor(inode));
if(dev == NULL)
return -ENODEV;
switch(cmd & 0x0000ffff) {
case VFCGCTRL:
#if 0
VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCGCTRL)\n", dev->instance));
#endif
tmp = sbus_readl(&dev->regs->control);
if(copy_to_user(argp, &tmp, sizeof(unsigned int))) {
ret = -EFAULT;
break;
}
ret = 0;
break;
case VFCSCTRL:
ret = vfc_set_control_ioctl(inode, file, dev, arg);
break;
case VFCGVID:
ret = vfc_get_video_ioctl(inode, file, dev, arg);
break;
case VFCSVID:
ret = vfc_set_video_ioctl(inode, file, dev, arg);
break;
case VFCHUE:
VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCHUE)\n", dev->instance));
if(copy_from_user(&tmp,argp,sizeof(unsigned int))) {
VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer "
"to IOCTL(VFCHUE)", dev->instance));
ret = -EFAULT;
} else {
VFC_SAA9051_SA(dev,VFC_SAA9051_HUE) = tmp;
vfc_update_saa9051(dev);
ret = 0;
}
break;
case VFCPORTCHG:
ret = vfc_port_change_ioctl(inode, file, dev, arg);
break;
case VFCRDINFO:
ret = -EINVAL;
VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCRDINFO)\n", dev->instance));
break;
default:
ret = vfc_debug(vfc_get_dev_ptr(iminor(inode)), cmd, argp);
break;
};
return ret;
}
static int vfc_mmap(struct file *file, struct vm_area_struct *vma)
{
unsigned int map_size, ret, map_offset;
struct vfc_dev *dev;
dev = vfc_get_dev_ptr(iminor(file->f_path.dentry->d_inode));
if(dev == NULL)
return -ENODEV;
map_size = vma->vm_end - vma->vm_start;
if(map_size > sizeof(struct vfc_regs))
map_size = sizeof(struct vfc_regs);
vma->vm_flags |=
(VM_MAYREAD | VM_MAYWRITE | VM_MAYSHARE);
map_offset = (unsigned int) (long)dev->phys_regs;
ret = io_remap_pfn_range(vma, vma->vm_start,
MK_IOSPACE_PFN(dev->which_io,
map_offset >> PAGE_SHIFT),
map_size, vma->vm_page_prot);
if(ret)
return -EAGAIN;
return 0;
}
static const struct file_operations vfc_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.ioctl = vfc_ioctl,
.mmap = vfc_mmap,
.open = vfc_open,
.release = vfc_release,
};
static int vfc_probe(void)
{
struct sbus_bus *sbus;
struct sbus_dev *sdev = NULL;
int ret;
int instance = 0, cards = 0;
for_all_sbusdev(sdev, sbus) {
if (strcmp(sdev->prom_name, "vfc") == 0) {
cards++;
continue;
}
}
if (!cards)
return -ENODEV;
vfc_dev_lst = kmalloc(sizeof(struct vfc_dev *) *
(cards+1),
GFP_KERNEL);
if (vfc_dev_lst == NULL)
return -ENOMEM;
memset(vfc_dev_lst, 0, sizeof(struct vfc_dev *) * (cards + 1));
vfc_dev_lst[cards] = NULL;
ret = register_chrdev(VFC_MAJOR, vfcstr, &vfc_fops);
if(ret) {
printk(KERN_ERR "Unable to get major number %d\n", VFC_MAJOR);
kfree(vfc_dev_lst);
return -EIO;
}
instance = 0;
for_all_sbusdev(sdev, sbus) {
if (strcmp(sdev->prom_name, "vfc") == 0) {
vfc_dev_lst[instance]=(struct vfc_dev *)
kmalloc(sizeof(struct vfc_dev), GFP_KERNEL);
if (vfc_dev_lst[instance] == NULL)
return -ENOMEM;
ret = init_vfc_device(sdev,
vfc_dev_lst[instance],
instance);
if(ret) {
printk(KERN_ERR "Unable to initialize"
" vfc%d device\n",
instance);
} else {
}
instance++;
continue;
}
}
return 0;
}
#ifdef MODULE
int init_module(void)
#else
int vfc_init(void)
#endif
{
return vfc_probe();
}
#ifdef MODULE
static void deinit_vfc_device(struct vfc_dev *dev)
{
if(dev == NULL)
return;
sbus_iounmap(dev->regs, sizeof(struct vfc_regs));
kfree(dev);
}
void cleanup_module(void)
{
struct vfc_dev **devp;
unregister_chrdev(VFC_MAJOR,vfcstr);
for (devp = vfc_dev_lst; *devp; devp++)
deinit_vfc_device(*devp);
kfree(vfc_dev_lst);
return;
}
#endif
MODULE_LICENSE("GPL");

333
drivers/sbus/char/vfc_i2c.c Normal file
View File

@@ -0,0 +1,333 @@
/*
* drivers/sbus/char/vfc_i2c.c
*
* Driver for the Videopix Frame Grabber.
*
* Functions that support the Phillips i2c(I squared C) bus on the vfc
* Documentation for the Phillips I2C bus can be found on the
* phillips home page
*
* Copyright (C) 1996 Manish Vachharajani (mvachhar@noc.rutgers.edu)
*
*/
/* NOTE: It seems to me that the documentation regarding the
pcd8584t/pcf8584 does not show the correct way to address the i2c bus.
Based on the information on the I2C bus itself and the remainder of
the Phillips docs the following algorithms appear to be correct. I am
fairly certain that the flowcharts in the phillips docs are wrong. */
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/sbus.h>
#if 0
#define VFC_I2C_DEBUG
#endif
#include "vfc.h"
#include "vfc_i2c.h"
#define WRITE_S1(__val) \
sbus_writel(__val, &dev->regs->i2c_s1)
#define WRITE_REG(__val) \
sbus_writel(__val, &dev->regs->i2c_reg)
#define VFC_I2C_READ (0x1)
#define VFC_I2C_WRITE (0x0)
/******
The i2c bus controller chip on the VFC is a pcd8584t, but
phillips claims it doesn't exist. As far as I can tell it is
identical to the PCF8584 so I treat it like it is the pcf8584.
NOTE: The pcf8584 only cares
about the msb of the word you feed it
*****/
int vfc_pcf8584_init(struct vfc_dev *dev)
{
/* This will also choose register S0_OWN so we can set it. */
WRITE_S1(RESET);
/* The pcf8584 shifts this value left one bit and uses
* it as its i2c bus address.
*/
WRITE_REG(0x55000000);
/* This will set the i2c bus at the same speed sun uses,
* and set another magic bit.
*/
WRITE_S1(SELECT(S2));
WRITE_REG(0x14000000);
/* Enable the serial port, idle the i2c bus and set
* the data reg to s0.
*/
WRITE_S1(CLEAR_I2C_BUS);
udelay(100);
return 0;
}
void vfc_i2c_delay_no_busy(struct vfc_dev *dev, unsigned long usecs)
{
schedule_timeout_uninterruptible(usecs_to_jiffies(usecs));
}
void inline vfc_i2c_delay(struct vfc_dev *dev)
{
vfc_i2c_delay_no_busy(dev, 100);
}
int vfc_init_i2c_bus(struct vfc_dev *dev)
{
WRITE_S1(ENABLE_SERIAL | SELECT(S0) | ACK);
vfc_i2c_reset_bus(dev);
return 0;
}
int vfc_i2c_reset_bus(struct vfc_dev *dev)
{
VFC_I2C_DEBUG_PRINTK((KERN_DEBUG "vfc%d: Resetting the i2c bus\n",
dev->instance));
if(dev == NULL)
return -EINVAL;
if(dev->regs == NULL)
return -EINVAL;
WRITE_S1(SEND_I2C_STOP);
WRITE_S1(SEND_I2C_STOP | ACK);
vfc_i2c_delay(dev);
WRITE_S1(CLEAR_I2C_BUS);
VFC_I2C_DEBUG_PRINTK((KERN_DEBUG "vfc%d: I2C status %x\n",
dev->instance,
sbus_readl(&dev->regs->i2c_s1)));
return 0;
}
int vfc_i2c_wait_for_bus(struct vfc_dev *dev)
{
int timeout = 1000;
while(!(sbus_readl(&dev->regs->i2c_s1) & BB)) {
if(!(timeout--))
return -ETIMEDOUT;
vfc_i2c_delay(dev);
}
return 0;
}
int vfc_i2c_wait_for_pin(struct vfc_dev *dev, int ack)
{
int timeout = 1000;
int s1;
while ((s1 = sbus_readl(&dev->regs->i2c_s1)) & PIN) {
if (!(timeout--))
return -ETIMEDOUT;
vfc_i2c_delay(dev);
}
if (ack == VFC_I2C_ACK_CHECK) {
if(s1 & LRB)
return -EIO;
}
return 0;
}
#define SHIFT(a) ((a) << 24)
int vfc_i2c_xmit_addr(struct vfc_dev *dev, unsigned char addr, char mode)
{
int ret, raddr;
#if 1
WRITE_S1(SEND_I2C_STOP | ACK);
WRITE_S1(SELECT(S0) | ENABLE_SERIAL);
vfc_i2c_delay(dev);
#endif
switch(mode) {
case VFC_I2C_READ:
raddr = SHIFT(((unsigned int)addr | 0x1));
WRITE_REG(raddr);
VFC_I2C_DEBUG_PRINTK(("vfc%d: receiving from i2c addr 0x%x\n",
dev->instance, addr | 0x1));
break;
case VFC_I2C_WRITE:
raddr = SHIFT((unsigned int)addr & ~0x1);
WRITE_REG(raddr);
VFC_I2C_DEBUG_PRINTK(("vfc%d: sending to i2c addr 0x%x\n",
dev->instance, addr & ~0x1));
break;
default:
return -EINVAL;
};
WRITE_S1(SEND_I2C_START);
vfc_i2c_delay(dev);
ret = vfc_i2c_wait_for_pin(dev,VFC_I2C_ACK_CHECK); /* We wait
for the
i2c send
to finish
here but
Sun
doesn't,
hmm */
if (ret) {
printk(KERN_ERR "vfc%d: VFC xmit addr timed out or no ack\n",
dev->instance);
return ret;
} else if (mode == VFC_I2C_READ) {
if ((ret = sbus_readl(&dev->regs->i2c_reg) & 0xff000000) != raddr) {
printk(KERN_WARNING
"vfc%d: returned slave address "
"mismatch(%x,%x)\n",
dev->instance, raddr, ret);
}
}
return 0;
}
int vfc_i2c_xmit_byte(struct vfc_dev *dev,unsigned char *byte)
{
int ret;
u32 val = SHIFT((unsigned int)*byte);
WRITE_REG(val);
ret = vfc_i2c_wait_for_pin(dev, VFC_I2C_ACK_CHECK);
switch(ret) {
case -ETIMEDOUT:
printk(KERN_ERR "vfc%d: VFC xmit byte timed out or no ack\n",
dev->instance);
break;
case -EIO:
ret = XMIT_LAST_BYTE;
break;
default:
break;
};
return ret;
}
int vfc_i2c_recv_byte(struct vfc_dev *dev, unsigned char *byte, int last)
{
int ret;
if (last) {
WRITE_REG(NEGATIVE_ACK);
VFC_I2C_DEBUG_PRINTK(("vfc%d: sending negative ack\n",
dev->instance));
} else {
WRITE_S1(ACK);
}
ret = vfc_i2c_wait_for_pin(dev, VFC_I2C_NO_ACK_CHECK);
if(ret) {
printk(KERN_ERR "vfc%d: "
"VFC recv byte timed out\n",
dev->instance);
}
*byte = (sbus_readl(&dev->regs->i2c_reg)) >> 24;
return ret;
}
int vfc_i2c_recvbuf(struct vfc_dev *dev, unsigned char addr,
char *buf, int count)
{
int ret, last;
if(!(count && buf && dev && dev->regs) )
return -EINVAL;
if ((ret = vfc_i2c_wait_for_bus(dev))) {
printk(KERN_ERR "vfc%d: VFC I2C bus busy\n", dev->instance);
return ret;
}
if ((ret = vfc_i2c_xmit_addr(dev, addr, VFC_I2C_READ))) {
WRITE_S1(SEND_I2C_STOP);
vfc_i2c_delay(dev);
return ret;
}
last = 0;
while (count--) {
if (!count)
last = 1;
if ((ret = vfc_i2c_recv_byte(dev, buf, last))) {
printk(KERN_ERR "vfc%d: "
"VFC error while receiving byte\n",
dev->instance);
WRITE_S1(SEND_I2C_STOP);
ret = -EINVAL;
}
buf++;
}
WRITE_S1(SEND_I2C_STOP | ACK);
vfc_i2c_delay(dev);
return ret;
}
int vfc_i2c_sendbuf(struct vfc_dev *dev, unsigned char addr,
char *buf, int count)
{
int ret;
if (!(buf && dev && dev->regs))
return -EINVAL;
if ((ret = vfc_i2c_wait_for_bus(dev))) {
printk(KERN_ERR "vfc%d: VFC I2C bus busy\n", dev->instance);
return ret;
}
if ((ret = vfc_i2c_xmit_addr(dev, addr, VFC_I2C_WRITE))) {
WRITE_S1(SEND_I2C_STOP);
vfc_i2c_delay(dev);
return ret;
}
while(count--) {
ret = vfc_i2c_xmit_byte(dev, buf);
switch(ret) {
case XMIT_LAST_BYTE:
VFC_I2C_DEBUG_PRINTK(("vfc%d: "
"Receiver ended transmission with "
" %d bytes remaining\n",
dev->instance, count));
ret = 0;
goto done;
break;
case 0:
break;
default:
printk(KERN_ERR "vfc%d: "
"VFC error while sending byte\n", dev->instance);
break;
};
buf++;
}
done:
WRITE_S1(SEND_I2C_STOP | ACK);
vfc_i2c_delay(dev);
return ret;
}

View File

@@ -0,0 +1,44 @@
#ifndef _LINUX_VFC_I2C_H_
#define _LINUX_VFC_I2C_H_
/* control bits */
#define PIN (0x80000000)
#define ESO (0x40000000)
#define ES1 (0x20000000)
#define ES2 (0x10000000)
#define ENI (0x08000000)
#define STA (0x04000000)
#define STO (0x02000000)
#define ACK (0x01000000)
/* status bits */
#define STS (0x20000000)
#define BER (0x10000000)
#define LRB (0x08000000)
#define AAS (0x04000000)
#define LAB (0x02000000)
#define BB (0x01000000)
#define SEND_I2C_START (PIN | ESO | STA)
#define SEND_I2C_STOP (PIN | ESO | STO)
#define CLEAR_I2C_BUS (PIN | ESO | ACK)
#define NEGATIVE_ACK ((ESO) & ~ACK)
#define SELECT(a) (a)
#define S0 (PIN | ESO | ES1)
#define S0_OWN (PIN)
#define S2 (PIN | ES1)
#define S3 (PIN | ES2)
#define ENABLE_SERIAL (PIN | ESO)
#define DISABLE_SERIAL (PIN)
#define RESET (PIN)
#define XMIT_LAST_BYTE (1)
#define VFC_I2C_ACK_CHECK (1)
#define VFC_I2C_NO_ACK_CHECK (0)
#endif /* _LINUX_VFC_I2C_H_ */