Commit 3900abd5 authored by Christer Weinigel's avatar Christer Weinigel Committed by Russell King

[PATCH] Updated NatSemi SCx200 patches for Linux-2.5

This patch adds support for the National Semiconductor SCx200
processor family to Linux 2.5.

The patch consists of the following drivers:

  arch/i386/kernel/scx200.c -- give kernel access to the GPIO pins

  drivers/chars/scx200_gpio.c -- give userspace access to the GPIO pins
  drivers/chars/scx200_wdt.c -- watchdog timer driver

  drivers/i2c/scx200_i2c.c -- use any two GPIO pins as an I2C bus
  drivers/i2c/scx200_acb.c -- driver for the Access.BUS hardware

  drivers/mtd/maps/scx200_docflash.c -- driver for a CFI flash connected
                                      to the DOCCS pin
parent 10d033f7
......@@ -1433,6 +1433,12 @@ M: Kai.Makisara@metla.fi
L: linux-scsi@vger.kernel.org
S: Maintained
SCx200 CPU SUPPORT
P: Christer Weinigel
M: christer@weinigel.se
W: http://www.weinigel.se
S: Supported
SGI VISUAL WORKSTATION 320 AND 540
P: Bent Hagemark
M: bh@sgi.com
......
......@@ -1058,3 +1058,12 @@ CONFIG_SOFTWARE_SUSPEND
absence of features.
For more information take a look at Documentation/swsusp.txt.
CONFIG_SCx200
This provides basic support for the National Semiconductor SCx200
processor. Right now this is just a driver for the GPIO pins.
If you don't know what to do here, say N.
This support is also available as a module. If compiled as a
module, it will be called scx200.o.
......@@ -293,6 +293,8 @@ else
fi
fi
tristate 'NatSemi SCx200 support' CONFIG_SCx200
source drivers/pci/Config.in
bool 'EISA support' CONFIG_EISA
......
......@@ -29,4 +29,7 @@ obj-$(CONFIG_X86_NUMAQ) += numaq.o
EXTRA_AFLAGS := -traditional
export-objs += scx200.o
obj-$(CONFIG_SCx200) += scx200.o
include $(TOPDIR)/Rules.make
/* linux/arch/i386/kernel/scx200.c
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
National Semiconductor SCx200 support. */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/scx200.h>
#include <linux/scx200.h>
#define NAME "scx200"
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi SCx200 Driver");
MODULE_LICENSE("GPL");
unsigned scx200_gpio_base = 0;
long scx200_gpio_shadow[2];
spinlock_t scx200_gpio_lock = SPIN_LOCK_UNLOCKED;
static spinlock_t scx200_gpio_config_lock = SPIN_LOCK_UNLOCKED;
u32 scx200_gpio_configure(int index, u32 mask, u32 bits)
{
u32 config, new_config;
unsigned long flags;
spin_lock_irqsave(&scx200_gpio_config_lock, flags);
outl(index, scx200_gpio_base + 0x20);
config = inl(scx200_gpio_base + 0x24);
new_config = (config & mask) | bits;
outl(new_config, scx200_gpio_base + 0x24);
spin_unlock_irqrestore(&scx200_gpio_config_lock, flags);
return config;
}
void scx200_gpio_dump(unsigned index)
{
u32 config = scx200_gpio_configure(index, ~0, 0);
printk(KERN_DEBUG "GPIO%02u: 0x%08lx", index, (unsigned long)config);
if (config & 1)
printk(" OE"); /* output enabled */
else
printk(" TS"); /* tristate */
if (config & 2)
printk(" PP"); /* push pull */
else
printk(" OD"); /* open drain */
if (config & 4)
printk(" PUE"); /* pull up enabled */
else
printk(" PUD"); /* pull up disabled */
if (config & 8)
printk(" LOCKED"); /* locked */
if (config & 16)
printk(" LEVEL"); /* level input */
else
printk(" EDGE"); /* edge input */
if (config & 32)
printk(" HI"); /* trigger on rising edge */
else
printk(" LO"); /* trigger on falling edge */
if (config & 64)
printk(" DEBOUNCE"); /* debounce */
printk("\n");
}
int __init scx200_init(void)
{
struct pci_dev *bridge;
int bank;
unsigned base;
printk(KERN_INFO NAME ": NatSemi SCx200 Driver\n");
if ((bridge = pci_find_device(PCI_VENDOR_ID_NS,
PCI_DEVICE_ID_NS_SCx200_BRIDGE,
NULL)) == NULL)
return -ENODEV;
base = pci_resource_start(bridge, 0);
printk(KERN_INFO NAME ": GPIO base 0x%x\n", base);
if (request_region(base, SCx200_GPIO_SIZE, "NatSemi SCx200 GPIO") == 0) {
printk(KERN_ERR NAME ": can't allocate I/O for GPIOs\n");
return -EBUSY;
}
scx200_gpio_base = base;
/* read the current values driven on the GPIO signals */
for (bank = 0; bank < 2; ++bank)
scx200_gpio_shadow[bank] = inl(scx200_gpio_base + 0x10 * bank);
return 0;
}
void __exit scx200_cleanup(void)
{
release_region(scx200_gpio_base, SCx200_GPIO_SIZE);
}
module_init(scx200_init);
module_exit(scx200_cleanup);
EXPORT_SYMBOL(scx200_gpio_base);
EXPORT_SYMBOL(scx200_gpio_shadow);
EXPORT_SYMBOL(scx200_gpio_lock);
EXPORT_SYMBOL(scx200_gpio_configure);
EXPORT_SYMBOL(scx200_gpio_dump);
/*
Local variables:
compile-command: "make -k -C ../../.. SUBDIRS=arch/i386/kernel modules"
c-basic-offset: 8
End:
*/
......@@ -818,6 +818,12 @@ CONFIG_MIXCOMWD
module, say M here and read <file:Documentation/modules.txt>. Most
people will say N.
CONFIG_SCx200_WDT
Enable the built-in watchdog timer support on the National
Semiconductor SCx200 processors.
If compiled as a module, it will be called scx200_watchdog.o.
CONFIG_MACHZ_WDT
If you are using a ZF Micro MachZ processor, say Y here, otherwise
N. This is the driver for the watchdog timer builtin on that
......@@ -1021,3 +1027,9 @@ CONFIG_RAW_DRIVER
Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O.
See the raw(8) manpage for more details.
CONFIG_SCx200_GPIO
Give userspace access to the GPIO pins on the National
Semiconductor SCx200 processors.
If compiled as a module, it will be called scx200_gpio.o.
......@@ -131,6 +131,7 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then
tristate ' IB700 SBC Watchdog Timer' CONFIG_IB700_WDT
tristate ' Intel i810 TCO timer / Watchdog' CONFIG_I810_TCO
tristate ' Mixcom Watchdog' CONFIG_MIXCOMWD
tristate ' NatSemi SCx200 Watchdog' CONFIG_SCx200_WDT
tristate ' SBC-60XX Watchdog Timer' CONFIG_60XX_WDT
tristate ' W83877F (EMACS) Watchdog Timer' CONFIG_W83877F_WDT
tristate ' ZF MachZ Watchdog' CONFIG_MACHZ_WDT
......@@ -193,6 +194,8 @@ if [ "$CONFIG_X86" = "y" ]; then
tristate 'ACP Modem (Mwave) support' CONFIG_MWAVE
fi
dep_tristate 'NatSemi SCx200 GPIO Support' CONFIG_SCx200_GPIO $CONFIG_SCx200
tristate ' RAW driver (/dev/raw/rawN)' CONFIG_RAW_DRIVER
endmenu
......@@ -75,6 +75,7 @@ obj-$(CONFIG_PPDEV) += ppdev.o
obj-$(CONFIG_DZ) += dz.o
obj-$(CONFIG_NWBUTTON) += nwbutton.o
obj-$(CONFIG_NWFLASH) += nwflash.o
obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o
# Only one watchdog can succeed. We probe the hardware watchdog
# drivers first, then the softdog driver. This means if your hardware
......@@ -86,6 +87,7 @@ obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
obj-$(CONFIG_IB700_WDT) += ib700wdt.o
obj-$(CONFIG_MIXCOMWD) += mixcomwd.o
obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
obj-$(CONFIG_WDT) += wdt.o
obj-$(CONFIG_WDTPCI) += wdt_pci.o
......
/* linux/drivers/char/scx200_gpio.c
National Semiconductor SCx200 GPIO driver. Allows a user space
process to play with the GPIO pins.
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/scx200_gpio.h>
#define NAME "scx200_gpio"
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi SCx200 GPIO Pin Driver");
MODULE_LICENSE("GPL");
static int major = 0; /* default to dynamic major */
MODULE_PARM(major, "i");
MODULE_PARM_DESC(major, "Major device number");
static ssize_t scx200_gpio_write(struct file *file, const char *data,
size_t len, loff_t *ppos)
{
unsigned m = minor(file->f_dentry->d_inode->i_rdev);
size_t i;
if (ppos != &file->f_pos)
return -ESPIPE;
for (i = 0; i < len; ++i) {
char c;
if (get_user(c, data+i))
return -EFAULT;
switch (c)
{
case '0':
scx200_gpio_set(m, 0);
break;
case '1':
scx200_gpio_set(m, 1);
break;
case 'O':
printk(KERN_INFO NAME ": GPIO%d output enabled\n", m);
scx200_gpio_configure(m, ~1, 1);
break;
case 'o':
printk(KERN_INFO NAME ": GPIO%d output disabled\n", m);
scx200_gpio_configure(m, ~1, 0);
break;
case 'T':
printk(KERN_INFO NAME ": GPIO%d output is push pull\n", m);
scx200_gpio_configure(m, ~2, 2);
break;
case 't':
printk(KERN_INFO NAME ": GPIO%d output is open drain\n", m);
scx200_gpio_configure(m, ~2, 0);
break;
case 'P':
printk(KERN_INFO NAME ": GPIO%d pull up enabled\n", m);
scx200_gpio_configure(m, ~4, 4);
break;
case 'p':
printk(KERN_INFO NAME ": GPIO%d pull up disabled\n", m);
scx200_gpio_configure(m, ~4, 0);
break;
}
}
return len;
}
static ssize_t scx200_gpio_read(struct file *file, char *buf,
size_t len, loff_t *ppos)
{
unsigned m = minor(file->f_dentry->d_inode->i_rdev);
int value;
if (ppos != &file->f_pos)
return -ESPIPE;
value = scx200_gpio_get(m);
if (put_user(value ? '1' : '0', buf))
return -EFAULT;
return 1;
}
static int scx200_gpio_open(struct inode *inode, struct file *file)
{
unsigned m = minor(inode->i_rdev);
if (m > 63)
return -EINVAL;
return 0;
}
static int scx200_gpio_release(struct inode *inode, struct file *file)
{
return 0;
}
static struct file_operations scx200_gpio_fops = {
.owner = THIS_MODULE,
.write = scx200_gpio_write,
.read = scx200_gpio_read,
.open = scx200_gpio_open,
.release = scx200_gpio_release,
};
static int __init scx200_gpio_init(void)
{
int r;
printk(KERN_DEBUG NAME ": NatSemi SCx200 GPIO Driver\n");
if (!scx200_gpio_present()) {
printk(KERN_ERR NAME ": no SCx200 gpio pins available\n");
return -ENODEV;
}
r = register_chrdev(major, NAME, &scx200_gpio_fops);
if (r < 0) {
printk(KERN_ERR NAME ": unable to register character device\n");
return r;
}
if (!major) {
major = r;
printk(KERN_DEBUG NAME ": got dynamic major %d\n", major);
}
return 0;
}
static void __exit scx200_gpio_cleanup(void)
{
unregister_chrdev(major, NAME);
}
module_init(scx200_gpio_init);
module_exit(scx200_gpio_cleanup);
/*
Local variables:
compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules"
c-basic-offset: 8
End:
*/
/* linux/drivers/char/scx200_wdt.c
National Semiconductor SCx200 Watchdog support
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
Som code taken from:
National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver
(c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The author(s) of this software shall not be held liable for damages
of any nature resulting due to the use of this software. This
software is provided AS-IS with no warranties. */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/scx200.h>
#define NAME "scx200_wdt"
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi SCx200 Watchdog Driver");
MODULE_LICENSE("GPL");
#ifndef CONFIG_WATCHDOG_NOWAYOUT
#define CONFIG_WATCHDOG_NOWAYOUT 0
#endif
static int margin = 60; /* in seconds */
MODULE_PARM(margin, "i");
MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
static int nowayout = CONFIG_WATCHDOG_NOWAYOUT;
MODULE_PARM(nowayout, "i");
MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
static u16 wdto_restart;
static struct semaphore open_semaphore;
static unsigned expect_close;
/* Bits of the WDCNFG register */
#define W_ENABLE 0x00fa /* Enable watchdog */
#define W_DISABLE 0x0000 /* Disable watchdog */
/* The scaling factor for the timer, this depends on the value of W_ENABLE */
#define W_SCALE (32768/1024)
static void scx200_wdt_ping(void)
{
outw(wdto_restart, SCx200_CB_BASE + SCx200_WDT_WDTO);
}
static void scx200_wdt_update_margin(void)
{
printk(KERN_INFO NAME ": timer margin %d seconds\n", margin);
wdto_restart = margin * W_SCALE;
}
static void scx200_wdt_enable(void)
{
printk(KERN_DEBUG NAME ": enabling watchdog timer, wdto_restart = %d\n",
wdto_restart);
outw(0, SCx200_CB_BASE + SCx200_WDT_WDTO);
outb(SCx200_WDT_WDSTS_WDOVF, SCx200_CB_BASE + SCx200_WDT_WDSTS);
outw(W_ENABLE, SCx200_CB_BASE + SCx200_WDT_WDCNFG);
scx200_wdt_ping();
}
static void scx200_wdt_disable(void)
{
printk(KERN_DEBUG NAME ": disabling watchdog timer\n");
outw(0, SCx200_CB_BASE + SCx200_WDT_WDTO);
outb(SCx200_WDT_WDSTS_WDOVF, SCx200_CB_BASE + SCx200_WDT_WDSTS);
outw(W_DISABLE, SCx200_CB_BASE + SCx200_WDT_WDCNFG);
}
static int scx200_wdt_open(struct inode *inode, struct file *file)
{
/* only allow one at a time */
if (down_trylock(&open_semaphore))
return -EBUSY;
scx200_wdt_enable();
expect_close = 0;
return 0;
}
static int scx200_wdt_release(struct inode *inode, struct file *file)
{
if (!expect_close) {
printk(KERN_WARNING NAME ": watchdog device closed unexpectedly, will not disable the watchdog timer\n");
} else if (!nowayout) {
scx200_wdt_disable();
}
up(&open_semaphore);
return 0;
}
static int scx200_wdt_notify_sys(struct notifier_block *this,
unsigned long code, void *unused)
{
if (code == SYS_HALT || code == SYS_POWER_OFF)
if (!nowayout)
scx200_wdt_disable();
return NOTIFY_DONE;
}
static struct notifier_block scx200_wdt_notifier =
{
notifier_call: scx200_wdt_notify_sys
};
static ssize_t scx200_wdt_write(struct file *file, const char *data,
size_t len, loff_t *ppos)
{
if (ppos != &file->f_pos)
return -ESPIPE;
/* check for a magic close character */
if (len)
{
size_t i;
scx200_wdt_ping();
expect_close = 0;
for (i = 0; i < len; ++i) {
char c;
if (get_user(c, data+i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
return len;
}
return 0;
}
static int scx200_wdt_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
static struct watchdog_info ident = {
.identity = "NatSemi SCx200 Watchdog",
.firmware_version = 1,
.options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING),
};
int new_margin;
switch (cmd) {
default:
return -ENOTTY;
case WDIOC_GETSUPPORT:
if(copy_to_user((struct watchdog_info *)arg, &ident,
sizeof(ident)))
return -EFAULT;
return 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
if (put_user(0, (int *)arg))
return -EFAULT;
return 0;
case WDIOC_KEEPALIVE:
scx200_wdt_ping();
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(new_margin, (int *)arg))
return -EFAULT;
if (new_margin < 1)
return -EINVAL;
margin = new_margin;
scx200_wdt_update_margin();
scx200_wdt_ping();
case WDIOC_GETTIMEOUT:
if (put_user(margin, (int *)arg))
return -EFAULT;
return 0;
}
}
static struct file_operations scx200_wdt_fops = {
.owner = THIS_MODULE,
.write = scx200_wdt_write,
.ioctl = scx200_wdt_ioctl,
.open = scx200_wdt_open,
.release = scx200_wdt_release,
};
static struct miscdevice scx200_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = NAME,
.fops = &scx200_wdt_fops,
};
static int __init scx200_wdt_init(void)
{
int r;
printk(KERN_DEBUG NAME ": NatSemi SCx200 Watchdog Driver\n");
/* First check that this really is a NatSemi SCx200 CPU */
if ((pci_find_device(PCI_VENDOR_ID_NS,
PCI_DEVICE_ID_NS_SCx200_BRIDGE,
NULL)) == NULL)
return -ENODEV;
/* More sanity checks, verify that the configuration block is there */
if (!scx200_cb_probe(SCx200_CB_BASE)) {
printk(KERN_WARNING NAME ": no configuration block found\n");
return -ENODEV;
}
if (!request_region(SCx200_CB_BASE + SCx200_WDT_OFFSET,
SCx200_WDT_SIZE,
"NatSemi SCx200 Watchdog")) {
printk(KERN_WARNING NAME ": watchdog I/O region busy\n");
return -EBUSY;
}
scx200_wdt_update_margin();
scx200_wdt_disable();
sema_init(&open_semaphore, 1);
r = misc_register(&scx200_wdt_miscdev);
if (r)
return r;
r = register_reboot_notifier(&scx200_wdt_notifier);
if (r) {
printk(KERN_ERR NAME ": unable to register reboot notifier");
misc_deregister(&scx200_wdt_miscdev);
return r;
}
return 0;
}
static void __exit scx200_wdt_cleanup(void)
{
unregister_reboot_notifier(&scx200_wdt_notifier);
misc_deregister(&scx200_wdt_miscdev);
release_region(SCx200_CB_BASE + SCx200_WDT_OFFSET,
SCx200_WDT_SIZE);
}
module_init(scx200_wdt_init);
module_exit(scx200_wdt_cleanup);
/*
Local variables:
compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules"
c-basic-offset: 8
End:
*/
......@@ -91,6 +91,28 @@ CONFIG_I2C_CHARDEV
<file:Documentation/modules.txt>.
The module will be called i2c-dev.o.
CONFIG_SCx200_I2C
Enable the use of two GPIO pins of a SCx200 processor as an I2C bus.
If you don't know what to do here, say N.
If compiled as a module, it will be called scx200_i2c.o.
CONFIG_SCx200_I2C_SCL
Enter the GPIO pin number used for the SCL signal. This value can
also be specified with a module parameter.
CONFIG_SCx200_I2C_SDA
Enter the GPIO pin number used for the SSA signal. This value can
also be specified with a module parameter.
CONFIG_SCx200_ACB
Enable the use of the ACCESS.bus controllers of a SCx200 processor.
If you don't know what to do here, say N.
If compiled as a module, it will be called scx200_acb.o.
CONFIG_I2C_PROC
This provides support for i2c device entries in the /proc filesystem.
The entries will be found in /proc/sys/dev/sensors.
......
......@@ -13,6 +13,12 @@ if [ "$CONFIG_I2C" != "n" ]; then
dep_tristate ' Philips style parallel port adapter' CONFIG_I2C_PHILIPSPAR $CONFIG_I2C_ALGOBIT $CONFIG_PARPORT
dep_tristate ' ELV adapter' CONFIG_I2C_ELV $CONFIG_I2C_ALGOBIT
dep_tristate ' Velleman K9000 adapter' CONFIG_I2C_VELLEMAN $CONFIG_I2C_ALGOBIT
dep_tristate ' NatSemi SCx200 I2C using GPIO pins' CONFIG_SCx200_I2C $CONFIG_SCx200 $CONFIG_I2C_ALGOBIT
if [ "$CONFIG_SCx200_I2C" != "n" ]; then
int ' GPIO pin used for SCL' CONFIG_SCx200_I2C_SCL 12
int ' GPIO pin used for SDA' CONFIG_SCx200_I2C_SDA 13
fi
dep_tristate ' NatSemi SCx200 ACCESS.bus' CONFIG_SCx200_ACB $CONFIG_I2C
fi
dep_tristate 'I2C PCF 8584 interfaces' CONFIG_I2C_ALGOPCF $CONFIG_I2C
......
......@@ -15,6 +15,8 @@ obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
obj-$(CONFIG_ITE_I2C_ALGO) += i2c-algo-ite.o
obj-$(CONFIG_ITE_I2C_ADAP) += i2c-adap-ite.o
obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
obj-$(CONFIG_I2C_PROC) += i2c-proc.o
# This is needed for automatic patch generation: sensors code starts here
......
This diff is collapsed.
/* linux/drivers/i2c/scx200_i2c.c
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
National Semiconductor SCx200 I2C bus on GPIO pins
Based on i2c-velleman.c Copyright (C) 1995-96, 2000 Simon G. Vogl
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/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/io.h>
#include <linux/scx200_gpio.h>
#define NAME "scx200_i2c"
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi SCx200 I2C Driver");
MODULE_LICENSE("GPL");
MODULE_PARM(scl, "i");
MODULE_PARM_DESC(scl, "GPIO line for SCL");
MODULE_PARM(sda, "i");
MODULE_PARM_DESC(sda, "GPIO line for SDA");
static int scl = CONFIG_SCx200_I2C_SCL;
static int sda = CONFIG_SCx200_I2C_SDA;
static void scx200_i2c_setscl(void *data, int state)
{
scx200_gpio_set(scl, state);
}
static void scx200_i2c_setsda(void *data, int state)
{
scx200_gpio_set(sda, state);
}
static int scx200_i2c_getscl(void *data)
{
return scx200_gpio_get(scl);
}
static int scx200_i2c_getsda(void *data)
{
return scx200_gpio_get(sda);
}
static int scx200_i2c_reg(struct i2c_client *client)
{
return 0;
}
static int scx200_i2c_unreg(struct i2c_client *client)
{
return 0;
}
static void scx200_i2c_inc_use(struct i2c_adapter *adap)
{
MOD_INC_USE_COUNT;
}
static void scx200_i2c_dec_use(struct i2c_adapter *adap)
{
MOD_DEC_USE_COUNT;
}
/* ------------------------------------------------------------------------
* Encapsulate the above functions in the correct operations structure.
* This is only done when more than one hardware adapter is supported.
*/
static struct i2c_algo_bit_data scx200_i2c_data = {
NULL,
scx200_i2c_setsda,
scx200_i2c_setscl,
scx200_i2c_getsda,
scx200_i2c_getscl,
10, 10, 100, /* waits, timeout */
};
static struct i2c_adapter scx200_i2c_ops = {
.name = "NatSemi SCx200 I2C",
.id = I2C_HW_B_VELLE,
.algo_data = &scx200_i2c_data,
.inc_use = scx200_i2c_inc_use,
.dec_use = scx200_i2c_dec_use,
.client_register = scx200_i2c_reg,
.client_unregister = scx200_i2c_unreg,
};
int scx200_i2c_init(void)
{
printk(KERN_DEBUG NAME ": NatSemi SCx200 I2C Driver\n");
if (!scx200_gpio_present()) {
printk(KERN_ERR NAME ": no SCx200 gpio pins available\n");
return -ENODEV;
}
printk(KERN_DEBUG NAME ": SCL=GPIO%02u, SDA=GPIO%02u\n",
scl, sda);
if (scl == -1 || sda == -1 || scl == sda) {
printk(KERN_ERR NAME ": scl and sda must be specified\n");
return -EINVAL;
}
/* Configure GPIOs as open collector outputs */
scx200_gpio_configure(scl, ~2, 5);
scx200_gpio_configure(sda, ~2, 5);
if (i2c_bit_add_bus(&scx200_i2c_ops) < 0) {
printk(KERN_ERR NAME ": adapter %s registration failed\n",
scx200_i2c_ops.name);
return -ENODEV;
}
return 0;
}
void scx200_i2c_cleanup(void)
{
i2c_bit_del_bus(&scx200_i2c_ops);
}
module_init(scx200_i2c_init);
module_exit(scx200_i2c_cleanup);
/*
Local variables:
compile-command: "make -k -C ../.. SUBDIRS=drivers/i2c modules"
c-basic-offset: 8
End:
*/
......@@ -127,6 +127,14 @@ CONFIG_MTD_MIXMEM
you probably want to enable this mapping driver. More info is at
<http://www.itc.hu/>.
CONFIG_MTD_SCx200_DOCFLASH
Enable support for a flash chip mapped using the DOCCS signal on a
National Semiconductor SCx200 processor.
If you don't know what to do here, say N.
If compiled as a module, it will be called scx200_docflash.o.
CONFIG_MTD_OCTAGON
This provides a 'mapping' driver which supports the way in which
the flash chips are connected in the Octagon-5066 Single Board
......
......@@ -26,6 +26,7 @@ if [ "$CONFIG_X86" = "y" ]; then
dep_tristate ' JEDEC Flash device mapped on Mixcom piggyback card' CONFIG_MTD_MIXMEM $CONFIG_MTD_JEDEC
dep_tristate ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC
dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC
dep_tristate ' Flash device mapped with DOCCS on NatSemi SCx200' CONFIG_MTD_SCx200_DOCFLASH $CONFIG_MTD_CFI
dep_tristate ' BIOS flash chip on Intel L440GX boards' CONFIG_MTD_L440GX $CONFIG_MTD_JEDEC
fi
......
......@@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o
obj-$(CONFIG_MTD_NETSC520) += netsc520.o
obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o
obj-$(CONFIG_MTD_VMAX) += vmax301.o
obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o
obj-$(CONFIG_MTD_OCELOT) += ocelot.o
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
......
/* linux/drivers/mtd/maps/scx200_docflash.c
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
National Semiconductor SCx200 flash mapped with DOCCS
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/pci.h>
#include <linux/scx200.h>
#define NAME "scx200_docflash"
MODULE_AUTHOR("Christer Weinigel <wingel@hack.org>");
MODULE_DESCRIPTION("NatSemi SCx200 DOCCS Flash Driver");
MODULE_LICENSE("GPL");
/* Set this to one if you want to partition the flash */
#define PARTITION 1
MODULE_PARM(probe, "i");
MODULE_PARM_DESC(probe, "Probe for a BIOS mapping");
MODULE_PARM(size, "i");
MODULE_PARM_DESC(size, "Size of the flash mapping");
MODULE_PARM(width, "i");
MODULE_PARM_DESC(width, "Data width of the flash mapping (8/16)");
MODULE_PARM(flashtype, "s");
MODULE_PARM_DESC(flashtype, "Type of MTD probe to do");
static int probe = 0; /* Don't autoprobe */
static unsigned size = 0x1000000; /* 16 MB the whole ISA address space */
static unsigned width = 8; /* Default to 8 bits wide */
static char *flashtype = "cfi_probe";
static struct resource docmem = {
.flags = IORESOURCE_MEM,
.name = "NatSemi SCx200 DOCCS Flash",
};
static struct mtd_info *mymtd;
#if PARTITION
static struct mtd_partition partition_info[] = {
{
.name = "DOCCS Boot kernel",
.offset = 0,
.size = 0xc0000
},
{
.name = "DOCCS Low BIOS",
.offset = 0xc0000,
.size = 0x40000
},
{
.name = "DOCCS File system",
.offset = 0x100000,
.size = ~0 /* calculate from flash size */
},
{
.name = "DOCCS High BIOS",
.offset = ~0, /* calculate from flash size */
.size = 0x80000
},
};
#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
#endif
static __u8 scx200_docflash_read8(struct map_info *map, unsigned long ofs)
{
return __raw_readb(map->map_priv_1 + ofs);
}
static __u16 scx200_docflash_read16(struct map_info *map, unsigned long ofs)
{
return __raw_readw(map->map_priv_1 + ofs);
}
static void scx200_docflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
memcpy_fromio(to, map->map_priv_1 + from, len);
}
static void scx200_docflash_write8(struct map_info *map, __u8 d, unsigned long adr)
{
__raw_writeb(d, map->map_priv_1 + adr);
mb();
}
static void scx200_docflash_write16(struct map_info *map, __u16 d, unsigned long adr)
{
__raw_writew(d, map->map_priv_1 + adr);
mb();
}
static void scx200_docflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
memcpy_toio(map->map_priv_1 + to, from, len);
}
static struct map_info scx200_docflash_map = {
.name = "NatSemi SCx200 DOCCS Flash",
.read8 = scx200_docflash_read8,
.read16 = scx200_docflash_read16,
.copy_from = scx200_docflash_copy_from,
.write8 = scx200_docflash_write8,
.write16 = scx200_docflash_write16,
.copy_to = scx200_docflash_copy_to
};
int __init init_scx200_docflash(void)
{
unsigned u;
unsigned base;
unsigned ctrl;
unsigned pmr;
struct pci_dev *bridge;
printk(KERN_DEBUG NAME ": NatSemi SCx200 DOCCS Flash Driver\n");
if ((bridge = pci_find_device(PCI_VENDOR_ID_NS,
PCI_DEVICE_ID_NS_SCx200_BRIDGE,
NULL)) == NULL)
return -ENODEV;
if (!scx200_cb_probe(SCx200_CB_BASE)) {
printk(KERN_WARNING NAME ": no configuration block found\n");
return -ENODEV;
}
if (probe) {
/* Try to use the present flash mapping if any */
pci_read_config_dword(bridge, SCx200_DOCCS_BASE, &base);
pci_read_config_dword(bridge, SCx200_DOCCS_CTRL, &ctrl);
pmr = inl(SCx200_CB_BASE + SCx200_PMR);
if (base == 0
|| (ctrl & 0x07000000) != 0x07000000
|| (ctrl & 0x0007ffff) == 0)
return -ENODEV;
size = ((ctrl&0x1fff)<<13) + (1<<13);
for (u = size; u > 1; u >>= 1)
;
if (u != 1)
return -ENODEV;
if (pmr & (1<<6))
width = 16;
else
width = 8;
docmem.start = base;
docmem.end = base + size;
if (request_resource(&iomem_resource, &docmem)) {
printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n");
return -ENOMEM;
}
} else {
for (u = size; u > 1; u >>= 1)
;
if (u != 1) {
printk(KERN_ERR NAME ": invalid size for flash mapping\n");
return -EINVAL;
}
if (width != 8 && width != 16) {
printk(KERN_ERR NAME ": invalid bus width for flash mapping\n");
return -EINVAL;
}
if (allocate_resource(&iomem_resource, &docmem,
size,
0xc0000000, 0xffffffff,
size, NULL, NULL)) {
printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n");
return -ENOMEM;
}
ctrl = 0x07000000 | ((size-1) >> 13);
printk(KERN_INFO "DOCCS BASE=0x%08lx, CTRL=0x%08lx\n", (long)docmem.start, (long)ctrl);
pci_write_config_dword(bridge, SCx200_DOCCS_BASE, docmem.start);
pci_write_config_dword(bridge, SCx200_DOCCS_CTRL, ctrl);
pmr = inl(SCx200_CB_BASE + SCx200_PMR);
if (width == 8) {
pmr &= ~(1<<6);
} else {
pmr |= (1<<6);
}
outl(pmr, SCx200_CB_BASE + SCx200_PMR);
}
printk(KERN_INFO NAME ": DOCCS mapped at 0x%lx-0x%lx, width %d\n",
docmem.start, docmem.end, width);
scx200_docflash_map.size = size;
if (width == 8)
scx200_docflash_map.buswidth = 1;
else
scx200_docflash_map.buswidth = 2;
scx200_docflash_map.map_priv_1 = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size);
if (!scx200_docflash_map.map_priv_1) {
printk(KERN_ERR NAME ": failed to ioremap the flash\n");
release_resource(&docmem);
return -EIO;
}
mymtd = do_map_probe(flashtype, &scx200_docflash_map);
if (!mymtd) {
printk(KERN_ERR NAME ": unable to detect flash\n");
iounmap((void *)scx200_docflash_map.map_priv_1);
release_resource(&docmem);
return -ENXIO;
}
if (size < mymtd->size)
printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n");
mymtd->module = THIS_MODULE;
#if PARTITION
partition_info[3].offset = mymtd->size-partition_info[3].size;
partition_info[2].size = partition_info[3].offset-partition_info[2].offset;
add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS);
#else
add_mtd_device(mymtd);
#endif
return 0;
}
static void __exit cleanup_scx200_docflash(void)
{
if (mymtd) {
#if PARTITION
del_mtd_partitions(mymtd);
#else
del_mtd_device(mymtd);
#endif
map_destroy(mymtd);
}
if (scx200_docflash_map.map_priv_1) {
iounmap((void *)scx200_docflash_map.map_priv_1);
release_resource(&docmem);
}
}
module_init(init_scx200_docflash);
module_exit(cleanup_scx200_docflash);
/*
Local variables:
compile-command: "make -k -C ../../.. SUBDIRS=drivers/mtd/maps modules"
c-basic-offset: 8
End:
*/
......@@ -288,6 +288,12 @@
#define PCI_DEVICE_ID_NS_87560_USB 0x0012
#define PCI_DEVICE_ID_NS_83815 0x0020
#define PCI_DEVICE_ID_NS_83820 0x0022
#define PCI_DEVICE_ID_NS_SCx200_BRIDGE 0x0500
#define PCI_DEVICE_ID_NS_SCx200_SMI 0x0501
#define PCI_DEVICE_ID_NS_SCx200_IDE 0x0502
#define PCI_DEVICE_ID_NS_SCx200_AUDIO 0x0503
#define PCI_DEVICE_ID_NS_SCx200_VIDEO 0x0504
#define PCI_DEVICE_ID_NS_SCx200_XBUS 0x0505
#define PCI_DEVICE_ID_NS_87410 0xd001
#define PCI_VENDOR_ID_TSENG 0x100c
......
/* linux/include/linux/scx200.h
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
Defines for the National Semiconductor SCx200 Processors
*/
/* Interesting stuff for the National Semiconductor SCx200 CPU */
/* F0 PCI Header/Bridge Configuration Registers */
#define SCx200_DOCCS_BASE 0x78 /* DOCCS Base Address Register */
#define SCx200_DOCCS_CTRL 0x7c /* DOCCS Control Register */
/* GPIO Register Block */
#define SCx200_GPIO_SIZE 0x2c /* Size of GPIO register block */
/* General Configuration Block */
#define SCx200_CB_BASE 0x9000 /* Base fixed at 0x9000 according to errata */
/* Watchdog Timer */
#define SCx200_WDT_OFFSET 0x00 /* offset within configuration block */
#define SCx200_WDT_SIZE 0x05 /* size */
#define SCx200_WDT_WDTO 0x00 /* Time-Out Register */
#define SCx200_WDT_WDCNFG 0x02 /* Configuration Register */
#define SCx200_WDT_WDSTS 0x04 /* Status Register */
#define SCx200_WDT_WDSTS_WDOVF (1<<0) /* Overflow bit */
/* High Resolution Timer */
#define SCx200_TIMER_OFFSET 0x08
#define SCx200_TIMER_SIZE 0x05
/* Clock Generators */
#define SCx200_CLOCKGEN_OFFSET 0x10
#define SCx200_CLOCKGEN_SIZE 0x10
/* Pin Multiplexing and Miscellaneous Configuration Registers */
#define SCx200_MISC_OFFSET 0x30
#define SCx200_MISC_SIZE 0x10
#define SCx200_PMR 0x30 /* Pin Multiplexing Register */
#define SCx200_MCR 0x34 /* Miscellaneous Configuration Register */
#define SCx200_INTSEL 0x38 /* Interrupt Selection Register */
#define SCx200_IID 0x3c /* IA On a Chip Identification Number Reg */
#define SCx200_REV 0x3d /* Revision Register */
#define SCx200_CBA 0x3e /* Configuration Base Address Register */
/* Verify that the configuration block really is there */
#define scx200_cb_probe(base) (inw((base) + SCx200_CBA) == (base))
/*
Local variables:
compile-command: "make -C ../.. bzImage modules"
c-basic-offset: 8
End:
*/
#include <linux/spinlock.h>
u32 scx200_gpio_configure(int index, u32 set, u32 clear);
void scx200_gpio_dump(unsigned index);
extern unsigned scx200_gpio_base;
extern spinlock_t scx200_gpio_lock;
extern long scx200_gpio_shadow[2];
#define scx200_gpio_present() (scx200_gpio_base!=0)
/* Definitions to make sure I do the same thing in all functions */
#define __SCx200_GPIO_BANK unsigned bank = index>>5
#define __SCx200_GPIO_IOADDR unsigned short ioaddr = scx200_gpio_base+0x10*bank
#define __SCx200_GPIO_SHADOW long *shadow = scx200_gpio_shadow+bank
#define __SCx200_GPIO_INDEX index &= 31
#define __SCx200_GPIO_OUT __asm__ __volatile__("outsl":"=mS" (shadow):"d" (ioaddr), "0" (shadow))
/* returns the value of the GPIO pin */
static inline int scx200_gpio_get(int index) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_IOADDR + 0x04;
__SCx200_GPIO_INDEX;
return (inl(ioaddr) & (1<<index)) ? 1 : 0;
}
/* return the value driven on the GPIO signal (the value that will be
driven if the GPIO is configured as an output, it might not be the
state of the GPIO right now if the GPIO is configured as an input) */
static inline int scx200_gpio_current(int index) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_INDEX;
return (scx200_gpio_shadow[bank] & (1<<index)) ? 1 : 0;
}
/* drive the GPIO signal high */
static inline void scx200_gpio_set_high(int index) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_IOADDR;
__SCx200_GPIO_SHADOW;
__SCx200_GPIO_INDEX;
set_bit(index, shadow);
__SCx200_GPIO_OUT;
}
/* drive the GPIO signal low */
static inline void scx200_gpio_set_low(int index) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_IOADDR;
__SCx200_GPIO_SHADOW;
__SCx200_GPIO_INDEX;
clear_bit(index, shadow);
__SCx200_GPIO_OUT;
}
/* drive the GPIO signal to state */
static inline void scx200_gpio_set(int index, int state) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_IOADDR;
__SCx200_GPIO_SHADOW;
__SCx200_GPIO_INDEX;
if (state)
set_bit(index, shadow);
else
clear_bit(index, shadow);
__SCx200_GPIO_OUT;
}
/* toggle the GPIO signal */
static inline void scx200_gpio_change(int index) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_IOADDR;
__SCx200_GPIO_SHADOW;
__SCx200_GPIO_INDEX;
change_bit(index, shadow);
__SCx200_GPIO_OUT;
}
#undef __SCx200_GPIO_BANK
#undef __SCx200_GPIO_IOADDR
#undef __SCx200_GPIO_SHADOW
#undef __SCx200_GPIO_INDEX
#undef __SCx200_GPIO_OUT
/*
Local variables:
compile-command: "make -C ../.. bzImage modules"
c-basic-offset: 8
End:
*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment