dect
/
linux-2.6
Archived
13
0
Fork 0

sh: Kill off long-dead HD64465 cchip support.

This code has been dead for many years. The last update it received
was in 2003 in order to update it for the driver model changes, though
it had already been in disarray and unused before that point. The only
boards that ever used this chip have not had users in many years either,
so it is finally safe to just kill it off and move on with life.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
Paul Mundt 2008-10-28 20:07:44 +09:00
parent 5ff0594e2f
commit 3eeebf17f3
15 changed files with 1 additions and 1939 deletions

View File

@ -47,9 +47,7 @@ Next, for companion chips:
`-- sh
`-- cchips
`-- hd6446x
|-- hd64461
| `-- cchip-specific files
`-- hd64465
`-- hd64461
`-- cchip-specific files
... and so on. Headers for the companion chips are treated the same way as

View File

@ -22,20 +22,6 @@ config HD64461
Say Y if you want support for the HD64461.
Otherwise, say N.
config HD64465
bool "Hitachi HD64465 companion chip support"
---help---
The Hitachi HD64465 provides an interface for
the SH7750 CPU, supporting a LCD controller,
CRT color controller, IrDA, USB, PCMCIA,
keyboard controller, and a printer interface.
More information is available at
<http://global.hitachi.com/New/cnews/E/1998/981019B.html>.
Say Y if you want support for the HD64465.
Otherwise, say N.
endchoice
# These will also be split into the Kconfig's below
@ -61,23 +47,4 @@ config HD64461_ENABLER
via the HD64461 companion chip.
Otherwise, say N.
config HD64465_IOBASE
hex "HD64465 start address"
depends on HD64465
default "0xb0000000"
help
The default setting of the HD64465 IO base address is 0xb0000000.
Do not change this unless you know what you are doing.
config HD64465_IRQ
int "HD64465 IRQ"
depends on HD64465
default "5"
help
The default setting of the HD64465 IRQ is 5.
Do not change this unless you know what you are doing.
endmenu

View File

@ -1,4 +1,3 @@
obj-$(CONFIG_HD64461) += hd64461.o
obj-$(CONFIG_HD64465) += hd64465/
EXTRA_CFLAGS += -Werror

View File

@ -1,6 +0,0 @@
#
# Makefile for the HD64465
#
obj-y := setup.o io.o gpio.o

View File

@ -1,196 +0,0 @@
/*
* $Id: gpio.c,v 1.4 2003/05/19 22:24:18 lethal Exp $
* by Greg Banks <gbanks@pocketpenguins.com>
* (c) 2000 PocketPenguins Inc
*
* GPIO pin support for HD64465 companion chip.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/hd64465/gpio.h>
#define _PORTOF(portpin) (((portpin)>>3)&0x7)
#define _PINOF(portpin) ((portpin)&0x7)
/* Register addresses parametrised on port */
#define GPIO_CR(port) (HD64465_REG_GPACR+((port)<<1))
#define GPIO_DR(port) (HD64465_REG_GPADR+((port)<<1))
#define GPIO_ICR(port) (HD64465_REG_GPAICR+((port)<<1))
#define GPIO_ISR(port) (HD64465_REG_GPAISR+((port)<<1))
#define GPIO_NPORTS 5
#define MODNAME "hd64465_gpio"
EXPORT_SYMBOL(hd64465_gpio_configure);
EXPORT_SYMBOL(hd64465_gpio_get_pin);
EXPORT_SYMBOL(hd64465_gpio_get_port);
EXPORT_SYMBOL(hd64465_gpio_register_irq);
EXPORT_SYMBOL(hd64465_gpio_set_pin);
EXPORT_SYMBOL(hd64465_gpio_set_port);
EXPORT_SYMBOL(hd64465_gpio_unregister_irq);
/* TODO: each port should be protected with a spinlock */
void hd64465_gpio_configure(int portpin, int direction)
{
unsigned short cr;
unsigned int shift = (_PINOF(portpin)<<1);
cr = inw(GPIO_CR(_PORTOF(portpin)));
cr &= ~(3<<shift);
cr |= direction<<shift;
outw(cr, GPIO_CR(_PORTOF(portpin)));
}
void hd64465_gpio_set_pin(int portpin, unsigned int value)
{
unsigned short d;
unsigned short mask = 1<<(_PINOF(portpin));
d = inw(GPIO_DR(_PORTOF(portpin)));
if (value)
d |= mask;
else
d &= ~mask;
outw(d, GPIO_DR(_PORTOF(portpin)));
}
unsigned int hd64465_gpio_get_pin(int portpin)
{
return inw(GPIO_DR(_PORTOF(portpin))) & (1<<(_PINOF(portpin)));
}
/* TODO: for cleaner atomicity semantics, add a mask to this routine */
void hd64465_gpio_set_port(int port, unsigned int value)
{
outw(value, GPIO_DR(port));
}
unsigned int hd64465_gpio_get_port(int port)
{
return inw(GPIO_DR(port));
}
static struct {
void (*func)(int portpin, void *dev);
void *dev;
} handlers[GPIO_NPORTS * 8];
static irqreturn_t hd64465_gpio_interrupt(int irq, void *dev)
{
unsigned short port, pin, isr, mask, portpin;
for (port=0 ; port<GPIO_NPORTS ; port++) {
isr = inw(GPIO_ISR(port));
for (pin=0 ; pin<8 ; pin++) {
mask = 1<<pin;
if (isr & mask) {
portpin = (port<<3)|pin;
if (handlers[portpin].func != 0)
handlers[portpin].func(portpin, handlers[portpin].dev);
else
printk(KERN_NOTICE "unexpected GPIO interrupt, pin %c%d\n",
port+'A', (int)pin);
}
}
/* Write 1s back to ISR to clear it? That's what the manual says.. */
outw(isr, GPIO_ISR(port));
}
return IRQ_HANDLED;
}
void hd64465_gpio_register_irq(int portpin, int mode,
void (*handler)(int portpin, void *dev), void *dev)
{
unsigned long flags;
unsigned short icr, mask;
if (handler == 0)
return;
local_irq_save(flags);
handlers[portpin].func = handler;
handlers[portpin].dev = dev;
/*
* Configure Interrupt Control Register
*/
icr = inw(GPIO_ICR(_PORTOF(portpin)));
mask = (1<<_PINOF(portpin));
/* unmask interrupt */
icr &= ~mask;
/* set TS bit */
mask <<= 8;
icr &= ~mask;
if (mode == HD64465_GPIO_RISING)
icr |= mask;
outw(icr, GPIO_ICR(_PORTOF(portpin)));
local_irq_restore(flags);
}
void hd64465_gpio_unregister_irq(int portpin)
{
unsigned long flags;
unsigned short icr;
local_irq_save(flags);
/*
* Configure Interrupt Control Register
*/
icr = inw(GPIO_ICR(_PORTOF(portpin)));
icr |= (1<<_PINOF(portpin)); /* mask interrupt */
outw(icr, GPIO_ICR(_PORTOF(portpin)));
handlers[portpin].func = 0;
handlers[portpin].dev = 0;
local_irq_restore(flags);
}
static int __init hd64465_gpio_init(void)
{
if (!request_region(HD64465_REG_GPACR, 0x1000, MODNAME))
return -EBUSY;
if (request_irq(HD64465_IRQ_GPIO, hd64465_gpio_interrupt,
IRQF_DISABLED, MODNAME, 0))
goto out_irqfailed;
printk("HD64465 GPIO layer on irq %d\n", HD64465_IRQ_GPIO);
return 0;
out_irqfailed:
release_region(HD64465_REG_GPACR, 0x1000);
return -EINVAL;
}
static void __exit hd64465_gpio_exit(void)
{
release_region(HD64465_REG_GPACR, 0x1000);
free_irq(HD64465_IRQ_GPIO, 0);
}
module_init(hd64465_gpio_init);
module_exit(hd64465_gpio_exit);
MODULE_LICENSE("GPL");

View File

@ -1,211 +0,0 @@
/*
* $Id: io.c,v 1.4 2003/08/03 03:05:10 lethal Exp $
* by Greg Banks <gbanks@pocketpenguins.com>
* (c) 2000 PocketPenguins Inc
*
* Derived from io_hd64461.c, which bore the message:
* Copyright (C) 2000 YAEGASHI Takeshi
*
* Typical I/O routines for HD64465 system.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/io.h>
#include <asm/hd64465/hd64465.h>
#define HD64465_DEBUG 0
#if HD64465_DEBUG
#define DPRINTK(args...) printk(args)
#define DIPRINTK(n, args...) if (hd64465_io_debug>(n)) printk(args)
#else
#define DPRINTK(args...)
#define DIPRINTK(n, args...)
#endif
/* This is a hack suitable only for debugging IO port problems */
int hd64465_io_debug;
EXPORT_SYMBOL(hd64465_io_debug);
/* Low iomap maps port 0-1K to addresses in 8byte chunks */
#define HD64465_IOMAP_LO_THRESH 0x400
#define HD64465_IOMAP_LO_SHIFT 3
#define HD64465_IOMAP_LO_MASK ((1<<HD64465_IOMAP_LO_SHIFT)-1)
#define HD64465_IOMAP_LO_NMAP (HD64465_IOMAP_LO_THRESH>>HD64465_IOMAP_LO_SHIFT)
static unsigned long hd64465_iomap_lo[HD64465_IOMAP_LO_NMAP];
static unsigned char hd64465_iomap_lo_shift[HD64465_IOMAP_LO_NMAP];
/* High iomap maps port 1K-64K to addresses in 1K chunks */
#define HD64465_IOMAP_HI_THRESH 0x10000
#define HD64465_IOMAP_HI_SHIFT 10
#define HD64465_IOMAP_HI_MASK ((1<<HD64465_IOMAP_HI_SHIFT)-1)
#define HD64465_IOMAP_HI_NMAP (HD64465_IOMAP_HI_THRESH>>HD64465_IOMAP_HI_SHIFT)
static unsigned long hd64465_iomap_hi[HD64465_IOMAP_HI_NMAP];
static unsigned char hd64465_iomap_hi_shift[HD64465_IOMAP_HI_NMAP];
#define PORT2ADDR(x) (sh_mv.mv_isa_port2addr(x))
void hd64465_port_map(unsigned short baseport, unsigned int nports,
unsigned long addr, unsigned char shift)
{
unsigned int port, endport = baseport + nports;
DPRINTK("hd64465_port_map(base=0x%04hx, n=0x%04hx, addr=0x%08lx,endport=0x%04x)\n",
baseport, nports, addr,endport);
for (port = baseport ;
port < endport && port < HD64465_IOMAP_LO_THRESH ;
port += (1<<HD64465_IOMAP_LO_SHIFT)) {
DPRINTK(" maplo[0x%x] = 0x%08lx\n", port, addr);
hd64465_iomap_lo[port>>HD64465_IOMAP_LO_SHIFT] = addr;
hd64465_iomap_lo_shift[port>>HD64465_IOMAP_LO_SHIFT] = shift;
addr += (1<<(HD64465_IOMAP_LO_SHIFT));
}
for (port = max_t(unsigned int, baseport, HD64465_IOMAP_LO_THRESH);
port < endport && port < HD64465_IOMAP_HI_THRESH ;
port += (1<<HD64465_IOMAP_HI_SHIFT)) {
DPRINTK(" maphi[0x%x] = 0x%08lx\n", port, addr);
hd64465_iomap_hi[port>>HD64465_IOMAP_HI_SHIFT] = addr;
hd64465_iomap_hi_shift[port>>HD64465_IOMAP_HI_SHIFT] = shift;
addr += (1<<(HD64465_IOMAP_HI_SHIFT));
}
}
EXPORT_SYMBOL(hd64465_port_map);
void hd64465_port_unmap(unsigned short baseport, unsigned int nports)
{
unsigned int port, endport = baseport + nports;
DPRINTK("hd64465_port_unmap(base=0x%04hx, n=0x%04hx)\n",
baseport, nports);
for (port = baseport ;
port < endport && port < HD64465_IOMAP_LO_THRESH ;
port += (1<<HD64465_IOMAP_LO_SHIFT)) {
hd64465_iomap_lo[port>>HD64465_IOMAP_LO_SHIFT] = 0;
}
for (port = max_t(unsigned int, baseport, HD64465_IOMAP_LO_THRESH);
port < endport && port < HD64465_IOMAP_HI_THRESH ;
port += (1<<HD64465_IOMAP_HI_SHIFT)) {
hd64465_iomap_hi[port>>HD64465_IOMAP_HI_SHIFT] = 0;
}
}
EXPORT_SYMBOL(hd64465_port_unmap);
unsigned long hd64465_isa_port2addr(unsigned long port)
{
unsigned long addr = 0;
unsigned char shift;
/* handle remapping of low IO ports */
if (port < HD64465_IOMAP_LO_THRESH) {
addr = hd64465_iomap_lo[port >> HD64465_IOMAP_LO_SHIFT];
shift = hd64465_iomap_lo_shift[port >> HD64465_IOMAP_LO_SHIFT];
if (addr != 0)
addr += (port & HD64465_IOMAP_LO_MASK) << shift;
else
printk(KERN_NOTICE "io_hd64465: access to un-mapped port %lx\n", port);
} else if (port < HD64465_IOMAP_HI_THRESH) {
addr = hd64465_iomap_hi[port >> HD64465_IOMAP_HI_SHIFT];
shift = hd64465_iomap_hi_shift[port >> HD64465_IOMAP_HI_SHIFT];
if (addr != 0)
addr += (port & HD64465_IOMAP_HI_MASK) << shift;
else
printk(KERN_NOTICE "io_hd64465: access to un-mapped port %lx\n", port);
}
/* HD64465 internal devices (0xb0000000) */
else if (port < 0x20000)
addr = CONFIG_HD64465_IOBASE + port - 0x10000;
/* Whole physical address space (0xa0000000) */
else
addr = P2SEGADDR(port);
DIPRINTK(2, "PORT2ADDR(0x%08lx) = 0x%08lx\n", port, addr);
return addr;
}
static inline void delay(void)
{
ctrl_inw(0xa0000000);
}
unsigned char hd64465_inb(unsigned long port)
{
unsigned long addr = PORT2ADDR(port);
unsigned long b = (addr == 0 ? 0 : *(volatile unsigned char*)addr);
DIPRINTK(0, "inb(%08lx) = %02x\n", addr, (unsigned)b);
return b;
}
unsigned char hd64465_inb_p(unsigned long port)
{
unsigned long v;
unsigned long addr = PORT2ADDR(port);
v = (addr == 0 ? 0 : *(volatile unsigned char*)addr);
delay();
DIPRINTK(0, "inb_p(%08lx) = %02x\n", addr, (unsigned)v);
return v;
}
unsigned short hd64465_inw(unsigned long port)
{
unsigned long addr = PORT2ADDR(port);
unsigned long b = (addr == 0 ? 0 : *(volatile unsigned short*)addr);
DIPRINTK(0, "inw(%08lx) = %04lx\n", addr, b);
return b;
}
unsigned int hd64465_inl(unsigned long port)
{
unsigned long addr = PORT2ADDR(port);
unsigned int b = (addr == 0 ? 0 : *(volatile unsigned long*)addr);
DIPRINTK(0, "inl(%08lx) = %08x\n", addr, b);
return b;
}
void hd64465_outb(unsigned char b, unsigned long port)
{
unsigned long addr = PORT2ADDR(port);
DIPRINTK(0, "outb(%02x, %08lx)\n", (unsigned)b, addr);
if (addr != 0)
*(volatile unsigned char*)addr = b;
}
void hd64465_outb_p(unsigned char b, unsigned long port)
{
unsigned long addr = PORT2ADDR(port);
DIPRINTK(0, "outb_p(%02x, %08lx)\n", (unsigned)b, addr);
if (addr != 0)
*(volatile unsigned char*)addr = b;
delay();
}
void hd64465_outw(unsigned short b, unsigned long port)
{
unsigned long addr = PORT2ADDR(port);
DIPRINTK(0, "outw(%04x, %08lx)\n", (unsigned)b, addr);
if (addr != 0)
*(volatile unsigned short*)addr = b;
}
void hd64465_outl(unsigned int b, unsigned long port)
{
unsigned long addr = PORT2ADDR(port);
DIPRINTK(0, "outl(%08x, %08lx)\n", b, addr);
if (addr != 0)
*(volatile unsigned long*)addr = b;
}

View File

@ -1,181 +0,0 @@
/*
* $Id: setup.c,v 1.4 2003/08/03 03:05:10 lethal Exp $
*
* Setup and IRQ handling code for the HD64465 companion chip.
* by Greg Banks <gbanks@pocketpenguins.com>
* Copyright (c) 2000 PocketPenguins Inc
*
* Derived from setup_hd64461.c which bore the message:
* Copyright (C) 2000 YAEGASHI Takeshi
*/
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hd64465/hd64465.h>
static void disable_hd64465_irq(unsigned int irq)
{
unsigned short nimr;
unsigned short mask = 1 << (irq - HD64465_IRQ_BASE);
pr_debug("disable_hd64465_irq(%d): mask=%x\n", irq, mask);
nimr = inw(HD64465_REG_NIMR);
nimr |= mask;
outw(nimr, HD64465_REG_NIMR);
}
static void enable_hd64465_irq(unsigned int irq)
{
unsigned short nimr;
unsigned short mask = 1 << (irq - HD64465_IRQ_BASE);
pr_debug("enable_hd64465_irq(%d): mask=%x\n", irq, mask);
nimr = inw(HD64465_REG_NIMR);
nimr &= ~mask;
outw(nimr, HD64465_REG_NIMR);
}
static void mask_and_ack_hd64465(unsigned int irq)
{
disable_hd64465_irq(irq);
}
static void end_hd64465_irq(unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
enable_hd64465_irq(irq);
}
static unsigned int startup_hd64465_irq(unsigned int irq)
{
enable_hd64465_irq(irq);
return 0;
}
static void shutdown_hd64465_irq(unsigned int irq)
{
disable_hd64465_irq(irq);
}
static struct hw_interrupt_type hd64465_irq_type = {
.typename = "HD64465-IRQ",
.startup = startup_hd64465_irq,
.shutdown = shutdown_hd64465_irq,
.enable = enable_hd64465_irq,
.disable = disable_hd64465_irq,
.ack = mask_and_ack_hd64465,
.end = end_hd64465_irq,
};
static irqreturn_t hd64465_interrupt(int irq, void *dev_id)
{
printk(KERN_INFO
"HD64465: spurious interrupt, nirr: 0x%x nimr: 0x%x\n",
inw(HD64465_REG_NIRR), inw(HD64465_REG_NIMR));
return IRQ_NONE;
}
/*
* Support for a secondary IRQ demux step. This is necessary
* because the HD64465 presents a very thin interface to the
* PCMCIA bus; a lot of features (such as remapping interrupts)
* normally done in hardware by other PCMCIA host bridges is
* instead done in software.
*/
static struct {
int (*func)(int, void *);
void *dev;
} hd64465_demux[HD64465_IRQ_NUM];
void hd64465_register_irq_demux(int irq,
int (*demux)(int irq, void *dev), void *dev)
{
hd64465_demux[irq - HD64465_IRQ_BASE].func = demux;
hd64465_demux[irq - HD64465_IRQ_BASE].dev = dev;
}
EXPORT_SYMBOL(hd64465_register_irq_demux);
void hd64465_unregister_irq_demux(int irq)
{
hd64465_demux[irq - HD64465_IRQ_BASE].func = 0;
}
EXPORT_SYMBOL(hd64465_unregister_irq_demux);
int hd64465_irq_demux(int irq)
{
if (irq == CONFIG_HD64465_IRQ) {
unsigned short i, bit;
unsigned short nirr = inw(HD64465_REG_NIRR);
unsigned short nimr = inw(HD64465_REG_NIMR);
pr_debug("hd64465_irq_demux, nirr=%04x, nimr=%04x\n", nirr, nimr);
nirr &= ~nimr;
for (bit = 1, i = 0 ; i < HD64465_IRQ_NUM ; bit <<= 1, i++)
if (nirr & bit)
break;
if (i < HD64465_IRQ_NUM) {
irq = HD64465_IRQ_BASE + i;
if (hd64465_demux[i].func != 0)
irq = hd64465_demux[i].func(irq, hd64465_demux[i].dev);
}
}
return irq;
}
static struct irqaction irq0 = {
.handler = hd64465_interrupt,
.flags = IRQF_DISABLED,
.mask = CPU_MASK_NONE,
.name = "HD64465",
};
static int __init setup_hd64465(void)
{
int i;
unsigned short rev;
unsigned short smscr;
if (!MACH_HD64465)
return 0;
printk(KERN_INFO "HD64465 configured at 0x%x on irq %d(mapped into %d to %d)\n",
CONFIG_HD64465_IOBASE,
CONFIG_HD64465_IRQ,
HD64465_IRQ_BASE,
HD64465_IRQ_BASE+HD64465_IRQ_NUM-1);
if (inw(HD64465_REG_SDID) != HD64465_SDID) {
printk(KERN_ERR "HD64465 device ID not found, check base address\n");
}
rev = inw(HD64465_REG_SRR);
printk(KERN_INFO "HD64465 hardware revision %d.%d\n", (rev >> 8) & 0xff, rev & 0xff);
outw(0xffff, HD64465_REG_NIMR); /* mask all interrupts */
for (i = 0; i < HD64465_IRQ_NUM ; i++) {
irq_desc[HD64465_IRQ_BASE + i].chip = &hd64465_irq_type;
}
setup_irq(CONFIG_HD64465_IRQ, &irq0);
/* wake up the UART from STANDBY at this point */
smscr = inw(HD64465_REG_SMSCR);
outw(smscr & (~HD64465_SMSCR_UARTST), HD64465_REG_SMSCR);
/* remap IO ports for first ISA serial port to HD64465 UART */
hd64465_port_map(0x3f8, 8, CONFIG_HD64465_IOBASE + 0x8000, 1);
return 0;
}
module_init(setup_hd64465);

View File

@ -1,46 +0,0 @@
#ifndef _ASM_SH_HD64465_GPIO_
#define _ASM_SH_HD64465_GPIO_ 1
/*
* $Id: gpio.h,v 1.3 2003/05/04 19:30:14 lethal Exp $
*
* Hitachi HD64465 companion chip: General Purpose IO pins support.
* This layer enables other device drivers to configure GPIO
* pins, get and set their values, and register an interrupt
* routine for when input pins change in hardware.
*
* by Greg Banks <gbanks@pocketpenguins.com>
* (c) 2000 PocketPenguins Inc.
*/
#include <asm/hd64465.h>
/* Macro to construct a portpin number (used in all
* subsequent functions) from a port letter and a pin
* number, e.g. HD64465_GPIO_PORTPIN('A', 5).
*/
#define HD64465_GPIO_PORTPIN(port,pin) (((port)-'A')<<3|(pin))
/* Pin configuration constants for _configure() */
#define HD64465_GPIO_FUNCTION2 0 /* use the pin's *other* function */
#define HD64465_GPIO_OUT 1 /* output */
#define HD64465_GPIO_IN_PULLUP 2 /* input, pull-up MOS on */
#define HD64465_GPIO_IN 3 /* input */
/* Configure a pin's direction */
extern void hd64465_gpio_configure(int portpin, int direction);
/* Get, set value */
extern void hd64465_gpio_set_pin(int portpin, unsigned int value);
extern unsigned int hd64465_gpio_get_pin(int portpin);
extern void hd64465_gpio_set_port(int port, unsigned int value);
extern unsigned int hd64465_gpio_get_port(int port);
/* mode constants for _register_irq() */
#define HD64465_GPIO_FALLING 0
#define HD64465_GPIO_RISING 1
/* Interrupt on external value change */
extern void hd64465_gpio_register_irq(int portpin, int mode,
void (*handler)(int portpin, void *dev), void *dev);
extern void hd64465_gpio_unregister_irq(int portpin);
#endif /* _ASM_SH_HD64465_GPIO_ */

View File

@ -1,256 +0,0 @@
#ifndef _ASM_SH_HD64465_
#define _ASM_SH_HD64465_ 1
/*
* $Id: hd64465.h,v 1.3 2003/05/04 19:30:15 lethal Exp $
*
* Hitachi HD64465 companion chip support
*
* by Greg Banks <gbanks@pocketpenguins.com>
* (c) 2000 PocketPenguins Inc.
*
* Derived from <asm/hd64461.h> which bore the message:
* Copyright (C) 2000 YAEGASHI Takeshi
*/
#include <asm/io.h>
#include <asm/irq.h>
/*
* Note that registers are defined here as virtual port numbers,
* which have no meaning except to get translated by hd64465_isa_port2addr()
* to an address in the range 0xb0000000-0xb3ffffff. Note that
* this translation happens to consist of adding the lower 16 bits
* of the virtual port number to 0xb0000000. Note also that the manual
* shows addresses as absolute physical addresses starting at 0x10000000,
* so e.g. the NIRR register is listed as 0x15000 here, 0x10005000 in the
* manual, and accessed using address 0xb0005000 - Greg.
*/
/* System registers */
#define HD64465_REG_SRR 0x1000c /* System Revision Register */
#define HD64465_REG_SDID 0x10010 /* System Device ID Reg */
#define HD64465_SDID 0x8122 /* 64465 device ID */
/* Power Management registers */
#define HD64465_REG_SMSCR 0x10000 /* System Module Standby Control Reg */
#define HD64465_SMSCR_PS2ST 0x4000 /* PS/2 Standby */
#define HD64465_SMSCR_ADCST 0x1000 /* ADC Standby */
#define HD64465_SMSCR_UARTST 0x0800 /* UART Standby */
#define HD64465_SMSCR_SCDIST 0x0200 /* Serial Codec Standby */
#define HD64465_SMSCR_PPST 0x0100 /* Parallel Port Standby */
#define HD64465_SMSCR_PC0ST 0x0040 /* PCMCIA0 Standby */
#define HD64465_SMSCR_PC1ST 0x0020 /* PCMCIA1 Standby */
#define HD64465_SMSCR_AFEST 0x0010 /* AFE Standby */
#define HD64465_SMSCR_TM0ST 0x0008 /* Timer0 Standby */
#define HD64465_SMSCR_TM1ST 0x0004 /* Timer1 Standby */
#define HD64465_SMSCR_IRDAST 0x0002 /* IRDA Standby */
#define HD64465_SMSCR_KBCST 0x0001 /* Keyboard Controller Standby */
/* Interrupt Controller registers */
#define HD64465_REG_NIRR 0x15000 /* Interrupt Request Register */
#define HD64465_REG_NIMR 0x15002 /* Interrupt Mask Register */
#define HD64465_REG_NITR 0x15004 /* Interrupt Trigger Mode Register */
/* Timer registers */
#define HD64465_REG_TCVR1 0x16000 /* Timer 1 constant value register */
#define HD64465_REG_TCVR0 0x16002 /* Timer 0 constant value register */
#define HD64465_REG_TRVR1 0x16004 /* Timer 1 read value register */
#define HD64465_REG_TRVR0 0x16006 /* Timer 0 read value register */
#define HD64465_REG_TCR1 0x16008 /* Timer 1 control register */
#define HD64465_REG_TCR0 0x1600A /* Timer 0 control register */
#define HD64465_TCR_EADT 0x10 /* Enable ADTRIG# signal */
#define HD64465_TCR_ETMO 0x08 /* Enable TMO signal */
#define HD64465_TCR_PST_MASK 0x06 /* Clock Prescale */
#define HD64465_TCR_PST_1 0x06 /* 1:1 */
#define HD64465_TCR_PST_4 0x04 /* 1:4 */
#define HD64465_TCR_PST_8 0x02 /* 1:8 */
#define HD64465_TCR_PST_16 0x00 /* 1:16 */
#define HD64465_TCR_TSTP 0x01 /* Start/Stop timer */
#define HD64465_REG_TIRR 0x1600C /* Timer interrupt request register */
#define HD64465_REG_TIDR 0x1600E /* Timer interrupt disable register */
#define HD64465_REG_PWM1CS 0x16010 /* PWM 1 clock scale register */
#define HD64465_REG_PWM1LPC 0x16012 /* PWM 1 low pulse width counter register */
#define HD64465_REG_PWM1HPC 0x16014 /* PWM 1 high pulse width counter register */
#define HD64465_REG_PWM0CS 0x16018 /* PWM 0 clock scale register */
#define HD64465_REG_PWM0LPC 0x1601A /* PWM 0 low pulse width counter register */
#define HD64465_REG_PWM0HPC 0x1601C /* PWM 0 high pulse width counter register */
/* Analog/Digital Converter registers */
#define HD64465_REG_ADDRA 0x1E000 /* A/D data register A */
#define HD64465_REG_ADDRB 0x1E002 /* A/D data register B */
#define HD64465_REG_ADDRC 0x1E004 /* A/D data register C */
#define HD64465_REG_ADDRD 0x1E006 /* A/D data register D */
#define HD64465_REG_ADCSR 0x1E008 /* A/D control/status register */
#define HD64465_ADCSR_ADF 0x80 /* A/D End Flag */
#define HD64465_ADCSR_ADST 0x40 /* A/D Start Flag */
#define HD64465_ADCSR_ADIS 0x20 /* A/D Interrupt Status */
#define HD64465_ADCSR_TRGE 0x10 /* A/D Trigger Enable */
#define HD64465_ADCSR_ADIE 0x08 /* A/D Interrupt Enable */
#define HD64465_ADCSR_SCAN 0x04 /* A/D Scan Mode */
#define HD64465_ADCSR_CH_MASK 0x03 /* A/D Channel */
#define HD64465_REG_ADCALCR 0x1E00A /* A/D calibration sample control */
#define HD64465_REG_ADCAL 0x1E00C /* A/D calibration data register */
/* General Purpose I/O ports registers */
#define HD64465_REG_GPACR 0x14000 /* Port A Control Register */
#define HD64465_REG_GPBCR 0x14002 /* Port B Control Register */
#define HD64465_REG_GPCCR 0x14004 /* Port C Control Register */
#define HD64465_REG_GPDCR 0x14006 /* Port D Control Register */
#define HD64465_REG_GPECR 0x14008 /* Port E Control Register */
#define HD64465_REG_GPADR 0x14010 /* Port A Data Register */
#define HD64465_REG_GPBDR 0x14012 /* Port B Data Register */
#define HD64465_REG_GPCDR 0x14014 /* Port C Data Register */
#define HD64465_REG_GPDDR 0x14016 /* Port D Data Register */
#define HD64465_REG_GPEDR 0x14018 /* Port E Data Register */
#define HD64465_REG_GPAICR 0x14020 /* Port A Interrupt Control Register */
#define HD64465_REG_GPBICR 0x14022 /* Port B Interrupt Control Register */
#define HD64465_REG_GPCICR 0x14024 /* Port C Interrupt Control Register */
#define HD64465_REG_GPDICR 0x14026 /* Port D Interrupt Control Register */
#define HD64465_REG_GPEICR 0x14028 /* Port E Interrupt Control Register */
#define HD64465_REG_GPAISR 0x14040 /* Port A Interrupt Status Register */
#define HD64465_REG_GPBISR 0x14042 /* Port B Interrupt Status Register */
#define HD64465_REG_GPCISR 0x14044 /* Port C Interrupt Status Register */
#define HD64465_REG_GPDISR 0x14046 /* Port D Interrupt Status Register */
#define HD64465_REG_GPEISR 0x14048 /* Port E Interrupt Status Register */
/* PCMCIA bridge interface */
#define HD64465_REG_PCC0ISR 0x12000 /* socket 0 interface status */
#define HD64465_PCCISR_PREADY 0x80 /* mem card ready / io card IREQ */
#define HD64465_PCCISR_PIREQ 0x80
#define HD64465_PCCISR_PMWP 0x40 /* mem card write-protected */
#define HD64465_PCCISR_PVS2 0x20 /* voltage select pin 2 */
#define HD64465_PCCISR_PVS1 0x10 /* voltage select pin 1 */
#define HD64465_PCCISR_PCD_MASK 0x0c /* card detect */
#define HD64465_PCCISR_PBVD_MASK 0x03 /* battery voltage */
#define HD64465_PCCISR_PBVD_BATGOOD 0x03 /* battery good */
#define HD64465_PCCISR_PBVD_BATWARN 0x01 /* battery low warning */
#define HD64465_PCCISR_PBVD_BATDEAD1 0x02 /* battery dead */
#define HD64465_PCCISR_PBVD_BATDEAD2 0x00 /* battery dead */
#define HD64465_REG_PCC0GCR 0x12002 /* socket 0 general control */
#define HD64465_PCCGCR_PDRV 0x80 /* output drive */
#define HD64465_PCCGCR_PCCR 0x40 /* PC card reset */
#define HD64465_PCCGCR_PCCT 0x20 /* PC card type, 1=IO&mem, 0=mem */
#define HD64465_PCCGCR_PVCC0 0x10 /* voltage control pin VCC0SEL0 */
#define HD64465_PCCGCR_PMMOD 0x08 /* memory mode */
#define HD64465_PCCGCR_PPA25 0x04 /* pin A25 */
#define HD64465_PCCGCR_PPA24 0x02 /* pin A24 */
#define HD64465_PCCGCR_PREG 0x01 /* ping PCC0REG# */
#define HD64465_REG_PCC0CSCR 0x12004 /* socket 0 card status change */
#define HD64465_PCCCSCR_PSCDI 0x80 /* sw card detect intr */
#define HD64465_PCCCSCR_PSWSEL 0x40 /* power select */
#define HD64465_PCCCSCR_PIREQ 0x20 /* IREQ intr req */
#define HD64465_PCCCSCR_PSC 0x10 /* STSCHG (status change) pin */
#define HD64465_PCCCSCR_PCDC 0x08 /* CD (card detect) change */
#define HD64465_PCCCSCR_PRC 0x04 /* ready change */
#define HD64465_PCCCSCR_PBW 0x02 /* battery warning change */
#define HD64465_PCCCSCR_PBD 0x01 /* battery dead change */
#define HD64465_REG_PCC0CSCIER 0x12006 /* socket 0 card status change interrupt enable */
#define HD64465_PCCCSCIER_PCRE 0x80 /* change reset enable */
#define HD64465_PCCCSCIER_PIREQE_MASK 0x60 /* IREQ enable */
#define HD64465_PCCCSCIER_PIREQE_DISABLED 0x00 /* IREQ disabled */
#define HD64465_PCCCSCIER_PIREQE_LEVEL 0x20 /* IREQ level-triggered */
#define HD64465_PCCCSCIER_PIREQE_FALLING 0x40 /* IREQ falling-edge-trig */
#define HD64465_PCCCSCIER_PIREQE_RISING 0x60 /* IREQ rising-edge-trig */
#define HD64465_PCCCSCIER_PSCE 0x10 /* status change enable */
#define HD64465_PCCCSCIER_PCDE 0x08 /* card detect change enable */
#define HD64465_PCCCSCIER_PRE 0x04 /* ready change enable */
#define HD64465_PCCCSCIER_PBWE 0x02 /* battery warn change enable */
#define HD64465_PCCCSCIER_PBDE 0x01 /* battery dead change enable*/
#define HD64465_REG_PCC0SCR 0x12008 /* socket 0 software control */
#define HD64465_PCCSCR_SHDN 0x10 /* TPS2206 SHutDowN pin */
#define HD64465_PCCSCR_SWP 0x01 /* write protect */
#define HD64465_REG_PCCPSR 0x1200A /* serial power switch control */
#define HD64465_REG_PCC1ISR 0x12010 /* socket 1 interface status */
#define HD64465_REG_PCC1GCR 0x12012 /* socket 1 general control */
#define HD64465_REG_PCC1CSCR 0x12014 /* socket 1 card status change */
#define HD64465_REG_PCC1CSCIER 0x12016 /* socket 1 card status change interrupt enable */
#define HD64465_REG_PCC1SCR 0x12018 /* socket 1 software control */
/* PS/2 Keyboard and mouse controller -- *not* register compatible */
#define HD64465_REG_KBCSR 0x1dc00 /* Keyboard Control/Status reg */
#define HD64465_KBCSR_KBCIE 0x8000 /* KBCK Input Enable */
#define HD64465_KBCSR_KBCOE 0x4000 /* KBCK Output Enable */
#define HD64465_KBCSR_KBDOE 0x2000 /* KB DATA Output Enable */
#define HD64465_KBCSR_KBCD 0x1000 /* KBCK Driven */
#define HD64465_KBCSR_KBDD 0x0800 /* KB DATA Driven */
#define HD64465_KBCSR_KBCS 0x0400 /* KBCK pin Status */
#define HD64465_KBCSR_KBDS 0x0200 /* KB DATA pin Status */
#define HD64465_KBCSR_KBDP 0x0100 /* KB DATA Parity bit */
#define HD64465_KBCSR_KBD_MASK 0x00ff /* KD DATA shift reg */
#define HD64465_REG_KBISR 0x1dc04 /* Keyboard Interrupt Status reg */
#define HD64465_KBISR_KBRDF 0x0001 /* KB Received Data Full */
#define HD64465_REG_MSCSR 0x1dc10 /* Mouse Control/Status reg */
#define HD64465_REG_MSISR 0x1dc14 /* Mouse Interrupt Status reg */
/*
* Logical address at which the HD64465 is mapped. Note that this
* should always be in the P2 segment (uncached and untranslated).
*/
#ifndef CONFIG_HD64465_IOBASE
#define CONFIG_HD64465_IOBASE 0xb0000000
#endif
/*
* The HD64465 multiplexes all its modules' interrupts onto
* this single interrupt.
*/
#ifndef CONFIG_HD64465_IRQ
#define CONFIG_HD64465_IRQ 5
#endif
#define _HD64465_IO_MASK 0xf8000000
#define is_hd64465_addr(addr) \
((addr & _HD64465_IO_MASK) == (CONFIG_HD64465_IOBASE & _HD64465_IO_MASK))
/*
* A range of 16 virtual interrupts generated by
* demuxing the HD64465 muxed interrupt.
*/
#define HD64465_IRQ_BASE OFFCHIP_IRQ_BASE
#define HD64465_IRQ_NUM 16
#define HD64465_IRQ_ADC (HD64465_IRQ_BASE+0)
#define HD64465_IRQ_USB (HD64465_IRQ_BASE+1)
#define HD64465_IRQ_SCDI (HD64465_IRQ_BASE+2)
#define HD64465_IRQ_PARALLEL (HD64465_IRQ_BASE+3)
/* bit 4 is reserved */
#define HD64465_IRQ_UART (HD64465_IRQ_BASE+5)
#define HD64465_IRQ_IRDA (HD64465_IRQ_BASE+6)
#define HD64465_IRQ_PS2MOUSE (HD64465_IRQ_BASE+7)
#define HD64465_IRQ_KBC (HD64465_IRQ_BASE+8)
#define HD64465_IRQ_TIMER1 (HD64465_IRQ_BASE+9)
#define HD64465_IRQ_TIMER0 (HD64465_IRQ_BASE+10)
#define HD64465_IRQ_GPIO (HD64465_IRQ_BASE+11)
#define HD64465_IRQ_AFE (HD64465_IRQ_BASE+12)
#define HD64465_IRQ_PCMCIA1 (HD64465_IRQ_BASE+13)
#define HD64465_IRQ_PCMCIA0 (HD64465_IRQ_BASE+14)
#define HD64465_IRQ_PS2KBD (HD64465_IRQ_BASE+15)
/* Constants for PCMCIA mappings */
#define HD64465_PCC_WINDOW 0x01000000
#define HD64465_PCC0_BASE 0xb8000000 /* area 6 */
#define HD64465_PCC0_ATTR (HD64465_PCC0_BASE)
#define HD64465_PCC0_COMM (HD64465_PCC0_BASE+HD64465_PCC_WINDOW)
#define HD64465_PCC0_IO (HD64465_PCC0_BASE+2*HD64465_PCC_WINDOW)
#define HD64465_PCC1_BASE 0xb4000000 /* area 5 */
#define HD64465_PCC1_ATTR (HD64465_PCC1_BASE)
#define HD64465_PCC1_COMM (HD64465_PCC1_BASE+HD64465_PCC_WINDOW)
#define HD64465_PCC1_IO (HD64465_PCC1_BASE+2*HD64465_PCC_WINDOW)
/*
* Base of USB controller interface (as memory)
*/
#define HD64465_USB_BASE (CONFIG_HD64465_IOBASE+0xb000)
#define HD64465_USB_LEN 0x1000
/*
* Base of embedded SRAM, used for USB controller.
*/
#define HD64465_SRAM_BASE (CONFIG_HD64465_IOBASE+0x9000)
#define HD64465_SRAM_LEN 0x1000
#endif /* _ASM_SH_HD64465_ */

View File

@ -1,44 +0,0 @@
/*
* include/asm-sh/hd64465/io.h
*
* By Greg Banks <gbanks@pocketpenguins.com>
* (c) 2000 PocketPenguins Inc.
*
* Derived from io_hd64461.h, which bore the message:
* Copyright 2000 Stuart Menefy (stuart.menefy@st.com)
*
* May be copied or modified under the terms of the GNU General Public
* License. See linux/COPYING for more information.
*
* IO functions for an HD64465 "Windows CE Intelligent Peripheral Controller".
*/
#ifndef _ASM_SH_IO_HD64465_H
#define _ASM_SH_IO_HD64465_H
extern unsigned char hd64465_inb(unsigned long port);
extern unsigned short hd64465_inw(unsigned long port);
extern unsigned int hd64465_inl(unsigned long port);
extern void hd64465_outb(unsigned char value, unsigned long port);
extern void hd64465_outw(unsigned short value, unsigned long port);
extern void hd64465_outl(unsigned int value, unsigned long port);
extern unsigned char hd64465_inb_p(unsigned long port);
extern void hd64465_outb_p(unsigned char value, unsigned long port);
extern unsigned long hd64465_isa_port2addr(unsigned long offset);
extern int hd64465_irq_demux(int irq);
/* Provision for generic secondary demux step -- used by PCMCIA code */
extern void hd64465_register_irq_demux(int irq,
int (*demux)(int irq, void *dev), void *dev);
extern void hd64465_unregister_irq_demux(int irq);
/* Set this variable to 1 to see port traffic */
extern int hd64465_io_debug;
/* Map a range of ports to a range of kernel virtual memory.
*/
extern void hd64465_port_map(unsigned short baseport, unsigned int nports,
unsigned long addr, unsigned char shift);
extern void hd64465_port_unmap(unsigned short baseport, unsigned int nports);
#endif /* _ASM_SH_IO_HD64465_H */

View File

@ -7,8 +7,6 @@
#ifndef _ASM_SERIAL_H
#define _ASM_SERIAL_H
#include <linux/kernel.h>
/*
* This assumes you have a 1.8432 MHz clock for your UART.
*
@ -18,19 +16,4 @@
*/
#define BASE_BAUD ( 1843200 / 16 )
#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
#ifdef CONFIG_HD64465
#include <asm/hd64465/hd64465.h>
#define SERIAL_PORT_DFNS \
/* UART CLK PORT IRQ FLAGS */ \
{ 0, BASE_BAUD, 0x3F8, HD64465_IRQ_UART, STD_COM_FLAGS } /* ttyS0 */
#else
#define SERIAL_PORT_DFNS
#endif
#endif /* _ASM_SERIAL_H */

View File

@ -13,7 +13,6 @@ RTS7751R2D SH_RTS7751R2D
# List of companion chips / MFDs.
#
HD64461 HD64461
HD64465 HD64465
#
# List of boards.

View File

@ -188,10 +188,6 @@ config PCMCIA_M8XX
This driver is also available as a module called m8xx_pcmcia.
config HD64465_PCMCIA
tristate "HD64465 host bridge support"
depends on HD64465 && PCMCIA
config PCMCIA_AU1X00
tristate "Au1x00 pcmcia support"
depends on SOC_AU1X00 && PCMCIA

View File

@ -22,7 +22,6 @@ obj-$(CONFIG_I82365) += i82365.o
obj-$(CONFIG_I82092) += i82092.o
obj-$(CONFIG_TCIC) += tcic.o
obj-$(CONFIG_PCMCIA_M8XX) += m8xx_pcmcia.o
obj-$(CONFIG_HD64465_PCMCIA) += hd64465_ss.o
obj-$(CONFIG_PCMCIA_SA1100) += sa11xx_core.o sa1100_cs.o
obj-$(CONFIG_PCMCIA_SA1111) += sa11xx_core.o sa1111_cs.o
obj-$(CONFIG_M32R_PCC) += m32r_pcc.o

View File

@ -1,939 +0,0 @@
/*
* Device driver for the PCMCIA controller module of the
* Hitachi HD64465 handheld companion chip.
*
* Note that the HD64465 provides a very thin PCMCIA host bridge
* layer, requiring a lot of the work of supporting cards to be
* performed by the processor. For example: mapping of card
* interrupts to processor IRQs is done by IRQ demuxing software;
* IO and memory mappings are fixed; setting voltages according
* to card Voltage Select pins etc is done in software.
*
* Note also that this driver uses only the simple, fixed,
* 16MB, 16-bit wide mappings to PCMCIA spaces defined by the
* HD64465. Larger mappings, smaller mappings, or mappings of
* different width to the same socket, are all possible only by
* involving the SH7750's MMU, which is considered unnecessary here.
* The downside is that it may be possible for some drivers to
* break because they need or expect 8-bit mappings.
*
* This driver currently supports only the following configuration:
* SH7750 CPU, HD64465, TPS2206 voltage control chip.
*
* by Greg Banks <gbanks@pocketpenguins.com>
* (c) 2000 PocketPenguins Inc
*/
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <asm/errno.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/hd64465/hd64465.h>
#include <asm/hd64465/io.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
#include <pcmcia/ss.h>
#define MODNAME "hd64465_ss"
/* #define HD64465_DEBUG 1 */
#if HD64465_DEBUG
#define DPRINTK(args...) printk(MODNAME ": " args)
#else
#define DPRINTK(args...)
#endif
extern int hd64465_io_debug;
extern void * p3_ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);
extern void p3_iounmap(void *addr);
/*============================================================*/
#define HS_IO_MAP_SIZE (64*1024)
typedef struct hs_socket_t
{
unsigned int number;
u_int irq;
u_long mem_base;
void *io_base;
u_long mem_length;
u_int ctrl_base;
socket_state_t state;
pccard_io_map io_maps[MAX_IO_WIN];
pccard_mem_map mem_maps[MAX_WIN];
struct pcmcia_socket socket;
} hs_socket_t;
#define HS_MAX_SOCKETS 2
static hs_socket_t hs_sockets[HS_MAX_SOCKETS];
#define hs_in(sp, r) inb((sp)->ctrl_base + (r))
#define hs_out(sp, v, r) outb(v, (sp)->ctrl_base + (r))
/* translate a boolean value to a bit in a register */
#define bool_to_regbit(sp, r, bi, bo) \
do { \
unsigned short v = hs_in(sp, r); \
if (bo) \
v |= (bi); \
else \
v &= ~(bi); \
hs_out(sp, v, r); \
} while(0)
/* register offsets from HD64465_REG_PCC[01]ISR */
#define ISR 0x0
#define GCR 0x2
#define CSCR 0x4
#define CSCIER 0x6
#define SCR 0x8
/* Mask and values for CSCIER register */
#define IER_MASK 0x80
#define IER_ON 0x3f /* interrupts on */
#define IER_OFF 0x00 /* interrupts off */
/*============================================================*/
#if HD64465_DEBUG > 10
static void cis_hex_dump(const unsigned char *x, int len)
{
int i;
for (i=0 ; i<len ; i++)
{
if (!(i & 0xf))
printk("\n%08x", (unsigned)(x + i));
printk(" %02x", *(volatile unsigned short*)x);
x += 2;
}
printk("\n");
}
#endif
/*============================================================*/
/*
* This code helps create the illusion that the IREQ line from
* the PC card is mapped to one of the CPU's IRQ lines by the
* host bridge hardware (which is how every host bridge *except*
* the HD64465 works). In particular, it supports enabling
* and disabling the IREQ line by code which knows nothing
* about the host bridge (e.g. device drivers, IDE code) using
* the request_irq(), free_irq(), probe_irq_on() and probe_irq_off()
* functions. Also, it supports sharing the mapped IRQ with
* real hardware IRQs from the -IRL0-3 lines.
*/
#define HS_NUM_MAPPED_IRQS 16 /* Limitation of the PCMCIA code */
static struct
{
/* index is mapped irq number */
hs_socket_t *sock;
hw_irq_controller *old_handler;
} hs_mapped_irq[HS_NUM_MAPPED_IRQS];
static void hs_socket_enable_ireq(hs_socket_t *sp)
{
unsigned short cscier;
DPRINTK("hs_socket_enable_ireq(sock=%d)\n", sp->number);
cscier = hs_in(sp, CSCIER);
cscier &= ~HD64465_PCCCSCIER_PIREQE_MASK;
cscier |= HD64465_PCCCSCIER_PIREQE_LEVEL;
hs_out(sp, cscier, CSCIER);
}
static void hs_socket_disable_ireq(hs_socket_t *sp)
{
unsigned short cscier;
DPRINTK("hs_socket_disable_ireq(sock=%d)\n", sp->number);
cscier = hs_in(sp, CSCIER);
cscier &= ~HD64465_PCCCSCIER_PIREQE_MASK;
hs_out(sp, cscier, CSCIER);
}
static unsigned int hs_startup_irq(unsigned int irq)
{
hs_socket_enable_ireq(hs_mapped_irq[irq].sock);
hs_mapped_irq[irq].old_handler->startup(irq);
return 0;
}
static void hs_shutdown_irq(unsigned int irq)
{
hs_socket_disable_ireq(hs_mapped_irq[irq].sock);
hs_mapped_irq[irq].old_handler->shutdown(irq);
}
static void hs_enable_irq(unsigned int irq)
{
hs_socket_enable_ireq(hs_mapped_irq[irq].sock);
hs_mapped_irq[irq].old_handler->enable(irq);
}
static void hs_disable_irq(unsigned int irq)
{
hs_socket_disable_ireq(hs_mapped_irq[irq].sock);
hs_mapped_irq[irq].old_handler->disable(irq);
}
extern struct hw_interrupt_type no_irq_type;
static void hs_mask_and_ack_irq(unsigned int irq)
{
hs_socket_disable_ireq(hs_mapped_irq[irq].sock);
/* ack_none() spuriously complains about an unexpected IRQ */
if (hs_mapped_irq[irq].old_handler != &no_irq_type)
hs_mapped_irq[irq].old_handler->ack(irq);
}
static void hs_end_irq(unsigned int irq)
{
hs_socket_enable_ireq(hs_mapped_irq[irq].sock);
hs_mapped_irq[irq].old_handler->end(irq);
}
static struct hw_interrupt_type hd64465_ss_irq_type = {
.typename = "PCMCIA-IRQ",
.startup = hs_startup_irq,
.shutdown = hs_shutdown_irq,
.enable = hs_enable_irq,
.disable = hs_disable_irq,
.ack = hs_mask_and_ack_irq,
.end = hs_end_irq
};
/*
* This function should only ever be called with interrupts disabled.
*/
static void hs_map_irq(hs_socket_t *sp, unsigned int irq)
{
struct irq_desc *desc;
DPRINTK("hs_map_irq(sock=%d irq=%d)\n", sp->number, irq);
if (irq >= HS_NUM_MAPPED_IRQS)
return;
desc = irq_to_desc(irq);
hs_mapped_irq[irq].sock = sp;
/* insert ourselves as the irq controller */
hs_mapped_irq[irq].old_handler = desc->chip;
desc->chip = &hd64465_ss_irq_type;
}
/*
* This function should only ever be called with interrupts disabled.
*/
static void hs_unmap_irq(hs_socket_t *sp, unsigned int irq)
{
struct irq_desc *desc;
DPRINTK("hs_unmap_irq(sock=%d irq=%d)\n", sp->number, irq);
if (irq >= HS_NUM_MAPPED_IRQS)
return;
desc = irq_to_desc(irq);
/* restore the original irq controller */
desc->chip = hs_mapped_irq[irq].old_handler;
}
/*============================================================*/
/*
* Set Vpp and Vcc (in tenths of a Volt). Does not
* support the hi-Z state.
*
* Note, this assumes the board uses a TPS2206 chip to control
* the Vcc and Vpp voltages to the hs_sockets. If your board
* uses the MIC2563 (also supported by the HD64465) then you
* will have to modify this function.
*/
/* 0V 3.3V 5.5V */
static const u_char hs_tps2206_avcc[3] = { 0x00, 0x04, 0x08 };
static const u_char hs_tps2206_bvcc[3] = { 0x00, 0x80, 0x40 };
static int hs_set_voltages(hs_socket_t *sp, int Vcc, int Vpp)
{
u_int psr;
u_int vcci = 0;
u_int sock = sp->number;
DPRINTK("hs_set_voltage(%d, %d, %d)\n", sock, Vcc, Vpp);
switch (Vcc)
{
case 0: vcci = 0; break;
case 33: vcci = 1; break;
case 50: vcci = 2; break;
default: return 0;
}
/* Note: Vpp = 120 not supported -- Greg Banks */
if (Vpp != 0 && Vpp != Vcc)
return 0;
/* The PSR register holds 8 of the 9 bits which control
* the TPS2206 via its serial interface.
*/
psr = inw(HD64465_REG_PCCPSR);
switch (sock)
{
case 0:
psr &= 0x0f;
psr |= hs_tps2206_avcc[vcci];
psr |= (Vpp == 0 ? 0x00 : 0x02);
break;
case 1:
psr &= 0xf0;
psr |= hs_tps2206_bvcc[vcci];
psr |= (Vpp == 0 ? 0x00 : 0x20);
break;
};
outw(psr, HD64465_REG_PCCPSR);
return 1;
}
/*============================================================*/
/*
* Drive the RESET line to the card.
*/
static void hs_reset_socket(hs_socket_t *sp, int on)
{
unsigned short v;
v = hs_in(sp, GCR);
if (on)
v |= HD64465_PCCGCR_PCCR;
else
v &= ~HD64465_PCCGCR_PCCR;
hs_out(sp, v, GCR);
}
/*============================================================*/
static int hs_init(struct pcmcia_socket *s)
{
hs_socket_t *sp = container_of(s, struct hs_socket_t, socket);
DPRINTK("hs_init(%d)\n", sp->number);
return 0;
}
/*============================================================*/
static int hs_get_status(struct pcmcia_socket *s, u_int *value)
{
hs_socket_t *sp = container_of(s, struct hs_socket_t, socket);
unsigned int isr;
u_int status = 0;
isr = hs_in(sp, ISR);
/* Card is seated and powered when *both* CD pins are low */
if ((isr & HD64465_PCCISR_PCD_MASK) == 0)
{
status |= SS_DETECT; /* card present */
switch (isr & HD64465_PCCISR_PBVD_MASK)
{
case HD64465_PCCISR_PBVD_BATGOOD:
break;
case HD64465_PCCISR_PBVD_BATWARN:
status |= SS_BATWARN;
break;
default:
status |= SS_BATDEAD;
break;
}
if (isr & HD64465_PCCISR_PREADY)
status |= SS_READY;
if (isr & HD64465_PCCISR_PMWP)
status |= SS_WRPROT;
/* Voltage Select pins interpreted as per Table 4-5 of the std.
* Assuming we have the TPS2206, the socket is a "Low Voltage
* key, 3.3V and 5V available, no X.XV available".
*/
switch (isr & (HD64465_PCCISR_PVS2|HD64465_PCCISR_PVS1))
{
case HD64465_PCCISR_PVS1:
printk(KERN_NOTICE MODNAME ": cannot handle X.XV card, ignored\n");
status = 0;
break;
case 0:
case HD64465_PCCISR_PVS2:
/* 3.3V */
status |= SS_3VCARD;
break;
case HD64465_PCCISR_PVS2|HD64465_PCCISR_PVS1:
/* 5V */
break;
}
/* TODO: SS_POWERON */
/* TODO: SS_STSCHG */
}
DPRINTK("hs_get_status(%d) = %x\n", sock, status);
*value = status;
return 0;
}
/*============================================================*/
static int hs_set_socket(struct pcmcia_socket *s, socket_state_t *state)
{
hs_socket_t *sp = container_of(s, struct hs_socket_t, socket);
u_long flags;
u_int changed;
unsigned short cscier;
DPRINTK("hs_set_socket(sock=%d, flags=%x, csc_mask=%x, Vcc=%d, Vpp=%d, io_irq=%d)\n",
sock, state->flags, state->csc_mask, state->Vcc, state->Vpp, state->io_irq);
local_irq_save(flags); /* Don't want interrupts happening here */
if (state->Vpp != sp->state.Vpp ||
state->Vcc != sp->state.Vcc) {
if (!hs_set_voltages(sp, state->Vcc, state->Vpp)) {
local_irq_restore(flags);
return -EINVAL;
}
}
/* hd64465_io_debug = 1; */
/*
* Handle changes in the Card Status Change mask,
* by propagating to the CSCR register
*/
changed = sp->state.csc_mask ^ state->csc_mask;
cscier = hs_in(sp, CSCIER);
if (changed & SS_DETECT) {
if (state->csc_mask & SS_DETECT)
cscier |= HD64465_PCCCSCIER_PCDE;
else
cscier &= ~HD64465_PCCCSCIER_PCDE;
}
if (changed & SS_READY) {
if (state->csc_mask & SS_READY)
cscier |= HD64465_PCCCSCIER_PRE;
else
cscier &= ~HD64465_PCCCSCIER_PRE;
}
if (changed & SS_BATDEAD) {
if (state->csc_mask & SS_BATDEAD)
cscier |= HD64465_PCCCSCIER_PBDE;
else
cscier &= ~HD64465_PCCCSCIER_PBDE;
}
if (changed & SS_BATWARN) {
if (state->csc_mask & SS_BATWARN)
cscier |= HD64465_PCCCSCIER_PBWE;
else
cscier &= ~HD64465_PCCCSCIER_PBWE;
}
if (changed & SS_STSCHG) {
if (state->csc_mask & SS_STSCHG)
cscier |= HD64465_PCCCSCIER_PSCE;
else
cscier &= ~HD64465_PCCCSCIER_PSCE;
}
hs_out(sp, cscier, CSCIER);
if (sp->state.io_irq && !state->io_irq)
hs_unmap_irq(sp, sp->state.io_irq);
else if (!sp->state.io_irq && state->io_irq)
hs_map_irq(sp, state->io_irq);
/*
* Handle changes in the flags field,
* by propagating to config registers.
*/
changed = sp->state.flags ^ state->flags;
if (changed & SS_IOCARD) {
DPRINTK("card type: %s\n",
(state->flags & SS_IOCARD ? "i/o" : "memory" ));
bool_to_regbit(sp, GCR, HD64465_PCCGCR_PCCT,
state->flags & SS_IOCARD);
}
if (changed & SS_RESET) {
DPRINTK("%s reset card\n",
(state->flags & SS_RESET ? "start" : "stop"));
bool_to_regbit(sp, GCR, HD64465_PCCGCR_PCCR,
state->flags & SS_RESET);
}
if (changed & SS_OUTPUT_ENA) {
DPRINTK("%sabling card output\n",
(state->flags & SS_OUTPUT_ENA ? "en" : "dis"));
bool_to_regbit(sp, GCR, HD64465_PCCGCR_PDRV,
state->flags & SS_OUTPUT_ENA);
}
/* TODO: SS_SPKR_ENA */
/* hd64465_io_debug = 0; */
sp->state = *state;
local_irq_restore(flags);
#if HD64465_DEBUG > 10
if (state->flags & SS_OUTPUT_ENA)
cis_hex_dump((const unsigned char*)sp->mem_base, 0x100);
#endif
return 0;
}
/*============================================================*/
static int hs_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
{
hs_socket_t *sp = container_of(s, struct hs_socket_t, socket);
int map = io->map;
int sock = sp->number;
struct pccard_io_map *sio;
pgprot_t prot;
DPRINTK("hs_set_io_map(sock=%d, map=%d, flags=0x%x, speed=%dns, start=%#lx, stop=%#lx)\n",
sock, map, io->flags, io->speed, io->start, io->stop);
if (map >= MAX_IO_WIN)
return -EINVAL;
sio = &sp->io_maps[map];
/* check for null changes */
if (io->flags == sio->flags &&
io->start == sio->start &&
io->stop == sio->stop)
return 0;
if (io->flags & MAP_AUTOSZ)
prot = PAGE_KERNEL_PCC(sock, _PAGE_PCC_IODYN);
else if (io->flags & MAP_16BIT)
prot = PAGE_KERNEL_PCC(sock, _PAGE_PCC_IO16);
else
prot = PAGE_KERNEL_PCC(sock, _PAGE_PCC_IO8);
/* TODO: handle MAP_USE_WAIT */
if (io->flags & MAP_USE_WAIT)
printk(KERN_INFO MODNAME ": MAP_USE_WAIT unimplemented\n");
/* TODO: handle MAP_PREFETCH */
if (io->flags & MAP_PREFETCH)
printk(KERN_INFO MODNAME ": MAP_PREFETCH unimplemented\n");
/* TODO: handle MAP_WRPROT */
if (io->flags & MAP_WRPROT)
printk(KERN_INFO MODNAME ": MAP_WRPROT unimplemented\n");
/* TODO: handle MAP_0WS */
if (io->flags & MAP_0WS)
printk(KERN_INFO MODNAME ": MAP_0WS unimplemented\n");
if (io->flags & MAP_ACTIVE) {
unsigned long pstart, psize, paddrbase;
paddrbase = virt_to_phys((void*)(sp->mem_base + 2 * HD64465_PCC_WINDOW));
pstart = io->start & PAGE_MASK;
psize = ((io->stop + PAGE_SIZE) & PAGE_MASK) - pstart;
/*
* Change PTEs in only that portion of the mapping requested
* by the caller. This means that most of the time, most of
* the PTEs in the io_vma will be unmapped and only the bottom
* page will be mapped. But the code allows for weird cards
* that might want IO ports > 4K.
*/
sp->io_base = p3_ioremap(paddrbase + pstart, psize, pgprot_val(prot));
/*
* Change the mapping used by inb() outb() etc
*/
hd64465_port_map(io->start,
io->stop - io->start + 1,
(unsigned long)sp->io_base + io->start, 0);
} else {
hd64465_port_unmap(sio->start, sio->stop - sio->start + 1);
p3_iounmap(sp->io_base);
}
*sio = *io;
return 0;
}
/*============================================================*/
static int hs_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
{
hs_socket_t *sp = container_of(s, struct hs_socket_t, socket);
struct pccard_mem_map *smem;
int map = mem->map;
unsigned long paddr;
#if 0
DPRINTK("hs_set_mem_map(sock=%d, map=%d, flags=0x%x, card_start=0x%08x)\n",
sock, map, mem->flags, mem->card_start);
#endif
if (map >= MAX_WIN)
return -EINVAL;
smem = &sp->mem_maps[map];
paddr = sp->mem_base; /* base of Attribute mapping */
if (!(mem->flags & MAP_ATTRIB))
paddr += HD64465_PCC_WINDOW; /* base of Common mapping */
paddr += mem->card_start;
/* Because we specified SS_CAP_STATIC_MAP, we are obliged
* at this time to report the system address corresponding
* to the card address requested. This is how Socket Services
* queries our fixed mapping. I wish this fact had been
* documented - Greg Banks.
*/
mem->static_start = paddr;
*smem = *mem;
return 0;
}
/* TODO: do we need to use the MMU to access Common memory ??? */
/*============================================================*/
/*
* This function is registered with the HD64465 glue code to do a
* secondary demux step on the PCMCIA interrupts. It handles
* mapping the IREQ request from the card to a standard Linux
* IRQ, as requested by SocketServices.
*/
static int hs_irq_demux(int irq, void *dev)
{
hs_socket_t *sp = dev;
u_int cscr;
DPRINTK("hs_irq_demux(irq=%d)\n", irq);
if (sp->state.io_irq &&
(cscr = hs_in(sp, CSCR)) & HD64465_PCCCSCR_PIREQ) {
cscr &= ~HD64465_PCCCSCR_PIREQ;
hs_out(sp, cscr, CSCR);
return sp->state.io_irq;
}
return irq;
}
/*============================================================*/
/*
* Interrupt handling routine.
*/
static irqreturn_t hs_interrupt(int irq, void *dev)
{
hs_socket_t *sp = dev;
u_int events = 0;
u_int cscr;
cscr = hs_in(sp, CSCR);
DPRINTK("hs_interrupt, cscr=%04x\n", cscr);
/* check for bus-related changes to be reported to Socket Services */
if (cscr & HD64465_PCCCSCR_PCDC) {
/* double-check for a 16-bit card, as we don't support CardBus */
if ((hs_in(sp, ISR) & HD64465_PCCISR_PCD_MASK) != 0) {
printk(KERN_NOTICE MODNAME
": socket %d, card not a supported card type or not inserted correctly\n",
sp->number);
/* Don't do the rest unless a card is present */
cscr &= ~(HD64465_PCCCSCR_PCDC|
HD64465_PCCCSCR_PRC|
HD64465_PCCCSCR_PBW|
HD64465_PCCCSCR_PBD|
HD64465_PCCCSCR_PSC);
} else {
cscr &= ~HD64465_PCCCSCR_PCDC;
events |= SS_DETECT; /* card insertion or removal */
}
}
if (cscr & HD64465_PCCCSCR_PRC) {
cscr &= ~HD64465_PCCCSCR_PRC;
events |= SS_READY; /* ready signal changed */
}
if (cscr & HD64465_PCCCSCR_PBW) {
cscr &= ~HD64465_PCCCSCR_PSC;
events |= SS_BATWARN; /* battery warning */
}
if (cscr & HD64465_PCCCSCR_PBD) {
cscr &= ~HD64465_PCCCSCR_PSC;
events |= SS_BATDEAD; /* battery dead */
}
if (cscr & HD64465_PCCCSCR_PSC) {
cscr &= ~HD64465_PCCCSCR_PSC;
events |= SS_STSCHG; /* STSCHG (status changed) signal */
}
if (cscr & HD64465_PCCCSCR_PIREQ) {
cscr &= ~HD64465_PCCCSCR_PIREQ;
/* This should have been dealt with during irq demux */
printk(KERN_NOTICE MODNAME ": unexpected IREQ from card\n");
}
hs_out(sp, cscr, CSCR);
if (events)
pcmcia_parse_events(&sp->socket, events);
return IRQ_HANDLED;
}
/*============================================================*/
static struct pccard_operations hs_operations = {
.init = hs_init,
.get_status = hs_get_status,
.set_socket = hs_set_socket,
.set_io_map = hs_set_io_map,
.set_mem_map = hs_set_mem_map,
};
static int hs_init_socket(hs_socket_t *sp, int irq, unsigned long mem_base,
unsigned int ctrl_base)
{
unsigned short v;
int i, err;
memset(sp, 0, sizeof(*sp));
sp->irq = irq;
sp->mem_base = mem_base;
sp->mem_length = 4*HD64465_PCC_WINDOW; /* 16MB */
sp->ctrl_base = ctrl_base;
for (i=0 ; i<MAX_IO_WIN ; i++)
sp->io_maps[i].map = i;
for (i=0 ; i<MAX_WIN ; i++)
sp->mem_maps[i].map = i;
hd64465_register_irq_demux(sp->irq, hs_irq_demux, sp);
if ((err = request_irq(sp->irq, hs_interrupt, IRQF_DISABLED, MODNAME, sp)) < 0)
return err;
if (request_mem_region(sp->mem_base, sp->mem_length, MODNAME) == 0) {
sp->mem_base = 0;
return -ENOMEM;
}
/* According to section 3.2 of the PCMCIA standard, low-voltage
* capable cards must implement cold insertion, i.e. Vpp and
* Vcc set to 0 before card is inserted.
*/
/*hs_set_voltages(sp, 0, 0);*/
/* hi-Z the outputs to the card and set 16MB map mode */
v = hs_in(sp, GCR);
v &= ~HD64465_PCCGCR_PCCT; /* memory-only card */
hs_out(sp, v, GCR);
v = hs_in(sp, GCR);
v |= HD64465_PCCGCR_PDRV; /* enable outputs to card */
hs_out(sp, v, GCR);
v = hs_in(sp, GCR);
v |= HD64465_PCCGCR_PMMOD; /* 16MB mapping mode */
hs_out(sp, v, GCR);
v = hs_in(sp, GCR);
/* lowest 16MB of Common */
v &= ~(HD64465_PCCGCR_PPA25|HD64465_PCCGCR_PPA24);
hs_out(sp, v, GCR);
hs_reset_socket(sp, 1);
printk(KERN_INFO "HD64465 PCMCIA bridge socket %d at 0x%08lx irq %d\n",
i, sp->mem_base, sp->irq);
return 0;
}
static void hs_exit_socket(hs_socket_t *sp)
{
unsigned short cscier, gcr;
unsigned long flags;
local_irq_save(flags);
/* turn off interrupts in hardware */
cscier = hs_in(sp, CSCIER);
cscier = (cscier & IER_MASK) | IER_OFF;
hs_out(sp, cscier, CSCIER);
/* hi-Z the outputs to the card */
gcr = hs_in(sp, GCR);
gcr &= HD64465_PCCGCR_PDRV;
hs_out(sp, gcr, GCR);
/* power the card down */
hs_set_voltages(sp, 0, 0);
if (sp->mem_base != 0)
release_mem_region(sp->mem_base, sp->mem_length);
if (sp->irq != 0) {
free_irq(sp->irq, hs_interrupt);
hd64465_unregister_irq_demux(sp->irq);
}
local_irq_restore(flags);
}
static struct device_driver hd64465_driver = {
.name = "hd64465-pcmcia",
.bus = &platform_bus_type,
.suspend = pcmcia_socket_dev_suspend,
.resume = pcmcia_socket_dev_resume,
};
static struct platform_device hd64465_device = {
.name = "hd64465-pcmcia",
.id = 0,
};
static int __init init_hs(void)
{
int i;
unsigned short v;
/* hd64465_io_debug = 1; */
if (driver_register(&hd64465_driver))
return -EINVAL;
/* Wake both sockets out of STANDBY mode */
/* TODO: wait 15ms */
v = inw(HD64465_REG_SMSCR);
v &= ~(HD64465_SMSCR_PC0ST|HD64465_SMSCR_PC1ST);
outw(v, HD64465_REG_SMSCR);
/* keep power controller out of shutdown mode */
v = inb(HD64465_REG_PCC0SCR);
v |= HD64465_PCCSCR_SHDN;
outb(v, HD64465_REG_PCC0SCR);
/* use serial (TPS2206) power controller */
v = inb(HD64465_REG_PCC0CSCR);
v |= HD64465_PCCCSCR_PSWSEL;
outb(v, HD64465_REG_PCC0CSCR);
/*
* Setup hs_sockets[] structures and request system resources.
* TODO: on memory allocation failure, power down the socket
* before quitting.
*/
for (i=0; i<HS_MAX_SOCKETS; i++) {
hs_set_voltages(&hs_sockets[i], 0, 0);
hs_sockets[i].socket.features |= SS_CAP_PCCARD | SS_CAP_STATIC_MAP; /* mappings are fixed in host memory */
hs_sockets[i].socket.resource_ops = &pccard_static_ops;
hs_sockets[i].socket.irq_mask = 0xffde;/*0xffff*/ /* IRQs mapped in s/w so can do any, really */
hs_sockets[i].socket.map_size = HD64465_PCC_WINDOW; /* 16MB fixed window size */
hs_sockets[i].socket.owner = THIS_MODULE;
hs_sockets[i].socket.ss_entry = &hs_operations;
}
i = hs_init_socket(&hs_sockets[0],
HD64465_IRQ_PCMCIA0,
HD64465_PCC0_BASE,
HD64465_REG_PCC0ISR);
if (i < 0) {
unregister_driver(&hd64465_driver);
return i;
}
i = hs_init_socket(&hs_sockets[1],
HD64465_IRQ_PCMCIA1,
HD64465_PCC1_BASE,
HD64465_REG_PCC1ISR);
if (i < 0) {
unregister_driver(&hd64465_driver);
return i;
}
/* hd64465_io_debug = 0; */
platform_device_register(&hd64465_device);
for (i=0; i<HS_MAX_SOCKETS; i++) {
unsigned int ret;
hs_sockets[i].socket.dev.parent = &hd64465_device.dev;
hs_sockets[i].number = i;
ret = pcmcia_register_socket(&hs_sockets[i].socket);
if (ret && i)
pcmcia_unregister_socket(&hs_sockets[0].socket);
}
return 0;
}
static void __exit exit_hs(void)
{
int i;
for (i=0 ; i<HS_MAX_SOCKETS ; i++) {
pcmcia_unregister_socket(&hs_sockets[i].socket);
hs_exit_socket(&hs_sockets[i]);
}
platform_device_unregister(&hd64465_device);
unregister_driver(&hd64465_driver);
}
module_init(init_hs);
module_exit(exit_hs);
/*============================================================*/
/*END*/