Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

298
drivers/pcmcia/Kconfig Normal file
View file

@ -0,0 +1,298 @@
#
# PCCARD (PCMCIA/CardBus) bus subsystem configuration
#
menuconfig PCCARD
tristate "PCCard (PCMCIA/CardBus) support"
---help---
Say Y here if you want to attach PCMCIA- or PC-cards to your Linux
computer. These are credit-card size devices such as network cards,
modems or hard drives often used with laptops computers. There are
actually two varieties of these cards: 16 bit PCMCIA and 32 bit
CardBus cards.
To compile this driver as modules, choose M here: the
module will be called pcmcia_core.
if PCCARD
config PCMCIA
tristate "16-bit PCMCIA support"
select CRC32
default y
---help---
This option enables support for 16-bit PCMCIA cards. Most older
PC-cards are such 16-bit PCMCIA cards, so unless you know you're
only using 32-bit CardBus cards, say Y or M here.
To use 16-bit PCMCIA cards, you will need supporting software in
most cases. (see the file <file:Documentation/Changes> for
location and details).
To compile this driver as modules, choose M here: the
module will be called pcmcia.
If unsure, say Y.
config PCMCIA_LOAD_CIS
bool "Load CIS updates from userspace"
depends on PCMCIA
select FW_LOADER
default y
help
Some PCMCIA cards require an updated Card Information Structure (CIS)
to be loaded from userspace to work correctly. If you say Y here,
and your userspace is arranged correctly, this will be loaded
automatically using the in-kernel firmware loader and the hotplug
subsystem, instead of relying on cardmgr from pcmcia-cs to do so.
If unsure, say Y.
config CARDBUS
bool "32-bit CardBus support"
depends on PCI
default y
---help---
CardBus is a bus mastering architecture for PC-cards, which allows
for 32 bit PC-cards (the original PCMCIA standard specifies only
a 16 bit wide bus). Many newer PC-cards are actually CardBus cards.
To use 32 bit PC-cards, you also need a CardBus compatible host
bridge. Virtually all modern PCMCIA bridges do this, and most of
them are "yenta-compatible", so say Y or M there, too.
If unsure, say Y.
comment "PC-card bridges"
config YENTA
tristate "CardBus yenta-compatible bridge support"
depends on PCI
select CARDBUS if !EXPERT
select PCCARD_NONSTATIC if PCMCIA != n
---help---
This option enables support for CardBus host bridges. Virtually
all modern PCMCIA bridges are CardBus compatible. A "bridge" is
the hardware inside your computer that PCMCIA cards are plugged
into.
To compile this driver as modules, choose M here: the
module will be called yenta_socket.
If unsure, say Y.
config YENTA_O2
default y
bool "Special initialization for O2Micro bridges" if EXPERT
depends on YENTA
config YENTA_RICOH
default y
bool "Special initialization for Ricoh bridges" if EXPERT
depends on YENTA
config YENTA_TI
default y
bool "Special initialization for TI and EnE bridges" if EXPERT
depends on YENTA
config YENTA_ENE_TUNE
default y
bool "Auto-tune EnE bridges for CB cards" if EXPERT
depends on YENTA_TI && CARDBUS
config YENTA_TOSHIBA
default y
bool "Special initialization for Toshiba ToPIC bridges" if EXPERT
depends on YENTA
config PD6729
tristate "Cirrus PD6729 compatible bridge support"
depends on PCMCIA && PCI
select PCCARD_NONSTATIC
help
This provides support for the Cirrus PD6729 PCI-to-PCMCIA bridge
device, found in some older laptops and PCMCIA card readers.
config I82092
tristate "i82092 compatible bridge support"
depends on PCMCIA && PCI
select PCCARD_NONSTATIC
help
This provides support for the Intel I82092AA PCI-to-PCMCIA bridge device,
found in some older laptops and more commonly in evaluation boards for the
chip.
config I82365
tristate "i82365 compatible bridge support"
depends on PCMCIA && ISA
select PCCARD_NONSTATIC
help
Say Y here to include support for ISA-bus PCMCIA host bridges that
are register compatible with the Intel i82365. These are found on
older laptops and ISA-bus card readers for desktop systems. A
"bridge" is the hardware inside your computer that PCMCIA cards are
plugged into. If unsure, say N.
config TCIC
tristate "Databook TCIC host bridge support"
depends on PCMCIA && ISA
select PCCARD_NONSTATIC
help
Say Y here to include support for the Databook TCIC family of PCMCIA
host bridges. These are only found on a handful of old systems.
"Bridge" is the name used for the hardware inside your computer that
PCMCIA cards are plugged into. If unsure, say N.
config PCMCIA_ALCHEMY_DEVBOARD
tristate "Alchemy Db/Pb1xxx PCMCIA socket services"
depends on MIPS_ALCHEMY && PCMCIA
select 64BIT_PHYS_ADDR
help
Enable this driver of you want PCMCIA support on your Alchemy
Db1000, Db/Pb1100, Db/Pb1500, Db/Pb1550, Db/Pb1200, DB1300
board. NOT suitable for the PB1000!
This driver is also available as a module called db1xxx_ss.ko
config PCMCIA_XXS1500
tristate "MyCable XXS1500 PCMCIA socket support"
depends on PCMCIA && MIPS_XXS1500
select 64BIT_PHYS_ADDR
help
Support for the PCMCIA/CF socket interface on MyCable XXS1500
systems.
This driver is also available as a module called xxs1500_ss.ko
config PCMCIA_BCM63XX
tristate "bcm63xx pcmcia support"
depends on BCM63XX && PCMCIA
config PCMCIA_SOC_COMMON
tristate
config PCMCIA_SA11XX_BASE
tristate
config PCMCIA_SA1100
tristate "SA1100 support"
depends on ARM && ARCH_SA1100 && PCMCIA
select PCMCIA_SOC_COMMON
select PCMCIA_SA11XX_BASE
help
Say Y here to include support for SA11x0-based PCMCIA or CF
sockets, found on HP iPAQs, Yopy, and other StrongARM(R)/
Xscale(R) embedded machines.
This driver is also available as a module called sa1100_cs.
config PCMCIA_SA1111
tristate "SA1111 support"
depends on ARM && SA1111 && PCMCIA
select PCMCIA_SOC_COMMON
select PCMCIA_SA11XX_BASE if ARCH_SA1100
select PCMCIA_PXA2XX if ARCH_LUBBOCK && SA1111
help
Say Y here to include support for SA1111-based PCMCIA or CF
sockets, found on the Jornada 720, Graphicsmaster and other
StrongARM(R)/Xscale(R) embedded machines.
This driver is also available as a module called sa1111_cs.
config PCMCIA_PXA2XX
tristate "PXA2xx support"
depends on ARM && ARCH_PXA && PCMCIA
depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \
|| MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \
|| ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \
|| MACH_VPAC270 || MACH_BALLOON3 || MACH_COLIBRI \
|| MACH_COLIBRI320 || MACH_H4700)
select PCMCIA_SOC_COMMON
help
Say Y here to include support for the PXA2xx PCMCIA controller
config PCMCIA_DEBUG
bool "Enable debugging"
depends on (PCMCIA_SA1111 || PCMCIA_SA1100 || PCMCIA_PXA2XX)
help
Say Y here to enable debugging for the SoC PCMCIA layer.
You will need to choose the debugging level either via the
kernel command line, or module options depending whether
you build the drivers as modules.
The kernel command line options are:
sa11xx_core.pc_debug=N
pxa2xx_core.pc_debug=N
The module option is called pc_debug=N
In all the above examples, N is the debugging verbosity
level.
config PCMCIA_PROBE
bool
default y if ISA && !ARCH_SA1100 && !PARISC
config M32R_PCC
bool "M32R PCMCIA I/F"
depends on M32R && CHIP_M32700 && PCMCIA
help
Say Y here to use the M32R PCMCIA controller.
config M32R_CFC
bool "M32R CF I/F Controller"
depends on M32R && (PLAT_USRV || PLAT_M32700UT || PLAT_MAPPI2 || PLAT_MAPPI3 || PLAT_OPSPUT)
help
Say Y here to use the M32R CompactFlash controller.
config M32R_CFC_NUM
int "M32R CF I/F number"
depends on M32R_CFC
default "1" if PLAT_USRV || PLAT_M32700UT || PLAT_MAPPI2 || PLAT_MAPPI3 || PLAT_OPSPUT
help
Set the number of M32R CF slots.
config PCMCIA_VRC4171
tristate "NEC VRC4171 Card Controllers support"
depends on CPU_VR41XX && ISA && PCMCIA
config PCMCIA_VRC4173
tristate "NEC VRC4173 CARDU support"
depends on CPU_VR41XX && PCI && PCMCIA
config OMAP_CF
tristate "OMAP CompactFlash Controller"
depends on PCMCIA && ARCH_OMAP16XX
help
Say Y here to support the CompactFlash controller on OMAP.
Note that this doesn't support "True IDE" mode.
config BFIN_CFPCMCIA
tristate "Blackfin CompactFlash PCMCIA Driver"
depends on PCMCIA && BLACKFIN
help
Say Y here to support the CompactFlash PCMCIA driver for Blackfin.
config AT91_CF
tristate "AT91 CompactFlash Controller"
depends on PCMCIA && ARCH_AT91
help
Say Y here to support the CompactFlash controller on AT91 chips.
Or choose M to compile the driver as a module named "at91_cf".
config ELECTRA_CF
tristate "Electra CompactFlash Controller"
depends on PCMCIA && PPC_PASEMI
help
Say Y here to support the CompactFlash controller on the
PA Semi Electra eval board.
config PCCARD_NONSTATIC
bool
config PCCARD_IODYN
bool
endif # PCCARD

76
drivers/pcmcia/Makefile Normal file
View file

@ -0,0 +1,76 @@
#
# Makefile for the kernel pcmcia subsystem (c/o David Hinds)
#
pcmcia_core-y += cs.o socket_sysfs.o
pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o
obj-$(CONFIG_PCCARD) += pcmcia_core.o
pcmcia-y += ds.o pcmcia_resource.o cistpl.o pcmcia_cis.o
obj-$(CONFIG_PCMCIA) += pcmcia.o
pcmcia_rsrc-y += rsrc_mgr.o
pcmcia_rsrc-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o
pcmcia_rsrc-$(CONFIG_PCCARD_IODYN) += rsrc_iodyn.o
obj-$(CONFIG_PCCARD) += pcmcia_rsrc.o
# socket drivers
obj-$(CONFIG_YENTA) += yenta_socket.o
obj-$(CONFIG_PD6729) += pd6729.o
obj-$(CONFIG_I82365) += i82365.o
obj-$(CONFIG_I82092) += i82092.o
obj-$(CONFIG_TCIC) += tcic.o
obj-$(CONFIG_PCMCIA_SOC_COMMON) += soc_common.o
obj-$(CONFIG_PCMCIA_SA11XX_BASE) += sa11xx_base.o
obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o
obj-$(CONFIG_PCMCIA_SA1111) += sa1111_cs.o
obj-$(CONFIG_M32R_PCC) += m32r_pcc.o
obj-$(CONFIG_M32R_CFC) += m32r_cfc.o
obj-$(CONFIG_PCMCIA_BCM63XX) += bcm63xx_pcmcia.o
obj-$(CONFIG_PCMCIA_VRC4171) += vrc4171_card.o
obj-$(CONFIG_PCMCIA_VRC4173) += vrc4173_cardu.o
obj-$(CONFIG_OMAP_CF) += omap_cf.o
obj-$(CONFIG_BFIN_CFPCMCIA) += bfin_cf_pcmcia.o
obj-$(CONFIG_AT91_CF) += at91_cf.o
obj-$(CONFIG_ELECTRA_CF) += electra_cf.o
obj-$(CONFIG_PCMCIA_ALCHEMY_DEVBOARD) += db1xxx_ss.o
sa1111_cs-y += sa1111_generic.o
sa1111_cs-$(CONFIG_ASSABET_NEPONSET) += sa1111_neponset.o
sa1111_cs-$(CONFIG_SA1100_BADGE4) += sa1111_badge4.o
sa1111_cs-$(CONFIG_SA1100_JORNADA720) += sa1111_jornada720.o
sa1111_cs-$(CONFIG_ARCH_LUBBOCK) += sa1111_lubbock.o
sa1100_cs-y += sa1100_generic.o
sa1100_cs-$(CONFIG_SA1100_ASSABET) += sa1100_assabet.o
sa1100_cs-$(CONFIG_SA1100_CERF) += sa1100_cerf.o
sa1100_cs-$(CONFIG_SA1100_COLLIE) += pxa2xx_sharpsl.o
sa1100_cs-$(CONFIG_SA1100_H3100) += sa1100_h3600.o
sa1100_cs-$(CONFIG_SA1100_H3600) += sa1100_h3600.o
sa1100_cs-$(CONFIG_SA1100_NANOENGINE) += sa1100_nanoengine.o
sa1100_cs-$(CONFIG_SA1100_SHANNON) += sa1100_shannon.o
sa1100_cs-$(CONFIG_SA1100_SIMPAD) += sa1100_simpad.o
pxa2xx_cm_x2xx_cs-y += pxa2xx_cm_x2xx.o pxa2xx_cm_x255.o pxa2xx_cm_x270.o
pxa2xx-obj-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o
pxa2xx-obj-$(CONFIG_PXA_SHARPSL) += pxa2xx_sharpsl.o
pxa2xx-obj-$(CONFIG_MACH_ARMCORE) += pxa2xx_cm_x2xx_cs.o
pxa2xx-obj-$(CONFIG_ARCOM_PCMCIA) += pxa2xx_viper.o
pxa2xx-obj-$(CONFIG_TRIZEPS_PCMCIA) += pxa2xx_trizeps4.o
pxa2xx-obj-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o
pxa2xx-obj-$(CONFIG_MACH_PALMTC) += pxa2xx_palmtc.o
pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o
pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o
pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o
pxa2xx-obj-$(CONFIG_MACH_VPAC270) += pxa2xx_vpac270.o
pxa2xx-obj-$(CONFIG_MACH_BALLOON3) += pxa2xx_balloon3.o
pxa2xx-obj-$(CONFIG_MACH_COLIBRI) += pxa2xx_colibri.o
pxa2xx-obj-$(CONFIG_MACH_COLIBRI320) += pxa2xx_colibri.o
pxa2xx-obj-$(CONFIG_MACH_H4700) += pxa2xx_hx4700.o
obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y)
obj-$(CONFIG_PCMCIA_XXS1500) += xxs1500_ss.o

418
drivers/pcmcia/at91_cf.c Normal file
View file

@ -0,0 +1,418 @@
/*
* at91_cf.c -- AT91 CompactFlash controller driver
*
* Copyright (C) 2005 David Brownell
*
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/platform_data/atmel.h>
#include <linux/io.h>
#include <linux/sizes.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <pcmcia/ss.h>
#include <mach/at91rm9200_mc.h>
#include <mach/at91_ramc.h>
/*
* A0..A10 work in each range; A23 indicates I/O space; A25 is CFRNW;
* some other bit in {A24,A22..A11} is nREG to flag memory access
* (vs attributes). So more than 2KB/region would just be waste.
* Note: These are offsets from the physical base address.
*/
#define CF_ATTR_PHYS (0)
#define CF_IO_PHYS (1 << 23)
#define CF_MEM_PHYS (0x017ff800)
/*--------------------------------------------------------------------------*/
struct at91_cf_socket {
struct pcmcia_socket socket;
unsigned present:1;
struct platform_device *pdev;
struct at91_cf_data *board;
unsigned long phys_baseaddr;
};
static inline int at91_cf_present(struct at91_cf_socket *cf)
{
return !gpio_get_value(cf->board->det_pin);
}
/*--------------------------------------------------------------------------*/
static int at91_cf_ss_init(struct pcmcia_socket *s)
{
return 0;
}
static irqreturn_t at91_cf_irq(int irq, void *_cf)
{
struct at91_cf_socket *cf = _cf;
if (irq == gpio_to_irq(cf->board->det_pin)) {
unsigned present = at91_cf_present(cf);
/* kick pccard as needed */
if (present != cf->present) {
cf->present = present;
dev_dbg(&cf->pdev->dev, "card %s\n",
present ? "present" : "gone");
pcmcia_parse_events(&cf->socket, SS_DETECT);
}
}
return IRQ_HANDLED;
}
static int at91_cf_get_status(struct pcmcia_socket *s, u_int *sp)
{
struct at91_cf_socket *cf;
if (!sp)
return -EINVAL;
cf = container_of(s, struct at91_cf_socket, socket);
/* NOTE: CF is always 3VCARD */
if (at91_cf_present(cf)) {
int rdy = gpio_is_valid(cf->board->irq_pin); /* RDY/nIRQ */
int vcc = gpio_is_valid(cf->board->vcc_pin);
*sp = SS_DETECT | SS_3VCARD;
if (!rdy || gpio_get_value(cf->board->irq_pin))
*sp |= SS_READY;
if (!vcc || gpio_get_value(cf->board->vcc_pin))
*sp |= SS_POWERON;
} else
*sp = 0;
return 0;
}
static int
at91_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
{
struct at91_cf_socket *cf;
cf = container_of(sock, struct at91_cf_socket, socket);
/* switch Vcc if needed and possible */
if (gpio_is_valid(cf->board->vcc_pin)) {
switch (s->Vcc) {
case 0:
gpio_set_value(cf->board->vcc_pin, 0);
break;
case 33:
gpio_set_value(cf->board->vcc_pin, 1);
break;
default:
return -EINVAL;
}
}
/* toggle reset if needed */
gpio_set_value(cf->board->rst_pin, s->flags & SS_RESET);
dev_dbg(&cf->pdev->dev, "Vcc %d, io_irq %d, flags %04x csc %04x\n",
s->Vcc, s->io_irq, s->flags, s->csc_mask);
return 0;
}
static int at91_cf_ss_suspend(struct pcmcia_socket *s)
{
return at91_cf_set_socket(s, &dead_socket);
}
/* we already mapped the I/O region */
static int at91_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
{
struct at91_cf_socket *cf;
u32 csr;
cf = container_of(s, struct at91_cf_socket, socket);
io->flags &= (MAP_ACTIVE | MAP_16BIT | MAP_AUTOSZ);
/*
* Use 16 bit accesses unless/until we need 8-bit i/o space.
*/
csr = at91_ramc_read(0, AT91_SMC_CSR(cf->board->chipselect)) & ~AT91_SMC_DBW;
/*
* NOTE: this CF controller ignores IOIS16, so we can't really do
* MAP_AUTOSZ. The 16bit mode allows single byte access on either
* D0-D7 (even addr) or D8-D15 (odd), so it's close enough for many
* purposes (and handles ide-cs).
*
* The 8bit mode is needed for odd byte access on D0-D7. It seems
* some cards only like that way to get at the odd byte, despite
* CF 3.0 spec table 35 also giving the D8-D15 option.
*/
if (!(io->flags & (MAP_16BIT | MAP_AUTOSZ))) {
csr |= AT91_SMC_DBW_8;
dev_dbg(&cf->pdev->dev, "8bit i/o bus\n");
} else {
csr |= AT91_SMC_DBW_16;
dev_dbg(&cf->pdev->dev, "16bit i/o bus\n");
}
at91_ramc_write(0, AT91_SMC_CSR(cf->board->chipselect), csr);
io->start = cf->socket.io_offset;
io->stop = io->start + SZ_2K - 1;
return 0;
}
/* pcmcia layer maps/unmaps mem regions */
static int
at91_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map)
{
struct at91_cf_socket *cf;
if (map->card_start)
return -EINVAL;
cf = container_of(s, struct at91_cf_socket, socket);
map->flags &= (MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT);
if (map->flags & MAP_ATTRIB)
map->static_start = cf->phys_baseaddr + CF_ATTR_PHYS;
else
map->static_start = cf->phys_baseaddr + CF_MEM_PHYS;
return 0;
}
static struct pccard_operations at91_cf_ops = {
.init = at91_cf_ss_init,
.suspend = at91_cf_ss_suspend,
.get_status = at91_cf_get_status,
.set_socket = at91_cf_set_socket,
.set_io_map = at91_cf_set_io_map,
.set_mem_map = at91_cf_set_mem_map,
};
/*--------------------------------------------------------------------------*/
#if defined(CONFIG_OF)
static const struct of_device_id at91_cf_dt_ids[] = {
{ .compatible = "atmel,at91rm9200-cf" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, at91_cf_dt_ids);
static int at91_cf_dt_init(struct platform_device *pdev)
{
struct at91_cf_data *board;
board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL);
if (!board)
return -ENOMEM;
board->irq_pin = of_get_gpio(pdev->dev.of_node, 0);
board->det_pin = of_get_gpio(pdev->dev.of_node, 1);
board->vcc_pin = of_get_gpio(pdev->dev.of_node, 2);
board->rst_pin = of_get_gpio(pdev->dev.of_node, 3);
pdev->dev.platform_data = board;
return 0;
}
#else
static int at91_cf_dt_init(struct platform_device *pdev)
{
return -ENODEV;
}
#endif
static int at91_cf_probe(struct platform_device *pdev)
{
struct at91_cf_socket *cf;
struct at91_cf_data *board = pdev->dev.platform_data;
struct resource *io;
int status;
if (!board) {
status = at91_cf_dt_init(pdev);
if (status)
return status;
board = pdev->dev.platform_data;
}
if (!gpio_is_valid(board->det_pin) || !gpio_is_valid(board->rst_pin))
return -ENODEV;
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!io)
return -ENODEV;
cf = devm_kzalloc(&pdev->dev, sizeof(*cf), GFP_KERNEL);
if (!cf)
return -ENOMEM;
cf->board = board;
cf->pdev = pdev;
cf->phys_baseaddr = io->start;
platform_set_drvdata(pdev, cf);
/* must be a GPIO; ergo must trigger on both edges */
status = devm_gpio_request(&pdev->dev, board->det_pin, "cf_det");
if (status < 0)
return status;
status = devm_request_irq(&pdev->dev, gpio_to_irq(board->det_pin),
at91_cf_irq, 0, "at91_cf detect", cf);
if (status < 0)
return status;
device_init_wakeup(&pdev->dev, 1);
status = devm_gpio_request(&pdev->dev, board->rst_pin, "cf_rst");
if (status < 0)
goto fail0a;
if (gpio_is_valid(board->vcc_pin)) {
status = devm_gpio_request(&pdev->dev, board->vcc_pin, "cf_vcc");
if (status < 0)
goto fail0a;
}
/*
* The card driver will request this irq later as needed.
* but it causes lots of "irqNN: nobody cared" messages
* unless we report that we handle everything (sigh).
* (Note: DK board doesn't wire the IRQ pin...)
*/
if (gpio_is_valid(board->irq_pin)) {
status = devm_gpio_request(&pdev->dev, board->irq_pin, "cf_irq");
if (status < 0)
goto fail0a;
status = devm_request_irq(&pdev->dev, gpio_to_irq(board->irq_pin),
at91_cf_irq, IRQF_SHARED, "at91_cf", cf);
if (status < 0)
goto fail0a;
cf->socket.pci_irq = gpio_to_irq(board->irq_pin);
} else
cf->socket.pci_irq = nr_irqs + 1;
/* pcmcia layer only remaps "real" memory not iospace */
cf->socket.io_offset = (unsigned long) devm_ioremap(&pdev->dev,
cf->phys_baseaddr + CF_IO_PHYS, SZ_2K);
if (!cf->socket.io_offset) {
status = -ENXIO;
goto fail0a;
}
/* reserve chip-select regions */
if (!devm_request_mem_region(&pdev->dev, io->start, resource_size(io), "at91_cf")) {
status = -ENXIO;
goto fail0a;
}
dev_info(&pdev->dev, "irqs det #%d, io #%d\n",
gpio_to_irq(board->det_pin), gpio_to_irq(board->irq_pin));
cf->socket.owner = THIS_MODULE;
cf->socket.dev.parent = &pdev->dev;
cf->socket.ops = &at91_cf_ops;
cf->socket.resource_ops = &pccard_static_ops;
cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
| SS_CAP_MEM_ALIGN;
cf->socket.map_size = SZ_2K;
cf->socket.io[0].res = io;
status = pcmcia_register_socket(&cf->socket);
if (status < 0)
goto fail0a;
return 0;
fail0a:
device_init_wakeup(&pdev->dev, 0);
return status;
}
static int at91_cf_remove(struct platform_device *pdev)
{
struct at91_cf_socket *cf = platform_get_drvdata(pdev);
pcmcia_unregister_socket(&cf->socket);
device_init_wakeup(&pdev->dev, 0);
return 0;
}
#ifdef CONFIG_PM
static int at91_cf_suspend(struct platform_device *pdev, pm_message_t mesg)
{
struct at91_cf_socket *cf = platform_get_drvdata(pdev);
struct at91_cf_data *board = cf->board;
if (device_may_wakeup(&pdev->dev)) {
enable_irq_wake(gpio_to_irq(board->det_pin));
if (gpio_is_valid(board->irq_pin))
enable_irq_wake(gpio_to_irq(board->irq_pin));
}
return 0;
}
static int at91_cf_resume(struct platform_device *pdev)
{
struct at91_cf_socket *cf = platform_get_drvdata(pdev);
struct at91_cf_data *board = cf->board;
if (device_may_wakeup(&pdev->dev)) {
disable_irq_wake(gpio_to_irq(board->det_pin));
if (gpio_is_valid(board->irq_pin))
disable_irq_wake(gpio_to_irq(board->irq_pin));
}
return 0;
}
#else
#define at91_cf_suspend NULL
#define at91_cf_resume NULL
#endif
static struct platform_driver at91_cf_driver = {
.driver = {
.name = "at91_cf",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(at91_cf_dt_ids),
},
.probe = at91_cf_probe,
.remove = at91_cf_remove,
.suspend = at91_cf_suspend,
.resume = at91_cf_resume,
};
module_platform_driver(at91_cf_driver);
MODULE_DESCRIPTION("AT91 Compact Flash Driver");
MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:at91_cf");

View file

@ -0,0 +1,537 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/timer.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <bcm63xx_regs.h>
#include <bcm63xx_io.h>
#include "bcm63xx_pcmcia.h"
#define PFX "bcm63xx_pcmcia: "
#ifdef CONFIG_CARDBUS
/* if cardbus is used, platform device needs reference to actual pci
* device */
static struct pci_dev *bcm63xx_cb_dev;
#endif
/*
* read/write helper for pcmcia regs
*/
static inline u32 pcmcia_readl(struct bcm63xx_pcmcia_socket *skt, u32 off)
{
return bcm_readl(skt->base + off);
}
static inline void pcmcia_writel(struct bcm63xx_pcmcia_socket *skt,
u32 val, u32 off)
{
bcm_writel(val, skt->base + off);
}
/*
* This callback should (re-)initialise the socket, turn on status
* interrupts and PCMCIA bus, and wait for power to stabilise so that
* the card status signals report correctly.
*
* Hardware cannot do that.
*/
static int bcm63xx_pcmcia_sock_init(struct pcmcia_socket *sock)
{
return 0;
}
/*
* This callback should remove power on the socket, disable IRQs from
* the card, turn off status interrupts, and disable the PCMCIA bus.
*
* Hardware cannot do that.
*/
static int bcm63xx_pcmcia_suspend(struct pcmcia_socket *sock)
{
return 0;
}
/*
* Implements the set_socket() operation for the in-kernel PCMCIA
* service (formerly SS_SetSocket in Card Services). We more or
* less punt all of this work and let the kernel handle the details
* of power configuration, reset, &c. We also record the value of
* `state' in order to regurgitate it to the PCMCIA core later.
*/
static int bcm63xx_pcmcia_set_socket(struct pcmcia_socket *sock,
socket_state_t *state)
{
struct bcm63xx_pcmcia_socket *skt;
unsigned long flags;
u32 val;
skt = sock->driver_data;
spin_lock_irqsave(&skt->lock, flags);
/* note: hardware cannot control socket power, so we will
* always report SS_POWERON */
/* apply socket reset */
val = pcmcia_readl(skt, PCMCIA_C1_REG);
if (state->flags & SS_RESET)
val |= PCMCIA_C1_RESET_MASK;
else
val &= ~PCMCIA_C1_RESET_MASK;
/* reverse reset logic for cardbus card */
if (skt->card_detected && (skt->card_type & CARD_CARDBUS))
val ^= PCMCIA_C1_RESET_MASK;
pcmcia_writel(skt, val, PCMCIA_C1_REG);
/* keep requested state for event reporting */
skt->requested_state = *state;
spin_unlock_irqrestore(&skt->lock, flags);
return 0;
}
/*
* identity cardtype from VS[12] input, CD[12] input while only VS2 is
* floating, and CD[12] input while only VS1 is floating
*/
enum {
IN_VS1 = (1 << 0),
IN_VS2 = (1 << 1),
IN_CD1_VS2H = (1 << 2),
IN_CD2_VS2H = (1 << 3),
IN_CD1_VS1H = (1 << 4),
IN_CD2_VS1H = (1 << 5),
};
static const u8 vscd_to_cardtype[] = {
/* VS1 float, VS2 float */
[IN_VS1 | IN_VS2] = (CARD_PCCARD | CARD_5V),
/* VS1 grounded, VS2 float */
[IN_VS2] = (CARD_PCCARD | CARD_5V | CARD_3V),
/* VS1 grounded, VS2 grounded */
[0] = (CARD_PCCARD | CARD_5V | CARD_3V | CARD_XV),
/* VS1 tied to CD1, VS2 float */
[IN_VS1 | IN_VS2 | IN_CD1_VS1H] = (CARD_CARDBUS | CARD_3V),
/* VS1 grounded, VS2 tied to CD2 */
[IN_VS2 | IN_CD2_VS2H] = (CARD_CARDBUS | CARD_3V | CARD_XV),
/* VS1 tied to CD2, VS2 grounded */
[IN_VS1 | IN_CD2_VS1H] = (CARD_CARDBUS | CARD_3V | CARD_XV | CARD_YV),
/* VS1 float, VS2 grounded */
[IN_VS1] = (CARD_PCCARD | CARD_XV),
/* VS1 float, VS2 tied to CD2 */
[IN_VS1 | IN_VS2 | IN_CD2_VS2H] = (CARD_CARDBUS | CARD_3V),
/* VS1 float, VS2 tied to CD1 */
[IN_VS1 | IN_VS2 | IN_CD1_VS2H] = (CARD_CARDBUS | CARD_XV | CARD_YV),
/* VS1 tied to CD2, VS2 float */
[IN_VS1 | IN_VS2 | IN_CD2_VS1H] = (CARD_CARDBUS | CARD_YV),
/* VS2 grounded, VS1 is tied to CD1, CD2 is grounded */
[IN_VS1 | IN_CD1_VS1H] = 0, /* ignore cardbay */
};
/*
* poll hardware to check card insertion status
*/
static unsigned int __get_socket_status(struct bcm63xx_pcmcia_socket *skt)
{
unsigned int stat;
u32 val;
stat = 0;
/* check CD for card presence */
val = pcmcia_readl(skt, PCMCIA_C1_REG);
if (!(val & PCMCIA_C1_CD1_MASK) && !(val & PCMCIA_C1_CD2_MASK))
stat |= SS_DETECT;
/* if new insertion, detect cardtype */
if ((stat & SS_DETECT) && !skt->card_detected) {
unsigned int stat = 0;
/* float VS1, float VS2 */
val |= PCMCIA_C1_VS1OE_MASK;
val |= PCMCIA_C1_VS2OE_MASK;
pcmcia_writel(skt, val, PCMCIA_C1_REG);
/* wait for output to stabilize and read VS[12] */
udelay(10);
val = pcmcia_readl(skt, PCMCIA_C1_REG);
stat |= (val & PCMCIA_C1_VS1_MASK) ? IN_VS1 : 0;
stat |= (val & PCMCIA_C1_VS2_MASK) ? IN_VS2 : 0;
/* drive VS1 low, float VS2 */
val &= ~PCMCIA_C1_VS1OE_MASK;
val |= PCMCIA_C1_VS2OE_MASK;
pcmcia_writel(skt, val, PCMCIA_C1_REG);
/* wait for output to stabilize and read CD[12] */
udelay(10);
val = pcmcia_readl(skt, PCMCIA_C1_REG);
stat |= (val & PCMCIA_C1_CD1_MASK) ? IN_CD1_VS2H : 0;
stat |= (val & PCMCIA_C1_CD2_MASK) ? IN_CD2_VS2H : 0;
/* float VS1, drive VS2 low */
val |= PCMCIA_C1_VS1OE_MASK;
val &= ~PCMCIA_C1_VS2OE_MASK;
pcmcia_writel(skt, val, PCMCIA_C1_REG);
/* wait for output to stabilize and read CD[12] */
udelay(10);
val = pcmcia_readl(skt, PCMCIA_C1_REG);
stat |= (val & PCMCIA_C1_CD1_MASK) ? IN_CD1_VS1H : 0;
stat |= (val & PCMCIA_C1_CD2_MASK) ? IN_CD2_VS1H : 0;
/* guess cardtype from all this */
skt->card_type = vscd_to_cardtype[stat];
if (!skt->card_type)
dev_err(&skt->socket.dev, "unsupported card type\n");
/* drive both VS pin to 0 again */
val &= ~(PCMCIA_C1_VS1OE_MASK | PCMCIA_C1_VS2OE_MASK);
/* enable correct logic */
val &= ~(PCMCIA_C1_EN_PCMCIA_MASK | PCMCIA_C1_EN_CARDBUS_MASK);
if (skt->card_type & CARD_PCCARD)
val |= PCMCIA_C1_EN_PCMCIA_MASK;
else
val |= PCMCIA_C1_EN_CARDBUS_MASK;
pcmcia_writel(skt, val, PCMCIA_C1_REG);
}
skt->card_detected = (stat & SS_DETECT) ? 1 : 0;
/* report card type/voltage */
if (skt->card_type & CARD_CARDBUS)
stat |= SS_CARDBUS;
if (skt->card_type & CARD_3V)
stat |= SS_3VCARD;
if (skt->card_type & CARD_XV)
stat |= SS_XVCARD;
stat |= SS_POWERON;
if (gpio_get_value(skt->pd->ready_gpio))
stat |= SS_READY;
return stat;
}
/*
* core request to get current socket status
*/
static int bcm63xx_pcmcia_get_status(struct pcmcia_socket *sock,
unsigned int *status)
{
struct bcm63xx_pcmcia_socket *skt;
skt = sock->driver_data;
spin_lock_bh(&skt->lock);
*status = __get_socket_status(skt);
spin_unlock_bh(&skt->lock);
return 0;
}
/*
* socket polling timer callback
*/
static void bcm63xx_pcmcia_poll(unsigned long data)
{
struct bcm63xx_pcmcia_socket *skt;
unsigned int stat, events;
skt = (struct bcm63xx_pcmcia_socket *)data;
spin_lock_bh(&skt->lock);
stat = __get_socket_status(skt);
/* keep only changed bits, and mask with required one from the
* core */
events = (stat ^ skt->old_status) & skt->requested_state.csc_mask;
skt->old_status = stat;
spin_unlock_bh(&skt->lock);
if (events)
pcmcia_parse_events(&skt->socket, events);
mod_timer(&skt->timer,
jiffies + msecs_to_jiffies(BCM63XX_PCMCIA_POLL_RATE));
}
static int bcm63xx_pcmcia_set_io_map(struct pcmcia_socket *sock,
struct pccard_io_map *map)
{
/* this doesn't seem to be called by pcmcia layer if static
* mapping is used */
return 0;
}
static int bcm63xx_pcmcia_set_mem_map(struct pcmcia_socket *sock,
struct pccard_mem_map *map)
{
struct bcm63xx_pcmcia_socket *skt;
struct resource *res;
skt = sock->driver_data;
if (map->flags & MAP_ATTRIB)
res = skt->attr_res;
else
res = skt->common_res;
map->static_start = res->start + map->card_start;
return 0;
}
static struct pccard_operations bcm63xx_pcmcia_operations = {
.init = bcm63xx_pcmcia_sock_init,
.suspend = bcm63xx_pcmcia_suspend,
.get_status = bcm63xx_pcmcia_get_status,
.set_socket = bcm63xx_pcmcia_set_socket,
.set_io_map = bcm63xx_pcmcia_set_io_map,
.set_mem_map = bcm63xx_pcmcia_set_mem_map,
};
/*
* register pcmcia socket to core
*/
static int bcm63xx_drv_pcmcia_probe(struct platform_device *pdev)
{
struct bcm63xx_pcmcia_socket *skt;
struct pcmcia_socket *sock;
struct resource *res, *irq_res;
unsigned int regmem_size = 0, iomem_size = 0;
u32 val;
int ret;
skt = kzalloc(sizeof(*skt), GFP_KERNEL);
if (!skt)
return -ENOMEM;
spin_lock_init(&skt->lock);
sock = &skt->socket;
sock->driver_data = skt;
/* make sure we have all resources we need */
skt->common_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
skt->attr_res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
skt->pd = pdev->dev.platform_data;
if (!skt->common_res || !skt->attr_res || !irq_res || !skt->pd) {
ret = -EINVAL;
goto err;
}
/* remap pcmcia registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regmem_size = resource_size(res);
if (!request_mem_region(res->start, regmem_size, "bcm63xx_pcmcia")) {
ret = -EINVAL;
goto err;
}
skt->reg_res = res;
skt->base = ioremap(res->start, regmem_size);
if (!skt->base) {
ret = -ENOMEM;
goto err;
}
/* remap io registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
iomem_size = resource_size(res);
skt->io_base = ioremap(res->start, iomem_size);
if (!skt->io_base) {
ret = -ENOMEM;
goto err;
}
/* resources are static */
sock->resource_ops = &pccard_static_ops;
sock->ops = &bcm63xx_pcmcia_operations;
sock->owner = THIS_MODULE;
sock->dev.parent = &pdev->dev;
sock->features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
sock->io_offset = (unsigned long)skt->io_base;
sock->pci_irq = irq_res->start;
#ifdef CONFIG_CARDBUS
sock->cb_dev = bcm63xx_cb_dev;
if (bcm63xx_cb_dev)
sock->features |= SS_CAP_CARDBUS;
#endif
/* assume common & attribute memory have the same size */
sock->map_size = resource_size(skt->common_res);
/* initialize polling timer */
setup_timer(&skt->timer, bcm63xx_pcmcia_poll, (unsigned long)skt);
/* initialize pcmcia control register, drive VS[12] to 0,
* leave CB IDSEL to the old value since it is set by the PCI
* layer */
val = pcmcia_readl(skt, PCMCIA_C1_REG);
val &= PCMCIA_C1_CBIDSEL_MASK;
val |= PCMCIA_C1_EN_PCMCIA_GPIO_MASK;
pcmcia_writel(skt, val, PCMCIA_C1_REG);
/*
* Hardware has only one set of timings registers, not one for
* each memory access type, so we configure them for the
* slowest one: attribute memory.
*/
val = PCMCIA_C2_DATA16_MASK;
val |= 10 << PCMCIA_C2_RWCOUNT_SHIFT;
val |= 6 << PCMCIA_C2_INACTIVE_SHIFT;
val |= 3 << PCMCIA_C2_SETUP_SHIFT;
val |= 3 << PCMCIA_C2_HOLD_SHIFT;
pcmcia_writel(skt, val, PCMCIA_C2_REG);
ret = pcmcia_register_socket(sock);
if (ret)
goto err;
/* start polling socket */
mod_timer(&skt->timer,
jiffies + msecs_to_jiffies(BCM63XX_PCMCIA_POLL_RATE));
platform_set_drvdata(pdev, skt);
return 0;
err:
if (skt->io_base)
iounmap(skt->io_base);
if (skt->base)
iounmap(skt->base);
if (skt->reg_res)
release_mem_region(skt->reg_res->start, regmem_size);
kfree(skt);
return ret;
}
static int bcm63xx_drv_pcmcia_remove(struct platform_device *pdev)
{
struct bcm63xx_pcmcia_socket *skt;
struct resource *res;
skt = platform_get_drvdata(pdev);
del_timer_sync(&skt->timer);
iounmap(skt->base);
iounmap(skt->io_base);
res = skt->reg_res;
release_mem_region(res->start, resource_size(res));
kfree(skt);
return 0;
}
struct platform_driver bcm63xx_pcmcia_driver = {
.probe = bcm63xx_drv_pcmcia_probe,
.remove = bcm63xx_drv_pcmcia_remove,
.driver = {
.name = "bcm63xx_pcmcia",
.owner = THIS_MODULE,
},
};
#ifdef CONFIG_CARDBUS
static int bcm63xx_cb_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
/* keep pci device */
bcm63xx_cb_dev = dev;
return platform_driver_register(&bcm63xx_pcmcia_driver);
}
static void bcm63xx_cb_exit(struct pci_dev *dev)
{
platform_driver_unregister(&bcm63xx_pcmcia_driver);
bcm63xx_cb_dev = NULL;
}
static const struct pci_device_id bcm63xx_cb_table[] = {
{
.vendor = PCI_VENDOR_ID_BROADCOM,
.device = BCM6348_CPU_ID,
.subvendor = PCI_VENDOR_ID_BROADCOM,
.subdevice = PCI_ANY_ID,
.class = PCI_CLASS_BRIDGE_CARDBUS << 8,
.class_mask = ~0,
},
{
.vendor = PCI_VENDOR_ID_BROADCOM,
.device = BCM6358_CPU_ID,
.subvendor = PCI_VENDOR_ID_BROADCOM,
.subdevice = PCI_ANY_ID,
.class = PCI_CLASS_BRIDGE_CARDBUS << 8,
.class_mask = ~0,
},
{ },
};
MODULE_DEVICE_TABLE(pci, bcm63xx_cb_table);
static struct pci_driver bcm63xx_cardbus_driver = {
.name = "bcm63xx_cardbus",
.id_table = bcm63xx_cb_table,
.probe = bcm63xx_cb_probe,
.remove = bcm63xx_cb_exit,
};
#endif
/*
* if cardbus support is enabled, register our platform device after
* our fake cardbus bridge has been registered
*/
static int __init bcm63xx_pcmcia_init(void)
{
#ifdef CONFIG_CARDBUS
return pci_register_driver(&bcm63xx_cardbus_driver);
#else
return platform_driver_register(&bcm63xx_pcmcia_driver);
#endif
}
static void __exit bcm63xx_pcmcia_exit(void)
{
#ifdef CONFIG_CARDBUS
return pci_unregister_driver(&bcm63xx_cardbus_driver);
#else
platform_driver_unregister(&bcm63xx_pcmcia_driver);
#endif
}
module_init(bcm63xx_pcmcia_init);
module_exit(bcm63xx_pcmcia_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>");
MODULE_DESCRIPTION("Linux PCMCIA Card Services: bcm63xx Socket Controller");

View file

@ -0,0 +1,60 @@
#ifndef BCM63XX_PCMCIA_H_
#define BCM63XX_PCMCIA_H_
#include <linux/types.h>
#include <linux/timer.h>
#include <pcmcia/ss.h>
#include <bcm63xx_dev_pcmcia.h>
/* socket polling rate in ms */
#define BCM63XX_PCMCIA_POLL_RATE 500
enum {
CARD_CARDBUS = (1 << 0),
CARD_PCCARD = (1 << 1),
CARD_5V = (1 << 2),
CARD_3V = (1 << 3),
CARD_XV = (1 << 4),
CARD_YV = (1 << 5),
};
struct bcm63xx_pcmcia_socket {
struct pcmcia_socket socket;
/* platform specific data */
struct bcm63xx_pcmcia_platform_data *pd;
/* all regs access are protected by this spinlock */
spinlock_t lock;
/* pcmcia registers resource */
struct resource *reg_res;
/* base remapped address of registers */
void __iomem *base;
/* whether a card is detected at the moment */
int card_detected;
/* type of detected card (mask of above enum) */
u8 card_type;
/* keep last socket status to implement event reporting */
unsigned int old_status;
/* backup of requested socket state */
socket_state_t requested_state;
/* timer used for socket status polling */
struct timer_list timer;
/* attribute/common memory resources */
struct resource *attr_res;
struct resource *common_res;
struct resource *io_res;
/* base address of io memory */
void __iomem *io_base;
};
#endif /* BCM63XX_PCMCIA_H_ */

View file

@ -0,0 +1,317 @@
/*
* file: drivers/pcmcia/bfin_cf.c
*
* based on: drivers/pcmcia/omap_cf.c
* omap_cf.c -- OMAP 16xx CompactFlash controller driver
*
* Copyright (c) 2005 David Brownell
* Copyright (c) 2006-2008 Michael Hennerich Analog Devices Inc.
*
* bugs: enter bugs at http://blackfin.uclinux.org/
*
* 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, 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; see the file copying.
* if not, write to the free software foundation,
* 59 temple place - suite 330, boston, ma 02111-1307, usa.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <pcmcia/ss.h>
#include <pcmcia/cisreg.h>
#include <asm/gpio.h>
#define SZ_1K 0x00000400
#define SZ_8K 0x00002000
#define SZ_2K (2 * SZ_1K)
#define POLL_INTERVAL (2 * HZ)
#define CF_ATASEL_ENA 0x20311802 /* Inverts RESET */
#define CF_ATASEL_DIS 0x20311800
#define bfin_cf_present(pfx) (gpio_get_value(pfx))
/*--------------------------------------------------------------------------*/
static const char driver_name[] = "bfin_cf_pcmcia";
struct bfin_cf_socket {
struct pcmcia_socket socket;
struct timer_list timer;
unsigned present:1;
unsigned active:1;
struct platform_device *pdev;
unsigned long phys_cf_io;
unsigned long phys_cf_attr;
u_int irq;
u_short cd_pfx;
};
/*--------------------------------------------------------------------------*/
static int bfin_cf_reset(void)
{
outw(0, CF_ATASEL_ENA);
mdelay(200);
outw(0, CF_ATASEL_DIS);
return 0;
}
static int bfin_cf_ss_init(struct pcmcia_socket *s)
{
return 0;
}
/* the timer is primarily to kick this socket's pccardd */
static void bfin_cf_timer(unsigned long _cf)
{
struct bfin_cf_socket *cf = (void *)_cf;
unsigned short present = bfin_cf_present(cf->cd_pfx);
if (present != cf->present) {
cf->present = present;
dev_dbg(&cf->pdev->dev, ": card %s\n",
present ? "present" : "gone");
pcmcia_parse_events(&cf->socket, SS_DETECT);
}
if (cf->active)
mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
}
static int bfin_cf_get_status(struct pcmcia_socket *s, u_int *sp)
{
struct bfin_cf_socket *cf;
if (!sp)
return -EINVAL;
cf = container_of(s, struct bfin_cf_socket, socket);
if (bfin_cf_present(cf->cd_pfx)) {
*sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
s->pcmcia_irq = 0;
s->pci_irq = cf->irq;
} else
*sp = 0;
return 0;
}
static int
bfin_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
{
struct bfin_cf_socket *cf;
cf = container_of(sock, struct bfin_cf_socket, socket);
switch (s->Vcc) {
case 0:
case 33:
break;
case 50:
break;
default:
return -EINVAL;
}
if (s->flags & SS_RESET) {
disable_irq(cf->irq);
bfin_cf_reset();
enable_irq(cf->irq);
}
dev_dbg(&cf->pdev->dev, ": Vcc %d, io_irq %d, flags %04x csc %04x\n",
s->Vcc, s->io_irq, s->flags, s->csc_mask);
return 0;
}
static int bfin_cf_ss_suspend(struct pcmcia_socket *s)
{
return bfin_cf_set_socket(s, &dead_socket);
}
/* regions are 2K each: mem, attrib, io (and reserved-for-ide) */
static int bfin_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
{
struct bfin_cf_socket *cf;
cf = container_of(s, struct bfin_cf_socket, socket);
io->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT;
io->start = cf->phys_cf_io;
io->stop = io->start + SZ_2K - 1;
return 0;
}
static int
bfin_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map)
{
struct bfin_cf_socket *cf;
if (map->card_start)
return -EINVAL;
cf = container_of(s, struct bfin_cf_socket, socket);
map->static_start = cf->phys_cf_io;
map->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT;
if (map->flags & MAP_ATTRIB)
map->static_start = cf->phys_cf_attr;
return 0;
}
static struct pccard_operations bfin_cf_ops = {
.init = bfin_cf_ss_init,
.suspend = bfin_cf_ss_suspend,
.get_status = bfin_cf_get_status,
.set_socket = bfin_cf_set_socket,
.set_io_map = bfin_cf_set_io_map,
.set_mem_map = bfin_cf_set_mem_map,
};
/*--------------------------------------------------------------------------*/
static int bfin_cf_probe(struct platform_device *pdev)
{
struct bfin_cf_socket *cf;
struct resource *io_mem, *attr_mem;
int irq;
unsigned short cd_pfx;
int status = 0;
dev_info(&pdev->dev, "Blackfin CompactFlash/PCMCIA Socket Driver\n");
irq = platform_get_irq(pdev, 0);
if (irq <= 0)
return -EINVAL;
cd_pfx = platform_get_irq(pdev, 1); /*Card Detect GPIO PIN */
if (gpio_request(cd_pfx, "pcmcia: CD")) {
dev_err(&pdev->dev,
"Failed ro request Card Detect GPIO_%d\n",
cd_pfx);
return -EBUSY;
}
gpio_direction_input(cd_pfx);
cf = kzalloc(sizeof *cf, GFP_KERNEL);
if (!cf) {
gpio_free(cd_pfx);
return -ENOMEM;
}
cf->cd_pfx = cd_pfx;
setup_timer(&cf->timer, bfin_cf_timer, (unsigned long)cf);
cf->pdev = pdev;
platform_set_drvdata(pdev, cf);
cf->irq = irq;
cf->socket.pci_irq = irq;
irq_set_irq_type(irq, IRQF_TRIGGER_LOW);
io_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
attr_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!io_mem || !attr_mem)
goto fail0;
cf->phys_cf_io = io_mem->start;
cf->phys_cf_attr = attr_mem->start;
/* pcmcia layer only remaps "real" memory */
cf->socket.io_offset = (unsigned long)
ioremap(cf->phys_cf_io, SZ_2K);
if (!cf->socket.io_offset)
goto fail0;
dev_err(&pdev->dev, ": on irq %d\n", irq);
dev_dbg(&pdev->dev, ": %s\n",
bfin_cf_present(cf->cd_pfx) ? "present" : "(not present)");
cf->socket.owner = THIS_MODULE;
cf->socket.dev.parent = &pdev->dev;
cf->socket.ops = &bfin_cf_ops;
cf->socket.resource_ops = &pccard_static_ops;
cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
| SS_CAP_MEM_ALIGN;
cf->socket.map_size = SZ_2K;
status = pcmcia_register_socket(&cf->socket);
if (status < 0)
goto fail2;
cf->active = 1;
mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
return 0;
fail2:
iounmap((void __iomem *)cf->socket.io_offset);
release_mem_region(cf->phys_cf_io, SZ_8K);
fail0:
gpio_free(cf->cd_pfx);
kfree(cf);
platform_set_drvdata(pdev, NULL);
return status;
}
static int bfin_cf_remove(struct platform_device *pdev)
{
struct bfin_cf_socket *cf = platform_get_drvdata(pdev);
gpio_free(cf->cd_pfx);
cf->active = 0;
pcmcia_unregister_socket(&cf->socket);
del_timer_sync(&cf->timer);
iounmap((void __iomem *)cf->socket.io_offset);
release_mem_region(cf->phys_cf_io, SZ_8K);
platform_set_drvdata(pdev, NULL);
kfree(cf);
return 0;
}
static struct platform_driver bfin_cf_driver = {
.driver = {
.name = driver_name,
.owner = THIS_MODULE,
},
.probe = bfin_cf_probe,
.remove = bfin_cf_remove,
};
module_platform_driver(bfin_cf_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("BFIN CF/PCMCIA Driver");
MODULE_LICENSE("GPL");

126
drivers/pcmcia/cardbus.c Normal file
View file

@ -0,0 +1,126 @@
/*
* cardbus.c -- 16-bit PCMCIA core support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* (C) 1999 David A. Hinds
*/
/*
* Cardbus handling has been re-written to be more of a PCI bridge thing,
* and the PCI code basically does all the resource handling.
*
* Linus, Jan 2000
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <pcmcia/ss.h>
static void cardbus_config_irq_and_cls(struct pci_bus *bus, int irq)
{
struct pci_dev *dev;
list_for_each_entry(dev, &bus->devices, bus_list) {
u8 irq_pin;
/*
* Since there is only one interrupt available to
* CardBus devices, all devices downstream of this
* device must be using this IRQ.
*/
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq_pin);
if (irq_pin) {
dev->irq = irq;
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
}
/*
* Some controllers transfer very slowly with 0 CLS.
* Configure it. This may fail as CLS configuration
* is mandatory only for MWI.
*/
pci_set_cacheline_size(dev);
if (dev->subordinate)
cardbus_config_irq_and_cls(dev->subordinate, irq);
}
}
/**
* cb_alloc() - add CardBus device
* @s: the pcmcia_socket where the CardBus device is located
*
* cb_alloc() allocates the kernel data structures for a Cardbus device
* and handles the lowest level PCI device setup issues.
*/
int __ref cb_alloc(struct pcmcia_socket *s)
{
struct pci_bus *bus = s->cb_dev->subordinate;
struct pci_dev *dev;
unsigned int max, pass;
pci_lock_rescan_remove();
s->functions = pci_scan_slot(bus, PCI_DEVFN(0, 0));
pci_fixup_cardbus(bus);
max = bus->busn_res.start;
for (pass = 0; pass < 2; pass++)
list_for_each_entry(dev, &bus->devices, bus_list)
if (pci_is_bridge(dev))
max = pci_scan_bridge(bus, dev, max, pass);
/*
* Size all resources below the CardBus controller.
*/
pci_bus_size_bridges(bus);
pci_bus_assign_resources(bus);
cardbus_config_irq_and_cls(bus, s->pci_irq);
/* socket specific tune function */
if (s->tune_bridge)
s->tune_bridge(s, bus);
pci_bus_add_devices(bus);
pci_unlock_rescan_remove();
return 0;
}
/**
* cb_free() - remove CardBus device
* @s: the pcmcia_socket where the CardBus device was located
*
* cb_free() handles the lowest level PCI device cleanup.
*/
void cb_free(struct pcmcia_socket *s)
{
struct pci_dev *bridge, *dev, *tmp;
struct pci_bus *bus;
bridge = s->cb_dev;
if (!bridge)
return;
bus = bridge->subordinate;
if (!bus)
return;
pci_lock_rescan_remove();
list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list)
pci_stop_and_remove_bus_device(dev);
pci_unlock_rescan_remove();
}

147
drivers/pcmcia/cirrus.h Normal file
View file

@ -0,0 +1,147 @@
/*
* cirrus.h 1.4 1999/10/25 20:03:34
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License
* at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and
* limitations under the License.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in which
* case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use
* your version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the MPL or the GPL.
*/
#ifndef _LINUX_CIRRUS_H
#define _LINUX_CIRRUS_H
#define PD67_MISC_CTL_1 0x16 /* Misc control 1 */
#define PD67_FIFO_CTL 0x17 /* FIFO control */
#define PD67_MISC_CTL_2 0x1E /* Misc control 2 */
#define PD67_CHIP_INFO 0x1f /* Chip information */
#define PD67_ATA_CTL 0x026 /* 6730: ATA control */
#define PD67_EXT_INDEX 0x2e /* Extension index */
#define PD67_EXT_DATA 0x2f /* Extension data */
/* PD6722 extension registers -- indexed in PD67_EXT_INDEX */
#define PD67_DATA_MASK0 0x01 /* Data mask 0 */
#define PD67_DATA_MASK1 0x02 /* Data mask 1 */
#define PD67_DMA_CTL 0x03 /* DMA control */
/* PD6730 extension registers -- indexed in PD67_EXT_INDEX */
#define PD67_EXT_CTL_1 0x03 /* Extension control 1 */
#define PD67_MEM_PAGE(n) ((n)+5) /* PCI window bits 31:24 */
#define PD67_EXTERN_DATA 0x0a
#define PD67_MISC_CTL_3 0x25
#define PD67_SMB_PWR_CTL 0x26
/* I/O window address offset */
#define PD67_IO_OFF(w) (0x36+((w)<<1))
/* Timing register sets */
#define PD67_TIME_SETUP(n) (0x3a + 3*(n))
#define PD67_TIME_CMD(n) (0x3b + 3*(n))
#define PD67_TIME_RECOV(n) (0x3c + 3*(n))
/* Flags for PD67_MISC_CTL_1 */
#define PD67_MC1_5V_DET 0x01 /* 5v detect */
#define PD67_MC1_MEDIA_ENA 0x01 /* 6730: Multimedia enable */
#define PD67_MC1_VCC_3V 0x02 /* 3.3v Vcc */
#define PD67_MC1_PULSE_MGMT 0x04
#define PD67_MC1_PULSE_IRQ 0x08
#define PD67_MC1_SPKR_ENA 0x10
#define PD67_MC1_INPACK_ENA 0x80
/* Flags for PD67_FIFO_CTL */
#define PD67_FIFO_EMPTY 0x80
/* Flags for PD67_MISC_CTL_2 */
#define PD67_MC2_FREQ_BYPASS 0x01
#define PD67_MC2_DYNAMIC_MODE 0x02
#define PD67_MC2_SUSPEND 0x04
#define PD67_MC2_5V_CORE 0x08
#define PD67_MC2_LED_ENA 0x10 /* IRQ 12 is LED enable */
#define PD67_MC2_FAST_PCI 0x10 /* 6729: PCI bus > 25 MHz */
#define PD67_MC2_3STATE_BIT7 0x20 /* Floppy change bit */
#define PD67_MC2_DMA_MODE 0x40
#define PD67_MC2_IRQ15_RI 0x80 /* IRQ 15 is ring enable */
/* Flags for PD67_CHIP_INFO */
#define PD67_INFO_SLOTS 0x20 /* 0 = 1 slot, 1 = 2 slots */
#define PD67_INFO_CHIP_ID 0xc0
#define PD67_INFO_REV 0x1c
/* Fields in PD67_TIME_* registers */
#define PD67_TIME_SCALE 0xc0
#define PD67_TIME_SCALE_1 0x00
#define PD67_TIME_SCALE_16 0x40
#define PD67_TIME_SCALE_256 0x80
#define PD67_TIME_SCALE_4096 0xc0
#define PD67_TIME_MULT 0x3f
/* Fields in PD67_DMA_CTL */
#define PD67_DMA_MODE 0xc0
#define PD67_DMA_OFF 0x00
#define PD67_DMA_DREQ_INPACK 0x40
#define PD67_DMA_DREQ_WP 0x80
#define PD67_DMA_DREQ_BVD2 0xc0
#define PD67_DMA_PULLUP 0x20 /* Disable socket pullups? */
/* Fields in PD67_EXT_CTL_1 */
#define PD67_EC1_VCC_PWR_LOCK 0x01
#define PD67_EC1_AUTO_PWR_CLEAR 0x02
#define PD67_EC1_LED_ENA 0x04
#define PD67_EC1_INV_CARD_IRQ 0x08
#define PD67_EC1_INV_MGMT_IRQ 0x10
#define PD67_EC1_PULLUP_CTL 0x20
/* Fields in PD67_MISC_CTL_3 */
#define PD67_MC3_IRQ_MASK 0x03
#define PD67_MC3_IRQ_PCPCI 0x00
#define PD67_MC3_IRQ_EXTERN 0x01
#define PD67_MC3_IRQ_PCIWAY 0x02
#define PD67_MC3_IRQ_PCI 0x03
#define PD67_MC3_PWR_MASK 0x0c
#define PD67_MC3_PWR_SERIAL 0x00
#define PD67_MC3_PWR_TI2202 0x08
#define PD67_MC3_PWR_SMB 0x0c
/* Register definitions for Cirrus PD6832 PCI-to-CardBus bridge */
/* PD6832 extension registers -- indexed in PD67_EXT_INDEX */
#define PD68_EXT_CTL_2 0x0b
#define PD68_PCI_SPACE 0x22
#define PD68_PCCARD_SPACE 0x23
#define PD68_WINDOW_TYPE 0x24
#define PD68_EXT_CSC 0x2e
#define PD68_MISC_CTL_4 0x2f
#define PD68_MISC_CTL_5 0x30
#define PD68_MISC_CTL_6 0x31
/* Extra flags in PD67_MISC_CTL_3 */
#define PD68_MC3_HW_SUSP 0x10
#define PD68_MC3_MM_EXPAND 0x40
#define PD68_MC3_MM_ARM 0x80
/* Bridge Control Register */
#define PD6832_BCR_MGMT_IRQ_ENA 0x0800
/* Socket Number Register */
#define PD6832_SOCKET_NUMBER 0x004c /* 8 bit */
#endif /* _LINUX_CIRRUS_H */

1600
drivers/pcmcia/cistpl.c Normal file

File diff suppressed because it is too large Load diff

922
drivers/pcmcia/cs.c Normal file
View file

@ -0,0 +1,922 @@
/*
* cs.c -- Kernel Card Services - core services
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* (C) 1999 David A. Hinds
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/device.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <asm/irq.h>
#include <pcmcia/ss.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include "cs_internal.h"
/* Module parameters */
MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
MODULE_DESCRIPTION("Linux Kernel Card Services");
MODULE_LICENSE("GPL");
#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
INT_MODULE_PARM(setup_delay, 10); /* centiseconds */
INT_MODULE_PARM(resume_delay, 20); /* centiseconds */
INT_MODULE_PARM(shutdown_delay, 3); /* centiseconds */
INT_MODULE_PARM(vcc_settle, 40); /* centiseconds */
INT_MODULE_PARM(reset_time, 10); /* usecs */
INT_MODULE_PARM(unreset_delay, 10); /* centiseconds */
INT_MODULE_PARM(unreset_check, 10); /* centiseconds */
INT_MODULE_PARM(unreset_limit, 30); /* unreset_check's */
/* Access speed for attribute memory windows */
INT_MODULE_PARM(cis_speed, 300); /* ns */
socket_state_t dead_socket = {
.csc_mask = SS_DETECT,
};
EXPORT_SYMBOL(dead_socket);
/* List of all sockets, protected by a rwsem */
LIST_HEAD(pcmcia_socket_list);
EXPORT_SYMBOL(pcmcia_socket_list);
DECLARE_RWSEM(pcmcia_socket_list_rwsem);
EXPORT_SYMBOL(pcmcia_socket_list_rwsem);
struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt)
{
struct device *dev = get_device(&skt->dev);
if (!dev)
return NULL;
return dev_get_drvdata(dev);
}
EXPORT_SYMBOL(pcmcia_get_socket);
void pcmcia_put_socket(struct pcmcia_socket *skt)
{
put_device(&skt->dev);
}
EXPORT_SYMBOL(pcmcia_put_socket);
static void pcmcia_release_socket(struct device *dev)
{
struct pcmcia_socket *socket = dev_get_drvdata(dev);
complete(&socket->socket_released);
}
static int pccardd(void *__skt);
/**
* pcmcia_register_socket - add a new pcmcia socket device
* @socket: the &socket to register
*/
int pcmcia_register_socket(struct pcmcia_socket *socket)
{
struct task_struct *tsk;
int ret;
if (!socket || !socket->ops || !socket->dev.parent || !socket->resource_ops)
return -EINVAL;
dev_dbg(&socket->dev, "pcmcia_register_socket(0x%p)\n", socket->ops);
/* try to obtain a socket number [yes, it gets ugly if we
* register more than 2^sizeof(unsigned int) pcmcia
* sockets... but the socket number is deprecated
* anyways, so I don't care] */
down_write(&pcmcia_socket_list_rwsem);
if (list_empty(&pcmcia_socket_list))
socket->sock = 0;
else {
unsigned int found, i = 1;
struct pcmcia_socket *tmp;
do {
found = 1;
list_for_each_entry(tmp, &pcmcia_socket_list, socket_list) {
if (tmp->sock == i)
found = 0;
}
i++;
} while (!found);
socket->sock = i - 1;
}
list_add_tail(&socket->socket_list, &pcmcia_socket_list);
up_write(&pcmcia_socket_list_rwsem);
#ifndef CONFIG_CARDBUS
/*
* If we do not support Cardbus, ensure that
* the Cardbus socket capability is disabled.
*/
socket->features &= ~SS_CAP_CARDBUS;
#endif
/* set proper values in socket->dev */
dev_set_drvdata(&socket->dev, socket);
socket->dev.class = &pcmcia_socket_class;
dev_set_name(&socket->dev, "pcmcia_socket%u", socket->sock);
/* base address = 0, map = 0 */
socket->cis_mem.flags = 0;
socket->cis_mem.speed = cis_speed;
INIT_LIST_HEAD(&socket->cis_cache);
init_completion(&socket->socket_released);
init_completion(&socket->thread_done);
mutex_init(&socket->skt_mutex);
mutex_init(&socket->ops_mutex);
spin_lock_init(&socket->thread_lock);
if (socket->resource_ops->init) {
mutex_lock(&socket->ops_mutex);
ret = socket->resource_ops->init(socket);
mutex_unlock(&socket->ops_mutex);
if (ret)
goto err;
}
tsk = kthread_run(pccardd, socket, "pccardd");
if (IS_ERR(tsk)) {
ret = PTR_ERR(tsk);
goto err;
}
wait_for_completion(&socket->thread_done);
if (!socket->thread) {
dev_printk(KERN_WARNING, &socket->dev,
"PCMCIA: warning: socket thread did not start\n");
return -EIO;
}
pcmcia_parse_events(socket, SS_DETECT);
/*
* Let's try to get the PCMCIA module for 16-bit PCMCIA support.
* If it fails, it doesn't matter -- we still have 32-bit CardBus
* support to offer, so this is not a failure mode.
*/
request_module_nowait("pcmcia");
return 0;
err:
down_write(&pcmcia_socket_list_rwsem);
list_del(&socket->socket_list);
up_write(&pcmcia_socket_list_rwsem);
return ret;
} /* pcmcia_register_socket */
EXPORT_SYMBOL(pcmcia_register_socket);
/**
* pcmcia_unregister_socket - remove a pcmcia socket device
* @socket: the &socket to unregister
*/
void pcmcia_unregister_socket(struct pcmcia_socket *socket)
{
if (!socket)
return;
dev_dbg(&socket->dev, "pcmcia_unregister_socket(0x%p)\n", socket->ops);
if (socket->thread)
kthread_stop(socket->thread);
/* remove from our own list */
down_write(&pcmcia_socket_list_rwsem);
list_del(&socket->socket_list);
up_write(&pcmcia_socket_list_rwsem);
/* wait for sysfs to drop all references */
if (socket->resource_ops->exit) {
mutex_lock(&socket->ops_mutex);
socket->resource_ops->exit(socket);
mutex_unlock(&socket->ops_mutex);
}
wait_for_completion(&socket->socket_released);
} /* pcmcia_unregister_socket */
EXPORT_SYMBOL(pcmcia_unregister_socket);
struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr)
{
struct pcmcia_socket *s;
down_read(&pcmcia_socket_list_rwsem);
list_for_each_entry(s, &pcmcia_socket_list, socket_list)
if (s->sock == nr) {
up_read(&pcmcia_socket_list_rwsem);
return s;
}
up_read(&pcmcia_socket_list_rwsem);
return NULL;
}
EXPORT_SYMBOL(pcmcia_get_socket_by_nr);
static int socket_reset(struct pcmcia_socket *skt)
{
int status, i;
dev_dbg(&skt->dev, "reset\n");
skt->socket.flags |= SS_OUTPUT_ENA | SS_RESET;
skt->ops->set_socket(skt, &skt->socket);
udelay((long)reset_time);
skt->socket.flags &= ~SS_RESET;
skt->ops->set_socket(skt, &skt->socket);
msleep(unreset_delay * 10);
for (i = 0; i < unreset_limit; i++) {
skt->ops->get_status(skt, &status);
if (!(status & SS_DETECT))
return -ENODEV;
if (status & SS_READY)
return 0;
msleep(unreset_check * 10);
}
dev_printk(KERN_ERR, &skt->dev, "time out after reset.\n");
return -ETIMEDOUT;
}
/*
* socket_setup() and socket_shutdown() are called by the main event handler
* when card insertion and removal events are received.
* socket_setup() turns on socket power and resets the socket, in two stages.
* socket_shutdown() unconfigures a socket and turns off socket power.
*/
static void socket_shutdown(struct pcmcia_socket *s)
{
int status;
dev_dbg(&s->dev, "shutdown\n");
if (s->callback)
s->callback->remove(s);
mutex_lock(&s->ops_mutex);
s->state &= SOCKET_INUSE | SOCKET_PRESENT;
msleep(shutdown_delay * 10);
s->state &= SOCKET_INUSE;
/* Blank out the socket state */
s->socket = dead_socket;
s->ops->init(s);
s->ops->set_socket(s, &s->socket);
s->lock_count = 0;
kfree(s->fake_cis);
s->fake_cis = NULL;
s->functions = 0;
/* From here on we can be sure that only we (that is, the
* pccardd thread) accesses this socket, and all (16-bit)
* PCMCIA interactions are gone. Therefore, release
* ops_mutex so that we don't get a sysfs-related lockdep
* warning.
*/
mutex_unlock(&s->ops_mutex);
#ifdef CONFIG_CARDBUS
cb_free(s);
#endif
/* give socket some time to power down */
msleep(100);
s->ops->get_status(s, &status);
if (status & SS_POWERON) {
dev_printk(KERN_ERR, &s->dev,
"*** DANGER *** unable to remove socket power\n");
}
s->state &= ~SOCKET_INUSE;
}
static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
{
int status, i;
dev_dbg(&skt->dev, "setup\n");
skt->ops->get_status(skt, &status);
if (!(status & SS_DETECT))
return -ENODEV;
msleep(initial_delay * 10);
for (i = 0; i < 100; i++) {
skt->ops->get_status(skt, &status);
if (!(status & SS_DETECT))
return -ENODEV;
if (!(status & SS_PENDING))
break;
msleep(100);
}
if (status & SS_PENDING) {
dev_printk(KERN_ERR, &skt->dev,
"voltage interrogation timed out.\n");
return -ETIMEDOUT;
}
if (status & SS_CARDBUS) {
if (!(skt->features & SS_CAP_CARDBUS)) {
dev_printk(KERN_ERR, &skt->dev,
"cardbus cards are not supported.\n");
return -EINVAL;
}
skt->state |= SOCKET_CARDBUS;
} else
skt->state &= ~SOCKET_CARDBUS;
/*
* Decode the card voltage requirements, and apply power to the card.
*/
if (status & SS_3VCARD)
skt->socket.Vcc = skt->socket.Vpp = 33;
else if (!(status & SS_XVCARD))
skt->socket.Vcc = skt->socket.Vpp = 50;
else {
dev_printk(KERN_ERR, &skt->dev, "unsupported voltage key.\n");
return -EIO;
}
if (skt->power_hook)
skt->power_hook(skt, HOOK_POWER_PRE);
skt->socket.flags = 0;
skt->ops->set_socket(skt, &skt->socket);
/*
* Wait "vcc_settle" for the supply to stabilise.
*/
msleep(vcc_settle * 10);
skt->ops->get_status(skt, &status);
if (!(status & SS_POWERON)) {
dev_printk(KERN_ERR, &skt->dev, "unable to apply power.\n");
return -EIO;
}
status = socket_reset(skt);
if (skt->power_hook)
skt->power_hook(skt, HOOK_POWER_POST);
return status;
}
/*
* Handle card insertion. Setup the socket, reset the card,
* and then tell the rest of PCMCIA that a card is present.
*/
static int socket_insert(struct pcmcia_socket *skt)
{
int ret;
dev_dbg(&skt->dev, "insert\n");
mutex_lock(&skt->ops_mutex);
if (skt->state & SOCKET_INUSE) {
mutex_unlock(&skt->ops_mutex);
return -EINVAL;
}
skt->state |= SOCKET_INUSE;
ret = socket_setup(skt, setup_delay);
if (ret == 0) {
skt->state |= SOCKET_PRESENT;
dev_printk(KERN_NOTICE, &skt->dev,
"pccard: %s card inserted into slot %d\n",
(skt->state & SOCKET_CARDBUS) ? "CardBus" : "PCMCIA",
skt->sock);
#ifdef CONFIG_CARDBUS
if (skt->state & SOCKET_CARDBUS) {
cb_alloc(skt);
skt->state |= SOCKET_CARDBUS_CONFIG;
}
#endif
dev_dbg(&skt->dev, "insert done\n");
mutex_unlock(&skt->ops_mutex);
if (!(skt->state & SOCKET_CARDBUS) && (skt->callback))
skt->callback->add(skt);
} else {
mutex_unlock(&skt->ops_mutex);
socket_shutdown(skt);
}
return ret;
}
static int socket_suspend(struct pcmcia_socket *skt)
{
if (skt->state & SOCKET_SUSPEND)
return -EBUSY;
mutex_lock(&skt->ops_mutex);
skt->suspended_state = skt->state;
skt->socket = dead_socket;
skt->ops->set_socket(skt, &skt->socket);
if (skt->ops->suspend)
skt->ops->suspend(skt);
skt->state |= SOCKET_SUSPEND;
mutex_unlock(&skt->ops_mutex);
return 0;
}
static int socket_early_resume(struct pcmcia_socket *skt)
{
mutex_lock(&skt->ops_mutex);
skt->socket = dead_socket;
skt->ops->init(skt);
skt->ops->set_socket(skt, &skt->socket);
if (skt->state & SOCKET_PRESENT)
skt->resume_status = socket_setup(skt, resume_delay);
mutex_unlock(&skt->ops_mutex);
return 0;
}
static int socket_late_resume(struct pcmcia_socket *skt)
{
int ret = 0;
mutex_lock(&skt->ops_mutex);
skt->state &= ~SOCKET_SUSPEND;
mutex_unlock(&skt->ops_mutex);
if (!(skt->state & SOCKET_PRESENT)) {
ret = socket_insert(skt);
if (ret == -ENODEV)
ret = 0;
return ret;
}
if (skt->resume_status) {
socket_shutdown(skt);
return 0;
}
if (skt->suspended_state != skt->state) {
dev_dbg(&skt->dev,
"suspend state 0x%x != resume state 0x%x\n",
skt->suspended_state, skt->state);
socket_shutdown(skt);
return socket_insert(skt);
}
if (!(skt->state & SOCKET_CARDBUS) && (skt->callback))
ret = skt->callback->early_resume(skt);
return ret;
}
/*
* Finalize the resume. In case of a cardbus socket, we have
* to rebind the devices as we can't be certain that it has been
* replaced, or not.
*/
static int socket_complete_resume(struct pcmcia_socket *skt)
{
int ret = 0;
#ifdef CONFIG_CARDBUS
if (skt->state & SOCKET_CARDBUS) {
/* We can't be sure the CardBus card is the same
* as the one previously inserted. Therefore, remove
* and re-add... */
cb_free(skt);
ret = cb_alloc(skt);
if (ret)
cb_free(skt);
}
#endif
return ret;
}
/*
* Resume a socket. If a card is present, verify its CIS against
* our cached copy. If they are different, the card has been
* replaced, and we need to tell the drivers.
*/
static int socket_resume(struct pcmcia_socket *skt)
{
int err;
if (!(skt->state & SOCKET_SUSPEND))
return -EBUSY;
socket_early_resume(skt);
err = socket_late_resume(skt);
if (!err)
err = socket_complete_resume(skt);
return err;
}
static void socket_remove(struct pcmcia_socket *skt)
{
dev_printk(KERN_NOTICE, &skt->dev,
"pccard: card ejected from slot %d\n", skt->sock);
socket_shutdown(skt);
}
/*
* Process a socket card detect status change.
*
* If we don't have a card already present, delay the detect event for
* about 20ms (to be on the safe side) before reading the socket status.
*
* Some i82365-based systems send multiple SS_DETECT events during card
* insertion, and the "card present" status bit seems to bounce. This
* will probably be true with GPIO-based card detection systems after
* the product has aged.
*/
static void socket_detect_change(struct pcmcia_socket *skt)
{
if (!(skt->state & SOCKET_SUSPEND)) {
int status;
if (!(skt->state & SOCKET_PRESENT))
msleep(20);
skt->ops->get_status(skt, &status);
if ((skt->state & SOCKET_PRESENT) &&
!(status & SS_DETECT))
socket_remove(skt);
if (!(skt->state & SOCKET_PRESENT) &&
(status & SS_DETECT))
socket_insert(skt);
}
}
static int pccardd(void *__skt)
{
struct pcmcia_socket *skt = __skt;
int ret;
skt->thread = current;
skt->socket = dead_socket;
skt->ops->init(skt);
skt->ops->set_socket(skt, &skt->socket);
/* register with the device core */
ret = device_register(&skt->dev);
if (ret) {
dev_printk(KERN_WARNING, &skt->dev,
"PCMCIA: unable to register socket\n");
skt->thread = NULL;
complete(&skt->thread_done);
return 0;
}
ret = pccard_sysfs_add_socket(&skt->dev);
if (ret)
dev_warn(&skt->dev, "err %d adding socket attributes\n", ret);
complete(&skt->thread_done);
/* wait for userspace to catch up */
msleep(250);
set_freezable();
for (;;) {
unsigned long flags;
unsigned int events;
unsigned int sysfs_events;
set_current_state(TASK_INTERRUPTIBLE);
spin_lock_irqsave(&skt->thread_lock, flags);
events = skt->thread_events;
skt->thread_events = 0;
sysfs_events = skt->sysfs_events;
skt->sysfs_events = 0;
spin_unlock_irqrestore(&skt->thread_lock, flags);
mutex_lock(&skt->skt_mutex);
if (events & SS_DETECT)
socket_detect_change(skt);
if (sysfs_events) {
if (sysfs_events & PCMCIA_UEVENT_EJECT)
socket_remove(skt);
if (sysfs_events & PCMCIA_UEVENT_INSERT)
socket_insert(skt);
if ((sysfs_events & PCMCIA_UEVENT_SUSPEND) &&
!(skt->state & SOCKET_CARDBUS)) {
if (skt->callback)
ret = skt->callback->suspend(skt);
else
ret = 0;
if (!ret) {
socket_suspend(skt);
msleep(100);
}
}
if ((sysfs_events & PCMCIA_UEVENT_RESUME) &&
!(skt->state & SOCKET_CARDBUS)) {
ret = socket_resume(skt);
if (!ret && skt->callback)
skt->callback->resume(skt);
}
if ((sysfs_events & PCMCIA_UEVENT_REQUERY) &&
!(skt->state & SOCKET_CARDBUS)) {
if (!ret && skt->callback)
skt->callback->requery(skt);
}
}
mutex_unlock(&skt->skt_mutex);
if (events || sysfs_events)
continue;
if (kthread_should_stop())
break;
schedule();
try_to_freeze();
}
/* make sure we are running before we exit */
set_current_state(TASK_RUNNING);
/* shut down socket, if a device is still present */
if (skt->state & SOCKET_PRESENT) {
mutex_lock(&skt->skt_mutex);
socket_remove(skt);
mutex_unlock(&skt->skt_mutex);
}
/* remove from the device core */
pccard_sysfs_remove_socket(&skt->dev);
device_unregister(&skt->dev);
return 0;
}
/*
* Yenta (at least) probes interrupts before registering the socket and
* starting the handler thread.
*/
void pcmcia_parse_events(struct pcmcia_socket *s, u_int events)
{
unsigned long flags;
dev_dbg(&s->dev, "parse_events: events %08x\n", events);
if (s->thread) {
spin_lock_irqsave(&s->thread_lock, flags);
s->thread_events |= events;
spin_unlock_irqrestore(&s->thread_lock, flags);
wake_up_process(s->thread);
}
} /* pcmcia_parse_events */
EXPORT_SYMBOL(pcmcia_parse_events);
/**
* pcmcia_parse_uevents() - tell pccardd to issue manual commands
* @s: the PCMCIA socket we wan't to command
* @events: events to pass to pccardd
*
* userspace-issued insert, eject, suspend and resume commands must be
* handled by pccardd to avoid any sysfs-related deadlocks. Valid events
* are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert),
* PCMCIA_UEVENT_RESUME (for resume), PCMCIA_UEVENT_SUSPEND (for suspend)
* and PCMCIA_UEVENT_REQUERY (for re-querying the PCMCIA card).
*/
void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events)
{
unsigned long flags;
dev_dbg(&s->dev, "parse_uevents: events %08x\n", events);
if (s->thread) {
spin_lock_irqsave(&s->thread_lock, flags);
s->sysfs_events |= events;
spin_unlock_irqrestore(&s->thread_lock, flags);
wake_up_process(s->thread);
}
}
EXPORT_SYMBOL(pcmcia_parse_uevents);
/* register pcmcia_callback */
int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
{
int ret = 0;
/* s->skt_mutex also protects s->callback */
mutex_lock(&s->skt_mutex);
if (c) {
/* registration */
if (s->callback) {
ret = -EBUSY;
goto err;
}
s->callback = c;
if ((s->state & (SOCKET_PRESENT|SOCKET_CARDBUS)) == SOCKET_PRESENT)
s->callback->add(s);
} else
s->callback = NULL;
err:
mutex_unlock(&s->skt_mutex);
return ret;
}
EXPORT_SYMBOL(pccard_register_pcmcia);
/* I'm not sure which "reset" function this is supposed to use,
* but for now, it uses the low-level interface's reset, not the
* CIS register.
*/
int pcmcia_reset_card(struct pcmcia_socket *skt)
{
int ret;
dev_dbg(&skt->dev, "resetting socket\n");
mutex_lock(&skt->skt_mutex);
do {
if (!(skt->state & SOCKET_PRESENT)) {
dev_dbg(&skt->dev, "can't reset, not present\n");
ret = -ENODEV;
break;
}
if (skt->state & SOCKET_SUSPEND) {
dev_dbg(&skt->dev, "can't reset, suspended\n");
ret = -EBUSY;
break;
}
if (skt->state & SOCKET_CARDBUS) {
dev_dbg(&skt->dev, "can't reset, is cardbus\n");
ret = -EPERM;
break;
}
if (skt->callback)
skt->callback->suspend(skt);
mutex_lock(&skt->ops_mutex);
ret = socket_reset(skt);
mutex_unlock(&skt->ops_mutex);
if ((ret == 0) && (skt->callback))
skt->callback->resume(skt);
ret = 0;
} while (0);
mutex_unlock(&skt->skt_mutex);
return ret;
} /* reset_card */
EXPORT_SYMBOL(pcmcia_reset_card);
static int pcmcia_socket_uevent(struct device *dev,
struct kobj_uevent_env *env)
{
struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev);
if (add_uevent_var(env, "SOCKET_NO=%u", s->sock))
return -ENOMEM;
return 0;
}
static struct completion pcmcia_unload;
static void pcmcia_release_socket_class(struct class *data)
{
complete(&pcmcia_unload);
}
#ifdef CONFIG_PM
static int __pcmcia_pm_op(struct device *dev,
int (*callback) (struct pcmcia_socket *skt))
{
struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev);
int ret;
mutex_lock(&s->skt_mutex);
ret = callback(s);
mutex_unlock(&s->skt_mutex);
return ret;
}
static int pcmcia_socket_dev_suspend_noirq(struct device *dev)
{
return __pcmcia_pm_op(dev, socket_suspend);
}
static int pcmcia_socket_dev_resume_noirq(struct device *dev)
{
return __pcmcia_pm_op(dev, socket_early_resume);
}
static int __used pcmcia_socket_dev_resume(struct device *dev)
{
return __pcmcia_pm_op(dev, socket_late_resume);
}
static void __used pcmcia_socket_dev_complete(struct device *dev)
{
WARN(__pcmcia_pm_op(dev, socket_complete_resume),
"failed to complete resume");
}
static const struct dev_pm_ops pcmcia_socket_pm_ops = {
/* dev_resume may be called with IRQs enabled */
SET_SYSTEM_SLEEP_PM_OPS(NULL,
pcmcia_socket_dev_resume)
/* late suspend must be called with IRQs disabled */
.suspend_noirq = pcmcia_socket_dev_suspend_noirq,
.freeze_noirq = pcmcia_socket_dev_suspend_noirq,
.poweroff_noirq = pcmcia_socket_dev_suspend_noirq,
/* early resume must be called with IRQs disabled */
.resume_noirq = pcmcia_socket_dev_resume_noirq,
.thaw_noirq = pcmcia_socket_dev_resume_noirq,
.restore_noirq = pcmcia_socket_dev_resume_noirq,
.complete = pcmcia_socket_dev_complete,
};
#define PCMCIA_SOCKET_CLASS_PM_OPS (&pcmcia_socket_pm_ops)
#else /* CONFIG_PM */
#define PCMCIA_SOCKET_CLASS_PM_OPS NULL
#endif /* CONFIG_PM */
struct class pcmcia_socket_class = {
.name = "pcmcia_socket",
.dev_uevent = pcmcia_socket_uevent,
.dev_release = pcmcia_release_socket,
.class_release = pcmcia_release_socket_class,
.pm = PCMCIA_SOCKET_CLASS_PM_OPS,
};
EXPORT_SYMBOL(pcmcia_socket_class);
static int __init init_pcmcia_cs(void)
{
init_completion(&pcmcia_unload);
return class_register(&pcmcia_socket_class);
}
static void __exit exit_pcmcia_cs(void)
{
class_unregister(&pcmcia_socket_class);
wait_for_completion(&pcmcia_unload);
}
subsys_initcall(init_pcmcia_cs);
module_exit(exit_pcmcia_cs);

View file

@ -0,0 +1,184 @@
/*
* cs_internal.h -- definitions internal to the PCMCIA core modules
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* (C) 1999 David A. Hinds
* (C) 2003 - 2010 Dominik Brodowski
*
*
* This file contains definitions _only_ needed by the PCMCIA core modules.
* It must not be included by PCMCIA socket drivers or by PCMCIA device
* drivers.
*/
#ifndef _LINUX_CS_INTERNAL_H
#define _LINUX_CS_INTERNAL_H
#include <linux/kref.h>
/* Flags in client state */
#define CLIENT_WIN_REQ(i) (0x1<<(i))
/* Flag to access all functions */
#define BIND_FN_ALL 0xff
/* Each card function gets one of these guys */
typedef struct config_t {
struct kref ref;
unsigned int state;
struct resource io[MAX_IO_WIN]; /* io ports */
struct resource mem[MAX_WIN]; /* mem areas */
} config_t;
struct cis_cache_entry {
struct list_head node;
unsigned int addr;
unsigned int len;
unsigned int attr;
unsigned char cache[0];
};
struct pccard_resource_ops {
int (*validate_mem) (struct pcmcia_socket *s);
int (*find_io) (struct pcmcia_socket *s,
unsigned int attr,
unsigned int *base,
unsigned int num,
unsigned int align,
struct resource **parent);
struct resource* (*find_mem) (unsigned long base, unsigned long num,
unsigned long align, int low,
struct pcmcia_socket *s);
int (*init) (struct pcmcia_socket *s);
void (*exit) (struct pcmcia_socket *s);
};
/* Flags in config state */
#define CONFIG_LOCKED 0x01
#define CONFIG_IRQ_REQ 0x02
#define CONFIG_IO_REQ 0x04
/* Flags in socket state */
#define SOCKET_PRESENT 0x0008
#define SOCKET_INUSE 0x0010
#define SOCKET_SUSPEND 0x0080
#define SOCKET_WIN_REQ(i) (0x0100<<(i))
#define SOCKET_CARDBUS 0x8000
#define SOCKET_CARDBUS_CONFIG 0x10000
/*
* Stuff internal to module "pcmcia_rsrc":
*/
extern int static_init(struct pcmcia_socket *s);
extern struct resource *pcmcia_make_resource(unsigned long start,
unsigned long end,
int flags, const char *name);
/*
* Stuff internal to module "pcmcia_core":
*/
/* socket_sysfs.c */
extern int pccard_sysfs_add_socket(struct device *dev);
extern void pccard_sysfs_remove_socket(struct device *dev);
/* cardbus.c */
int cb_alloc(struct pcmcia_socket *s);
void cb_free(struct pcmcia_socket *s);
/*
* Stuff exported by module "pcmcia_core" to module "pcmcia"
*/
struct pcmcia_callback{
struct module *owner;
int (*add) (struct pcmcia_socket *s);
int (*remove) (struct pcmcia_socket *s);
void (*requery) (struct pcmcia_socket *s);
int (*validate) (struct pcmcia_socket *s, unsigned int *i);
int (*suspend) (struct pcmcia_socket *s);
int (*early_resume) (struct pcmcia_socket *s);
int (*resume) (struct pcmcia_socket *s);
};
/* cs.c */
extern struct rw_semaphore pcmcia_socket_list_rwsem;
extern struct list_head pcmcia_socket_list;
extern struct class pcmcia_socket_class;
int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c);
struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr);
void pcmcia_parse_uevents(struct pcmcia_socket *socket, unsigned int events);
#define PCMCIA_UEVENT_EJECT 0x0001
#define PCMCIA_UEVENT_INSERT 0x0002
#define PCMCIA_UEVENT_SUSPEND 0x0004
#define PCMCIA_UEVENT_RESUME 0x0008
#define PCMCIA_UEVENT_REQUERY 0x0010
struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt);
void pcmcia_put_socket(struct pcmcia_socket *skt);
/*
* Stuff internal to module "pcmcia".
*/
/* ds.c */
extern struct bus_type pcmcia_bus_type;
struct pcmcia_device;
/* pcmcia_resource.c */
extern int pcmcia_release_configuration(struct pcmcia_device *p_dev);
extern int pcmcia_validate_mem(struct pcmcia_socket *s);
extern struct resource *pcmcia_find_mem_region(u_long base,
u_long num,
u_long align,
int low,
struct pcmcia_socket *s);
void pcmcia_cleanup_irq(struct pcmcia_socket *s);
int pcmcia_setup_irq(struct pcmcia_device *p_dev);
/* cistpl.c */
extern struct bin_attribute pccard_cis_attr;
int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr,
u_int addr, u_int len, void *ptr);
int pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr,
u_int addr, u_int len, void *ptr);
void release_cis_mem(struct pcmcia_socket *s);
void destroy_cis_cache(struct pcmcia_socket *s);
int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function,
cisdata_t code, void *parse);
int pcmcia_replace_cis(struct pcmcia_socket *s,
const u8 *data, const size_t len);
int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *count);
int verify_cis_cache(struct pcmcia_socket *s);
int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function,
cisdata_t code, cisparse_t *parse, void *priv_data,
int (*loop_tuple) (tuple_t *tuple,
cisparse_t *parse,
void *priv_data));
int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function,
tuple_t *tuple);
int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function,
tuple_t *tuple);
int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple);
#endif /* _LINUX_CS_INTERNAL_H */

587
drivers/pcmcia/db1xxx_ss.c Normal file
View file

@ -0,0 +1,587 @@
/*
* PCMCIA socket code for the Alchemy Db1xxx/Pb1xxx boards.
*
* Copyright (c) 2009 Manuel Lauss <manuel.lauss@gmail.com>
*
*/
/* This is a fairly generic PCMCIA socket driver suitable for the
* following Alchemy Development boards:
* Db1000, Db/Pb1500, Db/Pb1100, Db/Pb1550, Db/Pb1200, Db1300
*
* The Db1000 is used as a reference: Per-socket card-, carddetect- and
* statuschange IRQs connected to SoC GPIOs, control and status register
* bits arranged in per-socket groups in an external PLD. All boards
* listed here use this layout, including bit positions and meanings.
* Of course there are exceptions in later boards:
*
* - Pb1100/Pb1500: single socket only; voltage key bits VS are
* at STATUS[5:4] (instead of STATUS[1:0]).
* - Au1200-based: additional card-eject irqs, irqs not gpios!
* - Db1300: Db1200-like, no pwr ctrl, single socket (#1).
*/
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/resource.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <pcmcia/ss.h>
#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-db1x00/bcsr.h>
#define MEM_MAP_SIZE 0x400000
#define IO_MAP_SIZE 0x1000
struct db1x_pcmcia_sock {
struct pcmcia_socket socket;
int nr; /* socket number */
void *virt_io;
phys_addr_t phys_io;
phys_addr_t phys_attr;
phys_addr_t phys_mem;
/* previous flags for set_socket() */
unsigned int old_flags;
/* interrupt sources: linux irq numbers! */
int insert_irq; /* default carddetect irq */
int stschg_irq; /* card-status-change irq */
int card_irq; /* card irq */
int eject_irq; /* db1200/pb1200 have these */
#define BOARD_TYPE_DEFAULT 0 /* most boards */
#define BOARD_TYPE_DB1200 1 /* IRQs aren't gpios */
#define BOARD_TYPE_PB1100 2 /* VS bits slightly different */
#define BOARD_TYPE_DB1300 3 /* no power control */
int board_type;
};
#define to_db1x_socket(x) container_of(x, struct db1x_pcmcia_sock, socket)
static int db1300_card_inserted(struct db1x_pcmcia_sock *sock)
{
return bcsr_read(BCSR_SIGSTAT) & (1 << 8);
}
/* DB/PB1200: check CPLD SIGSTATUS register bit 10/12 */
static int db1200_card_inserted(struct db1x_pcmcia_sock *sock)
{
unsigned short sigstat;
sigstat = bcsr_read(BCSR_SIGSTAT);
return sigstat & 1 << (8 + 2 * sock->nr);
}
/* carddetect gpio: low-active */
static int db1000_card_inserted(struct db1x_pcmcia_sock *sock)
{
return !gpio_get_value(irq_to_gpio(sock->insert_irq));
}
static int db1x_card_inserted(struct db1x_pcmcia_sock *sock)
{
switch (sock->board_type) {
case BOARD_TYPE_DB1200:
return db1200_card_inserted(sock);
case BOARD_TYPE_DB1300:
return db1300_card_inserted(sock);
default:
return db1000_card_inserted(sock);
}
}
/* STSCHG tends to bounce heavily when cards are inserted/ejected.
* To avoid this, the interrupt is normally disabled and only enabled
* after reset to a card has been de-asserted.
*/
static inline void set_stschg(struct db1x_pcmcia_sock *sock, int en)
{
if (sock->stschg_irq != -1) {
if (en)
enable_irq(sock->stschg_irq);
else
disable_irq(sock->stschg_irq);
}
}
static irqreturn_t db1000_pcmcia_cdirq(int irq, void *data)
{
struct db1x_pcmcia_sock *sock = data;
pcmcia_parse_events(&sock->socket, SS_DETECT);
return IRQ_HANDLED;
}
static irqreturn_t db1000_pcmcia_stschgirq(int irq, void *data)
{
struct db1x_pcmcia_sock *sock = data;
pcmcia_parse_events(&sock->socket, SS_STSCHG);
return IRQ_HANDLED;
}
static irqreturn_t db1200_pcmcia_cdirq(int irq, void *data)
{
struct db1x_pcmcia_sock *sock = data;
/* Db/Pb1200 have separate per-socket insertion and ejection
* interrupts which stay asserted as long as the card is
* inserted/missing. The one which caused us to be called
* needs to be disabled and the other one enabled.
*/
if (irq == sock->insert_irq) {
disable_irq_nosync(sock->insert_irq);
enable_irq(sock->eject_irq);
} else {
disable_irq_nosync(sock->eject_irq);
enable_irq(sock->insert_irq);
}
pcmcia_parse_events(&sock->socket, SS_DETECT);
return IRQ_HANDLED;
}
static int db1x_pcmcia_setup_irqs(struct db1x_pcmcia_sock *sock)
{
int ret;
if (sock->stschg_irq != -1) {
ret = request_irq(sock->stschg_irq, db1000_pcmcia_stschgirq,
0, "pcmcia_stschg", sock);
if (ret)
return ret;
}
/* Db/Pb1200 have separate per-socket insertion and ejection
* interrupts, which should show edge behaviour but don't.
* So interrupts are disabled until both insertion and
* ejection handler have been registered and the currently
* active one disabled.
*/
if ((sock->board_type == BOARD_TYPE_DB1200) ||
(sock->board_type == BOARD_TYPE_DB1300)) {
ret = request_irq(sock->insert_irq, db1200_pcmcia_cdirq,
0, "pcmcia_insert", sock);
if (ret)
goto out1;
ret = request_irq(sock->eject_irq, db1200_pcmcia_cdirq,
0, "pcmcia_eject", sock);
if (ret) {
free_irq(sock->insert_irq, sock);
goto out1;
}
/* enable the currently silent one */
if (db1x_card_inserted(sock))
enable_irq(sock->eject_irq);
else
enable_irq(sock->insert_irq);
} else {
/* all other (older) Db1x00 boards use a GPIO to show
* card detection status: use both-edge triggers.
*/
irq_set_irq_type(sock->insert_irq, IRQ_TYPE_EDGE_BOTH);
ret = request_irq(sock->insert_irq, db1000_pcmcia_cdirq,
0, "pcmcia_carddetect", sock);
if (ret)
goto out1;
}
return 0; /* all done */
out1:
if (sock->stschg_irq != -1)
free_irq(sock->stschg_irq, sock);
return ret;
}
static void db1x_pcmcia_free_irqs(struct db1x_pcmcia_sock *sock)
{
if (sock->stschg_irq != -1)
free_irq(sock->stschg_irq, sock);
free_irq(sock->insert_irq, sock);
if (sock->eject_irq != -1)
free_irq(sock->eject_irq, sock);
}
/*
* configure a PCMCIA socket on the Db1x00 series of boards (and
* compatibles).
*
* 2 external registers are involved:
* pcmcia_status (offset 0x04): bits [0:1/2:3]: read card voltage id
* pcmcia_control(offset 0x10):
* bits[0:1] set vcc for card
* bits[2:3] set vpp for card
* bit 4: enable data buffers
* bit 7: reset# for card
* add 8 for second socket.
*/
static int db1x_pcmcia_configure(struct pcmcia_socket *skt,
struct socket_state_t *state)
{
struct db1x_pcmcia_sock *sock = to_db1x_socket(skt);
unsigned short cr_clr, cr_set;
unsigned int changed;
int v, p, ret;
/* card voltage setup */
cr_clr = (0xf << (sock->nr * 8)); /* clear voltage settings */
cr_set = 0;
v = p = ret = 0;
switch (state->Vcc) {
case 50:
++v;
case 33:
++v;
case 0:
break;
default:
printk(KERN_INFO "pcmcia%d unsupported Vcc %d\n",
sock->nr, state->Vcc);
}
switch (state->Vpp) {
case 12:
++p;
case 33:
case 50:
++p;
case 0:
break;
default:
printk(KERN_INFO "pcmcia%d unsupported Vpp %d\n",
sock->nr, state->Vpp);
}
/* sanity check: Vpp must be 0, 12, or Vcc */
if (((state->Vcc == 33) && (state->Vpp == 50)) ||
((state->Vcc == 50) && (state->Vpp == 33))) {
printk(KERN_INFO "pcmcia%d bad Vcc/Vpp combo (%d %d)\n",
sock->nr, state->Vcc, state->Vpp);
v = p = 0;
ret = -EINVAL;
}
/* create new voltage code */
if (sock->board_type != BOARD_TYPE_DB1300)
cr_set |= ((v << 2) | p) << (sock->nr * 8);
changed = state->flags ^ sock->old_flags;
if (changed & SS_RESET) {
if (state->flags & SS_RESET) {
set_stschg(sock, 0);
/* assert reset, disable io buffers */
cr_clr |= (1 << (7 + (sock->nr * 8)));
cr_clr |= (1 << (4 + (sock->nr * 8)));
} else {
/* de-assert reset, enable io buffers */
cr_set |= 1 << (7 + (sock->nr * 8));
cr_set |= 1 << (4 + (sock->nr * 8));
}
}
/* update PCMCIA configuration */
bcsr_mod(BCSR_PCMCIA, cr_clr, cr_set);
sock->old_flags = state->flags;
/* reset was taken away: give card time to initialize properly */
if ((changed & SS_RESET) && !(state->flags & SS_RESET)) {
msleep(500);
set_stschg(sock, 1);
}
return ret;
}
/* VCC bits at [3:2]/[11:10] */
#define GET_VCC(cr, socknr) \
((((cr) >> 2) >> ((socknr) * 8)) & 3)
/* VS bits at [0:1]/[3:2] */
#define GET_VS(sr, socknr) \
(((sr) >> (2 * (socknr))) & 3)
/* reset bits at [7]/[15] */
#define GET_RESET(cr, socknr) \
((cr) & (1 << (7 + (8 * (socknr)))))
static int db1x_pcmcia_get_status(struct pcmcia_socket *skt,
unsigned int *value)
{
struct db1x_pcmcia_sock *sock = to_db1x_socket(skt);
unsigned short cr, sr;
unsigned int status;
status = db1x_card_inserted(sock) ? SS_DETECT : 0;
cr = bcsr_read(BCSR_PCMCIA);
sr = bcsr_read(BCSR_STATUS);
/* PB1100/PB1500: voltage key bits are at [5:4] */
if (sock->board_type == BOARD_TYPE_PB1100)
sr >>= 4;
/* determine card type */
switch (GET_VS(sr, sock->nr)) {
case 0:
case 2:
status |= SS_3VCARD; /* 3V card */
case 3:
break; /* 5V card: set nothing */
default:
status |= SS_XVCARD; /* treated as unsupported in core */
}
/* if Vcc is not zero, we have applied power to a card */
status |= GET_VCC(cr, sock->nr) ? SS_POWERON : 0;
/* DB1300: power always on, but don't tell when no card present */
if ((sock->board_type == BOARD_TYPE_DB1300) && (status & SS_DETECT))
status = SS_POWERON | SS_3VCARD | SS_DETECT;
/* reset de-asserted? then we're ready */
status |= (GET_RESET(cr, sock->nr)) ? SS_READY : SS_RESET;
*value = status;
return 0;
}
static int db1x_pcmcia_sock_init(struct pcmcia_socket *skt)
{
return 0;
}
static int db1x_pcmcia_sock_suspend(struct pcmcia_socket *skt)
{
return 0;
}
static int au1x00_pcmcia_set_io_map(struct pcmcia_socket *skt,
struct pccard_io_map *map)
{
struct db1x_pcmcia_sock *sock = to_db1x_socket(skt);
map->start = (u32)sock->virt_io;
map->stop = map->start + IO_MAP_SIZE;
return 0;
}
static int au1x00_pcmcia_set_mem_map(struct pcmcia_socket *skt,
struct pccard_mem_map *map)
{
struct db1x_pcmcia_sock *sock = to_db1x_socket(skt);
if (map->flags & MAP_ATTRIB)
map->static_start = sock->phys_attr + map->card_start;
else
map->static_start = sock->phys_mem + map->card_start;
return 0;
}
static struct pccard_operations db1x_pcmcia_operations = {
.init = db1x_pcmcia_sock_init,
.suspend = db1x_pcmcia_sock_suspend,
.get_status = db1x_pcmcia_get_status,
.set_socket = db1x_pcmcia_configure,
.set_io_map = au1x00_pcmcia_set_io_map,
.set_mem_map = au1x00_pcmcia_set_mem_map,
};
static int db1x_pcmcia_socket_probe(struct platform_device *pdev)
{
struct db1x_pcmcia_sock *sock;
struct resource *r;
int ret, bid;
sock = kzalloc(sizeof(struct db1x_pcmcia_sock), GFP_KERNEL);
if (!sock)
return -ENOMEM;
sock->nr = pdev->id;
bid = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI));
switch (bid) {
case BCSR_WHOAMI_PB1500:
case BCSR_WHOAMI_PB1500R2:
case BCSR_WHOAMI_PB1100:
sock->board_type = BOARD_TYPE_PB1100;
break;
case BCSR_WHOAMI_DB1000 ... BCSR_WHOAMI_PB1550_SDR:
sock->board_type = BOARD_TYPE_DEFAULT;
break;
case BCSR_WHOAMI_PB1200 ... BCSR_WHOAMI_DB1200:
sock->board_type = BOARD_TYPE_DB1200;
break;
case BCSR_WHOAMI_DB1300:
sock->board_type = BOARD_TYPE_DB1300;
break;
default:
printk(KERN_INFO "db1xxx-ss: unknown board %d!\n", bid);
ret = -ENODEV;
goto out0;
};
/*
* gather resources necessary and optional nice-to-haves to
* operate a socket:
* This includes IRQs for Carddetection/ejection, the card
* itself and optional status change detection.
* Also, the memory areas covered by a socket. For these
* we require the real 36bit addresses (see the au1000.h
* header for more information).
*/
/* card: irq assigned to the card itself. */
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "card");
sock->card_irq = r ? r->start : 0;
/* insert: irq which triggers on card insertion/ejection */
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "insert");
sock->insert_irq = r ? r->start : -1;
/* stschg: irq which trigger on card status change (optional) */
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "stschg");
sock->stschg_irq = r ? r->start : -1;
/* eject: irq which triggers on ejection (DB1200/PB1200 only) */
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "eject");
sock->eject_irq = r ? r->start : -1;
ret = -ENODEV;
/* 36bit PCMCIA Attribute area address */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr");
if (!r) {
printk(KERN_ERR "pcmcia%d has no 'pseudo-attr' resource!\n",
sock->nr);
goto out0;
}
sock->phys_attr = r->start;
/* 36bit PCMCIA Memory area address */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem");
if (!r) {
printk(KERN_ERR "pcmcia%d has no 'pseudo-mem' resource!\n",
sock->nr);
goto out0;
}
sock->phys_mem = r->start;
/* 36bit PCMCIA IO area address */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io");
if (!r) {
printk(KERN_ERR "pcmcia%d has no 'pseudo-io' resource!\n",
sock->nr);
goto out0;
}
sock->phys_io = r->start;
/*
* PCMCIA client drivers use the inb/outb macros to access
* the IO registers. Since mips_io_port_base is added
* to the access address of the mips implementation of
* inb/outb, we need to subtract it here because we want
* to access the I/O or MEM address directly, without
* going through this "mips_io_port_base" mechanism.
*/
sock->virt_io = (void *)(ioremap(sock->phys_io, IO_MAP_SIZE) -
mips_io_port_base);
if (!sock->virt_io) {
printk(KERN_ERR "pcmcia%d: cannot remap IO area\n",
sock->nr);
ret = -ENOMEM;
goto out0;
}
sock->socket.ops = &db1x_pcmcia_operations;
sock->socket.owner = THIS_MODULE;
sock->socket.pci_irq = sock->card_irq;
sock->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
sock->socket.map_size = MEM_MAP_SIZE;
sock->socket.io_offset = (unsigned long)sock->virt_io;
sock->socket.dev.parent = &pdev->dev;
sock->socket.resource_ops = &pccard_static_ops;
platform_set_drvdata(pdev, sock);
ret = db1x_pcmcia_setup_irqs(sock);
if (ret) {
printk(KERN_ERR "pcmcia%d cannot setup interrupts\n",
sock->nr);
goto out1;
}
set_stschg(sock, 0);
ret = pcmcia_register_socket(&sock->socket);
if (ret) {
printk(KERN_ERR "pcmcia%d failed to register\n", sock->nr);
goto out2;
}
printk(KERN_INFO "Alchemy Db/Pb1xxx pcmcia%d @ io/attr/mem %09llx"
"(%p) %09llx %09llx card/insert/stschg/eject irqs @ %d "
"%d %d %d\n", sock->nr, sock->phys_io, sock->virt_io,
sock->phys_attr, sock->phys_mem, sock->card_irq,
sock->insert_irq, sock->stschg_irq, sock->eject_irq);
return 0;
out2:
db1x_pcmcia_free_irqs(sock);
out1:
iounmap((void *)(sock->virt_io + (u32)mips_io_port_base));
out0:
kfree(sock);
return ret;
}
static int db1x_pcmcia_socket_remove(struct platform_device *pdev)
{
struct db1x_pcmcia_sock *sock = platform_get_drvdata(pdev);
db1x_pcmcia_free_irqs(sock);
pcmcia_unregister_socket(&sock->socket);
iounmap((void *)(sock->virt_io + (u32)mips_io_port_base));
kfree(sock);
return 0;
}
static struct platform_driver db1x_pcmcia_socket_driver = {
.driver = {
.name = "db1xxx_pcmcia",
.owner = THIS_MODULE,
},
.probe = db1x_pcmcia_socket_probe,
.remove = db1x_pcmcia_socket_remove,
};
module_platform_driver(db1x_pcmcia_socket_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PCMCIA Socket Services for Alchemy Db/Pb1x00 boards");
MODULE_AUTHOR("Manuel Lauss");

1445
drivers/pcmcia/ds.c Normal file

File diff suppressed because it is too large Load diff

374
drivers/pcmcia/electra_cf.c Normal file
View file

@ -0,0 +1,374 @@
/*
* Copyright (C) 2007 PA Semi, Inc
*
* Maintained by: Olof Johansson <olof@lixom.net>
*
* Based on drivers/pcmcia/omap_cf.c
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <pcmcia/ss.h>
static const char driver_name[] = "electra-cf";
struct electra_cf_socket {
struct pcmcia_socket socket;
struct timer_list timer;
unsigned present:1;
unsigned active:1;
struct platform_device *ofdev;
unsigned long mem_phys;
void __iomem * mem_base;
unsigned long mem_size;
void __iomem * io_virt;
unsigned int io_base;
unsigned int io_size;
u_int irq;
struct resource iomem;
void __iomem * gpio_base;
int gpio_detect;
int gpio_vsense;
int gpio_3v;
int gpio_5v;
};
#define POLL_INTERVAL (2 * HZ)
static int electra_cf_present(struct electra_cf_socket *cf)
{
unsigned int gpio;
gpio = in_le32(cf->gpio_base+0x40);
return !(gpio & (1 << cf->gpio_detect));
}
static int electra_cf_ss_init(struct pcmcia_socket *s)
{
return 0;
}
/* the timer is primarily to kick this socket's pccardd */
static void electra_cf_timer(unsigned long _cf)
{
struct electra_cf_socket *cf = (void *) _cf;
int present = electra_cf_present(cf);
if (present != cf->present) {
cf->present = present;
pcmcia_parse_events(&cf->socket, SS_DETECT);
}
if (cf->active)
mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
}
static irqreturn_t electra_cf_irq(int irq, void *_cf)
{
electra_cf_timer((unsigned long)_cf);
return IRQ_HANDLED;
}
static int electra_cf_get_status(struct pcmcia_socket *s, u_int *sp)
{
struct electra_cf_socket *cf;
if (!sp)
return -EINVAL;
cf = container_of(s, struct electra_cf_socket, socket);
/* NOTE CF is always 3VCARD */
if (electra_cf_present(cf)) {
*sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
s->pci_irq = cf->irq;
} else
*sp = 0;
return 0;
}
static int electra_cf_set_socket(struct pcmcia_socket *sock,
struct socket_state_t *s)
{
unsigned int gpio;
unsigned int vcc;
struct electra_cf_socket *cf;
cf = container_of(sock, struct electra_cf_socket, socket);
/* "reset" means no power in our case */
vcc = (s->flags & SS_RESET) ? 0 : s->Vcc;
switch (vcc) {
case 0:
gpio = 0;
break;
case 33:
gpio = (1 << cf->gpio_3v);
break;
case 5:
gpio = (1 << cf->gpio_5v);
break;
default:
return -EINVAL;
}
gpio |= 1 << (cf->gpio_3v + 16); /* enwr */
gpio |= 1 << (cf->gpio_5v + 16); /* enwr */
out_le32(cf->gpio_base+0x90, gpio);
pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n",
driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask);
return 0;
}
static int electra_cf_set_io_map(struct pcmcia_socket *s,
struct pccard_io_map *io)
{
return 0;
}
static int electra_cf_set_mem_map(struct pcmcia_socket *s,
struct pccard_mem_map *map)
{
struct electra_cf_socket *cf;
if (map->card_start)
return -EINVAL;
cf = container_of(s, struct electra_cf_socket, socket);
map->static_start = cf->mem_phys;
map->flags &= MAP_ACTIVE|MAP_ATTRIB;
if (!(map->flags & MAP_ATTRIB))
map->static_start += 0x800;
return 0;
}
static struct pccard_operations electra_cf_ops = {
.init = electra_cf_ss_init,
.get_status = electra_cf_get_status,
.set_socket = electra_cf_set_socket,
.set_io_map = electra_cf_set_io_map,
.set_mem_map = electra_cf_set_mem_map,
};
static int electra_cf_probe(struct platform_device *ofdev)
{
struct device *device = &ofdev->dev;
struct device_node *np = ofdev->dev.of_node;
struct electra_cf_socket *cf;
struct resource mem, io;
int status;
const unsigned int *prop;
int err;
struct vm_struct *area;
err = of_address_to_resource(np, 0, &mem);
if (err)
return -EINVAL;
err = of_address_to_resource(np, 1, &io);
if (err)
return -EINVAL;
cf = kzalloc(sizeof *cf, GFP_KERNEL);
if (!cf)
return -ENOMEM;
setup_timer(&cf->timer, electra_cf_timer, (unsigned long)cf);
cf->irq = NO_IRQ;
cf->ofdev = ofdev;
cf->mem_phys = mem.start;
cf->mem_size = PAGE_ALIGN(resource_size(&mem));
cf->mem_base = ioremap(cf->mem_phys, cf->mem_size);
cf->io_size = PAGE_ALIGN(resource_size(&io));
area = __get_vm_area(cf->io_size, 0, PHB_IO_BASE, PHB_IO_END);
if (area == NULL)
return -ENOMEM;
cf->io_virt = (void __iomem *)(area->addr);
cf->gpio_base = ioremap(0xfc103000, 0x1000);
dev_set_drvdata(device, cf);
if (!cf->mem_base || !cf->io_virt || !cf->gpio_base ||
(__ioremap_at(io.start, cf->io_virt, cf->io_size,
_PAGE_NO_CACHE | _PAGE_GUARDED) == NULL)) {
dev_err(device, "can't ioremap ranges\n");
status = -ENOMEM;
goto fail1;
}
cf->io_base = (unsigned long)cf->io_virt - VMALLOC_END;
cf->iomem.start = (unsigned long)cf->mem_base;
cf->iomem.end = (unsigned long)cf->mem_base + (mem.end - mem.start);
cf->iomem.flags = IORESOURCE_MEM;
cf->irq = irq_of_parse_and_map(np, 0);
status = request_irq(cf->irq, electra_cf_irq, IRQF_SHARED,
driver_name, cf);
if (status < 0) {
dev_err(device, "request_irq failed\n");
goto fail1;
}
cf->socket.pci_irq = cf->irq;
prop = of_get_property(np, "card-detect-gpio", NULL);
if (!prop)
goto fail1;
cf->gpio_detect = *prop;
prop = of_get_property(np, "card-vsense-gpio", NULL);
if (!prop)
goto fail1;
cf->gpio_vsense = *prop;
prop = of_get_property(np, "card-3v-gpio", NULL);
if (!prop)
goto fail1;
cf->gpio_3v = *prop;
prop = of_get_property(np, "card-5v-gpio", NULL);
if (!prop)
goto fail1;
cf->gpio_5v = *prop;
cf->socket.io_offset = cf->io_base;
/* reserve chip-select regions */
if (!request_mem_region(cf->mem_phys, cf->mem_size, driver_name)) {
status = -ENXIO;
dev_err(device, "Can't claim memory region\n");
goto fail1;
}
if (!request_region(cf->io_base, cf->io_size, driver_name)) {
status = -ENXIO;
dev_err(device, "Can't claim I/O region\n");
goto fail2;
}
cf->socket.owner = THIS_MODULE;
cf->socket.dev.parent = &ofdev->dev;
cf->socket.ops = &electra_cf_ops;
cf->socket.resource_ops = &pccard_static_ops;
cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP |
SS_CAP_MEM_ALIGN;
cf->socket.map_size = 0x800;
status = pcmcia_register_socket(&cf->socket);
if (status < 0) {
dev_err(device, "pcmcia_register_socket failed\n");
goto fail3;
}
dev_info(device, "at mem 0x%lx io 0x%llx irq %d\n",
cf->mem_phys, io.start, cf->irq);
cf->active = 1;
electra_cf_timer((unsigned long)cf);
return 0;
fail3:
release_region(cf->io_base, cf->io_size);
fail2:
release_mem_region(cf->mem_phys, cf->mem_size);
fail1:
if (cf->irq != NO_IRQ)
free_irq(cf->irq, cf);
if (cf->io_virt)
__iounmap_at(cf->io_virt, cf->io_size);
if (cf->mem_base)
iounmap(cf->mem_base);
if (cf->gpio_base)
iounmap(cf->gpio_base);
device_init_wakeup(&ofdev->dev, 0);
kfree(cf);
return status;
}
static int electra_cf_remove(struct platform_device *ofdev)
{
struct device *device = &ofdev->dev;
struct electra_cf_socket *cf;
cf = dev_get_drvdata(device);
cf->active = 0;
pcmcia_unregister_socket(&cf->socket);
free_irq(cf->irq, cf);
del_timer_sync(&cf->timer);
__iounmap_at(cf->io_virt, cf->io_size);
iounmap(cf->mem_base);
iounmap(cf->gpio_base);
release_mem_region(cf->mem_phys, cf->mem_size);
release_region(cf->io_base, cf->io_size);
kfree(cf);
return 0;
}
static const struct of_device_id electra_cf_match[] = {
{
.compatible = "electra-cf",
},
{},
};
MODULE_DEVICE_TABLE(of, electra_cf_match);
static struct platform_driver electra_cf_driver = {
.driver = {
.name = driver_name,
.owner = THIS_MODULE,
.of_match_table = electra_cf_match,
},
.probe = electra_cf_probe,
.remove = electra_cf_remove,
};
module_platform_driver(electra_cf_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>");
MODULE_DESCRIPTION("PA Semi Electra CF driver");

704
drivers/pcmcia/i82092.c Normal file
View file

@ -0,0 +1,704 @@
/*
* Driver for Intel I82092AA PCI-PCMCIA bridge.
*
* (C) 2001 Red Hat, Inc.
*
* Author: Arjan Van De Ven <arjanv@redhat.com>
* Loosly based on i82365.c from the pcmcia-cs package
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <pcmcia/ss.h>
#include <asm/io.h>
#include "i82092aa.h"
#include "i82365.h"
MODULE_LICENSE("GPL");
/* PCI core routines */
static const struct pci_device_id i82092aa_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82092AA_0) },
{ }
};
MODULE_DEVICE_TABLE(pci, i82092aa_pci_ids);
static struct pci_driver i82092aa_pci_driver = {
.name = "i82092aa",
.id_table = i82092aa_pci_ids,
.probe = i82092aa_pci_probe,
.remove = i82092aa_pci_remove,
};
/* the pccard structure and its functions */
static struct pccard_operations i82092aa_operations = {
.init = i82092aa_init,
.get_status = i82092aa_get_status,
.set_socket = i82092aa_set_socket,
.set_io_map = i82092aa_set_io_map,
.set_mem_map = i82092aa_set_mem_map,
};
/* The card can do up to 4 sockets, allocate a structure for each of them */
struct socket_info {
int number;
int card_state; /* 0 = no socket,
1 = empty socket,
2 = card but not initialized,
3 = operational card */
unsigned int io_base; /* base io address of the socket */
struct pcmcia_socket socket;
struct pci_dev *dev; /* The PCI device for the socket */
};
#define MAX_SOCKETS 4
static struct socket_info sockets[MAX_SOCKETS];
static int socket_count; /* shortcut */
static int i82092aa_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
unsigned char configbyte;
int i, ret;
enter("i82092aa_pci_probe");
if ((ret = pci_enable_device(dev)))
return ret;
pci_read_config_byte(dev, 0x40, &configbyte); /* PCI Configuration Control */
switch(configbyte&6) {
case 0:
socket_count = 2;
break;
case 2:
socket_count = 1;
break;
case 4:
case 6:
socket_count = 4;
break;
default:
printk(KERN_ERR "i82092aa: Oops, you did something we didn't think of.\n");
ret = -EIO;
goto err_out_disable;
}
printk(KERN_INFO "i82092aa: configured as a %d socket device.\n", socket_count);
if (!request_region(pci_resource_start(dev, 0), 2, "i82092aa")) {
ret = -EBUSY;
goto err_out_disable;
}
for (i = 0;i<socket_count;i++) {
sockets[i].card_state = 1; /* 1 = present but empty */
sockets[i].io_base = pci_resource_start(dev, 0);
sockets[i].socket.features |= SS_CAP_PCCARD;
sockets[i].socket.map_size = 0x1000;
sockets[i].socket.irq_mask = 0;
sockets[i].socket.pci_irq = dev->irq;
sockets[i].socket.cb_dev = dev;
sockets[i].socket.owner = THIS_MODULE;
sockets[i].number = i;
if (card_present(i)) {
sockets[i].card_state = 3;
dprintk(KERN_DEBUG "i82092aa: slot %i is occupied\n",i);
} else {
dprintk(KERN_DEBUG "i82092aa: slot %i is vacant\n",i);
}
}
/* Now, specifiy that all interrupts are to be done as PCI interrupts */
configbyte = 0xFF; /* bitmask, one bit per event, 1 = PCI interrupt, 0 = ISA interrupt */
pci_write_config_byte(dev, 0x50, configbyte); /* PCI Interrupt Routing Register */
/* Register the interrupt handler */
dprintk(KERN_DEBUG "Requesting interrupt %i \n",dev->irq);
if ((ret = request_irq(dev->irq, i82092aa_interrupt, IRQF_SHARED, "i82092aa", i82092aa_interrupt))) {
printk(KERN_ERR "i82092aa: Failed to register IRQ %d, aborting\n", dev->irq);
goto err_out_free_res;
}
for (i = 0; i<socket_count; i++) {
sockets[i].socket.dev.parent = &dev->dev;
sockets[i].socket.ops = &i82092aa_operations;
sockets[i].socket.resource_ops = &pccard_nonstatic_ops;
ret = pcmcia_register_socket(&sockets[i].socket);
if (ret) {
goto err_out_free_sockets;
}
}
leave("i82092aa_pci_probe");
return 0;
err_out_free_sockets:
if (i) {
for (i--;i>=0;i--) {
pcmcia_unregister_socket(&sockets[i].socket);
}
}
free_irq(dev->irq, i82092aa_interrupt);
err_out_free_res:
release_region(pci_resource_start(dev, 0), 2);
err_out_disable:
pci_disable_device(dev);
return ret;
}
static void i82092aa_pci_remove(struct pci_dev *dev)
{
int i;
enter("i82092aa_pci_remove");
free_irq(dev->irq, i82092aa_interrupt);
for (i = 0; i < socket_count; i++)
pcmcia_unregister_socket(&sockets[i].socket);
leave("i82092aa_pci_remove");
}
static DEFINE_SPINLOCK(port_lock);
/* basic value read/write functions */
static unsigned char indirect_read(int socket, unsigned short reg)
{
unsigned short int port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock,flags);
reg += socket * 0x40;
port = sockets[socket].io_base;
outb(reg,port);
val = inb(port+1);
spin_unlock_irqrestore(&port_lock,flags);
return val;
}
#if 0
static unsigned short indirect_read16(int socket, unsigned short reg)
{
unsigned short int port;
unsigned short tmp;
unsigned long flags;
spin_lock_irqsave(&port_lock,flags);
reg = reg + socket * 0x40;
port = sockets[socket].io_base;
outb(reg,port);
tmp = inb(port+1);
reg++;
outb(reg,port);
tmp = tmp | (inb(port+1)<<8);
spin_unlock_irqrestore(&port_lock,flags);
return tmp;
}
#endif
static void indirect_write(int socket, unsigned short reg, unsigned char value)
{
unsigned short int port;
unsigned long flags;
spin_lock_irqsave(&port_lock,flags);
reg = reg + socket * 0x40;
port = sockets[socket].io_base;
outb(reg,port);
outb(value,port+1);
spin_unlock_irqrestore(&port_lock,flags);
}
static void indirect_setbit(int socket, unsigned short reg, unsigned char mask)
{
unsigned short int port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock,flags);
reg = reg + socket * 0x40;
port = sockets[socket].io_base;
outb(reg,port);
val = inb(port+1);
val |= mask;
outb(reg,port);
outb(val,port+1);
spin_unlock_irqrestore(&port_lock,flags);
}
static void indirect_resetbit(int socket, unsigned short reg, unsigned char mask)
{
unsigned short int port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock,flags);
reg = reg + socket * 0x40;
port = sockets[socket].io_base;
outb(reg,port);
val = inb(port+1);
val &= ~mask;
outb(reg,port);
outb(val,port+1);
spin_unlock_irqrestore(&port_lock,flags);
}
static void indirect_write16(int socket, unsigned short reg, unsigned short value)
{
unsigned short int port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock,flags);
reg = reg + socket * 0x40;
port = sockets[socket].io_base;
outb(reg,port);
val = value & 255;
outb(val,port+1);
reg++;
outb(reg,port);
val = value>>8;
outb(val,port+1);
spin_unlock_irqrestore(&port_lock,flags);
}
/* simple helper functions */
/* External clock time, in nanoseconds. 120 ns = 8.33 MHz */
static int cycle_time = 120;
static int to_cycles(int ns)
{
if (cycle_time!=0)
return ns/cycle_time;
else
return 0;
}
/* Interrupt handler functionality */
static irqreturn_t i82092aa_interrupt(int irq, void *dev)
{
int i;
int loopcount = 0;
int handled = 0;
unsigned int events, active=0;
/* enter("i82092aa_interrupt");*/
while (1) {
loopcount++;
if (loopcount>20) {
printk(KERN_ERR "i82092aa: infinite eventloop in interrupt \n");
break;
}
active = 0;
for (i=0;i<socket_count;i++) {
int csc;
if (sockets[i].card_state==0) /* Inactive socket, should not happen */
continue;
csc = indirect_read(i,I365_CSC); /* card status change register */
if (csc==0) /* no events on this socket */
continue;
handled = 1;
events = 0;
if (csc & I365_CSC_DETECT) {
events |= SS_DETECT;
printk("Card detected in socket %i!\n",i);
}
if (indirect_read(i,I365_INTCTL) & I365_PC_IOCARD) {
/* For IO/CARDS, bit 0 means "read the card" */
events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0;
} else {
/* Check for battery/ready events */
events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0;
events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0;
events |= (csc & I365_CSC_READY) ? SS_READY : 0;
}
if (events) {
pcmcia_parse_events(&sockets[i].socket, events);
}
active |= events;
}
if (active==0) /* no more events to handle */
break;
}
return IRQ_RETVAL(handled);
/* leave("i82092aa_interrupt");*/
}
/* socket functions */
static int card_present(int socketno)
{
unsigned int val;
enter("card_present");
if ((socketno<0) || (socketno >= MAX_SOCKETS))
return 0;
if (sockets[socketno].io_base == 0)
return 0;
val = indirect_read(socketno, 1); /* Interface status register */
if ((val&12)==12) {
leave("card_present 1");
return 1;
}
leave("card_present 0");
return 0;
}
static void set_bridge_state(int sock)
{
enter("set_bridge_state");
indirect_write(sock, I365_GBLCTL,0x00);
indirect_write(sock, I365_GENCTL,0x00);
indirect_setbit(sock, I365_INTCTL,0x08);
leave("set_bridge_state");
}
static int i82092aa_init(struct pcmcia_socket *sock)
{
int i;
struct resource res = { .start = 0, .end = 0x0fff };
pccard_io_map io = { 0, 0, 0, 0, 1 };
pccard_mem_map mem = { .res = &res, };
enter("i82092aa_init");
for (i = 0; i < 2; i++) {
io.map = i;
i82092aa_set_io_map(sock, &io);
}
for (i = 0; i < 5; i++) {
mem.map = i;
i82092aa_set_mem_map(sock, &mem);
}
leave("i82092aa_init");
return 0;
}
static int i82092aa_get_status(struct pcmcia_socket *socket, u_int *value)
{
unsigned int sock = container_of(socket, struct socket_info, socket)->number;
unsigned int status;
enter("i82092aa_get_status");
status = indirect_read(sock,I365_STATUS); /* Interface Status Register */
*value = 0;
if ((status & I365_CS_DETECT) == I365_CS_DETECT) {
*value |= SS_DETECT;
}
/* IO cards have a different meaning of bits 0,1 */
/* Also notice the inverse-logic on the bits */
if (indirect_read(sock, I365_INTCTL) & I365_PC_IOCARD) {
/* IO card */
if (!(status & I365_CS_STSCHG))
*value |= SS_STSCHG;
} else { /* non I/O card */
if (!(status & I365_CS_BVD1))
*value |= SS_BATDEAD;
if (!(status & I365_CS_BVD2))
*value |= SS_BATWARN;
}
if (status & I365_CS_WRPROT)
(*value) |= SS_WRPROT; /* card is write protected */
if (status & I365_CS_READY)
(*value) |= SS_READY; /* card is not busy */
if (status & I365_CS_POWERON)
(*value) |= SS_POWERON; /* power is applied to the card */
leave("i82092aa_get_status");
return 0;
}
static int i82092aa_set_socket(struct pcmcia_socket *socket, socket_state_t *state)
{
unsigned int sock = container_of(socket, struct socket_info, socket)->number;
unsigned char reg;
enter("i82092aa_set_socket");
/* First, set the global controller options */
set_bridge_state(sock);
/* Values for the IGENC register */
reg = 0;
if (!(state->flags & SS_RESET)) /* The reset bit has "inverse" logic */
reg = reg | I365_PC_RESET;
if (state->flags & SS_IOCARD)
reg = reg | I365_PC_IOCARD;
indirect_write(sock,I365_INTCTL,reg); /* IGENC, Interrupt and General Control Register */
/* Power registers */
reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */
if (state->flags & SS_PWR_AUTO) {
printk("Auto power\n");
reg |= I365_PWR_AUTO; /* automatic power mngmnt */
}
if (state->flags & SS_OUTPUT_ENA) {
printk("Power Enabled \n");
reg |= I365_PWR_OUT; /* enable power */
}
switch (state->Vcc) {
case 0:
break;
case 50:
printk("setting voltage to Vcc to 5V on socket %i\n",sock);
reg |= I365_VCC_5V;
break;
default:
printk("i82092aa: i82092aa_set_socket called with invalid VCC power value: %i ", state->Vcc);
leave("i82092aa_set_socket");
return -EINVAL;
}
switch (state->Vpp) {
case 0:
printk("not setting Vpp on socket %i\n",sock);
break;
case 50:
printk("setting Vpp to 5.0 for socket %i\n",sock);
reg |= I365_VPP1_5V | I365_VPP2_5V;
break;
case 120:
printk("setting Vpp to 12.0\n");
reg |= I365_VPP1_12V | I365_VPP2_12V;
break;
default:
printk("i82092aa: i82092aa_set_socket called with invalid VPP power value: %i ", state->Vcc);
leave("i82092aa_set_socket");
return -EINVAL;
}
if (reg != indirect_read(sock,I365_POWER)) /* only write if changed */
indirect_write(sock,I365_POWER,reg);
/* Enable specific interrupt events */
reg = 0x00;
if (state->csc_mask & SS_DETECT) {
reg |= I365_CSC_DETECT;
}
if (state->flags & SS_IOCARD) {
if (state->csc_mask & SS_STSCHG)
reg |= I365_CSC_STSCHG;
} else {
if (state->csc_mask & SS_BATDEAD)
reg |= I365_CSC_BVD1;
if (state->csc_mask & SS_BATWARN)
reg |= I365_CSC_BVD2;
if (state->csc_mask & SS_READY)
reg |= I365_CSC_READY;
}
/* now write the value and clear the (probably bogus) pending stuff by doing a dummy read*/
indirect_write(sock,I365_CSCINT,reg);
(void)indirect_read(sock,I365_CSC);
leave("i82092aa_set_socket");
return 0;
}
static int i82092aa_set_io_map(struct pcmcia_socket *socket, struct pccard_io_map *io)
{
unsigned int sock = container_of(socket, struct socket_info, socket)->number;
unsigned char map, ioctl;
enter("i82092aa_set_io_map");
map = io->map;
/* Check error conditions */
if (map > 1) {
leave("i82092aa_set_io_map with invalid map");
return -EINVAL;
}
if ((io->start > 0xffff) || (io->stop > 0xffff) || (io->stop < io->start)){
leave("i82092aa_set_io_map with invalid io");
return -EINVAL;
}
/* Turn off the window before changing anything */
if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_IO(map))
indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_IO(map));
/* printk("set_io_map: Setting range to %x - %x \n",io->start,io->stop); */
/* write the new values */
indirect_write16(sock,I365_IO(map)+I365_W_START,io->start);
indirect_write16(sock,I365_IO(map)+I365_W_STOP,io->stop);
ioctl = indirect_read(sock,I365_IOCTL) & ~I365_IOCTL_MASK(map);
if (io->flags & (MAP_16BIT|MAP_AUTOSZ))
ioctl |= I365_IOCTL_16BIT(map);
indirect_write(sock,I365_IOCTL,ioctl);
/* Turn the window back on if needed */
if (io->flags & MAP_ACTIVE)
indirect_setbit(sock,I365_ADDRWIN,I365_ENA_IO(map));
leave("i82092aa_set_io_map");
return 0;
}
static int i82092aa_set_mem_map(struct pcmcia_socket *socket, struct pccard_mem_map *mem)
{
struct socket_info *sock_info = container_of(socket, struct socket_info, socket);
unsigned int sock = sock_info->number;
struct pci_bus_region region;
unsigned short base, i;
unsigned char map;
enter("i82092aa_set_mem_map");
pcibios_resource_to_bus(sock_info->dev->bus, &region, mem->res);
map = mem->map;
if (map > 4) {
leave("i82092aa_set_mem_map: invalid map");
return -EINVAL;
}
if ( (mem->card_start > 0x3ffffff) || (region.start > region.end) ||
(mem->speed > 1000) ) {
leave("i82092aa_set_mem_map: invalid address / speed");
printk("invalid mem map for socket %i: %llx to %llx with a "
"start of %x\n",
sock,
(unsigned long long)region.start,
(unsigned long long)region.end,
mem->card_start);
return -EINVAL;
}
/* Turn off the window before changing anything */
if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_MEM(map))
indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_MEM(map));
/* printk("set_mem_map: Setting map %i range to %x - %x on socket %i, speed is %i, active = %i \n",map, region.start,region.end,sock,mem->speed,mem->flags & MAP_ACTIVE); */
/* write the start address */
base = I365_MEM(map);
i = (region.start >> 12) & 0x0fff;
if (mem->flags & MAP_16BIT)
i |= I365_MEM_16BIT;
if (mem->flags & MAP_0WS)
i |= I365_MEM_0WS;
indirect_write16(sock,base+I365_W_START,i);
/* write the stop address */
i= (region.end >> 12) & 0x0fff;
switch (to_cycles(mem->speed)) {
case 0:
break;
case 1:
i |= I365_MEM_WS0;
break;
case 2:
i |= I365_MEM_WS1;
break;
default:
i |= I365_MEM_WS1 | I365_MEM_WS0;
break;
}
indirect_write16(sock,base+I365_W_STOP,i);
/* card start */
i = ((mem->card_start - region.start) >> 12) & 0x3fff;
if (mem->flags & MAP_WRPROT)
i |= I365_MEM_WRPROT;
if (mem->flags & MAP_ATTRIB) {
/* printk("requesting attribute memory for socket %i\n",sock);*/
i |= I365_MEM_REG;
} else {
/* printk("requesting normal memory for socket %i\n",sock);*/
}
indirect_write16(sock,base+I365_W_OFF,i);
/* Enable the window if necessary */
if (mem->flags & MAP_ACTIVE)
indirect_setbit(sock, I365_ADDRWIN, I365_ENA_MEM(map));
leave("i82092aa_set_mem_map");
return 0;
}
static int i82092aa_module_init(void)
{
return pci_register_driver(&i82092aa_pci_driver);
}
static void i82092aa_module_exit(void)
{
enter("i82092aa_module_exit");
pci_unregister_driver(&i82092aa_pci_driver);
if (sockets[0].io_base>0)
release_region(sockets[0].io_base, 2);
leave("i82092aa_module_exit");
}
module_init(i82092aa_module_init);
module_exit(i82092aa_module_exit);

36
drivers/pcmcia/i82092aa.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef _INCLUDE_GUARD_i82092aa_H_
#define _INCLUDE_GUARD_i82092aa_H_
#include <linux/interrupt.h>
/* Debuging defines */
#ifdef NOTRACE
#define enter(x) printk("Enter: %s, %s line %i\n",x,__FILE__,__LINE__)
#define leave(x) printk("Leave: %s, %s line %i\n",x,__FILE__,__LINE__)
#define dprintk(fmt, args...) printk(fmt , ## args)
#else
#define enter(x) do {} while (0)
#define leave(x) do {} while (0)
#define dprintk(fmt, args...) do {} while (0)
#endif
/* prototypes */
static int i82092aa_pci_probe(struct pci_dev *dev, const struct pci_device_id *id);
static void i82092aa_pci_remove(struct pci_dev *dev);
static int card_present(int socketno);
static irqreturn_t i82092aa_interrupt(int irq, void *dev);
static int i82092aa_get_status(struct pcmcia_socket *socket, u_int *value);
static int i82092aa_set_socket(struct pcmcia_socket *socket, socket_state_t *state);
static int i82092aa_set_io_map(struct pcmcia_socket *socket, struct pccard_io_map *io);
static int i82092aa_set_mem_map(struct pcmcia_socket *socket, struct pccard_mem_map *mem);
static int i82092aa_init(struct pcmcia_socket *socket);
#endif

1356
drivers/pcmcia/i82365.c Normal file

File diff suppressed because it is too large Load diff

136
drivers/pcmcia/i82365.h Normal file
View file

@ -0,0 +1,136 @@
/*
* i82365.h 1.15 1999/10/25 20:03:34
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License
* at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and
* limitations under the License.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in which
* case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use
* your version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the MPL or the GPL.
*/
#ifndef _LINUX_I82365_H
#define _LINUX_I82365_H
/* register definitions for the Intel 82365SL PCMCIA controller */
/* Offsets for PCIC registers */
#define I365_IDENT 0x00 /* Identification and revision */
#define I365_STATUS 0x01 /* Interface status */
#define I365_POWER 0x02 /* Power and RESETDRV control */
#define I365_INTCTL 0x03 /* Interrupt and general control */
#define I365_CSC 0x04 /* Card status change */
#define I365_CSCINT 0x05 /* Card status change interrupt control */
#define I365_ADDRWIN 0x06 /* Address window enable */
#define I365_IOCTL 0x07 /* I/O control */
#define I365_GENCTL 0x16 /* Card detect and general control */
#define I365_GBLCTL 0x1E /* Global control register */
/* Offsets for I/O and memory window registers */
#define I365_IO(map) (0x08+((map)<<2))
#define I365_MEM(map) (0x10+((map)<<3))
#define I365_W_START 0
#define I365_W_STOP 2
#define I365_W_OFF 4
/* Flags for I365_STATUS */
#define I365_CS_BVD1 0x01
#define I365_CS_STSCHG 0x01
#define I365_CS_BVD2 0x02
#define I365_CS_SPKR 0x02
#define I365_CS_DETECT 0x0C
#define I365_CS_WRPROT 0x10
#define I365_CS_READY 0x20 /* Inverted */
#define I365_CS_POWERON 0x40
#define I365_CS_GPI 0x80
/* Flags for I365_POWER */
#define I365_PWR_OFF 0x00 /* Turn off the socket */
#define I365_PWR_OUT 0x80 /* Output enable */
#define I365_PWR_NORESET 0x40 /* Disable RESETDRV on resume */
#define I365_PWR_AUTO 0x20 /* Auto pwr switch enable */
#define I365_VCC_MASK 0x18 /* Mask for turning off Vcc */
/* There are different layouts for B-step and DF-step chips: the B
step has independent Vpp1/Vpp2 control, and the DF step has only
Vpp1 control, plus 3V control */
#define I365_VCC_5V 0x10 /* Vcc = 5.0v */
#define I365_VCC_3V 0x18 /* Vcc = 3.3v */
#define I365_VPP2_MASK 0x0c /* Mask for turning off Vpp2 */
#define I365_VPP2_5V 0x04 /* Vpp2 = 5.0v */
#define I365_VPP2_12V 0x08 /* Vpp2 = 12.0v */
#define I365_VPP1_MASK 0x03 /* Mask for turning off Vpp1 */
#define I365_VPP1_5V 0x01 /* Vpp1 = 5.0v */
#define I365_VPP1_12V 0x02 /* Vpp1 = 12.0v */
/* Flags for I365_INTCTL */
#define I365_RING_ENA 0x80
#define I365_PC_RESET 0x40
#define I365_PC_IOCARD 0x20
#define I365_INTR_ENA 0x10
#define I365_IRQ_MASK 0x0F
/* Flags for I365_CSC and I365_CSCINT*/
#define I365_CSC_BVD1 0x01
#define I365_CSC_STSCHG 0x01
#define I365_CSC_BVD2 0x02
#define I365_CSC_READY 0x04
#define I365_CSC_DETECT 0x08
#define I365_CSC_ANY 0x0F
#define I365_CSC_GPI 0x10
#define I365_CSC_IRQ_MASK 0xF0
/* Flags for I365_ADDRWIN */
#define I365_ENA_IO(map) (0x40 << (map))
#define I365_ENA_MEM(map) (0x01 << (map))
/* Flags for I365_IOCTL */
#define I365_IOCTL_MASK(map) (0x0F << (map<<2))
#define I365_IOCTL_WAIT(map) (0x08 << (map<<2))
#define I365_IOCTL_0WS(map) (0x04 << (map<<2))
#define I365_IOCTL_IOCS16(map) (0x02 << (map<<2))
#define I365_IOCTL_16BIT(map) (0x01 << (map<<2))
/* Flags for I365_GENCTL */
#define I365_CTL_16DELAY 0x01
#define I365_CTL_RESET 0x02
#define I365_CTL_GPI_ENA 0x04
#define I365_CTL_GPI_CTL 0x08
#define I365_CTL_RESUME 0x10
#define I365_CTL_SW_IRQ 0x20
/* Flags for I365_GBLCTL */
#define I365_GBL_PWRDOWN 0x01
#define I365_GBL_CSC_LEV 0x02
#define I365_GBL_WRBACK 0x04
#define I365_GBL_IRQ_0_LEV 0x08
#define I365_GBL_IRQ_1_LEV 0x10
/* Flags for memory window registers */
#define I365_MEM_16BIT 0x8000 /* In memory start high byte */
#define I365_MEM_0WS 0x4000
#define I365_MEM_WS1 0x8000 /* In memory stop high byte */
#define I365_MEM_WS0 0x4000
#define I365_MEM_WRPROT 0x8000 /* In offset high byte */
#define I365_MEM_REG 0x4000
#define I365_REG(slot, reg) (((slot) << 6) + reg)
#endif /* _LINUX_I82365_H */

797
drivers/pcmcia/m32r_cfc.c Normal file
View file

@ -0,0 +1,797 @@
/*
* drivers/pcmcia/m32r_cfc.c
*
* Device driver for the CFC functionality of M32R.
*
* Copyright (c) 2001, 2002, 2003, 2004
* Hiroyuki Kondo, Naoto Sugai, Hayato Fujiwara
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <pcmcia/ss.h>
#undef MAX_IO_WIN /* FIXME */
#define MAX_IO_WIN 1
#undef MAX_WIN /* FIXME */
#define MAX_WIN 1
#include "m32r_cfc.h"
/* Poll status interval -- 0 means default to interrupt */
static int poll_interval = 0;
typedef enum pcc_space { as_none = 0, as_comm, as_attr, as_io } pcc_as_t;
typedef struct pcc_socket {
u_short type, flags;
struct pcmcia_socket socket;
unsigned int number;
unsigned int ioaddr;
u_long mapaddr;
u_long base; /* PCC register base */
u_char cs_irq1, cs_irq2, intr;
pccard_io_map io_map[MAX_IO_WIN];
pccard_mem_map mem_map[MAX_WIN];
u_char io_win;
u_char mem_win;
pcc_as_t current_space;
u_char last_iodbex;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc;
#endif
} pcc_socket_t;
static int pcc_sockets = 0;
static pcc_socket_t socket[M32R_MAX_PCC] = {
{ 0, }, /* ... */
};
/*====================================================================*/
static unsigned int pcc_get(u_short, unsigned int);
static void pcc_set(u_short, unsigned int , unsigned int );
static DEFINE_SPINLOCK(pcc_lock);
#if !defined(CONFIG_PLAT_USRV)
static inline u_long pcc_port2addr(unsigned long port, int size) {
u_long addr = 0;
u_long odd;
if (size == 1) { /* byte access */
odd = (port&1) << 11;
port -= port & 1;
addr = CFC_IO_MAPBASE_BYTE - CFC_IOPORT_BASE + odd + port;
} else if (size == 2)
addr = CFC_IO_MAPBASE_WORD - CFC_IOPORT_BASE + port;
return addr;
}
#else /* CONFIG_PLAT_USRV */
static inline u_long pcc_port2addr(unsigned long port, int size) {
u_long odd;
u_long addr = ((port - CFC_IOPORT_BASE) & 0xf000) << 8;
if (size == 1) { /* byte access */
odd = port & 1;
port -= odd;
odd <<= 11;
addr = (addr | CFC_IO_MAPBASE_BYTE) + odd + (port & 0xfff);
} else if (size == 2) /* word access */
addr = (addr | CFC_IO_MAPBASE_WORD) + (port & 0xfff);
return addr;
}
#endif /* CONFIG_PLAT_USRV */
void pcc_ioread_byte(int sock, unsigned long port, void *buf, size_t size,
size_t nmemb, int flag)
{
u_long addr;
unsigned char *bp = (unsigned char *)buf;
unsigned long flags;
pr_debug("m32r_cfc: pcc_ioread_byte: sock=%d, port=%#lx, buf=%p, "
"size=%u, nmemb=%d, flag=%d\n",
sock, port, buf, size, nmemb, flag);
addr = pcc_port2addr(port, 1);
if (!addr) {
printk("m32r_cfc:ioread_byte null port :%#lx\n",port);
return;
}
pr_debug("m32r_cfc: pcc_ioread_byte: addr=%#lx\n", addr);
spin_lock_irqsave(&pcc_lock, flags);
/* read Byte */
while (nmemb--)
*bp++ = readb(addr);
spin_unlock_irqrestore(&pcc_lock, flags);
}
void pcc_ioread_word(int sock, unsigned long port, void *buf, size_t size,
size_t nmemb, int flag)
{
u_long addr;
unsigned short *bp = (unsigned short *)buf;
unsigned long flags;
pr_debug("m32r_cfc: pcc_ioread_word: sock=%d, port=%#lx, "
"buf=%p, size=%u, nmemb=%d, flag=%d\n",
sock, port, buf, size, nmemb, flag);
if (size != 2)
printk("m32r_cfc: ioread_word :illigal size %u : %#lx\n", size,
port);
if (size == 9)
printk("m32r_cfc: ioread_word :insw \n");
addr = pcc_port2addr(port, 2);
if (!addr) {
printk("m32r_cfc:ioread_word null port :%#lx\n",port);
return;
}
pr_debug("m32r_cfc: pcc_ioread_word: addr=%#lx\n", addr);
spin_lock_irqsave(&pcc_lock, flags);
/* read Word */
while (nmemb--)
*bp++ = readw(addr);
spin_unlock_irqrestore(&pcc_lock, flags);
}
void pcc_iowrite_byte(int sock, unsigned long port, void *buf, size_t size,
size_t nmemb, int flag)
{
u_long addr;
unsigned char *bp = (unsigned char *)buf;
unsigned long flags;
pr_debug("m32r_cfc: pcc_iowrite_byte: sock=%d, port=%#lx, "
"buf=%p, size=%u, nmemb=%d, flag=%d\n",
sock, port, buf, size, nmemb, flag);
/* write Byte */
addr = pcc_port2addr(port, 1);
if (!addr) {
printk("m32r_cfc:iowrite_byte null port:%#lx\n",port);
return;
}
pr_debug("m32r_cfc: pcc_iowrite_byte: addr=%#lx\n", addr);
spin_lock_irqsave(&pcc_lock, flags);
while (nmemb--)
writeb(*bp++, addr);
spin_unlock_irqrestore(&pcc_lock, flags);
}
void pcc_iowrite_word(int sock, unsigned long port, void *buf, size_t size,
size_t nmemb, int flag)
{
u_long addr;
unsigned short *bp = (unsigned short *)buf;
unsigned long flags;
pr_debug("m32r_cfc: pcc_iowrite_word: sock=%d, port=%#lx, "
"buf=%p, size=%u, nmemb=%d, flag=%d\n",
sock, port, buf, size, nmemb, flag);
if(size != 2)
printk("m32r_cfc: iowrite_word :illigal size %u : %#lx\n",
size, port);
if(size == 9)
printk("m32r_cfc: iowrite_word :outsw \n");
addr = pcc_port2addr(port, 2);
if (!addr) {
printk("m32r_cfc:iowrite_word null addr :%#lx\n",port);
return;
}
#if 1
if (addr & 1) {
printk("m32r_cfc:iowrite_word port addr (%#lx):%#lx\n", port,
addr);
return;
}
#endif
pr_debug("m32r_cfc: pcc_iowrite_word: addr=%#lx\n", addr);
spin_lock_irqsave(&pcc_lock, flags);
while (nmemb--)
writew(*bp++, addr);
spin_unlock_irqrestore(&pcc_lock, flags);
}
/*====================================================================*/
#define IS_REGISTERED 0x2000
#define IS_ALIVE 0x8000
typedef struct pcc_t {
char *name;
u_short flags;
} pcc_t;
static pcc_t pcc[] = {
#if !defined(CONFIG_PLAT_USRV)
{ "m32r_cfc", 0 }, { "", 0 },
#else /* CONFIG_PLAT_USRV */
{ "m32r_cfc", 0 }, { "m32r_cfc", 0 }, { "m32r_cfc", 0 },
{ "m32r_cfc", 0 }, { "m32r_cfc", 0 }, { "", 0 },
#endif /* CONFIG_PLAT_USRV */
};
static irqreturn_t pcc_interrupt(int, void *);
/*====================================================================*/
static struct timer_list poll_timer;
static unsigned int pcc_get(u_short sock, unsigned int reg)
{
unsigned int val = inw(reg);
pr_debug("m32r_cfc: pcc_get: reg(0x%08x)=0x%04x\n", reg, val);
return val;
}
static void pcc_set(u_short sock, unsigned int reg, unsigned int data)
{
outw(data, reg);
pr_debug("m32r_cfc: pcc_set: reg(0x%08x)=0x%04x\n", reg, data);
}
/*======================================================================
See if a card is present, powered up, in IO mode, and already
bound to a (non PC Card) Linux driver. We leave these alone.
We make an exception for cards that seem to be serial devices.
======================================================================*/
static int __init is_alive(u_short sock)
{
unsigned int stat;
pr_debug("m32r_cfc: is_alive:\n");
printk("CF: ");
stat = pcc_get(sock, (unsigned int)PLD_CFSTS);
if (!stat)
printk("No ");
printk("Card is detected at socket %d : stat = 0x%08x\n", sock, stat);
pr_debug("m32r_cfc: is_alive: sock stat is 0x%04x\n", stat);
return 0;
}
static void add_pcc_socket(ulong base, int irq, ulong mapaddr,
unsigned int ioaddr)
{
pcc_socket_t *t = &socket[pcc_sockets];
pr_debug("m32r_cfc: add_pcc_socket: base=%#lx, irq=%d, "
"mapaddr=%#lx, ioaddr=%08x\n",
base, irq, mapaddr, ioaddr);
/* add sockets */
t->ioaddr = ioaddr;
t->mapaddr = mapaddr;
#if !defined(CONFIG_PLAT_USRV)
t->base = 0;
t->flags = 0;
t->cs_irq1 = irq; // insert irq
t->cs_irq2 = irq + 1; // eject irq
#else /* CONFIG_PLAT_USRV */
t->base = base;
t->flags = 0;
t->cs_irq1 = 0; // insert irq
t->cs_irq2 = 0; // eject irq
#endif /* CONFIG_PLAT_USRV */
if (is_alive(pcc_sockets))
t->flags |= IS_ALIVE;
/* add pcc */
#if !defined(CONFIG_PLAT_USRV)
request_region((unsigned int)PLD_CFRSTCR, 0x20, "m32r_cfc");
#else /* CONFIG_PLAT_USRV */
{
unsigned int reg_base;
reg_base = (unsigned int)PLD_CFRSTCR;
reg_base |= pcc_sockets << 8;
request_region(reg_base, 0x20, "m32r_cfc");
}
#endif /* CONFIG_PLAT_USRV */
printk(KERN_INFO " %s ", pcc[pcc_sockets].name);
printk("pcc at 0x%08lx\n", t->base);
/* Update socket interrupt information, capabilities */
t->socket.features |= (SS_CAP_PCCARD | SS_CAP_STATIC_MAP);
t->socket.map_size = M32R_PCC_MAPSIZE;
t->socket.io_offset = ioaddr; /* use for io access offset */
t->socket.irq_mask = 0;
#if !defined(CONFIG_PLAT_USRV)
t->socket.pci_irq = PLD_IRQ_CFIREQ ; /* card interrupt */
#else /* CONFIG_PLAT_USRV */
t->socket.pci_irq = PLD_IRQ_CF0 + pcc_sockets;
#endif /* CONFIG_PLAT_USRV */
#ifndef CONFIG_PLAT_USRV
/* insert interrupt */
request_irq(irq, pcc_interrupt, 0, "m32r_cfc", pcc_interrupt);
#ifndef CONFIG_PLAT_MAPPI3
/* eject interrupt */
request_irq(irq+1, pcc_interrupt, 0, "m32r_cfc", pcc_interrupt);
#endif
pr_debug("m32r_cfc: enable CFMSK, RDYSEL\n");
pcc_set(pcc_sockets, (unsigned int)PLD_CFIMASK, 0x01);
#endif /* CONFIG_PLAT_USRV */
#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
pcc_set(pcc_sockets, (unsigned int)PLD_CFCR1, 0x0200);
#endif
pcc_sockets++;
return;
}
/*====================================================================*/
static irqreturn_t pcc_interrupt(int irq, void *dev)
{
int i;
u_int events = 0;
int handled = 0;
pr_debug("m32r_cfc: pcc_interrupt: irq=%d, dev=%p\n", irq, dev);
for (i = 0; i < pcc_sockets; i++) {
if (socket[i].cs_irq1 != irq && socket[i].cs_irq2 != irq)
continue;
handled = 1;
pr_debug("m32r_cfc: pcc_interrupt: socket %d irq 0x%02x ",
i, irq);
events |= SS_DETECT; /* insert or eject */
if (events)
pcmcia_parse_events(&socket[i].socket, events);
}
pr_debug("m32r_cfc: pcc_interrupt: done\n");
return IRQ_RETVAL(handled);
} /* pcc_interrupt */
static void pcc_interrupt_wrapper(u_long data)
{
pr_debug("m32r_cfc: pcc_interrupt_wrapper:\n");
pcc_interrupt(0, NULL);
init_timer(&poll_timer);
poll_timer.expires = jiffies + poll_interval;
add_timer(&poll_timer);
}
/*====================================================================*/
static int _pcc_get_status(u_short sock, u_int *value)
{
u_int status;
pr_debug("m32r_cfc: _pcc_get_status:\n");
status = pcc_get(sock, (unsigned int)PLD_CFSTS);
*value = (status) ? SS_DETECT : 0;
pr_debug("m32r_cfc: _pcc_get_status: status=0x%08x\n", status);
#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
if ( status ) {
/* enable CF power */
status = inw((unsigned int)PLD_CPCR);
if (!(status & PLD_CPCR_CF)) {
pr_debug("m32r_cfc: _pcc_get_status: "
"power on (CPCR=0x%08x)\n", status);
status |= PLD_CPCR_CF;
outw(status, (unsigned int)PLD_CPCR);
udelay(100);
}
*value |= SS_POWERON;
pcc_set(sock, (unsigned int)PLD_CFBUFCR,0);/* enable buffer */
udelay(100);
*value |= SS_READY; /* always ready */
*value |= SS_3VCARD;
} else {
/* disable CF power */
status = inw((unsigned int)PLD_CPCR);
status &= ~PLD_CPCR_CF;
outw(status, (unsigned int)PLD_CPCR);
udelay(100);
pr_debug("m32r_cfc: _pcc_get_status: "
"power off (CPCR=0x%08x)\n", status);
}
#elif defined(CONFIG_PLAT_MAPPI2) || defined(CONFIG_PLAT_MAPPI3)
if ( status ) {
status = pcc_get(sock, (unsigned int)PLD_CPCR);
if (status == 0) { /* power off */
pcc_set(sock, (unsigned int)PLD_CPCR, 1);
pcc_set(sock, (unsigned int)PLD_CFBUFCR,0); /* force buffer off for ZA-36 */
udelay(50);
}
*value |= SS_POWERON;
pcc_set(sock, (unsigned int)PLD_CFBUFCR,0);
udelay(50);
pcc_set(sock, (unsigned int)PLD_CFRSTCR, 0x0101);
udelay(25); /* for IDE reset */
pcc_set(sock, (unsigned int)PLD_CFRSTCR, 0x0100);
mdelay(2); /* for IDE reset */
*value |= SS_READY;
*value |= SS_3VCARD;
} else {
/* disable CF power */
pcc_set(sock, (unsigned int)PLD_CPCR, 0);
udelay(100);
pr_debug("m32r_cfc: _pcc_get_status: "
"power off (CPCR=0x%08x)\n", status);
}
#else
#error no platform configuration
#endif
pr_debug("m32r_cfc: _pcc_get_status: GetStatus(%d) = %#4.4x\n",
sock, *value);
return 0;
} /* _get_status */
/*====================================================================*/
static int _pcc_set_socket(u_short sock, socket_state_t *state)
{
pr_debug("m32r_cfc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
"io_irq %d, csc_mask %#2.2x)\n", sock, state->flags,
state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT) || defined(CONFIG_PLAT_MAPPI2) || defined(CONFIG_PLAT_MAPPI3)
if (state->Vcc) {
if ((state->Vcc != 50) && (state->Vcc != 33))
return -EINVAL;
/* accept 5V and 3.3V */
}
#endif
if (state->flags & SS_RESET) {
pr_debug(":RESET\n");
pcc_set(sock,(unsigned int)PLD_CFRSTCR,0x101);
}else{
pcc_set(sock,(unsigned int)PLD_CFRSTCR,0x100);
}
if (state->flags & SS_OUTPUT_ENA){
pr_debug(":OUTPUT_ENA\n");
/* bit clear */
pcc_set(sock,(unsigned int)PLD_CFBUFCR,0);
} else {
pcc_set(sock,(unsigned int)PLD_CFBUFCR,1);
}
if(state->flags & SS_IOCARD){
pr_debug(":IOCARD");
}
if (state->flags & SS_PWR_AUTO) {
pr_debug(":PWR_AUTO");
}
if (state->csc_mask & SS_DETECT)
pr_debug(":csc-SS_DETECT");
if (state->flags & SS_IOCARD) {
if (state->csc_mask & SS_STSCHG)
pr_debug(":STSCHG");
} else {
if (state->csc_mask & SS_BATDEAD)
pr_debug(":BATDEAD");
if (state->csc_mask & SS_BATWARN)
pr_debug(":BATWARN");
if (state->csc_mask & SS_READY)
pr_debug(":READY");
}
pr_debug("\n");
return 0;
} /* _set_socket */
/*====================================================================*/
static int _pcc_set_io_map(u_short sock, struct pccard_io_map *io)
{
u_char map;
pr_debug("m32r_cfc: SetIOMap(%d, %d, %#2.2x, %d ns, "
"%#llx-%#llx)\n", sock, io->map, io->flags,
io->speed, (unsigned long long)io->start,
(unsigned long long)io->stop);
map = io->map;
return 0;
} /* _set_io_map */
/*====================================================================*/
static int _pcc_set_mem_map(u_short sock, struct pccard_mem_map *mem)
{
u_char map = mem->map;
u_long addr;
pcc_socket_t *t = &socket[sock];
pr_debug("m32r_cfc: SetMemMap(%d, %d, %#2.2x, %d ns, "
"%#llx, %#x)\n", sock, map, mem->flags,
mem->speed, (unsigned long long)mem->static_start,
mem->card_start);
/*
* sanity check
*/
if ((map > MAX_WIN) || (mem->card_start > 0x3ffffff)){
return -EINVAL;
}
/*
* de-activate
*/
if ((mem->flags & MAP_ACTIVE) == 0) {
t->current_space = as_none;
return 0;
}
/*
* Set mode
*/
if (mem->flags & MAP_ATTRIB) {
t->current_space = as_attr;
} else {
t->current_space = as_comm;
}
/*
* Set address
*/
addr = t->mapaddr + (mem->card_start & M32R_PCC_MAPMASK);
mem->static_start = addr + mem->card_start;
return 0;
} /* _set_mem_map */
#if 0 /* driver model ordering issue */
/*======================================================================
Routines for accessing socket information and register dumps via
/proc/bus/pccard/...
======================================================================*/
static ssize_t show_info(struct class_device *class_dev, char *buf)
{
pcc_socket_t *s = container_of(class_dev, struct pcc_socket,
socket.dev);
return sprintf(buf, "type: %s\nbase addr: 0x%08lx\n",
pcc[s->type].name, s->base);
}
static ssize_t show_exca(struct class_device *class_dev, char *buf)
{
/* FIXME */
return 0;
}
static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL);
#endif
/*====================================================================*/
/* this is horribly ugly... proper locking needs to be done here at
* some time... */
#define LOCKED(x) do { \
int retval; \
unsigned long flags; \
spin_lock_irqsave(&pcc_lock, flags); \
retval = x; \
spin_unlock_irqrestore(&pcc_lock, flags); \
return retval; \
} while (0)
static int pcc_get_status(struct pcmcia_socket *s, u_int *value)
{
unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
if (socket[sock].flags & IS_ALIVE) {
dev_dbg(&s->dev, "pcc_get_status: sock(%d) -EINVAL\n", sock);
*value = 0;
return -EINVAL;
}
dev_dbg(&s->dev, "pcc_get_status: sock(%d)\n", sock);
LOCKED(_pcc_get_status(sock, value));
}
static int pcc_set_socket(struct pcmcia_socket *s, socket_state_t *state)
{
unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
if (socket[sock].flags & IS_ALIVE) {
dev_dbg(&s->dev, "pcc_set_socket: sock(%d) -EINVAL\n", sock);
return -EINVAL;
}
dev_dbg(&s->dev, "pcc_set_socket: sock(%d)\n", sock);
LOCKED(_pcc_set_socket(sock, state));
}
static int pcc_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
{
unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
if (socket[sock].flags & IS_ALIVE) {
dev_dbg(&s->dev, "pcc_set_io_map: sock(%d) -EINVAL\n", sock);
return -EINVAL;
}
dev_dbg(&s->dev, "pcc_set_io_map: sock(%d)\n", sock);
LOCKED(_pcc_set_io_map(sock, io));
}
static int pcc_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
{
unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
if (socket[sock].flags & IS_ALIVE) {
dev_dbg(&s->dev, "pcc_set_mem_map: sock(%d) -EINVAL\n", sock);
return -EINVAL;
}
dev_dbg(&s->dev, "pcc_set_mem_map: sock(%d)\n", sock);
LOCKED(_pcc_set_mem_map(sock, mem));
}
static int pcc_init(struct pcmcia_socket *s)
{
dev_dbg(&s->dev, "pcc_init()\n");
return 0;
}
static struct pccard_operations pcc_operations = {
.init = pcc_init,
.get_status = pcc_get_status,
.set_socket = pcc_set_socket,
.set_io_map = pcc_set_io_map,
.set_mem_map = pcc_set_mem_map,
};
/*====================================================================*/
static struct platform_driver pcc_driver = {
.driver = {
.name = "cfc",
.owner = THIS_MODULE,
},
};
static struct platform_device pcc_device = {
.name = "cfc",
.id = 0,
};
/*====================================================================*/
static int __init init_m32r_pcc(void)
{
int i, ret;
ret = platform_driver_register(&pcc_driver);
if (ret)
return ret;
ret = platform_device_register(&pcc_device);
if (ret){
platform_driver_unregister(&pcc_driver);
return ret;
}
#if defined(CONFIG_PLAT_MAPPI2) || defined(CONFIG_PLAT_MAPPI3)
pcc_set(0, (unsigned int)PLD_CFCR0, 0x0f0f);
pcc_set(0, (unsigned int)PLD_CFCR1, 0x0200);
#endif
pcc_sockets = 0;
#if !defined(CONFIG_PLAT_USRV)
add_pcc_socket(M32R_PCC0_BASE, PLD_IRQ_CFC_INSERT, CFC_ATTR_MAPBASE,
CFC_IOPORT_BASE);
#else /* CONFIG_PLAT_USRV */
{
ulong base, mapaddr;
unsigned int ioaddr;
for (i = 0 ; i < M32R_MAX_PCC ; i++) {
base = (ulong)PLD_CFRSTCR;
base = base | (i << 8);
ioaddr = (i + 1) << 12;
mapaddr = CFC_ATTR_MAPBASE | (i << 20);
add_pcc_socket(base, 0, mapaddr, ioaddr);
}
}
#endif /* CONFIG_PLAT_USRV */
if (pcc_sockets == 0) {
printk("socket is not found.\n");
platform_device_unregister(&pcc_device);
platform_driver_unregister(&pcc_driver);
return -ENODEV;
}
/* Set up interrupt handler(s) */
for (i = 0 ; i < pcc_sockets ; i++) {
socket[i].socket.dev.parent = &pcc_device.dev;
socket[i].socket.ops = &pcc_operations;
socket[i].socket.resource_ops = &pccard_static_ops;
socket[i].socket.owner = THIS_MODULE;
socket[i].number = i;
ret = pcmcia_register_socket(&socket[i].socket);
if (!ret)
socket[i].flags |= IS_REGISTERED;
#if 0 /* driver model ordering issue */
class_device_create_file(&socket[i].socket.dev,
&class_device_attr_info);
class_device_create_file(&socket[i].socket.dev,
&class_device_attr_exca);
#endif
}
/* Finally, schedule a polling interrupt */
if (poll_interval != 0) {
poll_timer.function = pcc_interrupt_wrapper;
poll_timer.data = 0;
init_timer(&poll_timer);
poll_timer.expires = jiffies + poll_interval;
add_timer(&poll_timer);
}
return 0;
} /* init_m32r_pcc */
static void __exit exit_m32r_pcc(void)
{
int i;
for (i = 0; i < pcc_sockets; i++)
if (socket[i].flags & IS_REGISTERED)
pcmcia_unregister_socket(&socket[i].socket);
platform_device_unregister(&pcc_device);
if (poll_interval != 0)
del_timer_sync(&poll_timer);
platform_driver_unregister(&pcc_driver);
} /* exit_m32r_pcc */
module_init(init_m32r_pcc);
module_exit(exit_m32r_pcc);
MODULE_LICENSE("Dual MPL/GPL");
/*====================================================================*/

87
drivers/pcmcia/m32r_cfc.h Normal file
View file

@ -0,0 +1,87 @@
/*
* Copyright (C) 2001 by Hiroyuki Kondo
*/
#if !defined(CONFIG_M32R_CFC_NUM)
#define M32R_MAX_PCC 2
#else
#define M32R_MAX_PCC CONFIG_M32R_CFC_NUM
#endif
/*
* M32R PC Card Controller
*/
#define M32R_PCC0_BASE 0x00ef7000
#define M32R_PCC1_BASE 0x00ef7020
/*
* Register offsets
*/
#define PCCR 0x00
#define PCADR 0x04
#define PCMOD 0x08
#define PCIRC 0x0c
#define PCCSIGCR 0x10
#define PCATCR 0x14
/*
* PCCR
*/
#define PCCR_PCEN (1UL<<(31-31))
/*
* PCIRC
*/
#define PCIRC_BWERR (1UL<<(31-7))
#define PCIRC_CDIN1 (1UL<<(31-14))
#define PCIRC_CDIN2 (1UL<<(31-15))
#define PCIRC_BEIEN (1UL<<(31-23))
#define PCIRC_CIIEN (1UL<<(31-30))
#define PCIRC_COIEN (1UL<<(31-31))
/*
* PCCSIGCR
*/
#define PCCSIGCR_SEN (1UL<<(31-3))
#define PCCSIGCR_VEN (1UL<<(31-7))
#define PCCSIGCR_CRST (1UL<<(31-15))
#define PCCSIGCR_COCR (1UL<<(31-31))
/*
*
*/
#define PCMOD_AS_ATTRIB (1UL<<(31-19))
#define PCMOD_AS_IO (1UL<<(31-18))
#define PCMOD_CBSZ (1UL<<(31-23)) /* set for 8bit */
#define PCMOD_DBEX (1UL<<(31-31)) /* set for excahnge */
/*
* M32R PCC Map addr
*/
#define M32R_PCC0_MAPBASE 0x14000000
#define M32R_PCC1_MAPBASE 0x16000000
#define M32R_PCC_MAPMAX 0x02000000
#define M32R_PCC_MAPSIZE 0x00001000 /* XXX */
#define M32R_PCC_MAPMASK (~(M32R_PCC_MAPMAX-1))
#define CFC_IOPORT_BASE 0x1000
#if defined(CONFIG_PLAT_MAPPI3)
#define CFC_ATTR_MAPBASE 0x14014000
#define CFC_IO_MAPBASE_BYTE 0xb4012000
#define CFC_IO_MAPBASE_WORD 0xb4002000
#elif !defined(CONFIG_PLAT_USRV)
#define CFC_ATTR_MAPBASE 0x0c014000
#define CFC_IO_MAPBASE_BYTE 0xac012000
#define CFC_IO_MAPBASE_WORD 0xac002000
#else
#define CFC_ATTR_MAPBASE 0x04014000
#define CFC_IO_MAPBASE_BYTE 0xa4012000
#define CFC_IO_MAPBASE_WORD 0xa4002000
#endif /* CONFIG_PLAT_USRV */

759
drivers/pcmcia/m32r_pcc.c Normal file
View file

@ -0,0 +1,759 @@
/*
* drivers/pcmcia/m32r_pcc.c
*
* Device driver for the PCMCIA functionality of M32R.
*
* Copyright (c) 2001, 2002, 2003, 2004
* Hiroyuki Kondo, Naoto Sugai, Hayato Fujiwara
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/addrspace.h>
#include <pcmcia/ss.h>
/* XXX: should be moved into asm/irq.h */
#define PCC0_IRQ 24
#define PCC1_IRQ 25
#include "m32r_pcc.h"
#define CHAOS_PCC_DEBUG
#ifdef CHAOS_PCC_DEBUG
static volatile u_short dummy_readbuf;
#endif
#define PCC_DEBUG_DBEX
/* Poll status interval -- 0 means default to interrupt */
static int poll_interval = 0;
typedef enum pcc_space { as_none = 0, as_comm, as_attr, as_io } pcc_as_t;
typedef struct pcc_socket {
u_short type, flags;
struct pcmcia_socket socket;
unsigned int number;
unsigned int ioaddr;
u_long mapaddr;
u_long base; /* PCC register base */
u_char cs_irq, intr;
pccard_io_map io_map[MAX_IO_WIN];
pccard_mem_map mem_map[MAX_WIN];
u_char io_win;
u_char mem_win;
pcc_as_t current_space;
u_char last_iodbex;
#ifdef CHAOS_PCC_DEBUG
u_char last_iosize;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc;
#endif
} pcc_socket_t;
static int pcc_sockets = 0;
static pcc_socket_t socket[M32R_MAX_PCC] = {
{ 0, }, /* ... */
};
/*====================================================================*/
static unsigned int pcc_get(u_short, unsigned int);
static void pcc_set(u_short, unsigned int , unsigned int );
static DEFINE_SPINLOCK(pcc_lock);
void pcc_iorw(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int wr, int flag)
{
u_long addr;
u_long flags;
int need_ex;
#ifdef PCC_DEBUG_DBEX
int _dbex;
#endif
pcc_socket_t *t = &socket[sock];
#ifdef CHAOS_PCC_DEBUG
int map_changed = 0;
#endif
/* Need lock ? */
spin_lock_irqsave(&pcc_lock, flags);
/*
* Check if need dbex
*/
need_ex = (size > 1 && flag == 0) ? PCMOD_DBEX : 0;
#ifdef PCC_DEBUG_DBEX
_dbex = need_ex;
need_ex = 0;
#endif
/*
* calculate access address
*/
addr = t->mapaddr + port - t->ioaddr + KSEG1; /* XXX */
/*
* Check current mapping
*/
if (t->current_space != as_io || t->last_iodbex != need_ex) {
u_long cbsz;
/*
* Disable first
*/
pcc_set(sock, PCCR, 0);
/*
* Set mode and io address
*/
cbsz = (t->flags & MAP_16BIT) ? 0 : PCMOD_CBSZ;
pcc_set(sock, PCMOD, PCMOD_AS_IO | cbsz | need_ex);
pcc_set(sock, PCADR, addr & 0x1ff00000);
/*
* Enable and read it
*/
pcc_set(sock, PCCR, 1);
#ifdef CHAOS_PCC_DEBUG
#if 0
map_changed = (t->current_space == as_attr && size == 2); /* XXX */
#else
map_changed = 1;
#endif
#endif
t->current_space = as_io;
}
/*
* access to IO space
*/
if (size == 1) {
/* Byte */
unsigned char *bp = (unsigned char *)buf;
#ifdef CHAOS_DEBUG
if (map_changed) {
dummy_readbuf = readb(addr);
}
#endif
if (wr) {
/* write Byte */
while (nmemb--) {
writeb(*bp++, addr);
}
} else {
/* read Byte */
while (nmemb--) {
*bp++ = readb(addr);
}
}
} else {
/* Word */
unsigned short *bp = (unsigned short *)buf;
#ifdef CHAOS_PCC_DEBUG
if (map_changed) {
dummy_readbuf = readw(addr);
}
#endif
if (wr) {
/* write Word */
while (nmemb--) {
#ifdef PCC_DEBUG_DBEX
if (_dbex) {
unsigned char *cp = (unsigned char *)bp;
unsigned short tmp;
tmp = cp[1] << 8 | cp[0];
writew(tmp, addr);
bp++;
} else
#endif
writew(*bp++, addr);
}
} else {
/* read Word */
while (nmemb--) {
#ifdef PCC_DEBUG_DBEX
if (_dbex) {
unsigned char *cp = (unsigned char *)bp;
unsigned short tmp;
tmp = readw(addr);
cp[0] = tmp & 0xff;
cp[1] = (tmp >> 8) & 0xff;
bp++;
} else
#endif
*bp++ = readw(addr);
}
}
}
#if 1
/* addr is no longer used */
if ((addr = pcc_get(sock, PCIRC)) & PCIRC_BWERR) {
printk("m32r_pcc: BWERR detected : port 0x%04lx : iosize %dbit\n",
port, size * 8);
pcc_set(sock, PCIRC, addr);
}
#endif
/*
* save state
*/
t->last_iosize = size;
t->last_iodbex = need_ex;
/* Need lock ? */
spin_unlock_irqrestore(&pcc_lock,flags);
return;
}
void pcc_ioread(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int flag) {
pcc_iorw(sock, port, buf, size, nmemb, 0, flag);
}
void pcc_iowrite(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int flag) {
pcc_iorw(sock, port, buf, size, nmemb, 1, flag);
}
/*====================================================================*/
#define IS_REGISTERED 0x2000
#define IS_ALIVE 0x8000
typedef struct pcc_t {
char *name;
u_short flags;
} pcc_t;
static pcc_t pcc[] = {
{ "xnux2", 0 }, { "xnux2", 0 },
};
static irqreturn_t pcc_interrupt(int, void *);
/*====================================================================*/
static struct timer_list poll_timer;
static unsigned int pcc_get(u_short sock, unsigned int reg)
{
return inl(socket[sock].base + reg);
}
static void pcc_set(u_short sock, unsigned int reg, unsigned int data)
{
outl(data, socket[sock].base + reg);
}
/*======================================================================
See if a card is present, powered up, in IO mode, and already
bound to a (non PC Card) Linux driver. We leave these alone.
We make an exception for cards that seem to be serial devices.
======================================================================*/
static int __init is_alive(u_short sock)
{
unsigned int stat;
unsigned int f;
stat = pcc_get(sock, PCIRC);
f = (stat & (PCIRC_CDIN1 | PCIRC_CDIN2)) >> 16;
if(!f){
printk("m32r_pcc: No Card is detected at socket %d : stat = 0x%08x\n",stat,sock);
return 0;
}
if(f!=3)
printk("m32r_pcc: Insertion fail (%.8x) at socket %d\n",stat,sock);
else
printk("m32r_pcc: Card is Inserted at socket %d(%.8x)\n",sock,stat);
return 0;
}
static void add_pcc_socket(ulong base, int irq, ulong mapaddr,
unsigned int ioaddr)
{
pcc_socket_t *t = &socket[pcc_sockets];
/* add sockets */
t->ioaddr = ioaddr;
t->mapaddr = mapaddr;
t->base = base;
#ifdef CHAOS_PCC_DEBUG
t->flags = MAP_16BIT;
#else
t->flags = 0;
#endif
if (is_alive(pcc_sockets))
t->flags |= IS_ALIVE;
/* add pcc */
if (t->base > 0) {
request_region(t->base, 0x20, "m32r-pcc");
}
printk(KERN_INFO " %s ", pcc[pcc_sockets].name);
printk("pcc at 0x%08lx\n", t->base);
/* Update socket interrupt information, capabilities */
t->socket.features |= (SS_CAP_PCCARD | SS_CAP_STATIC_MAP);
t->socket.map_size = M32R_PCC_MAPSIZE;
t->socket.io_offset = ioaddr; /* use for io access offset */
t->socket.irq_mask = 0;
t->socket.pci_irq = 2 + pcc_sockets; /* XXX */
request_irq(irq, pcc_interrupt, 0, "m32r-pcc", pcc_interrupt);
pcc_sockets++;
return;
}
/*====================================================================*/
static irqreturn_t pcc_interrupt(int irq, void *dev)
{
int i, j, irc;
u_int events, active;
int handled = 0;
pr_debug("m32r_pcc: pcc_interrupt(%d)\n", irq);
for (j = 0; j < 20; j++) {
active = 0;
for (i = 0; i < pcc_sockets; i++) {
if ((socket[i].cs_irq != irq) &&
(socket[i].socket.pci_irq != irq))
continue;
handled = 1;
irc = pcc_get(i, PCIRC);
irc >>=16;
pr_debug("m32r_pcc: interrupt: socket %d pcirc 0x%02x ",
i, irc);
if (!irc)
continue;
events = (irc) ? SS_DETECT : 0;
events |= (pcc_get(i,PCCR) & PCCR_PCEN) ? SS_READY : 0;
pr_debug("m32r_pcc: event 0x%02x\n", events);
if (events)
pcmcia_parse_events(&socket[i].socket, events);
active |= events;
active = 0;
}
if (!active) break;
}
if (j == 20)
printk(KERN_NOTICE "m32r-pcc: infinite loop in interrupt handler\n");
pr_debug("m32r_pcc: interrupt done\n");
return IRQ_RETVAL(handled);
} /* pcc_interrupt */
static void pcc_interrupt_wrapper(u_long data)
{
pcc_interrupt(0, NULL);
init_timer(&poll_timer);
poll_timer.expires = jiffies + poll_interval;
add_timer(&poll_timer);
}
/*====================================================================*/
static int _pcc_get_status(u_short sock, u_int *value)
{
u_int status;
status = pcc_get(sock,PCIRC);
*value = ((status & PCIRC_CDIN1) && (status & PCIRC_CDIN2))
? SS_DETECT : 0;
status = pcc_get(sock,PCCR);
#if 0
*value |= (status & PCCR_PCEN) ? SS_READY : 0;
#else
*value |= SS_READY; /* XXX: always */
#endif
status = pcc_get(sock,PCCSIGCR);
*value |= (status & PCCSIGCR_VEN) ? SS_POWERON : 0;
pr_debug("m32r_pcc: GetStatus(%d) = %#4.4x\n", sock, *value);
return 0;
} /* _get_status */
/*====================================================================*/
static int _pcc_set_socket(u_short sock, socket_state_t *state)
{
u_long reg = 0;
pr_debug("m32r_pcc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
"io_irq %d, csc_mask %#2.2x)", sock, state->flags,
state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
if (state->Vcc) {
/*
* 5V only
*/
if (state->Vcc == 50) {
reg |= PCCSIGCR_VEN;
} else {
return -EINVAL;
}
}
if (state->flags & SS_RESET) {
pr_debug("m32r_pcc: :RESET\n");
reg |= PCCSIGCR_CRST;
}
if (state->flags & SS_OUTPUT_ENA){
pr_debug("m32r_pcc: :OUTPUT_ENA\n");
/* bit clear */
} else {
reg |= PCCSIGCR_SEN;
}
pcc_set(sock,PCCSIGCR,reg);
if(state->flags & SS_IOCARD){
pr_debug("m32r_pcc: :IOCARD");
}
if (state->flags & SS_PWR_AUTO) {
pr_debug("m32r_pcc: :PWR_AUTO");
}
if (state->csc_mask & SS_DETECT)
pr_debug("m32r_pcc: :csc-SS_DETECT");
if (state->flags & SS_IOCARD) {
if (state->csc_mask & SS_STSCHG)
pr_debug("m32r_pcc: :STSCHG");
} else {
if (state->csc_mask & SS_BATDEAD)
pr_debug("m32r_pcc: :BATDEAD");
if (state->csc_mask & SS_BATWARN)
pr_debug("m32r_pcc: :BATWARN");
if (state->csc_mask & SS_READY)
pr_debug("m32r_pcc: :READY");
}
pr_debug("m32r_pcc: \n");
return 0;
} /* _set_socket */
/*====================================================================*/
static int _pcc_set_io_map(u_short sock, struct pccard_io_map *io)
{
u_char map;
pr_debug("m32r_pcc: SetIOMap(%d, %d, %#2.2x, %d ns, "
"%#llx-%#llx)\n", sock, io->map, io->flags,
io->speed, (unsigned long long)io->start,
(unsigned long long)io->stop);
map = io->map;
return 0;
} /* _set_io_map */
/*====================================================================*/
static int _pcc_set_mem_map(u_short sock, struct pccard_mem_map *mem)
{
u_char map = mem->map;
u_long mode;
u_long addr;
pcc_socket_t *t = &socket[sock];
#ifdef CHAOS_PCC_DEBUG
#if 0
pcc_as_t last = t->current_space;
#endif
#endif
pr_debug("m32r_pcc: SetMemMap(%d, %d, %#2.2x, %d ns, "
"%#llx, %#x)\n", sock, map, mem->flags,
mem->speed, (unsigned long long)mem->static_start,
mem->card_start);
/*
* sanity check
*/
if ((map > MAX_WIN) || (mem->card_start > 0x3ffffff)){
return -EINVAL;
}
/*
* de-activate
*/
if ((mem->flags & MAP_ACTIVE) == 0) {
t->current_space = as_none;
return 0;
}
/*
* Disable first
*/
pcc_set(sock, PCCR, 0);
/*
* Set mode
*/
if (mem->flags & MAP_ATTRIB) {
mode = PCMOD_AS_ATTRIB | PCMOD_CBSZ;
t->current_space = as_attr;
} else {
mode = 0; /* common memory */
t->current_space = as_comm;
}
pcc_set(sock, PCMOD, mode);
/*
* Set address
*/
addr = t->mapaddr + (mem->card_start & M32R_PCC_MAPMASK);
pcc_set(sock, PCADR, addr);
mem->static_start = addr + mem->card_start;
/*
* Enable again
*/
pcc_set(sock, PCCR, 1);
#ifdef CHAOS_PCC_DEBUG
#if 0
if (last != as_attr) {
#else
if (1) {
#endif
dummy_readbuf = *(u_char *)(addr + KSEG1);
}
#endif
return 0;
} /* _set_mem_map */
#if 0 /* driver model ordering issue */
/*======================================================================
Routines for accessing socket information and register dumps via
/proc/bus/pccard/...
======================================================================*/
static ssize_t show_info(struct class_device *class_dev, char *buf)
{
pcc_socket_t *s = container_of(class_dev, struct pcc_socket,
socket.dev);
return sprintf(buf, "type: %s\nbase addr: 0x%08lx\n",
pcc[s->type].name, s->base);
}
static ssize_t show_exca(struct class_device *class_dev, char *buf)
{
/* FIXME */
return 0;
}
static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL);
#endif
/*====================================================================*/
/* this is horribly ugly... proper locking needs to be done here at
* some time... */
#define LOCKED(x) do { \
int retval; \
unsigned long flags; \
spin_lock_irqsave(&pcc_lock, flags); \
retval = x; \
spin_unlock_irqrestore(&pcc_lock, flags); \
return retval; \
} while (0)
static int pcc_get_status(struct pcmcia_socket *s, u_int *value)
{
unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
if (socket[sock].flags & IS_ALIVE) {
*value = 0;
return -EINVAL;
}
LOCKED(_pcc_get_status(sock, value));
}
static int pcc_set_socket(struct pcmcia_socket *s, socket_state_t *state)
{
unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
if (socket[sock].flags & IS_ALIVE)
return -EINVAL;
LOCKED(_pcc_set_socket(sock, state));
}
static int pcc_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
{
unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
if (socket[sock].flags & IS_ALIVE)
return -EINVAL;
LOCKED(_pcc_set_io_map(sock, io));
}
static int pcc_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
{
unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
if (socket[sock].flags & IS_ALIVE)
return -EINVAL;
LOCKED(_pcc_set_mem_map(sock, mem));
}
static int pcc_init(struct pcmcia_socket *s)
{
pr_debug("m32r_pcc: init call\n");
return 0;
}
static struct pccard_operations pcc_operations = {
.init = pcc_init,
.get_status = pcc_get_status,
.set_socket = pcc_set_socket,
.set_io_map = pcc_set_io_map,
.set_mem_map = pcc_set_mem_map,
};
/*====================================================================*/
static struct platform_driver pcc_driver = {
.driver = {
.name = "pcc",
.owner = THIS_MODULE,
},
};
static struct platform_device pcc_device = {
.name = "pcc",
.id = 0,
};
/*====================================================================*/
static int __init init_m32r_pcc(void)
{
int i, ret;
ret = platform_driver_register(&pcc_driver);
if (ret)
return ret;
ret = platform_device_register(&pcc_device);
if (ret){
platform_driver_unregister(&pcc_driver);
return ret;
}
printk(KERN_INFO "m32r PCC probe:\n");
pcc_sockets = 0;
add_pcc_socket(M32R_PCC0_BASE, PCC0_IRQ, M32R_PCC0_MAPBASE, 0x1000);
#ifdef CONFIG_M32RPCC_SLOT2
add_pcc_socket(M32R_PCC1_BASE, PCC1_IRQ, M32R_PCC1_MAPBASE, 0x2000);
#endif
if (pcc_sockets == 0) {
printk("socket is not found.\n");
platform_device_unregister(&pcc_device);
platform_driver_unregister(&pcc_driver);
return -ENODEV;
}
/* Set up interrupt handler(s) */
for (i = 0 ; i < pcc_sockets ; i++) {
socket[i].socket.dev.parent = &pcc_device.dev;
socket[i].socket.ops = &pcc_operations;
socket[i].socket.resource_ops = &pccard_static_ops;
socket[i].socket.owner = THIS_MODULE;
socket[i].number = i;
ret = pcmcia_register_socket(&socket[i].socket);
if (!ret)
socket[i].flags |= IS_REGISTERED;
#if 0 /* driver model ordering issue */
class_device_create_file(&socket[i].socket.dev,
&class_device_attr_info);
class_device_create_file(&socket[i].socket.dev,
&class_device_attr_exca);
#endif
}
/* Finally, schedule a polling interrupt */
if (poll_interval != 0) {
poll_timer.function = pcc_interrupt_wrapper;
poll_timer.data = 0;
init_timer(&poll_timer);
poll_timer.expires = jiffies + poll_interval;
add_timer(&poll_timer);
}
return 0;
} /* init_m32r_pcc */
static void __exit exit_m32r_pcc(void)
{
int i;
for (i = 0; i < pcc_sockets; i++)
if (socket[i].flags & IS_REGISTERED)
pcmcia_unregister_socket(&socket[i].socket);
platform_device_unregister(&pcc_device);
if (poll_interval != 0)
del_timer_sync(&poll_timer);
platform_driver_unregister(&pcc_driver);
} /* exit_m32r_pcc */
module_init(init_m32r_pcc);
module_exit(exit_m32r_pcc);
MODULE_LICENSE("Dual MPL/GPL");
/*====================================================================*/

65
drivers/pcmcia/m32r_pcc.h Normal file
View file

@ -0,0 +1,65 @@
/*
* Copyright (C) 2001 by Hiroyuki Kondo
*/
#define M32R_MAX_PCC 2
/*
* M32R PC Card Controller
*/
#define M32R_PCC0_BASE 0x00ef7000
#define M32R_PCC1_BASE 0x00ef7020
/*
* Register offsets
*/
#define PCCR 0x00
#define PCADR 0x04
#define PCMOD 0x08
#define PCIRC 0x0c
#define PCCSIGCR 0x10
#define PCATCR 0x14
/*
* PCCR
*/
#define PCCR_PCEN (1UL<<(31-31))
/*
* PCIRC
*/
#define PCIRC_BWERR (1UL<<(31-7))
#define PCIRC_CDIN1 (1UL<<(31-14))
#define PCIRC_CDIN2 (1UL<<(31-15))
#define PCIRC_BEIEN (1UL<<(31-23))
#define PCIRC_CIIEN (1UL<<(31-30))
#define PCIRC_COIEN (1UL<<(31-31))
/*
* PCCSIGCR
*/
#define PCCSIGCR_SEN (1UL<<(31-3))
#define PCCSIGCR_VEN (1UL<<(31-7))
#define PCCSIGCR_CRST (1UL<<(31-15))
#define PCCSIGCR_COCR (1UL<<(31-31))
/*
*
*/
#define PCMOD_AS_ATTRIB (1UL<<(31-19))
#define PCMOD_AS_IO (1UL<<(31-18))
#define PCMOD_CBSZ (1UL<<(31-23)) /* set for 8bit */
#define PCMOD_DBEX (1UL<<(31-31)) /* set for excahnge */
/*
* M32R PCC Map addr
*/
#define M32R_PCC0_MAPBASE 0x14000000
#define M32R_PCC1_MAPBASE 0x16000000
#define M32R_PCC_MAPMAX 0x02000000
#define M32R_PCC_MAPSIZE 0x00001000 /* XXX */
#define M32R_PCC_MAPMASK (~(M32R_PCC_MAPMAX-1))

183
drivers/pcmcia/o2micro.h Normal file
View file

@ -0,0 +1,183 @@
/*
* o2micro.h 1.13 1999/10/25 20:03:34
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License
* at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and
* limitations under the License.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in which
* case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use
* your version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the MPL or the GPL.
*/
#ifndef _LINUX_O2MICRO_H
#define _LINUX_O2MICRO_H
/* Additional PCI configuration registers */
#define O2_MUX_CONTROL 0x90 /* 32 bit */
#define O2_MUX_RING_OUT 0x0000000f
#define O2_MUX_SKTB_ACTV 0x000000f0
#define O2_MUX_SCTA_ACTV_ENA 0x00000100
#define O2_MUX_SCTB_ACTV_ENA 0x00000200
#define O2_MUX_SER_IRQ_ROUTE 0x0000e000
#define O2_MUX_SER_PCI 0x00010000
#define O2_MUX_SKTA_TURBO 0x000c0000 /* for 6833, 6860 */
#define O2_MUX_SKTB_TURBO 0x00300000
#define O2_MUX_AUX_VCC_3V 0x00400000
#define O2_MUX_PCI_VCC_5V 0x00800000
#define O2_MUX_PME_MUX 0x0f000000
/* Additional ExCA registers */
#define O2_MODE_A 0x38
#define O2_MODE_A_2 0x26 /* for 6833B, 6860C */
#define O2_MODE_A_CD_PULSE 0x04
#define O2_MODE_A_SUSP_EDGE 0x08
#define O2_MODE_A_HOST_SUSP 0x10
#define O2_MODE_A_PWR_MASK 0x60
#define O2_MODE_A_QUIET 0x80
#define O2_MODE_B 0x39
#define O2_MODE_B_2 0x2e /* for 6833B, 6860C */
#define O2_MODE_B_IDENT 0x03
#define O2_MODE_B_ID_BSTEP 0x00
#define O2_MODE_B_ID_CSTEP 0x01
#define O2_MODE_B_ID_O2 0x02
#define O2_MODE_B_VS1 0x04
#define O2_MODE_B_VS2 0x08
#define O2_MODE_B_IRQ15_RI 0x80
#define O2_MODE_C 0x3a
#define O2_MODE_C_DREQ_MASK 0x03
#define O2_MODE_C_DREQ_INPACK 0x01
#define O2_MODE_C_DREQ_WP 0x02
#define O2_MODE_C_DREQ_BVD2 0x03
#define O2_MODE_C_ZVIDEO 0x08
#define O2_MODE_C_IREQ_SEL 0x30
#define O2_MODE_C_MGMT_SEL 0xc0
#define O2_MODE_D 0x3b
#define O2_MODE_D_IRQ_MODE 0x03
#define O2_MODE_D_PCI_CLKRUN 0x04
#define O2_MODE_D_CB_CLKRUN 0x08
#define O2_MODE_D_SKT_ACTV 0x20
#define O2_MODE_D_PCI_FIFO 0x40 /* for OZ6729, OZ6730 */
#define O2_MODE_D_W97_IRQ 0x40
#define O2_MODE_D_ISA_IRQ 0x80
#define O2_MHPG_DMA 0x3c
#define O2_MHPG_CHANNEL 0x07
#define O2_MHPG_CINT_ENA 0x08
#define O2_MHPG_CSC_ENA 0x10
#define O2_FIFO_ENA 0x3d
#define O2_FIFO_ZVIDEO_3 0x08
#define O2_FIFO_PCI_FIFO 0x10
#define O2_FIFO_POSTWR 0x40
#define O2_FIFO_BUFFER 0x80
#define O2_MODE_E 0x3e
#define O2_MODE_E_MHPG_DMA 0x01
#define O2_MODE_E_SPKR_OUT 0x02
#define O2_MODE_E_LED_OUT 0x08
#define O2_MODE_E_SKTA_ACTV 0x10
#define O2_RESERVED1 0x94
#define O2_RESERVED2 0xD4
#define O2_RES_READ_PREFETCH 0x02
#define O2_RES_WRITE_BURST 0x08
static int o2micro_override(struct yenta_socket *socket)
{
/*
* 'reserved' register at 0x94/D4. allows setting read prefetch and write
* bursting. read prefetching for example makes the RME Hammerfall DSP
* working. for some bridges it is at 0x94, for others at 0xD4. it's
* ok to write to both registers on all O2 bridges.
* from Eric Still, 02Micro.
*/
u8 a, b;
bool use_speedup;
if (PCI_FUNC(socket->dev->devfn) == 0) {
a = config_readb(socket, O2_RESERVED1);
b = config_readb(socket, O2_RESERVED2);
dev_dbg(&socket->dev->dev, "O2: 0x94/0xD4: %02x/%02x\n", a, b);
switch (socket->dev->device) {
/*
* older bridges have problems with both read prefetch and write
* bursting depending on the combination of the chipset, bridge
* and the cardbus card. so disable them to be on the safe side.
*/
case PCI_DEVICE_ID_O2_6729:
case PCI_DEVICE_ID_O2_6730:
case PCI_DEVICE_ID_O2_6812:
case PCI_DEVICE_ID_O2_6832:
case PCI_DEVICE_ID_O2_6836:
case PCI_DEVICE_ID_O2_6933:
use_speedup = false;
break;
default:
use_speedup = true;
break;
}
/* the user may override our decision */
if (strcasecmp(o2_speedup, "on") == 0)
use_speedup = true;
else if (strcasecmp(o2_speedup, "off") == 0)
use_speedup = false;
else if (strcasecmp(o2_speedup, "default") != 0)
dev_warn(&socket->dev->dev,
"O2: Unknown parameter, using 'default'");
if (use_speedup) {
dev_info(&socket->dev->dev,
"O2: enabling read prefetch/write burst. If you experience problems or performance issues, use the yenta_socket parameter 'o2_speedup=off'\n");
config_writeb(socket, O2_RESERVED1,
a | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST);
config_writeb(socket, O2_RESERVED2,
b | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST);
} else {
dev_info(&socket->dev->dev,
"O2: disabling read prefetch/write burst. If you experience problems or performance issues, use the yenta_socket parameter 'o2_speedup=on'\n");
config_writeb(socket, O2_RESERVED1,
a & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST));
config_writeb(socket, O2_RESERVED2,
b & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST));
}
}
return 0;
}
static void o2micro_restore_state(struct yenta_socket *socket)
{
/*
* as long as read prefetch is the only thing in
* o2micro_override, it's safe to call it from here
*/
o2micro_override(socket);
}
#endif /* _LINUX_O2MICRO_H */

360
drivers/pcmcia/omap_cf.c Normal file
View file

@ -0,0 +1,360 @@
/*
* omap_cf.c -- OMAP 16xx CompactFlash controller driver
*
* Copyright (c) 2005 David Brownell
*
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <pcmcia/ss.h>
#include <mach/hardware.h>
#include <asm/io.h>
#include <asm/sizes.h>
#include <mach/mux.h>
#include <mach/tc.h>
/* NOTE: don't expect this to support many I/O cards. The 16xx chips have
* hard-wired timings to support Compact Flash memory cards; they won't work
* with various other devices (like WLAN adapters) without some external
* logic to help out.
*
* NOTE: CF controller docs disagree with address space docs as to where
* CF_BASE really lives; this is a doc erratum.
*/
#define CF_BASE 0xfffe2800
/* status; read after IRQ */
#define CF_STATUS (CF_BASE + 0x00)
# define CF_STATUS_BAD_READ (1 << 2)
# define CF_STATUS_BAD_WRITE (1 << 1)
# define CF_STATUS_CARD_DETECT (1 << 0)
/* which chipselect (CS0..CS3) is used for CF (active low) */
#define CF_CFG (CF_BASE + 0x02)
/* card reset */
#define CF_CONTROL (CF_BASE + 0x04)
# define CF_CONTROL_RESET (1 << 0)
#define omap_cf_present() (!(omap_readw(CF_STATUS) & CF_STATUS_CARD_DETECT))
/*--------------------------------------------------------------------------*/
static const char driver_name[] = "omap_cf";
struct omap_cf_socket {
struct pcmcia_socket socket;
struct timer_list timer;
unsigned present:1;
unsigned active:1;
struct platform_device *pdev;
unsigned long phys_cf;
u_int irq;
struct resource iomem;
};
#define POLL_INTERVAL (2 * HZ)
/*--------------------------------------------------------------------------*/
static int omap_cf_ss_init(struct pcmcia_socket *s)
{
return 0;
}
/* the timer is primarily to kick this socket's pccardd */
static void omap_cf_timer(unsigned long _cf)
{
struct omap_cf_socket *cf = (void *) _cf;
unsigned present = omap_cf_present();
if (present != cf->present) {
cf->present = present;
pr_debug("%s: card %s\n", driver_name,
present ? "present" : "gone");
pcmcia_parse_events(&cf->socket, SS_DETECT);
}
if (cf->active)
mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
}
/* This irq handler prevents "irqNNN: nobody cared" messages as drivers
* claim the card's IRQ. It may also detect some card insertions, but
* not removals; it can't always eliminate timer irqs.
*/
static irqreturn_t omap_cf_irq(int irq, void *_cf)
{
omap_cf_timer((unsigned long)_cf);
return IRQ_HANDLED;
}
static int omap_cf_get_status(struct pcmcia_socket *s, u_int *sp)
{
if (!sp)
return -EINVAL;
/* NOTE CF is always 3VCARD */
if (omap_cf_present()) {
struct omap_cf_socket *cf;
*sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
cf = container_of(s, struct omap_cf_socket, socket);
s->pcmcia_irq = 0;
s->pci_irq = cf->irq;
} else
*sp = 0;
return 0;
}
static int
omap_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
{
u16 control;
/* REVISIT some non-OSK boards may support power switching */
switch (s->Vcc) {
case 0:
case 33:
break;
default:
return -EINVAL;
}
control = omap_readw(CF_CONTROL);
if (s->flags & SS_RESET)
omap_writew(CF_CONTROL_RESET, CF_CONTROL);
else
omap_writew(0, CF_CONTROL);
pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n",
driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask);
return 0;
}
static int omap_cf_ss_suspend(struct pcmcia_socket *s)
{
pr_debug("%s: %s\n", driver_name, __func__);
return omap_cf_set_socket(s, &dead_socket);
}
/* regions are 2K each: mem, attrib, io (and reserved-for-ide) */
static int
omap_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
{
struct omap_cf_socket *cf;
cf = container_of(s, struct omap_cf_socket, socket);
io->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT;
io->start = cf->phys_cf + SZ_4K;
io->stop = io->start + SZ_2K - 1;
return 0;
}
static int
omap_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map)
{
struct omap_cf_socket *cf;
if (map->card_start)
return -EINVAL;
cf = container_of(s, struct omap_cf_socket, socket);
map->static_start = cf->phys_cf;
map->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT;
if (map->flags & MAP_ATTRIB)
map->static_start += SZ_2K;
return 0;
}
static struct pccard_operations omap_cf_ops = {
.init = omap_cf_ss_init,
.suspend = omap_cf_ss_suspend,
.get_status = omap_cf_get_status,
.set_socket = omap_cf_set_socket,
.set_io_map = omap_cf_set_io_map,
.set_mem_map = omap_cf_set_mem_map,
};
/*--------------------------------------------------------------------------*/
/*
* NOTE: right now the only board-specific platform_data is
* "what chipselect is used". Boards could want more.
*/
static int __init omap_cf_probe(struct platform_device *pdev)
{
unsigned seg;
struct omap_cf_socket *cf;
int irq;
int status;
seg = (int) pdev->dev.platform_data;
if (seg == 0 || seg > 3)
return -ENODEV;
/* either CFLASH.IREQ (INT_1610_CF) or some GPIO */
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return -EINVAL;
cf = kzalloc(sizeof *cf, GFP_KERNEL);
if (!cf)
return -ENOMEM;
init_timer(&cf->timer);
cf->timer.function = omap_cf_timer;
cf->timer.data = (unsigned long) cf;
cf->pdev = pdev;
platform_set_drvdata(pdev, cf);
/* this primarily just shuts up irq handling noise */
status = request_irq(irq, omap_cf_irq, IRQF_SHARED,
driver_name, cf);
if (status < 0)
goto fail0;
cf->irq = irq;
cf->socket.pci_irq = irq;
switch (seg) {
/* NOTE: CS0 could be configured too ... */
case 1:
cf->phys_cf = OMAP_CS1_PHYS;
break;
case 2:
cf->phys_cf = OMAP_CS2_PHYS;
break;
case 3:
cf->phys_cf = omap_cs3_phys();
break;
default:
goto fail1;
}
cf->iomem.start = cf->phys_cf;
cf->iomem.end = cf->iomem.end + SZ_8K - 1;
cf->iomem.flags = IORESOURCE_MEM;
/* pcmcia layer only remaps "real" memory */
cf->socket.io_offset = (unsigned long)
ioremap(cf->phys_cf + SZ_4K, SZ_2K);
if (!cf->socket.io_offset)
goto fail1;
if (!request_mem_region(cf->phys_cf, SZ_8K, driver_name))
goto fail1;
/* NOTE: CF conflicts with MMC1 */
omap_cfg_reg(W11_1610_CF_CD1);
omap_cfg_reg(P11_1610_CF_CD2);
omap_cfg_reg(R11_1610_CF_IOIS16);
omap_cfg_reg(V10_1610_CF_IREQ);
omap_cfg_reg(W10_1610_CF_RESET);
omap_writew(~(1 << seg), CF_CFG);
pr_info("%s: cs%d on irq %d\n", driver_name, seg, irq);
/* NOTE: better EMIFS setup might support more cards; but the
* TRM only shows how to affect regular flash signals, not their
* CF/PCMCIA variants...
*/
pr_debug("%s: cs%d, previous ccs %08x acs %08x\n", driver_name,
seg, omap_readl(EMIFS_CCS(seg)), omap_readl(EMIFS_ACS(seg)));
omap_writel(0x0004a1b3, EMIFS_CCS(seg)); /* synch mode 4 etc */
omap_writel(0x00000000, EMIFS_ACS(seg)); /* OE hold/setup */
/* CF uses armxor_ck, which is "always" available */
pr_debug("%s: sts %04x cfg %04x control %04x %s\n", driver_name,
omap_readw(CF_STATUS), omap_readw(CF_CFG),
omap_readw(CF_CONTROL),
omap_cf_present() ? "present" : "(not present)");
cf->socket.owner = THIS_MODULE;
cf->socket.dev.parent = &pdev->dev;
cf->socket.ops = &omap_cf_ops;
cf->socket.resource_ops = &pccard_static_ops;
cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
| SS_CAP_MEM_ALIGN;
cf->socket.map_size = SZ_2K;
cf->socket.io[0].res = &cf->iomem;
status = pcmcia_register_socket(&cf->socket);
if (status < 0)
goto fail2;
cf->active = 1;
mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
return 0;
fail2:
release_mem_region(cf->phys_cf, SZ_8K);
fail1:
if (cf->socket.io_offset)
iounmap((void __iomem *) cf->socket.io_offset);
free_irq(irq, cf);
fail0:
kfree(cf);
return status;
}
static int __exit omap_cf_remove(struct platform_device *pdev)
{
struct omap_cf_socket *cf = platform_get_drvdata(pdev);
cf->active = 0;
pcmcia_unregister_socket(&cf->socket);
del_timer_sync(&cf->timer);
iounmap((void __iomem *) cf->socket.io_offset);
release_mem_region(cf->phys_cf, SZ_8K);
free_irq(cf->irq, cf);
kfree(cf);
return 0;
}
static struct platform_driver omap_cf_driver = {
.driver = {
.name = (char *) driver_name,
.owner = THIS_MODULE,
},
.remove = __exit_p(omap_cf_remove),
};
static int __init omap_cf_init(void)
{
if (cpu_is_omap16xx())
return platform_driver_probe(&omap_cf_driver, omap_cf_probe);
return -ENODEV;
}
static void __exit omap_cf_exit(void)
{
if (cpu_is_omap16xx())
platform_driver_unregister(&omap_cf_driver);
}
module_init(omap_cf_init);
module_exit(omap_cf_exit);
MODULE_DESCRIPTION("OMAP CF Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:omap_cf");

438
drivers/pcmcia/pcmcia_cis.c Normal file
View file

@ -0,0 +1,438 @@
/*
* PCMCIA high-level CIS access functions
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* Copyright (C) 1999 David A. Hinds
* Copyright (C) 2004-2010 Dominik Brodowski
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ss.h>
#include <pcmcia/ds.h>
#include "cs_internal.h"
/**
* pccard_read_tuple() - internal CIS tuple access
* @s: the struct pcmcia_socket where the card is inserted
* @function: the device function we loop for
* @code: which CIS code shall we look for?
* @parse: buffer where the tuple shall be parsed (or NULL, if no parse)
*
* pccard_read_tuple() reads out one tuple and attempts to parse it
*/
int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function,
cisdata_t code, void *parse)
{
tuple_t tuple;
cisdata_t *buf;
int ret;
buf = kmalloc(256, GFP_KERNEL);
if (buf == NULL) {
dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
return -ENOMEM;
}
tuple.DesiredTuple = code;
tuple.Attributes = 0;
if (function == BIND_FN_ALL)
tuple.Attributes = TUPLE_RETURN_COMMON;
ret = pccard_get_first_tuple(s, function, &tuple);
if (ret != 0)
goto done;
tuple.TupleData = buf;
tuple.TupleOffset = 0;
tuple.TupleDataMax = 255;
ret = pccard_get_tuple_data(s, &tuple);
if (ret != 0)
goto done;
ret = pcmcia_parse_tuple(&tuple, parse);
done:
kfree(buf);
return ret;
}
/**
* pccard_loop_tuple() - loop over tuples in the CIS
* @s: the struct pcmcia_socket where the card is inserted
* @function: the device function we loop for
* @code: which CIS code shall we look for?
* @parse: buffer where the tuple shall be parsed (or NULL, if no parse)
* @priv_data: private data to be passed to the loop_tuple function.
* @loop_tuple: function to call for each CIS entry of type @function. IT
* gets passed the raw tuple, the paresed tuple (if @parse is
* set) and @priv_data.
*
* pccard_loop_tuple() loops over all CIS entries of type @function, and
* calls the @loop_tuple function for each entry. If the call to @loop_tuple
* returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
*/
int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function,
cisdata_t code, cisparse_t *parse, void *priv_data,
int (*loop_tuple) (tuple_t *tuple,
cisparse_t *parse,
void *priv_data))
{
tuple_t tuple;
cisdata_t *buf;
int ret;
buf = kzalloc(256, GFP_KERNEL);
if (buf == NULL) {
dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
return -ENOMEM;
}
tuple.TupleData = buf;
tuple.TupleDataMax = 255;
tuple.TupleOffset = 0;
tuple.DesiredTuple = code;
tuple.Attributes = 0;
ret = pccard_get_first_tuple(s, function, &tuple);
while (!ret) {
if (pccard_get_tuple_data(s, &tuple))
goto next_entry;
if (parse)
if (pcmcia_parse_tuple(&tuple, parse))
goto next_entry;
ret = loop_tuple(&tuple, parse, priv_data);
if (!ret)
break;
next_entry:
ret = pccard_get_next_tuple(s, function, &tuple);
}
kfree(buf);
return ret;
}
/**
* pcmcia_io_cfg_data_width() - convert cfgtable to data path width parameter
*/
static int pcmcia_io_cfg_data_width(unsigned int flags)
{
if (!(flags & CISTPL_IO_8BIT))
return IO_DATA_PATH_WIDTH_16;
if (!(flags & CISTPL_IO_16BIT))
return IO_DATA_PATH_WIDTH_8;
return IO_DATA_PATH_WIDTH_AUTO;
}
struct pcmcia_cfg_mem {
struct pcmcia_device *p_dev;
int (*conf_check) (struct pcmcia_device *p_dev, void *priv_data);
void *priv_data;
cisparse_t parse;
cistpl_cftable_entry_t dflt;
};
/**
* pcmcia_do_loop_config() - internal helper for pcmcia_loop_config()
*
* pcmcia_do_loop_config() is the internal callback for the call from
* pcmcia_loop_config() to pccard_loop_tuple(). Data is transferred
* by a struct pcmcia_cfg_mem.
*/
static int pcmcia_do_loop_config(tuple_t *tuple, cisparse_t *parse, void *priv)
{
struct pcmcia_cfg_mem *cfg_mem = priv;
struct pcmcia_device *p_dev = cfg_mem->p_dev;
cistpl_cftable_entry_t *cfg = &parse->cftable_entry;
cistpl_cftable_entry_t *dflt = &cfg_mem->dflt;
unsigned int flags = p_dev->config_flags;
unsigned int vcc = p_dev->socket->socket.Vcc;
dev_dbg(&p_dev->dev, "testing configuration %x, autoconf %x\n",
cfg->index, flags);
/* default values */
cfg_mem->p_dev->config_index = cfg->index;
if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
cfg_mem->dflt = *cfg;
/* check for matching Vcc? */
if (flags & CONF_AUTO_CHECK_VCC) {
if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000)
return -ENODEV;
} else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) {
if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000)
return -ENODEV;
}
}
/* set Vpp? */
if (flags & CONF_AUTO_SET_VPP) {
if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
p_dev->vpp = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM))
p_dev->vpp =
dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000;
}
/* enable audio? */
if ((flags & CONF_AUTO_AUDIO) && (cfg->flags & CISTPL_CFTABLE_AUDIO))
p_dev->config_flags |= CONF_ENABLE_SPKR;
/* IO window settings? */
if (flags & CONF_AUTO_SET_IO) {
cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
int i = 0;
p_dev->resource[0]->start = p_dev->resource[0]->end = 0;
p_dev->resource[1]->start = p_dev->resource[1]->end = 0;
if (io->nwin == 0)
return -ENODEV;
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
p_dev->resource[0]->flags |=
pcmcia_io_cfg_data_width(io->flags);
if (io->nwin > 1) {
/* For multifunction cards, by convention, we
* configure the network function with window 0,
* and serial with window 1 */
i = (io->win[1].len > io->win[0].len);
p_dev->resource[1]->flags = p_dev->resource[0]->flags;
p_dev->resource[1]->start = io->win[1-i].base;
p_dev->resource[1]->end = io->win[1-i].len;
}
p_dev->resource[0]->start = io->win[i].base;
p_dev->resource[0]->end = io->win[i].len;
p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK;
}
/* MEM window settings? */
if (flags & CONF_AUTO_SET_IOMEM) {
/* so far, we only set one memory window */
cistpl_mem_t *mem = (cfg->mem.nwin) ? &cfg->mem : &dflt->mem;
p_dev->resource[2]->start = p_dev->resource[2]->end = 0;
if (mem->nwin == 0)
return -ENODEV;
p_dev->resource[2]->start = mem->win[0].host_addr;
p_dev->resource[2]->end = mem->win[0].len;
if (p_dev->resource[2]->end < 0x1000)
p_dev->resource[2]->end = 0x1000;
p_dev->card_addr = mem->win[0].card_addr;
}
dev_dbg(&p_dev->dev,
"checking configuration %x: %pr %pr %pr (%d lines)\n",
p_dev->config_index, p_dev->resource[0], p_dev->resource[1],
p_dev->resource[2], p_dev->io_lines);
return cfg_mem->conf_check(p_dev, cfg_mem->priv_data);
}
/**
* pcmcia_loop_config() - loop over configuration options
* @p_dev: the struct pcmcia_device which we need to loop for.
* @conf_check: function to call for each configuration option.
* It gets passed the struct pcmcia_device and private data
* being passed to pcmcia_loop_config()
* @priv_data: private data to be passed to the conf_check function.
*
* pcmcia_loop_config() loops over all configuration options, and calls
* the driver-specific conf_check() for each one, checking whether
* it is a valid one. Returns 0 on success or errorcode otherwise.
*/
int pcmcia_loop_config(struct pcmcia_device *p_dev,
int (*conf_check) (struct pcmcia_device *p_dev,
void *priv_data),
void *priv_data)
{
struct pcmcia_cfg_mem *cfg_mem;
int ret;
cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL);
if (cfg_mem == NULL)
return -ENOMEM;
cfg_mem->p_dev = p_dev;
cfg_mem->conf_check = conf_check;
cfg_mem->priv_data = priv_data;
ret = pccard_loop_tuple(p_dev->socket, p_dev->func,
CISTPL_CFTABLE_ENTRY, &cfg_mem->parse,
cfg_mem, pcmcia_do_loop_config);
kfree(cfg_mem);
return ret;
}
EXPORT_SYMBOL(pcmcia_loop_config);
struct pcmcia_loop_mem {
struct pcmcia_device *p_dev;
void *priv_data;
int (*loop_tuple) (struct pcmcia_device *p_dev,
tuple_t *tuple,
void *priv_data);
};
/**
* pcmcia_do_loop_tuple() - internal helper for pcmcia_loop_config()
*
* pcmcia_do_loop_tuple() is the internal callback for the call from
* pcmcia_loop_tuple() to pccard_loop_tuple(). Data is transferred
* by a struct pcmcia_cfg_mem.
*/
static int pcmcia_do_loop_tuple(tuple_t *tuple, cisparse_t *parse, void *priv)
{
struct pcmcia_loop_mem *loop = priv;
return loop->loop_tuple(loop->p_dev, tuple, loop->priv_data);
};
/**
* pcmcia_loop_tuple() - loop over tuples in the CIS
* @p_dev: the struct pcmcia_device which we need to loop for.
* @code: which CIS code shall we look for?
* @priv_data: private data to be passed to the loop_tuple function.
* @loop_tuple: function to call for each CIS entry of type @function. IT
* gets passed the raw tuple and @priv_data.
*
* pcmcia_loop_tuple() loops over all CIS entries of type @function, and
* calls the @loop_tuple function for each entry. If the call to @loop_tuple
* returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
*/
int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code,
int (*loop_tuple) (struct pcmcia_device *p_dev,
tuple_t *tuple,
void *priv_data),
void *priv_data)
{
struct pcmcia_loop_mem loop = {
.p_dev = p_dev,
.loop_tuple = loop_tuple,
.priv_data = priv_data};
return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL,
&loop, pcmcia_do_loop_tuple);
}
EXPORT_SYMBOL(pcmcia_loop_tuple);
struct pcmcia_loop_get {
size_t len;
cisdata_t **buf;
};
/**
* pcmcia_do_get_tuple() - internal helper for pcmcia_get_tuple()
*
* pcmcia_do_get_tuple() is the internal callback for the call from
* pcmcia_get_tuple() to pcmcia_loop_tuple(). As we're only interested in
* the first tuple, return 0 unconditionally. Create a memory buffer large
* enough to hold the content of the tuple, and fill it with the tuple data.
* The caller is responsible to free the buffer.
*/
static int pcmcia_do_get_tuple(struct pcmcia_device *p_dev, tuple_t *tuple,
void *priv)
{
struct pcmcia_loop_get *get = priv;
*get->buf = kzalloc(tuple->TupleDataLen, GFP_KERNEL);
if (*get->buf) {
get->len = tuple->TupleDataLen;
memcpy(*get->buf, tuple->TupleData, tuple->TupleDataLen);
} else
dev_dbg(&p_dev->dev, "do_get_tuple: out of memory\n");
return 0;
}
/**
* pcmcia_get_tuple() - get first tuple from CIS
* @p_dev: the struct pcmcia_device which we need to loop for.
* @code: which CIS code shall we look for?
* @buf: pointer to store the buffer to.
*
* pcmcia_get_tuple() gets the content of the first CIS entry of type @code.
* It returns the buffer length (or zero). The caller is responsible to free
* the buffer passed in @buf.
*/
size_t pcmcia_get_tuple(struct pcmcia_device *p_dev, cisdata_t code,
unsigned char **buf)
{
struct pcmcia_loop_get get = {
.len = 0,
.buf = buf,
};
*get.buf = NULL;
pcmcia_loop_tuple(p_dev, code, pcmcia_do_get_tuple, &get);
return get.len;
}
EXPORT_SYMBOL(pcmcia_get_tuple);
/**
* pcmcia_do_get_mac() - internal helper for pcmcia_get_mac_from_cis()
*
* pcmcia_do_get_mac() is the internal callback for the call from
* pcmcia_get_mac_from_cis() to pcmcia_loop_tuple(). We check whether the
* tuple contains a proper LAN_NODE_ID of length 6, and copy the data
* to struct net_device->dev_addr[i].
*/
static int pcmcia_do_get_mac(struct pcmcia_device *p_dev, tuple_t *tuple,
void *priv)
{
struct net_device *dev = priv;
int i;
if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID)
return -EINVAL;
if (tuple->TupleDataLen < ETH_ALEN + 2) {
dev_warn(&p_dev->dev, "Invalid CIS tuple length for "
"LAN_NODE_ID\n");
return -EINVAL;
}
if (tuple->TupleData[1] != ETH_ALEN) {
dev_warn(&p_dev->dev, "Invalid header for LAN_NODE_ID\n");
return -EINVAL;
}
for (i = 0; i < 6; i++)
dev->dev_addr[i] = tuple->TupleData[i+2];
return 0;
}
/**
* pcmcia_get_mac_from_cis() - read out MAC address from CISTPL_FUNCE
* @p_dev: the struct pcmcia_device for which we want the address.
* @dev: a properly prepared struct net_device to store the info to.
*
* pcmcia_get_mac_from_cis() reads out the hardware MAC address from
* CISTPL_FUNCE and stores it into struct net_device *dev->dev_addr which
* must be set up properly by the driver (see examples!).
*/
int pcmcia_get_mac_from_cis(struct pcmcia_device *p_dev, struct net_device *dev)
{
return pcmcia_loop_tuple(p_dev, CISTPL_FUNCE, pcmcia_do_get_mac, dev);
}
EXPORT_SYMBOL(pcmcia_get_mac_from_cis);

File diff suppressed because it is too large Load diff

780
drivers/pcmcia/pd6729.c Normal file
View file

@ -0,0 +1,780 @@
/*
* Driver for the Cirrus PD6729 PCI-PCMCIA bridge.
*
* Based on the i82092.c driver.
*
* This software may be used and distributed according to the terms of
* the GNU General Public License, incorporated herein by reference.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/io.h>
#include <pcmcia/ss.h>
#include "pd6729.h"
#include "i82365.h"
#include "cirrus.h"
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Driver for the Cirrus PD6729 PCI-PCMCIA bridge");
MODULE_AUTHOR("Jun Komuro <komurojun-mbn@nifty.com>");
#define MAX_SOCKETS 2
/*
* simple helper functions
* External clock time, in nanoseconds. 120 ns = 8.33 MHz
*/
#define to_cycles(ns) ((ns)/120)
#ifndef NO_IRQ
#define NO_IRQ ((unsigned int)(0))
#endif
/*
* PARAMETERS
* irq_mode=n
* Specifies the interrupt delivery mode. The default (1) is to use PCI
* interrupts; a value of 0 selects ISA interrupts. This must be set for
* correct operation of PCI card readers.
*/
static int irq_mode = 1; /* 0 = ISA interrupt, 1 = PCI interrupt */
module_param(irq_mode, int, 0444);
MODULE_PARM_DESC(irq_mode,
"interrupt delivery mode. 0 = ISA, 1 = PCI. default is 1");
static DEFINE_SPINLOCK(port_lock);
/* basic value read/write functions */
static unsigned char indirect_read(struct pd6729_socket *socket,
unsigned short reg)
{
unsigned long port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock, flags);
reg += socket->number * 0x40;
port = socket->io_base;
outb(reg, port);
val = inb(port + 1);
spin_unlock_irqrestore(&port_lock, flags);
return val;
}
static unsigned short indirect_read16(struct pd6729_socket *socket,
unsigned short reg)
{
unsigned long port;
unsigned short tmp;
unsigned long flags;
spin_lock_irqsave(&port_lock, flags);
reg = reg + socket->number * 0x40;
port = socket->io_base;
outb(reg, port);
tmp = inb(port + 1);
reg++;
outb(reg, port);
tmp = tmp | (inb(port + 1) << 8);
spin_unlock_irqrestore(&port_lock, flags);
return tmp;
}
static void indirect_write(struct pd6729_socket *socket, unsigned short reg,
unsigned char value)
{
unsigned long port;
unsigned long flags;
spin_lock_irqsave(&port_lock, flags);
reg = reg + socket->number * 0x40;
port = socket->io_base;
outb(reg, port);
outb(value, port + 1);
spin_unlock_irqrestore(&port_lock, flags);
}
static void indirect_setbit(struct pd6729_socket *socket, unsigned short reg,
unsigned char mask)
{
unsigned long port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock, flags);
reg = reg + socket->number * 0x40;
port = socket->io_base;
outb(reg, port);
val = inb(port + 1);
val |= mask;
outb(reg, port);
outb(val, port + 1);
spin_unlock_irqrestore(&port_lock, flags);
}
static void indirect_resetbit(struct pd6729_socket *socket, unsigned short reg,
unsigned char mask)
{
unsigned long port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock, flags);
reg = reg + socket->number * 0x40;
port = socket->io_base;
outb(reg, port);
val = inb(port + 1);
val &= ~mask;
outb(reg, port);
outb(val, port + 1);
spin_unlock_irqrestore(&port_lock, flags);
}
static void indirect_write16(struct pd6729_socket *socket, unsigned short reg,
unsigned short value)
{
unsigned long port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock, flags);
reg = reg + socket->number * 0x40;
port = socket->io_base;
outb(reg, port);
val = value & 255;
outb(val, port + 1);
reg++;
outb(reg, port);
val = value >> 8;
outb(val, port + 1);
spin_unlock_irqrestore(&port_lock, flags);
}
/* Interrupt handler functionality */
static irqreturn_t pd6729_interrupt(int irq, void *dev)
{
struct pd6729_socket *socket = (struct pd6729_socket *)dev;
int i;
int loopcount = 0;
int handled = 0;
unsigned int events, active = 0;
while (1) {
loopcount++;
if (loopcount > 20) {
printk(KERN_ERR "pd6729: infinite eventloop "
"in interrupt\n");
break;
}
active = 0;
for (i = 0; i < MAX_SOCKETS; i++) {
unsigned int csc;
/* card status change register */
csc = indirect_read(&socket[i], I365_CSC);
if (csc == 0) /* no events on this socket */
continue;
handled = 1;
events = 0;
if (csc & I365_CSC_DETECT) {
events |= SS_DETECT;
dev_vdbg(&socket[i].socket.dev,
"Card detected in socket %i!\n", i);
}
if (indirect_read(&socket[i], I365_INTCTL)
& I365_PC_IOCARD) {
/* For IO/CARDS, bit 0 means "read the card" */
events |= (csc & I365_CSC_STSCHG)
? SS_STSCHG : 0;
} else {
/* Check for battery/ready events */
events |= (csc & I365_CSC_BVD1)
? SS_BATDEAD : 0;
events |= (csc & I365_CSC_BVD2)
? SS_BATWARN : 0;
events |= (csc & I365_CSC_READY)
? SS_READY : 0;
}
if (events)
pcmcia_parse_events(&socket[i].socket, events);
active |= events;
}
if (active == 0) /* no more events to handle */
break;
}
return IRQ_RETVAL(handled);
}
/* socket functions */
static void pd6729_interrupt_wrapper(unsigned long data)
{
struct pd6729_socket *socket = (struct pd6729_socket *) data;
pd6729_interrupt(0, (void *)socket);
mod_timer(&socket->poll_timer, jiffies + HZ);
}
static int pd6729_get_status(struct pcmcia_socket *sock, u_int *value)
{
struct pd6729_socket *socket
= container_of(sock, struct pd6729_socket, socket);
unsigned int status;
unsigned int data;
struct pd6729_socket *t;
/* Interface Status Register */
status = indirect_read(socket, I365_STATUS);
*value = 0;
if ((status & I365_CS_DETECT) == I365_CS_DETECT)
*value |= SS_DETECT;
/*
* IO cards have a different meaning of bits 0,1
* Also notice the inverse-logic on the bits
*/
if (indirect_read(socket, I365_INTCTL) & I365_PC_IOCARD) {
/* IO card */
if (!(status & I365_CS_STSCHG))
*value |= SS_STSCHG;
} else {
/* non I/O card */
if (!(status & I365_CS_BVD1))
*value |= SS_BATDEAD;
if (!(status & I365_CS_BVD2))
*value |= SS_BATWARN;
}
if (status & I365_CS_WRPROT)
*value |= SS_WRPROT; /* card is write protected */
if (status & I365_CS_READY)
*value |= SS_READY; /* card is not busy */
if (status & I365_CS_POWERON)
*value |= SS_POWERON; /* power is applied to the card */
t = (socket->number) ? socket : socket + 1;
indirect_write(t, PD67_EXT_INDEX, PD67_EXTERN_DATA);
data = indirect_read16(t, PD67_EXT_DATA);
*value |= (data & PD67_EXD_VS1(socket->number)) ? 0 : SS_3VCARD;
return 0;
}
static int pd6729_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
{
struct pd6729_socket *socket
= container_of(sock, struct pd6729_socket, socket);
unsigned char reg, data;
/* First, set the global controller options */
indirect_write(socket, I365_GBLCTL, 0x00);
indirect_write(socket, I365_GENCTL, 0x00);
/* Values for the IGENC register */
socket->card_irq = state->io_irq;
reg = 0;
/* The reset bit has "inverse" logic */
if (!(state->flags & SS_RESET))
reg |= I365_PC_RESET;
if (state->flags & SS_IOCARD)
reg |= I365_PC_IOCARD;
/* IGENC, Interrupt and General Control Register */
indirect_write(socket, I365_INTCTL, reg);
/* Power registers */
reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */
if (state->flags & SS_PWR_AUTO) {
dev_dbg(&sock->dev, "Auto power\n");
reg |= I365_PWR_AUTO; /* automatic power mngmnt */
}
if (state->flags & SS_OUTPUT_ENA) {
dev_dbg(&sock->dev, "Power Enabled\n");
reg |= I365_PWR_OUT; /* enable power */
}
switch (state->Vcc) {
case 0:
break;
case 33:
dev_dbg(&sock->dev,
"setting voltage to Vcc to 3.3V on socket %i\n",
socket->number);
reg |= I365_VCC_5V;
indirect_setbit(socket, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
break;
case 50:
dev_dbg(&sock->dev,
"setting voltage to Vcc to 5V on socket %i\n",
socket->number);
reg |= I365_VCC_5V;
indirect_resetbit(socket, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
break;
default:
dev_dbg(&sock->dev,
"pd6729_set_socket called with invalid VCC power "
"value: %i\n", state->Vcc);
return -EINVAL;
}
switch (state->Vpp) {
case 0:
dev_dbg(&sock->dev, "not setting Vpp on socket %i\n",
socket->number);
break;
case 33:
case 50:
dev_dbg(&sock->dev, "setting Vpp to Vcc for socket %i\n",
socket->number);
reg |= I365_VPP1_5V;
break;
case 120:
dev_dbg(&sock->dev, "setting Vpp to 12.0\n");
reg |= I365_VPP1_12V;
break;
default:
dev_dbg(&sock->dev, "pd6729: pd6729_set_socket called with "
"invalid VPP power value: %i\n", state->Vpp);
return -EINVAL;
}
/* only write if changed */
if (reg != indirect_read(socket, I365_POWER))
indirect_write(socket, I365_POWER, reg);
if (irq_mode == 1) {
/* all interrupts are to be done as PCI interrupts */
data = PD67_EC1_INV_MGMT_IRQ | PD67_EC1_INV_CARD_IRQ;
} else
data = 0;
indirect_write(socket, PD67_EXT_INDEX, PD67_EXT_CTL_1);
indirect_write(socket, PD67_EXT_DATA, data);
/* Enable specific interrupt events */
reg = 0x00;
if (state->csc_mask & SS_DETECT)
reg |= I365_CSC_DETECT;
if (state->flags & SS_IOCARD) {
if (state->csc_mask & SS_STSCHG)
reg |= I365_CSC_STSCHG;
} else {
if (state->csc_mask & SS_BATDEAD)
reg |= I365_CSC_BVD1;
if (state->csc_mask & SS_BATWARN)
reg |= I365_CSC_BVD2;
if (state->csc_mask & SS_READY)
reg |= I365_CSC_READY;
}
if (irq_mode == 1)
reg |= 0x30; /* management IRQ: PCI INTA# = "irq 3" */
indirect_write(socket, I365_CSCINT, reg);
reg = indirect_read(socket, I365_INTCTL);
if (irq_mode == 1)
reg |= 0x03; /* card IRQ: PCI INTA# = "irq 3" */
else
reg |= socket->card_irq;
indirect_write(socket, I365_INTCTL, reg);
/* now clear the (probably bogus) pending stuff by doing a dummy read */
(void)indirect_read(socket, I365_CSC);
return 0;
}
static int pd6729_set_io_map(struct pcmcia_socket *sock,
struct pccard_io_map *io)
{
struct pd6729_socket *socket
= container_of(sock, struct pd6729_socket, socket);
unsigned char map, ioctl;
map = io->map;
/* Check error conditions */
if (map > 1) {
dev_dbg(&sock->dev, "pd6729_set_io_map with invalid map\n");
return -EINVAL;
}
/* Turn off the window before changing anything */
if (indirect_read(socket, I365_ADDRWIN) & I365_ENA_IO(map))
indirect_resetbit(socket, I365_ADDRWIN, I365_ENA_IO(map));
/* dev_dbg(&sock->dev, "set_io_map: Setting range to %x - %x\n",
io->start, io->stop);*/
/* write the new values */
indirect_write16(socket, I365_IO(map)+I365_W_START, io->start);
indirect_write16(socket, I365_IO(map)+I365_W_STOP, io->stop);
ioctl = indirect_read(socket, I365_IOCTL) & ~I365_IOCTL_MASK(map);
if (io->flags & MAP_0WS)
ioctl |= I365_IOCTL_0WS(map);
if (io->flags & MAP_16BIT)
ioctl |= I365_IOCTL_16BIT(map);
if (io->flags & MAP_AUTOSZ)
ioctl |= I365_IOCTL_IOCS16(map);
indirect_write(socket, I365_IOCTL, ioctl);
/* Turn the window back on if needed */
if (io->flags & MAP_ACTIVE)
indirect_setbit(socket, I365_ADDRWIN, I365_ENA_IO(map));
return 0;
}
static int pd6729_set_mem_map(struct pcmcia_socket *sock,
struct pccard_mem_map *mem)
{
struct pd6729_socket *socket
= container_of(sock, struct pd6729_socket, socket);
unsigned short base, i;
unsigned char map;
map = mem->map;
if (map > 4) {
dev_warn(&sock->dev, "invalid map requested\n");
return -EINVAL;
}
if ((mem->res->start > mem->res->end) || (mem->speed > 1000)) {
dev_warn(&sock->dev, "invalid invalid address / speed\n");
return -EINVAL;
}
/* Turn off the window before changing anything */
if (indirect_read(socket, I365_ADDRWIN) & I365_ENA_MEM(map))
indirect_resetbit(socket, I365_ADDRWIN, I365_ENA_MEM(map));
/* write the start address */
base = I365_MEM(map);
i = (mem->res->start >> 12) & 0x0fff;
if (mem->flags & MAP_16BIT)
i |= I365_MEM_16BIT;
if (mem->flags & MAP_0WS)
i |= I365_MEM_0WS;
indirect_write16(socket, base + I365_W_START, i);
/* write the stop address */
i = (mem->res->end >> 12) & 0x0fff;
switch (to_cycles(mem->speed)) {
case 0:
break;
case 1:
i |= I365_MEM_WS0;
break;
case 2:
i |= I365_MEM_WS1;
break;
default:
i |= I365_MEM_WS1 | I365_MEM_WS0;
break;
}
indirect_write16(socket, base + I365_W_STOP, i);
/* Take care of high byte */
indirect_write(socket, PD67_EXT_INDEX, PD67_MEM_PAGE(map));
indirect_write(socket, PD67_EXT_DATA, mem->res->start >> 24);
/* card start */
i = ((mem->card_start - mem->res->start) >> 12) & 0x3fff;
if (mem->flags & MAP_WRPROT)
i |= I365_MEM_WRPROT;
if (mem->flags & MAP_ATTRIB) {
/* dev_dbg(&sock->dev, "requesting attribute memory for "
"socket %i\n", socket->number);*/
i |= I365_MEM_REG;
} else {
/* dev_dbg(&sock->dev, "requesting normal memory for "
"socket %i\n", socket->number);*/
}
indirect_write16(socket, base + I365_W_OFF, i);
/* Enable the window if necessary */
if (mem->flags & MAP_ACTIVE)
indirect_setbit(socket, I365_ADDRWIN, I365_ENA_MEM(map));
return 0;
}
static int pd6729_init(struct pcmcia_socket *sock)
{
int i;
struct resource res = { .end = 0x0fff };
pccard_io_map io = { 0, 0, 0, 0, 1 };
pccard_mem_map mem = { .res = &res, };
pd6729_set_socket(sock, &dead_socket);
for (i = 0; i < 2; i++) {
io.map = i;
pd6729_set_io_map(sock, &io);
}
for (i = 0; i < 5; i++) {
mem.map = i;
pd6729_set_mem_map(sock, &mem);
}
return 0;
}
/* the pccard structure and its functions */
static struct pccard_operations pd6729_operations = {
.init = pd6729_init,
.get_status = pd6729_get_status,
.set_socket = pd6729_set_socket,
.set_io_map = pd6729_set_io_map,
.set_mem_map = pd6729_set_mem_map,
};
static irqreturn_t pd6729_test(int irq, void *dev)
{
pr_devel("-> hit on irq %d\n", irq);
return IRQ_HANDLED;
}
static int pd6729_check_irq(int irq)
{
int ret;
ret = request_irq(irq, pd6729_test, IRQF_PROBE_SHARED, "x",
pd6729_test);
if (ret)
return -1;
free_irq(irq, pd6729_test);
return 0;
}
static u_int pd6729_isa_scan(void)
{
u_int mask0, mask = 0;
int i;
if (irq_mode == 1) {
printk(KERN_INFO "pd6729: PCI card interrupts, "
"PCI status changes\n");
return 0;
}
mask0 = PD67_MASK;
/* just find interrupts that aren't in use */
for (i = 0; i < 16; i++)
if ((mask0 & (1 << i)) && (pd6729_check_irq(i) == 0))
mask |= (1 << i);
printk(KERN_INFO "pd6729: ISA irqs = ");
for (i = 0; i < 16; i++)
if (mask & (1<<i))
printk("%s%d", ((mask & ((1<<i)-1)) ? "," : ""), i);
if (mask == 0)
printk("none!");
else
printk(" polling status changes.\n");
return mask;
}
static int pd6729_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
int i, j, ret;
u_int mask;
char configbyte;
struct pd6729_socket *socket;
socket = kzalloc(sizeof(struct pd6729_socket) * MAX_SOCKETS,
GFP_KERNEL);
if (!socket) {
dev_warn(&dev->dev, "failed to kzalloc socket.\n");
return -ENOMEM;
}
ret = pci_enable_device(dev);
if (ret) {
dev_warn(&dev->dev, "failed to enable pci_device.\n");
goto err_out_free_mem;
}
if (!pci_resource_start(dev, 0)) {
dev_warn(&dev->dev, "refusing to load the driver as the "
"io_base is NULL.\n");
ret = -ENOMEM;
goto err_out_disable;
}
dev_info(&dev->dev, "Cirrus PD6729 PCI to PCMCIA Bridge at 0x%llx "
"on irq %d\n",
(unsigned long long)pci_resource_start(dev, 0), dev->irq);
/*
* Since we have no memory BARs some firmware may not
* have had PCI_COMMAND_MEMORY enabled, yet the device needs it.
*/
pci_read_config_byte(dev, PCI_COMMAND, &configbyte);
if (!(configbyte & PCI_COMMAND_MEMORY)) {
dev_dbg(&dev->dev, "pd6729: Enabling PCI_COMMAND_MEMORY.\n");
configbyte |= PCI_COMMAND_MEMORY;
pci_write_config_byte(dev, PCI_COMMAND, configbyte);
}
ret = pci_request_regions(dev, "pd6729");
if (ret) {
dev_warn(&dev->dev, "pci request region failed.\n");
goto err_out_disable;
}
if (dev->irq == NO_IRQ)
irq_mode = 0; /* fall back to ISA interrupt mode */
mask = pd6729_isa_scan();
if (irq_mode == 0 && mask == 0) {
dev_warn(&dev->dev, "no ISA interrupt is available.\n");
ret = -ENODEV;
goto err_out_free_res;
}
for (i = 0; i < MAX_SOCKETS; i++) {
socket[i].io_base = pci_resource_start(dev, 0);
socket[i].socket.features |= SS_CAP_PAGE_REGS | SS_CAP_PCCARD;
socket[i].socket.map_size = 0x1000;
socket[i].socket.irq_mask = mask;
socket[i].socket.pci_irq = dev->irq;
socket[i].socket.cb_dev = dev;
socket[i].socket.owner = THIS_MODULE;
socket[i].number = i;
socket[i].socket.ops = &pd6729_operations;
socket[i].socket.resource_ops = &pccard_nonstatic_ops;
socket[i].socket.dev.parent = &dev->dev;
socket[i].socket.driver_data = &socket[i];
}
pci_set_drvdata(dev, socket);
if (irq_mode == 1) {
/* Register the interrupt handler */
ret = request_irq(dev->irq, pd6729_interrupt, IRQF_SHARED,
"pd6729", socket);
if (ret) {
dev_err(&dev->dev, "Failed to register irq %d\n",
dev->irq);
goto err_out_free_res;
}
} else {
/* poll Card status change */
init_timer(&socket->poll_timer);
socket->poll_timer.function = pd6729_interrupt_wrapper;
socket->poll_timer.data = (unsigned long)socket;
socket->poll_timer.expires = jiffies + HZ;
add_timer(&socket->poll_timer);
}
for (i = 0; i < MAX_SOCKETS; i++) {
ret = pcmcia_register_socket(&socket[i].socket);
if (ret) {
dev_warn(&dev->dev, "pcmcia_register_socket failed.\n");
for (j = 0; j < i ; j++)
pcmcia_unregister_socket(&socket[j].socket);
goto err_out_free_res2;
}
}
return 0;
err_out_free_res2:
if (irq_mode == 1)
free_irq(dev->irq, socket);
else
del_timer_sync(&socket->poll_timer);
err_out_free_res:
pci_release_regions(dev);
err_out_disable:
pci_disable_device(dev);
err_out_free_mem:
kfree(socket);
return ret;
}
static void pd6729_pci_remove(struct pci_dev *dev)
{
int i;
struct pd6729_socket *socket = pci_get_drvdata(dev);
for (i = 0; i < MAX_SOCKETS; i++) {
/* Turn off all interrupt sources */
indirect_write(&socket[i], I365_CSCINT, 0);
indirect_write(&socket[i], I365_INTCTL, 0);
pcmcia_unregister_socket(&socket[i].socket);
}
if (irq_mode == 1)
free_irq(dev->irq, socket);
else
del_timer_sync(&socket->poll_timer);
pci_release_regions(dev);
pci_disable_device(dev);
kfree(socket);
}
static const struct pci_device_id pd6729_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_6729) },
{ }
};
MODULE_DEVICE_TABLE(pci, pd6729_pci_ids);
static struct pci_driver pd6729_pci_driver = {
.name = "pd6729",
.id_table = pd6729_pci_ids,
.probe = pd6729_pci_probe,
.remove = pd6729_pci_remove,
};
module_pci_driver(pd6729_pci_driver);

23
drivers/pcmcia/pd6729.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef _INCLUDE_GUARD_PD6729_H_
#define _INCLUDE_GUARD_PD6729_H_
/* Flags for I365_GENCTL */
#define I365_DF_VS1 0x40 /* DF-step Voltage Sense */
#define I365_DF_VS2 0x80
/* Fields in PD67_EXTERN_DATA */
#define PD67_EXD_VS1(s) (0x01 << ((s) << 1))
#define PD67_EXD_VS2(s) (0x02 << ((s) << 1))
/* Default ISA interrupt mask */
#define PD67_MASK 0x0eb8 /* irq 11,10,9,7,5,4,3 */
struct pd6729_socket {
int number;
int card_irq;
unsigned long io_base; /* base io address of the socket */
struct pcmcia_socket socket;
struct timer_list poll_timer;
};
#endif

View file

@ -0,0 +1,140 @@
/*
* linux/drivers/pcmcia/pxa2xx_balloon3.c
*
* Balloon3 PCMCIA specific routines.
*
* Author: Nick Bane
* Created: June, 2006
* Copyright: Toby Churchill Ltd
* Derived from pxa2xx_mainstone.c, by Nico Pitre
*
* Various modification by Marek Vasut <marek.vasut@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <mach/balloon3.h>
#include <asm/mach-types.h>
#include "soc_common.h"
static int balloon3_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
uint16_t ver;
ver = __raw_readw(BALLOON3_FPGA_VER);
if (ver < 0x4f08)
pr_warn("The FPGA code, version 0x%04x, is too old. "
"PCMCIA/CF support might be broken in this version!",
ver);
skt->socket.pci_irq = BALLOON3_BP_CF_NRDY_IRQ;
skt->stat[SOC_STAT_CD].gpio = BALLOON3_GPIO_S0_CD;
skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD";
skt->stat[SOC_STAT_BVD1].irq = BALLOON3_BP_NSTSCHG_IRQ;
skt->stat[SOC_STAT_BVD1].name = "PCMCIA0 STSCHG";
return 0;
}
static unsigned long balloon3_pcmcia_status[2] = {
BALLOON3_CF_nSTSCHG_BVD1,
BALLOON3_CF_nSTSCHG_BVD1
};
static void balloon3_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
uint16_t status;
int flip;
/* This actually reads the STATUS register */
status = __raw_readw(BALLOON3_CF_STATUS_REG);
flip = (status ^ balloon3_pcmcia_status[skt->nr])
& BALLOON3_CF_nSTSCHG_BVD1;
/*
* Workaround for STSCHG which can't be deasserted:
* We therefore disable/enable corresponding IRQs
* as needed to avoid IRQ locks.
*/
if (flip) {
balloon3_pcmcia_status[skt->nr] = status;
if (status & BALLOON3_CF_nSTSCHG_BVD1)
enable_irq(BALLOON3_BP_NSTSCHG_IRQ);
else
disable_irq(BALLOON3_BP_NSTSCHG_IRQ);
}
state->ready = !!(status & BALLOON3_CF_nIRQ);
state->bvd1 = !!(status & BALLOON3_CF_nSTSCHG_BVD1);
state->bvd2 = 0; /* not available */
state->vs_3v = 1; /* Always true its a CF card */
state->vs_Xv = 0; /* not available */
}
static int balloon3_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
__raw_writew(BALLOON3_CF_RESET, BALLOON3_CF_CONTROL_REG +
((state->flags & SS_RESET) ?
BALLOON3_FPGA_SETnCLR : 0));
return 0;
}
static struct pcmcia_low_level balloon3_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = balloon3_pcmcia_hw_init,
.socket_state = balloon3_pcmcia_socket_state,
.configure_socket = balloon3_pcmcia_configure_socket,
.first = 0,
.nr = 1,
};
static struct platform_device *balloon3_pcmcia_device;
static int __init balloon3_pcmcia_init(void)
{
int ret;
if (!machine_is_balloon3())
return -ENODEV;
balloon3_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!balloon3_pcmcia_device)
return -ENOMEM;
ret = platform_device_add_data(balloon3_pcmcia_device,
&balloon3_pcmcia_ops, sizeof(balloon3_pcmcia_ops));
if (!ret)
ret = platform_device_add(balloon3_pcmcia_device);
if (ret)
platform_device_put(balloon3_pcmcia_device);
return ret;
}
static void __exit balloon3_pcmcia_exit(void)
{
platform_device_unregister(balloon3_pcmcia_device);
}
module_init(balloon3_pcmcia_init);
module_exit(balloon3_pcmcia_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nick Bane <nick@cecomputing.co.uk>");
MODULE_ALIAS("platform:pxa2xx-pcmcia");
MODULE_DESCRIPTION("Balloon3 board CF/PCMCIA driver");

View file

@ -0,0 +1,392 @@
/*======================================================================
Device driver for the PCMCIA control functionality of PXA2xx
microprocessors.
The contents of this file may be used under the
terms of the GNU Public License version 2 (the "GPL")
(c) Ian Molton (spyro@f2s.com) 2003
(c) Stefan Eletzhofer (stefan.eletzhofer@inquant.de) 2003,4
derived from sa11xx_base.c
Portions created by John G. Dorsey are
Copyright (C) 1999 John G. Dorsey.
======================================================================*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <mach/hardware.h>
#include <mach/smemc.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <mach/pxa2xx-regs.h>
#include <asm/mach-types.h>
#include <pcmcia/ss.h>
#include <pcmcia/cistpl.h>
#include "soc_common.h"
#include "pxa2xx_base.h"
/*
* Personal Computer Memory Card International Association (PCMCIA) sockets
*/
#define PCMCIAPrtSp 0x04000000 /* PCMCIA Partition Space [byte] */
#define PCMCIASp (4*PCMCIAPrtSp) /* PCMCIA Space [byte] */
#define PCMCIAIOSp PCMCIAPrtSp /* PCMCIA I/O Space [byte] */
#define PCMCIAAttrSp PCMCIAPrtSp /* PCMCIA Attribute Space [byte] */
#define PCMCIAMemSp PCMCIAPrtSp /* PCMCIA Memory Space [byte] */
#define PCMCIA0Sp PCMCIASp /* PCMCIA 0 Space [byte] */
#define PCMCIA0IOSp PCMCIAIOSp /* PCMCIA 0 I/O Space [byte] */
#define PCMCIA0AttrSp PCMCIAAttrSp /* PCMCIA 0 Attribute Space [byte] */
#define PCMCIA0MemSp PCMCIAMemSp /* PCMCIA 0 Memory Space [byte] */
#define PCMCIA1Sp PCMCIASp /* PCMCIA 1 Space [byte] */
#define PCMCIA1IOSp PCMCIAIOSp /* PCMCIA 1 I/O Space [byte] */
#define PCMCIA1AttrSp PCMCIAAttrSp /* PCMCIA 1 Attribute Space [byte] */
#define PCMCIA1MemSp PCMCIAMemSp /* PCMCIA 1 Memory Space [byte] */
#define _PCMCIA(Nb) /* PCMCIA [0..1] */ \
(0x20000000 + (Nb) * PCMCIASp)
#define _PCMCIAIO(Nb) _PCMCIA(Nb) /* PCMCIA I/O [0..1] */
#define _PCMCIAAttr(Nb) /* PCMCIA Attribute [0..1] */ \
(_PCMCIA(Nb) + 2 * PCMCIAPrtSp)
#define _PCMCIAMem(Nb) /* PCMCIA Memory [0..1] */ \
(_PCMCIA(Nb) + 3 * PCMCIAPrtSp)
#define _PCMCIA0 _PCMCIA(0) /* PCMCIA 0 */
#define _PCMCIA0IO _PCMCIAIO(0) /* PCMCIA 0 I/O */
#define _PCMCIA0Attr _PCMCIAAttr(0) /* PCMCIA 0 Attribute */
#define _PCMCIA0Mem _PCMCIAMem(0) /* PCMCIA 0 Memory */
#define _PCMCIA1 _PCMCIA(1) /* PCMCIA 1 */
#define _PCMCIA1IO _PCMCIAIO(1) /* PCMCIA 1 I/O */
#define _PCMCIA1Attr _PCMCIAAttr(1) /* PCMCIA 1 Attribute */
#define _PCMCIA1Mem _PCMCIAMem(1) /* PCMCIA 1 Memory */
#define MCXX_SETUP_MASK (0x7f)
#define MCXX_ASST_MASK (0x1f)
#define MCXX_HOLD_MASK (0x3f)
#define MCXX_SETUP_SHIFT (0)
#define MCXX_ASST_SHIFT (7)
#define MCXX_HOLD_SHIFT (14)
static inline u_int pxa2xx_mcxx_hold(u_int pcmcia_cycle_ns,
u_int mem_clk_10khz)
{
u_int code = pcmcia_cycle_ns * mem_clk_10khz;
return (code / 300000) + ((code % 300000) ? 1 : 0) - 1;
}
static inline u_int pxa2xx_mcxx_asst(u_int pcmcia_cycle_ns,
u_int mem_clk_10khz)
{
u_int code = pcmcia_cycle_ns * mem_clk_10khz;
return (code / 300000) + ((code % 300000) ? 1 : 0) + 1;
}
static inline u_int pxa2xx_mcxx_setup(u_int pcmcia_cycle_ns,
u_int mem_clk_10khz)
{
u_int code = pcmcia_cycle_ns * mem_clk_10khz;
return (code / 100000) + ((code % 100000) ? 1 : 0) - 1;
}
/* This function returns the (approximate) command assertion period, in
* nanoseconds, for a given CPU clock frequency and MCXX_ASST value:
*/
static inline u_int pxa2xx_pcmcia_cmd_time(u_int mem_clk_10khz,
u_int pcmcia_mcxx_asst)
{
return (300000 * (pcmcia_mcxx_asst + 1) / mem_clk_10khz);
}
static int pxa2xx_pcmcia_set_mcmem( int sock, int speed, int clock )
{
uint32_t val;
val = ((pxa2xx_mcxx_setup(speed, clock)
& MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
| ((pxa2xx_mcxx_asst(speed, clock)
& MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
| ((pxa2xx_mcxx_hold(speed, clock)
& MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
__raw_writel(val, MCMEM(sock));
return 0;
}
static int pxa2xx_pcmcia_set_mcio( int sock, int speed, int clock )
{
uint32_t val;
val = ((pxa2xx_mcxx_setup(speed, clock)
& MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
| ((pxa2xx_mcxx_asst(speed, clock)
& MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
| ((pxa2xx_mcxx_hold(speed, clock)
& MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
__raw_writel(val, MCIO(sock));
return 0;
}
static int pxa2xx_pcmcia_set_mcatt( int sock, int speed, int clock )
{
uint32_t val;
val = ((pxa2xx_mcxx_setup(speed, clock)
& MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
| ((pxa2xx_mcxx_asst(speed, clock)
& MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
| ((pxa2xx_mcxx_hold(speed, clock)
& MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
__raw_writel(val, MCATT(sock));
return 0;
}
static int pxa2xx_pcmcia_set_mcxx(struct soc_pcmcia_socket *skt, unsigned int clk)
{
struct soc_pcmcia_timing timing;
int sock = skt->nr;
soc_common_pcmcia_get_timing(skt, &timing);
pxa2xx_pcmcia_set_mcmem(sock, timing.mem, clk);
pxa2xx_pcmcia_set_mcatt(sock, timing.attr, clk);
pxa2xx_pcmcia_set_mcio(sock, timing.io, clk);
return 0;
}
static int pxa2xx_pcmcia_set_timing(struct soc_pcmcia_socket *skt)
{
unsigned long clk = clk_get_rate(skt->clk);
return pxa2xx_pcmcia_set_mcxx(skt, clk / 10000);
}
#ifdef CONFIG_CPU_FREQ
static int
pxa2xx_pcmcia_frequency_change(struct soc_pcmcia_socket *skt,
unsigned long val,
struct cpufreq_freqs *freqs)
{
switch (val) {
case CPUFREQ_PRECHANGE:
if (freqs->new > freqs->old) {
debug(skt, 2, "new frequency %u.%uMHz > %u.%uMHz, "
"pre-updating\n",
freqs->new / 1000, (freqs->new / 100) % 10,
freqs->old / 1000, (freqs->old / 100) % 10);
pxa2xx_pcmcia_set_timing(skt);
}
break;
case CPUFREQ_POSTCHANGE:
if (freqs->new < freqs->old) {
debug(skt, 2, "new frequency %u.%uMHz < %u.%uMHz, "
"post-updating\n",
freqs->new / 1000, (freqs->new / 100) % 10,
freqs->old / 1000, (freqs->old / 100) % 10);
pxa2xx_pcmcia_set_timing(skt);
}
break;
}
return 0;
}
#endif
void pxa2xx_configure_sockets(struct device *dev)
{
struct pcmcia_low_level *ops = dev->platform_data;
/*
* We have at least one socket, so set MECR:CIT
* (Card Is There)
*/
uint32_t mecr = MECR_CIT;
/* Set MECR:NOS (Number Of Sockets) */
if ((ops->first + ops->nr) > 1 ||
machine_is_viper() || machine_is_arcom_zeus())
mecr |= MECR_NOS;
__raw_writel(mecr, MECR);
}
EXPORT_SYMBOL(pxa2xx_configure_sockets);
static const char *skt_names[] = {
"PCMCIA socket 0",
"PCMCIA socket 1",
};
#define SKT_DEV_INFO_SIZE(n) \
(sizeof(struct skt_dev_info) + (n)*sizeof(struct soc_pcmcia_socket))
int pxa2xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt)
{
skt->res_skt.start = _PCMCIA(skt->nr);
skt->res_skt.end = _PCMCIA(skt->nr) + PCMCIASp - 1;
skt->res_skt.name = skt_names[skt->nr];
skt->res_skt.flags = IORESOURCE_MEM;
skt->res_io.start = _PCMCIAIO(skt->nr);
skt->res_io.end = _PCMCIAIO(skt->nr) + PCMCIAIOSp - 1;
skt->res_io.name = "io";
skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
skt->res_mem.start = _PCMCIAMem(skt->nr);
skt->res_mem.end = _PCMCIAMem(skt->nr) + PCMCIAMemSp - 1;
skt->res_mem.name = "memory";
skt->res_mem.flags = IORESOURCE_MEM;
skt->res_attr.start = _PCMCIAAttr(skt->nr);
skt->res_attr.end = _PCMCIAAttr(skt->nr) + PCMCIAAttrSp - 1;
skt->res_attr.name = "attribute";
skt->res_attr.flags = IORESOURCE_MEM;
return soc_pcmcia_add_one(skt);
}
EXPORT_SYMBOL(pxa2xx_drv_pcmcia_add_one);
void pxa2xx_drv_pcmcia_ops(struct pcmcia_low_level *ops)
{
/* Provide our PXA2xx specific timing routines. */
ops->set_timing = pxa2xx_pcmcia_set_timing;
#ifdef CONFIG_CPU_FREQ
ops->frequency_change = pxa2xx_pcmcia_frequency_change;
#endif
}
EXPORT_SYMBOL(pxa2xx_drv_pcmcia_ops);
static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev)
{
int i, ret = 0;
struct pcmcia_low_level *ops;
struct skt_dev_info *sinfo;
struct soc_pcmcia_socket *skt;
struct clk *clk;
ops = (struct pcmcia_low_level *)dev->dev.platform_data;
if (!ops) {
ret = -ENODEV;
goto err0;
}
if (cpu_is_pxa320() && ops->nr > 1) {
dev_err(&dev->dev, "pxa320 supports only one pcmcia slot");
ret = -EINVAL;
goto err0;
}
clk = clk_get(&dev->dev, NULL);
if (IS_ERR(clk))
return -ENODEV;
pxa2xx_drv_pcmcia_ops(ops);
sinfo = kzalloc(SKT_DEV_INFO_SIZE(ops->nr), GFP_KERNEL);
if (!sinfo) {
clk_put(clk);
return -ENOMEM;
}
sinfo->nskt = ops->nr;
sinfo->clk = clk;
/* Initialize processor specific parameters */
for (i = 0; i < ops->nr; i++) {
skt = &sinfo->skt[i];
skt->nr = ops->first + i;
skt->clk = clk;
soc_pcmcia_init_one(skt, ops, &dev->dev);
ret = pxa2xx_drv_pcmcia_add_one(skt);
if (ret)
goto err1;
}
pxa2xx_configure_sockets(&dev->dev);
dev_set_drvdata(&dev->dev, sinfo);
return 0;
err1:
while (--i >= 0)
soc_pcmcia_remove_one(&sinfo->skt[i]);
clk_put(clk);
kfree(sinfo);
err0:
return ret;
}
static int pxa2xx_drv_pcmcia_remove(struct platform_device *dev)
{
struct skt_dev_info *sinfo = platform_get_drvdata(dev);
int i;
platform_set_drvdata(dev, NULL);
for (i = 0; i < sinfo->nskt; i++)
soc_pcmcia_remove_one(&sinfo->skt[i]);
clk_put(sinfo->clk);
kfree(sinfo);
return 0;
}
static int pxa2xx_drv_pcmcia_resume(struct device *dev)
{
pxa2xx_configure_sockets(dev);
return 0;
}
static const struct dev_pm_ops pxa2xx_drv_pcmcia_pm_ops = {
.resume = pxa2xx_drv_pcmcia_resume,
};
static struct platform_driver pxa2xx_pcmcia_driver = {
.probe = pxa2xx_drv_pcmcia_probe,
.remove = pxa2xx_drv_pcmcia_remove,
.driver = {
.name = "pxa2xx-pcmcia",
.owner = THIS_MODULE,
.pm = &pxa2xx_drv_pcmcia_pm_ops,
},
};
static int __init pxa2xx_pcmcia_init(void)
{
return platform_driver_register(&pxa2xx_pcmcia_driver);
}
static void __exit pxa2xx_pcmcia_exit(void)
{
platform_driver_unregister(&pxa2xx_pcmcia_driver);
}
fs_initcall(pxa2xx_pcmcia_init);
module_exit(pxa2xx_pcmcia_exit);
MODULE_AUTHOR("Stefan Eletzhofer <stefan.eletzhofer@inquant.de> and Ian Molton <spyro@f2s.com>");
MODULE_DESCRIPTION("Linux PCMCIA Card Services: PXA2xx core socket driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pxa2xx-pcmcia");

View file

@ -0,0 +1,4 @@
int pxa2xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt);
void pxa2xx_drv_pcmcia_ops(struct pcmcia_low_level *ops);
void pxa2xx_configure_sockets(struct device *dev);

View file

@ -0,0 +1,128 @@
/*
* linux/drivers/pcmcia/pxa/pxa_cm_x255.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Compulab Ltd., 2003, 2007, 2008
* Mike Rapoport <mike@compulab.co.il>
*
*/
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/export.h>
#include "soc_common.h"
#define GPIO_PCMCIA_SKTSEL (54)
#define GPIO_PCMCIA_S0_CD_VALID (16)
#define GPIO_PCMCIA_S1_CD_VALID (17)
#define GPIO_PCMCIA_S0_RDYINT (6)
#define GPIO_PCMCIA_S1_RDYINT (8)
#define GPIO_PCMCIA_RESET (9)
static int cmx255_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int ret = gpio_request(GPIO_PCMCIA_RESET, "PCCard reset");
if (ret)
return ret;
gpio_direction_output(GPIO_PCMCIA_RESET, 0);
if (skt->nr == 0) {
skt->stat[SOC_STAT_CD].gpio = GPIO_PCMCIA_S0_CD_VALID;
skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD";
skt->stat[SOC_STAT_RDY].gpio = GPIO_PCMCIA_S0_RDYINT;
skt->stat[SOC_STAT_RDY].name = "PCMCIA0 RDY";
} else {
skt->stat[SOC_STAT_CD].gpio = GPIO_PCMCIA_S1_CD_VALID;
skt->stat[SOC_STAT_CD].name = "PCMCIA1 CD";
skt->stat[SOC_STAT_RDY].gpio = GPIO_PCMCIA_S1_RDYINT;
skt->stat[SOC_STAT_RDY].name = "PCMCIA1 RDY";
}
return 0;
}
static void cmx255_pcmcia_shutdown(struct soc_pcmcia_socket *skt)
{
gpio_free(GPIO_PCMCIA_RESET);
}
static void cmx255_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
state->vs_3v = 0;
state->vs_Xv = 0;
}
static int cmx255_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
switch (skt->nr) {
case 0:
if (state->flags & SS_RESET) {
gpio_set_value(GPIO_PCMCIA_SKTSEL, 0);
udelay(1);
gpio_set_value(GPIO_PCMCIA_RESET, 1);
udelay(10);
gpio_set_value(GPIO_PCMCIA_RESET, 0);
}
break;
case 1:
if (state->flags & SS_RESET) {
gpio_set_value(GPIO_PCMCIA_SKTSEL, 1);
udelay(1);
gpio_set_value(GPIO_PCMCIA_RESET, 1);
udelay(10);
gpio_set_value(GPIO_PCMCIA_RESET, 0);
}
break;
}
return 0;
}
static struct pcmcia_low_level cmx255_pcmcia_ops __initdata = {
.owner = THIS_MODULE,
.hw_init = cmx255_pcmcia_hw_init,
.hw_shutdown = cmx255_pcmcia_shutdown,
.socket_state = cmx255_pcmcia_socket_state,
.configure_socket = cmx255_pcmcia_configure_socket,
.nr = 1,
};
static struct platform_device *cmx255_pcmcia_device;
int __init cmx255_pcmcia_init(void)
{
int ret;
cmx255_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!cmx255_pcmcia_device)
return -ENOMEM;
ret = platform_device_add_data(cmx255_pcmcia_device, &cmx255_pcmcia_ops,
sizeof(cmx255_pcmcia_ops));
if (ret == 0) {
printk(KERN_INFO "Registering cm-x255 PCMCIA interface.\n");
ret = platform_device_add(cmx255_pcmcia_device);
}
if (ret)
platform_device_put(cmx255_pcmcia_device);
return ret;
}
void __exit cmx255_pcmcia_exit(void)
{
platform_device_unregister(cmx255_pcmcia_device);
}

View file

@ -0,0 +1,107 @@
/*
* linux/drivers/pcmcia/pxa/pxa_cm_x270.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Compulab Ltd., 2003, 2007, 2008
* Mike Rapoport <mike@compulab.co.il>
*
*/
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/export.h>
#include "soc_common.h"
#define GPIO_PCMCIA_S0_CD_VALID (84)
#define GPIO_PCMCIA_S0_RDYINT (82)
#define GPIO_PCMCIA_RESET (53)
static int cmx270_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int ret = gpio_request(GPIO_PCMCIA_RESET, "PCCard reset");
if (ret)
return ret;
gpio_direction_output(GPIO_PCMCIA_RESET, 0);
skt->stat[SOC_STAT_CD].gpio = GPIO_PCMCIA_S0_CD_VALID;
skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD";
skt->stat[SOC_STAT_RDY].gpio = GPIO_PCMCIA_S0_RDYINT;
skt->stat[SOC_STAT_RDY].name = "PCMCIA0 RDY";
return ret;
}
static void cmx270_pcmcia_shutdown(struct soc_pcmcia_socket *skt)
{
gpio_free(GPIO_PCMCIA_RESET);
}
static void cmx270_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
state->vs_3v = 0;
state->vs_Xv = 0;
}
static int cmx270_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
switch (skt->nr) {
case 0:
if (state->flags & SS_RESET) {
gpio_set_value(GPIO_PCMCIA_RESET, 1);
udelay(10);
gpio_set_value(GPIO_PCMCIA_RESET, 0);
}
break;
}
return 0;
}
static struct pcmcia_low_level cmx270_pcmcia_ops __initdata = {
.owner = THIS_MODULE,
.hw_init = cmx270_pcmcia_hw_init,
.hw_shutdown = cmx270_pcmcia_shutdown,
.socket_state = cmx270_pcmcia_socket_state,
.configure_socket = cmx270_pcmcia_configure_socket,
.nr = 1,
};
static struct platform_device *cmx270_pcmcia_device;
int __init cmx270_pcmcia_init(void)
{
int ret;
cmx270_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!cmx270_pcmcia_device)
return -ENOMEM;
ret = platform_device_add_data(cmx270_pcmcia_device, &cmx270_pcmcia_ops,
sizeof(cmx270_pcmcia_ops));
if (ret == 0) {
printk(KERN_INFO "Registering cm-x270 PCMCIA interface.\n");
ret = platform_device_add(cmx270_pcmcia_device);
}
if (ret)
platform_device_put(cmx270_pcmcia_device);
return ret;
}
void __exit cmx270_pcmcia_exit(void)
{
platform_device_unregister(cmx270_pcmcia_device);
}

View file

@ -0,0 +1,48 @@
/*
* linux/drivers/pcmcia/pxa/pxa_cm_x2xx.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Compulab Ltd., 2003, 2007, 2008
* Mike Rapoport <mike@compulab.co.il>
*
*/
#include <linux/module.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
int cmx255_pcmcia_init(void);
int cmx270_pcmcia_init(void);
void cmx255_pcmcia_exit(void);
void cmx270_pcmcia_exit(void);
static int __init cmx2xx_pcmcia_init(void)
{
int ret = -ENODEV;
if (machine_is_armcore() && cpu_is_pxa25x())
ret = cmx255_pcmcia_init();
else if (machine_is_armcore() && cpu_is_pxa27x())
ret = cmx270_pcmcia_init();
return ret;
}
static void __exit cmx2xx_pcmcia_exit(void)
{
if (machine_is_armcore() && cpu_is_pxa25x())
cmx255_pcmcia_exit();
else if (machine_is_armcore() && cpu_is_pxa27x())
cmx270_pcmcia_exit();
}
module_init(cmx2xx_pcmcia_init);
module_exit(cmx2xx_pcmcia_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
MODULE_DESCRIPTION("CM-x2xx PCMCIA driver");

View file

@ -0,0 +1,169 @@
/*
* linux/drivers/pcmcia/pxa2xx_colibri.c
*
* Driver for Toradex Colibri PXA270 CF socket
*
* Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <asm/mach-types.h>
#include "soc_common.h"
#define COLIBRI270_RESET_GPIO 53
#define COLIBRI270_PPEN_GPIO 107
#define COLIBRI270_BVD1_GPIO 83
#define COLIBRI270_BVD2_GPIO 82
#define COLIBRI270_DETECT_GPIO 84
#define COLIBRI270_READY_GPIO 1
#define COLIBRI320_RESET_GPIO 77
#define COLIBRI320_PPEN_GPIO 57
#define COLIBRI320_BVD1_GPIO 53
#define COLIBRI320_BVD2_GPIO 79
#define COLIBRI320_DETECT_GPIO 81
#define COLIBRI320_READY_GPIO 29
enum {
DETECT = 0,
READY = 1,
BVD1 = 2,
BVD2 = 3,
PPEN = 4,
RESET = 5,
};
/* Contents of this array are configured on-the-fly in init function */
static struct gpio colibri_pcmcia_gpios[] = {
{ 0, GPIOF_IN, "PCMCIA Detect" },
{ 0, GPIOF_IN, "PCMCIA Ready" },
{ 0, GPIOF_IN, "PCMCIA BVD1" },
{ 0, GPIOF_IN, "PCMCIA BVD2" },
{ 0, GPIOF_INIT_LOW, "PCMCIA PPEN" },
{ 0, GPIOF_INIT_HIGH,"PCMCIA Reset" },
};
static int colibri_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int ret;
ret = gpio_request_array(colibri_pcmcia_gpios,
ARRAY_SIZE(colibri_pcmcia_gpios));
if (ret)
goto err1;
skt->socket.pci_irq = gpio_to_irq(colibri_pcmcia_gpios[READY].gpio);
skt->stat[SOC_STAT_CD].irq = gpio_to_irq(colibri_pcmcia_gpios[DETECT].gpio);
skt->stat[SOC_STAT_CD].name = "PCMCIA CD";
err1:
return ret;
}
static void colibri_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
gpio_free_array(colibri_pcmcia_gpios,
ARRAY_SIZE(colibri_pcmcia_gpios));
}
static void colibri_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
state->detect = !!gpio_get_value(colibri_pcmcia_gpios[DETECT].gpio);
state->ready = !!gpio_get_value(colibri_pcmcia_gpios[READY].gpio);
state->bvd1 = !!gpio_get_value(colibri_pcmcia_gpios[BVD1].gpio);
state->bvd2 = !!gpio_get_value(colibri_pcmcia_gpios[BVD2].gpio);
state->vs_3v = 1;
state->vs_Xv = 0;
}
static int
colibri_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
gpio_set_value(colibri_pcmcia_gpios[PPEN].gpio,
!(state->Vcc == 33 && state->Vpp < 50));
gpio_set_value(colibri_pcmcia_gpios[RESET].gpio,
state->flags & SS_RESET);
return 0;
}
static struct pcmcia_low_level colibri_pcmcia_ops = {
.owner = THIS_MODULE,
.first = 0,
.nr = 1,
.hw_init = colibri_pcmcia_hw_init,
.hw_shutdown = colibri_pcmcia_hw_shutdown,
.socket_state = colibri_pcmcia_socket_state,
.configure_socket = colibri_pcmcia_configure_socket,
};
static struct platform_device *colibri_pcmcia_device;
static int __init colibri_pcmcia_init(void)
{
int ret;
if (!machine_is_colibri() && !machine_is_colibri320())
return -ENODEV;
colibri_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!colibri_pcmcia_device)
return -ENOMEM;
/* Colibri PXA270 */
if (machine_is_colibri()) {
colibri_pcmcia_gpios[RESET].gpio = COLIBRI270_RESET_GPIO;
colibri_pcmcia_gpios[PPEN].gpio = COLIBRI270_PPEN_GPIO;
colibri_pcmcia_gpios[BVD1].gpio = COLIBRI270_BVD1_GPIO;
colibri_pcmcia_gpios[BVD2].gpio = COLIBRI270_BVD2_GPIO;
colibri_pcmcia_gpios[DETECT].gpio = COLIBRI270_DETECT_GPIO;
colibri_pcmcia_gpios[READY].gpio = COLIBRI270_READY_GPIO;
/* Colibri PXA320 */
} else if (machine_is_colibri320()) {
colibri_pcmcia_gpios[RESET].gpio = COLIBRI320_RESET_GPIO;
colibri_pcmcia_gpios[PPEN].gpio = COLIBRI320_PPEN_GPIO;
colibri_pcmcia_gpios[BVD1].gpio = COLIBRI320_BVD1_GPIO;
colibri_pcmcia_gpios[BVD2].gpio = COLIBRI320_BVD2_GPIO;
colibri_pcmcia_gpios[DETECT].gpio = COLIBRI320_DETECT_GPIO;
colibri_pcmcia_gpios[READY].gpio = COLIBRI320_READY_GPIO;
}
ret = platform_device_add_data(colibri_pcmcia_device,
&colibri_pcmcia_ops, sizeof(colibri_pcmcia_ops));
if (!ret)
ret = platform_device_add(colibri_pcmcia_device);
if (ret)
platform_device_put(colibri_pcmcia_device);
return ret;
}
static void __exit colibri_pcmcia_exit(void)
{
platform_device_unregister(colibri_pcmcia_device);
}
module_init(colibri_pcmcia_init);
module_exit(colibri_pcmcia_exit);
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
MODULE_DESCRIPTION("PCMCIA support for Toradex Colibri PXA270/PXA320");
MODULE_ALIAS("platform:pxa2xx-pcmcia");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,130 @@
/*
* Toshiba e740 PCMCIA specific routines.
*
* (c) 2004 Ian Molton <spyro@f2s.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <mach/eseries-gpio.h>
#include <asm/irq.h>
#include <asm/mach-types.h>
#include "soc_common.h"
static int e740_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
if (skt->nr == 0) {
skt->stat[SOC_STAT_CD].gpio = GPIO_E740_PCMCIA_CD0;
skt->stat[SOC_STAT_CD].name = "CF card detect";
skt->stat[SOC_STAT_RDY].gpio = GPIO_E740_PCMCIA_RDY0;
skt->stat[SOC_STAT_RDY].name = "CF ready";
} else {
skt->stat[SOC_STAT_CD].gpio = GPIO_E740_PCMCIA_CD1;
skt->stat[SOC_STAT_CD].name = "Wifi switch";
skt->stat[SOC_STAT_RDY].gpio = GPIO_E740_PCMCIA_RDY1;
skt->stat[SOC_STAT_RDY].name = "Wifi ready";
}
return 0;
}
static void e740_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
state->vs_3v = 1;
state->vs_Xv = 0;
}
static int e740_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
if (state->flags & SS_RESET) {
if (skt->nr == 0)
gpio_set_value(GPIO_E740_PCMCIA_RST0, 1);
else
gpio_set_value(GPIO_E740_PCMCIA_RST1, 1);
} else {
if (skt->nr == 0)
gpio_set_value(GPIO_E740_PCMCIA_RST0, 0);
else
gpio_set_value(GPIO_E740_PCMCIA_RST1, 0);
}
switch (state->Vcc) {
case 0: /* Socket off */
if (skt->nr == 0)
gpio_set_value(GPIO_E740_PCMCIA_PWR0, 0);
else
gpio_set_value(GPIO_E740_PCMCIA_PWR1, 1);
break;
case 50:
case 33: /* socket on */
if (skt->nr == 0)
gpio_set_value(GPIO_E740_PCMCIA_PWR0, 1);
else
gpio_set_value(GPIO_E740_PCMCIA_PWR1, 0);
break;
default:
printk(KERN_ERR "e740_cs: Unsupported Vcc: %d\n", state->Vcc);
}
return 0;
}
static struct pcmcia_low_level e740_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = e740_pcmcia_hw_init,
.socket_state = e740_pcmcia_socket_state,
.configure_socket = e740_pcmcia_configure_socket,
.nr = 2,
};
static struct platform_device *e740_pcmcia_device;
static int __init e740_pcmcia_init(void)
{
int ret;
if (!machine_is_e740())
return -ENODEV;
e740_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!e740_pcmcia_device)
return -ENOMEM;
ret = platform_device_add_data(e740_pcmcia_device, &e740_pcmcia_ops,
sizeof(e740_pcmcia_ops));
if (!ret)
ret = platform_device_add(e740_pcmcia_device);
if (ret)
platform_device_put(e740_pcmcia_device);
return ret;
}
static void __exit e740_pcmcia_exit(void)
{
platform_device_unregister(e740_pcmcia_device);
}
module_init(e740_pcmcia_init);
module_exit(e740_pcmcia_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
MODULE_ALIAS("platform:pxa2xx-pcmcia");
MODULE_DESCRIPTION("e740 PCMCIA platform support");

View file

@ -0,0 +1,121 @@
/*
* Copyright (C) 2012 Paul Parsons <lost.distance@yahoo.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <asm/mach-types.h>
#include <mach/hx4700.h>
#include "soc_common.h"
static struct gpio gpios[] = {
{ GPIO114_HX4700_CF_RESET, GPIOF_OUT_INIT_LOW, "CF reset" },
{ EGPIO4_CF_3V3_ON, GPIOF_OUT_INIT_LOW, "CF 3.3V enable" },
};
static int hx4700_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int ret;
ret = gpio_request_array(gpios, ARRAY_SIZE(gpios));
if (ret)
goto out;
/*
* IRQ type must be set before soc_pcmcia_hw_init() calls request_irq().
* The asic3 default IRQ type is level trigger low level detect, exactly
* the the signal present on GPIOD4_CF_nCD when a CF card is inserted.
* If the IRQ type is not changed, the asic3 interrupt handler will loop
* repeatedly because it is unable to clear the level trigger interrupt.
*/
irq_set_irq_type(gpio_to_irq(GPIOD4_CF_nCD), IRQ_TYPE_EDGE_BOTH);
skt->stat[SOC_STAT_CD].gpio = GPIOD4_CF_nCD;
skt->stat[SOC_STAT_CD].name = "PCMCIA CD";
skt->stat[SOC_STAT_RDY].gpio = GPIO60_HX4700_CF_RNB;
skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready";
out:
return ret;
}
static void hx4700_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
gpio_free_array(gpios, ARRAY_SIZE(gpios));
}
static void hx4700_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
state->vs_3v = 1;
state->vs_Xv = 0;
}
static int hx4700_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
switch (state->Vcc) {
case 0:
gpio_set_value(EGPIO4_CF_3V3_ON, 0);
break;
case 33:
gpio_set_value(EGPIO4_CF_3V3_ON, 1);
break;
default:
printk(KERN_ERR "pcmcia: Unsupported Vcc: %d\n", state->Vcc);
return -EINVAL;
}
gpio_set_value(GPIO114_HX4700_CF_RESET, (state->flags & SS_RESET) != 0);
return 0;
}
static struct pcmcia_low_level hx4700_pcmcia_ops = {
.owner = THIS_MODULE,
.nr = 1,
.hw_init = hx4700_pcmcia_hw_init,
.hw_shutdown = hx4700_pcmcia_hw_shutdown,
.socket_state = hx4700_pcmcia_socket_state,
.configure_socket = hx4700_pcmcia_configure_socket,
};
static struct platform_device *hx4700_pcmcia_device;
static int __init hx4700_pcmcia_init(void)
{
struct platform_device *pdev;
if (!machine_is_h4700())
return -ENODEV;
pdev = platform_device_register_data(NULL, "pxa2xx-pcmcia", -1,
&hx4700_pcmcia_ops, sizeof(hx4700_pcmcia_ops));
if (IS_ERR(pdev))
return PTR_ERR(pdev);
hx4700_pcmcia_device = pdev;
return 0;
}
static void __exit hx4700_pcmcia_exit(void)
{
platform_device_unregister(hx4700_pcmcia_device);
}
module_init(hx4700_pcmcia_init);
module_exit(hx4700_pcmcia_exit);
MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>");
MODULE_DESCRIPTION("HP iPAQ hx4700 PCMCIA driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,170 @@
/*
* linux/drivers/pcmcia/pxa2xx_mainstone.c
*
* Mainstone PCMCIA specific routines.
*
* Created: May 12, 2004
* Author: Nicolas Pitre
* Copyright: MontaVista Software Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <pcmcia/ss.h>
#include <asm/mach-types.h>
#include <asm/irq.h>
#include <mach/pxa2xx-regs.h>
#include <mach/mainstone.h>
#include "soc_common.h"
static int mst_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
/*
* Setup default state of GPIO outputs
* before we enable them as outputs.
*/
if (skt->nr == 0) {
skt->socket.pci_irq = MAINSTONE_S0_IRQ;
skt->stat[SOC_STAT_CD].irq = MAINSTONE_S0_CD_IRQ;
skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD";
skt->stat[SOC_STAT_BVD1].irq = MAINSTONE_S0_STSCHG_IRQ;
skt->stat[SOC_STAT_BVD1].name = "PCMCIA0 STSCHG";
} else {
skt->socket.pci_irq = MAINSTONE_S1_IRQ;
skt->stat[SOC_STAT_CD].irq = MAINSTONE_S1_CD_IRQ;
skt->stat[SOC_STAT_CD].name = "PCMCIA1 CD";
skt->stat[SOC_STAT_BVD1].irq = MAINSTONE_S1_STSCHG_IRQ;
skt->stat[SOC_STAT_BVD1].name = "PCMCIA1 STSCHG";
}
return 0;
}
static unsigned long mst_pcmcia_status[2];
static void mst_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
unsigned long status, flip;
status = (skt->nr == 0) ? MST_PCMCIA0 : MST_PCMCIA1;
flip = (status ^ mst_pcmcia_status[skt->nr]) & MST_PCMCIA_nSTSCHG_BVD1;
/*
* Workaround for STSCHG which can't be deasserted:
* We therefore disable/enable corresponding IRQs
* as needed to avoid IRQ locks.
*/
if (flip) {
mst_pcmcia_status[skt->nr] = status;
if (status & MST_PCMCIA_nSTSCHG_BVD1)
enable_irq( (skt->nr == 0) ? MAINSTONE_S0_STSCHG_IRQ
: MAINSTONE_S1_STSCHG_IRQ );
else
disable_irq( (skt->nr == 0) ? MAINSTONE_S0_STSCHG_IRQ
: MAINSTONE_S1_STSCHG_IRQ );
}
state->detect = (status & MST_PCMCIA_nCD) ? 0 : 1;
state->ready = (status & MST_PCMCIA_nIRQ) ? 1 : 0;
state->bvd1 = (status & MST_PCMCIA_nSTSCHG_BVD1) ? 1 : 0;
state->bvd2 = (status & MST_PCMCIA_nSPKR_BVD2) ? 1 : 0;
state->vs_3v = (status & MST_PCMCIA_nVS1) ? 0 : 1;
state->vs_Xv = (status & MST_PCMCIA_nVS2) ? 0 : 1;
}
static int mst_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
unsigned long power = 0;
int ret = 0;
switch (state->Vcc) {
case 0: power |= MST_PCMCIA_PWR_VCC_0; break;
case 33: power |= MST_PCMCIA_PWR_VCC_33; break;
case 50: power |= MST_PCMCIA_PWR_VCC_50; break;
default:
printk(KERN_ERR "%s(): bad Vcc %u\n",
__func__, state->Vcc);
ret = -1;
}
switch (state->Vpp) {
case 0: power |= MST_PCMCIA_PWR_VPP_0; break;
case 120: power |= MST_PCMCIA_PWR_VPP_120; break;
default:
if(state->Vpp == state->Vcc) {
power |= MST_PCMCIA_PWR_VPP_VCC;
} else {
printk(KERN_ERR "%s(): bad Vpp %u\n",
__func__, state->Vpp);
ret = -1;
}
}
if (state->flags & SS_RESET)
power |= MST_PCMCIA_RESET;
switch (skt->nr) {
case 0: MST_PCMCIA0 = power; break;
case 1: MST_PCMCIA1 = power; break;
default: ret = -1;
}
return ret;
}
static struct pcmcia_low_level mst_pcmcia_ops __initdata = {
.owner = THIS_MODULE,
.hw_init = mst_pcmcia_hw_init,
.socket_state = mst_pcmcia_socket_state,
.configure_socket = mst_pcmcia_configure_socket,
.nr = 2,
};
static struct platform_device *mst_pcmcia_device;
static int __init mst_pcmcia_init(void)
{
int ret;
if (!machine_is_mainstone())
return -ENODEV;
mst_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!mst_pcmcia_device)
return -ENOMEM;
ret = platform_device_add_data(mst_pcmcia_device, &mst_pcmcia_ops,
sizeof(mst_pcmcia_ops));
if (ret == 0)
ret = platform_device_add(mst_pcmcia_device);
if (ret)
platform_device_put(mst_pcmcia_device);
return ret;
}
static void __exit mst_pcmcia_exit(void)
{
platform_device_unregister(mst_pcmcia_device);
}
fs_initcall(mst_pcmcia_init);
module_exit(mst_pcmcia_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pxa2xx-pcmcia");

View file

@ -0,0 +1,114 @@
/*
* linux/drivers/pcmcia/pxa2xx_palmld.c
*
* Driver for Palm LifeDrive PCMCIA
*
* Copyright (C) 2006 Alex Osborne <ato@meshy.org>
* Copyright (C) 2007-2011 Marek Vasut <marek.vasut@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <asm/mach-types.h>
#include <mach/palmld.h>
#include "soc_common.h"
static struct gpio palmld_pcmcia_gpios[] = {
{ GPIO_NR_PALMLD_PCMCIA_POWER, GPIOF_INIT_LOW, "PCMCIA Power" },
{ GPIO_NR_PALMLD_PCMCIA_RESET, GPIOF_INIT_HIGH,"PCMCIA Reset" },
};
static int palmld_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int ret;
ret = gpio_request_array(palmld_pcmcia_gpios,
ARRAY_SIZE(palmld_pcmcia_gpios));
skt->stat[SOC_STAT_RDY].gpio = GPIO_NR_PALMLD_PCMCIA_READY;
skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready";
return ret;
}
static void palmld_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
gpio_free_array(palmld_pcmcia_gpios, ARRAY_SIZE(palmld_pcmcia_gpios));
}
static void palmld_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
state->detect = 1; /* always inserted */
state->vs_3v = 1;
state->vs_Xv = 0;
}
static int palmld_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
gpio_set_value(GPIO_NR_PALMLD_PCMCIA_POWER, 1);
gpio_set_value(GPIO_NR_PALMLD_PCMCIA_RESET,
!!(state->flags & SS_RESET));
return 0;
}
static struct pcmcia_low_level palmld_pcmcia_ops = {
.owner = THIS_MODULE,
.first = 1,
.nr = 1,
.hw_init = palmld_pcmcia_hw_init,
.hw_shutdown = palmld_pcmcia_hw_shutdown,
.socket_state = palmld_pcmcia_socket_state,
.configure_socket = palmld_pcmcia_configure_socket,
};
static struct platform_device *palmld_pcmcia_device;
static int __init palmld_pcmcia_init(void)
{
int ret;
if (!machine_is_palmld())
return -ENODEV;
palmld_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!palmld_pcmcia_device)
return -ENOMEM;
ret = platform_device_add_data(palmld_pcmcia_device, &palmld_pcmcia_ops,
sizeof(palmld_pcmcia_ops));
if (!ret)
ret = platform_device_add(palmld_pcmcia_device);
if (ret)
platform_device_put(palmld_pcmcia_device);
return ret;
}
static void __exit palmld_pcmcia_exit(void)
{
platform_device_unregister(palmld_pcmcia_device);
}
module_init(palmld_pcmcia_init);
module_exit(palmld_pcmcia_exit);
MODULE_AUTHOR("Alex Osborne <ato@meshy.org>,"
" Marek Vasut <marek.vasut@gmail.com>");
MODULE_DESCRIPTION("PCMCIA support for Palm LifeDrive");
MODULE_ALIAS("platform:pxa2xx-pcmcia");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,166 @@
/*
* linux/drivers/pcmcia/pxa2xx_palmtc.c
*
* Driver for Palm Tungsten|C PCMCIA
*
* Copyright (C) 2008 Alex Osborne <ato@meshy.org>
* Copyright (C) 2009-2011 Marek Vasut <marek.vasut@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <asm/mach-types.h>
#include <mach/palmtc.h>
#include "soc_common.h"
static struct gpio palmtc_pcmcia_gpios[] = {
{ GPIO_NR_PALMTC_PCMCIA_POWER1, GPIOF_INIT_LOW, "PCMCIA Power 1" },
{ GPIO_NR_PALMTC_PCMCIA_POWER2, GPIOF_INIT_LOW, "PCMCIA Power 2" },
{ GPIO_NR_PALMTC_PCMCIA_POWER3, GPIOF_INIT_LOW, "PCMCIA Power 3" },
{ GPIO_NR_PALMTC_PCMCIA_RESET, GPIOF_INIT_HIGH,"PCMCIA Reset" },
{ GPIO_NR_PALMTC_PCMCIA_PWRREADY, GPIOF_IN, "PCMCIA Power Ready" },
};
static int palmtc_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int ret;
ret = gpio_request_array(palmtc_pcmcia_gpios,
ARRAY_SIZE(palmtc_pcmcia_gpios));
skt->stat[SOC_STAT_RDY].gpio = GPIO_NR_PALMTC_PCMCIA_READY;
skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready";
return ret;
}
static void palmtc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
gpio_free_array(palmtc_pcmcia_gpios, ARRAY_SIZE(palmtc_pcmcia_gpios));
}
static void palmtc_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
state->detect = 1; /* always inserted */
state->vs_3v = 1;
state->vs_Xv = 0;
}
static int palmtc_wifi_powerdown(void)
{
gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 1);
gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER2, 0);
mdelay(40);
gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER1, 0);
return 0;
}
static int palmtc_wifi_powerup(void)
{
int timeout = 50;
gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER3, 1);
mdelay(50);
/* Power up the card, 1.8V first, after a while 3.3V */
gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER1, 1);
mdelay(100);
gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER2, 1);
/* Wait till the card is ready */
while (!gpio_get_value(GPIO_NR_PALMTC_PCMCIA_PWRREADY) &&
timeout) {
mdelay(1);
timeout--;
}
/* Power down the WiFi in case of error */
if (!timeout) {
palmtc_wifi_powerdown();
return 1;
}
/* Reset the card */
gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 1);
mdelay(20);
gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 0);
mdelay(25);
gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER3, 0);
return 0;
}
static int palmtc_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
int ret = 1;
if (state->Vcc == 0)
ret = palmtc_wifi_powerdown();
else if (state->Vcc == 33)
ret = palmtc_wifi_powerup();
return ret;
}
static struct pcmcia_low_level palmtc_pcmcia_ops = {
.owner = THIS_MODULE,
.first = 0,
.nr = 1,
.hw_init = palmtc_pcmcia_hw_init,
.hw_shutdown = palmtc_pcmcia_hw_shutdown,
.socket_state = palmtc_pcmcia_socket_state,
.configure_socket = palmtc_pcmcia_configure_socket,
};
static struct platform_device *palmtc_pcmcia_device;
static int __init palmtc_pcmcia_init(void)
{
int ret;
if (!machine_is_palmtc())
return -ENODEV;
palmtc_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!palmtc_pcmcia_device)
return -ENOMEM;
ret = platform_device_add_data(palmtc_pcmcia_device, &palmtc_pcmcia_ops,
sizeof(palmtc_pcmcia_ops));
if (!ret)
ret = platform_device_add(palmtc_pcmcia_device);
if (ret)
platform_device_put(palmtc_pcmcia_device);
return ret;
}
static void __exit palmtc_pcmcia_exit(void)
{
platform_device_unregister(palmtc_pcmcia_device);
}
module_init(palmtc_pcmcia_init);
module_exit(palmtc_pcmcia_exit);
MODULE_AUTHOR("Alex Osborne <ato@meshy.org>,"
" Marek Vasut <marek.vasut@gmail.com>");
MODULE_DESCRIPTION("PCMCIA support for Palm Tungsten|C");
MODULE_ALIAS("platform:pxa2xx-pcmcia");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,115 @@
/*
* linux/drivers/pcmcia/pxa2xx_palmtx.c
*
* Driver for Palm T|X PCMCIA
*
* Copyright (C) 2007-2011 Marek Vasut <marek.vasut@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <asm/mach-types.h>
#include <mach/palmtx.h>
#include "soc_common.h"
static struct gpio palmtx_pcmcia_gpios[] = {
{ GPIO_NR_PALMTX_PCMCIA_POWER1, GPIOF_INIT_LOW, "PCMCIA Power 1" },
{ GPIO_NR_PALMTX_PCMCIA_POWER2, GPIOF_INIT_LOW, "PCMCIA Power 2" },
{ GPIO_NR_PALMTX_PCMCIA_RESET, GPIOF_INIT_HIGH,"PCMCIA Reset" },
};
static int palmtx_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int ret;
ret = gpio_request_array(palmtx_pcmcia_gpios,
ARRAY_SIZE(palmtx_pcmcia_gpios));
skt->stat[SOC_STAT_RDY].gpio = GPIO_NR_PALMTX_PCMCIA_READY;
skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready";
return ret;
}
static void palmtx_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
gpio_free_array(palmtx_pcmcia_gpios, ARRAY_SIZE(palmtx_pcmcia_gpios));
}
static void palmtx_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
state->detect = 1; /* always inserted */
state->vs_3v = 1;
state->vs_Xv = 0;
}
static int
palmtx_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
gpio_set_value(GPIO_NR_PALMTX_PCMCIA_POWER1, 1);
gpio_set_value(GPIO_NR_PALMTX_PCMCIA_POWER2, 1);
gpio_set_value(GPIO_NR_PALMTX_PCMCIA_RESET,
!!(state->flags & SS_RESET));
return 0;
}
static struct pcmcia_low_level palmtx_pcmcia_ops = {
.owner = THIS_MODULE,
.first = 0,
.nr = 1,
.hw_init = palmtx_pcmcia_hw_init,
.hw_shutdown = palmtx_pcmcia_hw_shutdown,
.socket_state = palmtx_pcmcia_socket_state,
.configure_socket = palmtx_pcmcia_configure_socket,
};
static struct platform_device *palmtx_pcmcia_device;
static int __init palmtx_pcmcia_init(void)
{
int ret;
if (!machine_is_palmtx())
return -ENODEV;
palmtx_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!palmtx_pcmcia_device)
return -ENOMEM;
ret = platform_device_add_data(palmtx_pcmcia_device, &palmtx_pcmcia_ops,
sizeof(palmtx_pcmcia_ops));
if (!ret)
ret = platform_device_add(palmtx_pcmcia_device);
if (ret)
platform_device_put(palmtx_pcmcia_device);
return ret;
}
static void __exit palmtx_pcmcia_exit(void)
{
platform_device_unregister(palmtx_pcmcia_device);
}
module_init(palmtx_pcmcia_init);
module_exit(palmtx_pcmcia_exit);
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
MODULE_DESCRIPTION("PCMCIA support for Palm T|X");
MODULE_ALIAS("platform:pxa2xx-pcmcia");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,262 @@
/*
* Sharp SL-C7xx Series PCMCIA routines
*
* Copyright (c) 2004-2005 Richard Purdie
*
* Based on Sharp's 2.4 kernel patches and pxa2xx_mainstone.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/hardware/scoop.h>
#include "soc_common.h"
#define NO_KEEP_VS 0x0001
#define SCOOP_DEV platform_scoop_config->devs
static void sharpsl_pcmcia_init_reset(struct soc_pcmcia_socket *skt)
{
struct scoop_pcmcia_dev *scoopdev = &SCOOP_DEV[skt->nr];
reset_scoop(scoopdev->dev);
/* Shared power controls need to be handled carefully */
if (platform_scoop_config->power_ctrl)
platform_scoop_config->power_ctrl(scoopdev->dev, 0x0000, skt->nr);
else
write_scoop_reg(scoopdev->dev, SCOOP_CPR, 0x0000);
scoopdev->keep_vs = NO_KEEP_VS;
scoopdev->keep_rd = 0;
}
static int sharpsl_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
if (SCOOP_DEV[skt->nr].cd_irq >= 0) {
skt->stat[SOC_STAT_CD].irq = SCOOP_DEV[skt->nr].cd_irq;
skt->stat[SOC_STAT_CD].name = SCOOP_DEV[skt->nr].cd_irq_str;
}
skt->socket.pci_irq = SCOOP_DEV[skt->nr].irq;
return 0;
}
static void sharpsl_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
unsigned short cpr, csr;
struct device *scoop = SCOOP_DEV[skt->nr].dev;
cpr = read_scoop_reg(SCOOP_DEV[skt->nr].dev, SCOOP_CPR);
write_scoop_reg(scoop, SCOOP_IRM, 0x00FF);
write_scoop_reg(scoop, SCOOP_ISR, 0x0000);
write_scoop_reg(scoop, SCOOP_IRM, 0x0000);
csr = read_scoop_reg(scoop, SCOOP_CSR);
if (csr & 0x0004) {
/* card eject */
write_scoop_reg(scoop, SCOOP_CDR, 0x0000);
SCOOP_DEV[skt->nr].keep_vs = NO_KEEP_VS;
}
else if (!(SCOOP_DEV[skt->nr].keep_vs & NO_KEEP_VS)) {
/* keep vs1,vs2 */
write_scoop_reg(scoop, SCOOP_CDR, 0x0000);
csr |= SCOOP_DEV[skt->nr].keep_vs;
}
else if (cpr & 0x0003) {
/* power on */
write_scoop_reg(scoop, SCOOP_CDR, 0x0000);
SCOOP_DEV[skt->nr].keep_vs = (csr & 0x00C0);
}
else {
/* card detect */
if ((machine_is_spitz() || machine_is_borzoi()) && skt->nr == 1) {
write_scoop_reg(scoop, SCOOP_CDR, 0x0000);
} else {
write_scoop_reg(scoop, SCOOP_CDR, 0x0002);
}
}
state->detect = (csr & 0x0004) ? 0 : 1;
state->ready = (csr & 0x0002) ? 1 : 0;
state->bvd1 = (csr & 0x0010) ? 1 : 0;
state->bvd2 = (csr & 0x0020) ? 1 : 0;
state->wrprot = (csr & 0x0008) ? 1 : 0;
state->vs_3v = (csr & 0x0040) ? 0 : 1;
state->vs_Xv = (csr & 0x0080) ? 0 : 1;
if ((cpr & 0x0080) && ((cpr & 0x8040) != 0x8040)) {
printk(KERN_ERR "sharpsl_pcmcia_socket_state(): CPR=%04X, Low voltage!\n", cpr);
}
}
static int sharpsl_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
unsigned long flags;
struct device *scoop = SCOOP_DEV[skt->nr].dev;
unsigned short cpr, ncpr, ccr, nccr, mcr, nmcr, imr, nimr;
switch (state->Vcc) {
case 0: break;
case 33: break;
case 50: break;
default:
printk(KERN_ERR "sharpsl_pcmcia_configure_socket(): bad Vcc %u\n", state->Vcc);
return -1;
}
if ((state->Vpp!=state->Vcc) && (state->Vpp!=0)) {
printk(KERN_ERR "CF slot cannot support Vpp %u\n", state->Vpp);
return -1;
}
local_irq_save(flags);
nmcr = (mcr = read_scoop_reg(scoop, SCOOP_MCR)) & ~0x0010;
ncpr = (cpr = read_scoop_reg(scoop, SCOOP_CPR)) & ~0x0083;
nccr = (ccr = read_scoop_reg(scoop, SCOOP_CCR)) & ~0x0080;
nimr = (imr = read_scoop_reg(scoop, SCOOP_IMR)) & ~0x003E;
if ((machine_is_spitz() || machine_is_borzoi() || machine_is_akita()) && skt->nr == 0) {
ncpr |= (state->Vcc == 33) ? 0x0002 :
(state->Vcc == 50) ? 0x0002 : 0;
} else {
ncpr |= (state->Vcc == 33) ? 0x0001 :
(state->Vcc == 50) ? 0x0002 : 0;
}
nmcr |= (state->flags&SS_IOCARD) ? 0x0010 : 0;
ncpr |= (state->flags&SS_OUTPUT_ENA) ? 0x0080 : 0;
nccr |= (state->flags&SS_RESET)? 0x0080: 0;
nimr |= ((skt->status&SS_DETECT) ? 0x0004 : 0)|
((skt->status&SS_READY) ? 0x0002 : 0)|
((skt->status&SS_BATDEAD)? 0x0010 : 0)|
((skt->status&SS_BATWARN)? 0x0020 : 0)|
((skt->status&SS_STSCHG) ? 0x0010 : 0)|
((skt->status&SS_WRPROT) ? 0x0008 : 0);
if (!(ncpr & 0x0003)) {
SCOOP_DEV[skt->nr].keep_rd = 0;
} else if (!SCOOP_DEV[skt->nr].keep_rd) {
if (nccr & 0x0080)
SCOOP_DEV[skt->nr].keep_rd = 1;
else
nccr |= 0x0080;
}
if (mcr != nmcr)
write_scoop_reg(scoop, SCOOP_MCR, nmcr);
if (cpr != ncpr) {
if (platform_scoop_config->power_ctrl)
platform_scoop_config->power_ctrl(scoop, ncpr , skt->nr);
else
write_scoop_reg(scoop, SCOOP_CPR, ncpr);
}
if (ccr != nccr)
write_scoop_reg(scoop, SCOOP_CCR, nccr);
if (imr != nimr)
write_scoop_reg(scoop, SCOOP_IMR, nimr);
local_irq_restore(flags);
return 0;
}
static void sharpsl_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
{
sharpsl_pcmcia_init_reset(skt);
/* Enable interrupt */
write_scoop_reg(SCOOP_DEV[skt->nr].dev, SCOOP_IMR, 0x00C0);
write_scoop_reg(SCOOP_DEV[skt->nr].dev, SCOOP_MCR, 0x0101);
SCOOP_DEV[skt->nr].keep_vs = NO_KEEP_VS;
}
static void sharpsl_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
{
sharpsl_pcmcia_init_reset(skt);
}
static struct pcmcia_low_level sharpsl_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = sharpsl_pcmcia_hw_init,
.socket_state = sharpsl_pcmcia_socket_state,
.configure_socket = sharpsl_pcmcia_configure_socket,
.socket_init = sharpsl_pcmcia_socket_init,
.socket_suspend = sharpsl_pcmcia_socket_suspend,
.first = 0,
.nr = 0,
};
#ifdef CONFIG_SA1100_COLLIE
#include "sa11xx_base.h"
int pcmcia_collie_init(struct device *dev)
{
int ret = -ENODEV;
if (machine_is_collie())
ret = sa11xx_drv_pcmcia_probe(dev, &sharpsl_pcmcia_ops, 0, 1);
return ret;
}
#else
static struct platform_device *sharpsl_pcmcia_device;
static int __init sharpsl_pcmcia_init(void)
{
int ret;
if (!platform_scoop_config)
return -ENODEV;
sharpsl_pcmcia_ops.nr = platform_scoop_config->num_devs;
sharpsl_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!sharpsl_pcmcia_device)
return -ENOMEM;
ret = platform_device_add_data(sharpsl_pcmcia_device,
&sharpsl_pcmcia_ops, sizeof(sharpsl_pcmcia_ops));
if (ret == 0) {
sharpsl_pcmcia_device->dev.parent = platform_scoop_config->devs[0].dev;
ret = platform_device_add(sharpsl_pcmcia_device);
}
if (ret)
platform_device_put(sharpsl_pcmcia_device);
return ret;
}
static void __exit sharpsl_pcmcia_exit(void)
{
platform_device_unregister(sharpsl_pcmcia_device);
}
fs_initcall(sharpsl_pcmcia_init);
module_exit(sharpsl_pcmcia_exit);
#endif
MODULE_DESCRIPTION("Sharp SL Series PCMCIA Support");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pxa2xx-pcmcia");

View file

@ -0,0 +1,140 @@
/*
* linux/drivers/pcmcia/pxa2xx_stargate2.c
*
* Stargate 2 PCMCIA specific routines.
*
* Created: December 6, 2005
* Author: Ed C. Epp
* Copyright: Intel Corp 2005
* Jonathan Cameron <jic23@cam.ac.uk> 2009
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <pcmcia/ss.h>
#include <asm/irq.h>
#include <asm/mach-types.h>
#include "soc_common.h"
#define SG2_S0_POWER_CTL 108
#define SG2_S0_GPIO_RESET 82
#define SG2_S0_GPIO_DETECT 53
#define SG2_S0_GPIO_READY 81
static struct gpio sg2_pcmcia_gpios[] = {
{ SG2_S0_GPIO_RESET, GPIOF_OUT_INIT_HIGH, "PCMCIA Reset" },
{ SG2_S0_POWER_CTL, GPIOF_OUT_INIT_HIGH, "PCMCIA Power Ctrl" },
};
static int sg2_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
skt->stat[SOC_STAT_CD].gpio = SG2_S0_GPIO_DETECT;
skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD";
skt->stat[SOC_STAT_RDY].gpio = SG2_S0_GPIO_READY;
skt->stat[SOC_STAT_RDY].name = "PCMCIA0 RDY";
return 0;
}
static void sg2_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
state->bvd1 = 0; /* not available - battery detect on card */
state->bvd2 = 0; /* not available */
state->vs_3v = 1; /* not available - voltage detect for card */
state->vs_Xv = 0; /* not available */
}
static int sg2_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
/* Enable card power */
switch (state->Vcc) {
case 0:
/* sets power ctl register high */
gpio_set_value(SG2_S0_POWER_CTL, 1);
break;
case 33:
case 50:
/* sets power control register low (clear) */
gpio_set_value(SG2_S0_POWER_CTL, 0);
msleep(100);
break;
default:
pr_err("%s(): bad Vcc %u\n",
__func__, state->Vcc);
return -1;
}
/* reset */
gpio_set_value(SG2_S0_GPIO_RESET, !!(state->flags & SS_RESET));
return 0;
}
static struct pcmcia_low_level sg2_pcmcia_ops __initdata = {
.owner = THIS_MODULE,
.hw_init = sg2_pcmcia_hw_init,
.socket_state = sg2_pcmcia_socket_state,
.configure_socket = sg2_pcmcia_configure_socket,
.nr = 1,
};
static struct platform_device *sg2_pcmcia_device;
static int __init sg2_pcmcia_init(void)
{
int ret;
if (!machine_is_stargate2())
return -ENODEV;
sg2_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!sg2_pcmcia_device)
return -ENOMEM;
ret = gpio_request_array(sg2_pcmcia_gpios, ARRAY_SIZE(sg2_pcmcia_gpios));
if (ret)
goto error_put_platform_device;
ret = platform_device_add_data(sg2_pcmcia_device,
&sg2_pcmcia_ops,
sizeof(sg2_pcmcia_ops));
if (ret)
goto error_free_gpios;
ret = platform_device_add(sg2_pcmcia_device);
if (ret)
goto error_free_gpios;
return 0;
error_free_gpios:
gpio_free_array(sg2_pcmcia_gpios, ARRAY_SIZE(sg2_pcmcia_gpios));
error_put_platform_device:
platform_device_put(sg2_pcmcia_device);
return ret;
}
static void __exit sg2_pcmcia_exit(void)
{
platform_device_unregister(sg2_pcmcia_device);
gpio_free_array(sg2_pcmcia_gpios, ARRAY_SIZE(sg2_pcmcia_gpios));
}
fs_initcall(sg2_pcmcia_init);
module_exit(sg2_pcmcia_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pxa2xx-pcmcia");

View file

@ -0,0 +1,203 @@
/*
* linux/drivers/pcmcia/pxa2xx_trizeps4.c
*
* TRIZEPS PCMCIA specific routines.
*
* Author: Jürgen Schindele
* Created: 20 02, 2006
* Copyright: Jürgen Schindele
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <asm/mach-types.h>
#include <asm/irq.h>
#include <mach/pxa2xx-regs.h>
#include <mach/trizeps4.h>
#include "soc_common.h"
extern void board_pcmcia_power(int power);
static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
/* we dont have voltage/card/ready detection
* so we dont need interrupts for it
*/
switch (skt->nr) {
case 0:
skt->stat[SOC_STAT_CD].gpio = GPIO_PCD;
skt->stat[SOC_STAT_CD].name = "cs0_cd";
skt->stat[SOC_STAT_RDY].gpio = GPIO_PRDY;
skt->stat[SOC_STAT_RDY].name = "cs0_rdy";
break;
default:
break;
}
/* release the reset of this card */
pr_debug("%s: sock %d irq %d\n", __func__, skt->nr, skt->socket.pci_irq);
return 0;
}
static unsigned long trizeps_pcmcia_status[2];
static void trizeps_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
unsigned short status = 0, change;
status = CFSR_readw();
change = (status ^ trizeps_pcmcia_status[skt->nr]) &
ConXS_CFSR_BVD_MASK;
if (change) {
trizeps_pcmcia_status[skt->nr] = status;
if (status & ConXS_CFSR_BVD1) {
/* enable_irq empty */
} else {
/* disable_irq empty */
}
}
switch (skt->nr) {
case 0:
/* just fill in fix states */
state->bvd1 = (status & ConXS_CFSR_BVD1) ? 1 : 0;
state->bvd2 = (status & ConXS_CFSR_BVD2) ? 1 : 0;
state->vs_3v = (status & ConXS_CFSR_VS1) ? 0 : 1;
state->vs_Xv = (status & ConXS_CFSR_VS2) ? 0 : 1;
break;
#ifndef CONFIG_MACH_TRIZEPS_CONXS
/* on ConXS we only have one slot. Second is inactive */
case 1:
state->detect = 0;
state->ready = 0;
state->bvd1 = 0;
state->bvd2 = 0;
state->vs_3v = 0;
state->vs_Xv = 0;
break;
#endif
}
}
static int trizeps_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
int ret = 0;
unsigned short power = 0;
/* we do nothing here just check a bit */
switch (state->Vcc) {
case 0: power &= 0xfc; break;
case 33: power |= ConXS_BCR_S0_VCC_3V3; break;
case 50:
pr_err("%s(): Vcc 5V not supported in socket\n", __func__);
break;
default:
pr_err("%s(): bad Vcc %u\n", __func__, state->Vcc);
ret = -1;
}
switch (state->Vpp) {
case 0: power &= 0xf3; break;
case 33: power |= ConXS_BCR_S0_VPP_3V3; break;
case 120:
pr_err("%s(): Vpp 12V not supported in socket\n", __func__);
break;
default:
if (state->Vpp != state->Vcc) {
pr_err("%s(): bad Vpp %u\n", __func__, state->Vpp);
ret = -1;
}
}
switch (skt->nr) {
case 0: /* we only have 3.3V */
board_pcmcia_power(power);
break;
#ifndef CONFIG_MACH_TRIZEPS_CONXS
/* on ConXS we only have one slot. Second is inactive */
case 1:
#endif
default:
break;
}
return ret;
}
static void trizeps_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
{
/* default is on */
board_pcmcia_power(0x9);
}
static void trizeps_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
{
board_pcmcia_power(0x0);
}
static struct pcmcia_low_level trizeps_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = trizeps_pcmcia_hw_init,
.socket_state = trizeps_pcmcia_socket_state,
.configure_socket = trizeps_pcmcia_configure_socket,
.socket_init = trizeps_pcmcia_socket_init,
.socket_suspend = trizeps_pcmcia_socket_suspend,
#ifdef CONFIG_MACH_TRIZEPS_CONXS
.nr = 1,
#else
.nr = 2,
#endif
.first = 0,
};
static struct platform_device *trizeps_pcmcia_device;
static int __init trizeps_pcmcia_init(void)
{
int ret;
if (!machine_is_trizeps4() && !machine_is_trizeps4wl())
return -ENODEV;
trizeps_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!trizeps_pcmcia_device)
return -ENOMEM;
ret = platform_device_add_data(trizeps_pcmcia_device,
&trizeps_pcmcia_ops, sizeof(trizeps_pcmcia_ops));
if (ret == 0)
ret = platform_device_add(trizeps_pcmcia_device);
if (ret)
platform_device_put(trizeps_pcmcia_device);
return ret;
}
static void __exit trizeps_pcmcia_exit(void)
{
platform_device_unregister(trizeps_pcmcia_device);
}
fs_initcall(trizeps_pcmcia_init);
module_exit(trizeps_pcmcia_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Juergen Schindele");
MODULE_ALIAS("platform:pxa2xx-pcmcia");

View file

@ -0,0 +1,183 @@
/*
* Viper/Zeus PCMCIA support
* Copyright 2004 Arcom Control Systems
*
* Maintained by Marc Zyngier <maz@misterjones.org>
*
* Based on:
* iPAQ h2200 PCMCIA support
* Copyright 2004 Koen Kooi <koen@vestingbar.nl>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <pcmcia/ss.h>
#include <asm/irq.h>
#include <linux/platform_data/pcmcia-pxa2xx_viper.h>
#include "soc_common.h"
#include "pxa2xx_base.h"
static struct platform_device *arcom_pcmcia_dev;
static inline struct arcom_pcmcia_pdata *viper_get_pdata(void)
{
return arcom_pcmcia_dev->dev.platform_data;
}
static int viper_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
unsigned long flags;
skt->stat[SOC_STAT_CD].gpio = pdata->cd_gpio;
skt->stat[SOC_STAT_CD].name = "PCMCIA_CD";
skt->stat[SOC_STAT_RDY].gpio = pdata->rdy_gpio;
skt->stat[SOC_STAT_RDY].name = "CF ready";
if (gpio_request(pdata->pwr_gpio, "CF power"))
goto err_request_pwr;
local_irq_save(flags);
if (gpio_direction_output(pdata->pwr_gpio, 0)) {
local_irq_restore(flags);
goto err_dir;
}
local_irq_restore(flags);
return 0;
err_dir:
gpio_free(pdata->pwr_gpio);
err_request_pwr:
dev_err(&arcom_pcmcia_dev->dev, "Failed to setup PCMCIA GPIOs\n");
return -1;
}
/*
* Release all resources.
*/
static void viper_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
gpio_free(pdata->pwr_gpio);
}
static void viper_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
state->vs_3v = 1; /* Can only apply 3.3V */
state->vs_Xv = 0;
}
static int viper_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
/* Silently ignore Vpp, output enable, speaker enable. */
pdata->reset(state->flags & SS_RESET);
/* Apply socket voltage */
switch (state->Vcc) {
case 0:
gpio_set_value(pdata->pwr_gpio, 0);
break;
case 33:
gpio_set_value(pdata->pwr_gpio, 1);
break;
default:
dev_err(&arcom_pcmcia_dev->dev, "Unsupported Vcc:%d\n", state->Vcc);
return -1;
}
return 0;
}
static struct pcmcia_low_level viper_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = viper_pcmcia_hw_init,
.hw_shutdown = viper_pcmcia_hw_shutdown,
.socket_state = viper_pcmcia_socket_state,
.configure_socket = viper_pcmcia_configure_socket,
.nr = 1,
};
static struct platform_device *viper_pcmcia_device;
static int viper_pcmcia_probe(struct platform_device *pdev)
{
int ret;
/* I can't imagine more than one device, but you never know... */
if (arcom_pcmcia_dev)
return -EEXIST;
if (!pdev->dev.platform_data)
return -EINVAL;
viper_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!viper_pcmcia_device)
return -ENOMEM;
arcom_pcmcia_dev = pdev;
viper_pcmcia_device->dev.parent = &pdev->dev;
ret = platform_device_add_data(viper_pcmcia_device,
&viper_pcmcia_ops,
sizeof(viper_pcmcia_ops));
if (!ret)
ret = platform_device_add(viper_pcmcia_device);
if (ret) {
platform_device_put(viper_pcmcia_device);
arcom_pcmcia_dev = NULL;
}
return ret;
}
static int viper_pcmcia_remove(struct platform_device *pdev)
{
platform_device_unregister(viper_pcmcia_device);
arcom_pcmcia_dev = NULL;
return 0;
}
static struct platform_device_id viper_pcmcia_id_table[] = {
{ .name = "viper-pcmcia", },
{ .name = "zeus-pcmcia", },
{ },
};
static struct platform_driver viper_pcmcia_driver = {
.probe = viper_pcmcia_probe,
.remove = viper_pcmcia_remove,
.driver = {
.name = "arcom-pcmcia",
.owner = THIS_MODULE,
},
.id_table = viper_pcmcia_id_table,
};
module_platform_driver(viper_pcmcia_driver);
MODULE_DEVICE_TABLE(platform, viper_pcmcia_id_table);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,142 @@
/*
* linux/drivers/pcmcia/pxa2xx_vpac270.c
*
* Driver for Voipac PXA270 PCMCIA and CF sockets
*
* Copyright (C) 2010-2011 Marek Vasut <marek.vasut@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/mach-types.h>
#include <asm/gpio.h>
#include <mach/vpac270.h>
#include "soc_common.h"
static struct gpio vpac270_pcmcia_gpios[] = {
{ GPIO107_VPAC270_PCMCIA_PPEN, GPIOF_INIT_LOW, "PCMCIA PPEN" },
{ GPIO11_VPAC270_PCMCIA_RESET, GPIOF_INIT_LOW, "PCMCIA Reset" },
};
static struct gpio vpac270_cf_gpios[] = {
{ GPIO16_VPAC270_CF_RESET, GPIOF_INIT_LOW, "CF Reset" },
};
static int vpac270_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int ret;
if (skt->nr == 0) {
ret = gpio_request_array(vpac270_pcmcia_gpios,
ARRAY_SIZE(vpac270_pcmcia_gpios));
skt->stat[SOC_STAT_CD].gpio = GPIO84_VPAC270_PCMCIA_CD;
skt->stat[SOC_STAT_CD].name = "PCMCIA CD";
skt->stat[SOC_STAT_RDY].gpio = GPIO35_VPAC270_PCMCIA_RDY;
skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready";
} else {
ret = gpio_request_array(vpac270_cf_gpios,
ARRAY_SIZE(vpac270_cf_gpios));
skt->stat[SOC_STAT_CD].gpio = GPIO17_VPAC270_CF_CD;
skt->stat[SOC_STAT_CD].name = "CF CD";
skt->stat[SOC_STAT_RDY].gpio = GPIO12_VPAC270_CF_RDY;
skt->stat[SOC_STAT_RDY].name = "CF Ready";
}
return ret;
}
static void vpac270_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
if (skt->nr == 0)
gpio_free_array(vpac270_pcmcia_gpios,
ARRAY_SIZE(vpac270_pcmcia_gpios));
else
gpio_free_array(vpac270_cf_gpios,
ARRAY_SIZE(vpac270_cf_gpios));
}
static void vpac270_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
state->vs_3v = 1;
state->vs_Xv = 0;
}
static int
vpac270_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
if (skt->nr == 0) {
gpio_set_value(GPIO11_VPAC270_PCMCIA_RESET,
(state->flags & SS_RESET));
gpio_set_value(GPIO107_VPAC270_PCMCIA_PPEN,
!(state->Vcc == 33 || state->Vcc == 50));
} else {
gpio_set_value(GPIO16_VPAC270_CF_RESET,
(state->flags & SS_RESET));
}
return 0;
}
static struct pcmcia_low_level vpac270_pcmcia_ops = {
.owner = THIS_MODULE,
.first = 0,
.nr = 2,
.hw_init = vpac270_pcmcia_hw_init,
.hw_shutdown = vpac270_pcmcia_hw_shutdown,
.socket_state = vpac270_pcmcia_socket_state,
.configure_socket = vpac270_pcmcia_configure_socket,
};
static struct platform_device *vpac270_pcmcia_device;
static int __init vpac270_pcmcia_init(void)
{
int ret;
if (!machine_is_vpac270())
return -ENODEV;
vpac270_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!vpac270_pcmcia_device)
return -ENOMEM;
ret = platform_device_add_data(vpac270_pcmcia_device,
&vpac270_pcmcia_ops, sizeof(vpac270_pcmcia_ops));
if (!ret)
ret = platform_device_add(vpac270_pcmcia_device);
if (ret)
platform_device_put(vpac270_pcmcia_device);
return ret;
}
static void __exit vpac270_pcmcia_exit(void)
{
platform_device_unregister(vpac270_pcmcia_device);
}
module_init(vpac270_pcmcia_init);
module_exit(vpac270_pcmcia_exit);
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
MODULE_DESCRIPTION("PCMCIA support for Voipac PXA270");
MODULE_ALIAS("platform:pxa2xx-pcmcia");
MODULE_LICENSE("GPL");

206
drivers/pcmcia/ricoh.h Normal file
View file

@ -0,0 +1,206 @@
/*
* ricoh.h 1.9 1999/10/25 20:03:34
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License
* at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and
* limitations under the License.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in which
* case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use
* your version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the MPL or the GPL.
*/
#ifndef _LINUX_RICOH_H
#define _LINUX_RICOH_H
#define RF5C_MODE_CTL 0x1f /* Mode control */
#define RF5C_PWR_CTL 0x2f /* Mixed voltage control */
#define RF5C_CHIP_ID 0x3a /* Chip identification */
#define RF5C_MODE_CTL_3 0x3b /* Mode control 3 */
/* I/O window address offset */
#define RF5C_IO_OFF(w) (0x36+((w)<<1))
/* Flags for RF5C_MODE_CTL */
#define RF5C_MODE_ATA 0x01 /* ATA mode */
#define RF5C_MODE_LED_ENA 0x02 /* IRQ 12 is LED */
#define RF5C_MODE_CA21 0x04
#define RF5C_MODE_CA22 0x08
#define RF5C_MODE_CA23 0x10
#define RF5C_MODE_CA24 0x20
#define RF5C_MODE_CA25 0x40
#define RF5C_MODE_3STATE_BIT7 0x80
/* Flags for RF5C_PWR_CTL */
#define RF5C_PWR_VCC_3V 0x01
#define RF5C_PWR_IREQ_HIGH 0x02
#define RF5C_PWR_INPACK_ENA 0x04
#define RF5C_PWR_5V_DET 0x08
#define RF5C_PWR_TC_SEL 0x10 /* Terminal Count: irq 11 or 15 */
#define RF5C_PWR_DREQ_LOW 0x20
#define RF5C_PWR_DREQ_OFF 0x00 /* DREQ steering control */
#define RF5C_PWR_DREQ_INPACK 0x40
#define RF5C_PWR_DREQ_SPKR 0x80
#define RF5C_PWR_DREQ_IOIS16 0xc0
/* Values for RF5C_CHIP_ID */
#define RF5C_CHIP_RF5C296 0x32
#define RF5C_CHIP_RF5C396 0xb2
/* Flags for RF5C_MODE_CTL_3 */
#define RF5C_MCTL3_DISABLE 0x01 /* Disable PCMCIA interface */
#define RF5C_MCTL3_DMA_ENA 0x02
/* Register definitions for Ricoh PCI-to-CardBus bridges */
/* Extra bits in CB_BRIDGE_CONTROL */
#define RL5C46X_BCR_3E0_ENA 0x0800
#define RL5C46X_BCR_3E2_ENA 0x1000
/* Bridge Configuration Register */
#define RL5C4XX_CONFIG 0x80 /* 16 bit */
#define RL5C4XX_CONFIG_IO_1_MODE 0x0200
#define RL5C4XX_CONFIG_IO_0_MODE 0x0100
#define RL5C4XX_CONFIG_PREFETCH 0x0001
/* Misc Control Register */
#define RL5C4XX_MISC 0x0082 /* 16 bit */
#define RL5C4XX_MISC_HW_SUSPEND_ENA 0x0002
#define RL5C4XX_MISC_VCCEN_POL 0x0100
#define RL5C4XX_MISC_VPPEN_POL 0x0200
#define RL5C46X_MISC_SUSPEND 0x0001
#define RL5C46X_MISC_PWR_SAVE_2 0x0004
#define RL5C46X_MISC_IFACE_BUSY 0x0008
#define RL5C46X_MISC_B_LOCK 0x0010
#define RL5C46X_MISC_A_LOCK 0x0020
#define RL5C46X_MISC_PCI_LOCK 0x0040
#define RL5C47X_MISC_IFACE_BUSY 0x0004
#define RL5C47X_MISC_PCI_INT_MASK 0x0018
#define RL5C47X_MISC_PCI_INT_DIS 0x0020
#define RL5C47X_MISC_SUBSYS_WR 0x0040
#define RL5C47X_MISC_SRIRQ_ENA 0x0080
#define RL5C47X_MISC_5V_DISABLE 0x0400
#define RL5C47X_MISC_LED_POL 0x0800
/* 16-bit Interface Control Register */
#define RL5C4XX_16BIT_CTL 0x0084 /* 16 bit */
#define RL5C4XX_16CTL_IO_TIMING 0x0100
#define RL5C4XX_16CTL_MEM_TIMING 0x0200
#define RL5C46X_16CTL_LEVEL_1 0x0010
#define RL5C46X_16CTL_LEVEL_2 0x0020
/* 16-bit IO and memory timing registers */
#define RL5C4XX_16BIT_IO_0 0x0088 /* 16 bit */
#define RL5C4XX_16BIT_MEM_0 0x008a /* 16 bit */
#define RL5C4XX_SETUP_MASK 0x0007
#define RL5C4XX_SETUP_SHIFT 0
#define RL5C4XX_CMD_MASK 0x01f0
#define RL5C4XX_CMD_SHIFT 4
#define RL5C4XX_HOLD_MASK 0x1c00
#define RL5C4XX_HOLD_SHIFT 10
#define RL5C4XX_MISC_CONTROL 0x2F /* 8 bit */
#define RL5C4XX_ZV_ENABLE 0x08
#ifdef __YENTA_H
#define rl_misc(socket) ((socket)->private[0])
#define rl_ctl(socket) ((socket)->private[1])
#define rl_io(socket) ((socket)->private[2])
#define rl_mem(socket) ((socket)->private[3])
#define rl_config(socket) ((socket)->private[4])
static void ricoh_zoom_video(struct pcmcia_socket *sock, int onoff)
{
u8 reg;
struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
reg = config_readb(socket, RL5C4XX_MISC_CONTROL);
if (onoff)
/* Zoom zoom, we will all go together, zoom zoom, zoom zoom */
reg |= RL5C4XX_ZV_ENABLE;
else
reg &= ~RL5C4XX_ZV_ENABLE;
config_writeb(socket, RL5C4XX_MISC_CONTROL, reg);
}
static void ricoh_set_zv(struct yenta_socket *socket)
{
if(socket->dev->vendor == PCI_VENDOR_ID_RICOH)
{
switch(socket->dev->device)
{
/* There may be more .. */
case PCI_DEVICE_ID_RICOH_RL5C478:
socket->socket.zoom_video = ricoh_zoom_video;
break;
}
}
}
static void ricoh_save_state(struct yenta_socket *socket)
{
rl_misc(socket) = config_readw(socket, RL5C4XX_MISC);
rl_ctl(socket) = config_readw(socket, RL5C4XX_16BIT_CTL);
rl_io(socket) = config_readw(socket, RL5C4XX_16BIT_IO_0);
rl_mem(socket) = config_readw(socket, RL5C4XX_16BIT_MEM_0);
rl_config(socket) = config_readw(socket, RL5C4XX_CONFIG);
}
static void ricoh_restore_state(struct yenta_socket *socket)
{
config_writew(socket, RL5C4XX_MISC, rl_misc(socket));
config_writew(socket, RL5C4XX_16BIT_CTL, rl_ctl(socket));
config_writew(socket, RL5C4XX_16BIT_IO_0, rl_io(socket));
config_writew(socket, RL5C4XX_16BIT_MEM_0, rl_mem(socket));
config_writew(socket, RL5C4XX_CONFIG, rl_config(socket));
}
/*
* Magic Ricoh initialization code..
*/
static int ricoh_override(struct yenta_socket *socket)
{
u16 config, ctl;
config = config_readw(socket, RL5C4XX_CONFIG);
/* Set the default timings, don't trust the original values */
ctl = RL5C4XX_16CTL_IO_TIMING | RL5C4XX_16CTL_MEM_TIMING;
if(socket->dev->device < PCI_DEVICE_ID_RICOH_RL5C475) {
ctl |= RL5C46X_16CTL_LEVEL_1 | RL5C46X_16CTL_LEVEL_2;
} else {
config |= RL5C4XX_CONFIG_PREFETCH;
}
config_writew(socket, RL5C4XX_16BIT_CTL, ctl);
config_writew(socket, RL5C4XX_CONFIG, config);
ricoh_set_zv(socket);
return 0;
}
#endif /* CONFIG_CARDBUS */
#endif /* _LINUX_RICOH_H */

171
drivers/pcmcia/rsrc_iodyn.c Normal file
View file

@ -0,0 +1,171 @@
/*
* rsrc_iodyn.c -- Resource management routines for MEM-static sockets.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* (C) 1999 David A. Hinds
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <pcmcia/ss.h>
#include <pcmcia/cistpl.h>
#include "cs_internal.h"
struct pcmcia_align_data {
unsigned long mask;
unsigned long offset;
};
static resource_size_t pcmcia_align(void *align_data,
const struct resource *res,
resource_size_t size, resource_size_t align)
{
struct pcmcia_align_data *data = align_data;
resource_size_t start;
start = (res->start & ~data->mask) + data->offset;
if (start < res->start)
start += data->mask + 1;
#ifdef CONFIG_X86
if (res->flags & IORESOURCE_IO) {
if (start & 0x300)
start = (start + 0x3ff) & ~0x3ff;
}
#endif
#ifdef CONFIG_M68K
if (res->flags & IORESOURCE_IO) {
if ((res->start + size - 1) >= 1024)
start = res->end;
}
#endif
return start;
}
static struct resource *__iodyn_find_io_region(struct pcmcia_socket *s,
unsigned long base, int num,
unsigned long align)
{
struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO,
dev_name(&s->dev));
struct pcmcia_align_data data;
unsigned long min = base;
int ret;
data.mask = align - 1;
data.offset = base & data.mask;
#ifdef CONFIG_PCI
if (s->cb_dev) {
ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
min, 0, pcmcia_align, &data);
} else
#endif
ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
1, pcmcia_align, &data);
if (ret != 0) {
kfree(res);
res = NULL;
}
return res;
}
static int iodyn_find_io(struct pcmcia_socket *s, unsigned int attr,
unsigned int *base, unsigned int num,
unsigned int align, struct resource **parent)
{
int i, ret = 0;
/* Check for an already-allocated window that must conflict with
* what was asked for. It is a hack because it does not catch all
* potential conflicts, just the most obvious ones.
*/
for (i = 0; i < MAX_IO_WIN; i++) {
if (!s->io[i].res)
continue;
if (!*base)
continue;
if ((s->io[i].res->start & (align-1)) == *base)
return -EBUSY;
}
for (i = 0; i < MAX_IO_WIN; i++) {
struct resource *res = s->io[i].res;
unsigned int try;
if (res && (res->flags & IORESOURCE_BITS) !=
(attr & IORESOURCE_BITS))
continue;
if (!res) {
if (align == 0)
align = 0x10000;
res = s->io[i].res = __iodyn_find_io_region(s, *base,
num, align);
if (!res)
return -EINVAL;
*base = res->start;
s->io[i].res->flags =
((res->flags & ~IORESOURCE_BITS) |
(attr & IORESOURCE_BITS));
s->io[i].InUse = num;
*parent = res;
return 0;
}
/* Try to extend top of window */
try = res->end + 1;
if ((*base == 0) || (*base == try)) {
if (adjust_resource(s->io[i].res, res->start,
resource_size(res) + num))
continue;
*base = try;
s->io[i].InUse += num;
*parent = res;
return 0;
}
/* Try to extend bottom of window */
try = res->start - num;
if ((*base == 0) || (*base == try)) {
if (adjust_resource(s->io[i].res,
res->start - num,
resource_size(res) + num))
continue;
*base = try;
s->io[i].InUse += num;
*parent = res;
return 0;
}
}
return -EINVAL;
}
struct pccard_resource_ops pccard_iodyn_ops = {
.validate_mem = NULL,
.find_io = iodyn_find_io,
.find_mem = NULL,
.init = static_init,
.exit = NULL,
};
EXPORT_SYMBOL(pccard_iodyn_ops);

72
drivers/pcmcia/rsrc_mgr.c Normal file
View file

@ -0,0 +1,72 @@
/*
* rsrc_mgr.c -- Resource management routines and/or wrappers
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* (C) 1999 David A. Hinds
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <pcmcia/ss.h>
#include <pcmcia/cistpl.h>
#include "cs_internal.h"
int static_init(struct pcmcia_socket *s)
{
/* the good thing about SS_CAP_STATIC_MAP sockets is
* that they don't need a resource database */
s->resource_setup_done = 1;
return 0;
}
struct resource *pcmcia_make_resource(unsigned long start, unsigned long end,
int flags, const char *name)
{
struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
if (res) {
res->name = name;
res->start = start;
res->end = start + end - 1;
res->flags = flags;
}
return res;
}
static int static_find_io(struct pcmcia_socket *s, unsigned int attr,
unsigned int *base, unsigned int num,
unsigned int align, struct resource **parent)
{
if (!s->io_offset)
return -EINVAL;
*base = s->io_offset | (*base & 0x0fff);
*parent = NULL;
return 0;
}
struct pccard_resource_ops pccard_static_ops = {
.validate_mem = NULL,
.find_io = static_find_io,
.find_mem = NULL,
.init = static_init,
.exit = NULL,
};
EXPORT_SYMBOL(pccard_static_ops);
MODULE_AUTHOR("David A. Hinds, Dominik Brodowski");
MODULE_LICENSE("GPL");
MODULE_ALIAS("rsrc_nonstatic");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,106 @@
/*
* drivers/pcmcia/sa1100_assabet.c
*
* PCMCIA implementation routines for Assabet
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <asm/mach-types.h>
#include <mach/assabet.h>
#include "sa1100_generic.h"
static int assabet_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
skt->stat[SOC_STAT_CD].gpio = ASSABET_GPIO_CF_CD;
skt->stat[SOC_STAT_CD].name = "CF CD";
skt->stat[SOC_STAT_BVD1].gpio = ASSABET_GPIO_CF_BVD1;
skt->stat[SOC_STAT_BVD1].name = "CF BVD1";
skt->stat[SOC_STAT_BVD2].gpio = ASSABET_GPIO_CF_BVD2;
skt->stat[SOC_STAT_BVD2].name = "CF BVD2";
skt->stat[SOC_STAT_RDY].gpio = ASSABET_GPIO_CF_IRQ;
skt->stat[SOC_STAT_RDY].name = "CF RDY";
return 0;
}
static void
assabet_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
{
state->vs_3v = 1; /* Can only apply 3.3V on Assabet. */
state->vs_Xv = 0;
}
static int
assabet_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
{
unsigned int mask;
switch (state->Vcc) {
case 0:
mask = 0;
break;
case 50:
printk(KERN_WARNING "%s(): CS asked for 5V, applying 3.3V...\n",
__func__);
case 33: /* Can only apply 3.3V to the CF slot. */
mask = ASSABET_BCR_CF_PWR;
break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __func__,
state->Vcc);
return -1;
}
/* Silently ignore Vpp, speaker enable. */
if (state->flags & SS_RESET)
mask |= ASSABET_BCR_CF_RST;
if (!(state->flags & SS_OUTPUT_ENA))
mask |= ASSABET_BCR_CF_BUS_OFF;
ASSABET_BCR_frob(ASSABET_BCR_CF_RST | ASSABET_BCR_CF_PWR |
ASSABET_BCR_CF_BUS_OFF, mask);
return 0;
}
/*
* Disable card status IRQs on suspend.
*/
static void assabet_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
{
/*
* Tristate the CF bus signals. Also assert CF
* reset as per user guide page 4-11.
*/
ASSABET_BCR_set(ASSABET_BCR_CF_BUS_OFF | ASSABET_BCR_CF_RST);
}
static struct pcmcia_low_level assabet_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = assabet_pcmcia_hw_init,
.socket_state = assabet_pcmcia_socket_state,
.configure_socket = assabet_pcmcia_configure_socket,
.socket_suspend = assabet_pcmcia_socket_suspend,
};
int pcmcia_assabet_init(struct device *dev)
{
int ret = -ENODEV;
if (machine_is_assabet() && !machine_has_neponset())
ret = sa11xx_drv_pcmcia_probe(dev, &assabet_pcmcia_ops, 1, 1);
return ret;
}

View file

@ -0,0 +1,92 @@
/*
* drivers/pcmcia/sa1100_cerf.c
*
* PCMCIA implementation routines for CerfBoard
* Based off the Assabet.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/irq.h>
#include <mach/cerf.h>
#include "sa1100_generic.h"
#define CERF_SOCKET 1
static int cerf_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int ret;
ret = gpio_request_one(CERF_GPIO_CF_RESET, GPIOF_OUT_INIT_LOW, "CF_RESET");
if (ret)
return ret;
skt->stat[SOC_STAT_CD].gpio = CERF_GPIO_CF_CD;
skt->stat[SOC_STAT_CD].name = "CF_CD";
skt->stat[SOC_STAT_BVD1].gpio = CERF_GPIO_CF_BVD1;
skt->stat[SOC_STAT_BVD1].name = "CF_BVD1";
skt->stat[SOC_STAT_BVD2].gpio = CERF_GPIO_CF_BVD2;
skt->stat[SOC_STAT_BVD2].name = "CF_BVD2";
skt->stat[SOC_STAT_RDY].gpio = CERF_GPIO_CF_IRQ;
skt->stat[SOC_STAT_RDY].name = "CF_IRQ";
return 0;
}
static void cerf_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
gpio_free(CERF_GPIO_CF_RESET);
}
static void
cerf_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
{
state->vs_3v = 1;
state->vs_Xv = 0;
}
static int
cerf_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
switch (state->Vcc) {
case 0:
case 50:
case 33:
break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
__func__, state->Vcc);
return -1;
}
gpio_set_value(CERF_GPIO_CF_RESET, !!(state->flags & SS_RESET));
return 0;
}
static struct pcmcia_low_level cerf_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = cerf_pcmcia_hw_init,
.hw_shutdown = cerf_pcmcia_hw_shutdown,
.socket_state = cerf_pcmcia_socket_state,
.configure_socket = cerf_pcmcia_configure_socket,
};
int pcmcia_cerf_init(struct device *dev)
{
int ret = -ENODEV;
if (machine_is_cerf())
ret = sa11xx_drv_pcmcia_probe(dev, &cerf_pcmcia_ops, CERF_SOCKET, 1);
return ret;
}

View file

@ -0,0 +1,137 @@
/*======================================================================
Device driver for the PCMCIA control functionality of StrongARM
SA-1100 microprocessors.
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.
The initial developer of the original code is John G. Dorsey
<john+@cs.cmu.edu>. Portions created by John G. Dorsey are
Copyright (C) 1999 John G. Dorsey. All Rights Reserved.
Alternatively, the contents of this file may be used under the
terms of the GNU Public License version 2 (the "GPL"), in which
case the provisions of the GPL are applicable instead of the
above. If you wish to allow the use of your version of this file
only under the terms of the GPL and not to allow others to use
your version of this file under the MPL, indicate your decision
by deleting the provisions above and replace them with the notice
and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this
file under either the MPL or the GPL.
======================================================================*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <pcmcia/ss.h>
#include <asm/hardware/scoop.h>
#include "sa1100_generic.h"
int __init pcmcia_collie_init(struct device *dev);
static int (*sa11x0_pcmcia_hw_init[])(struct device *dev) = {
#ifdef CONFIG_SA1100_ASSABET
pcmcia_assabet_init,
#endif
#ifdef CONFIG_SA1100_CERF
pcmcia_cerf_init,
#endif
#if defined(CONFIG_SA1100_H3100) || defined(CONFIG_SA1100_H3600)
pcmcia_h3600_init,
#endif
#ifdef CONFIG_SA1100_NANOENGINE
pcmcia_nanoengine_init,
#endif
#ifdef CONFIG_SA1100_SHANNON
pcmcia_shannon_init,
#endif
#ifdef CONFIG_SA1100_SIMPAD
pcmcia_simpad_init,
#endif
#ifdef CONFIG_SA1100_COLLIE
pcmcia_collie_init,
#endif
};
static int sa11x0_drv_pcmcia_probe(struct platform_device *dev)
{
int i, ret = -ENODEV;
/*
* Initialise any "on-board" PCMCIA sockets.
*/
for (i = 0; i < ARRAY_SIZE(sa11x0_pcmcia_hw_init); i++) {
ret = sa11x0_pcmcia_hw_init[i](&dev->dev);
if (ret == 0)
break;
}
return ret;
}
static int sa11x0_drv_pcmcia_remove(struct platform_device *dev)
{
struct skt_dev_info *sinfo = platform_get_drvdata(dev);
int i;
platform_set_drvdata(dev, NULL);
for (i = 0; i < sinfo->nskt; i++)
soc_pcmcia_remove_one(&sinfo->skt[i]);
kfree(sinfo);
return 0;
}
static struct platform_driver sa11x0_pcmcia_driver = {
.driver = {
.name = "sa11x0-pcmcia",
.owner = THIS_MODULE,
},
.probe = sa11x0_drv_pcmcia_probe,
.remove = sa11x0_drv_pcmcia_remove,
};
/* sa11x0_pcmcia_init()
* ^^^^^^^^^^^^^^^^^^^^
*
* This routine performs low-level PCMCIA initialization and then
* registers this socket driver with Card Services.
*
* Returns: 0 on success, -ve error code on failure
*/
static int __init sa11x0_pcmcia_init(void)
{
return platform_driver_register(&sa11x0_pcmcia_driver);
}
/* sa11x0_pcmcia_exit()
* ^^^^^^^^^^^^^^^^^^^^
* Invokes the low-level kernel service to free IRQs associated with this
* socket controller and reset GPIO edge detection.
*/
static void __exit sa11x0_pcmcia_exit(void)
{
platform_driver_unregister(&sa11x0_pcmcia_driver);
}
MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-11x0 Socket Controller");
MODULE_LICENSE("Dual MPL/GPL");
fs_initcall(sa11x0_pcmcia_init);
module_exit(sa11x0_pcmcia_exit);

View file

@ -0,0 +1,25 @@
#include "soc_common.h"
#include "sa11xx_base.h"
/*
* Declaration for all machine specific init/exit functions.
*/
extern int pcmcia_adsbitsy_init(struct device *);
extern int pcmcia_assabet_init(struct device *);
extern int pcmcia_badge4_init(struct device *);
extern int pcmcia_cerf_init(struct device *);
extern int pcmcia_flexanet_init(struct device *);
extern int pcmcia_freebird_init(struct device *);
extern int pcmcia_gcplus_init(struct device *);
extern int pcmcia_graphicsmaster_init(struct device *);
extern int pcmcia_h3600_init(struct device *);
extern int pcmcia_nanoengine_init(struct device *);
extern int pcmcia_pangolin_init(struct device *);
extern int pcmcia_pfs168_init(struct device *);
extern int pcmcia_shannon_init(struct device *);
extern int pcmcia_simpad_init(struct device *);
extern int pcmcia_stork_init(struct device *);
extern int pcmcia_system3_init(struct device *);
extern int pcmcia_trizeps_init(struct device *);
extern int pcmcia_xp860_init(struct device *);
extern int pcmcia_yopy_init(struct device *);

View file

@ -0,0 +1,164 @@
/*
* drivers/pcmcia/sa1100_h3600.c
*
* PCMCIA implementation routines for H3600
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/mach-types.h>
#include <mach/h3xxx.h>
#include "sa1100_generic.h"
static int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int err;
switch (skt->nr) {
case 0:
skt->stat[SOC_STAT_CD].gpio = H3XXX_GPIO_PCMCIA_CD0;
skt->stat[SOC_STAT_CD].name = "PCMCIA CD0";
skt->stat[SOC_STAT_RDY].gpio = H3XXX_GPIO_PCMCIA_IRQ0;
skt->stat[SOC_STAT_RDY].name = "PCMCIA IRQ0";
err = gpio_request(H3XXX_EGPIO_OPT_NVRAM_ON, "OPT NVRAM ON");
if (err)
goto err01;
err = gpio_direction_output(H3XXX_EGPIO_OPT_NVRAM_ON, 0);
if (err)
goto err03;
err = gpio_request(H3XXX_EGPIO_OPT_ON, "OPT ON");
if (err)
goto err03;
err = gpio_direction_output(H3XXX_EGPIO_OPT_ON, 0);
if (err)
goto err04;
err = gpio_request(H3XXX_EGPIO_OPT_RESET, "OPT RESET");
if (err)
goto err04;
err = gpio_direction_output(H3XXX_EGPIO_OPT_RESET, 0);
if (err)
goto err05;
err = gpio_request(H3XXX_EGPIO_CARD_RESET, "PCMCIA CARD RESET");
if (err)
goto err05;
err = gpio_direction_output(H3XXX_EGPIO_CARD_RESET, 0);
if (err)
goto err06;
break;
case 1:
skt->stat[SOC_STAT_CD].gpio = H3XXX_GPIO_PCMCIA_CD1;
skt->stat[SOC_STAT_CD].name = "PCMCIA CD1";
skt->stat[SOC_STAT_RDY].gpio = H3XXX_GPIO_PCMCIA_IRQ1;
skt->stat[SOC_STAT_RDY].name = "PCMCIA IRQ1";
break;
}
return 0;
err06: gpio_free(H3XXX_EGPIO_CARD_RESET);
err05: gpio_free(H3XXX_EGPIO_OPT_RESET);
err04: gpio_free(H3XXX_EGPIO_OPT_ON);
err03: gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON);
err01: gpio_free(H3XXX_GPIO_PCMCIA_IRQ0);
return err;
}
static void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
switch (skt->nr) {
case 0:
/* Disable CF bus: */
gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 0);
gpio_set_value(H3XXX_EGPIO_OPT_ON, 0);
gpio_set_value(H3XXX_EGPIO_OPT_RESET, 1);
gpio_free(H3XXX_EGPIO_CARD_RESET);
gpio_free(H3XXX_EGPIO_OPT_RESET);
gpio_free(H3XXX_EGPIO_OPT_ON);
gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON);
break;
case 1:
break;
}
}
static void
h3600_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
{
state->bvd1 = 0;
state->bvd2 = 0;
state->vs_3v = 0;
state->vs_Xv = 0;
}
static int
h3600_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
{
if (state->Vcc != 0 && state->Vcc != 33 && state->Vcc != 50) {
printk(KERN_ERR "h3600_pcmcia: unrecognized Vcc %u.%uV\n",
state->Vcc / 10, state->Vcc % 10);
return -1;
}
gpio_set_value(H3XXX_EGPIO_CARD_RESET, !!(state->flags & SS_RESET));
/* Silently ignore Vpp, output enable, speaker enable. */
return 0;
}
static void h3600_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
{
/* Enable CF bus: */
gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 1);
gpio_set_value(H3XXX_EGPIO_OPT_ON, 1);
gpio_set_value(H3XXX_EGPIO_OPT_RESET, 0);
msleep(10);
}
static void h3600_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
{
/*
* FIXME: This doesn't fit well. We don't have the mechanism in
* the generic PCMCIA layer to deal with the idea of two sockets
* on one bus. We rely on the cs.c behaviour shutting down
* socket 0 then socket 1.
*/
if (skt->nr == 1) {
gpio_set_value(H3XXX_EGPIO_OPT_ON, 0);
gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 0);
/* hmm, does this suck power? */
gpio_set_value(H3XXX_EGPIO_OPT_RESET, 1);
}
}
struct pcmcia_low_level h3600_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = h3600_pcmcia_hw_init,
.hw_shutdown = h3600_pcmcia_hw_shutdown,
.socket_state = h3600_pcmcia_socket_state,
.configure_socket = h3600_pcmcia_configure_socket,
.socket_init = h3600_pcmcia_socket_init,
.socket_suspend = h3600_pcmcia_socket_suspend,
};
int pcmcia_h3600_init(struct device *dev)
{
int ret = -ENODEV;
if (machine_is_h3600() || machine_is_h3100())
ret = sa11xx_drv_pcmcia_probe(dev, &h3600_pcmcia_ops, 0, 2);
return ret;
}

View file

@ -0,0 +1,133 @@
/*
* drivers/pcmcia/sa1100_nanoengine.c
*
* PCMCIA implementation routines for BSI nanoEngine.
*
* In order to have a fully functional pcmcia subsystem in a BSE nanoEngine
* board you should carefully read this:
* http://cambuca.ldhs.cetuc.puc-rio.br/nanoengine/
*
* Copyright (C) 2010 Marcelo Roberto Jimenez <mroberto@cpti.cetuc.puc-rio.br>
*
* Based on original work for kernel 2.4 by
* Miguel Freitas <miguel@cpti.cetuc.puc-rio.br>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/signal.h>
#include <asm/mach-types.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/nanoengine.h>
#include "sa1100_generic.h"
struct nanoengine_pins {
unsigned output_pins;
unsigned clear_outputs;
int gpio_rst;
int gpio_cd;
int gpio_rdy;
};
static struct nanoengine_pins nano_skts[] = {
{
.gpio_rst = GPIO_PC_RESET0,
.gpio_cd = GPIO_PC_CD0,
.gpio_rdy = GPIO_PC_READY0,
}, {
.gpio_rst = GPIO_PC_RESET1,
.gpio_cd = GPIO_PC_CD1,
.gpio_rdy = GPIO_PC_READY1,
}
};
unsigned num_nano_pcmcia_sockets = ARRAY_SIZE(nano_skts);
static int nanoengine_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
unsigned i = skt->nr;
int ret;
if (i >= num_nano_pcmcia_sockets)
return -ENXIO;
ret = gpio_request_one(nano_skts[i].gpio_rst, GPIOF_OUT_INIT_LOW,
i ? "PC RST1" : "PC RST0");
if (ret)
return ret;
skt->stat[SOC_STAT_CD].gpio = nano_skts[i].gpio_cd;
skt->stat[SOC_STAT_CD].name = i ? "PC CD1" : "PC CD0";
skt->stat[SOC_STAT_RDY].gpio = nano_skts[i].gpio_rdy;
skt->stat[SOC_STAT_RDY].name = i ? "PC RDY1" : "PC RDY0";
return 0;
}
static void nanoengine_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
gpio_free(nano_skts[skt->nr].gpio_rst);
}
static int nanoengine_pcmcia_configure_socket(
struct soc_pcmcia_socket *skt, const socket_state_t *state)
{
unsigned i = skt->nr;
if (i >= num_nano_pcmcia_sockets)
return -ENXIO;
gpio_set_value(nano_skts[skt->nr].gpio_rst, !!(state->flags & SS_RESET));
return 0;
}
static void nanoengine_pcmcia_socket_state(
struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
{
unsigned i = skt->nr;
if (i >= num_nano_pcmcia_sockets)
return;
state->bvd1 = 1;
state->bvd2 = 1;
state->vs_3v = 1; /* Can only apply 3.3V */
state->vs_Xv = 0;
}
static struct pcmcia_low_level nanoengine_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = nanoengine_pcmcia_hw_init,
.hw_shutdown = nanoengine_pcmcia_hw_shutdown,
.configure_socket = nanoengine_pcmcia_configure_socket,
.socket_state = nanoengine_pcmcia_socket_state,
};
int pcmcia_nanoengine_init(struct device *dev)
{
int ret = -ENODEV;
if (machine_is_nanoengine())
ret = sa11xx_drv_pcmcia_probe(
dev, &nanoengine_pcmcia_ops, 0, 2);
return ret;
}

View file

@ -0,0 +1,103 @@
/*
* drivers/pcmcia/sa1100_shannon.c
*
* PCMCIA implementation routines for Shannon
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <mach/shannon.h>
#include <asm/irq.h>
#include "sa1100_generic.h"
static int shannon_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
/* All those are inputs */
GAFR &= ~(GPIO_GPIO(SHANNON_GPIO_EJECT_0) |
GPIO_GPIO(SHANNON_GPIO_EJECT_1) |
GPIO_GPIO(SHANNON_GPIO_RDY_0) |
GPIO_GPIO(SHANNON_GPIO_RDY_1));
if (skt->nr == 0) {
skt->stat[SOC_STAT_CD].gpio = SHANNON_GPIO_EJECT_0;
skt->stat[SOC_STAT_CD].name = "PCMCIA_CD_0";
skt->stat[SOC_STAT_RDY].gpio = SHANNON_GPIO_RDY_0;
skt->stat[SOC_STAT_RDY].name = "PCMCIA_RDY_0";
} else {
skt->stat[SOC_STAT_CD].gpio = SHANNON_GPIO_EJECT_1;
skt->stat[SOC_STAT_CD].name = "PCMCIA_CD_1";
skt->stat[SOC_STAT_RDY].gpio = SHANNON_GPIO_RDY_1;
skt->stat[SOC_STAT_RDY].name = "PCMCIA_RDY_1";
}
return 0;
}
static void
shannon_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
switch (skt->nr) {
case 0:
state->bvd1 = 1;
state->bvd2 = 1;
state->vs_3v = 1; /* FIXME Can only apply 3.3V on Shannon. */
state->vs_Xv = 0;
break;
case 1:
state->bvd1 = 1;
state->bvd2 = 1;
state->vs_3v = 1; /* FIXME Can only apply 3.3V on Shannon. */
state->vs_Xv = 0;
break;
}
}
static int
shannon_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
switch (state->Vcc) {
case 0: /* power off */
printk(KERN_WARNING "%s(): CS asked for 0V, still applying 3.3V..\n", __func__);
break;
case 50:
printk(KERN_WARNING "%s(): CS asked for 5V, applying 3.3V..\n", __func__);
case 33:
break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
__func__, state->Vcc);
return -1;
}
printk(KERN_WARNING "%s(): Warning, Can't perform reset\n", __func__);
/* Silently ignore Vpp, output enable, speaker enable. */
return 0;
}
static struct pcmcia_low_level shannon_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = shannon_pcmcia_hw_init,
.socket_state = shannon_pcmcia_socket_state,
.configure_socket = shannon_pcmcia_configure_socket,
};
int pcmcia_shannon_init(struct device *dev)
{
int ret = -ENODEV;
if (machine_is_shannon())
ret = sa11xx_drv_pcmcia_probe(dev, &shannon_pcmcia_ops, 0, 2);
return ret;
}

View file

@ -0,0 +1,120 @@
/*
* drivers/pcmcia/sa1100_simpad.c
*
* PCMCIA implementation routines for simpad
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/init.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/irq.h>
#include <mach/simpad.h>
#include "sa1100_generic.h"
static int simpad_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
simpad_clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
skt->stat[SOC_STAT_CD].gpio = GPIO_CF_CD;
skt->stat[SOC_STAT_CD].name = "CF_CD";
skt->stat[SOC_STAT_RDY].gpio = GPIO_CF_IRQ;
skt->stat[SOC_STAT_RDY].name = "CF_RDY";
return 0;
}
static void simpad_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
/* Disable CF bus: */
/*simpad_set_cs3_bit(PCMCIA_BUFF_DIS);*/
simpad_clear_cs3_bit(PCMCIA_RESET);
}
static void
simpad_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
long cs3reg = simpad_get_cs3_ro();
/* the detect signal is inverted - fix that up here */
state->detect = !state->detect;
state->bvd1 = 1; /* Might be cs3reg & PCMCIA_BVD1 */
state->bvd2 = 1; /* Might be cs3reg & PCMCIA_BVD2 */
if ((cs3reg & (PCMCIA_VS1|PCMCIA_VS2)) ==
(PCMCIA_VS1|PCMCIA_VS2)) {
state->vs_3v=0;
state->vs_Xv=0;
} else {
state->vs_3v=1;
state->vs_Xv=0;
}
}
static int
simpad_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
unsigned long flags;
local_irq_save(flags);
/* Murphy: see table of MIC2562a-1 */
switch (state->Vcc) {
case 0:
simpad_clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
break;
case 33:
simpad_clear_cs3_bit(VCC_3V_EN|EN1);
simpad_set_cs3_bit(VCC_5V_EN|EN0);
break;
case 50:
simpad_clear_cs3_bit(VCC_5V_EN|EN1);
simpad_set_cs3_bit(VCC_3V_EN|EN0);
break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
__func__, state->Vcc);
simpad_clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
local_irq_restore(flags);
return -1;
}
local_irq_restore(flags);
return 0;
}
static void simpad_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
{
simpad_set_cs3_bit(PCMCIA_RESET);
}
static struct pcmcia_low_level simpad_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = simpad_pcmcia_hw_init,
.hw_shutdown = simpad_pcmcia_hw_shutdown,
.socket_state = simpad_pcmcia_socket_state,
.configure_socket = simpad_pcmcia_configure_socket,
.socket_suspend = simpad_pcmcia_socket_suspend,
};
int pcmcia_simpad_init(struct device *dev)
{
int ret = -ENODEV;
if (machine_is_simpad())
ret = sa11xx_drv_pcmcia_probe(dev, &simpad_pcmcia_ops, 1, 1);
return ret;
}

View file

@ -0,0 +1,166 @@
/*
* linux/drivers/pcmcia/sa1100_badge4.c
*
* BadgePAD 4 PCMCIA specific routines
*
* Christopher Hoover <ch@hpl.hp.com>
*
* Copyright (C) 2002 Hewlett-Packard Company
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <mach/badge4.h>
#include <asm/hardware/sa1111.h>
#include "sa1111_generic.h"
/*
* BadgePAD 4 Details
*
* PCM Vcc:
*
* PCM Vcc on BadgePAD 4 can be jumpered for 3v3 (short pins 1 and 3
* on JP6) or 5v0 (short pins 3 and 5 on JP6).
*
* PCM Vpp:
*
* PCM Vpp on BadgePAD 4 can be jumpered for 12v0 (short pins 4 and 6
* on JP6) or tied to PCM Vcc (short pins 2 and 4 on JP6). N.B.,
* 12v0 operation requires that the power supply actually supply 12v0
* via pin 7 of JP7.
*
* CF Vcc:
*
* CF Vcc on BadgePAD 4 can be jumpered either for 3v3 (short pins 1
* and 2 on JP10) or 5v0 (short pins 2 and 3 on JP10).
*
* Unfortunately there's no way programmatically to determine how a
* given board is jumpered. This code assumes a default jumpering
* as described below.
*
* If the defaults aren't correct, you may override them with a pcmv
* setup argument: pcmv=<pcm vcc>,<pcm vpp>,<cf vcc>. The units are
* tenths of volts; e.g. pcmv=33,120,50 indicates 3v3 PCM Vcc, 12v0
* PCM Vpp, and 5v0 CF Vcc.
*
*/
static int badge4_pcmvcc = 50; /* pins 3 and 5 jumpered on JP6 */
static int badge4_pcmvpp = 50; /* pins 2 and 4 jumpered on JP6 */
static int badge4_cfvcc = 33; /* pins 1 and 2 jumpered on JP10 */
static void complain_about_jumpering(const char *whom,
const char *supply,
int given, int wanted)
{
printk(KERN_ERR
"%s: %s %d.%dV wanted but board is jumpered for %s %d.%dV operation"
"; re-jumper the board and/or use pcmv=xx,xx,xx\n",
whom, supply,
wanted / 10, wanted % 10,
supply,
given / 10, given % 10);
}
static int
badge4_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
{
int ret;
switch (skt->nr) {
case 0:
if ((state->Vcc != 0) &&
(state->Vcc != badge4_pcmvcc)) {
complain_about_jumpering(__func__, "pcmvcc",
badge4_pcmvcc, state->Vcc);
// Apply power regardless of the jumpering.
// return -1;
}
if ((state->Vpp != 0) &&
(state->Vpp != badge4_pcmvpp)) {
complain_about_jumpering(__func__, "pcmvpp",
badge4_pcmvpp, state->Vpp);
return -1;
}
break;
case 1:
if ((state->Vcc != 0) &&
(state->Vcc != badge4_cfvcc)) {
complain_about_jumpering(__func__, "cfvcc",
badge4_cfvcc, state->Vcc);
return -1;
}
break;
default:
return -1;
}
ret = sa1111_pcmcia_configure_socket(skt, state);
if (ret == 0) {
unsigned long flags;
int need5V;
local_irq_save(flags);
need5V = ((state->Vcc == 50) || (state->Vpp == 50));
badge4_set_5V(BADGE4_5V_PCMCIA_SOCK(skt->nr), need5V);
local_irq_restore(flags);
}
return ret;
}
static struct pcmcia_low_level badge4_pcmcia_ops = {
.owner = THIS_MODULE,
.configure_socket = badge4_pcmcia_configure_socket,
.first = 0,
.nr = 2,
};
int pcmcia_badge4_init(struct device *dev)
{
int ret = -ENODEV;
if (machine_is_badge4()) {
printk(KERN_INFO
"%s: badge4_pcmvcc=%d, badge4_pcmvpp=%d, badge4_cfvcc=%d\n",
__func__,
badge4_pcmvcc, badge4_pcmvpp, badge4_cfvcc);
sa11xx_drv_pcmcia_ops(&badge4_pcmcia_ops);
ret = sa1111_pcmcia_add(dev, &badge4_pcmcia_ops,
sa11xx_drv_pcmcia_add_one);
}
return ret;
}
static int __init pcmv_setup(char *s)
{
int v[4];
s = get_options(s, ARRAY_SIZE(v), v);
if (v[0] >= 1) badge4_pcmvcc = v[1];
if (v[0] >= 2) badge4_pcmvpp = v[2];
if (v[0] >= 3) badge4_cfvcc = v[3];
return 1;
}
__setup("pcmv=", pcmv_setup);

View file

@ -0,0 +1,254 @@
/*
* linux/drivers/pcmcia/sa1111_generic.c
*
* We implement the generic parts of a SA1111 PCMCIA driver. This
* basically means we handle everything except controlling the
* power. Power is machine specific...
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <pcmcia/ss.h>
#include <mach/hardware.h>
#include <asm/hardware/sa1111.h>
#include <asm/irq.h>
#include "sa1111_generic.h"
/*
* These are offsets from the above base.
*/
#define PCCR 0x0000
#define PCSSR 0x0004
#define PCSR 0x0008
#define PCSR_S0_READY (1<<0)
#define PCSR_S1_READY (1<<1)
#define PCSR_S0_DETECT (1<<2)
#define PCSR_S1_DETECT (1<<3)
#define PCSR_S0_VS1 (1<<4)
#define PCSR_S0_VS2 (1<<5)
#define PCSR_S1_VS1 (1<<6)
#define PCSR_S1_VS2 (1<<7)
#define PCSR_S0_WP (1<<8)
#define PCSR_S1_WP (1<<9)
#define PCSR_S0_BVD1 (1<<10)
#define PCSR_S0_BVD2 (1<<11)
#define PCSR_S1_BVD1 (1<<12)
#define PCSR_S1_BVD2 (1<<13)
#define PCCR_S0_RST (1<<0)
#define PCCR_S1_RST (1<<1)
#define PCCR_S0_FLT (1<<2)
#define PCCR_S1_FLT (1<<3)
#define PCCR_S0_PWAITEN (1<<4)
#define PCCR_S1_PWAITEN (1<<5)
#define PCCR_S0_PSE (1<<6)
#define PCCR_S1_PSE (1<<7)
#define PCSSR_S0_SLEEP (1<<0)
#define PCSSR_S1_SLEEP (1<<1)
#define IDX_IRQ_S0_READY_NINT (0)
#define IDX_IRQ_S0_CD_VALID (1)
#define IDX_IRQ_S0_BVD1_STSCHG (2)
#define IDX_IRQ_S1_READY_NINT (3)
#define IDX_IRQ_S1_CD_VALID (4)
#define IDX_IRQ_S1_BVD1_STSCHG (5)
void sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
{
struct sa1111_pcmcia_socket *s = to_skt(skt);
unsigned long status = sa1111_readl(s->dev->mapbase + PCSR);
switch (skt->nr) {
case 0:
state->detect = status & PCSR_S0_DETECT ? 0 : 1;
state->ready = status & PCSR_S0_READY ? 1 : 0;
state->bvd1 = status & PCSR_S0_BVD1 ? 1 : 0;
state->bvd2 = status & PCSR_S0_BVD2 ? 1 : 0;
state->wrprot = status & PCSR_S0_WP ? 1 : 0;
state->vs_3v = status & PCSR_S0_VS1 ? 0 : 1;
state->vs_Xv = status & PCSR_S0_VS2 ? 0 : 1;
break;
case 1:
state->detect = status & PCSR_S1_DETECT ? 0 : 1;
state->ready = status & PCSR_S1_READY ? 1 : 0;
state->bvd1 = status & PCSR_S1_BVD1 ? 1 : 0;
state->bvd2 = status & PCSR_S1_BVD2 ? 1 : 0;
state->wrprot = status & PCSR_S1_WP ? 1 : 0;
state->vs_3v = status & PCSR_S1_VS1 ? 0 : 1;
state->vs_Xv = status & PCSR_S1_VS2 ? 0 : 1;
break;
}
}
int sa1111_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
{
struct sa1111_pcmcia_socket *s = to_skt(skt);
unsigned int pccr_skt_mask, pccr_set_mask, val;
unsigned long flags;
switch (skt->nr) {
case 0:
pccr_skt_mask = PCCR_S0_RST|PCCR_S0_FLT|PCCR_S0_PWAITEN|PCCR_S0_PSE;
break;
case 1:
pccr_skt_mask = PCCR_S1_RST|PCCR_S1_FLT|PCCR_S1_PWAITEN|PCCR_S1_PSE;
break;
default:
return -1;
}
pccr_set_mask = 0;
if (state->Vcc != 0)
pccr_set_mask |= PCCR_S0_PWAITEN|PCCR_S1_PWAITEN;
if (state->Vcc == 50)
pccr_set_mask |= PCCR_S0_PSE|PCCR_S1_PSE;
if (state->flags & SS_RESET)
pccr_set_mask |= PCCR_S0_RST|PCCR_S1_RST;
if (state->flags & SS_OUTPUT_ENA)
pccr_set_mask |= PCCR_S0_FLT|PCCR_S1_FLT;
local_irq_save(flags);
val = sa1111_readl(s->dev->mapbase + PCCR);
val &= ~pccr_skt_mask;
val |= pccr_set_mask & pccr_skt_mask;
sa1111_writel(val, s->dev->mapbase + PCCR);
local_irq_restore(flags);
return 0;
}
int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops,
int (*add)(struct soc_pcmcia_socket *))
{
struct sa1111_pcmcia_socket *s;
int i, ret = 0;
ops->socket_state = sa1111_pcmcia_socket_state;
for (i = 0; i < ops->nr; i++) {
s = kzalloc(sizeof(*s), GFP_KERNEL);
if (!s)
return -ENOMEM;
s->soc.nr = ops->first + i;
soc_pcmcia_init_one(&s->soc, ops, &dev->dev);
s->dev = dev;
if (s->soc.nr) {
s->soc.socket.pci_irq = dev->irq[IDX_IRQ_S1_READY_NINT];
s->soc.stat[SOC_STAT_CD].irq = dev->irq[IDX_IRQ_S1_CD_VALID];
s->soc.stat[SOC_STAT_CD].name = "SA1111 CF card detect";
s->soc.stat[SOC_STAT_BVD1].irq = dev->irq[IDX_IRQ_S1_BVD1_STSCHG];
s->soc.stat[SOC_STAT_BVD1].name = "SA1111 CF BVD1";
} else {
s->soc.socket.pci_irq = dev->irq[IDX_IRQ_S0_READY_NINT];
s->soc.stat[SOC_STAT_CD].irq = dev->irq[IDX_IRQ_S0_CD_VALID];
s->soc.stat[SOC_STAT_CD].name = "SA1111 PCMCIA card detect";
s->soc.stat[SOC_STAT_BVD1].irq = dev->irq[IDX_IRQ_S0_BVD1_STSCHG];
s->soc.stat[SOC_STAT_BVD1].name = "SA1111 PCMCIA BVD1";
}
ret = add(&s->soc);
if (ret == 0) {
s->next = dev_get_drvdata(&dev->dev);
dev_set_drvdata(&dev->dev, s);
} else
kfree(s);
}
return ret;
}
static int pcmcia_probe(struct sa1111_dev *dev)
{
void __iomem *base;
int ret;
ret = sa1111_enable_device(dev);
if (ret)
return ret;
dev_set_drvdata(&dev->dev, NULL);
if (!request_mem_region(dev->res.start, 512, SA1111_DRIVER_NAME(dev))) {
sa1111_disable_device(dev);
return -EBUSY;
}
base = dev->mapbase;
/*
* Initialise the suspend state.
*/
sa1111_writel(PCSSR_S0_SLEEP | PCSSR_S1_SLEEP, base + PCSSR);
sa1111_writel(PCCR_S0_FLT | PCCR_S1_FLT, base + PCCR);
#ifdef CONFIG_SA1100_BADGE4
pcmcia_badge4_init(&dev->dev);
#endif
#ifdef CONFIG_SA1100_JORNADA720
pcmcia_jornada720_init(&dev->dev);
#endif
#ifdef CONFIG_ARCH_LUBBOCK
pcmcia_lubbock_init(dev);
#endif
#ifdef CONFIG_ASSABET_NEPONSET
pcmcia_neponset_init(dev);
#endif
return 0;
}
static int pcmcia_remove(struct sa1111_dev *dev)
{
struct sa1111_pcmcia_socket *next, *s = dev_get_drvdata(&dev->dev);
dev_set_drvdata(&dev->dev, NULL);
for (; s; s = next) {
next = s->next;
soc_pcmcia_remove_one(&s->soc);
kfree(s);
}
release_mem_region(dev->res.start, 512);
sa1111_disable_device(dev);
return 0;
}
static struct sa1111_driver pcmcia_driver = {
.drv = {
.name = "sa1111-pcmcia",
},
.devid = SA1111_DEVID_PCMCIA,
.probe = pcmcia_probe,
.remove = pcmcia_remove,
};
static int __init sa1111_drv_pcmcia_init(void)
{
return sa1111_driver_register(&pcmcia_driver);
}
static void __exit sa1111_drv_pcmcia_exit(void)
{
sa1111_driver_unregister(&pcmcia_driver);
}
fs_initcall(sa1111_drv_pcmcia_init);
module_exit(sa1111_drv_pcmcia_exit);
MODULE_DESCRIPTION("SA1111 PCMCIA card socket driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,25 @@
#include "soc_common.h"
#include "sa11xx_base.h"
struct sa1111_pcmcia_socket {
struct soc_pcmcia_socket soc;
struct sa1111_dev *dev;
struct sa1111_pcmcia_socket *next;
};
static inline struct sa1111_pcmcia_socket *to_skt(struct soc_pcmcia_socket *s)
{
return container_of(s, struct sa1111_pcmcia_socket, soc);
}
int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops,
int (*add)(struct soc_pcmcia_socket *));
extern void sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *, struct pcmcia_state *);
extern int sa1111_pcmcia_configure_socket(struct soc_pcmcia_socket *, const socket_state_t *);
extern int pcmcia_badge4_init(struct device *);
extern int pcmcia_jornada720_init(struct device *);
extern int pcmcia_lubbock_init(struct sa1111_dev *);
extern int pcmcia_neponset_init(struct sa1111_dev *);

View file

@ -0,0 +1,116 @@
/*
* drivers/pcmcia/sa1100_jornada720.c
*
* Jornada720 PCMCIA specific routines
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include <asm/hardware/sa1111.h>
#include <asm/mach-types.h>
#include "sa1111_generic.h"
/* Does SOCKET1_3V actually do anything? */
#define SOCKET0_POWER GPIO_GPIO0
#define SOCKET0_3V GPIO_GPIO2
#define SOCKET1_POWER (GPIO_GPIO1 | GPIO_GPIO3)
#define SOCKET1_3V GPIO_GPIO3
static int
jornada720_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
{
struct sa1111_pcmcia_socket *s = to_skt(skt);
unsigned int pa_dwr_mask, pa_dwr_set;
int ret;
printk(KERN_INFO "%s(): config socket %d vcc %d vpp %d\n", __func__,
skt->nr, state->Vcc, state->Vpp);
switch (skt->nr) {
case 0:
pa_dwr_mask = SOCKET0_POWER | SOCKET0_3V;
switch (state->Vcc) {
default:
case 0:
pa_dwr_set = 0;
break;
case 33:
pa_dwr_set = SOCKET0_POWER | SOCKET0_3V;
break;
case 50:
pa_dwr_set = SOCKET0_POWER;
break;
}
break;
case 1:
pa_dwr_mask = SOCKET1_POWER;
switch (state->Vcc) {
default:
case 0:
pa_dwr_set = 0;
break;
case 33:
pa_dwr_set = SOCKET1_POWER;
break;
case 50:
pa_dwr_set = SOCKET1_POWER;
break;
}
break;
default:
return -1;
}
if (state->Vpp != state->Vcc && state->Vpp != 0) {
printk(KERN_ERR "%s(): slot cannot support VPP %u\n",
__func__, state->Vpp);
return -EPERM;
}
ret = sa1111_pcmcia_configure_socket(skt, state);
if (ret == 0)
sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set);
return ret;
}
static struct pcmcia_low_level jornada720_pcmcia_ops = {
.owner = THIS_MODULE,
.configure_socket = jornada720_pcmcia_configure_socket,
.first = 0,
.nr = 2,
};
int pcmcia_jornada720_init(struct device *dev)
{
int ret = -ENODEV;
struct sa1111_dev *sadev = SA1111_DEV(dev);
if (machine_is_jornada720()) {
unsigned int pin = GPIO_A0 | GPIO_A1 | GPIO_A2 | GPIO_A3;
GRER |= 0x00000002;
/* Set GPIO_A<3:1> to be outputs for PCMCIA/CF power controller: */
sa1111_set_io_dir(sadev, pin, 0, 0);
sa1111_set_io(sadev, pin, 0);
sa1111_set_sleep_io(sadev, pin, 0);
sa11xx_drv_pcmcia_ops(&jornada720_pcmcia_ops);
ret = sa1111_pcmcia_add(sadev, &jornada720_pcmcia_ops,
sa11xx_drv_pcmcia_add_one);
}
return ret;
}

View file

@ -0,0 +1,236 @@
/*
* linux/drivers/pcmcia/pxa2xx_lubbock.c
*
* Author: George Davis
* Created: Jan 10, 2002
* Copyright: MontaVista Software Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Originally based upon linux/drivers/pcmcia/sa1100_neponset.c
*
* Lubbock PCMCIA specific routines.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <mach/hardware.h>
#include <asm/hardware/sa1111.h>
#include <asm/mach-types.h>
#include <mach/lubbock.h>
#include "sa1111_generic.h"
static int
lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
struct sa1111_pcmcia_socket *s = to_skt(skt);
unsigned int pa_dwr_mask, pa_dwr_set, misc_mask, misc_set;
int ret = 0;
pa_dwr_mask = pa_dwr_set = misc_mask = misc_set = 0;
/* Lubbock uses the Maxim MAX1602, with the following connections:
*
* Socket 0 (PCMCIA):
* MAX1602 Lubbock Register
* Pin Signal
* ----- ------- ----------------------
* A0VPP S0_PWR0 SA-1111 GPIO A<0>
* A1VPP S0_PWR1 SA-1111 GPIO A<1>
* A0VCC S0_PWR2 SA-1111 GPIO A<2>
* A1VCC S0_PWR3 SA-1111 GPIO A<3>
* VX VCC
* VY +3.3V
* 12IN +12V
* CODE +3.3V Cirrus Code, CODE = High (VY)
*
* Socket 1 (CF):
* MAX1602 Lubbock Register
* Pin Signal
* ----- ------- ----------------------
* A0VPP GND VPP is not connected
* A1VPP GND VPP is not connected
* A0VCC S1_PWR0 MISC_WR<14>
* A1VCC S1_PWR1 MISC_WR<15>
* VX VCC
* VY +3.3V
* 12IN GND VPP is not connected
* CODE +3.3V Cirrus Code, CODE = High (VY)
*
*/
again:
switch (skt->nr) {
case 0:
pa_dwr_mask = GPIO_A0 | GPIO_A1 | GPIO_A2 | GPIO_A3;
switch (state->Vcc) {
case 0: /* Hi-Z */
break;
case 33: /* VY */
pa_dwr_set |= GPIO_A3;
break;
case 50: /* VX */
pa_dwr_set |= GPIO_A2;
break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
__func__, state->Vcc);
ret = -1;
}
switch (state->Vpp) {
case 0: /* Hi-Z */
break;
case 120: /* 12IN */
pa_dwr_set |= GPIO_A1;
break;
default: /* VCC */
if (state->Vpp == state->Vcc)
pa_dwr_set |= GPIO_A0;
else {
printk(KERN_ERR "%s(): unrecognized Vpp %u\n",
__func__, state->Vpp);
ret = -1;
break;
}
}
break;
case 1:
misc_mask = (1 << 15) | (1 << 14);
switch (state->Vcc) {
case 0: /* Hi-Z */
break;
case 33: /* VY */
misc_set |= 1 << 15;
break;
case 50: /* VX */
misc_set |= 1 << 14;
break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
__func__, state->Vcc);
ret = -1;
break;
}
if (state->Vpp != state->Vcc && state->Vpp != 0) {
printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n",
__func__, state->Vpp);
ret = -1;
break;
}
break;
default:
ret = -1;
}
if (ret == 0)
ret = sa1111_pcmcia_configure_socket(skt, state);
if (ret == 0) {
lubbock_set_misc_wr(misc_mask, misc_set);
sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set);
}
#if 1
if (ret == 0 && state->Vcc == 33) {
struct pcmcia_state new_state;
/*
* HACK ALERT:
* We can't sense the voltage properly on Lubbock before
* actually applying some power to the socket (catch 22).
* Resense the socket Voltage Sense pins after applying
* socket power.
*
* Note: It takes about 2.5ms for the MAX1602 VCC output
* to rise.
*/
mdelay(3);
sa1111_pcmcia_socket_state(skt, &new_state);
if (!new_state.vs_3v && !new_state.vs_Xv) {
/*
* Switch to 5V, Configure socket with 5V voltage
*/
lubbock_set_misc_wr(misc_mask, 0);
sa1111_set_io(s->dev, pa_dwr_mask, 0);
/*
* It takes about 100ms to turn off Vcc.
*/
mdelay(100);
/*
* We need to hack around the const qualifier as
* well to keep this ugly workaround localized and
* not force it to the rest of the code. Barf bags
* available in the seat pocket in front of you!
*/
((socket_state_t *)state)->Vcc = 50;
((socket_state_t *)state)->Vpp = 50;
goto again;
}
}
#endif
return ret;
}
static struct pcmcia_low_level lubbock_pcmcia_ops = {
.owner = THIS_MODULE,
.configure_socket = lubbock_pcmcia_configure_socket,
.first = 0,
.nr = 2,
};
#include "pxa2xx_base.h"
int pcmcia_lubbock_init(struct sa1111_dev *sadev)
{
int ret = -ENODEV;
if (machine_is_lubbock()) {
/*
* Set GPIO_A<3:0> to be outputs for the MAX1600,
* and switch to standby mode.
*/
sa1111_set_io_dir(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0, 0);
sa1111_set_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0);
sa1111_set_sleep_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0);
/* Set CF Socket 1 power to standby mode. */
lubbock_set_misc_wr((1 << 15) | (1 << 14), 0);
pxa2xx_drv_pcmcia_ops(&lubbock_pcmcia_ops);
pxa2xx_configure_sockets(&sadev->dev);
ret = sa1111_pcmcia_add(sadev, &lubbock_pcmcia_ops,
pxa2xx_drv_pcmcia_add_one);
}
return ret;
}
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,129 @@
/*
* linux/drivers/pcmcia/sa1100_neponset.c
*
* Neponset PCMCIA specific routines
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <mach/neponset.h>
#include <asm/hardware/sa1111.h>
#include "sa1111_generic.h"
/*
* Neponset uses the Maxim MAX1600, with the following connections:
*
* MAX1600 Neponset
*
* A0VCC SA-1111 GPIO A<1>
* A1VCC SA-1111 GPIO A<0>
* A0VPP CPLD NCR A0VPP
* A1VPP CPLD NCR A1VPP
* B0VCC SA-1111 GPIO A<2>
* B1VCC SA-1111 GPIO A<3>
* B0VPP ground (slot B is CF)
* B1VPP ground (slot B is CF)
*
* VX VCC (5V)
* VY VCC3_3 (3.3V)
* 12INA 12V
* 12INB ground (slot B is CF)
*
* The MAX1600 CODE pin is tied to ground, placing the device in
* "Standard Intel code" mode. Refer to the Maxim data sheet for
* the corresponding truth table.
*/
static int
neponset_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
{
struct sa1111_pcmcia_socket *s = to_skt(skt);
unsigned int ncr_mask, ncr_set, pa_dwr_mask, pa_dwr_set;
int ret;
switch (skt->nr) {
case 0:
pa_dwr_mask = GPIO_A0 | GPIO_A1;
ncr_mask = NCR_A0VPP | NCR_A1VPP;
if (state->Vpp == 0)
ncr_set = 0;
else if (state->Vpp == 120)
ncr_set = NCR_A1VPP;
else if (state->Vpp == state->Vcc)
ncr_set = NCR_A0VPP;
else {
printk(KERN_ERR "%s(): unrecognized VPP %u\n",
__func__, state->Vpp);
return -1;
}
break;
case 1:
pa_dwr_mask = GPIO_A2 | GPIO_A3;
ncr_mask = 0;
ncr_set = 0;
if (state->Vpp != state->Vcc && state->Vpp != 0) {
printk(KERN_ERR "%s(): CF slot cannot support VPP %u\n",
__func__, state->Vpp);
return -1;
}
break;
default:
return -1;
}
/*
* pa_dwr_set is the mask for selecting Vcc on both sockets.
* pa_dwr_mask selects which bits (and therefore socket) we change.
*/
switch (state->Vcc) {
default:
case 0: pa_dwr_set = 0; break;
case 33: pa_dwr_set = GPIO_A1|GPIO_A2; break;
case 50: pa_dwr_set = GPIO_A0|GPIO_A3; break;
}
ret = sa1111_pcmcia_configure_socket(skt, state);
if (ret == 0) {
neponset_ncr_frob(ncr_mask, ncr_set);
sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set);
}
return ret;
}
static struct pcmcia_low_level neponset_pcmcia_ops = {
.owner = THIS_MODULE,
.configure_socket = neponset_pcmcia_configure_socket,
.first = 0,
.nr = 2,
};
int pcmcia_neponset_init(struct sa1111_dev *sadev)
{
int ret = -ENODEV;
if (machine_is_assabet()) {
/*
* Set GPIO_A<3:0> to be outputs for the MAX1600,
* and switch to standby mode.
*/
sa1111_set_io_dir(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0, 0);
sa1111_set_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0);
sa1111_set_sleep_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0);
sa11xx_drv_pcmcia_ops(&neponset_pcmcia_ops);
ret = sa1111_pcmcia_add(sadev, &neponset_pcmcia_ops,
sa11xx_drv_pcmcia_add_one);
}
return ret;
}

View file

@ -0,0 +1,266 @@
/*======================================================================
Device driver for the PCMCIA control functionality of StrongARM
SA-1100 microprocessors.
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.
The initial developer of the original code is John G. Dorsey
<john+@cs.cmu.edu>. Portions created by John G. Dorsey are
Copyright (C) 1999 John G. Dorsey. All Rights Reserved.
Alternatively, the contents of this file may be used under the
terms of the GNU Public License version 2 (the "GPL"), in which
case the provisions of the GPL are applicable instead of the
above. If you wish to allow the use of your version of this file
only under the terms of the GPL and not to allow others to use
your version of this file under the MPL, indicate your decision
by deleting the provisions above and replace them with the notice
and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this
file under either the MPL or the GPL.
======================================================================*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include "soc_common.h"
#include "sa11xx_base.h"
/*
* sa1100_pcmcia_default_mecr_timing
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*
* Calculate MECR clock wait states for given CPU clock
* speed and command wait state. This function can be over-
* written by a board specific version.
*
* The default is to simply calculate the BS values as specified in
* the INTEL SA1100 development manual
* "Expansion Memory (PCMCIA) Configuration Register (MECR)"
* that's section 10.2.5 in _my_ version of the manual ;)
*/
static unsigned int
sa1100_pcmcia_default_mecr_timing(struct soc_pcmcia_socket *skt,
unsigned int cpu_speed,
unsigned int cmd_time)
{
return sa1100_pcmcia_mecr_bs(cmd_time, cpu_speed);
}
/* sa1100_pcmcia_set_mecr()
* ^^^^^^^^^^^^^^^^^^^^^^^^
*
* set MECR value for socket <sock> based on this sockets
* io, mem and attribute space access speed.
* Call board specific BS value calculation to allow boards
* to tweak the BS values.
*/
static int
sa1100_pcmcia_set_mecr(struct soc_pcmcia_socket *skt, unsigned int cpu_clock)
{
struct soc_pcmcia_timing timing;
u32 mecr, old_mecr;
unsigned long flags;
unsigned int bs_io, bs_mem, bs_attr;
soc_common_pcmcia_get_timing(skt, &timing);
bs_io = skt->ops->get_timing(skt, cpu_clock, timing.io);
bs_mem = skt->ops->get_timing(skt, cpu_clock, timing.mem);
bs_attr = skt->ops->get_timing(skt, cpu_clock, timing.attr);
local_irq_save(flags);
old_mecr = mecr = MECR;
MECR_FAST_SET(mecr, skt->nr, 0);
MECR_BSIO_SET(mecr, skt->nr, bs_io);
MECR_BSA_SET(mecr, skt->nr, bs_attr);
MECR_BSM_SET(mecr, skt->nr, bs_mem);
if (old_mecr != mecr)
MECR = mecr;
local_irq_restore(flags);
debug(skt, 2, "FAST %X BSM %X BSA %X BSIO %X\n",
MECR_FAST_GET(mecr, skt->nr),
MECR_BSM_GET(mecr, skt->nr), MECR_BSA_GET(mecr, skt->nr),
MECR_BSIO_GET(mecr, skt->nr));
return 0;
}
#ifdef CONFIG_CPU_FREQ
static int
sa1100_pcmcia_frequency_change(struct soc_pcmcia_socket *skt,
unsigned long val,
struct cpufreq_freqs *freqs)
{
switch (val) {
case CPUFREQ_PRECHANGE:
if (freqs->new > freqs->old)
sa1100_pcmcia_set_mecr(skt, freqs->new);
break;
case CPUFREQ_POSTCHANGE:
if (freqs->new < freqs->old)
sa1100_pcmcia_set_mecr(skt, freqs->new);
break;
}
return 0;
}
#endif
static int
sa1100_pcmcia_set_timing(struct soc_pcmcia_socket *skt)
{
return sa1100_pcmcia_set_mecr(skt, cpufreq_get(0));
}
static int
sa1100_pcmcia_show_timing(struct soc_pcmcia_socket *skt, char *buf)
{
struct soc_pcmcia_timing timing;
unsigned int clock = cpufreq_get(0);
unsigned long mecr = MECR;
char *p = buf;
soc_common_pcmcia_get_timing(skt, &timing);
p+=sprintf(p, "I/O : %u (%u)\n", timing.io,
sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, skt->nr)));
p+=sprintf(p, "attribute: %u (%u)\n", timing.attr,
sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, skt->nr)));
p+=sprintf(p, "common : %u (%u)\n", timing.mem,
sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, skt->nr)));
return p - buf;
}
static const char *skt_names[] = {
"PCMCIA socket 0",
"PCMCIA socket 1",
};
#define SKT_DEV_INFO_SIZE(n) \
(sizeof(struct skt_dev_info) + (n)*sizeof(struct soc_pcmcia_socket))
int sa11xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt)
{
skt->res_skt.start = _PCMCIA(skt->nr);
skt->res_skt.end = _PCMCIA(skt->nr) + PCMCIASp - 1;
skt->res_skt.name = skt_names[skt->nr];
skt->res_skt.flags = IORESOURCE_MEM;
skt->res_io.start = _PCMCIAIO(skt->nr);
skt->res_io.end = _PCMCIAIO(skt->nr) + PCMCIAIOSp - 1;
skt->res_io.name = "io";
skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
skt->res_mem.start = _PCMCIAMem(skt->nr);
skt->res_mem.end = _PCMCIAMem(skt->nr) + PCMCIAMemSp - 1;
skt->res_mem.name = "memory";
skt->res_mem.flags = IORESOURCE_MEM;
skt->res_attr.start = _PCMCIAAttr(skt->nr);
skt->res_attr.end = _PCMCIAAttr(skt->nr) + PCMCIAAttrSp - 1;
skt->res_attr.name = "attribute";
skt->res_attr.flags = IORESOURCE_MEM;
return soc_pcmcia_add_one(skt);
}
EXPORT_SYMBOL(sa11xx_drv_pcmcia_add_one);
void sa11xx_drv_pcmcia_ops(struct pcmcia_low_level *ops)
{
/*
* set default MECR calculation if the board specific
* code did not specify one...
*/
if (!ops->get_timing)
ops->get_timing = sa1100_pcmcia_default_mecr_timing;
/* Provide our SA11x0 specific timing routines. */
ops->set_timing = sa1100_pcmcia_set_timing;
ops->show_timing = sa1100_pcmcia_show_timing;
#ifdef CONFIG_CPU_FREQ
ops->frequency_change = sa1100_pcmcia_frequency_change;
#endif
}
EXPORT_SYMBOL(sa11xx_drv_pcmcia_ops);
int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops,
int first, int nr)
{
struct skt_dev_info *sinfo;
struct soc_pcmcia_socket *skt;
int i, ret = 0;
sa11xx_drv_pcmcia_ops(ops);
sinfo = kzalloc(SKT_DEV_INFO_SIZE(nr), GFP_KERNEL);
if (!sinfo)
return -ENOMEM;
sinfo->nskt = nr;
/* Initialize processor specific parameters */
for (i = 0; i < nr; i++) {
skt = &sinfo->skt[i];
skt->nr = first + i;
soc_pcmcia_init_one(skt, ops, dev);
ret = sa11xx_drv_pcmcia_add_one(skt);
if (ret)
break;
}
if (ret) {
while (--i >= 0)
soc_pcmcia_remove_one(&sinfo->skt[i]);
kfree(sinfo);
} else {
dev_set_drvdata(dev, sinfo);
}
return ret;
}
EXPORT_SYMBOL(sa11xx_drv_pcmcia_probe);
static int __init sa11xx_pcmcia_init(void)
{
return 0;
}
fs_initcall(sa11xx_pcmcia_init);
static void __exit sa11xx_pcmcia_exit(void) {}
module_exit(sa11xx_pcmcia_exit);
MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-11xx core socket driver");
MODULE_LICENSE("Dual MPL/GPL");

View file

@ -0,0 +1,125 @@
/*======================================================================
Device driver for the PCMCIA control functionality of StrongARM
SA-1100 microprocessors.
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.
The initial developer of the original code is John G. Dorsey
<john+@cs.cmu.edu>. Portions created by John G. Dorsey are
Copyright (C) 1999 John G. Dorsey. All Rights Reserved.
Alternatively, the contents of this file may be used under the
terms of the GNU Public License version 2 (the "GPL"), in which
case the provisions of the GPL are applicable instead of the
above. If you wish to allow the use of your version of this file
only under the terms of the GPL and not to allow others to use
your version of this file under the MPL, indicate your decision
by deleting the provisions above and replace them with the notice
and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this
file under either the MPL or the GPL.
======================================================================*/
#if !defined(_PCMCIA_SA1100_H)
# define _PCMCIA_SA1100_H
/* SA-1100 PCMCIA Memory and I/O timing
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* The SA-1110 Developer's Manual, section 10.2.5, says the following:
*
* "To calculate the recommended BS_xx value for each address space:
* divide the command width time (the greater of twIOWR and twIORD,
* or the greater of twWE and twOE) by processor cycle time; divide
* by 2; divide again by 3 (number of BCLK's per command assertion);
* round up to the next whole number; and subtract 1."
*/
/* MECR: Expansion Memory Configuration Register
* (SA-1100 Developers Manual, p.10-13; SA-1110 Developers Manual, p.10-24)
*
* MECR layout is:
*
* FAST1 BSM1<4:0> BSA1<4:0> BSIO1<4:0> FAST0 BSM0<4:0> BSA0<4:0> BSIO0<4:0>
*
* (This layout is actually true only for the SA-1110; the FASTn bits are
* reserved on the SA-1100.)
*/
#define MECR_SOCKET_0_SHIFT (0)
#define MECR_SOCKET_1_SHIFT (16)
#define MECR_BS_MASK (0x1f)
#define MECR_FAST_MODE_MASK (0x01)
#define MECR_BSIO_SHIFT (0)
#define MECR_BSA_SHIFT (5)
#define MECR_BSM_SHIFT (10)
#define MECR_FAST_SHIFT (15)
#define MECR_SET(mecr, sock, shift, mask, bs) \
((mecr)=((mecr)&~(((mask)<<(shift))<<\
((sock)==0?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT)))|\
(((bs)<<(shift))<<((sock)==0?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT)))
#define MECR_GET(mecr, sock, shift, mask) \
((((mecr)>>(((sock)==0)?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT))>>\
(shift))&(mask))
#define MECR_BSIO_SET(mecr, sock, bs) \
MECR_SET((mecr), (sock), MECR_BSIO_SHIFT, MECR_BS_MASK, (bs))
#define MECR_BSIO_GET(mecr, sock) \
MECR_GET((mecr), (sock), MECR_BSIO_SHIFT, MECR_BS_MASK)
#define MECR_BSA_SET(mecr, sock, bs) \
MECR_SET((mecr), (sock), MECR_BSA_SHIFT, MECR_BS_MASK, (bs))
#define MECR_BSA_GET(mecr, sock) \
MECR_GET((mecr), (sock), MECR_BSA_SHIFT, MECR_BS_MASK)
#define MECR_BSM_SET(mecr, sock, bs) \
MECR_SET((mecr), (sock), MECR_BSM_SHIFT, MECR_BS_MASK, (bs))
#define MECR_BSM_GET(mecr, sock) \
MECR_GET((mecr), (sock), MECR_BSM_SHIFT, MECR_BS_MASK)
#define MECR_FAST_SET(mecr, sock, fast) \
MECR_SET((mecr), (sock), MECR_FAST_SHIFT, MECR_FAST_MODE_MASK, (fast))
#define MECR_FAST_GET(mecr, sock) \
MECR_GET((mecr), (sock), MECR_FAST_SHIFT, MECR_FAST_MODE_MASK)
/* This function implements the BS value calculation for setting the MECR
* using integer arithmetic:
*/
static inline unsigned int sa1100_pcmcia_mecr_bs(unsigned int pcmcia_cycle_ns,
unsigned int cpu_clock_khz){
unsigned int t = ((pcmcia_cycle_ns * cpu_clock_khz) / 6) - 1000000;
return (t / 1000000) + (((t % 1000000) == 0) ? 0 : 1);
}
/* This function returns the (approximate) command assertion period, in
* nanoseconds, for a given CPU clock frequency and MECR BS value:
*/
static inline unsigned int sa1100_pcmcia_cmd_time(unsigned int cpu_clock_khz,
unsigned int pcmcia_mecr_bs){
return (((10000000 * 2) / cpu_clock_khz) * (3 * (pcmcia_mecr_bs + 1))) / 10;
}
int sa11xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt);
void sa11xx_drv_pcmcia_ops(struct pcmcia_low_level *ops);
extern int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr);
#endif /* !defined(_PCMCIA_SA1100_H) */

817
drivers/pcmcia/soc_common.c Normal file
View file

@ -0,0 +1,817 @@
/*======================================================================
Common support code for the PCMCIA control functionality of
integrated SOCs like the SA-11x0 and PXA2xx microprocessors.
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.
The initial developer of the original code is John G. Dorsey
<john+@cs.cmu.edu>. Portions created by John G. Dorsey are
Copyright (C) 1999 John G. Dorsey. All Rights Reserved.
Alternatively, the contents of this file may be used under the
terms of the GNU Public License version 2 (the "GPL"), in which
case the provisions of the GPL are applicable instead of the
above. If you wish to allow the use of your version of this file
only under the terms of the GPL and not to allow others to use
your version of this file under the MPL, indicate your decision
by deleting the provisions above and replace them with the notice
and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this
file under either the MPL or the GPL.
======================================================================*/
#include <linux/cpufreq.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <mach/hardware.h>
#include "soc_common.h"
static irqreturn_t soc_common_pcmcia_interrupt(int irq, void *dev);
#ifdef CONFIG_PCMCIA_DEBUG
static int pc_debug;
module_param(pc_debug, int, 0644);
void soc_pcmcia_debug(struct soc_pcmcia_socket *skt, const char *func,
int lvl, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
if (pc_debug > lvl) {
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
printk(KERN_DEBUG "skt%u: %s: %pV", skt->nr, func, &vaf);
va_end(args);
}
}
EXPORT_SYMBOL(soc_pcmcia_debug);
#endif
#define to_soc_pcmcia_socket(x) \
container_of(x, struct soc_pcmcia_socket, socket)
static unsigned short
calc_speed(unsigned short *spds, int num, unsigned short dflt)
{
unsigned short speed = 0;
int i;
for (i = 0; i < num; i++)
if (speed < spds[i])
speed = spds[i];
if (speed == 0)
speed = dflt;
return speed;
}
void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *skt,
struct soc_pcmcia_timing *timing)
{
timing->io =
calc_speed(skt->spd_io, MAX_IO_WIN, SOC_PCMCIA_IO_ACCESS);
timing->mem =
calc_speed(skt->spd_mem, MAX_WIN, SOC_PCMCIA_3V_MEM_ACCESS);
timing->attr =
calc_speed(skt->spd_attr, MAX_WIN, SOC_PCMCIA_3V_MEM_ACCESS);
}
EXPORT_SYMBOL(soc_common_pcmcia_get_timing);
static void __soc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt,
unsigned int nr)
{
unsigned int i;
for (i = 0; i < nr; i++) {
if (skt->stat[i].irq)
free_irq(skt->stat[i].irq, skt);
if (gpio_is_valid(skt->stat[i].gpio))
gpio_free(skt->stat[i].gpio);
}
if (skt->ops->hw_shutdown)
skt->ops->hw_shutdown(skt);
}
static void soc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
__soc_pcmcia_hw_shutdown(skt, ARRAY_SIZE(skt->stat));
}
static int soc_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int ret = 0, i;
if (skt->ops->hw_init) {
ret = skt->ops->hw_init(skt);
if (ret)
return ret;
}
for (i = 0; i < ARRAY_SIZE(skt->stat); i++) {
if (gpio_is_valid(skt->stat[i].gpio)) {
int irq;
ret = gpio_request_one(skt->stat[i].gpio, GPIOF_IN,
skt->stat[i].name);
if (ret) {
__soc_pcmcia_hw_shutdown(skt, i);
return ret;
}
irq = gpio_to_irq(skt->stat[i].gpio);
if (i == SOC_STAT_RDY)
skt->socket.pci_irq = irq;
else
skt->stat[i].irq = irq;
}
if (skt->stat[i].irq) {
ret = request_irq(skt->stat[i].irq,
soc_common_pcmcia_interrupt,
IRQF_TRIGGER_NONE,
skt->stat[i].name, skt);
if (ret) {
if (gpio_is_valid(skt->stat[i].gpio))
gpio_free(skt->stat[i].gpio);
__soc_pcmcia_hw_shutdown(skt, i);
return ret;
}
}
}
return ret;
}
static void soc_pcmcia_hw_enable(struct soc_pcmcia_socket *skt)
{
int i;
for (i = 0; i < ARRAY_SIZE(skt->stat); i++)
if (skt->stat[i].irq) {
irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_EDGE_RISING);
irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_EDGE_BOTH);
}
}
static void soc_pcmcia_hw_disable(struct soc_pcmcia_socket *skt)
{
int i;
for (i = 0; i < ARRAY_SIZE(skt->stat); i++)
if (skt->stat[i].irq)
irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_NONE);
}
static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt)
{
struct pcmcia_state state;
unsigned int stat;
memset(&state, 0, sizeof(struct pcmcia_state));
/* Make battery voltage state report 'good' */
state.bvd1 = 1;
state.bvd2 = 1;
/* CD is active low by default */
if (gpio_is_valid(skt->stat[SOC_STAT_CD].gpio))
state.detect = !gpio_get_value(skt->stat[SOC_STAT_CD].gpio);
/* RDY and BVD are active high by default */
if (gpio_is_valid(skt->stat[SOC_STAT_RDY].gpio))
state.ready = !!gpio_get_value(skt->stat[SOC_STAT_RDY].gpio);
if (gpio_is_valid(skt->stat[SOC_STAT_BVD1].gpio))
state.bvd1 = !!gpio_get_value(skt->stat[SOC_STAT_BVD1].gpio);
if (gpio_is_valid(skt->stat[SOC_STAT_BVD2].gpio))
state.bvd2 = !!gpio_get_value(skt->stat[SOC_STAT_BVD2].gpio);
skt->ops->socket_state(skt, &state);
stat = state.detect ? SS_DETECT : 0;
stat |= state.ready ? SS_READY : 0;
stat |= state.wrprot ? SS_WRPROT : 0;
stat |= state.vs_3v ? SS_3VCARD : 0;
stat |= state.vs_Xv ? SS_XVCARD : 0;
/* The power status of individual sockets is not available
* explicitly from the hardware, so we just remember the state
* and regurgitate it upon request:
*/
stat |= skt->cs_state.Vcc ? SS_POWERON : 0;
if (skt->cs_state.flags & SS_IOCARD)
stat |= state.bvd1 ? SS_STSCHG : 0;
else {
if (state.bvd1 == 0)
stat |= SS_BATDEAD;
else if (state.bvd2 == 0)
stat |= SS_BATWARN;
}
return stat;
}
/*
* soc_common_pcmcia_config_skt
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*
* Convert PCMCIA socket state to our socket configure structure.
*/
static int soc_common_pcmcia_config_skt(
struct soc_pcmcia_socket *skt, socket_state_t *state)
{
int ret;
ret = skt->ops->configure_socket(skt, state);
if (ret == 0) {
/*
* This really needs a better solution. The IRQ
* may or may not be claimed by the driver.
*/
if (skt->irq_state != 1 && state->io_irq) {
skt->irq_state = 1;
irq_set_irq_type(skt->socket.pci_irq,
IRQ_TYPE_EDGE_FALLING);
} else if (skt->irq_state == 1 && state->io_irq == 0) {
skt->irq_state = 0;
irq_set_irq_type(skt->socket.pci_irq, IRQ_TYPE_NONE);
}
skt->cs_state = *state;
}
if (ret < 0)
printk(KERN_ERR "soc_common_pcmcia: unable to configure "
"socket %d\n", skt->nr);
return ret;
}
/* soc_common_pcmcia_sock_init()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*
* (Re-)Initialise the socket, turning on status interrupts
* and PCMCIA bus. This must wait for power to stabilise
* so that the card status signals report correctly.
*
* Returns: 0
*/
static int soc_common_pcmcia_sock_init(struct pcmcia_socket *sock)
{
struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock);
debug(skt, 2, "initializing socket\n");
if (skt->ops->socket_init)
skt->ops->socket_init(skt);
soc_pcmcia_hw_enable(skt);
return 0;
}
/*
* soc_common_pcmcia_suspend()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
*
* Remove power on the socket, disable IRQs from the card.
* Turn off status interrupts, and disable the PCMCIA bus.
*
* Returns: 0
*/
static int soc_common_pcmcia_suspend(struct pcmcia_socket *sock)
{
struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock);
debug(skt, 2, "suspending socket\n");
soc_pcmcia_hw_disable(skt);
if (skt->ops->socket_suspend)
skt->ops->socket_suspend(skt);
return 0;
}
static DEFINE_SPINLOCK(status_lock);
static void soc_common_check_status(struct soc_pcmcia_socket *skt)
{
unsigned int events;
debug(skt, 4, "entering PCMCIA monitoring thread\n");
do {
unsigned int status;
unsigned long flags;
status = soc_common_pcmcia_skt_state(skt);
spin_lock_irqsave(&status_lock, flags);
events = (status ^ skt->status) & skt->cs_state.csc_mask;
skt->status = status;
spin_unlock_irqrestore(&status_lock, flags);
debug(skt, 4, "events: %s%s%s%s%s%s\n",
events == 0 ? "<NONE>" : "",
events & SS_DETECT ? "DETECT " : "",
events & SS_READY ? "READY " : "",
events & SS_BATDEAD ? "BATDEAD " : "",
events & SS_BATWARN ? "BATWARN " : "",
events & SS_STSCHG ? "STSCHG " : "");
if (events)
pcmcia_parse_events(&skt->socket, events);
} while (events);
}
/* Let's poll for events in addition to IRQs since IRQ only is unreliable... */
static void soc_common_pcmcia_poll_event(unsigned long dummy)
{
struct soc_pcmcia_socket *skt = (struct soc_pcmcia_socket *)dummy;
debug(skt, 4, "polling for events\n");
mod_timer(&skt->poll_timer, jiffies + SOC_PCMCIA_POLL_PERIOD);
soc_common_check_status(skt);
}
/*
* Service routine for socket driver interrupts (requested by the
* low-level PCMCIA init() operation via soc_common_pcmcia_thread()).
* The actual interrupt-servicing work is performed by
* soc_common_pcmcia_thread(), largely because the Card Services event-
* handling code performs scheduling operations which cannot be
* executed from within an interrupt context.
*/
static irqreturn_t soc_common_pcmcia_interrupt(int irq, void *dev)
{
struct soc_pcmcia_socket *skt = dev;
debug(skt, 3, "servicing IRQ %d\n", irq);
soc_common_check_status(skt);
return IRQ_HANDLED;
}
/*
* Implements the get_status() operation for the in-kernel PCMCIA
* service (formerly SS_GetStatus in Card Services). Essentially just
* fills in bits in `status' according to internal driver state or
* the value of the voltage detect chipselect register.
*
* As a debugging note, during card startup, the PCMCIA core issues
* three set_socket() commands in a row the first with RESET deasserted,
* the second with RESET asserted, and the last with RESET deasserted
* again. Following the third set_socket(), a get_status() command will
* be issued. The kernel is looking for the SS_READY flag (see
* setup_socket(), reset_socket(), and unreset_socket() in cs.c).
*
* Returns: 0
*/
static int
soc_common_pcmcia_get_status(struct pcmcia_socket *sock, unsigned int *status)
{
struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock);
skt->status = soc_common_pcmcia_skt_state(skt);
*status = skt->status;
return 0;
}
/*
* Implements the set_socket() operation for the in-kernel PCMCIA
* service (formerly SS_SetSocket in Card Services). We more or
* less punt all of this work and let the kernel handle the details
* of power configuration, reset, &c. We also record the value of
* `state' in order to regurgitate it to the PCMCIA core later.
*/
static int soc_common_pcmcia_set_socket(
struct pcmcia_socket *sock, socket_state_t *state)
{
struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock);
debug(skt, 2, "mask: %s%s%s%s%s%s flags: %s%s%s%s%s%s Vcc %d Vpp %d irq %d\n",
(state->csc_mask == 0) ? "<NONE> " : "",
(state->csc_mask & SS_DETECT) ? "DETECT " : "",
(state->csc_mask & SS_READY) ? "READY " : "",
(state->csc_mask & SS_BATDEAD) ? "BATDEAD " : "",
(state->csc_mask & SS_BATWARN) ? "BATWARN " : "",
(state->csc_mask & SS_STSCHG) ? "STSCHG " : "",
(state->flags == 0) ? "<NONE> " : "",
(state->flags & SS_PWR_AUTO) ? "PWR_AUTO " : "",
(state->flags & SS_IOCARD) ? "IOCARD " : "",
(state->flags & SS_RESET) ? "RESET " : "",
(state->flags & SS_SPKR_ENA) ? "SPKR_ENA " : "",
(state->flags & SS_OUTPUT_ENA) ? "OUTPUT_ENA " : "",
state->Vcc, state->Vpp, state->io_irq);
return soc_common_pcmcia_config_skt(skt, state);
}
/*
* Implements the set_io_map() operation for the in-kernel PCMCIA
* service (formerly SS_SetIOMap in Card Services). We configure
* the map speed as requested, but override the address ranges
* supplied by Card Services.
*
* Returns: 0 on success, -1 on error
*/
static int soc_common_pcmcia_set_io_map(
struct pcmcia_socket *sock, struct pccard_io_map *map)
{
struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock);
unsigned short speed = map->speed;
debug(skt, 2, "map %u speed %u start 0x%08llx stop 0x%08llx\n",
map->map, map->speed, (unsigned long long)map->start,
(unsigned long long)map->stop);
debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n",
(map->flags == 0) ? "<NONE>" : "",
(map->flags & MAP_ACTIVE) ? "ACTIVE " : "",
(map->flags & MAP_16BIT) ? "16BIT " : "",
(map->flags & MAP_AUTOSZ) ? "AUTOSZ " : "",
(map->flags & MAP_0WS) ? "0WS " : "",
(map->flags & MAP_WRPROT) ? "WRPROT " : "",
(map->flags & MAP_USE_WAIT) ? "USE_WAIT " : "",
(map->flags & MAP_PREFETCH) ? "PREFETCH " : "");
if (map->map >= MAX_IO_WIN) {
printk(KERN_ERR "%s(): map (%d) out of range\n", __func__,
map->map);
return -1;
}
if (map->flags & MAP_ACTIVE) {
if (speed == 0)
speed = SOC_PCMCIA_IO_ACCESS;
} else {
speed = 0;
}
skt->spd_io[map->map] = speed;
skt->ops->set_timing(skt);
if (map->stop == 1)
map->stop = PAGE_SIZE-1;
map->stop -= map->start;
map->stop += skt->socket.io_offset;
map->start = skt->socket.io_offset;
return 0;
}
/*
* Implements the set_mem_map() operation for the in-kernel PCMCIA
* service (formerly SS_SetMemMap in Card Services). We configure
* the map speed as requested, but override the address ranges
* supplied by Card Services.
*
* Returns: 0 on success, -ERRNO on error
*/
static int soc_common_pcmcia_set_mem_map(
struct pcmcia_socket *sock, struct pccard_mem_map *map)
{
struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock);
struct resource *res;
unsigned short speed = map->speed;
debug(skt, 2, "map %u speed %u card_start %08x\n",
map->map, map->speed, map->card_start);
debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n",
(map->flags == 0) ? "<NONE>" : "",
(map->flags & MAP_ACTIVE) ? "ACTIVE " : "",
(map->flags & MAP_16BIT) ? "16BIT " : "",
(map->flags & MAP_AUTOSZ) ? "AUTOSZ " : "",
(map->flags & MAP_0WS) ? "0WS " : "",
(map->flags & MAP_WRPROT) ? "WRPROT " : "",
(map->flags & MAP_ATTRIB) ? "ATTRIB " : "",
(map->flags & MAP_USE_WAIT) ? "USE_WAIT " : "");
if (map->map >= MAX_WIN)
return -EINVAL;
if (map->flags & MAP_ACTIVE) {
if (speed == 0)
speed = 300;
} else {
speed = 0;
}
if (map->flags & MAP_ATTRIB) {
res = &skt->res_attr;
skt->spd_attr[map->map] = speed;
skt->spd_mem[map->map] = 0;
} else {
res = &skt->res_mem;
skt->spd_attr[map->map] = 0;
skt->spd_mem[map->map] = speed;
}
skt->ops->set_timing(skt);
map->static_start = res->start + map->card_start;
return 0;
}
struct bittbl {
unsigned int mask;
const char *name;
};
static struct bittbl status_bits[] = {
{ SS_WRPROT, "SS_WRPROT" },
{ SS_BATDEAD, "SS_BATDEAD" },
{ SS_BATWARN, "SS_BATWARN" },
{ SS_READY, "SS_READY" },
{ SS_DETECT, "SS_DETECT" },
{ SS_POWERON, "SS_POWERON" },
{ SS_STSCHG, "SS_STSCHG" },
{ SS_3VCARD, "SS_3VCARD" },
{ SS_XVCARD, "SS_XVCARD" },
};
static struct bittbl conf_bits[] = {
{ SS_PWR_AUTO, "SS_PWR_AUTO" },
{ SS_IOCARD, "SS_IOCARD" },
{ SS_RESET, "SS_RESET" },
{ SS_DMA_MODE, "SS_DMA_MODE" },
{ SS_SPKR_ENA, "SS_SPKR_ENA" },
{ SS_OUTPUT_ENA, "SS_OUTPUT_ENA" },
};
static void dump_bits(char **p, const char *prefix,
unsigned int val, struct bittbl *bits, int sz)
{
char *b = *p;
int i;
b += sprintf(b, "%-9s:", prefix);
for (i = 0; i < sz; i++)
if (val & bits[i].mask)
b += sprintf(b, " %s", bits[i].name);
*b++ = '\n';
*p = b;
}
/*
* Implements the /sys/class/pcmcia_socket/??/status file.
*
* Returns: the number of characters added to the buffer
*/
static ssize_t show_status(
struct device *dev, struct device_attribute *attr, char *buf)
{
struct soc_pcmcia_socket *skt =
container_of(dev, struct soc_pcmcia_socket, socket.dev);
char *p = buf;
p += sprintf(p, "slot : %d\n", skt->nr);
dump_bits(&p, "status", skt->status,
status_bits, ARRAY_SIZE(status_bits));
dump_bits(&p, "csc_mask", skt->cs_state.csc_mask,
status_bits, ARRAY_SIZE(status_bits));
dump_bits(&p, "cs_flags", skt->cs_state.flags,
conf_bits, ARRAY_SIZE(conf_bits));
p += sprintf(p, "Vcc : %d\n", skt->cs_state.Vcc);
p += sprintf(p, "Vpp : %d\n", skt->cs_state.Vpp);
p += sprintf(p, "IRQ : %d (%d)\n", skt->cs_state.io_irq,
skt->socket.pci_irq);
if (skt->ops->show_timing)
p += skt->ops->show_timing(skt, p);
return p-buf;
}
static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
static struct pccard_operations soc_common_pcmcia_operations = {
.init = soc_common_pcmcia_sock_init,
.suspend = soc_common_pcmcia_suspend,
.get_status = soc_common_pcmcia_get_status,
.set_socket = soc_common_pcmcia_set_socket,
.set_io_map = soc_common_pcmcia_set_io_map,
.set_mem_map = soc_common_pcmcia_set_mem_map,
};
static LIST_HEAD(soc_pcmcia_sockets);
static DEFINE_MUTEX(soc_pcmcia_sockets_lock);
#ifdef CONFIG_CPU_FREQ
static int
soc_pcmcia_notifier(struct notifier_block *nb, unsigned long val, void *data)
{
struct soc_pcmcia_socket *skt;
struct cpufreq_freqs *freqs = data;
int ret = 0;
mutex_lock(&soc_pcmcia_sockets_lock);
list_for_each_entry(skt, &soc_pcmcia_sockets, node)
if (skt->ops->frequency_change)
ret += skt->ops->frequency_change(skt, val, freqs);
mutex_unlock(&soc_pcmcia_sockets_lock);
return ret;
}
static struct notifier_block soc_pcmcia_notifier_block = {
.notifier_call = soc_pcmcia_notifier
};
static int soc_pcmcia_cpufreq_register(void)
{
int ret;
ret = cpufreq_register_notifier(&soc_pcmcia_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
if (ret < 0)
printk(KERN_ERR "Unable to register CPU frequency change "
"notifier for PCMCIA (%d)\n", ret);
return ret;
}
fs_initcall(soc_pcmcia_cpufreq_register);
static void soc_pcmcia_cpufreq_unregister(void)
{
cpufreq_unregister_notifier(&soc_pcmcia_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
}
module_exit(soc_pcmcia_cpufreq_unregister);
#endif
void soc_pcmcia_init_one(struct soc_pcmcia_socket *skt,
struct pcmcia_low_level *ops, struct device *dev)
{
int i;
skt->ops = ops;
skt->socket.owner = ops->owner;
skt->socket.dev.parent = dev;
skt->socket.pci_irq = NO_IRQ;
for (i = 0; i < ARRAY_SIZE(skt->stat); i++)
skt->stat[i].gpio = -EINVAL;
}
EXPORT_SYMBOL(soc_pcmcia_init_one);
void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt)
{
mutex_lock(&soc_pcmcia_sockets_lock);
del_timer_sync(&skt->poll_timer);
pcmcia_unregister_socket(&skt->socket);
soc_pcmcia_hw_shutdown(skt);
/* should not be required; violates some lowlevel drivers */
soc_common_pcmcia_config_skt(skt, &dead_socket);
list_del(&skt->node);
mutex_unlock(&soc_pcmcia_sockets_lock);
iounmap(skt->virt_io);
skt->virt_io = NULL;
release_resource(&skt->res_attr);
release_resource(&skt->res_mem);
release_resource(&skt->res_io);
release_resource(&skt->res_skt);
}
EXPORT_SYMBOL(soc_pcmcia_remove_one);
int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt)
{
int ret;
init_timer(&skt->poll_timer);
skt->poll_timer.function = soc_common_pcmcia_poll_event;
skt->poll_timer.data = (unsigned long)skt;
skt->poll_timer.expires = jiffies + SOC_PCMCIA_POLL_PERIOD;
ret = request_resource(&iomem_resource, &skt->res_skt);
if (ret)
goto out_err_1;
ret = request_resource(&skt->res_skt, &skt->res_io);
if (ret)
goto out_err_2;
ret = request_resource(&skt->res_skt, &skt->res_mem);
if (ret)
goto out_err_3;
ret = request_resource(&skt->res_skt, &skt->res_attr);
if (ret)
goto out_err_4;
skt->virt_io = ioremap(skt->res_io.start, 0x10000);
if (skt->virt_io == NULL) {
ret = -ENOMEM;
goto out_err_5;
}
mutex_lock(&soc_pcmcia_sockets_lock);
list_add(&skt->node, &soc_pcmcia_sockets);
/*
* We initialize default socket timing here, because
* we are not guaranteed to see a SetIOMap operation at
* runtime.
*/
skt->ops->set_timing(skt);
ret = soc_pcmcia_hw_init(skt);
if (ret)
goto out_err_6;
skt->socket.ops = &soc_common_pcmcia_operations;
skt->socket.features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD;
skt->socket.resource_ops = &pccard_static_ops;
skt->socket.irq_mask = 0;
skt->socket.map_size = PAGE_SIZE;
skt->socket.io_offset = (unsigned long)skt->virt_io;
skt->status = soc_common_pcmcia_skt_state(skt);
ret = pcmcia_register_socket(&skt->socket);
if (ret)
goto out_err_7;
add_timer(&skt->poll_timer);
mutex_unlock(&soc_pcmcia_sockets_lock);
ret = device_create_file(&skt->socket.dev, &dev_attr_status);
if (ret)
goto out_err_8;
return ret;
out_err_8:
mutex_lock(&soc_pcmcia_sockets_lock);
del_timer_sync(&skt->poll_timer);
pcmcia_unregister_socket(&skt->socket);
out_err_7:
soc_pcmcia_hw_shutdown(skt);
out_err_6:
list_del(&skt->node);
mutex_unlock(&soc_pcmcia_sockets_lock);
iounmap(skt->virt_io);
out_err_5:
release_resource(&skt->res_attr);
out_err_4:
release_resource(&skt->res_mem);
out_err_3:
release_resource(&skt->res_io);
out_err_2:
release_resource(&skt->res_skt);
out_err_1:
return ret;
}
EXPORT_SYMBOL(soc_pcmcia_add_one);
MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
MODULE_DESCRIPTION("Linux PCMCIA Card Services: Common SoC support");
MODULE_LICENSE("Dual MPL/GPL");

193
drivers/pcmcia/soc_common.h Normal file
View file

@ -0,0 +1,193 @@
/*
* linux/drivers/pcmcia/soc_common.h
*
* Copyright (C) 2000 John G Dorsey <john+@cs.cmu.edu>
*
* This file contains definitions for the PCMCIA support code common to
* integrated SOCs like the SA-11x0 and PXA2xx microprocessors.
*/
#ifndef _ASM_ARCH_PCMCIA
#define _ASM_ARCH_PCMCIA
/* include the world */
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <pcmcia/ss.h>
#include <pcmcia/cistpl.h>
struct device;
struct pcmcia_low_level;
/*
* This structure encapsulates per-socket state which we might need to
* use when responding to a Card Services query of some kind.
*/
struct soc_pcmcia_socket {
struct pcmcia_socket socket;
/*
* Info from low level handler
*/
unsigned int nr;
struct clk *clk;
/*
* Core PCMCIA state
*/
const struct pcmcia_low_level *ops;
unsigned int status;
socket_state_t cs_state;
unsigned short spd_io[MAX_IO_WIN];
unsigned short spd_mem[MAX_WIN];
unsigned short spd_attr[MAX_WIN];
struct resource res_skt;
struct resource res_io;
struct resource res_mem;
struct resource res_attr;
void __iomem *virt_io;
struct {
int gpio;
unsigned int irq;
const char *name;
} stat[4];
#define SOC_STAT_CD 0 /* Card detect */
#define SOC_STAT_BVD1 1 /* BATDEAD / IOSTSCHG */
#define SOC_STAT_BVD2 2 /* BATWARN / IOSPKR */
#define SOC_STAT_RDY 3 /* Ready / Interrupt */
unsigned int irq_state;
struct timer_list poll_timer;
struct list_head node;
};
struct skt_dev_info {
int nskt;
struct clk *clk;
struct soc_pcmcia_socket skt[0];
};
struct pcmcia_state {
unsigned detect: 1,
ready: 1,
bvd1: 1,
bvd2: 1,
wrprot: 1,
vs_3v: 1,
vs_Xv: 1;
};
struct pcmcia_low_level {
struct module *owner;
/* first socket in system */
int first;
/* nr of sockets */
int nr;
int (*hw_init)(struct soc_pcmcia_socket *);
void (*hw_shutdown)(struct soc_pcmcia_socket *);
void (*socket_state)(struct soc_pcmcia_socket *, struct pcmcia_state *);
int (*configure_socket)(struct soc_pcmcia_socket *, const socket_state_t *);
/*
* Enable card status IRQs on (re-)initialisation. This can
* be called at initialisation, power management event, or
* pcmcia event.
*/
void (*socket_init)(struct soc_pcmcia_socket *);
/*
* Disable card status IRQs and PCMCIA bus on suspend.
*/
void (*socket_suspend)(struct soc_pcmcia_socket *);
/*
* Hardware specific timing routines.
* If provided, the get_timing routine overrides the SOC default.
*/
unsigned int (*get_timing)(struct soc_pcmcia_socket *, unsigned int, unsigned int);
int (*set_timing)(struct soc_pcmcia_socket *);
int (*show_timing)(struct soc_pcmcia_socket *, char *);
#ifdef CONFIG_CPU_FREQ
/*
* CPUFREQ support.
*/
int (*frequency_change)(struct soc_pcmcia_socket *, unsigned long, struct cpufreq_freqs *);
#endif
};
struct soc_pcmcia_timing {
unsigned short io;
unsigned short mem;
unsigned short attr;
};
extern void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *, struct soc_pcmcia_timing *);
void soc_pcmcia_init_one(struct soc_pcmcia_socket *skt,
struct pcmcia_low_level *ops, struct device *dev);
void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt);
int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt);
#ifdef CONFIG_PCMCIA_DEBUG
extern void soc_pcmcia_debug(struct soc_pcmcia_socket *skt, const char *func,
int lvl, const char *fmt, ...);
#define debug(skt, lvl, fmt, arg...) \
soc_pcmcia_debug(skt, __func__, lvl, fmt , ## arg)
#else
#define debug(skt, lvl, fmt, arg...) do { } while (0)
#endif
/*
* The PC Card Standard, Release 7, section 4.13.4, says that twIORD
* has a minimum value of 165ns. Section 4.13.5 says that twIOWR has
* a minimum value of 165ns, as well. Section 4.7.2 (describing
* common and attribute memory write timing) says that twWE has a
* minimum value of 150ns for a 250ns cycle time (for 5V operation;
* see section 4.7.4), or 300ns for a 600ns cycle time (for 3.3V
* operation, also section 4.7.4). Section 4.7.3 says that taOE
* has a maximum value of 150ns for a 300ns cycle time (for 5V
* operation), or 300ns for a 600ns cycle time (for 3.3V operation).
*
* When configuring memory maps, Card Services appears to adopt the policy
* that a memory access time of "0" means "use the default." The default
* PCMCIA I/O command width time is 165ns. The default PCMCIA 5V attribute
* and memory command width time is 150ns; the PCMCIA 3.3V attribute and
* memory command width time is 300ns.
*/
#define SOC_PCMCIA_IO_ACCESS (165)
#define SOC_PCMCIA_5V_MEM_ACCESS (150)
#define SOC_PCMCIA_3V_MEM_ACCESS (300)
#define SOC_PCMCIA_ATTR_MEM_ACCESS (300)
/*
* The socket driver actually works nicely in interrupt-driven form,
* so the (relatively infrequent) polling is "just to be sure."
*/
#define SOC_PCMCIA_POLL_PERIOD (2*HZ)
/* I/O pins replacing memory pins
* (PCMCIA System Architecture, 2nd ed., by Don Anderson, p.75)
*
* These signals change meaning when going from memory-only to
* memory-or-I/O interface:
*/
#define iostschg bvd1
#define iospkr bvd2
#endif

View file

@ -0,0 +1,231 @@
/*
* socket_sysfs.c -- most of socket-related sysfs output
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* (C) 2003 - 2004 Dominik Brodowski
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <asm/irq.h>
#include <pcmcia/ss.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include "cs_internal.h"
#define to_socket(_dev) container_of(_dev, struct pcmcia_socket, dev)
static ssize_t pccard_show_type(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct pcmcia_socket *s = to_socket(dev);
if (!(s->state & SOCKET_PRESENT))
return -ENODEV;
if (s->state & SOCKET_CARDBUS)
return sprintf(buf, "32-bit\n");
return sprintf(buf, "16-bit\n");
}
static DEVICE_ATTR(card_type, 0444, pccard_show_type, NULL);
static ssize_t pccard_show_voltage(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct pcmcia_socket *s = to_socket(dev);
if (!(s->state & SOCKET_PRESENT))
return -ENODEV;
if (s->socket.Vcc)
return sprintf(buf, "%d.%dV\n", s->socket.Vcc / 10,
s->socket.Vcc % 10);
return sprintf(buf, "X.XV\n");
}
static DEVICE_ATTR(card_voltage, 0444, pccard_show_voltage, NULL);
static ssize_t pccard_show_vpp(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct pcmcia_socket *s = to_socket(dev);
if (!(s->state & SOCKET_PRESENT))
return -ENODEV;
return sprintf(buf, "%d.%dV\n", s->socket.Vpp / 10, s->socket.Vpp % 10);
}
static DEVICE_ATTR(card_vpp, 0444, pccard_show_vpp, NULL);
static ssize_t pccard_show_vcc(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct pcmcia_socket *s = to_socket(dev);
if (!(s->state & SOCKET_PRESENT))
return -ENODEV;
return sprintf(buf, "%d.%dV\n", s->socket.Vcc / 10, s->socket.Vcc % 10);
}
static DEVICE_ATTR(card_vcc, 0444, pccard_show_vcc, NULL);
static ssize_t pccard_store_insert(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct pcmcia_socket *s = to_socket(dev);
if (!count)
return -EINVAL;
pcmcia_parse_uevents(s, PCMCIA_UEVENT_INSERT);
return count;
}
static DEVICE_ATTR(card_insert, 0200, NULL, pccard_store_insert);
static ssize_t pccard_show_card_pm_state(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct pcmcia_socket *s = to_socket(dev);
return sprintf(buf, "%s\n", s->state & SOCKET_SUSPEND ? "off" : "on");
}
static ssize_t pccard_store_card_pm_state(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct pcmcia_socket *s = to_socket(dev);
ssize_t ret = count;
if (!count)
return -EINVAL;
if (!strncmp(buf, "off", 3))
pcmcia_parse_uevents(s, PCMCIA_UEVENT_SUSPEND);
else {
if (!strncmp(buf, "on", 2))
pcmcia_parse_uevents(s, PCMCIA_UEVENT_RESUME);
else
ret = -EINVAL;
}
return ret;
}
static DEVICE_ATTR(card_pm_state, 0644, pccard_show_card_pm_state, pccard_store_card_pm_state);
static ssize_t pccard_store_eject(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct pcmcia_socket *s = to_socket(dev);
if (!count)
return -EINVAL;
pcmcia_parse_uevents(s, PCMCIA_UEVENT_EJECT);
return count;
}
static DEVICE_ATTR(card_eject, 0200, NULL, pccard_store_eject);
static ssize_t pccard_show_irq_mask(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct pcmcia_socket *s = to_socket(dev);
return sprintf(buf, "0x%04x\n", s->irq_mask);
}
static ssize_t pccard_store_irq_mask(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
ssize_t ret;
struct pcmcia_socket *s = to_socket(dev);
u32 mask;
if (!count)
return -EINVAL;
ret = sscanf(buf, "0x%x\n", &mask);
if (ret == 1) {
mutex_lock(&s->ops_mutex);
s->irq_mask &= mask;
mutex_unlock(&s->ops_mutex);
ret = 0;
}
return ret ? ret : count;
}
static DEVICE_ATTR(card_irq_mask, 0600, pccard_show_irq_mask, pccard_store_irq_mask);
static ssize_t pccard_show_resource(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pcmcia_socket *s = to_socket(dev);
return sprintf(buf, "%s\n", s->resource_setup_done ? "yes" : "no");
}
static ssize_t pccard_store_resource(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct pcmcia_socket *s = to_socket(dev);
if (!count)
return -EINVAL;
mutex_lock(&s->ops_mutex);
if (!s->resource_setup_done)
s->resource_setup_done = 1;
mutex_unlock(&s->ops_mutex);
pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
return count;
}
static DEVICE_ATTR(available_resources_setup_done, 0600, pccard_show_resource, pccard_store_resource);
static struct attribute *pccard_socket_attributes[] = {
&dev_attr_card_type.attr,
&dev_attr_card_voltage.attr,
&dev_attr_card_vpp.attr,
&dev_attr_card_vcc.attr,
&dev_attr_card_insert.attr,
&dev_attr_card_pm_state.attr,
&dev_attr_card_eject.attr,
&dev_attr_card_irq_mask.attr,
&dev_attr_available_resources_setup_done.attr,
NULL,
};
static const struct attribute_group socket_attrs = {
.attrs = pccard_socket_attributes,
};
int pccard_sysfs_add_socket(struct device *dev)
{
return sysfs_create_group(&dev->kobj, &socket_attrs);
}
void pccard_sysfs_remove_socket(struct device *dev)
{
sysfs_remove_group(&dev->kobj, &socket_attrs);
}

808
drivers/pcmcia/tcic.c Normal file
View file

@ -0,0 +1,808 @@
/*======================================================================
Device driver for Databook TCIC-2 PCMCIA controller
tcic.c 1.111 2000/02/15 04:13:12
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.
The initial developer of the original code is David A. Hinds
<dahinds@users.sourceforge.net>. Portions created by David A. Hinds
are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
Alternatively, the contents of this file may be used under the
terms of the GNU General Public License version 2 (the "GPL"), in which
case the provisions of the GPL are applicable instead of the
above. If you wish to allow the use of your version of this file
only under the terms of the GPL and not to allow others to use
your version of this file under the MPL, indicate your decision
by deleting the provisions above and replace them with the notice
and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this
file under either the MPL or the GPL.
======================================================================*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
#include <asm/io.h>
#include <pcmcia/ss.h>
#include "tcic.h"
MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
MODULE_DESCRIPTION("Databook TCIC-2 PCMCIA socket driver");
MODULE_LICENSE("Dual MPL/GPL");
/*====================================================================*/
/* Parameters that can be set with 'insmod' */
/* The base port address of the TCIC-2 chip */
static unsigned long tcic_base = TCIC_BASE;
/* Specify a socket number to ignore */
static int ignore = -1;
/* Probe for safe interrupts? */
static int do_scan = 1;
/* Bit map of interrupts to choose from */
static u_int irq_mask = 0xffff;
static int irq_list[16];
static unsigned int irq_list_count;
/* The card status change interrupt -- 0 means autoselect */
static int cs_irq;
/* Poll status interval -- 0 means default to interrupt */
static int poll_interval;
/* Delay for card status double-checking */
static int poll_quick = HZ/20;
/* CCLK external clock time, in nanoseconds. 70 ns = 14.31818 MHz */
static int cycle_time = 70;
module_param(tcic_base, ulong, 0444);
module_param(ignore, int, 0444);
module_param(do_scan, int, 0444);
module_param(irq_mask, int, 0444);
module_param_array(irq_list, int, &irq_list_count, 0444);
module_param(cs_irq, int, 0444);
module_param(poll_interval, int, 0444);
module_param(poll_quick, int, 0444);
module_param(cycle_time, int, 0444);
/*====================================================================*/
static irqreturn_t tcic_interrupt(int irq, void *dev);
static void tcic_timer(u_long data);
static struct pccard_operations tcic_operations;
struct tcic_socket {
u_short psock;
u_char last_sstat;
u_char id;
struct pcmcia_socket socket;
};
static struct timer_list poll_timer;
static int tcic_timer_pending;
static int sockets;
static struct tcic_socket socket_table[2];
/*====================================================================*/
/* Trick when selecting interrupts: the TCIC sktirq pin is supposed
to map to irq 11, but is coded as 0 or 1 in the irq registers. */
#define TCIC_IRQ(x) ((x) ? (((x) == 11) ? 1 : (x)) : 15)
#ifdef DEBUG_X
static u_char tcic_getb(u_char reg)
{
u_char val = inb(tcic_base+reg);
printk(KERN_DEBUG "tcic_getb(%#lx) = %#x\n", tcic_base+reg, val);
return val;
}
static u_short tcic_getw(u_char reg)
{
u_short val = inw(tcic_base+reg);
printk(KERN_DEBUG "tcic_getw(%#lx) = %#x\n", tcic_base+reg, val);
return val;
}
static void tcic_setb(u_char reg, u_char data)
{
printk(KERN_DEBUG "tcic_setb(%#lx, %#x)\n", tcic_base+reg, data);
outb(data, tcic_base+reg);
}
static void tcic_setw(u_char reg, u_short data)
{
printk(KERN_DEBUG "tcic_setw(%#lx, %#x)\n", tcic_base+reg, data);
outw(data, tcic_base+reg);
}
#else
#define tcic_getb(reg) inb(tcic_base+reg)
#define tcic_getw(reg) inw(tcic_base+reg)
#define tcic_setb(reg, data) outb(data, tcic_base+reg)
#define tcic_setw(reg, data) outw(data, tcic_base+reg)
#endif
static void tcic_setl(u_char reg, u_int data)
{
#ifdef DEBUG_X
printk(KERN_DEBUG "tcic_setl(%#x, %#lx)\n", tcic_base+reg, data);
#endif
outw(data & 0xffff, tcic_base+reg);
outw(data >> 16, tcic_base+reg+2);
}
static void tcic_aux_setb(u_short reg, u_char data)
{
u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
tcic_setb(TCIC_MODE, mode);
tcic_setb(TCIC_AUX, data);
}
static u_short tcic_aux_getw(u_short reg)
{
u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
tcic_setb(TCIC_MODE, mode);
return tcic_getw(TCIC_AUX);
}
static void tcic_aux_setw(u_short reg, u_short data)
{
u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
tcic_setb(TCIC_MODE, mode);
tcic_setw(TCIC_AUX, data);
}
/*====================================================================*/
/* Time conversion functions */
static int to_cycles(int ns)
{
if (ns < 14)
return 0;
else
return 2*(ns-14)/cycle_time;
}
/*====================================================================*/
static volatile u_int irq_hits;
static irqreturn_t __init tcic_irq_count(int irq, void *dev)
{
irq_hits++;
return IRQ_HANDLED;
}
static u_int __init try_irq(int irq)
{
u_short cfg;
irq_hits = 0;
if (request_irq(irq, tcic_irq_count, 0, "irq scan", tcic_irq_count) != 0)
return -1;
mdelay(10);
if (irq_hits) {
free_irq(irq, tcic_irq_count);
return -1;
}
/* Generate one interrupt */
cfg = TCIC_SYSCFG_AUTOBUSY | 0x0a00;
tcic_aux_setw(TCIC_AUX_SYSCFG, cfg | TCIC_IRQ(irq));
tcic_setb(TCIC_IENA, TCIC_IENA_ERR | TCIC_IENA_CFG_HIGH);
tcic_setb(TCIC_ICSR, TCIC_ICSR_ERR | TCIC_ICSR_JAM);
udelay(1000);
free_irq(irq, tcic_irq_count);
/* Turn off interrupts */
tcic_setb(TCIC_IENA, TCIC_IENA_CFG_OFF);
while (tcic_getb(TCIC_ICSR))
tcic_setb(TCIC_ICSR, TCIC_ICSR_JAM);
tcic_aux_setw(TCIC_AUX_SYSCFG, cfg);
return (irq_hits != 1);
}
static u_int __init irq_scan(u_int mask0)
{
u_int mask1;
int i;
#ifdef __alpha__
#define PIC 0x4d0
/* Don't probe level-triggered interrupts -- reserved for PCI */
int level_mask = inb_p(PIC) | (inb_p(PIC+1) << 8);
if (level_mask)
mask0 &= ~level_mask;
#endif
mask1 = 0;
if (do_scan) {
for (i = 0; i < 16; i++)
if ((mask0 & (1 << i)) && (try_irq(i) == 0))
mask1 |= (1 << i);
for (i = 0; i < 16; i++)
if ((mask1 & (1 << i)) && (try_irq(i) != 0)) {
mask1 ^= (1 << i);
}
}
if (mask1) {
printk("scanned");
} else {
/* Fallback: just find interrupts that aren't in use */
for (i = 0; i < 16; i++)
if ((mask0 & (1 << i)) &&
(request_irq(i, tcic_irq_count, 0, "x", tcic_irq_count) == 0)) {
mask1 |= (1 << i);
free_irq(i, tcic_irq_count);
}
printk("default");
}
printk(") = ");
for (i = 0; i < 16; i++)
if (mask1 & (1<<i))
printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i);
printk(" ");
return mask1;
}
/*======================================================================
See if a card is present, powered up, in IO mode, and already
bound to a (non-PCMCIA) Linux driver.
We make an exception for cards that look like serial devices.
======================================================================*/
static int __init is_active(int s)
{
u_short scf1, ioctl, base, num;
u_char pwr, sstat;
u_int addr;
tcic_setl(TCIC_ADDR, (s << TCIC_ADDR_SS_SHFT)
| TCIC_ADDR_INDREG | TCIC_SCF1(s));
scf1 = tcic_getw(TCIC_DATA);
pwr = tcic_getb(TCIC_PWR);
sstat = tcic_getb(TCIC_SSTAT);
addr = TCIC_IWIN(s, 0);
tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X);
base = tcic_getw(TCIC_DATA);
tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X);
ioctl = tcic_getw(TCIC_DATA);
if (ioctl & TCIC_ICTL_TINY)
num = 1;
else {
num = (base ^ (base-1));
base = base & (base-1);
}
if ((sstat & TCIC_SSTAT_CD) && (pwr & TCIC_PWR_VCC(s)) &&
(scf1 & TCIC_SCF1_IOSTS) && (ioctl & TCIC_ICTL_ENA) &&
((base & 0xfeef) != 0x02e8)) {
struct resource *res = request_region(base, num, "tcic-2");
if (!res) /* region is busy */
return 1;
release_region(base, num);
}
return 0;
}
/*======================================================================
This returns the revision code for the specified socket.
======================================================================*/
static int __init get_tcic_id(void)
{
u_short id;
tcic_aux_setw(TCIC_AUX_TEST, TCIC_TEST_DIAG);
id = tcic_aux_getw(TCIC_AUX_ILOCK);
id = (id & TCIC_ILOCKTEST_ID_MASK) >> TCIC_ILOCKTEST_ID_SH;
tcic_aux_setw(TCIC_AUX_TEST, 0);
return id;
}
/*====================================================================*/
static struct platform_driver tcic_driver = {
.driver = {
.name = "tcic-pcmcia",
.owner = THIS_MODULE,
},
};
static struct platform_device tcic_device = {
.name = "tcic-pcmcia",
.id = 0,
};
static int __init init_tcic(void)
{
int i, sock, ret = 0;
u_int mask, scan;
if (platform_driver_register(&tcic_driver))
return -1;
printk(KERN_INFO "Databook TCIC-2 PCMCIA probe: ");
sock = 0;
if (!request_region(tcic_base, 16, "tcic-2")) {
printk("could not allocate ports,\n ");
platform_driver_unregister(&tcic_driver);
return -ENODEV;
}
else {
tcic_setw(TCIC_ADDR, 0);
if (tcic_getw(TCIC_ADDR) == 0) {
tcic_setw(TCIC_ADDR, 0xc3a5);
if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2;
}
if (sock == 0) {
/* See if resetting the controller does any good */
tcic_setb(TCIC_SCTRL, TCIC_SCTRL_RESET);
tcic_setb(TCIC_SCTRL, 0);
tcic_setw(TCIC_ADDR, 0);
if (tcic_getw(TCIC_ADDR) == 0) {
tcic_setw(TCIC_ADDR, 0xc3a5);
if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2;
}
}
}
if (sock == 0) {
printk("not found.\n");
release_region(tcic_base, 16);
platform_driver_unregister(&tcic_driver);
return -ENODEV;
}
sockets = 0;
for (i = 0; i < sock; i++) {
if ((i == ignore) || is_active(i)) continue;
socket_table[sockets].psock = i;
socket_table[sockets].id = get_tcic_id();
socket_table[sockets].socket.owner = THIS_MODULE;
/* only 16-bit cards, memory windows must be size-aligned */
/* No PCI or CardBus support */
socket_table[sockets].socket.features = SS_CAP_PCCARD | SS_CAP_MEM_ALIGN;
/* irq 14, 11, 10, 7, 6, 5, 4, 3 */
socket_table[sockets].socket.irq_mask = 0x4cf8;
/* 4K minimum window size */
socket_table[sockets].socket.map_size = 0x1000;
sockets++;
}
switch (socket_table[0].id) {
case TCIC_ID_DB86082:
printk("DB86082"); break;
case TCIC_ID_DB86082A:
printk("DB86082A"); break;
case TCIC_ID_DB86084:
printk("DB86084"); break;
case TCIC_ID_DB86084A:
printk("DB86084A"); break;
case TCIC_ID_DB86072:
printk("DB86072"); break;
case TCIC_ID_DB86184:
printk("DB86184"); break;
case TCIC_ID_DB86082B:
printk("DB86082B"); break;
default:
printk("Unknown ID 0x%02x", socket_table[0].id);
}
/* Set up polling */
poll_timer.function = &tcic_timer;
poll_timer.data = 0;
init_timer(&poll_timer);
/* Build interrupt mask */
printk(KERN_CONT ", %d sockets\n", sockets);
printk(KERN_INFO " irq list (");
if (irq_list_count == 0)
mask = irq_mask;
else
for (i = mask = 0; i < irq_list_count; i++)
mask |= (1<<irq_list[i]);
/* irq 14, 11, 10, 7, 6, 5, 4, 3 */
mask &= 0x4cf8;
/* Scan interrupts */
mask = irq_scan(mask);
for (i=0;i<sockets;i++)
socket_table[i].socket.irq_mask = mask;
/* Check for only two interrupts available */
scan = (mask & (mask-1));
if (((scan & (scan-1)) == 0) && (poll_interval == 0))
poll_interval = HZ;
if (poll_interval == 0) {
/* Avoid irq 12 unless it is explicitly requested */
u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12));
for (i = 15; i > 0; i--)
if ((cs_mask & (1 << i)) &&
(request_irq(i, tcic_interrupt, 0, "tcic",
tcic_interrupt) == 0))
break;
cs_irq = i;
if (cs_irq == 0) poll_interval = HZ;
}
if (socket_table[0].socket.irq_mask & (1 << 11))
printk("sktirq is irq 11, ");
if (cs_irq != 0)
printk("status change on irq %d\n", cs_irq);
else
printk("polled status, interval = %d ms\n",
poll_interval * 1000 / HZ);
for (i = 0; i < sockets; i++) {
tcic_setw(TCIC_ADDR+2, socket_table[i].psock << TCIC_SS_SHFT);
socket_table[i].last_sstat = tcic_getb(TCIC_SSTAT);
}
/* jump start interrupt handler, if needed */
tcic_interrupt(0, NULL);
platform_device_register(&tcic_device);
for (i = 0; i < sockets; i++) {
socket_table[i].socket.ops = &tcic_operations;
socket_table[i].socket.resource_ops = &pccard_nonstatic_ops;
socket_table[i].socket.dev.parent = &tcic_device.dev;
ret = pcmcia_register_socket(&socket_table[i].socket);
if (ret && i)
pcmcia_unregister_socket(&socket_table[0].socket);
}
return ret;
return 0;
} /* init_tcic */
/*====================================================================*/
static void __exit exit_tcic(void)
{
int i;
del_timer_sync(&poll_timer);
if (cs_irq != 0) {
tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00);
free_irq(cs_irq, tcic_interrupt);
}
release_region(tcic_base, 16);
for (i = 0; i < sockets; i++) {
pcmcia_unregister_socket(&socket_table[i].socket);
}
platform_device_unregister(&tcic_device);
platform_driver_unregister(&tcic_driver);
} /* exit_tcic */
/*====================================================================*/
static irqreturn_t tcic_interrupt(int irq, void *dev)
{
int i, quick = 0;
u_char latch, sstat;
u_short psock;
u_int events;
static volatile int active = 0;
if (active) {
printk(KERN_NOTICE "tcic: reentered interrupt handler!\n");
return IRQ_NONE;
} else
active = 1;
pr_debug("tcic_interrupt()\n");
for (i = 0; i < sockets; i++) {
psock = socket_table[i].psock;
tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
| TCIC_ADDR_INDREG | TCIC_SCF1(psock));
sstat = tcic_getb(TCIC_SSTAT);
latch = sstat ^ socket_table[psock].last_sstat;
socket_table[i].last_sstat = sstat;
if (tcic_getb(TCIC_ICSR) & TCIC_ICSR_CDCHG) {
tcic_setb(TCIC_ICSR, TCIC_ICSR_CLEAR);
quick = 1;
}
if (latch == 0)
continue;
events = (latch & TCIC_SSTAT_CD) ? SS_DETECT : 0;
events |= (latch & TCIC_SSTAT_WP) ? SS_WRPROT : 0;
if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) {
events |= (latch & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0;
} else {
events |= (latch & TCIC_SSTAT_RDY) ? SS_READY : 0;
events |= (latch & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0;
events |= (latch & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0;
}
if (events) {
pcmcia_parse_events(&socket_table[i].socket, events);
}
}
/* Schedule next poll, if needed */
if (((cs_irq == 0) || quick) && (!tcic_timer_pending)) {
poll_timer.expires = jiffies + (quick ? poll_quick : poll_interval);
add_timer(&poll_timer);
tcic_timer_pending = 1;
}
active = 0;
pr_debug("interrupt done\n");
return IRQ_HANDLED;
} /* tcic_interrupt */
static void tcic_timer(u_long data)
{
pr_debug("tcic_timer()\n");
tcic_timer_pending = 0;
tcic_interrupt(0, NULL);
} /* tcic_timer */
/*====================================================================*/
static int tcic_get_status(struct pcmcia_socket *sock, u_int *value)
{
u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
u_char reg;
tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
| TCIC_ADDR_INDREG | TCIC_SCF1(psock));
reg = tcic_getb(TCIC_SSTAT);
*value = (reg & TCIC_SSTAT_CD) ? SS_DETECT : 0;
*value |= (reg & TCIC_SSTAT_WP) ? SS_WRPROT : 0;
if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) {
*value |= (reg & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0;
} else {
*value |= (reg & TCIC_SSTAT_RDY) ? SS_READY : 0;
*value |= (reg & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0;
*value |= (reg & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0;
}
reg = tcic_getb(TCIC_PWR);
if (reg & (TCIC_PWR_VCC(psock)|TCIC_PWR_VPP(psock)))
*value |= SS_POWERON;
dev_dbg(&sock->dev, "GetStatus(%d) = %#2.2x\n", psock, *value);
return 0;
} /* tcic_get_status */
/*====================================================================*/
static int tcic_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
{
u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
u_char reg;
u_short scf1, scf2;
dev_dbg(&sock->dev, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
"io_irq %d, csc_mask %#2.2x)\n", psock, state->flags,
state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
tcic_setw(TCIC_ADDR+2, (psock << TCIC_SS_SHFT) | TCIC_ADR2_INDREG);
reg = tcic_getb(TCIC_PWR);
reg &= ~(TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock));
if (state->Vcc == 50) {
switch (state->Vpp) {
case 0: reg |= TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock); break;
case 50: reg |= TCIC_PWR_VCC(psock); break;
case 120: reg |= TCIC_PWR_VPP(psock); break;
default: return -EINVAL;
}
} else if (state->Vcc != 0)
return -EINVAL;
if (reg != tcic_getb(TCIC_PWR))
tcic_setb(TCIC_PWR, reg);
reg = TCIC_ILOCK_HOLD_CCLK | TCIC_ILOCK_CWAIT;
if (state->flags & SS_OUTPUT_ENA) {
tcic_setb(TCIC_SCTRL, TCIC_SCTRL_ENA);
reg |= TCIC_ILOCK_CRESENA;
} else
tcic_setb(TCIC_SCTRL, 0);
if (state->flags & SS_RESET)
reg |= TCIC_ILOCK_CRESET;
tcic_aux_setb(TCIC_AUX_ILOCK, reg);
tcic_setw(TCIC_ADDR, TCIC_SCF1(psock));
scf1 = TCIC_SCF1_FINPACK;
scf1 |= TCIC_IRQ(state->io_irq);
if (state->flags & SS_IOCARD) {
scf1 |= TCIC_SCF1_IOSTS;
if (state->flags & SS_SPKR_ENA)
scf1 |= TCIC_SCF1_SPKR;
if (state->flags & SS_DMA_MODE)
scf1 |= TCIC_SCF1_DREQ2 << TCIC_SCF1_DMA_SHIFT;
}
tcic_setw(TCIC_DATA, scf1);
/* Some general setup stuff, and configure status interrupt */
reg = TCIC_WAIT_ASYNC | TCIC_WAIT_SENSE | to_cycles(250);
tcic_aux_setb(TCIC_AUX_WCTL, reg);
tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00|
TCIC_IRQ(cs_irq));
/* Card status change interrupt mask */
tcic_setw(TCIC_ADDR, TCIC_SCF2(psock));
scf2 = TCIC_SCF2_MALL;
if (state->csc_mask & SS_DETECT) scf2 &= ~TCIC_SCF2_MCD;
if (state->flags & SS_IOCARD) {
if (state->csc_mask & SS_STSCHG) reg &= ~TCIC_SCF2_MLBAT1;
} else {
if (state->csc_mask & SS_BATDEAD) reg &= ~TCIC_SCF2_MLBAT1;
if (state->csc_mask & SS_BATWARN) reg &= ~TCIC_SCF2_MLBAT2;
if (state->csc_mask & SS_READY) reg &= ~TCIC_SCF2_MRDY;
}
tcic_setw(TCIC_DATA, scf2);
/* For the ISA bus, the irq should be active-high totem-pole */
tcic_setb(TCIC_IENA, TCIC_IENA_CDCHG | TCIC_IENA_CFG_HIGH);
return 0;
} /* tcic_set_socket */
/*====================================================================*/
static int tcic_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
{
u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
u_int addr;
u_short base, len, ioctl;
dev_dbg(&sock->dev, "SetIOMap(%d, %d, %#2.2x, %d ns, "
"%#llx-%#llx)\n", psock, io->map, io->flags, io->speed,
(unsigned long long)io->start, (unsigned long long)io->stop);
if ((io->map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
(io->stop < io->start)) return -EINVAL;
tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
addr = TCIC_IWIN(psock, io->map);
base = io->start; len = io->stop - io->start;
/* Check to see that len+1 is power of two, etc */
if ((len & (len+1)) || (base & len)) return -EINVAL;
base |= (len+1)>>1;
tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X);
tcic_setw(TCIC_DATA, base);
ioctl = (psock << TCIC_ICTL_SS_SHFT);
ioctl |= (len == 0) ? TCIC_ICTL_TINY : 0;
ioctl |= (io->flags & MAP_ACTIVE) ? TCIC_ICTL_ENA : 0;
ioctl |= to_cycles(io->speed) & TCIC_ICTL_WSCNT_MASK;
if (!(io->flags & MAP_AUTOSZ)) {
ioctl |= TCIC_ICTL_QUIET;
ioctl |= (io->flags & MAP_16BIT) ? TCIC_ICTL_BW_16 : TCIC_ICTL_BW_8;
}
tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X);
tcic_setw(TCIC_DATA, ioctl);
return 0;
} /* tcic_set_io_map */
/*====================================================================*/
static int tcic_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem)
{
u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
u_short addr, ctl;
u_long base, len, mmap;
dev_dbg(&sock->dev, "SetMemMap(%d, %d, %#2.2x, %d ns, "
"%#llx-%#llx, %#x)\n", psock, mem->map, mem->flags,
mem->speed, (unsigned long long)mem->res->start,
(unsigned long long)mem->res->end, mem->card_start);
if ((mem->map > 3) || (mem->card_start > 0x3ffffff) ||
(mem->res->start > 0xffffff) || (mem->res->end > 0xffffff) ||
(mem->res->start > mem->res->end) || (mem->speed > 1000))
return -EINVAL;
tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
addr = TCIC_MWIN(psock, mem->map);
base = mem->res->start; len = mem->res->end - mem->res->start;
if ((len & (len+1)) || (base & len)) return -EINVAL;
if (len == 0x0fff)
base = (base >> TCIC_MBASE_HA_SHFT) | TCIC_MBASE_4K_BIT;
else
base = (base | (len+1)>>1) >> TCIC_MBASE_HA_SHFT;
tcic_setw(TCIC_ADDR, addr + TCIC_MBASE_X);
tcic_setw(TCIC_DATA, base);
mmap = mem->card_start - mem->res->start;
mmap = (mmap >> TCIC_MMAP_CA_SHFT) & TCIC_MMAP_CA_MASK;
if (mem->flags & MAP_ATTRIB) mmap |= TCIC_MMAP_REG;
tcic_setw(TCIC_ADDR, addr + TCIC_MMAP_X);
tcic_setw(TCIC_DATA, mmap);
ctl = TCIC_MCTL_QUIET | (psock << TCIC_MCTL_SS_SHFT);
ctl |= to_cycles(mem->speed) & TCIC_MCTL_WSCNT_MASK;
ctl |= (mem->flags & MAP_16BIT) ? 0 : TCIC_MCTL_B8;
ctl |= (mem->flags & MAP_WRPROT) ? TCIC_MCTL_WP : 0;
ctl |= (mem->flags & MAP_ACTIVE) ? TCIC_MCTL_ENA : 0;
tcic_setw(TCIC_ADDR, addr + TCIC_MCTL_X);
tcic_setw(TCIC_DATA, ctl);
return 0;
} /* tcic_set_mem_map */
/*====================================================================*/
static int tcic_init(struct pcmcia_socket *s)
{
int i;
struct resource res = { .start = 0, .end = 0x1000 };
pccard_io_map io = { 0, 0, 0, 0, 1 };
pccard_mem_map mem = { .res = &res, };
for (i = 0; i < 2; i++) {
io.map = i;
tcic_set_io_map(s, &io);
}
for (i = 0; i < 5; i++) {
mem.map = i;
tcic_set_mem_map(s, &mem);
}
return 0;
}
static struct pccard_operations tcic_operations = {
.init = tcic_init,
.get_status = tcic_get_status,
.set_socket = tcic_set_socket,
.set_io_map = tcic_set_io_map,
.set_mem_map = tcic_set_mem_map,
};
/*====================================================================*/
module_init(init_tcic);
module_exit(exit_tcic);

266
drivers/pcmcia/tcic.h Normal file
View file

@ -0,0 +1,266 @@
/*
* tcic.h 1.13 1999/10/25 20:03:34
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License
* at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and
* limitations under the License.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in which
* case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use
* your version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the MPL or the GPL.
*/
#ifndef _LINUX_TCIC_H
#define _LINUX_TCIC_H
#define TCIC_BASE 0x240
/* offsets of registers from TCIC_BASE */
#define TCIC_DATA 0x00
#define TCIC_ADDR 0x02
#define TCIC_SCTRL 0x06
#define TCIC_SSTAT 0x07
#define TCIC_MODE 0x08
#define TCIC_PWR 0x09
#define TCIC_EDC 0x0A
#define TCIC_ICSR 0x0C
#define TCIC_IENA 0x0D
#define TCIC_AUX 0x0E
#define TCIC_SS_SHFT 12
#define TCIC_SS_MASK 0x7000
/* Flags for TCIC_ADDR */
#define TCIC_ADR2_REG 0x8000
#define TCIC_ADR2_INDREG 0x0800
#define TCIC_ADDR_REG 0x80000000
#define TCIC_ADDR_SS_SHFT (TCIC_SS_SHFT+16)
#define TCIC_ADDR_SS_MASK (TCIC_SS_MASK<<16)
#define TCIC_ADDR_INDREG 0x08000000
#define TCIC_ADDR_IO 0x04000000
#define TCIC_ADDR_MASK 0x03ffffff
/* Flags for TCIC_SCTRL */
#define TCIC_SCTRL_ENA 0x01
#define TCIC_SCTRL_INCMODE 0x18
#define TCIC_SCTRL_INCMODE_HOLD 0x00
#define TCIC_SCTRL_INCMODE_WORD 0x08
#define TCIC_SCTRL_INCMODE_REG 0x10
#define TCIC_SCTRL_INCMODE_AUTO 0x18
#define TCIC_SCTRL_EDCSUM 0x20
#define TCIC_SCTRL_RESET 0x80
/* Flags for TCIC_SSTAT */
#define TCIC_SSTAT_6US 0x01
#define TCIC_SSTAT_10US 0x02
#define TCIC_SSTAT_PROGTIME 0x04
#define TCIC_SSTAT_LBAT1 0x08
#define TCIC_SSTAT_LBAT2 0x10
#define TCIC_SSTAT_RDY 0x20 /* Inverted */
#define TCIC_SSTAT_WP 0x40
#define TCIC_SSTAT_CD 0x80 /* Card detect */
/* Flags for TCIC_MODE */
#define TCIC_MODE_PGMMASK 0x1f
#define TCIC_MODE_NORMAL 0x00
#define TCIC_MODE_PGMWR 0x01
#define TCIC_MODE_PGMRD 0x02
#define TCIC_MODE_PGMCE 0x04
#define TCIC_MODE_PGMDBW 0x08
#define TCIC_MODE_PGMWORD 0x10
#define TCIC_MODE_AUXSEL_MASK 0xe0
/* Registers accessed through TCIC_AUX, by setting TCIC_MODE */
#define TCIC_AUX_TCTL (0<<5)
#define TCIC_AUX_PCTL (1<<5)
#define TCIC_AUX_WCTL (2<<5)
#define TCIC_AUX_EXTERN (3<<5)
#define TCIC_AUX_PDATA (4<<5)
#define TCIC_AUX_SYSCFG (5<<5)
#define TCIC_AUX_ILOCK (6<<5)
#define TCIC_AUX_TEST (7<<5)
/* Flags for TCIC_PWR */
#define TCIC_PWR_VCC(sock) (0x01<<(sock))
#define TCIC_PWR_VCC_MASK 0x03
#define TCIC_PWR_VPP(sock) (0x08<<(sock))
#define TCIC_PWR_VPP_MASK 0x18
#define TCIC_PWR_CLIMENA 0x40
#define TCIC_PWR_CLIMSTAT 0x80
/* Flags for TCIC_ICSR */
#define TCIC_ICSR_CLEAR 0x01
#define TCIC_ICSR_SET 0x02
#define TCIC_ICSR_JAM (TCIC_ICSR_CLEAR|TCIC_ICSR_SET)
#define TCIC_ICSR_STOPCPU 0x04
#define TCIC_ICSR_ILOCK 0x08
#define TCIC_ICSR_PROGTIME 0x10
#define TCIC_ICSR_ERR 0x20
#define TCIC_ICSR_CDCHG 0x40
#define TCIC_ICSR_IOCHK 0x80
/* Flags for TCIC_IENA */
#define TCIC_IENA_CFG_MASK 0x03
#define TCIC_IENA_CFG_OFF 0x00 /* disabled */
#define TCIC_IENA_CFG_OD 0x01 /* active low, open drain */
#define TCIC_IENA_CFG_LOW 0x02 /* active low, totem pole */
#define TCIC_IENA_CFG_HIGH 0x03 /* active high, totem pole */
#define TCIC_IENA_ILOCK 0x08
#define TCIC_IENA_PROGTIME 0x10
#define TCIC_IENA_ERR 0x20 /* overcurrent or iochk */
#define TCIC_IENA_CDCHG 0x40
/* Flags for TCIC_AUX_WCTL */
#define TCIC_WAIT_COUNT_MASK 0x001f
#define TCIC_WAIT_ASYNC 0x0020
#define TCIC_WAIT_SENSE 0x0040
#define TCIC_WAIT_SRC 0x0080
#define TCIC_WCTL_WR 0x0100
#define TCIC_WCTL_RD 0x0200
#define TCIC_WCTL_CE 0x0400
#define TCIC_WCTL_LLBAT1 0x0800
#define TCIC_WCTL_LLBAT2 0x1000
#define TCIC_WCTL_LRDY 0x2000
#define TCIC_WCTL_LWP 0x4000
#define TCIC_WCTL_LCD 0x8000
/* Flags for TCIC_AUX_SYSCFG */
#define TCIC_SYSCFG_IRQ_MASK 0x000f
#define TCIC_SYSCFG_MCSFULL 0x0010
#define TCIC_SYSCFG_IO1723 0x0020
#define TCIC_SYSCFG_MCSXB 0x0040
#define TCIC_SYSCFG_ICSXB 0x0080
#define TCIC_SYSCFG_NOPDN 0x0100
#define TCIC_SYSCFG_MPSEL_SHFT 9
#define TCIC_SYSCFG_MPSEL_MASK 0x0e00
#define TCIC_SYSCFG_MPSENSE 0x2000
#define TCIC_SYSCFG_AUTOBUSY 0x4000
#define TCIC_SYSCFG_ACC 0x8000
#define TCIC_ILOCK_OUT 0x01
#define TCIC_ILOCK_SENSE 0x02
#define TCIC_ILOCK_CRESET 0x04
#define TCIC_ILOCK_CRESENA 0x08
#define TCIC_ILOCK_CWAIT 0x10
#define TCIC_ILOCK_CWAITSNS 0x20
#define TCIC_ILOCK_HOLD_MASK 0xc0
#define TCIC_ILOCK_HOLD_CCLK 0xc0
#define TCIC_ILOCKTEST_ID_SH 8
#define TCIC_ILOCKTEST_ID_MASK 0x7f00
#define TCIC_ILOCKTEST_MCIC_1 0x8000
#define TCIC_ID_DB86082 0x02
#define TCIC_ID_DB86082A 0x03
#define TCIC_ID_DB86084 0x04
#define TCIC_ID_DB86084A 0x08
#define TCIC_ID_DB86072 0x15
#define TCIC_ID_DB86184 0x14
#define TCIC_ID_DB86082B 0x17
#define TCIC_TEST_DIAG 0x8000
/*
* Indirectly addressed registers
*/
#define TCIC_SCF1(sock) ((sock)<<3)
#define TCIC_SCF2(sock) (((sock)<<3)+2)
/* Flags for SCF1 */
#define TCIC_SCF1_IRQ_MASK 0x000f
#define TCIC_SCF1_IRQ_OFF 0x0000
#define TCIC_SCF1_IRQOC 0x0010
#define TCIC_SCF1_PCVT 0x0020
#define TCIC_SCF1_IRDY 0x0040
#define TCIC_SCF1_ATA 0x0080
#define TCIC_SCF1_DMA_SHIFT 8
#define TCIC_SCF1_DMA_MASK 0x0700
#define TCIC_SCF1_DMA_OFF 0
#define TCIC_SCF1_DREQ2 2
#define TCIC_SCF1_IOSTS 0x0800
#define TCIC_SCF1_SPKR 0x1000
#define TCIC_SCF1_FINPACK 0x2000
#define TCIC_SCF1_DELWR 0x4000
#define TCIC_SCF1_HD7IDE 0x8000
/* Flags for SCF2 */
#define TCIC_SCF2_RI 0x0001
#define TCIC_SCF2_IDBR 0x0002
#define TCIC_SCF2_MDBR 0x0004
#define TCIC_SCF2_MLBAT1 0x0008
#define TCIC_SCF2_MLBAT2 0x0010
#define TCIC_SCF2_MRDY 0x0020
#define TCIC_SCF2_MWP 0x0040
#define TCIC_SCF2_MCD 0x0080
#define TCIC_SCF2_MALL 0x00f8
/* Indirect addresses for memory window registers */
#define TCIC_MWIN(sock,map) (0x100+(((map)+((sock)<<2))<<3))
#define TCIC_MBASE_X 2
#define TCIC_MMAP_X 4
#define TCIC_MCTL_X 6
#define TCIC_MBASE_4K_BIT 0x4000
#define TCIC_MBASE_HA_SHFT 12
#define TCIC_MBASE_HA_MASK 0x0fff
#define TCIC_MMAP_REG 0x8000
#define TCIC_MMAP_CA_SHFT 12
#define TCIC_MMAP_CA_MASK 0x3fff
#define TCIC_MCTL_WSCNT_MASK 0x001f
#define TCIC_MCTL_WCLK 0x0020
#define TCIC_MCTL_WCLK_CCLK 0x0000
#define TCIC_MCTL_WCLK_BCLK 0x0020
#define TCIC_MCTL_QUIET 0x0040
#define TCIC_MCTL_WP 0x0080
#define TCIC_MCTL_ACC 0x0100
#define TCIC_MCTL_KE 0x0200
#define TCIC_MCTL_EDC 0x0400
#define TCIC_MCTL_B8 0x0800
#define TCIC_MCTL_SS_SHFT TCIC_SS_SHFT
#define TCIC_MCTL_SS_MASK TCIC_SS_MASK
#define TCIC_MCTL_ENA 0x8000
/* Indirect addresses for I/O window registers */
#define TCIC_IWIN(sock,map) (0x200+(((map)+((sock)<<1))<<2))
#define TCIC_IBASE_X 0
#define TCIC_ICTL_X 2
#define TCIC_ICTL_WSCNT_MASK TCIC_MCTL_WSCNT_MASK
#define TCIC_ICTL_QUIET TCIC_MCTL_QUIET
#define TCIC_ICTL_1K 0x0080
#define TCIC_ICTL_PASS16 0x0100
#define TCIC_ICTL_ACC TCIC_MCTL_ACC
#define TCIC_ICTL_TINY 0x0200
#define TCIC_ICTL_B16 0x0400
#define TCIC_ICTL_B8 TCIC_MCTL_B8
#define TCIC_ICTL_BW_MASK (TCIC_ICTL_B16|TCIC_ICTL_B8)
#define TCIC_ICTL_BW_DYN 0
#define TCIC_ICTL_BW_8 TCIC_ICTL_B8
#define TCIC_ICTL_BW_16 TCIC_ICTL_B16
#define TCIC_ICTL_BW_ATA (TCIC_ICTL_B16|TCIC_ICTL_B8)
#define TCIC_ICTL_SS_SHFT TCIC_SS_SHFT
#define TCIC_ICTL_SS_MASK TCIC_SS_MASK
#define TCIC_ICTL_ENA TCIC_MCTL_ENA
#endif /* _LINUX_TCIC_H */

984
drivers/pcmcia/ti113x.h Normal file
View file

@ -0,0 +1,984 @@
/*
* ti113x.h 1.16 1999/10/25 20:03:34
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License
* at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and
* limitations under the License.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in which
* case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use
* your version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the MPL or the GPL.
*/
#ifndef _LINUX_TI113X_H
#define _LINUX_TI113X_H
/* Register definitions for TI 113X PCI-to-CardBus bridges */
/* System Control Register */
#define TI113X_SYSTEM_CONTROL 0x0080 /* 32 bit */
#define TI113X_SCR_SMIROUTE 0x04000000
#define TI113X_SCR_SMISTATUS 0x02000000
#define TI113X_SCR_SMIENB 0x01000000
#define TI113X_SCR_VCCPROT 0x00200000
#define TI113X_SCR_REDUCEZV 0x00100000
#define TI113X_SCR_CDREQEN 0x00080000
#define TI113X_SCR_CDMACHAN 0x00070000
#define TI113X_SCR_SOCACTIVE 0x00002000
#define TI113X_SCR_PWRSTREAM 0x00000800
#define TI113X_SCR_DELAYUP 0x00000400
#define TI113X_SCR_DELAYDOWN 0x00000200
#define TI113X_SCR_INTERROGATE 0x00000100
#define TI113X_SCR_CLKRUN_SEL 0x00000080
#define TI113X_SCR_PWRSAVINGS 0x00000040
#define TI113X_SCR_SUBSYSRW 0x00000020
#define TI113X_SCR_CB_DPAR 0x00000010
#define TI113X_SCR_CDMA_EN 0x00000008
#define TI113X_SCR_ASYNC_IRQ 0x00000004
#define TI113X_SCR_KEEPCLK 0x00000002
#define TI113X_SCR_CLKRUN_ENA 0x00000001
#define TI122X_SCR_SER_STEP 0xc0000000
#define TI122X_SCR_INTRTIE 0x20000000
#define TIXX21_SCR_TIEALL 0x10000000
#define TI122X_SCR_CBRSVD 0x00400000
#define TI122X_SCR_MRBURSTDN 0x00008000
#define TI122X_SCR_MRBURSTUP 0x00004000
#define TI122X_SCR_RIMUX 0x00000001
/* Multimedia Control Register */
#define TI1250_MULTIMEDIA_CTL 0x0084 /* 8 bit */
#define TI1250_MMC_ZVOUTEN 0x80
#define TI1250_MMC_PORTSEL 0x40
#define TI1250_MMC_ZVEN1 0x02
#define TI1250_MMC_ZVEN0 0x01
#define TI1250_GENERAL_STATUS 0x0085 /* 8 bit */
#define TI1250_GPIO0_CONTROL 0x0088 /* 8 bit */
#define TI1250_GPIO1_CONTROL 0x0089 /* 8 bit */
#define TI1250_GPIO2_CONTROL 0x008a /* 8 bit */
#define TI1250_GPIO3_CONTROL 0x008b /* 8 bit */
#define TI1250_GPIO_MODE_MASK 0xc0
/* IRQMUX/MFUNC Register */
#define TI122X_MFUNC 0x008c /* 32 bit */
#define TI122X_MFUNC0_MASK 0x0000000f
#define TI122X_MFUNC1_MASK 0x000000f0
#define TI122X_MFUNC2_MASK 0x00000f00
#define TI122X_MFUNC3_MASK 0x0000f000
#define TI122X_MFUNC4_MASK 0x000f0000
#define TI122X_MFUNC5_MASK 0x00f00000
#define TI122X_MFUNC6_MASK 0x0f000000
#define TI122X_MFUNC0_INTA 0x00000002
#define TI125X_MFUNC0_INTB 0x00000001
#define TI122X_MFUNC1_INTB 0x00000020
#define TI122X_MFUNC3_IRQSER 0x00001000
/* Retry Status Register */
#define TI113X_RETRY_STATUS 0x0090 /* 8 bit */
#define TI113X_RSR_PCIRETRY 0x80
#define TI113X_RSR_CBRETRY 0x40
#define TI113X_RSR_TEXP_CBB 0x20
#define TI113X_RSR_MEXP_CBB 0x10
#define TI113X_RSR_TEXP_CBA 0x08
#define TI113X_RSR_MEXP_CBA 0x04
#define TI113X_RSR_TEXP_PCI 0x02
#define TI113X_RSR_MEXP_PCI 0x01
/* Card Control Register */
#define TI113X_CARD_CONTROL 0x0091 /* 8 bit */
#define TI113X_CCR_RIENB 0x80
#define TI113X_CCR_ZVENABLE 0x40
#define TI113X_CCR_PCI_IRQ_ENA 0x20
#define TI113X_CCR_PCI_IREQ 0x10
#define TI113X_CCR_PCI_CSC 0x08
#define TI113X_CCR_SPKROUTEN 0x02
#define TI113X_CCR_IFG 0x01
#define TI1220_CCR_PORT_SEL 0x20
#define TI122X_CCR_AUD2MUX 0x04
/* Device Control Register */
#define TI113X_DEVICE_CONTROL 0x0092 /* 8 bit */
#define TI113X_DCR_5V_FORCE 0x40
#define TI113X_DCR_3V_FORCE 0x20
#define TI113X_DCR_IMODE_MASK 0x06
#define TI113X_DCR_IMODE_ISA 0x02
#define TI113X_DCR_IMODE_SERIAL 0x04
#define TI12XX_DCR_IMODE_PCI_ONLY 0x00
#define TI12XX_DCR_IMODE_ALL_SERIAL 0x06
/* Buffer Control Register */
#define TI113X_BUFFER_CONTROL 0x0093 /* 8 bit */
#define TI113X_BCR_CB_READ_DEPTH 0x08
#define TI113X_BCR_CB_WRITE_DEPTH 0x04
#define TI113X_BCR_PCI_READ_DEPTH 0x02
#define TI113X_BCR_PCI_WRITE_DEPTH 0x01
/* Diagnostic Register */
#define TI1250_DIAGNOSTIC 0x0093 /* 8 bit */
#define TI1250_DIAG_TRUE_VALUE 0x80
#define TI1250_DIAG_PCI_IREQ 0x40
#define TI1250_DIAG_PCI_CSC 0x20
#define TI1250_DIAG_ASYNC_CSC 0x01
/* DMA Registers */
#define TI113X_DMA_0 0x0094 /* 32 bit */
#define TI113X_DMA_1 0x0098 /* 32 bit */
/* ExCA IO offset registers */
#define TI113X_IO_OFFSET(map) (0x36+((map)<<1))
/* EnE test register */
#define ENE_TEST_C9 0xc9 /* 8bit */
#define ENE_TEST_C9_TLTENABLE 0x02
#define ENE_TEST_C9_PFENABLE_F0 0x04
#define ENE_TEST_C9_PFENABLE_F1 0x08
#define ENE_TEST_C9_PFENABLE (ENE_TEST_C9_PFENABLE_F0 | ENE_TEST_C9_PFENABLE_F1)
#define ENE_TEST_C9_WPDISALBLE_F0 0x40
#define ENE_TEST_C9_WPDISALBLE_F1 0x80
#define ENE_TEST_C9_WPDISALBLE (ENE_TEST_C9_WPDISALBLE_F0 | ENE_TEST_C9_WPDISALBLE_F1)
/*
* Texas Instruments CardBus controller overrides.
*/
#define ti_sysctl(socket) ((socket)->private[0])
#define ti_cardctl(socket) ((socket)->private[1])
#define ti_devctl(socket) ((socket)->private[2])
#define ti_diag(socket) ((socket)->private[3])
#define ti_mfunc(socket) ((socket)->private[4])
#define ene_test_c9(socket) ((socket)->private[5])
/*
* These are the TI specific power management handlers.
*/
static void ti_save_state(struct yenta_socket *socket)
{
ti_sysctl(socket) = config_readl(socket, TI113X_SYSTEM_CONTROL);
ti_mfunc(socket) = config_readl(socket, TI122X_MFUNC);
ti_cardctl(socket) = config_readb(socket, TI113X_CARD_CONTROL);
ti_devctl(socket) = config_readb(socket, TI113X_DEVICE_CONTROL);
ti_diag(socket) = config_readb(socket, TI1250_DIAGNOSTIC);
if (socket->dev->vendor == PCI_VENDOR_ID_ENE)
ene_test_c9(socket) = config_readb(socket, ENE_TEST_C9);
}
static void ti_restore_state(struct yenta_socket *socket)
{
config_writel(socket, TI113X_SYSTEM_CONTROL, ti_sysctl(socket));
config_writel(socket, TI122X_MFUNC, ti_mfunc(socket));
config_writeb(socket, TI113X_CARD_CONTROL, ti_cardctl(socket));
config_writeb(socket, TI113X_DEVICE_CONTROL, ti_devctl(socket));
config_writeb(socket, TI1250_DIAGNOSTIC, ti_diag(socket));
if (socket->dev->vendor == PCI_VENDOR_ID_ENE)
config_writeb(socket, ENE_TEST_C9, ene_test_c9(socket));
}
/*
* Zoom video control for TI122x/113x chips
*/
static void ti_zoom_video(struct pcmcia_socket *sock, int onoff)
{
u8 reg;
struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
/* If we don't have a Zoom Video switch this is harmless,
we just tristate the unused (ZV) lines */
reg = config_readb(socket, TI113X_CARD_CONTROL);
if (onoff)
/* Zoom zoom, we will all go together, zoom zoom, zoom zoom */
reg |= TI113X_CCR_ZVENABLE;
else
reg &= ~TI113X_CCR_ZVENABLE;
config_writeb(socket, TI113X_CARD_CONTROL, reg);
}
/*
* The 145x series can also use this. They have an additional
* ZV autodetect mode we don't use but don't actually need.
* FIXME: manual says its in func0 and func1 but disagrees with
* itself about this - do we need to force func0, if so we need
* to know a lot more about socket pairings in pcmcia_socket than
* we do now.. uggh.
*/
static void ti1250_zoom_video(struct pcmcia_socket *sock, int onoff)
{
struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
int shift = 0;
u8 reg;
ti_zoom_video(sock, onoff);
reg = config_readb(socket, TI1250_MULTIMEDIA_CTL);
reg |= TI1250_MMC_ZVOUTEN; /* ZV bus enable */
if(PCI_FUNC(socket->dev->devfn)==1)
shift = 1;
if(onoff)
{
reg &= ~(1<<6); /* Clear select bit */
reg |= shift<<6; /* Favour our socket */
reg |= 1<<shift; /* Socket zoom video on */
}
else
{
reg &= ~(1<<6); /* Clear select bit */
reg |= (1^shift)<<6; /* Favour other socket */
reg &= ~(1<<shift); /* Socket zoon video off */
}
config_writeb(socket, TI1250_MULTIMEDIA_CTL, reg);
}
static void ti_set_zv(struct yenta_socket *socket)
{
if(socket->dev->vendor == PCI_VENDOR_ID_TI)
{
switch(socket->dev->device)
{
/* There may be more .. */
case PCI_DEVICE_ID_TI_1220:
case PCI_DEVICE_ID_TI_1221:
case PCI_DEVICE_ID_TI_1225:
case PCI_DEVICE_ID_TI_4510:
socket->socket.zoom_video = ti_zoom_video;
break;
case PCI_DEVICE_ID_TI_1250:
case PCI_DEVICE_ID_TI_1251A:
case PCI_DEVICE_ID_TI_1251B:
case PCI_DEVICE_ID_TI_1450:
socket->socket.zoom_video = ti1250_zoom_video;
}
}
}
/*
* Generic TI init - TI has an extension for the
* INTCTL register that sets the PCI CSC interrupt.
* Make sure we set it correctly at open and init
* time
* - override: disable the PCI CSC interrupt. This makes
* it possible to use the CSC interrupt to probe the
* ISA interrupts.
* - init: set the interrupt to match our PCI state.
* This makes us correctly get PCI CSC interrupt
* events.
*/
static int ti_init(struct yenta_socket *socket)
{
u8 new, reg = exca_readb(socket, I365_INTCTL);
new = reg & ~I365_INTR_ENA;
if (socket->dev->irq)
new |= I365_INTR_ENA;
if (new != reg)
exca_writeb(socket, I365_INTCTL, new);
return 0;
}
static int ti_override(struct yenta_socket *socket)
{
u8 new, reg = exca_readb(socket, I365_INTCTL);
new = reg & ~I365_INTR_ENA;
if (new != reg)
exca_writeb(socket, I365_INTCTL, new);
ti_set_zv(socket);
return 0;
}
static void ti113x_use_isa_irq(struct yenta_socket *socket)
{
int isa_irq = -1;
u8 intctl;
u32 isa_irq_mask = 0;
if (!isa_probe)
return;
/* get a free isa int */
isa_irq_mask = yenta_probe_irq(socket, isa_interrupts);
if (!isa_irq_mask)
return; /* no useable isa irq found */
/* choose highest available */
for (; isa_irq_mask; isa_irq++)
isa_irq_mask >>= 1;
socket->cb_irq = isa_irq;
exca_writeb(socket, I365_CSCINT, (isa_irq << 4));
intctl = exca_readb(socket, I365_INTCTL);
intctl &= ~(I365_INTR_ENA | I365_IRQ_MASK); /* CSC Enable */
exca_writeb(socket, I365_INTCTL, intctl);
dev_info(&socket->dev->dev,
"Yenta TI113x: using isa irq %d for CardBus\n", isa_irq);
}
static int ti113x_override(struct yenta_socket *socket)
{
u8 cardctl;
cardctl = config_readb(socket, TI113X_CARD_CONTROL);
cardctl &= ~(TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_IREQ | TI113X_CCR_PCI_CSC);
if (socket->dev->irq)
cardctl |= TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_CSC | TI113X_CCR_PCI_IREQ;
else
ti113x_use_isa_irq(socket);
config_writeb(socket, TI113X_CARD_CONTROL, cardctl);
return ti_override(socket);
}
/* irqrouting for func0, probes PCI interrupt and ISA interrupts */
static void ti12xx_irqroute_func0(struct yenta_socket *socket)
{
u32 mfunc, mfunc_old, devctl;
u8 gpio3, gpio3_old;
int pci_irq_status;
mfunc = mfunc_old = config_readl(socket, TI122X_MFUNC);
devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
dev_printk(KERN_INFO, &socket->dev->dev,
"TI: mfunc 0x%08x, devctl 0x%02x\n", mfunc, devctl);
/* make sure PCI interrupts are enabled before probing */
ti_init(socket);
/* test PCI interrupts first. only try fixing if return value is 0! */
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status)
goto out;
/*
* We're here which means PCI interrupts are _not_ delivered. try to
* find the right setting (all serial or parallel)
*/
dev_printk(KERN_INFO, &socket->dev->dev,
"TI: probing PCI interrupt failed, trying to fix\n");
/* for serial PCI make sure MFUNC3 is set to IRQSER */
if ((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) {
switch (socket->dev->device) {
case PCI_DEVICE_ID_TI_1250:
case PCI_DEVICE_ID_TI_1251A:
case PCI_DEVICE_ID_TI_1251B:
case PCI_DEVICE_ID_TI_1450:
case PCI_DEVICE_ID_TI_1451A:
case PCI_DEVICE_ID_TI_4450:
case PCI_DEVICE_ID_TI_4451:
/* these chips have no IRQSER setting in MFUNC3 */
break;
default:
mfunc = (mfunc & ~TI122X_MFUNC3_MASK) | TI122X_MFUNC3_IRQSER;
/* write down if changed, probe */
if (mfunc != mfunc_old) {
config_writel(socket, TI122X_MFUNC, mfunc);
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
dev_printk(KERN_INFO, &socket->dev->dev,
"TI: all-serial interrupts ok\n");
mfunc_old = mfunc;
goto out;
}
/* not working, back to old value */
mfunc = mfunc_old;
config_writel(socket, TI122X_MFUNC, mfunc);
if (pci_irq_status == -1)
goto out;
}
}
/* serial PCI interrupts not working fall back to parallel */
dev_printk(KERN_INFO, &socket->dev->dev,
"TI: falling back to parallel PCI interrupts\n");
devctl &= ~TI113X_DCR_IMODE_MASK;
devctl |= TI113X_DCR_IMODE_SERIAL; /* serial ISA could be right */
config_writeb(socket, TI113X_DEVICE_CONTROL, devctl);
}
/* parallel PCI interrupts: route INTA */
switch (socket->dev->device) {
case PCI_DEVICE_ID_TI_1250:
case PCI_DEVICE_ID_TI_1251A:
case PCI_DEVICE_ID_TI_1251B:
case PCI_DEVICE_ID_TI_1450:
/* make sure GPIO3 is set to INTA */
gpio3 = gpio3_old = config_readb(socket, TI1250_GPIO3_CONTROL);
gpio3 &= ~TI1250_GPIO_MODE_MASK;
if (gpio3 != gpio3_old)
config_writeb(socket, TI1250_GPIO3_CONTROL, gpio3);
break;
default:
gpio3 = gpio3_old = 0;
mfunc = (mfunc & ~TI122X_MFUNC0_MASK) | TI122X_MFUNC0_INTA;
if (mfunc != mfunc_old)
config_writel(socket, TI122X_MFUNC, mfunc);
}
/* time to probe again */
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
mfunc_old = mfunc;
dev_printk(KERN_INFO, &socket->dev->dev,
"TI: parallel PCI interrupts ok\n");
} else {
/* not working, back to old value */
mfunc = mfunc_old;
config_writel(socket, TI122X_MFUNC, mfunc);
if (gpio3 != gpio3_old)
config_writeb(socket, TI1250_GPIO3_CONTROL, gpio3_old);
}
out:
if (pci_irq_status < 1) {
socket->cb_irq = 0;
dev_printk(KERN_INFO, &socket->dev->dev,
"Yenta TI: no PCI interrupts. Fish. "
"Please report.\n");
}
}
/* changes the irq of func1 to match that of func0 */
static int ti12xx_align_irqs(struct yenta_socket *socket, int *old_irq)
{
struct pci_dev *func0;
/* find func0 device */
func0 = pci_get_slot(socket->dev->bus, socket->dev->devfn & ~0x07);
if (!func0)
return 0;
if (old_irq)
*old_irq = socket->cb_irq;
socket->cb_irq = socket->dev->irq = func0->irq;
pci_dev_put(func0);
return 1;
}
/*
* ties INTA and INTB together. also changes the devices irq to that of
* the function 0 device. call from func1 only.
* returns 1 if INTRTIE changed, 0 otherwise.
*/
static int ti12xx_tie_interrupts(struct yenta_socket *socket, int *old_irq)
{
u32 sysctl;
int ret;
sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
if (sysctl & TI122X_SCR_INTRTIE)
return 0;
/* align */
ret = ti12xx_align_irqs(socket, old_irq);
if (!ret)
return 0;
/* tie */
sysctl |= TI122X_SCR_INTRTIE;
config_writel(socket, TI113X_SYSTEM_CONTROL, sysctl);
return 1;
}
/* undo what ti12xx_tie_interrupts() did */
static void ti12xx_untie_interrupts(struct yenta_socket *socket, int old_irq)
{
u32 sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
sysctl &= ~TI122X_SCR_INTRTIE;
config_writel(socket, TI113X_SYSTEM_CONTROL, sysctl);
socket->cb_irq = socket->dev->irq = old_irq;
}
/*
* irqrouting for func1, plays with INTB routing
* only touches MFUNC for INTB routing. all other bits are taken
* care of in func0 already.
*/
static void ti12xx_irqroute_func1(struct yenta_socket *socket)
{
u32 mfunc, mfunc_old, devctl, sysctl;
int pci_irq_status;
mfunc = mfunc_old = config_readl(socket, TI122X_MFUNC);
devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
dev_printk(KERN_INFO, &socket->dev->dev,
"TI: mfunc 0x%08x, devctl 0x%02x\n",
mfunc, devctl);
/* if IRQs are configured as tied, align irq of func1 with func0 */
sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
if (sysctl & TI122X_SCR_INTRTIE)
ti12xx_align_irqs(socket, NULL);
/* make sure PCI interrupts are enabled before probing */
ti_init(socket);
/* test PCI interrupts first. only try fixing if return value is 0! */
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status)
goto out;
/*
* We're here which means PCI interrupts are _not_ delivered. try to
* find the right setting
*/
dev_printk(KERN_INFO, &socket->dev->dev,
"TI: probing PCI interrupt failed, trying to fix\n");
/* if all serial: set INTRTIE, probe again */
if ((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) {
int old_irq;
if (ti12xx_tie_interrupts(socket, &old_irq)) {
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
dev_printk(KERN_INFO, &socket->dev->dev,
"TI: all-serial interrupts, tied ok\n");
goto out;
}
ti12xx_untie_interrupts(socket, old_irq);
}
}
/* parallel PCI: route INTB, probe again */
else {
int old_irq;
switch (socket->dev->device) {
case PCI_DEVICE_ID_TI_1250:
/* the 1250 has one pin for IRQSER/INTB depending on devctl */
break;
case PCI_DEVICE_ID_TI_1251A:
case PCI_DEVICE_ID_TI_1251B:
case PCI_DEVICE_ID_TI_1450:
/*
* those have a pin for IRQSER/INTB plus INTB in MFUNC0
* we alread probed the shared pin, now go for MFUNC0
*/
mfunc = (mfunc & ~TI122X_MFUNC0_MASK) | TI125X_MFUNC0_INTB;
break;
default:
mfunc = (mfunc & ~TI122X_MFUNC1_MASK) | TI122X_MFUNC1_INTB;
break;
}
/* write, probe */
if (mfunc != mfunc_old) {
config_writel(socket, TI122X_MFUNC, mfunc);
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
dev_printk(KERN_INFO, &socket->dev->dev,
"TI: parallel PCI interrupts ok\n");
goto out;
}
mfunc = mfunc_old;
config_writel(socket, TI122X_MFUNC, mfunc);
if (pci_irq_status == -1)
goto out;
}
/* still nothing: set INTRTIE */
if (ti12xx_tie_interrupts(socket, &old_irq)) {
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
dev_printk(KERN_INFO, &socket->dev->dev,
"TI: parallel PCI interrupts, tied ok\n");
goto out;
}
ti12xx_untie_interrupts(socket, old_irq);
}
}
out:
if (pci_irq_status < 1) {
socket->cb_irq = 0;
dev_printk(KERN_INFO, &socket->dev->dev,
"TI: no PCI interrupts. Fish. Please report.\n");
}
}
/* Returns true value if the second slot of a two-slot controller is empty */
static int ti12xx_2nd_slot_empty(struct yenta_socket *socket)
{
struct pci_dev *func;
struct yenta_socket *slot2;
int devfn;
unsigned int state;
int ret = 1;
u32 sysctl;
/* catch the two-slot controllers */
switch (socket->dev->device) {
case PCI_DEVICE_ID_TI_1220:
case PCI_DEVICE_ID_TI_1221:
case PCI_DEVICE_ID_TI_1225:
case PCI_DEVICE_ID_TI_1251A:
case PCI_DEVICE_ID_TI_1251B:
case PCI_DEVICE_ID_TI_1420:
case PCI_DEVICE_ID_TI_1450:
case PCI_DEVICE_ID_TI_1451A:
case PCI_DEVICE_ID_TI_1520:
case PCI_DEVICE_ID_TI_1620:
case PCI_DEVICE_ID_TI_4520:
case PCI_DEVICE_ID_TI_4450:
case PCI_DEVICE_ID_TI_4451:
/*
* there are way more, but they need to be added in yenta_socket.c
* and pci_ids.h first anyway.
*/
break;
case PCI_DEVICE_ID_TI_XX12:
case PCI_DEVICE_ID_TI_X515:
case PCI_DEVICE_ID_TI_X420:
case PCI_DEVICE_ID_TI_X620:
case PCI_DEVICE_ID_TI_XX21_XX11:
case PCI_DEVICE_ID_TI_7410:
case PCI_DEVICE_ID_TI_7610:
/*
* those are either single or dual slot CB with additional functions
* like 1394, smartcard reader, etc. check the TIEALL flag for them
* the TIEALL flag binds the IRQ of all functions together.
* we catch the single slot variants later.
*/
sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
if (sysctl & TIXX21_SCR_TIEALL)
return 0;
break;
/* single-slot controllers have the 2nd slot empty always :) */
default:
return 1;
}
/* get other slot */
devfn = socket->dev->devfn & ~0x07;
func = pci_get_slot(socket->dev->bus,
(socket->dev->devfn & 0x07) ? devfn : devfn | 0x01);
if (!func)
return 1;
/*
* check that the device id of both slots match. this is needed for the
* XX21 and the XX11 controller that share the same device id for single
* and dual slot controllers. return '2nd slot empty'. we already checked
* if the interrupt is tied to another function.
*/
if (socket->dev->device != func->device)
goto out;
slot2 = pci_get_drvdata(func);
if (!slot2)
goto out;
/* check state */
yenta_get_status(&slot2->socket, &state);
if (state & SS_DETECT) {
ret = 0;
goto out;
}
out:
pci_dev_put(func);
return ret;
}
/*
* TI specifiy parts for the power hook.
*
* some TI's with some CB's produces interrupt storm on power on. it has been
* seen with atheros wlan cards on TI1225 and TI1410. solution is simply to
* disable any CB interrupts during this time.
*/
static int ti12xx_power_hook(struct pcmcia_socket *sock, int operation)
{
struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
u32 mfunc, devctl, sysctl;
u8 gpio3;
/* only POWER_PRE and POWER_POST are interesting */
if ((operation != HOOK_POWER_PRE) && (operation != HOOK_POWER_POST))
return 0;
devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
mfunc = config_readl(socket, TI122X_MFUNC);
/*
* all serial/tied: only disable when modparm set. always doing it
* would mean a regression for working setups 'cos it disables the
* interrupts for both both slots on 2-slot controllers
* (and users of single slot controllers where it's save have to
* live with setting the modparm, most don't have to anyway)
*/
if (((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) &&
(pwr_irqs_off || ti12xx_2nd_slot_empty(socket))) {
switch (socket->dev->device) {
case PCI_DEVICE_ID_TI_1250:
case PCI_DEVICE_ID_TI_1251A:
case PCI_DEVICE_ID_TI_1251B:
case PCI_DEVICE_ID_TI_1450:
case PCI_DEVICE_ID_TI_1451A:
case PCI_DEVICE_ID_TI_4450:
case PCI_DEVICE_ID_TI_4451:
/* these chips have no IRQSER setting in MFUNC3 */
break;
default:
if (operation == HOOK_POWER_PRE)
mfunc = (mfunc & ~TI122X_MFUNC3_MASK);
else
mfunc = (mfunc & ~TI122X_MFUNC3_MASK) | TI122X_MFUNC3_IRQSER;
}
return 0;
}
/* do the job differently for func0/1 */
if ((PCI_FUNC(socket->dev->devfn) == 0) ||
((sysctl & TI122X_SCR_INTRTIE) &&
(pwr_irqs_off || ti12xx_2nd_slot_empty(socket)))) {
/* some bridges are different */
switch (socket->dev->device) {
case PCI_DEVICE_ID_TI_1250:
case PCI_DEVICE_ID_TI_1251A:
case PCI_DEVICE_ID_TI_1251B:
case PCI_DEVICE_ID_TI_1450:
/* those oldies use gpio3 for INTA */
gpio3 = config_readb(socket, TI1250_GPIO3_CONTROL);
if (operation == HOOK_POWER_PRE)
gpio3 = (gpio3 & ~TI1250_GPIO_MODE_MASK) | 0x40;
else
gpio3 &= ~TI1250_GPIO_MODE_MASK;
config_writeb(socket, TI1250_GPIO3_CONTROL, gpio3);
break;
default:
/* all new bridges are the same */
if (operation == HOOK_POWER_PRE)
mfunc &= ~TI122X_MFUNC0_MASK;
else
mfunc |= TI122X_MFUNC0_INTA;
config_writel(socket, TI122X_MFUNC, mfunc);
}
} else {
switch (socket->dev->device) {
case PCI_DEVICE_ID_TI_1251A:
case PCI_DEVICE_ID_TI_1251B:
case PCI_DEVICE_ID_TI_1450:
/* those have INTA elsewhere and INTB in MFUNC0 */
if (operation == HOOK_POWER_PRE)
mfunc &= ~TI122X_MFUNC0_MASK;
else
mfunc |= TI125X_MFUNC0_INTB;
config_writel(socket, TI122X_MFUNC, mfunc);
break;
default:
/* all new bridges are the same */
if (operation == HOOK_POWER_PRE)
mfunc &= ~TI122X_MFUNC1_MASK;
else
mfunc |= TI122X_MFUNC1_INTB;
config_writel(socket, TI122X_MFUNC, mfunc);
}
}
return 0;
}
static int ti12xx_override(struct yenta_socket *socket)
{
u32 val, val_orig;
/* make sure that memory burst is active */
val_orig = val = config_readl(socket, TI113X_SYSTEM_CONTROL);
if (disable_clkrun && PCI_FUNC(socket->dev->devfn) == 0) {
dev_printk(KERN_INFO, &socket->dev->dev,
"Disabling CLKRUN feature\n");
val |= TI113X_SCR_KEEPCLK;
}
if (!(val & TI122X_SCR_MRBURSTUP)) {
dev_printk(KERN_INFO, &socket->dev->dev,
"Enabling burst memory read transactions\n");
val |= TI122X_SCR_MRBURSTUP;
}
if (val_orig != val)
config_writel(socket, TI113X_SYSTEM_CONTROL, val);
/*
* Yenta expects controllers to use CSCINT to route
* CSC interrupts to PCI rather than INTVAL.
*/
val = config_readb(socket, TI1250_DIAGNOSTIC);
dev_printk(KERN_INFO, &socket->dev->dev,
"Using %s to route CSC interrupts to PCI\n",
(val & TI1250_DIAG_PCI_CSC) ? "CSCINT" : "INTVAL");
dev_printk(KERN_INFO, &socket->dev->dev,
"Routing CardBus interrupts to %s\n",
(val & TI1250_DIAG_PCI_IREQ) ? "PCI" : "ISA");
/* do irqrouting, depending on function */
if (PCI_FUNC(socket->dev->devfn) == 0)
ti12xx_irqroute_func0(socket);
else
ti12xx_irqroute_func1(socket);
/* install power hook */
socket->socket.power_hook = ti12xx_power_hook;
return ti_override(socket);
}
static int ti1250_override(struct yenta_socket *socket)
{
u8 old, diag;
old = config_readb(socket, TI1250_DIAGNOSTIC);
diag = old & ~(TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ);
if (socket->cb_irq)
diag |= TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ;
if (diag != old) {
dev_printk(KERN_INFO, &socket->dev->dev,
"adjusting diagnostic: %02x -> %02x\n",
old, diag);
config_writeb(socket, TI1250_DIAGNOSTIC, diag);
}
return ti12xx_override(socket);
}
/**
* EnE specific part. EnE bridges are register compatible with TI bridges but
* have their own test registers and more important their own little problems.
* Some fixup code to make everybody happy (TM).
*/
#ifdef CONFIG_YENTA_ENE_TUNE
/*
* set/clear various test bits:
* Defaults to clear the bit.
* - mask (u8) defines what bits to change
* - bits (u8) is the values to change them to
* -> it's
* current = (current & ~mask) | bits
*/
/* pci ids of devices that wants to have the bit set */
#define DEVID(_vend,_dev,_subvend,_subdev,mask,bits) { \
.vendor = _vend, \
.device = _dev, \
.subvendor = _subvend, \
.subdevice = _subdev, \
.driver_data = ((mask) << 8 | (bits)), \
}
static struct pci_device_id ene_tune_tbl[] = {
/* Echo Audio products based on motorola DSP56301 and DSP56361 */
DEVID(PCI_VENDOR_ID_MOTOROLA, 0x1801, 0xECC0, PCI_ANY_ID,
ENE_TEST_C9_TLTENABLE | ENE_TEST_C9_PFENABLE, ENE_TEST_C9_TLTENABLE),
DEVID(PCI_VENDOR_ID_MOTOROLA, 0x3410, 0xECC0, PCI_ANY_ID,
ENE_TEST_C9_TLTENABLE | ENE_TEST_C9_PFENABLE, ENE_TEST_C9_TLTENABLE),
{}
};
static void ene_tune_bridge(struct pcmcia_socket *sock, struct pci_bus *bus)
{
struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
struct pci_dev *dev;
struct pci_device_id *id = NULL;
u8 test_c9, old_c9, mask, bits;
list_for_each_entry(dev, &bus->devices, bus_list) {
id = (struct pci_device_id *) pci_match_id(ene_tune_tbl, dev);
if (id)
break;
}
test_c9 = old_c9 = config_readb(socket, ENE_TEST_C9);
if (id) {
mask = (id->driver_data >> 8) & 0xFF;
bits = id->driver_data & 0xFF;
test_c9 = (test_c9 & ~mask) | bits;
}
else
/* default to clear TLTEnable bit, old behaviour */
test_c9 &= ~ENE_TEST_C9_TLTENABLE;
dev_printk(KERN_INFO, &socket->dev->dev,
"EnE: chaning testregister 0xC9, %02x -> %02x\n",
old_c9, test_c9);
config_writeb(socket, ENE_TEST_C9, test_c9);
}
static int ene_override(struct yenta_socket *socket)
{
/* install tune_bridge() function */
socket->socket.tune_bridge = ene_tune_bridge;
return ti1250_override(socket);
}
#else
# define ene_override ti1250_override
#endif /* !CONFIG_YENTA_ENE_TUNE */
#endif /* _LINUX_TI113X_H */

152
drivers/pcmcia/topic.h Normal file
View file

@ -0,0 +1,152 @@
/*
* topic.h 1.8 1999/08/28 04:01:47
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License
* at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and
* limitations under the License.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in which
* case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use
* your version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the MPL or the GPL.
* topic.h $Release$ 1999/08/28 04:01:47
*/
#ifndef _LINUX_TOPIC_H
#define _LINUX_TOPIC_H
/* Register definitions for Toshiba ToPIC95/97/100 controllers */
#define TOPIC_SOCKET_CONTROL 0x0090 /* 32 bit */
#define TOPIC_SCR_IRQSEL 0x00000001
#define TOPIC_SLOT_CONTROL 0x00a0 /* 8 bit */
#define TOPIC_SLOT_SLOTON 0x80
#define TOPIC_SLOT_SLOTEN 0x40
#define TOPIC_SLOT_ID_LOCK 0x20
#define TOPIC_SLOT_ID_WP 0x10
#define TOPIC_SLOT_PORT_MASK 0x0c
#define TOPIC_SLOT_PORT_SHIFT 2
#define TOPIC_SLOT_OFS_MASK 0x03
#define TOPIC_CARD_CONTROL 0x00a1 /* 8 bit */
#define TOPIC_CCR_INTB 0x20
#define TOPIC_CCR_INTA 0x10
#define TOPIC_CCR_CLOCK 0x0c
#define TOPIC_CCR_PCICLK 0x0c
#define TOPIC_CCR_PCICLK_2 0x08
#define TOPIC_CCR_CCLK 0x04
#define TOPIC97_INT_CONTROL 0x00a1 /* 8 bit */
#define TOPIC97_ICR_INTB 0x20
#define TOPIC97_ICR_INTA 0x10
#define TOPIC97_ICR_STSIRQNP 0x04
#define TOPIC97_ICR_IRQNP 0x02
#define TOPIC97_ICR_IRQSEL 0x01
#define TOPIC_CARD_DETECT 0x00a3 /* 8 bit */
#define TOPIC_CDR_MODE_PC32 0x80
#define TOPIC_CDR_VS1 0x04
#define TOPIC_CDR_VS2 0x02
#define TOPIC_CDR_SW_DETECT 0x01
#define TOPIC_REGISTER_CONTROL 0x00a4 /* 32 bit */
#define TOPIC_RCR_RESUME_RESET 0x80000000
#define TOPIC_RCR_REMOVE_RESET 0x40000000
#define TOPIC97_RCR_CLKRUN_ENA 0x20000000
#define TOPIC97_RCR_TESTMODE 0x10000000
#define TOPIC97_RCR_IOPLUP 0x08000000
#define TOPIC_RCR_BUFOFF_PWROFF 0x02000000
#define TOPIC_RCR_BUFOFF_SIGOFF 0x01000000
#define TOPIC97_RCR_CB_DEV_MASK 0x0000f800
#define TOPIC97_RCR_CB_DEV_SHIFT 11
#define TOPIC97_RCR_RI_DISABLE 0x00000004
#define TOPIC97_RCR_CAUDIO_OFF 0x00000002
#define TOPIC_RCR_CAUDIO_INVERT 0x00000001
#define TOPIC97_MISC1 0x00ad /* 8bit */
#define TOPIC97_MISC1_CLOCKRUN_ENABLE 0x80
#define TOPIC97_MISC1_CLOCKRUN_MODE 0x40
#define TOPIC97_MISC1_DETECT_REQ_ENA 0x10
#define TOPIC97_MISC1_SCK_CLEAR_DIS 0x04
#define TOPIC97_MISC1_R2_LOW_ENABLE 0x10
#define TOPIC97_MISC2 0x00ae /* 8 bit */
#define TOPIC97_MISC2_SPWRCLK_MASK 0x70
#define TOPIC97_MISC2_SPWRMOD 0x08
#define TOPIC97_MISC2_SPWR_ENABLE 0x04
#define TOPIC97_MISC2_ZV_MODE 0x02
#define TOPIC97_MISC2_ZV_ENABLE 0x01
#define TOPIC97_ZOOM_VIDEO_CONTROL 0x009c /* 8 bit */
#define TOPIC97_ZV_CONTROL_ENABLE 0x01
#define TOPIC97_AUDIO_VIDEO_SWITCH 0x003c /* 8 bit */
#define TOPIC97_AVS_AUDIO_CONTROL 0x02
#define TOPIC97_AVS_VIDEO_CONTROL 0x01
#define TOPIC_EXCA_IF_CONTROL 0x3e /* 8 bit */
#define TOPIC_EXCA_IFC_33V_ENA 0x01
static void topic97_zoom_video(struct pcmcia_socket *sock, int onoff)
{
struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
u8 reg_zv, reg;
reg_zv = config_readb(socket, TOPIC97_ZOOM_VIDEO_CONTROL);
if (onoff) {
reg_zv |= TOPIC97_ZV_CONTROL_ENABLE;
config_writeb(socket, TOPIC97_ZOOM_VIDEO_CONTROL, reg_zv);
reg = config_readb(socket, TOPIC97_AUDIO_VIDEO_SWITCH);
reg |= TOPIC97_AVS_AUDIO_CONTROL | TOPIC97_AVS_VIDEO_CONTROL;
config_writeb(socket, TOPIC97_AUDIO_VIDEO_SWITCH, reg);
} else {
reg_zv &= ~TOPIC97_ZV_CONTROL_ENABLE;
config_writeb(socket, TOPIC97_ZOOM_VIDEO_CONTROL, reg_zv);
reg = config_readb(socket, TOPIC97_AUDIO_VIDEO_SWITCH);
reg &= ~(TOPIC97_AVS_AUDIO_CONTROL | TOPIC97_AVS_VIDEO_CONTROL);
config_writeb(socket, TOPIC97_AUDIO_VIDEO_SWITCH, reg);
}
}
static int topic97_override(struct yenta_socket *socket)
{
/* ToPIC97/100 support ZV */
socket->socket.zoom_video = topic97_zoom_video;
return 0;
}
static int topic95_override(struct yenta_socket *socket)
{
u8 fctrl;
/* enable 3.3V support for 16bit cards */
fctrl = exca_readb(socket, TOPIC_EXCA_IF_CONTROL);
exca_writeb(socket, TOPIC_EXCA_IF_CONTROL, fctrl | TOPIC_EXCA_IFC_33V_ENA);
/* tell yenta to use exca registers to power 16bit cards */
socket->flags |= YENTA_16BIT_POWER_EXCA | YENTA_16BIT_POWER_DF;
return 0;
}
#endif /* _LINUX_TOPIC_H */

106
drivers/pcmcia/vg468.h Normal file
View file

@ -0,0 +1,106 @@
/*
* vg468.h 1.11 1999/10/25 20:03:34
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License
* at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and
* limitations under the License.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in which
* case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use
* your version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the MPL or the GPL.
*/
#ifndef _LINUX_VG468_H
#define _LINUX_VG468_H
/* Special bit in I365_IDENT used for Vadem chip detection */
#define I365_IDENT_VADEM 0x08
/* Special definitions in I365_POWER */
#define VG468_VPP2_MASK 0x0c
#define VG468_VPP2_5V 0x04
#define VG468_VPP2_12V 0x08
/* Unique Vadem registers */
#define VG469_VSENSE 0x1f /* Card voltage sense */
#define VG469_VSELECT 0x2f /* Card voltage select */
#define VG468_CTL 0x38 /* Control register */
#define VG468_TIMER 0x39 /* Timer control */
#define VG468_MISC 0x3a /* Miscellaneous */
#define VG468_GPIO_CFG 0x3b /* GPIO configuration */
#define VG469_EXT_MODE 0x3c /* Extended mode register */
#define VG468_SELECT 0x3d /* Programmable chip select */
#define VG468_SELECT_CFG 0x3e /* Chip select configuration */
#define VG468_ATA 0x3f /* ATA control */
/* Flags for VG469_VSENSE */
#define VG469_VSENSE_A_VS1 0x01
#define VG469_VSENSE_A_VS2 0x02
#define VG469_VSENSE_B_VS1 0x04
#define VG469_VSENSE_B_VS2 0x08
/* Flags for VG469_VSELECT */
#define VG469_VSEL_VCC 0x03
#define VG469_VSEL_5V 0x00
#define VG469_VSEL_3V 0x03
#define VG469_VSEL_MAX 0x0c
#define VG469_VSEL_EXT_STAT 0x10
#define VG469_VSEL_EXT_BUS 0x20
#define VG469_VSEL_MIXED 0x40
#define VG469_VSEL_ISA 0x80
/* Flags for VG468_CTL */
#define VG468_CTL_SLOW 0x01 /* 600ns memory timing */
#define VG468_CTL_ASYNC 0x02 /* Asynchronous bus clocking */
#define VG468_CTL_TSSI 0x08 /* Tri-state some outputs */
#define VG468_CTL_DELAY 0x10 /* Card detect debounce */
#define VG468_CTL_INPACK 0x20 /* Obey INPACK signal? */
#define VG468_CTL_POLARITY 0x40 /* VCCEN polarity */
#define VG468_CTL_COMPAT 0x80 /* Compatibility stuff */
#define VG469_CTL_WS_COMPAT 0x04 /* Wait state compatibility */
#define VG469_CTL_STRETCH 0x10 /* LED stretch */
/* Flags for VG468_TIMER */
#define VG468_TIMER_ZEROPWR 0x10 /* Zero power control */
#define VG468_TIMER_SIGEN 0x20 /* Power up */
#define VG468_TIMER_STATUS 0x40 /* Activity timer status */
#define VG468_TIMER_RES 0x80 /* Timer resolution */
#define VG468_TIMER_MASK 0x0f /* Activity timer timeout */
/* Flags for VG468_MISC */
#define VG468_MISC_GPIO 0x04 /* General-purpose IO */
#define VG468_MISC_DMAWSB 0x08 /* DMA wait state control */
#define VG469_MISC_LEDENA 0x10 /* LED enable */
#define VG468_MISC_VADEMREV 0x40 /* Vadem revision control */
#define VG468_MISC_UNLOCK 0x80 /* Unique register lock */
/* Flags for VG469_EXT_MODE_A */
#define VG469_MODE_VPPST 0x03 /* Vpp steering control */
#define VG469_MODE_INT_SENSE 0x04 /* Internal voltage sense */
#define VG469_MODE_CABLE 0x08
#define VG469_MODE_COMPAT 0x10 /* i82365sl B or DF step */
#define VG469_MODE_TEST 0x20
#define VG469_MODE_RIO 0x40 /* Steer RIO to INTR? */
/* Flags for VG469_EXT_MODE_B */
#define VG469_MODE_B_3V 0x01 /* 3.3v for socket B */
#endif /* _LINUX_VG468_H */

View file

@ -0,0 +1,759 @@
/*
* vrc4171_card.c, NEC VRC4171 Card Controller driver for Socket Services.
*
* Copyright (C) 2003-2005 Yoichi Yuasa <yuasa@linux-mips.org>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <pcmcia/ss.h>
#include "i82365.h"
MODULE_DESCRIPTION("NEC VRC4171 Card Controllers driver for Socket Services");
MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
MODULE_LICENSE("GPL");
#define CARD_MAX_SLOTS 2
#define CARD_SLOTA 0
#define CARD_SLOTB 1
#define CARD_SLOTB_OFFSET 0x40
#define CARD_MEM_START 0x10000000
#define CARD_MEM_END 0x13ffffff
#define CARD_MAX_MEM_OFFSET 0x3ffffff
#define CARD_MAX_MEM_SPEED 1000
#define CARD_CONTROLLER_INDEX 0x03e0
#define CARD_CONTROLLER_DATA 0x03e1
/* Power register */
#define VPP_GET_VCC 0x01
#define POWER_ENABLE 0x10
#define CARD_VOLTAGE_SENSE 0x1f
#define VCC_3VORXV_CAPABLE 0x00
#define VCC_XV_ONLY 0x01
#define VCC_3V_CAPABLE 0x02
#define VCC_5V_ONLY 0x03
#define CARD_VOLTAGE_SELECT 0x2f
#define VCC_3V 0x01
#define VCC_5V 0x00
#define VCC_XV 0x02
#define VCC_STATUS_3V 0x02
#define VCC_STATUS_5V 0x01
#define VCC_STATUS_XV 0x03
#define GLOBAL_CONTROL 0x1e
#define EXWRBK 0x04
#define IRQPM_EN 0x08
#define CLRPMIRQ 0x10
#define INTERRUPT_STATUS 0x05fa
#define IRQ_A 0x02
#define IRQ_B 0x04
#define CONFIGURATION1 0x05fe
#define SLOTB_CONFIG 0xc000
#define SLOTB_NONE 0x0000
#define SLOTB_PCCARD 0x4000
#define SLOTB_CF 0x8000
#define SLOTB_FLASHROM 0xc000
#define CARD_CONTROLLER_START CARD_CONTROLLER_INDEX
#define CARD_CONTROLLER_END CARD_CONTROLLER_DATA
#define IO_MAX_MAPS 2
#define MEM_MAX_MAPS 5
typedef enum {
SLOT_PROBE = 0,
SLOT_NOPROBE_IO,
SLOT_NOPROBE_MEM,
SLOT_NOPROBE_ALL,
SLOT_INITIALIZED,
} vrc4171_slot_t;
typedef enum {
SLOTB_IS_NONE,
SLOTB_IS_PCCARD,
SLOTB_IS_CF,
SLOTB_IS_FLASHROM,
} vrc4171_slotb_t;
typedef struct vrc4171_socket {
vrc4171_slot_t slot;
struct pcmcia_socket pcmcia_socket;
char name[24];
int csc_irq;
int io_irq;
spinlock_t lock;
} vrc4171_socket_t;
static vrc4171_socket_t vrc4171_sockets[CARD_MAX_SLOTS];
static vrc4171_slotb_t vrc4171_slotb = SLOTB_IS_NONE;
static char vrc4171_card_name[] = "NEC VRC4171 Card Controller";
static unsigned int vrc4171_irq;
static uint16_t vrc4171_irq_mask = 0xdeb8;
static struct resource vrc4171_card_resource[3] = {
{ .name = vrc4171_card_name,
.start = CARD_CONTROLLER_START,
.end = CARD_CONTROLLER_END,
.flags = IORESOURCE_IO, },
{ .name = vrc4171_card_name,
.start = INTERRUPT_STATUS,
.end = INTERRUPT_STATUS,
.flags = IORESOURCE_IO, },
{ .name = vrc4171_card_name,
.start = CONFIGURATION1,
.end = CONFIGURATION1,
.flags = IORESOURCE_IO, },
};
static struct platform_device vrc4171_card_device = {
.name = vrc4171_card_name,
.id = 0,
.num_resources = 3,
.resource = vrc4171_card_resource,
};
static inline uint16_t vrc4171_get_irq_status(void)
{
return inw(INTERRUPT_STATUS);
}
static inline void vrc4171_set_multifunction_pin(vrc4171_slotb_t config)
{
uint16_t config1;
config1 = inw(CONFIGURATION1);
config1 &= ~SLOTB_CONFIG;
switch (config) {
case SLOTB_IS_NONE:
config1 |= SLOTB_NONE;
break;
case SLOTB_IS_PCCARD:
config1 |= SLOTB_PCCARD;
break;
case SLOTB_IS_CF:
config1 |= SLOTB_CF;
break;
case SLOTB_IS_FLASHROM:
config1 |= SLOTB_FLASHROM;
break;
default:
break;
}
outw(config1, CONFIGURATION1);
}
static inline uint8_t exca_read_byte(int slot, uint8_t index)
{
if (slot == CARD_SLOTB)
index += CARD_SLOTB_OFFSET;
outb(index, CARD_CONTROLLER_INDEX);
return inb(CARD_CONTROLLER_DATA);
}
static inline uint16_t exca_read_word(int slot, uint8_t index)
{
uint16_t data;
if (slot == CARD_SLOTB)
index += CARD_SLOTB_OFFSET;
outb(index++, CARD_CONTROLLER_INDEX);
data = inb(CARD_CONTROLLER_DATA);
outb(index, CARD_CONTROLLER_INDEX);
data |= ((uint16_t)inb(CARD_CONTROLLER_DATA)) << 8;
return data;
}
static inline uint8_t exca_write_byte(int slot, uint8_t index, uint8_t data)
{
if (slot == CARD_SLOTB)
index += CARD_SLOTB_OFFSET;
outb(index, CARD_CONTROLLER_INDEX);
outb(data, CARD_CONTROLLER_DATA);
return data;
}
static inline uint16_t exca_write_word(int slot, uint8_t index, uint16_t data)
{
if (slot == CARD_SLOTB)
index += CARD_SLOTB_OFFSET;
outb(index++, CARD_CONTROLLER_INDEX);
outb(data, CARD_CONTROLLER_DATA);
outb(index, CARD_CONTROLLER_INDEX);
outb((uint8_t)(data >> 8), CARD_CONTROLLER_DATA);
return data;
}
static inline int search_nonuse_irq(void)
{
int i;
for (i = 0; i < 16; i++) {
if (vrc4171_irq_mask & (1 << i)) {
vrc4171_irq_mask &= ~(1 << i);
return i;
}
}
return -1;
}
static int pccard_init(struct pcmcia_socket *sock)
{
vrc4171_socket_t *socket;
unsigned int slot;
sock->features |= SS_CAP_PCCARD | SS_CAP_PAGE_REGS;
sock->irq_mask = 0;
sock->map_size = 0x1000;
sock->pci_irq = vrc4171_irq;
slot = sock->sock;
socket = &vrc4171_sockets[slot];
socket->csc_irq = search_nonuse_irq();
socket->io_irq = search_nonuse_irq();
spin_lock_init(&socket->lock);
return 0;
}
static int pccard_get_status(struct pcmcia_socket *sock, u_int *value)
{
unsigned int slot;
uint8_t status, sense;
u_int val = 0;
if (sock == NULL || sock->sock >= CARD_MAX_SLOTS || value == NULL)
return -EINVAL;
slot = sock->sock;
status = exca_read_byte(slot, I365_STATUS);
if (exca_read_byte(slot, I365_INTCTL) & I365_PC_IOCARD) {
if (status & I365_CS_STSCHG)
val |= SS_STSCHG;
} else {
if (!(status & I365_CS_BVD1))
val |= SS_BATDEAD;
else if ((status & (I365_CS_BVD1 | I365_CS_BVD2)) == I365_CS_BVD1)
val |= SS_BATWARN;
}
if ((status & I365_CS_DETECT) == I365_CS_DETECT)
val |= SS_DETECT;
if (status & I365_CS_WRPROT)
val |= SS_WRPROT;
if (status & I365_CS_READY)
val |= SS_READY;
if (status & I365_CS_POWERON)
val |= SS_POWERON;
sense = exca_read_byte(slot, CARD_VOLTAGE_SENSE);
switch (sense) {
case VCC_3VORXV_CAPABLE:
val |= SS_3VCARD | SS_XVCARD;
break;
case VCC_XV_ONLY:
val |= SS_XVCARD;
break;
case VCC_3V_CAPABLE:
val |= SS_3VCARD;
break;
default:
/* 5V only */
break;
}
*value = val;
return 0;
}
static inline uint8_t set_Vcc_value(u_char Vcc)
{
switch (Vcc) {
case 33:
return VCC_3V;
case 50:
return VCC_5V;
}
/* Small voltage is chosen for safety. */
return VCC_3V;
}
static int pccard_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
{
vrc4171_socket_t *socket;
unsigned int slot;
uint8_t voltage, power, control, cscint;
if (sock == NULL || sock->sock >= CARD_MAX_SLOTS ||
(state->Vpp != state->Vcc && state->Vpp != 0) ||
(state->Vcc != 50 && state->Vcc != 33 && state->Vcc != 0))
return -EINVAL;
slot = sock->sock;
socket = &vrc4171_sockets[slot];
spin_lock_irq(&socket->lock);
voltage = set_Vcc_value(state->Vcc);
exca_write_byte(slot, CARD_VOLTAGE_SELECT, voltage);
power = POWER_ENABLE;
if (state->Vpp == state->Vcc)
power |= VPP_GET_VCC;
if (state->flags & SS_OUTPUT_ENA)
power |= I365_PWR_OUT;
exca_write_byte(slot, I365_POWER, power);
control = 0;
if (state->io_irq != 0)
control |= socket->io_irq;
if (state->flags & SS_IOCARD)
control |= I365_PC_IOCARD;
if (state->flags & SS_RESET)
control &= ~I365_PC_RESET;
else
control |= I365_PC_RESET;
exca_write_byte(slot, I365_INTCTL, control);
cscint = 0;
exca_write_byte(slot, I365_CSCINT, cscint);
exca_read_byte(slot, I365_CSC); /* clear CardStatus change */
if (state->csc_mask != 0)
cscint |= socket->csc_irq << 8;
if (state->flags & SS_IOCARD) {
if (state->csc_mask & SS_STSCHG)
cscint |= I365_CSC_STSCHG;
} else {
if (state->csc_mask & SS_BATDEAD)
cscint |= I365_CSC_BVD1;
if (state->csc_mask & SS_BATWARN)
cscint |= I365_CSC_BVD2;
}
if (state->csc_mask & SS_READY)
cscint |= I365_CSC_READY;
if (state->csc_mask & SS_DETECT)
cscint |= I365_CSC_DETECT;
exca_write_byte(slot, I365_CSCINT, cscint);
spin_unlock_irq(&socket->lock);
return 0;
}
static int pccard_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
{
unsigned int slot;
uint8_t ioctl, addrwin;
u_char map;
if (sock == NULL || sock->sock >= CARD_MAX_SLOTS ||
io == NULL || io->map >= IO_MAX_MAPS ||
io->start > 0xffff || io->stop > 0xffff || io->start > io->stop)
return -EINVAL;
slot = sock->sock;
map = io->map;
addrwin = exca_read_byte(slot, I365_ADDRWIN);
if (addrwin & I365_ENA_IO(map)) {
addrwin &= ~I365_ENA_IO(map);
exca_write_byte(slot, I365_ADDRWIN, addrwin);
}
exca_write_word(slot, I365_IO(map)+I365_W_START, io->start);
exca_write_word(slot, I365_IO(map)+I365_W_STOP, io->stop);
ioctl = 0;
if (io->speed > 0)
ioctl |= I365_IOCTL_WAIT(map);
if (io->flags & MAP_16BIT)
ioctl |= I365_IOCTL_16BIT(map);
if (io->flags & MAP_AUTOSZ)
ioctl |= I365_IOCTL_IOCS16(map);
if (io->flags & MAP_0WS)
ioctl |= I365_IOCTL_0WS(map);
exca_write_byte(slot, I365_IOCTL, ioctl);
if (io->flags & MAP_ACTIVE) {
addrwin |= I365_ENA_IO(map);
exca_write_byte(slot, I365_ADDRWIN, addrwin);
}
return 0;
}
static int pccard_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem)
{
unsigned int slot;
uint16_t start, stop, offset;
uint8_t addrwin;
u_char map;
if (sock == NULL || sock->sock >= CARD_MAX_SLOTS ||
mem == NULL || mem->map >= MEM_MAX_MAPS ||
mem->res->start < CARD_MEM_START || mem->res->start > CARD_MEM_END ||
mem->res->end < CARD_MEM_START || mem->res->end > CARD_MEM_END ||
mem->res->start > mem->res->end ||
mem->card_start > CARD_MAX_MEM_OFFSET ||
mem->speed > CARD_MAX_MEM_SPEED)
return -EINVAL;
slot = sock->sock;
map = mem->map;
addrwin = exca_read_byte(slot, I365_ADDRWIN);
if (addrwin & I365_ENA_MEM(map)) {
addrwin &= ~I365_ENA_MEM(map);
exca_write_byte(slot, I365_ADDRWIN, addrwin);
}
start = (mem->res->start >> 12) & 0x3fff;
if (mem->flags & MAP_16BIT)
start |= I365_MEM_16BIT;
exca_write_word(slot, I365_MEM(map)+I365_W_START, start);
stop = (mem->res->end >> 12) & 0x3fff;
switch (mem->speed) {
case 0:
break;
case 1:
stop |= I365_MEM_WS0;
break;
case 2:
stop |= I365_MEM_WS1;
break;
default:
stop |= I365_MEM_WS0 | I365_MEM_WS1;
break;
}
exca_write_word(slot, I365_MEM(map)+I365_W_STOP, stop);
offset = (mem->card_start >> 12) & 0x3fff;
if (mem->flags & MAP_ATTRIB)
offset |= I365_MEM_REG;
if (mem->flags & MAP_WRPROT)
offset |= I365_MEM_WRPROT;
exca_write_word(slot, I365_MEM(map)+I365_W_OFF, offset);
if (mem->flags & MAP_ACTIVE) {
addrwin |= I365_ENA_MEM(map);
exca_write_byte(slot, I365_ADDRWIN, addrwin);
}
return 0;
}
static struct pccard_operations vrc4171_pccard_operations = {
.init = pccard_init,
.get_status = pccard_get_status,
.set_socket = pccard_set_socket,
.set_io_map = pccard_set_io_map,
.set_mem_map = pccard_set_mem_map,
};
static inline unsigned int get_events(int slot)
{
unsigned int events = 0;
uint8_t status, csc;
status = exca_read_byte(slot, I365_STATUS);
csc = exca_read_byte(slot, I365_CSC);
if (exca_read_byte(slot, I365_INTCTL) & I365_PC_IOCARD) {
if ((csc & I365_CSC_STSCHG) && (status & I365_CS_STSCHG))
events |= SS_STSCHG;
} else {
if (csc & (I365_CSC_BVD1 | I365_CSC_BVD2)) {
if (!(status & I365_CS_BVD1))
events |= SS_BATDEAD;
else if ((status & (I365_CS_BVD1 | I365_CS_BVD2)) == I365_CS_BVD1)
events |= SS_BATWARN;
}
}
if ((csc & I365_CSC_READY) && (status & I365_CS_READY))
events |= SS_READY;
if ((csc & I365_CSC_DETECT) && ((status & I365_CS_DETECT) == I365_CS_DETECT))
events |= SS_DETECT;
return events;
}
static irqreturn_t pccard_interrupt(int irq, void *dev_id)
{
vrc4171_socket_t *socket;
unsigned int events;
irqreturn_t retval = IRQ_NONE;
uint16_t status;
status = vrc4171_get_irq_status();
if (status & IRQ_A) {
socket = &vrc4171_sockets[CARD_SLOTA];
if (socket->slot == SLOT_INITIALIZED) {
if (status & (1 << socket->csc_irq)) {
events = get_events(CARD_SLOTA);
if (events != 0) {
pcmcia_parse_events(&socket->pcmcia_socket, events);
retval = IRQ_HANDLED;
}
}
}
}
if (status & IRQ_B) {
socket = &vrc4171_sockets[CARD_SLOTB];
if (socket->slot == SLOT_INITIALIZED) {
if (status & (1 << socket->csc_irq)) {
events = get_events(CARD_SLOTB);
if (events != 0) {
pcmcia_parse_events(&socket->pcmcia_socket, events);
retval = IRQ_HANDLED;
}
}
}
}
return retval;
}
static inline void reserve_using_irq(int slot)
{
unsigned int irq;
irq = exca_read_byte(slot, I365_INTCTL);
irq &= 0x0f;
vrc4171_irq_mask &= ~(1 << irq);
irq = exca_read_byte(slot, I365_CSCINT);
irq = (irq & 0xf0) >> 4;
vrc4171_irq_mask &= ~(1 << irq);
}
static int vrc4171_add_sockets(void)
{
vrc4171_socket_t *socket;
int slot, retval;
for (slot = 0; slot < CARD_MAX_SLOTS; slot++) {
if (slot == CARD_SLOTB && vrc4171_slotb == SLOTB_IS_NONE)
continue;
socket = &vrc4171_sockets[slot];
if (socket->slot != SLOT_PROBE) {
uint8_t addrwin;
switch (socket->slot) {
case SLOT_NOPROBE_MEM:
addrwin = exca_read_byte(slot, I365_ADDRWIN);
addrwin &= 0x1f;
exca_write_byte(slot, I365_ADDRWIN, addrwin);
break;
case SLOT_NOPROBE_IO:
addrwin = exca_read_byte(slot, I365_ADDRWIN);
addrwin &= 0xc0;
exca_write_byte(slot, I365_ADDRWIN, addrwin);
break;
default:
break;
}
reserve_using_irq(slot);
continue;
}
sprintf(socket->name, "NEC VRC4171 Card Slot %1c", 'A' + slot);
socket->pcmcia_socket.dev.parent = &vrc4171_card_device.dev;
socket->pcmcia_socket.ops = &vrc4171_pccard_operations;
socket->pcmcia_socket.owner = THIS_MODULE;
retval = pcmcia_register_socket(&socket->pcmcia_socket);
if (retval < 0)
return retval;
exca_write_byte(slot, I365_ADDRWIN, 0);
exca_write_byte(slot, GLOBAL_CONTROL, 0);
socket->slot = SLOT_INITIALIZED;
}
return 0;
}
static void vrc4171_remove_sockets(void)
{
vrc4171_socket_t *socket;
int slot;
for (slot = 0; slot < CARD_MAX_SLOTS; slot++) {
if (slot == CARD_SLOTB && vrc4171_slotb == SLOTB_IS_NONE)
continue;
socket = &vrc4171_sockets[slot];
if (socket->slot == SLOT_INITIALIZED)
pcmcia_unregister_socket(&socket->pcmcia_socket);
socket->slot = SLOT_PROBE;
}
}
static int vrc4171_card_setup(char *options)
{
if (options == NULL || *options == '\0')
return 1;
if (strncmp(options, "irq:", 4) == 0) {
int irq;
options += 4;
irq = simple_strtoul(options, &options, 0);
if (irq >= 0 && irq < nr_irqs)
vrc4171_irq = irq;
if (*options != ',')
return 1;
options++;
}
if (strncmp(options, "slota:", 6) == 0) {
options += 6;
if (*options != '\0') {
if (strncmp(options, "memnoprobe", 10) == 0) {
vrc4171_sockets[CARD_SLOTA].slot = SLOT_NOPROBE_MEM;
options += 10;
} else if (strncmp(options, "ionoprobe", 9) == 0) {
vrc4171_sockets[CARD_SLOTA].slot = SLOT_NOPROBE_IO;
options += 9;
} else if ( strncmp(options, "noprobe", 7) == 0) {
vrc4171_sockets[CARD_SLOTA].slot = SLOT_NOPROBE_ALL;
options += 7;
}
if (*options != ',')
return 1;
options++;
} else
return 1;
}
if (strncmp(options, "slotb:", 6) == 0) {
options += 6;
if (*options != '\0') {
if (strncmp(options, "pccard", 6) == 0) {
vrc4171_slotb = SLOTB_IS_PCCARD;
options += 6;
} else if (strncmp(options, "cf", 2) == 0) {
vrc4171_slotb = SLOTB_IS_CF;
options += 2;
} else if (strncmp(options, "flashrom", 8) == 0) {
vrc4171_slotb = SLOTB_IS_FLASHROM;
options += 8;
} else if (strncmp(options, "none", 4) == 0) {
vrc4171_slotb = SLOTB_IS_NONE;
options += 4;
}
if (*options != ',')
return 1;
options++;
if (strncmp(options, "memnoprobe", 10) == 0)
vrc4171_sockets[CARD_SLOTB].slot = SLOT_NOPROBE_MEM;
if (strncmp(options, "ionoprobe", 9) == 0)
vrc4171_sockets[CARD_SLOTB].slot = SLOT_NOPROBE_IO;
if (strncmp(options, "noprobe", 7) == 0)
vrc4171_sockets[CARD_SLOTB].slot = SLOT_NOPROBE_ALL;
}
}
return 1;
}
__setup("vrc4171_card=", vrc4171_card_setup);
static struct platform_driver vrc4171_card_driver = {
.driver = {
.name = vrc4171_card_name,
.owner = THIS_MODULE,
},
};
static int vrc4171_card_init(void)
{
int retval;
retval = platform_driver_register(&vrc4171_card_driver);
if (retval < 0)
return retval;
retval = platform_device_register(&vrc4171_card_device);
if (retval < 0) {
platform_driver_unregister(&vrc4171_card_driver);
return retval;
}
vrc4171_set_multifunction_pin(vrc4171_slotb);
retval = vrc4171_add_sockets();
if (retval == 0)
retval = request_irq(vrc4171_irq, pccard_interrupt, IRQF_SHARED,
vrc4171_card_name, vrc4171_sockets);
if (retval < 0) {
vrc4171_remove_sockets();
platform_device_unregister(&vrc4171_card_device);
platform_driver_unregister(&vrc4171_card_driver);
return retval;
}
printk(KERN_INFO "%s, connected to IRQ %d\n",
vrc4171_card_driver.driver.name, vrc4171_irq);
return 0;
}
static void vrc4171_card_exit(void)
{
free_irq(vrc4171_irq, vrc4171_sockets);
vrc4171_remove_sockets();
platform_device_unregister(&vrc4171_card_device);
platform_driver_unregister(&vrc4171_card_driver);
}
module_init(vrc4171_card_init);
module_exit(vrc4171_card_exit);

View file

@ -0,0 +1,591 @@
/*
* FILE NAME
* drivers/pcmcia/vrc4173_cardu.c
*
* BRIEF MODULE DESCRIPTION
* NEC VRC4173 CARDU driver for Socket Services
* (This device doesn't support CardBus. it is supporting only 16bit PC Card.)
*
* Copyright 2002,2003 Yoichi Yuasa <yuasa@linux-mips.org>
*
* 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <asm/io.h>
#include <pcmcia/ss.h>
#include "vrc4173_cardu.h"
MODULE_DESCRIPTION("NEC VRC4173 CARDU driver for Socket Services");
MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
MODULE_LICENSE("GPL");
static int vrc4173_cardu_slots;
static vrc4173_socket_t cardu_sockets[CARDU_MAX_SOCKETS];
extern struct socket_info_t *pcmcia_register_socket (int slot,
struct pccard_operations *vtable,
int use_bus_pm);
extern void pcmcia_unregister_socket(struct socket_info_t *s);
static inline uint8_t exca_readb(vrc4173_socket_t *socket, uint16_t offset)
{
return readb(socket->base + EXCA_REGS_BASE + offset);
}
static inline uint16_t exca_readw(vrc4173_socket_t *socket, uint16_t offset)
{
uint16_t val;
val = readb(socket->base + EXCA_REGS_BASE + offset);
val |= (u16)readb(socket->base + EXCA_REGS_BASE + offset + 1) << 8;
return val;
}
static inline void exca_writeb(vrc4173_socket_t *socket, uint16_t offset, uint8_t val)
{
writeb(val, socket->base + EXCA_REGS_BASE + offset);
}
static inline void exca_writew(vrc4173_socket_t *socket, uint8_t offset, uint16_t val)
{
writeb((u8)val, socket->base + EXCA_REGS_BASE + offset);
writeb((u8)(val >> 8), socket->base + EXCA_REGS_BASE + offset + 1);
}
static inline uint32_t cardbus_socket_readl(vrc4173_socket_t *socket, u16 offset)
{
return readl(socket->base + CARDBUS_SOCKET_REGS_BASE + offset);
}
static inline void cardbus_socket_writel(vrc4173_socket_t *socket, u16 offset, uint32_t val)
{
writel(val, socket->base + CARDBUS_SOCKET_REGS_BASE + offset);
}
static void cardu_pciregs_init(struct pci_dev *dev)
{
u32 syscnt;
u16 brgcnt;
u8 devcnt;
pci_write_config_dword(dev, 0x1c, 0x10000000);
pci_write_config_dword(dev, 0x20, 0x17fff000);
pci_write_config_dword(dev, 0x2c, 0);
pci_write_config_dword(dev, 0x30, 0xfffc);
pci_read_config_word(dev, BRGCNT, &brgcnt);
brgcnt &= ~IREQ_INT;
pci_write_config_word(dev, BRGCNT, brgcnt);
pci_read_config_dword(dev, SYSCNT, &syscnt);
syscnt &= ~(BAD_VCC_REQ_DISB|PCPCI_EN|CH_ASSIGN_MASK|SUB_ID_WR_EN|PCI_CLK_RIN);
syscnt |= (CH_ASSIGN_NODMA|ASYN_INT_MODE);
pci_write_config_dword(dev, SYSCNT, syscnt);
pci_read_config_byte(dev, DEVCNT, &devcnt);
devcnt &= ~(ZOOM_VIDEO_EN|SR_PCI_INT_SEL_MASK|PCI_INT_MODE|IRQ_MODE);
devcnt |= (SR_PCI_INT_SEL_NONE|IFG);
pci_write_config_byte(dev, DEVCNT, devcnt);
pci_write_config_byte(dev, CHIPCNT, S_PREF_DISB);
pci_write_config_byte(dev, SERRDIS, 0);
}
static int cardu_init(unsigned int slot)
{
vrc4173_socket_t *socket = &cardu_sockets[slot];
cardu_pciregs_init(socket->dev);
/* CARD_SC bits are cleared by reading CARD_SC. */
exca_writeb(socket, GLO_CNT, 0);
socket->cap.features |= SS_CAP_PCCARD | SS_CAP_PAGE_REGS;
socket->cap.irq_mask = 0;
socket->cap.map_size = 0x1000;
socket->cap.pci_irq = socket->dev->irq;
socket->events = 0;
spin_lock_init(socket->event_lock);
/* Enable PC Card status interrupts */
exca_writeb(socket, CARD_SCI, CARD_DT_EN|RDY_EN|BAT_WAR_EN|BAT_DEAD_EN);
return 0;
}
static int cardu_register_callback(unsigned int sock,
void (*handler)(void *, unsigned int),
void * info)
{
vrc4173_socket_t *socket = &cardu_sockets[sock];
socket->handler = handler;
socket->info = info;
return 0;
}
static int cardu_inquire_socket(unsigned int sock, socket_cap_t *cap)
{
vrc4173_socket_t *socket = &cardu_sockets[sock];
*cap = socket->cap;
return 0;
}
static int cardu_get_status(unsigned int sock, u_int *value)
{
vrc4173_socket_t *socket = &cardu_sockets[sock];
uint32_t state;
uint8_t status;
u_int val = 0;
status = exca_readb(socket, IF_STATUS);
if (status & CARD_PWR) val |= SS_POWERON;
if (status & READY) val |= SS_READY;
if (status & CARD_WP) val |= SS_WRPROT;
if ((status & (CARD_DETECT1|CARD_DETECT2)) == (CARD_DETECT1|CARD_DETECT2))
val |= SS_DETECT;
if (exca_readb(socket, INT_GEN_CNT) & CARD_TYPE_IO) {
if (status & STSCHG) val |= SS_STSCHG;
} else {
status &= BV_DETECT_MASK;
if (status != BV_DETECT_GOOD) {
if (status == BV_DETECT_WARN) val |= SS_BATWARN;
else val |= SS_BATDEAD;
}
}
state = cardbus_socket_readl(socket, SKT_PRE_STATE);
if (state & VOL_3V_CARD_DT) val |= SS_3VCARD;
if (state & VOL_XV_CARD_DT) val |= SS_XVCARD;
if (state & CB_CARD_DT) val |= SS_CARDBUS;
if (!(state &
(VOL_YV_CARD_DT|VOL_XV_CARD_DT|VOL_3V_CARD_DT|VOL_5V_CARD_DT|CCD20|CCD10)))
val |= SS_PENDING;
*value = val;
return 0;
}
static inline uint8_t set_Vcc_value(u_char Vcc)
{
switch (Vcc) {
case 33:
return VCC_3V;
case 50:
return VCC_5V;
}
return VCC_0V;
}
static inline uint8_t set_Vpp_value(u_char Vpp)
{
switch (Vpp) {
case 33:
case 50:
return VPP_VCC;
case 120:
return VPP_12V;
}
return VPP_0V;
}
static int cardu_set_socket(unsigned int sock, socket_state_t *state)
{
vrc4173_socket_t *socket = &cardu_sockets[sock];
uint8_t val;
if (((state->Vpp == 33) || (state->Vpp == 50)) && (state->Vpp != state->Vcc))
return -EINVAL;
val = set_Vcc_value(state->Vcc);
val |= set_Vpp_value(state->Vpp);
if (state->flags & SS_OUTPUT_ENA) val |= CARD_OUT_EN;
exca_writeb(socket, PWR_CNT, val);
val = exca_readb(socket, INT_GEN_CNT) & CARD_REST0;
if (state->flags & SS_RESET) val &= ~CARD_REST0;
else val |= CARD_REST0;
if (state->flags & SS_IOCARD) val |= CARD_TYPE_IO;
exca_writeb(socket, INT_GEN_CNT, val);
return 0;
}
static int cardu_get_io_map(unsigned int sock, struct pccard_io_map *io)
{
vrc4173_socket_t *socket = &cardu_sockets[sock];
uint8_t ioctl, window;
u_char map;
map = io->map;
if (map > 1)
return -EINVAL;
io->start = exca_readw(socket, IO_WIN_SA(map));
io->stop = exca_readw(socket, IO_WIN_EA(map));
ioctl = exca_readb(socket, IO_WIN_CNT);
window = exca_readb(socket, ADR_WIN_EN);
io->flags = (window & IO_WIN_EN(map)) ? MAP_ACTIVE : 0;
if (ioctl & IO_WIN_DATA_AUTOSZ(map))
io->flags |= MAP_AUTOSZ;
else if (ioctl & IO_WIN_DATA_16BIT(map))
io->flags |= MAP_16BIT;
return 0;
}
static int cardu_set_io_map(unsigned int sock, struct pccard_io_map *io)
{
vrc4173_socket_t *socket = &cardu_sockets[sock];
uint16_t ioctl;
uint8_t window, enable;
u_char map;
map = io->map;
if (map > 1)
return -EINVAL;
window = exca_readb(socket, ADR_WIN_EN);
enable = IO_WIN_EN(map);
if (window & enable) {
window &= ~enable;
exca_writeb(socket, ADR_WIN_EN, window);
}
exca_writew(socket, IO_WIN_SA(map), io->start);
exca_writew(socket, IO_WIN_EA(map), io->stop);
ioctl = exca_readb(socket, IO_WIN_CNT) & ~IO_WIN_CNT_MASK(map);
if (io->flags & MAP_AUTOSZ) ioctl |= IO_WIN_DATA_AUTOSZ(map);
else if (io->flags & MAP_16BIT) ioctl |= IO_WIN_DATA_16BIT(map);
exca_writeb(socket, IO_WIN_CNT, ioctl);
if (io->flags & MAP_ACTIVE)
exca_writeb(socket, ADR_WIN_EN, window | enable);
return 0;
}
static int cardu_get_mem_map(unsigned int sock, struct pccard_mem_map *mem)
{
vrc4173_socket_t *socket = &cardu_sockets[sock];
uint32_t start, stop, offset, page;
uint8_t window;
u_char map;
map = mem->map;
if (map > 4)
return -EINVAL;
window = exca_readb(socket, ADR_WIN_EN);
mem->flags = (window & MEM_WIN_EN(map)) ? MAP_ACTIVE : 0;
start = exca_readw(socket, MEM_WIN_SA(map));
mem->flags |= (start & MEM_WIN_DSIZE) ? MAP_16BIT : 0;
start = (start & 0x0fff) << 12;
stop = exca_readw(socket, MEM_WIN_EA(map));
stop = ((stop & 0x0fff) << 12) + 0x0fff;
offset = exca_readw(socket, MEM_WIN_OA(map));
mem->flags |= (offset & MEM_WIN_WP) ? MAP_WRPROT : 0;
mem->flags |= (offset & MEM_WIN_REGSET) ? MAP_ATTRIB : 0;
offset = ((offset & 0x3fff) << 12) + start;
mem->card_start = offset & 0x03ffffff;
page = exca_readb(socket, MEM_WIN_SAU(map)) << 24;
mem->sys_start = start + page;
mem->sys_stop = start + page;
return 0;
}
static int cardu_set_mem_map(unsigned int sock, struct pccard_mem_map *mem)
{
vrc4173_socket_t *socket = &cardu_sockets[sock];
uint16_t value;
uint8_t window, enable;
u_long sys_start, sys_stop, card_start;
u_char map;
map = mem->map;
sys_start = mem->sys_start;
sys_stop = mem->sys_stop;
card_start = mem->card_start;
if (map > 4 || sys_start > sys_stop || ((sys_start ^ sys_stop) >> 24) ||
(card_start >> 26))
return -EINVAL;
window = exca_readb(socket, ADR_WIN_EN);
enable = MEM_WIN_EN(map);
if (window & enable) {
window &= ~enable;
exca_writeb(socket, ADR_WIN_EN, window);
}
exca_writeb(socket, MEM_WIN_SAU(map), sys_start >> 24);
value = (sys_start >> 12) & 0x0fff;
if (mem->flags & MAP_16BIT) value |= MEM_WIN_DSIZE;
exca_writew(socket, MEM_WIN_SA(map), value);
value = (sys_stop >> 12) & 0x0fff;
exca_writew(socket, MEM_WIN_EA(map), value);
value = ((card_start - sys_start) >> 12) & 0x3fff;
if (mem->flags & MAP_WRPROT) value |= MEM_WIN_WP;
if (mem->flags & MAP_ATTRIB) value |= MEM_WIN_REGSET;
exca_writew(socket, MEM_WIN_OA(map), value);
if (mem->flags & MAP_ACTIVE)
exca_writeb(socket, ADR_WIN_EN, window | enable);
return 0;
}
static void cardu_proc_setup(unsigned int sock, struct proc_dir_entry *base)
{
}
static struct pccard_operations cardu_operations = {
.init = cardu_init,
.register_callback = cardu_register_callback,
.inquire_socket = cardu_inquire_socket,
.get_status = cardu_get_status,
.set_socket = cardu_set_socket,
.get_io_map = cardu_get_io_map,
.set_io_map = cardu_set_io_map,
.get_mem_map = cardu_get_mem_map,
.set_mem_map = cardu_set_mem_map,
.proc_setup = cardu_proc_setup,
};
static void cardu_bh(void *data)
{
vrc4173_socket_t *socket = (vrc4173_socket_t *)data;
uint16_t events;
spin_lock_irq(&socket->event_lock);
events = socket->events;
socket->events = 0;
spin_unlock_irq(&socket->event_lock);
if (socket->handler)
socket->handler(socket->info, events);
}
static uint16_t get_events(vrc4173_socket_t *socket)
{
uint16_t events = 0;
uint8_t csc, status;
status = exca_readb(socket, IF_STATUS);
csc = exca_readb(socket, CARD_SC);
if ((csc & CARD_DT_CHG) &&
((status & (CARD_DETECT1|CARD_DETECT2)) == (CARD_DETECT1|CARD_DETECT2)))
events |= SS_DETECT;
if ((csc & RDY_CHG) && (status & READY))
events |= SS_READY;
if (exca_readb(socket, INT_GEN_CNT) & CARD_TYPE_IO) {
if ((csc & BAT_DEAD_ST_CHG) && (status & STSCHG))
events |= SS_STSCHG;
} else {
if (csc & (BAT_WAR_CHG|BAT_DEAD_ST_CHG)) {
if ((status & BV_DETECT_MASK) != BV_DETECT_GOOD) {
if (status == BV_DETECT_WARN) events |= SS_BATWARN;
else events |= SS_BATDEAD;
}
}
}
return events;
}
static void cardu_interrupt(int irq, void *dev_id)
{
vrc4173_socket_t *socket = (vrc4173_socket_t *)dev_id;
uint16_t events;
INIT_WORK(&socket->tq_work, cardu_bh, socket);
events = get_events(socket);
if (events) {
spin_lock(&socket->event_lock);
socket->events |= events;
spin_unlock(&socket->event_lock);
schedule_work(&socket->tq_work);
}
}
static int vrc4173_cardu_probe(struct pci_dev *dev,
const struct pci_device_id *ent)
{
vrc4173_socket_t *socket;
unsigned long start, len, flags;
int slot, err, ret;
slot = vrc4173_cardu_slots++;
socket = &cardu_sockets[slot];
if (socket->noprobe != 0)
return -EBUSY;
sprintf(socket->name, "NEC VRC4173 CARDU%1d", slot+1);
if ((err = pci_enable_device(dev)) < 0)
return err;
start = pci_resource_start(dev, 0);
if (start == 0) {
ret = -ENODEV;
goto disable;
}
len = pci_resource_len(dev, 0);
if (len == 0) {
ret = -ENODEV;
goto disable;
}
flags = pci_resource_flags(dev, 0);
if ((flags & IORESOURCE_MEM) == 0) {
ret = -EBUSY;
goto disable;
}
err = pci_request_regions(dev, socket->name);
if (err < 0) {
ret = err;
goto disable;
}
socket->base = ioremap(start, len);
if (socket->base == NULL) {
ret = -ENODEV;
goto release;
}
socket->dev = dev;
socket->pcmcia_socket = pcmcia_register_socket(slot, &cardu_operations, 1);
if (socket->pcmcia_socket == NULL) {
ret = -ENOMEM;
goto unmap;
}
if (request_irq(dev->irq, cardu_interrupt, IRQF_SHARED, socket->name, socket) < 0) {
ret = -EBUSY;
goto unregister;
}
printk(KERN_INFO "%s at %#08lx, IRQ %d\n", socket->name, start, dev->irq);
return 0;
unregister:
pcmcia_unregister_socket(socket->pcmcia_socket);
socket->pcmcia_socket = NULL;
unmap:
iounmap(socket->base);
socket->base = NULL;
release:
pci_release_regions(dev);
disable:
pci_disable_device(dev);
return ret;
}
static int vrc4173_cardu_setup(char *options)
{
if (options == NULL || *options == '\0')
return 1;
if (strncmp(options, "cardu1:", 7) == 0) {
options += 7;
if (*options != '\0') {
if (strncmp(options, "noprobe", 7) == 0) {
cardu_sockets[CARDU1].noprobe = 1;
options += 7;
}
if (*options != ',')
return 1;
} else
return 1;
}
if (strncmp(options, "cardu2:", 7) == 0) {
options += 7;
if ((*options != '\0') && (strncmp(options, "noprobe", 7) == 0))
cardu_sockets[CARDU2].noprobe = 1;
}
return 1;
}
__setup("vrc4173_cardu=", vrc4173_cardu_setup);
static const struct pci_device_id vrc4173_cardu_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NAPCCARD) },
{0, }
};
static struct pci_driver vrc4173_cardu_driver = {
.name = "NEC VRC4173 CARDU",
.probe = vrc4173_cardu_probe,
.id_table = vrc4173_cardu_id_table,
};
static int vrc4173_cardu_init(void)
{
vrc4173_cardu_slots = 0;
return pci_register_driver(&vrc4173_cardu_driver);
}
static void vrc4173_cardu_exit(void)
{
pci_unregister_driver(&vrc4173_cardu_driver);
}
module_init(vrc4173_cardu_init);
module_exit(vrc4173_cardu_exit);
MODULE_DEVICE_TABLE(pci, vrc4173_cardu_id_table);

View file

@ -0,0 +1,247 @@
/*
* FILE NAME
* drivers/pcmcia/vrc4173_cardu.h
*
* BRIEF MODULE DESCRIPTION
* Include file for NEC VRC4173 CARDU.
*
* Copyright 2002 Yoichi Yuasa <yuasa@linux-mips.org>
*
* 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* 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.
*/
#ifndef _VRC4173_CARDU_H
#define _VRC4173_CARDU_H
#include <linux/pci.h>
#include <pcmcia/ss.h>
#define CARDU_MAX_SOCKETS 2
#define CARDU1 0
#define CARDU2 1
/*
* PCI Configuration Registers
*/
#define BRGCNT 0x3e
#define POST_WR_EN 0x0400
#define MEM1_PREF_EN 0x0200
#define MEM0_PREF_EN 0x0100
#define IREQ_INT 0x0080
#define CARD_RST 0x0040
#define MABORT_MODE 0x0020
#define VGA_EN 0x0008
#define ISA_EN 0x0004
#define SERR_EN 0x0002
#define PERR_EN 0x0001
#define SYSCNT 0x80
#define BAD_VCC_REQ_DISB 0x00200000
#define PCPCI_EN 0x00080000
#define CH_ASSIGN_MASK 0x00070000
#define CH_ASSIGN_NODMA 0x00040000
#define SUB_ID_WR_EN 0x00000008
#define ASYN_INT_MODE 0x00000004
#define PCI_CLK_RIN 0x00000002
#define DEVCNT 0x91
#define ZOOM_VIDEO_EN 0x40
#define SR_PCI_INT_SEL_MASK 0x18
#define SR_PCI_INT_SEL_NONE 0x00
#define PCI_INT_MODE 0x04
#define IRQ_MODE 0x02
#define IFG 0x01
#define CHIPCNT 0x9c
#define S_PREF_DISB 0x10
#define SERRDIS 0x9f
#define SERR_DIS_MAB 0x10
#define SERR_DIS_TAB 0x08
#define SERR_DIS_DT_PERR 0x04
/*
* ExCA Registers
*/
#define EXCA_REGS_BASE 0x800
#define EXCA_REGS_SIZE 0x800
#define ID_REV 0x000
#define IF_TYPE_16BIT 0x80
#define IF_STATUS 0x001
#define CARD_PWR 0x40
#define READY 0x20
#define CARD_WP 0x10
#define CARD_DETECT2 0x08
#define CARD_DETECT1 0x04
#define BV_DETECT_MASK 0x03
#define BV_DETECT_GOOD 0x03 /* Memory card */
#define BV_DETECT_WARN 0x02
#define BV_DETECT_BAD1 0x01
#define BV_DETECT_BAD0 0x00
#define STSCHG 0x02 /* I/O card */
#define SPKR 0x01
#define PWR_CNT 0x002
#define CARD_OUT_EN 0x80
#define VCC_MASK 0x18
#define VCC_3V 0x18
#define VCC_5V 0x10
#define VCC_0V 0x00
#define VPP_MASK 0x03
#define VPP_12V 0x02
#define VPP_VCC 0x01
#define VPP_0V 0x00
#define INT_GEN_CNT 0x003
#define CARD_REST0 0x40
#define CARD_TYPE_MASK 0x20
#define CARD_TYPE_IO 0x20
#define CARD_TYPE_MEM 0x00
#define CARD_SC 0x004
#define CARD_DT_CHG 0x08
#define RDY_CHG 0x04
#define BAT_WAR_CHG 0x02
#define BAT_DEAD_ST_CHG 0x01
#define CARD_SCI 0x005
#define CARD_DT_EN 0x08
#define RDY_EN 0x04
#define BAT_WAR_EN 0x02
#define BAT_DEAD_EN 0x01
#define ADR_WIN_EN 0x006
#define IO_WIN_EN(x) (0x40 << (x))
#define MEM_WIN_EN(x) (0x01 << (x))
#define IO_WIN_CNT 0x007
#define IO_WIN_CNT_MASK(x) (0x03 << ((x) << 2))
#define IO_WIN_DATA_AUTOSZ(x) (0x02 << ((x) << 2))
#define IO_WIN_DATA_16BIT(x) (0x01 << ((x) << 2))
#define IO_WIN_SA(x) (0x008 + ((x) << 2))
#define IO_WIN_EA(x) (0x00a + ((x) << 2))
#define MEM_WIN_SA(x) (0x010 + ((x) << 3))
#define MEM_WIN_DSIZE 0x8000
#define MEM_WIN_EA(x) (0x012 + ((x) << 3))
#define MEM_WIN_OA(x) (0x014 + ((x) << 3))
#define MEM_WIN_WP 0x8000
#define MEM_WIN_REGSET 0x4000
#define GEN_CNT 0x016
#define VS2_STATUS 0x80
#define VS1_STATUS 0x40
#define EXCA_REG_RST_EN 0x02
#define GLO_CNT 0x01e
#define FUN_INT_LEV 0x08
#define INT_WB_CLR 0x04
#define CSC_INT_LEV 0x02
#define IO_WIN_OAL(x) (0x036 + ((x) << 1))
#define IO_WIN_OAH(x) (0x037 + ((x) << 1))
#define MEM_WIN_SAU(x) (0x040 + (x))
#define IO_SETUP_TIM 0x080
#define IO_CMD_TIM 0x081
#define IO_HOLD_TIM 0x082
#define MEM_SETUP_TIM(x) (0x084 + ((x) << 2))
#define MEM_CMD_TIM(x) (0x085 + ((x) << 2))
#define MEM_HOLD_TIM(x) (0x086 + ((x) << 2))
#define TIM_CLOCKS(x) ((x) - 1)
#define MEM_TIM_SEL1 0x08c
#define MEM_TIM_SEL2 0x08d
#define MEM_WIN_TIMSEL1(x) (0x03 << (((x) & 3) << 1))
#define MEM_WIN_PWEN 0x091
#define POSTWEN 0x01
/*
* CardBus Socket Registers
*/
#define CARDBUS_SOCKET_REGS_BASE 0x000
#define CARDBUS_SOCKET_REGS_SIZE 0x800
#define SKT_EV 0x000
#define POW_CYC_EV 0x00000008
#define CCD2_EV 0x00000004
#define CCD1_EV 0x00000002
#define CSTSCHG_EV 0x00000001
#define SKT_MASK 0x004
#define POW_CYC_MASK 0x00000008
#define CCD_MASK 0x00000006
#define CSC_MASK 0x00000001
#define SKT_PRE_STATE 0x008
#define SKT_FORCE_EV 0x00c
#define VOL_3V_SKT 0x20000000
#define VOL_5V_SKT 0x10000000
#define CVS_TEST 0x00004000
#define VOL_YV_CARD_DT 0x00002000
#define VOL_XV_CARD_DT 0x00001000
#define VOL_3V_CARD_DT 0x00000800
#define VOL_5V_CARD_DT 0x00000400
#define BAD_VCC_REQ 0x00000200
#define DATA_LOST 0x00000100
#define NOT_A_CARD 0x00000080
#define CREADY 0x00000040
#define CB_CARD_DT 0x00000020
#define R2_CARD_DT 0x00000010
#define POW_UP 0x00000008
#define CCD20 0x00000004
#define CCD10 0x00000002
#define CSTSCHG 0x00000001
#define SKT_CNT 0x010
#define STP_CLK_EN 0x00000080
#define VCC_CNT_MASK 0x00000070
#define VCC_CNT_3V 0x00000030
#define VCC_CNT_5V 0x00000020
#define VCC_CNT_0V 0x00000000
#define VPP_CNT_MASK 0x00000007
#define VPP_CNT_3V 0x00000003
#define VPP_CNT_5V 0x00000002
#define VPP_CNT_12V 0x00000001
#define VPP_CNT_0V 0x00000000
typedef struct vrc4173_socket {
int noprobe;
struct pci_dev *dev;
void *base;
void (*handler)(void *, unsigned int);
void *info;
socket_cap_t cap;
spinlock_t event_lock;
uint16_t events;
struct socket_info_t *pcmcia_socket;
struct work_struct tq_work;
char name[20];
} vrc4173_socket_t;
#endif /* _VRC4173_CARDU_H */

327
drivers/pcmcia/xxs1500_ss.c Normal file
View file

@ -0,0 +1,327 @@
/*
* PCMCIA socket code for the MyCable XXS1500 system.
*
* Copyright (c) 2009 Manuel Lauss <manuel.lauss@gmail.com>
*
*/
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/resource.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <pcmcia/ss.h>
#include <pcmcia/cistpl.h>
#include <asm/irq.h>
#include <asm/mach-au1x00/au1000.h>
#define MEM_MAP_SIZE 0x400000
#define IO_MAP_SIZE 0x1000
/*
* 3.3V cards only; all interfacing is done via gpios:
*
* 0/1: carddetect (00 = card present, xx = huh)
* 4: card irq
* 204: reset (high-act)
* 205: buffer enable (low-act)
* 208/209: card voltage key (00,01,10,11)
* 210: battwarn
* 211: batdead
* 214: power (low-act)
*/
#define GPIO_CDA 0
#define GPIO_CDB 1
#define GPIO_CARDIRQ 4
#define GPIO_RESET 204
#define GPIO_OUTEN 205
#define GPIO_VSL 208
#define GPIO_VSH 209
#define GPIO_BATTDEAD 210
#define GPIO_BATTWARN 211
#define GPIO_POWER 214
struct xxs1500_pcmcia_sock {
struct pcmcia_socket socket;
void *virt_io;
phys_addr_t phys_io;
phys_addr_t phys_attr;
phys_addr_t phys_mem;
/* previous flags for set_socket() */
unsigned int old_flags;
};
#define to_xxs_socket(x) container_of(x, struct xxs1500_pcmcia_sock, socket)
static irqreturn_t cdirq(int irq, void *data)
{
struct xxs1500_pcmcia_sock *sock = data;
pcmcia_parse_events(&sock->socket, SS_DETECT);
return IRQ_HANDLED;
}
static int xxs1500_pcmcia_configure(struct pcmcia_socket *skt,
struct socket_state_t *state)
{
struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt);
unsigned int changed;
/* power control */
switch (state->Vcc) {
case 0:
gpio_set_value(GPIO_POWER, 1); /* power off */
break;
case 33:
gpio_set_value(GPIO_POWER, 0); /* power on */
break;
case 50:
default:
return -EINVAL;
}
changed = state->flags ^ sock->old_flags;
if (changed & SS_RESET) {
if (state->flags & SS_RESET) {
gpio_set_value(GPIO_RESET, 1); /* assert reset */
gpio_set_value(GPIO_OUTEN, 1); /* buffers off */
} else {
gpio_set_value(GPIO_RESET, 0); /* deassert reset */
gpio_set_value(GPIO_OUTEN, 0); /* buffers on */
msleep(500);
}
}
sock->old_flags = state->flags;
return 0;
}
static int xxs1500_pcmcia_get_status(struct pcmcia_socket *skt,
unsigned int *value)
{
unsigned int status;
int i;
status = 0;
/* check carddetects: GPIO[0:1] must both be low */
if (!gpio_get_value(GPIO_CDA) && !gpio_get_value(GPIO_CDB))
status |= SS_DETECT;
/* determine card voltage: GPIO[208:209] binary value */
i = (!!gpio_get_value(GPIO_VSL)) | ((!!gpio_get_value(GPIO_VSH)) << 1);
switch (i) {
case 0:
case 1:
case 2:
status |= SS_3VCARD; /* 3V card */
break;
case 3: /* 5V card, unsupported */
default:
status |= SS_XVCARD; /* treated as unsupported in core */
}
/* GPIO214: low active power switch */
status |= gpio_get_value(GPIO_POWER) ? 0 : SS_POWERON;
/* GPIO204: high-active reset line */
status |= gpio_get_value(GPIO_RESET) ? SS_RESET : SS_READY;
/* other stuff */
status |= gpio_get_value(GPIO_BATTDEAD) ? 0 : SS_BATDEAD;
status |= gpio_get_value(GPIO_BATTWARN) ? 0 : SS_BATWARN;
*value = status;
return 0;
}
static int xxs1500_pcmcia_sock_init(struct pcmcia_socket *skt)
{
gpio_direction_input(GPIO_CDA);
gpio_direction_input(GPIO_CDB);
gpio_direction_input(GPIO_VSL);
gpio_direction_input(GPIO_VSH);
gpio_direction_input(GPIO_BATTDEAD);
gpio_direction_input(GPIO_BATTWARN);
gpio_direction_output(GPIO_RESET, 1); /* assert reset */
gpio_direction_output(GPIO_OUTEN, 1); /* disable buffers */
gpio_direction_output(GPIO_POWER, 1); /* power off */
return 0;
}
static int xxs1500_pcmcia_sock_suspend(struct pcmcia_socket *skt)
{
return 0;
}
static int au1x00_pcmcia_set_io_map(struct pcmcia_socket *skt,
struct pccard_io_map *map)
{
struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt);
map->start = (u32)sock->virt_io;
map->stop = map->start + IO_MAP_SIZE;
return 0;
}
static int au1x00_pcmcia_set_mem_map(struct pcmcia_socket *skt,
struct pccard_mem_map *map)
{
struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt);
if (map->flags & MAP_ATTRIB)
map->static_start = sock->phys_attr + map->card_start;
else
map->static_start = sock->phys_mem + map->card_start;
return 0;
}
static struct pccard_operations xxs1500_pcmcia_operations = {
.init = xxs1500_pcmcia_sock_init,
.suspend = xxs1500_pcmcia_sock_suspend,
.get_status = xxs1500_pcmcia_get_status,
.set_socket = xxs1500_pcmcia_configure,
.set_io_map = au1x00_pcmcia_set_io_map,
.set_mem_map = au1x00_pcmcia_set_mem_map,
};
static int xxs1500_pcmcia_probe(struct platform_device *pdev)
{
struct xxs1500_pcmcia_sock *sock;
struct resource *r;
int ret, irq;
sock = kzalloc(sizeof(struct xxs1500_pcmcia_sock), GFP_KERNEL);
if (!sock)
return -ENOMEM;
ret = -ENODEV;
/* 36bit PCMCIA Attribute area address */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr");
if (!r) {
dev_err(&pdev->dev, "missing 'pcmcia-attr' resource!\n");
goto out0;
}
sock->phys_attr = r->start;
/* 36bit PCMCIA Memory area address */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem");
if (!r) {
dev_err(&pdev->dev, "missing 'pcmcia-mem' resource!\n");
goto out0;
}
sock->phys_mem = r->start;
/* 36bit PCMCIA IO area address */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io");
if (!r) {
dev_err(&pdev->dev, "missing 'pcmcia-io' resource!\n");
goto out0;
}
sock->phys_io = r->start;
/*
* PCMCIA client drivers use the inb/outb macros to access
* the IO registers. Since mips_io_port_base is added
* to the access address of the mips implementation of
* inb/outb, we need to subtract it here because we want
* to access the I/O or MEM address directly, without
* going through this "mips_io_port_base" mechanism.
*/
sock->virt_io = (void *)(ioremap(sock->phys_io, IO_MAP_SIZE) -
mips_io_port_base);
if (!sock->virt_io) {
dev_err(&pdev->dev, "cannot remap IO area\n");
ret = -ENOMEM;
goto out0;
}
sock->socket.ops = &xxs1500_pcmcia_operations;
sock->socket.owner = THIS_MODULE;
sock->socket.pci_irq = gpio_to_irq(GPIO_CARDIRQ);
sock->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
sock->socket.map_size = MEM_MAP_SIZE;
sock->socket.io_offset = (unsigned long)sock->virt_io;
sock->socket.dev.parent = &pdev->dev;
sock->socket.resource_ops = &pccard_static_ops;
platform_set_drvdata(pdev, sock);
/* setup carddetect irq: use one of the 2 GPIOs as an
* edge detector.
*/
irq = gpio_to_irq(GPIO_CDA);
irq_set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
ret = request_irq(irq, cdirq, 0, "pcmcia_carddetect", sock);
if (ret) {
dev_err(&pdev->dev, "cannot setup cd irq\n");
goto out1;
}
ret = pcmcia_register_socket(&sock->socket);
if (ret) {
dev_err(&pdev->dev, "failed to register\n");
goto out2;
}
printk(KERN_INFO "MyCable XXS1500 PCMCIA socket services\n");
return 0;
out2:
free_irq(gpio_to_irq(GPIO_CDA), sock);
out1:
iounmap((void *)(sock->virt_io + (u32)mips_io_port_base));
out0:
kfree(sock);
return ret;
}
static int xxs1500_pcmcia_remove(struct platform_device *pdev)
{
struct xxs1500_pcmcia_sock *sock = platform_get_drvdata(pdev);
pcmcia_unregister_socket(&sock->socket);
free_irq(gpio_to_irq(GPIO_CDA), sock);
iounmap((void *)(sock->virt_io + (u32)mips_io_port_base));
kfree(sock);
return 0;
}
static struct platform_driver xxs1500_pcmcia_socket_driver = {
.driver = {
.name = "xxs1500_pcmcia",
.owner = THIS_MODULE,
},
.probe = xxs1500_pcmcia_probe,
.remove = xxs1500_pcmcia_remove,
};
module_platform_driver(xxs1500_pcmcia_socket_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PCMCIA Socket Services for MyCable XXS1500 systems");
MODULE_AUTHOR("Manuel Lauss");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,135 @@
#ifndef __YENTA_H
#define __YENTA_H
#include <asm/io.h>
#define CB_SOCKET_EVENT 0x00
#define CB_CSTSEVENT 0x00000001 /* Card status event */
#define CB_CD1EVENT 0x00000002 /* Card detect 1 change event */
#define CB_CD2EVENT 0x00000004 /* Card detect 2 change event */
#define CB_PWREVENT 0x00000008 /* PWRCYCLE change event */
#define CB_SOCKET_MASK 0x04
#define CB_CSTSMASK 0x00000001 /* Card status mask */
#define CB_CDMASK 0x00000006 /* Card detect 1&2 mask */
#define CB_PWRMASK 0x00000008 /* PWRCYCLE change mask */
#define CB_SOCKET_STATE 0x08
#define CB_CARDSTS 0x00000001 /* CSTSCHG status */
#define CB_CDETECT1 0x00000002 /* Card detect status 1 */
#define CB_CDETECT2 0x00000004 /* Card detect status 2 */
#define CB_PWRCYCLE 0x00000008 /* Socket powered */
#define CB_16BITCARD 0x00000010 /* 16-bit card detected */
#define CB_CBCARD 0x00000020 /* CardBus card detected */
#define CB_IREQCINT 0x00000040 /* READY(xIRQ)/xCINT high */
#define CB_NOTACARD 0x00000080 /* Unrecognizable PC card detected */
#define CB_DATALOST 0x00000100 /* Potential data loss due to card removal */
#define CB_BADVCCREQ 0x00000200 /* Invalid Vcc request by host software */
#define CB_5VCARD 0x00000400 /* Card Vcc at 5.0 volts? */
#define CB_3VCARD 0x00000800 /* Card Vcc at 3.3 volts? */
#define CB_XVCARD 0x00001000 /* Card Vcc at X.X volts? */
#define CB_YVCARD 0x00002000 /* Card Vcc at Y.Y volts? */
#define CB_5VSOCKET 0x10000000 /* Socket Vcc at 5.0 volts? */
#define CB_3VSOCKET 0x20000000 /* Socket Vcc at 3.3 volts? */
#define CB_XVSOCKET 0x40000000 /* Socket Vcc at X.X volts? */
#define CB_YVSOCKET 0x80000000 /* Socket Vcc at Y.Y volts? */
#define CB_SOCKET_FORCE 0x0C
#define CB_FCARDSTS 0x00000001 /* Force CSTSCHG */
#define CB_FCDETECT1 0x00000002 /* Force CD1EVENT */
#define CB_FCDETECT2 0x00000004 /* Force CD2EVENT */
#define CB_FPWRCYCLE 0x00000008 /* Force PWREVENT */
#define CB_F16BITCARD 0x00000010 /* Force 16-bit PCMCIA card */
#define CB_FCBCARD 0x00000020 /* Force CardBus line */
#define CB_FNOTACARD 0x00000080 /* Force NOTACARD */
#define CB_FDATALOST 0x00000100 /* Force data lost */
#define CB_FBADVCCREQ 0x00000200 /* Force bad Vcc request */
#define CB_F5VCARD 0x00000400 /* Force 5.0 volt card */
#define CB_F3VCARD 0x00000800 /* Force 3.3 volt card */
#define CB_FXVCARD 0x00001000 /* Force X.X volt card */
#define CB_FYVCARD 0x00002000 /* Force Y.Y volt card */
#define CB_CVSTEST 0x00004000 /* Card VS test */
#define CB_SOCKET_CONTROL 0x10
#define CB_SC_VPP_MASK 0x00000007
#define CB_SC_VPP_OFF 0x00000000
#define CB_SC_VPP_12V 0x00000001
#define CB_SC_VPP_5V 0x00000002
#define CB_SC_VPP_3V 0x00000003
#define CB_SC_VPP_XV 0x00000004
#define CB_SC_VPP_YV 0x00000005
#define CB_SC_VCC_MASK 0x00000070
#define CB_SC_VCC_OFF 0x00000000
#define CB_SC_VCC_5V 0x00000020
#define CB_SC_VCC_3V 0x00000030
#define CB_SC_VCC_XV 0x00000040
#define CB_SC_VCC_YV 0x00000050
#define CB_SC_CCLK_STOP 0x00000080
#define CB_SOCKET_POWER 0x20
#define CB_SKTACCES 0x02000000 /* A PC card access has occurred (clear on read) */
#define CB_SKTMODE 0x01000000 /* Clock frequency has changed (clear on read) */
#define CB_CLKCTRLEN 0x00010000 /* Clock control enabled (RW) */
#define CB_CLKCTRL 0x00000001 /* Stop(0) or slow(1) CB clock (RW) */
/*
* Cardbus configuration space
*/
#define CB_BRIDGE_BASE(m) (0x1c + 8*(m))
#define CB_BRIDGE_LIMIT(m) (0x20 + 8*(m))
#define CB_BRIDGE_CONTROL 0x3e
#define CB_BRIDGE_CPERREN 0x00000001
#define CB_BRIDGE_CSERREN 0x00000002
#define CB_BRIDGE_ISAEN 0x00000004
#define CB_BRIDGE_VGAEN 0x00000008
#define CB_BRIDGE_MABTMODE 0x00000020
#define CB_BRIDGE_CRST 0x00000040
#define CB_BRIDGE_INTR 0x00000080
#define CB_BRIDGE_PREFETCH0 0x00000100
#define CB_BRIDGE_PREFETCH1 0x00000200
#define CB_BRIDGE_POSTEN 0x00000400
#define CB_LEGACY_MODE_BASE 0x44
/*
* ExCA area extensions in Yenta
*/
#define CB_MEM_PAGE(map) (0x40 + (map))
/* control how 16bit cards are powered */
#define YENTA_16BIT_POWER_EXCA 0x00000001
#define YENTA_16BIT_POWER_DF 0x00000002
struct yenta_socket;
struct cardbus_type {
int (*override)(struct yenta_socket *);
void (*save_state)(struct yenta_socket *);
void (*restore_state)(struct yenta_socket *);
int (*sock_init)(struct yenta_socket *);
};
struct yenta_socket {
struct pci_dev *dev;
int cb_irq, io_irq;
void __iomem *base;
struct timer_list poll_timer;
struct pcmcia_socket socket;
struct cardbus_type *type;
u32 flags;
/* for PCI interrupt probing */
unsigned int probe_status;
/* A few words of private data for special stuff of overrides... */
unsigned int private[8];
/* PCI saved state */
u32 saved_state[2];
};
#endif