diff -urN linux-2.4.19-pre2/Documentation/Configure.help linux/Documentation/Configure.help --- linux-2.4.19-pre2/Documentation/Configure.help Sat Mar 2 14:46:10 2002 +++ linux/Documentation/Configure.help Sat Mar 2 15:24:59 2002 @@ -24451,6 +24451,74 @@ information: http://www.candelatech.com/~greear/vlan.html If unsure, you can safely say 'N'. +National Geode SCx200 support +CONFIG_ARCH_SCx200 + This provides basic support for the features of a National + Semiconductor SCx200 processor. + + If you don't know what to do here, say N. + + This support is also available as a module. You probably don't want + to do this unless you are a developer modifying this driver. If + compiled as a module, it will be called scx200.o. + +Nano Computer support +CONFIG_ARCH_SCx200_NANO + The Nano Computer is a SC2200 based computer from Nano Computer + Systems AB, URL: . Say Y here to create + a kernel that will have support for the Nano Computer. + + WARNING! Do NOT run a kernel compiled for the Nano Computer on any + other SC2200 platform; doing so could potentially do bad things to + your computer, such as fry your TFT screen. + + This support is also available as a module. You probably don't want + to do this unless you are a developer modifying this driver. If + compiled as a module, it will be called scx200_nano.o. + +NatSemi SCx200 Watchdog +CONFIG_SCx200_WATCHDOG + Enable the built-in watchdog timer support on SCx200 processors. + + If compiled as a module, it will be called scx200_watchdog.o. + +NatSemi SCx200 I2C using GPIO pins +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. + +GPIO pin used for SCL +CONFIG_SCx200_I2C_SCL + Enter the GPIO pin number used for the SCL signal. This value can + also be specified with a module parameter, if you want to do that, + leave the value as -1. + +GPIO pin used for SDA +CONFIG_SCx200_I2C_SDA + Enter the GPIO pin number used for the SSA signal. This value can + also be specified with a module parameter, if you want to do that, + leave the value as -1. + +NatSemi SCx200 ACCESS.bus +CONFIG_SCx200_AB + 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_ab.o. + +Flash device mapped with DOCCS on NatSemi SCx200 +CONFIG_MTD_SCx200_DOCFLASH + Enable support for an CFI flash mapped using the DOCCS signal on a + 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. + # # A couple of things I keep forgetting: # capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet, diff -urN linux-2.4.19-pre2/MAINTAINERS linux/MAINTAINERS --- linux-2.4.19-pre2/MAINTAINERS Sat Mar 2 14:46:12 2002 +++ linux/MAINTAINERS Sat Mar 2 15:24:59 2002 @@ -1375,6 +1375,12 @@ L: linux-scsi@vger.kernel.org S: Maintained +SCx200 CPU SUPPORT +P: Christer Weinigel +M: wingel@nano-system.com +W: http://www.nano-system.com +S: Supported + SGI VISUAL WORKSTATION 320 AND 540 P: Bent Hagemark M: bh@sgi.com diff -urN linux-2.4.19-pre2/arch/i386/config.in linux/arch/i386/config.in --- linux-2.4.19-pre2/arch/i386/config.in Sat Mar 2 14:46:13 2002 +++ linux/arch/i386/config.in Sat Mar 2 15:25:09 2002 @@ -238,6 +238,9 @@ fi fi +tristate 'National Geode SCx200 support' CONFIG_ARCH_SCx200 +dep_tristate ' Nano Computer support' CONFIG_ARCH_SCx200_NANO $CONFIG_ARCH_SCx200 + source drivers/pci/Config.in bool 'EISA support' CONFIG_EISA diff -urN linux-2.4.19-pre2/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile --- linux-2.4.19-pre2/arch/i386/kernel/Makefile Fri Nov 9 23:21:21 2001 +++ linux/arch/i386/kernel/Makefile Sat Mar 2 15:25:09 2002 @@ -41,4 +41,8 @@ obj-$(CONFIG_X86_IO_APIC) += io_apic.o acpitable.o obj-$(CONFIG_X86_VISWS_APIC) += visws_apic.o +export-objs += scx200.o +obj-$(CONFIG_ARCH_SCx200) += scx200.o +obj-$(CONFIG_ARCH_SCx200_NANO) += scx200_nano.o + include $(TOPDIR)/Rules.make diff -urN linux-2.4.19-pre2/arch/i386/kernel/scx200.c linux/arch/i386/kernel/scx200.c --- linux-2.4.19-pre2/arch/i386/kernel/scx200.c Thu Jan 1 01:00:00 1970 +++ linux/arch/i386/kernel/scx200.c Sat Mar 2 15:25:10 2002 @@ -0,0 +1,241 @@ +/* linux/arch/i386/kernel/scx200.c + + Copyright (c) 2001,2002 Christer Weinigel + + National Semiconductor SCx200 support + + Probe for a SCx200 CPU and locate the Configuration Block. + Provide a more user friendly interface to the GPIO pins. +*/ + +#include +#include +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Christer Weinigel "); +MODULE_DESCRIPTION("National Semiconductor Geode SCx200 support"); +MODULE_LICENSE("GPL"); + +extern int scx200_nano_init(void); + +#define CONFIG_BLOCK_SIZE 0x40 +#define GX_BASE_SIZE (64*1024) + +static char name[] = "scx200"; + +struct pci_dev *scx200_f0_pdev; +struct pci_dev *scx200_f5_pdev; +unsigned scx200_config_block; +void *gx1_control; + +EXPORT_SYMBOL(scx200_f0_pdev); +EXPORT_SYMBOL(scx200_f5_pdev); +EXPORT_SYMBOL(scx200_config_block); +EXPORT_SYMBOL(gx1_control); + +static int __init scx200_probe_config_block(unsigned cb_base) +{ + unsigned cb_data; + + /* Check for the presence of the Configuration block. */ + cb_data = inw(cb_base + 0x3e); + return (cb_data == cb_base); +} + +int __init scx200_init(void) +{ + int r; + int bank; + long flags; + u8 gcr; + unsigned gx_base; + unsigned cb_base; + const char *cb_text; + + printk(KERN_DEBUG "%s\n", __PRETTY_FUNCTION__); + + if ((scx200_f0_pdev = pci_find_device(PCI_VENDOR_ID_NS, + PCI_DEVICE_ID_NS_SCx200_BRIDGE, + NULL)) == NULL) + return -ENODEV; + + if ((scx200_f5_pdev = pci_find_device(PCI_VENDOR_ID_NS, + PCI_DEVICE_ID_NS_SCx200_XBUS, + NULL)) == NULL) + return -ENODEV; + + r = pci_request_regions(scx200_f0_pdev, name); + if (r) { + printk(KERN_WARNING "%s: unable to allocate the F0 I/O region\n", name); + return r; + } + + /* First check if the configuration BIOS has stored the + Configuration Block address in the scratch pad register */ + pci_read_config_dword(scx200_f5_pdev, 0x64, &cb_base); + if (cb_base && scx200_probe_config_block(cb_base)) { + cb_text = "BIOS"; + goto have_cb; + } + + /* The BIOS hasn't set up the scratch pad register, so first + try the default address specified in the SCx200 errata */ + cb_base = 0x9000; + if (scx200_probe_config_block(cb_base)) { + cb_text = "default"; + goto have_cb; + } + + /* Didn't work either, do it the hard way by scanning the I/O + space for it */ + printk(KERN_DEBUG "%s: Scanning for Configuration Block\n", name); + for (cb_base = 0x400; cb_base < 0x10000; cb_base += 64) { + if (scx200_probe_config_block(cb_base)) { + cb_text = "scanned"; + goto have_cb; + } + } + + printk(KERN_WARNING "%s: Unable to find Configuration Block\n", name); + cb_base = 0; + pci_release_regions(scx200_f0_pdev); + return -ENODEV; + + have_cb: + printk(KERN_INFO "%s: Configuration Block at 0x%04x (%s)\n", + name, cb_base, cb_text); + scx200_config_block = cb_base; + + if (!request_region(scx200_config_block, CONFIG_BLOCK_SIZE, name)) { + printk(KERN_WARNING "%s: unable to allocate the Configuration Block I/O region\n", name); + pci_release_regions(scx200_f0_pdev); + return -EBUSY; + } + + save_flags(flags); + cli(); + outb(0xb8, 0x22); /* Read GCR */ + gcr = inb(0x23); + restore_flags(flags); + gx_base = (gcr & 3) << 30; + if (gx_base) { + printk(KERN_INFO "%s: GX_BASE is 0x%08x\n", name, gx_base); + gx1_control = ioremap(gx_base, GX_BASE_SIZE); + if (!gx1_control) + printk(KERN_WARNING "%s: unable to allocate the GX_BASE I/O region\n", name); + } + else + printk(KERN_INFO "%s: GX_BASE is disabled\n",name); + + if (gx_base) { + printk(KERN_DEBUG "MC_MEM_CNTRL1: 0x%08x\n", readl(gx1_control + 0x8400)); + printk(KERN_DEBUG "MC_MEM_CNTRL2: 0x%08x\n", readl(gx1_control + 0x8404)); + printk(KERN_DEBUG "MC_MEM_BANK_CNFG: 0x%08x\n", readl(gx1_control + 0x8408)); + printk(KERN_DEBUG "MC_SYNC_TIM1: 0x%08x\n", readl(gx1_control + 0x840c)); + } + + scx200_gpio_base = pci_resource_start(scx200_f0_pdev, 0); + printk(KERN_INFO "%s: GPIOs at 0x%x\n", name, scx200_gpio_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); + +#ifdef CONFIG_ARCH_SCx200_NANO + scx200_nano_init(); +#endif + + return 0; +} + +#ifdef MODULE +void __exit scx200_cleanup(void) +{ + printk(KERN_DEBUG "%s\n", __PRETTY_FUNCTION__); + + if (gx1_control) + iounmap(gx1_control); + release_region(scx200_config_block, CONFIG_BLOCK_SIZE); + pci_release_regions(scx200_f0_pdev); +} + +module_init(scx200_init); +module_exit(scx200_cleanup); +#endif MODULE + +/* GPIO stuff */ + +unsigned scx200_gpio_base; +u32 scx200_gpio_shadow[2]; + +spinlock_t scx200_gpio_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t scx200_gpio_config_lock = SPIN_LOCK_UNLOCKED; + +/* this should be spinlock protected I suppose */ +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"); +} + +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 bzImage" + c-basic-offset: 8 + End: +*/ diff -urN linux-2.4.19-pre2/arch/i386/kernel/scx200_nano.c linux/arch/i386/kernel/scx200_nano.c --- linux-2.4.19-pre2/arch/i386/kernel/scx200_nano.c Thu Jan 1 01:00:00 1970 +++ linux/arch/i386/kernel/scx200_nano.c Sat Mar 2 15:25:10 2002 @@ -0,0 +1,197 @@ +/* linux/arch/i386/kernel/scx200_nano.c + + Copyright (c) 2001,2002 Christer Weinigel + + Do any hardware specific setup for the SC2200 based Nano Computer. + + WARNING! Do NOT run a kernel compiled for the Nano Computer on any + other SC2200 platform; doing so could potentially do bad things to + your computer, such as fry your TFT screen. */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Christer Weinigel "); +MODULE_DESCRIPTION("Nano Computer support"); +MODULE_LICENSE("GPL"); + +static char name[] = "scx200_nano"; + +/* Where should the flash be mapped? */ +#define DOC_WINDOW_ADDR (0xc0000000) +#define DOC_WINDOW_SIZE (16 * 1024 * 1024) +#define DOC_BUSWIDTH 1 + +static void __init nano_fixup_irq(void) +{ + struct pci_dev *dev; + int i; + u16 int_map; + int intb, intc; + + if (scx200_f0_pdev == NULL) { + printk(KERN_ERR "scx200_i2c: Not a SCx200 CPU\n"); + return; + } + + pci_read_config_word(scx200_f0_pdev, 0x5c, &int_map); + intb = (int_map >> 4) & 15; + intc = (int_map >> 8) & 15; + + for (i = 0; i < 4; i++) { + u8 pin; + int irq; + + dev = pci_find_slot(0, PCI_DEVFN(0x11, i)); + if (!dev) + break; + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (!pin) { + continue; + } + switch (pin) { + case 1: + case 3: + irq = intc; + break; + case 2: + case 4: + irq = intb; + break; + default: + irq = dev->irq; + } + + if (irq != dev->irq) { + printk(KERN_INFO "%s: IRQ fixup for %02x:%02x.%d, was %d, new %d\n", name, 0, 0x11, i, dev->irq, irq); + dev->irq = irq; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + } + } +} + +int __init scx200_nano_init(void) +{ + u32 pmr, mcr; + u32 doccs_base, doccs_ctrl; + + printk(KERN_DEBUG "%s\n", __PRETTY_FUNCTION__); + + if (scx200_f0_pdev == NULL) { + printk(KERN_ERR "%s: Not a SCx200 CPU\n", name); + return -ENODEV; + } + + pmr = inl(scx200_config_block + SCx200_PMR); + mcr = inl(scx200_config_block + SCx200_MCR); + + printk(KERN_DEBUG "%s: PMR = 0x%08x\n", name, pmr); + printk(KERN_DEBUG "%s: MCR = 0x%08x\n", name, mcr); + + pci_read_config_dword(scx200_f0_pdev, 0x78, &doccs_base); + pci_read_config_dword(scx200_f0_pdev, 0x7c, &doccs_ctrl); + + printk(KERN_DEBUG "%s: PCI DOCCS Base = 0x%08x\n", name, doccs_base); + printk(KERN_DEBUG "%s: PCI DOCCS Ctrl = 0x%08x\n", name, doccs_ctrl); + + printk(KERN_INFO "%s: GPIO01/#IOCS1,GPIO20/#DOCCS on H2,H3\n", name); + pmr &= ~(1<<23); + + printk(KERN_INFO "%s: Configuring GPIOs\n", name); + pmr &= ~(1<<13); /* GPIO01 not #IOCS1 */ + pmr &= ~((1<<14) | (1<<22)); /* GPIO32-GPIO39 not LPC */ + +#if 0 + pmr &= ~(1<<19); /* GPIO12-GPIO13 not ACCESS.bus 2 */ +#else + pmr |= 1<<19; /* AB2, not GPIO12,GPIO13 */ +#endif + + /* Pin header GPIOs */ + scx200_gpio_configure(36, 0, 0); /* GPIO36 = HGPIO1 */ + scx200_gpio_configure(35, 0, 0); /* GPIO35 = HGPIO2 */ + scx200_gpio_configure(1, 0, 0); /* GPIO1 = HGPIO3 */ + scx200_gpio_configure(17, 0, 0); /* GPIO17 = HGPIO4 */ + + /* Unused GPIOs */ + scx200_gpio_configure(37, 0, 0); /* GPIO37 = HGPIO8 not connected */ + scx200_gpio_configure(39, 0, 0); /* GPIO39/SERIRQ not connected */ + + /* LED GPIOs */ + scx200_gpio_configure(32, 7, 7); + scx200_gpio_configure(33, 7, 7); + scx200_gpio_configure(34, 7, 7); + + /* EEPROM U6WP GPIO */ + scx200_gpio_configure(38, 7, 7); + + printk(KERN_INFO "%s: #DOCCS not GPIO20, ZWS\n", name); + pmr |= (1<<7); /* #DOCCS not GPIO20 */ + mcr |= (1<<10); /* Zero Wait States */ + + printk(KERN_DEBUG "%s: DOC 0x%x-0x%x, width %d\n", + name, DOC_WINDOW_ADDR, DOC_WINDOW_ADDR+DOC_WINDOW_SIZE, + DOC_BUSWIDTH); + scx200_gpio_configure(20, 7, 7); /* DOCCS GPIOs (not needed?) */ + doccs_base = DOC_WINDOW_ADDR; + doccs_ctrl = ((DOC_WINDOW_SIZE-1) >> 13); + doccs_ctrl |= 0x07000000; + if (DOC_BUSWIDTH == 1) + pmr &= ~(1<<6); + else if (DOC_BUSWIDTH == 2) + pmr |= (1<<6); + else + printk(KERN_WARNING "%s: invalid DOC bus with %d\n", + name, DOC_BUSWIDTH); + + /* INTC */ + pmr |= 1<<4; /* #INTC not GPIO19 */ + pmr &= ~(1<<9); /* #INTC not IOCHRDY */ + + printk(KERN_DEBUG "%s: PCI DOCCS Base = 0x%08x\n", name, doccs_base); + printk(KERN_DEBUG "%s: PCI DOCCS Ctrl = 0x%08x\n", name, doccs_ctrl); + + pci_write_config_dword(scx200_f0_pdev, 0x78, doccs_base); + pci_write_config_dword(scx200_f0_pdev, 0x7c, doccs_ctrl); + + printk(KERN_DEBUG "%s: PMR = 0x%08x\n", name, pmr); + printk(KERN_DEBUG "%s: MCR = 0x%08x\n", name, mcr); + + outl(pmr, scx200_config_block + SCx200_PMR); + outl(mcr, scx200_config_block + SCx200_MCR); + + printk(KERN_DEBUG "%s: Removing EEPROM write protect\n", name); + scx200_gpio_set_low(38); + + /* turn off two top leds */ + scx200_gpio_set_low(32); + scx200_gpio_set_low(33); + scx200_gpio_set_high(34); + + nano_fixup_irq(); + + return 0; +} + +#ifdef MODULE +void __exit scx200_nano_cleanup(void) +{ + printk(KERN_INFO "%s\n", __PRETTY_FUNCTION__); +} + +module_init(scx200_nano_init); +module_exit(scx200_nano_cleanup); +#endif + +/* + Local variables: + compile-command: "make -k -C ../../.. SUBDIRS=arch/i386/kernel bzImage" + c-basic-offset: 8 + End: +*/ Binary files linux-2.4.19-pre2/core and linux/core differ diff -urN linux-2.4.19-pre2/drivers/char/Config.in linux/drivers/char/Config.in --- linux-2.4.19-pre2/drivers/char/Config.in Sat Mar 2 14:46:36 2002 +++ linux/drivers/char/Config.in Sat Mar 2 15:26:02 2002 @@ -196,6 +196,7 @@ tristate ' IB700 SBC Watchdog Timer' CONFIG_IB700_WDT tristate ' Intel i810 TCO timer / Watchdog' CONFIG_I810_TCO tristate ' Mixcom Watchdog' CONFIG_MIXCOMWD + dep_tristate ' NatSemi SCx200 Watchdog' CONFIG_SCx200_WATCHDOG $CONFIG_ARCH_SCx200 tristate ' SBC-60XX Watchdog Timer' CONFIG_60XX_WDT tristate ' W83877F (EMACS) Watchdog Timer' CONFIG_W83877F_WDT tristate ' ZF MachZ Watchdog' CONFIG_MACHZ_WDT diff -urN linux-2.4.19-pre2/drivers/char/Makefile linux/drivers/char/Makefile --- linux-2.4.19-pre2/drivers/char/Makefile Sat Mar 2 14:46:36 2002 +++ linux/drivers/char/Makefile Sat Mar 2 15:26:02 2002 @@ -247,6 +247,7 @@ obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_INDYDOG) += indydog.o +obj-$(CONFIG_SCx200_WATCHDOG) += scx200_watchdog.o subdir-$(CONFIG_MWAVE) += mwave ifeq ($(CONFIG_MWAVE),y) diff -urN linux-2.4.19-pre2/drivers/char/scx200_watchdog.c linux/drivers/char/scx200_watchdog.c --- linux-2.4.19-pre2/drivers/char/scx200_watchdog.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/char/scx200_watchdog.c Sat Mar 2 15:26:09 2002 @@ -0,0 +1,263 @@ +/* linux/drivers/char/scx200_watchdog.c + + National Semiconductor SCx200 Watchdog support + + Copyright (c) 2001,2002 Christer Weinigel + + Som code taken from: + National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver + (c) Copyright 2002 Zwane Mwaikambo + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Christer Weinigel "); +MODULE_DESCRIPTION("NatSemi SCx200 Watchdog"); +MODULE_LICENSE("GPL"); + +#ifndef CONFIG_WATCHDOG_NOWAYOUT +#define CONFIG_WATCHDOG_NOWAYOUT 0 +#endif + +static const char name[] = "scx200_watchdog"; + +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; + +#define WDTO 0x00 /* Time-Out Register */ +#define WDCNFG 0x02 /* Configuration Register */ +#define W_ENABLE 0x00fa /* Enable watchdog */ +#define W_DISABLE 0x0000 /* Disable watchdog */ +#define WDSTS 0x04 /* Status Register */ +#define WDOVF (1<<0) /* Overflow */ + +#define W_SCALE (32768/1024) /* This depends on the value of W_ENABLE */ + +static void scx200_watchdog_ping(void) +{ + outw(wdto_restart, scx200_config_block + WDTO); +} + +static void scx200_watchdog_update_margin(void) +{ + printk(KERN_INFO "%s: timer margin %d seconds\n", name, margin); + wdto_restart = margin * W_SCALE; +} + +static void scx200_watchdog_enable(void) +{ + printk(KERN_DEBUG "%s: enabling watchdog timer, wdto_restart = %d\n", + name, wdto_restart); + + outw(0, scx200_config_block + WDTO); + outb(WDOVF, scx200_config_block + WDSTS); + outw(W_ENABLE, scx200_config_block + WDCNFG); + + scx200_watchdog_ping(); +} + +static void scx200_watchdog_disable(void) +{ + printk(KERN_DEBUG "%s: disabling watchdog timer\n", name); + + outw(0, scx200_config_block + WDTO); + outb(WDOVF, scx200_config_block + WDSTS); + outw(W_DISABLE, scx200_config_block + WDCNFG); +} + +static int scx200_watchdog_open(struct inode *inode, struct file *file) +{ + /* only allow one at a time */ + if (down_trylock(&open_semaphore)) + return -EBUSY; + scx200_watchdog_enable(); + expect_close = 0; + + return 0; +} + +static int scx200_watchdog_release(struct inode *inode, struct file *file) +{ + if (!expect_close) { + printk(KERN_WARNING "%s: watchdog device closed unexpectedly, " + "will not disable the watchdog timer\n", name); + } else if (!nowayout) { + scx200_watchdog_disable(); + } + up(&open_semaphore); + + return 0; +} + +static int scx200_watchdog_notify_sys(struct notifier_block *this, + unsigned long code, void *unused) +{ + if (code == SYS_HALT || code == SYS_POWER_OFF) + if (!nowayout) + scx200_watchdog_disable(); + + return NOTIFY_DONE; +} + +static struct notifier_block scx200_watchdog_notifier = +{ + notifier_call: scx200_watchdog_notify_sys +}; + +static ssize_t scx200_watchdog_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_watchdog_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_watchdog_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + static struct watchdog_info ident = { + identity : "SCx200 Watchdog", + firmware_version : 1, + options : ( +#ifdef WDIOF_SETTIMEOUT + WDIOF_SETTIMEOUT | +#endif + 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: + return put_user(0, (int *)arg); + case WDIOC_KEEPALIVE: + scx200_watchdog_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_watchdog_update_margin(); + scx200_watchdog_ping(); +#ifdef WDIOC_GETTIMEOUT + case WDIOC_GETTIMEOUT: +#endif + return put_user(margin, (int *)arg); + } +} + +static struct file_operations scx200_watchdog_fops = { + owner: THIS_MODULE, + write: scx200_watchdog_write, + ioctl: scx200_watchdog_ioctl, + open: scx200_watchdog_open, + release: scx200_watchdog_release, +}; + +static struct miscdevice scx200_watchdog_miscdev = { + minor: WATCHDOG_MINOR, + name: name, + fops: &scx200_watchdog_fops, +}; + +static int __init scx200_watchdog_init(void) +{ + int r; + + if (scx200_f0_pdev == NULL) { + printk(KERN_ERR "%s: Not a SCx200 CPU\n", name); + return -ENODEV; + } + + scx200_watchdog_update_margin(); + scx200_watchdog_disable(); + + sema_init(&open_semaphore, 1); + + r = misc_register(&scx200_watchdog_miscdev); + if (r) + return r; + + r = register_reboot_notifier(&scx200_watchdog_notifier); + if (r) { + printk(KERN_ERR "%s: unable to register reboot notifier", + name); + misc_deregister(&scx200_watchdog_miscdev); + return r; + } + + return 0; +} + +static void __exit scx200_watchdog_cleanup(void) +{ + unregister_reboot_notifier(&scx200_watchdog_notifier); + misc_deregister(&scx200_watchdog_miscdev); +} + +module_init(scx200_watchdog_init); +module_exit(scx200_watchdog_cleanup); + +/* + Local variables: + compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules" + c-basic-offset: 8 + End: +*/ diff -urN linux-2.4.19-pre2/drivers/i2c/Config.in linux/drivers/i2c/Config.in --- linux-2.4.19-pre2/drivers/i2c/Config.in Mon Feb 25 20:37:57 2002 +++ linux/drivers/i2c/Config.in Sat Mar 2 15:26:17 2002 @@ -13,6 +13,12 @@ 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_ARCH_SCx200 $CONFIG_I2C_ALGOBIT + if [ "$CONFIG_I2C_SCx200" != "n" ]; then + int ' GPIO pin used for SCL' CONFIG_SCx200_I2C_SCL -1 + int ' GPIO pin used for SDA' CONFIG_SCx200_I2C_SDA -1 + fi + dep_tristate ' NatSemi SCx200 ACCESS.bus' CONFIG_SCx200_AB $CONFIG_ARCH_SCx200 fi dep_tristate 'I2C PCF 8584 interfaces' CONFIG_I2C_ALGOPCF $CONFIG_I2C diff -urN linux-2.4.19-pre2/drivers/i2c/Makefile linux/drivers/i2c/Makefile --- linux-2.4.19-pre2/drivers/i2c/Makefile Mon Feb 25 20:37:57 2002 +++ linux/drivers/i2c/Makefile Sat Mar 2 15:26:17 2002 @@ -18,6 +18,8 @@ obj-$(CONFIG_ITE_I2C_ALGO) += i2c-algo-ite.o obj-$(CONFIG_ITE_I2C_ADAP) += i2c-adap-ite.o obj-$(CONFIG_I2C_PROC) += i2c-proc.o +obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o +obj-$(CONFIG_SCx200_AB) += scx200_ab.o obj-$(CONFIG_I2C_KEYWEST) += i2c-keywest.o # This is needed for automatic patch generation: sensors code starts here diff -urN linux-2.4.19-pre2/drivers/i2c/scx200_ab.c linux/drivers/i2c/scx200_ab.c --- linux-2.4.19-pre2/drivers/i2c/scx200_ab.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/i2c/scx200_ab.c Sat Mar 2 15:26:17 2002 @@ -0,0 +1,583 @@ +/* linux/drivers/i2c/scx200_ab.c + + Copyright (c) 2001,2002 Christer Weinigel + + National Semiconductor SCx200 ACCESS.bus support + + Based on i2c-keywest.c which is: + Copyright (c) 2001 Benjamin Herrenschmidt + Copyright (c) 2000 Philip Edelbrock + + 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 +#include +#include +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Christer Weinigel "); +MODULE_DESCRIPTION("NatSemi SCx200 ACCESS.buss"); +MODULE_LICENSE("GPL"); + +#define MAX_DEVICES 4 +static int base[MAX_DEVICES] = { 0x840 }; +MODULE_PARM(base, "1-4i"); +MODULE_PARM_DESC(base, "Base addresses for the ACCESS.bus controllers"); + +static const char name[] = "scx200_ab"; + +#define DEBUG 0 + +#if DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) +#endif + +#define POLLED_MODE 1 +#define POLL_TIMEOUT (HZ) + +enum scx200_ab_state { + state_idle, + state_address, + state_command, + state_repeat_start, + state_quick, + state_read, + state_write, +}; + +static const char *scx200_ab_state_name[] = { + "idle", + "address", + "command", + "repeat_start", + "quick", + "read", + "write", +}; + +/* Physical interface */ +struct scx200_ab_iface +{ + struct scx200_ab_iface *next; + struct i2c_adapter adapter; + unsigned base; + struct semaphore sem; + + /* State machine data */ + enum scx200_ab_state state; + int result; + u8 address_byte; + u8 command; + u8 *ptr; + char needs_reset; + unsigned len; +}; + +#define ACBSDA (iface->base + 0) +#define ACBST (iface->base + 1) +#define ACBST_SDAST 0x40 /* SDA Status */ +#define ACBST_BER 0x20 +#define ACBST_NEGACK 0x10 /* Negative Acknowledge */ +#define ACBST_STASTR 0x08 /* Stall After Start */ +#define ACBST_MASTER 0x02 +#define ACBCST (iface->base + 2) +#define ACBCST_BB 0x02 +#define ACBCTL1 (iface->base + 3) +#define ACBCTL1_STASTRE 0x80 +#define ACBCTL1_NMINTE 0x40 +#define ACBCTL1_ACK 0x10 +#define ACBCTL1_STOP 0x02 +#define ACBCTL1_START 0x01 +#define ACBADDR (iface->base + 4) +#define ACBCTL2 (iface->base + 5) +#define ACBCTL2_ENABLE 0x01 + +/************************************************************************/ + +static void scx200_ab_machine(struct scx200_ab_iface *iface, u8 status) +{ + const char *errmsg; + + DBG("%s: state %s, status = 0x%02x\n", iface->adapter.name, + scx200_ab_state_name[iface->state], status); + + if (status & ACBST_BER) { + errmsg = "bus error"; + goto error; + } + if (!(status & ACBST_MASTER)) { + errmsg = "not master"; + goto error; + } + if (status & ACBST_NEGACK) + goto negack; + + switch (iface->state) { + case state_idle: + printk(KERN_WARNING "%s: interrupt in idle state\n", + iface->adapter.name); + break; + + case state_address: + /* Do a pointer write first */ + outb(iface->address_byte & ~1, ACBSDA); + + iface->state = state_command; + break; + + case state_command: + outb(iface->command, ACBSDA); + + if (iface->address_byte & 1) + iface->state = state_repeat_start; + else + iface->state = state_write; + break; + + case state_repeat_start: + outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1); + /* fallthrough */ + + case state_quick: + if (iface->address_byte & 1) { + if (iface->len == 1) + outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1); + else + outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1); + outb(iface->address_byte, ACBSDA); + + iface->state = state_read; + } else { + outb(iface->address_byte, ACBSDA); + + iface->state = state_write; + } + break; + + case state_read: + /* Set ACK if receiving the last byte */ + if (iface->len == 1) + outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1); + else + outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1); + + *iface->ptr++ = inb(ACBSDA); + --iface->len; + + if (iface->len == 0) { + iface->result = 0; + iface->state = state_idle; + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + } + + break; + + case state_write: + if (iface->len == 0) { + iface->result = 0; + iface->state = state_idle; + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + break; + } + + outb(*iface->ptr++, ACBSDA); + --iface->len; + + break; + } + + return; + + negack: + DBG("%s: negative acknowledge in state %s\n", + iface->adapter.name, scx200_ab_state_name[iface->state]); + + iface->state = state_idle; + iface->result = -ENXIO; + + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + outb(ACBST_STASTR | ACBST_NEGACK, ACBST); + return; + + error: + printk(KERN_ERR "%s: %s in state %s\n", iface->adapter.name, + errmsg, scx200_ab_state_name[iface->state]); + + iface->state = state_idle; + iface->result = -EIO; + iface->needs_reset = 1; +} + +static void scx200_ab_timeout(struct scx200_ab_iface *iface) +{ + printk(KERN_ERR "%s: timeout in state %s\n", iface->adapter.name, + scx200_ab_state_name[iface->state]); + + iface->state = state_idle; + iface->result = -EIO; + iface->needs_reset = 1; +} + +#ifdef POLLED_MODE +static void scx200_ab_poll(struct scx200_ab_iface *iface) +{ + u8 status = 0; + unsigned long timeout; + + timeout = jiffies + POLL_TIMEOUT; + while (time_before(jiffies, timeout)) { + status = inb(ACBST); + if ((status & (ACBST_SDAST|ACBST_BER|ACBST_NEGACK)) != 0) { + scx200_ab_machine(iface, status); + return; + } + current->policy |= SCHED_YIELD; + schedule(); + } + + scx200_ab_timeout(iface); +} +#endif /* POLLED_MODE */ + +static void scx200_ab_reset(struct scx200_ab_iface *iface) +{ + /* Disable the ACCESS.bus device and Configure the SCL + frequency: 16 clock cycles */ + outb(0x70, ACBCTL2); + /* Polling mode */ + outb(0, ACBCTL1); + /* Disable slave address */ + outb(0, ACBADDR); + /* Enable the ACCESS.bus device */ + outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2); + /* Free STALL after START */ + outb(inb(ACBCTL1) & ~(ACBCTL1_STASTRE | ACBCTL1_NMINTE), ACBCTL1); + /* Send a STOP */ + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + /* Clear BER, NEGACK and STASTR bits */ + outb(ACBST_BER | ACBST_NEGACK | ACBST_STASTR, ACBST); + /* Clear BB bit */ + outb(inb(ACBCST) | ACBCST_BB, ACBCST); +} + +static s32 scx200_ab_smbus_xfer(struct i2c_adapter *adapter, + u16 address, unsigned short flags, + char rw, u8 command, int size, + union i2c_smbus_data *data) +{ + struct scx200_ab_iface *iface = adapter->data; + int len; + u8 *buffer; + u16 cur_word; + int rc; + + switch (size) { + case I2C_SMBUS_QUICK: + len = 0; + buffer = NULL; + break; + case I2C_SMBUS_BYTE: + if (rw == I2C_SMBUS_READ) { + len = 1; + buffer = &data->byte; + } else { + len = 1; + buffer = &command; + } + break; + case I2C_SMBUS_BYTE_DATA: + len = 1; + buffer = &data->byte; + break; + case I2C_SMBUS_WORD_DATA: + len = 2; + cur_word = cpu_to_le16(data->word); + buffer = (u8 *)&cur_word; + break; + case I2C_SMBUS_BLOCK_DATA: + len = data->block[0]; + buffer = &data->block[1]; + break; + default: + return -EINVAL; + } + + DBG("%s: size=%d, address=0x%x, command=0x%x, len=%d, read=%d\n", + adapter->name, size, address, command, len, rw == I2C_SMBUS_READ); + + if (!len && rw == I2C_SMBUS_READ) { + printk(KERN_WARNING "%s: zero length read\n", + adapter->name); + return -EINVAL; + } + + if (len && !buffer) { + printk(KERN_WARNING "%s: nonzero length but no buffer\n", + adapter->name); + return -EFAULT; + } + + down(&iface->sem); + + iface->address_byte = address<<1; + if (rw == I2C_SMBUS_READ) + iface->address_byte |= 1; + iface->command = command; + iface->ptr = buffer; + iface->len = len; + iface->result = -EINVAL; + iface->needs_reset = 0; + + outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1); + + if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE) + iface->state = state_quick; + else + iface->state = state_address; + +#ifdef POLLED_MODE + while (iface->state != state_idle) + scx200_ab_poll(iface); +#else /* POLLED_MODE */ +#error Not implemented +#endif /* POLLED_MODE */ + + if (iface->needs_reset) + scx200_ab_reset(iface); + + rc = iface->result; + + up(&iface->sem); + + if (rc == 0 && size == I2C_SMBUS_WORD_DATA && rw == I2C_SMBUS_READ) + data->word = le16_to_cpu(cur_word); + +#if DEBUG + printk(KERN_DEBUG "%s: transfer done, result: %d", adapter->name, rc); + if (buffer) { + int i; + printk(" data:"); + for (i = 0; i < len; ++i) + printk(" %02x", buffer[i]); + printk("\n"); + } +#endif + DBG("\n"); + + return rc; +} + +static u32 scx200_ab_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static int scx200_ab_reg(struct i2c_client *client) +{ + return 0; +} + +static int scx200_ab_unreg(struct i2c_client *client) +{ + return 0; +} + +static void scx200_ab_inc_use(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +static void scx200_ab_dec_use(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +/* For now, we only handle combined mode (smbus) */ +static struct i2c_algorithm scx200_ab_algorithm = { + name: "SCx200 ACCESS.bus", + id: I2C_ALGO_SMBUS, + smbus_xfer: scx200_ab_smbus_xfer, + functionality: scx200_ab_func, +}; + +struct scx200_ab_iface *scx200_ab_list; + +int scx200_ab_probe(struct scx200_ab_iface *iface) +{ + u8 val; + + /* Disable the ACCESS.bus device and Configure the SCL + frequency: 16 clock cycles */ + outb(0x70, ACBCTL2); + + if (inb(ACBCTL2) != 0x70) { + DBG("%s: ACBCTL2 readback failed\n", iface->adapter.name); + return -ENXIO; + } + + outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1); + + val = inb(ACBCTL1); + if (val) { + DBG("%s: disabled, but ACBCTL1=0x%02x\n", + iface->adapter.name, val); + return -ENXIO; + } + + outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2); + + outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1); + + val = inb(ACBCTL1); + if ((val & ACBCTL1_NMINTE) != ACBCTL1_NMINTE) { + DBG("%s: enabled, but NMINTE won't be set, ACBCTL1=0x%02x\n", + iface->adapter.name, val); + return -ENXIO; + } + + return 0; +} + +void scx200_ab_destroy(struct scx200_ab_iface *iface) +{ + if (iface) { + if (iface->base) + release_region(iface->base, 8); + kfree(iface); + } +} + +int scx200_ab_create(int base, int index) +{ + struct scx200_ab_iface *iface; + struct i2c_adapter *adapter; + int rc = 0; + + iface = kmalloc(sizeof(*iface), GFP_KERNEL); + if (!iface) { + printk(KERN_ERR "%s: can't allocate memory\n", name); + rc = -ENOMEM; + goto errout; + } + + memset(iface, 0, sizeof(*iface)); + adapter = &iface->adapter; + adapter->data = iface; + sprintf(adapter->name, "SCx200_ACB%d", index); + adapter->id = I2C_ALGO_SMBUS; + adapter->algo = &scx200_ab_algorithm; + adapter->inc_use = scx200_ab_inc_use; + adapter->dec_use = scx200_ab_dec_use; + adapter->client_register = scx200_ab_reg; + adapter->client_unregister = scx200_ab_unreg; + + printk(KERN_INFO "%s: initializing\n", adapter->name); + + init_MUTEX(&iface->sem); + + if (request_region(base, 8, adapter->name) == 0) { + printk(KERN_ERR "%s: can't allocate io 0x%x-0x%x\n", + adapter->name, base, base + 8-1); + rc = -EBUSY; + goto errout; + } + iface->base = base; + + rc = scx200_ab_probe(iface); + if (rc) { + printk(KERN_WARNING "%s: probe failed\n", adapter->name); + goto errout; + } + + scx200_ab_reset(iface); + + if (i2c_add_adapter(adapter) < 0) { + printk(KERN_ERR "%s: failed to register %s\n", + adapter->name, adapter->name); + rc = -ENODEV; + goto errout; + } + + lock_kernel(); + iface->next = scx200_ab_list; + scx200_ab_list = iface; + unlock_kernel(); + + return 0; + + errout: + scx200_ab_destroy(iface); + return rc; +} + +int scx200_ab_init(void) +{ + int i; + int rc; + + if (pci_find_device(PCI_VENDOR_ID_NS, + PCI_DEVICE_ID_NS_SCx200_BRIDGE, + NULL) == NULL) { + printk(KERN_ERR "%s: Not a SCx200 CPU\n", name); + return -ENODEV; + } + + rc = -ENXIO; + for (i = 0; i < MAX_DEVICES; ++i) { + if (base[i] > 0) + rc = scx200_ab_create(base[i], i); + } + if (scx200_ab_list) + return 0; + return rc; +} + +void scx200_ab_cleanup(void) +{ + struct scx200_ab_iface *iface; + lock_kernel(); + while ((iface = scx200_ab_list) != NULL) { + scx200_ab_list = iface->next; + unlock_kernel(); + + i2c_del_adapter(&iface->adapter); + scx200_ab_destroy(iface); + + lock_kernel(); + } + unlock_kernel(); +} + +module_init(scx200_ab_init); +module_exit(scx200_ab_cleanup); + +/* + Local variables: + compile-command: "make -k -C ../.. SUBDIRS=drivers/i2c modules" + c-basic-offset: 8 + End: +*/ + diff -urN linux-2.4.19-pre2/drivers/i2c/scx200_i2c.c linux/drivers/i2c/scx200_i2c.c --- linux-2.4.19-pre2/drivers/i2c/scx200_i2c.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/i2c/scx200_i2c.c Sat Mar 2 15:26:17 2002 @@ -0,0 +1,153 @@ +/* linux/drivers/i2c/scx200_i2c.c + + Copyright (c) 2001,2002 Christer Weinigel + + 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 +#include +#include +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Christer Weinigel "); +MODULE_DESCRIPTION("NatSemi SCx200 I2C using GPIO pins"); +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 const char name[] = "scx200_i2c"; + +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: "SCx200 GPIO 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) +{ + if (scx200_f0_pdev == NULL) { + printk(KERN_ERR "%s: Not a SCx200 CPU\n", name); + return -ENODEV; + } + + printk(KERN_INFO "%s: SCL=GPIO%02u, SDA=GPIO%02u\n", + name, scl, sda); + + if (scl == -1 || sda == -1 || scl == sda) { + printk(KERN_ERR "%s: scl and sda must be specified\n", name); + 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 "%s: Adapter %s registration failed\n", + name, 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: +*/ diff -urN linux-2.4.19-pre2/drivers/mtd/maps/Config.in linux/drivers/mtd/maps/Config.in --- linux-2.4.19-pre2/drivers/mtd/maps/Config.in Sat Mar 2 14:46:44 2002 +++ linux/drivers/mtd/maps/Config.in Sat Mar 2 15:26:25 2002 @@ -27,6 +27,7 @@ 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 ' BIOS flash chip on Intel L440GX boards' CONFIG_MTD_L440GX $CONFIG_I386 $CONFIG_MTD_JEDEC + dep_tristate ' Flash device mapped with DOCCS on NatSemi SCx200' CONFIG_MTD_SCx200_DOCFLASH $CONFIG_ARCH_SCx200 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS fi if [ "$CONFIG_PPC" = "y" ]; then diff -urN linux-2.4.19-pre2/drivers/mtd/maps/Makefile linux/drivers/mtd/maps/Makefile --- linux-2.4.19-pre2/drivers/mtd/maps/Makefile Sat Mar 2 14:46:44 2002 +++ linux/drivers/mtd/maps/Makefile Sat Mar 2 15:26:25 2002 @@ -31,5 +31,6 @@ obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PB1000) += pb1xxx-flash.o obj-$(CONFIG_MTD_PB1500) += pb1xxx-flash.o +obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o include $(TOPDIR)/Rules.make diff -urN linux-2.4.19-pre2/drivers/mtd/maps/scx200_docflash.c linux/drivers/mtd/maps/scx200_docflash.c --- linux-2.4.19-pre2/drivers/mtd/maps/scx200_docflash.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/mtd/maps/scx200_docflash.c Sat Mar 2 15:26:25 2002 @@ -0,0 +1,177 @@ +/* linux/drivers/mtd/maps/scx200_docflash.c + + Copyright (c) 2001,2002 Christer Weinigel + + National Semiconductor SCx200 flash mapped with DOCCS +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Christer Weinigel "); +MODULE_DESCRIPTION("NatSemi SCx200 DOCCS Flash"); +MODULE_LICENSE("GPL"); + +static int partition = 1; + +static const char name[] = "scx200_docflash"; + +static struct mtd_info *mymtd; + +/* partition_info gives details on the logical partitions that the split the + * single flash device into. If the size if zero we use up to the end of the + * device. */ +static struct mtd_partition partition_info[] = { + { + name: "scx200 Boot kernel", + offset: 0, + size: 0xc0000 + }, + { + name: "scx200 Low BIOS", + offset: 0xc0000, + size: 0x40000 + }, + { + name: "scx200 File system", + offset: 0x100000, + size: ~0 /* calculate from flash size */ + }, + { + name: "scx200 High BIOS", + offset: ~0, /* calculate from flash size */ + size: 0x80000 + }, +}; +#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) + +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: "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 doccs_base; + unsigned doccs_ctrl; + unsigned pmr; + + if (scx200_f0_pdev == NULL) { + printk(KERN_ERR "%s: Not a SCx200 CPU\n", name); + return -ENODEV; + } + + /* read the current configuration for the DOCCS from the + SCx200 and use this to set up our flash mapping. It might + be a better idea to ask the Linux kernel for a memory + region where the flash can be mapped instead of relying on + the BIOS to do it correctly */ + pci_read_config_dword(scx200_f0_pdev, 0x78, &doccs_base); + pci_read_config_dword(scx200_f0_pdev, 0x7c, &doccs_ctrl); + pmr = inl(scx200_config_block + SCx200_PMR); + + scx200_docflash_map.size = ((doccs_ctrl & 0x1fff) << 13) + (1<<13); + + if (pmr & (1<<6)) /* is the flash 16 or 8 bits wide */ + scx200_docflash_map.buswidth = 2; + else + scx200_docflash_map.buswidth = 1; + + printk(KERN_INFO "%s: DOCCS mapped at 0x%x, size 0x%lx, width %d\n", + name, doccs_base, + scx200_docflash_map.size, scx200_docflash_map.buswidth); + + scx200_docflash_map.map_priv_1 = (unsigned long)ioremap(doccs_base, scx200_docflash_map.size); + if (!scx200_docflash_map.map_priv_1) { + printk(KERN_ERR "%s: failed to ioremap the flash\n", name); + return -EIO; + } + + mymtd = do_map_probe("cfi_probe", &scx200_docflash_map); + if (!mymtd) { + printk(KERN_ERR "%s: unable to detect flash\n", name); + iounmap((void *)scx200_docflash_map.map_priv_1); + return -ENXIO; + } + + 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); + } + return 0; +} + +static void __exit cleanup_scx200_docflash(void) +{ + if (mymtd) { + if (partition) + del_mtd_partitions(mymtd); + del_mtd_device(mymtd); + map_destroy(mymtd); + } + if (scx200_docflash_map.map_priv_1) { + iounmap((void *)scx200_docflash_map.map_priv_1); + scx200_docflash_map.map_priv_1 = 0; + } +} + +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: +*/ diff -urN linux-2.4.19-pre2/include/linux/pci_ids.h linux/include/linux/pci_ids.h --- linux-2.4.19-pre2/include/linux/pci_ids.h Sat Mar 2 14:47:39 2002 +++ linux/include/linux/pci_ids.h Sat Mar 2 15:27:42 2002 @@ -287,6 +287,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 diff -urN linux-2.4.19-pre2/include/linux/scx200.h linux/include/linux/scx200.h --- linux-2.4.19-pre2/include/linux/scx200.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/scx200.h Sat Mar 2 15:27:42 2002 @@ -0,0 +1,110 @@ +#include +#include + +/* Configuation block */ +#define SCx200_PMR 0x30 +#define SCx200_MCR 0x34 /* Miscellaneous Configuration Register */ + +/* scx200_main.c */ + +extern struct pci_dev *scx200_f0_pdev; +extern struct pci_dev *scx200_f5_pdev; +extern unsigned scx200_config_block; + +/* GPIO */ + +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 u32 scx200_gpio_shadow[2]; +extern u32 *dummy; + +/* 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 u32 *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<