patch-2.3.35 linux/drivers/pcmcia/yenta.c

Next file: linux/drivers/pcmcia/yenta.h
Previous file: linux/drivers/pcmcia/tcic.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.34/linux/drivers/pcmcia/yenta.c linux/drivers/pcmcia/yenta.c
@@ -0,0 +1,534 @@
+/*
+ * Regular lowlevel cardbus driver ("yenta")
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ */
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <pcmcia/ss.h>
+
+#include <asm/io.h>
+
+#include "yenta.h"
+#include "i82365.h"
+
+/* Don't ask.. */
+#define to_cycles(ns)	((ns)/120)
+#define to_ns(cycles)	((cycles)*120)
+
+static int yenta_inquire(pci_socket_t *socket, socket_cap_t *cap)
+{
+	*cap = socket->cap;
+	return 0;
+}
+
+/*
+ * Silly interface. We convert the cardbus status to a internal status,
+ * and we probably really should keep it in cardbus status form and
+ * only convert for old-style 16-bit PCMCIA cards..
+ */
+static int yenta_get_status(pci_socket_t *socket, unsigned int *value)
+{
+	u32 state = cb_readl(socket, CB_SOCKET_STATE);
+	u8 status;
+	unsigned int val;
+
+	/* Convert from Yenta status to old-style status */
+	val  = (state & CB_CARDSTS) ? SS_STSCHG : 0;
+	val |= (state & (CB_CDETECT1 | CB_CDETECT2)) ? 0 : SS_DETECT;
+	val |= (state & CB_PWRCYCLE) ? SS_POWERON | SS_READY : 0;
+	val |= (state & CB_CBCARD) ? SS_CARDBUS : 0;
+	val |= (state & CB_3VCARD) ? SS_3VCARD : 0;
+	val |= (state & CB_XVCARD) ? SS_XVCARD : 0;
+
+	/* Get the old compatibility status too.. */
+	status = exca_readb(socket, I365_STATUS);
+	val |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0;
+	val |= (status & I365_CS_READY) ? SS_READY : 0;
+	val |= (status & I365_CS_POWERON) ? SS_POWERON : 0;
+
+printk("yenta_get_status(%p)= %x\n", socket, val);
+
+	*value = val;
+	return 0;
+}
+
+static int yenta_Vcc_power(u32 control)
+{
+	switch ((control >> CB_VCCCTRL) & CB_PWRBITS) {
+	case CB_PWR5V: return 50;
+	case CB_PWR3V: return 33;
+	default: return 0;
+	}
+}
+
+static int yenta_Vpp_power(u32 control)
+{
+	switch ((control >> CB_VPPCTRL) & CB_PWRBITS) {
+	case CB_PWR12V: return 120;
+	case CB_PWR5V: return 50;
+	case CB_PWR3V: return 33;
+	default: return 0;
+	}
+}
+
+static int yenta_get_socket(pci_socket_t *socket, socket_state_t *state)
+{
+	u32 control = cb_readl(socket, CB_SOCKET_CONTROL);
+	u8 reg;
+
+	state->Vcc = yenta_Vcc_power(control);
+	state->Vpp = yenta_Vpp_power(control);
+	state->io_irq = socket->io_irq;
+
+	reg = exca_readb(socket, I365_POWER);
+	state->flags = (reg & I365_PWR_AUTO) ? SS_PWR_AUTO : 0;
+	state->flags |= (reg & I365_PWR_OUT) ? SS_OUTPUT_ENA : 0;
+
+	reg = exca_readb(socket, I365_INTCTL);
+	state->flags |= (reg & I365_PC_RESET) ? 0 : SS_RESET;
+	state->flags |= (reg & I365_PC_IOCARD) ? SS_IOCARD : 0;
+
+	reg = exca_readb(socket, I365_CSCINT);
+	state->csc_mask = (reg & I365_CSC_DETECT) ? SS_DETECT : 0;
+	if (state->flags & SS_IOCARD) {
+		state->csc_mask |= (reg & I365_CSC_STSCHG) ? SS_STSCHG : 0;
+	} else {
+		state->csc_mask |= (reg & I365_CSC_BVD1) ? SS_BATDEAD : 0;
+		state->csc_mask |= (reg & I365_CSC_BVD2) ? SS_BATWARN : 0;
+		state->csc_mask |= (reg & I365_CSC_READY) ? SS_READY : 0;
+	}
+
+printk("yenta_get_socket(%p) = %d, %d\n", socket, state->Vcc, state->Vpp);
+
+	return 0;
+}
+
+static int yenta_set_socket(pci_socket_t *socket, socket_state_t *state)
+{
+	u8 reg;
+	u16 bridge;
+	u32 control;
+
+printk("yenta_set_socket(%p, %d, %d, %x)\n", socket, state->Vcc, state->Vpp, state->flags);
+
+	bridge = config_readw(socket, CB_BRIDGE_CONTROL);
+	bridge &= ~CB_BRIDGE_CRST;
+	bridge |= (state->flags & SS_RESET) ? CB_BRIDGE_CRST : 0;
+	config_writew(socket, CB_BRIDGE_CONTROL, bridge);
+
+	/* Set the IO interrupt and socket state */
+	reg = state->io_irq;
+	reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET;
+	reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0;
+	exca_writeb(socket, I365_INTCTL, reg);
+
+	/* Set host interrupt and CSC mask state */
+	reg = socket->cb_irq << 4;
+	reg |= (state->csc_mask & SS_DETECT) ? I365_CSC_DETECT : 0;
+	if (state->flags & SS_IOCARD) {
+		reg |= (state->csc_mask & SS_STSCHG) ? I365_CSC_STSCHG : 0;
+	} else {
+		reg |= (state->csc_mask & SS_BATDEAD) ? I365_CSC_BVD1 : 0;
+		reg |= (state->csc_mask & SS_BATWARN) ? I365_CSC_BVD2 : 0;
+		reg |= (state->csc_mask & SS_READY) ? I365_CSC_READY : 0;
+	}
+	exca_writeb(socket, I365_CSCINT, reg);
+	exca_readb(socket, I365_CSC);
+
+	/*
+	 * Set power state..
+	 *
+	 * I wonder if we could do the Vcc/Vpp part through the
+	 * CB interface only..
+	 */
+	reg = I365_PWR_NORESET;
+	reg |= (state->flags & SS_PWR_AUTO) ? I365_PWR_AUTO : 0;
+	reg |= (state->flags & SS_OUTPUT_ENA) ? I365_PWR_OUT : 0;
+
+	control = CB_STOPCLK;
+	switch (state->Vcc) {
+	case 33:
+		control |= CB_PWR3V << CB_VCCCTRL;
+		reg |= I365_VCC_5V;
+		break;
+	case 50:
+		control |= CB_PWR5V << CB_VCCCTRL;
+		reg |= I365_VCC_5V;
+		break;
+	}
+	switch (state->Vpp) {
+	case 33:
+		control |= CB_PWR3V << CB_VPPCTRL;
+		reg |= I365_VPP1_5V;
+		break;
+	case 50:
+		control |= CB_PWR5V << CB_VPPCTRL;
+		reg |= I365_VPP1_5V;
+		break;
+	case 120:
+		control |= CB_PWR12V << CB_VPPCTRL;
+		reg |= I365_VPP1_12V;
+		break;
+	}
+	exca_writeb(socket, I365_POWER, reg);
+	cb_writel(socket, CB_SOCKET_CONTROL, control);
+
+	return 0;
+}
+
+static int yenta_get_io_map(pci_socket_t *socket, struct pccard_io_map *io)
+{
+	int map;
+	unsigned char ioctl, addr;
+
+	map = io->map;
+	if (map > 1)
+		return -EINVAL;
+
+	io->start = exca_readw(socket, I365_IO(map)+I365_W_START);
+	io->stop = exca_readw(socket, I365_IO(map)+I365_W_STOP);
+
+	ioctl = exca_readb(socket, I365_IOCTL);
+	addr = exca_readb(socket, I365_ADDRWIN);
+	io->speed = to_ns(ioctl & I365_IOCTL_WAIT(map)) ? 1 : 0;
+	io->flags  = (addr & I365_ENA_IO(map)) ? MAP_ACTIVE : 0;
+	io->flags |= (ioctl & I365_IOCTL_0WS(map)) ? MAP_0WS : 0;
+	io->flags |= (ioctl & I365_IOCTL_16BIT(map)) ? MAP_16BIT : 0;
+	io->flags |= (ioctl & I365_IOCTL_IOCS16(map)) ? MAP_AUTOSZ : 0;
+
+printk("yenta_get_io_map(%d) = %x, %x, %x\n", map, io->start, io->stop, io->flags);
+
+	return 0;
+}
+
+static int yenta_set_io_map(pci_socket_t *socket, struct pccard_io_map *io)
+{
+	int map;
+	unsigned char ioctl, addr, enable;
+
+	map = io->map;
+
+printk("yenta_set_io_map(%d, %x, %x, %x)\n", map, io->start, io->stop, io->flags);
+
+	if (map > 1)
+		return -EINVAL;
+
+	enable = I365_ENA_IO(map);
+	addr = exca_readb(socket, I365_ADDRWIN);
+
+	/* Disable the window before changing it.. */
+	if (addr & enable) {
+		addr &= ~enable;
+		exca_writeb(socket, I365_ADDRWIN, addr);
+	}
+
+	exca_writew(socket, I365_IO(map)+I365_W_START, io->start);
+	exca_writew(socket, I365_IO(map)+I365_W_STOP, io->stop);
+
+	ioctl = exca_readb(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);
+	exca_writeb(socket, I365_IOCTL, ioctl);
+
+	if (io->flags & MAP_ACTIVE)
+		exca_writeb(socket, I365_ADDRWIN, addr | enable);
+	return 0;
+}
+
+static int yenta_get_mem_map(pci_socket_t *socket, struct pccard_mem_map *mem)
+{
+	int map;
+	unsigned char addr;
+	unsigned int start, stop, page, offset;
+
+	map = mem->map;
+	if (map > 4)
+		return -EINVAL;
+
+	addr = exca_readb(socket, I365_ADDRWIN);
+	mem->flags = (addr & I365_ENA_MEM(map)) ? MAP_ACTIVE : 0;
+
+	start = exca_readw(socket, I365_MEM(map) + I365_W_START);
+	mem->flags |= (start & I365_MEM_16BIT) ? MAP_16BIT : 0;
+	mem->flags |= (start & I365_MEM_0WS) ? MAP_0WS : 0;
+	start = (start & 0x0fff) << 12;
+
+	stop = exca_readw(socket, I365_MEM(map) + I365_W_STOP);
+	mem->speed  = (stop & I365_MEM_WS0) ? 1 : 0;
+	mem->speed += (stop & I365_MEM_WS1) ? 2 : 0;
+	mem->speed = to_ns(mem->speed);
+	stop = ((stop & 0x0fff) << 12) + 0x0fff;
+
+	offset = exca_readw(socket, I365_MEM(map) + I365_W_OFF);
+	mem->flags |= (offset & I365_MEM_WRPROT) ? MAP_WRPROT : 0;
+	mem->flags |= (offset & I365_MEM_REG) ? MAP_ATTRIB : 0;
+	offset = ((offset & 0x3fff) << 12) + start;
+	mem->card_start = offset & 0x3ffffff;
+
+	page = exca_readb(socket, CB_MEM_PAGE(map)) << 24;
+	mem->sys_start = start + page;
+	mem->sys_stop = start + page;
+
+printk("yenta_get_map(%d) = %lx, %lx, %x\n", map, mem->sys_start, mem->sys_stop, mem->card_start);
+
+	return 0;
+}
+
+static int yenta_set_mem_map(pci_socket_t *socket, struct pccard_mem_map *mem)
+{
+	int map;
+	unsigned char addr, enable;
+	unsigned int start, stop, card_start;
+	unsigned short word;
+
+	map = mem->map;
+	start = mem->sys_start;
+	stop = mem->sys_stop;
+	card_start = mem->card_start;
+
+printk("yenta_set_map(%d, %x, %x, %x)\n", map, start, stop, card_start);
+
+	if (map > 4 || start > stop || ((start ^ stop) >> 24) || (card_start >> 26) || mem->speed > 1000)
+		return -EINVAL;
+
+	enable = I365_ENA_MEM(map);
+	addr = exca_readb(socket, I365_ADDRWIN);
+	if (addr & enable) {
+		addr &= ~enable;
+		exca_writeb(socket, I365_ADDRWIN, addr);
+	}
+
+	exca_writeb(socket, CB_MEM_PAGE(map), start >> 24);
+
+	word = (start >> 12) & 0x0fff;
+	if (mem->flags & MAP_16BIT)
+		word |= I365_MEM_16BIT;
+	if (mem->flags & MAP_0WS)
+		word |= I365_MEM_0WS;
+	exca_writew(socket, I365_MEM(map) + I365_W_START, word);
+
+	word = (stop >> 12) & 0x0fff;
+	switch (to_cycles(mem->speed)) {
+		case 0: break;
+		case 1:  word |= I365_MEM_WS0; break;
+		case 2:  word |= I365_MEM_WS1; break;
+		default: word |= I365_MEM_WS1 | I365_MEM_WS0; break;
+	}
+	exca_writew(socket, I365_MEM(map) + I365_W_STOP, word);
+
+	word = ((card_start - start) >> 12) & 0x3fff;
+	if (mem->flags & MAP_WRPROT)
+		word |= I365_MEM_WRPROT;
+	if (mem->flags & MAP_ATTRIB)
+		word |= I365_MEM_REG;
+	exca_writew(socket, I365_MEM(map) + I365_W_OFF, word);
+
+	if (mem->flags & MAP_ACTIVE)
+		exca_writeb(socket, I365_ADDRWIN, addr | enable);
+	return 0;
+}
+
+static int yenta_get_bridge(pci_socket_t *socket, struct cb_bridge_map *m)
+{
+	unsigned map;
+	
+	map = m->map;
+	if (map > 1)
+		return -EINVAL;
+
+	m->flags &= MAP_IOSPACE;
+	map += (m->flags & MAP_IOSPACE) ? 2 : 0;
+	m->start = config_readl(socket, CB_BRIDGE_BASE(map));
+	m->stop = config_readl(socket, CB_BRIDGE_LIMIT(map));
+	if (m->start || m->stop) {
+		m->flags |= MAP_ACTIVE;
+		m->stop |= (map > 1) ? 3 : 0x0fff;
+	}
+
+	/* Get prefetch state for memory mappings */
+	if (map < 2) {
+		u16 ctrl, prefetch_mask = CB_BRIDGE_PREFETCH0 << map;
+
+		ctrl = config_readw(socket, CB_BRIDGE_CONTROL);
+		m->flags |= (ctrl & prefetch_mask) ? MAP_PREFETCH : 0;
+	}
+	return 0;
+}
+
+static int yenta_set_bridge(pci_socket_t *socket, struct cb_bridge_map *m)
+{
+	unsigned map;
+	u32 start, end;
+	
+	map = m->map;
+	if (map > 1 || m->stop < m->start)
+		return -EINVAL;
+
+	if (m->flags & MAP_IOSPACE) {
+		if ((m->stop > 0xffff) || (m->start & 3) ||
+		    ((m->stop & 3) != 3))
+			return -EINVAL;
+		map += 2;
+	} else {
+		u16 ctrl, prefetch_mask = CB_BRIDGE_PREFETCH0 << map;
+
+		if ((m->start & 0x0fff) || ((m->stop & 0x0fff) != 0x0fff))
+			return -EINVAL;
+		ctrl = config_readw(socket, CB_BRIDGE_CONTROL);
+		ctrl &= ~prefetch_mask;
+		ctrl |= (m->flags & MAP_PREFETCH) ? prefetch_mask : 0;
+		config_writew(socket, CB_BRIDGE_CONTROL, ctrl);
+	}
+
+	start = 0;
+	end = 0;
+	if (m->flags & MAP_ACTIVE) {
+		start = m->start;
+		end = m->stop;
+	}
+	config_writel(socket, CB_BRIDGE_BASE(map), start);
+	config_writel(socket, CB_BRIDGE_LIMIT(map), end);
+	return 0;
+}
+
+static void yenta_proc_setup(pci_socket_t *socket, struct proc_dir_entry *base)
+{
+	/* Not done yet */
+}
+
+static void yenta_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	u8 csc;
+	u32 cb_event;
+	unsigned int events;
+	pci_socket_t *socket = (pci_socket_t *) dev_id;
+
+	/* Clear interrupt status for the event */
+	cb_event = cb_readl(socket, CB_SOCKET_EVENT);
+	cb_writel(socket, CB_SOCKET_EVENT, cb_event);
+
+	csc = exca_readb(socket, I365_CSC);
+
+	events = (cb_event & (CB_CD1EVENT | CB_CD2EVENT)) ? SS_DETECT : 0 ;
+	events |= (csc & I365_CSC_DETECT) ? SS_DETECT : 0;
+	if (exca_readb(socket, I365_INTCTL) & I365_PC_IOCARD) {
+		events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0;
+	} else {
+		events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0;
+		events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0;
+		events |= (csc & I365_CSC_READY) ? SS_READY : 0;
+	}
+
+	printk("Socket interrupt event %08x (%08x %02x)\n", events, cb_event, csc);
+
+	if (events && socket->handler)
+		socket->handler(socket->info, events);
+}
+
+static unsigned int yenta_probe_irq(pci_socket_t *socket)
+{
+	int i;
+	unsigned long val;
+
+	cb_writel(socket, CB_SOCKET_EVENT, -1);
+	cb_writel(socket, CB_SOCKET_MASK, CB_CSTSMASK);
+	val = probe_irq_on();
+	for (i = 1; i < 16; i++) {
+		exca_writeb(socket, I365_CSCINT, I365_CSC_STSCHG | (i << 4));
+		cb_writel(socket, CB_SOCKET_FORCE, CB_FCARDSTS);
+		udelay(100);
+		cb_writel(socket, CB_SOCKET_EVENT, -1);
+	}
+	cb_writel(socket, CB_SOCKET_MASK, 0);
+	return probe_irq_mask(val);
+}
+
+static void yenta_get_socket_capabilities(pci_socket_t *socket)
+{
+	socket->cap.features = SS_CAP_PAGE_REGS | SS_CAP_PCCARD | SS_CAP_CARDBUS;
+	socket->cap.irq_mask = yenta_probe_irq(socket);
+	if (socket->cb_irq && socket->cb_irq < 16)
+		socket->cap.irq_mask |= 1 << socket->cb_irq;
+	socket->cap.map_size = 0x1000;
+	socket->cap.pci_irq = socket->cb_irq;
+	socket->cap.cardbus = config_readb(socket, PCI_CB_CARD_BUS);
+	socket->cap.cb_bus = socket->dev->subordinate;
+	socket->cap.bus = NULL;
+
+	printk("Yenta IRQ list %04x\n", socket->cap.irq_mask);
+}
+
+/*
+ * Initialize a cardbus controller. Make sure we have a usable
+ * interrupt, and that we can map the cardbus area. Fill in the
+ * socket information structure..
+ */
+static int yenta_open(pci_socket_t *socket)
+{
+	struct pci_dev *dev = socket->dev;
+
+	/*
+	 * Do some basic sanity checking..
+	 */
+	if (pci_enable_device(dev)) {
+		printk("Unable to enable device\n");
+		return -1;
+	}
+	if (!dev->resource[0].start) {
+		printk("No cardbus resource!\n");
+		return -1;
+	}
+
+	/*
+	 * Ok, start setup.. Map the cardbus registers,
+	 * and request the IRQ.
+	 */
+	socket->base = ioremap(dev->resource[0].start, 0x1000);
+	if (!socket->base)
+		return -1;
+
+	if (dev->irq && !request_irq(dev->irq, yenta_interrupt, SA_SHIRQ, dev->name, socket))
+		socket->cb_irq = dev->irq;
+
+	/* Figure out what the dang thing can do.. */
+	yenta_get_socket_capabilities(socket);
+
+	/* Enable all events */
+	writel(0x0f, socket->base + 4);
+
+	printk("Socket status: %08x\n", readl(socket->base + 8));
+	return 0;
+}
+
+/*
+ * Close it down - release our resources and go home..
+ */
+static void yenta_close(pci_socket_t *sock)
+{
+	if (sock->cb_irq)
+		free_irq(sock->cb_irq, sock);
+	if (sock->base)
+		iounmap(sock->base);
+}
+
+struct pci_socket_ops yenta_operations = {
+	yenta_open,
+	yenta_close,
+	yenta_inquire,
+	yenta_get_status,
+	yenta_get_socket,
+	yenta_set_socket,
+	yenta_get_io_map,
+	yenta_set_io_map,
+	yenta_get_mem_map,
+	yenta_set_mem_map,
+	yenta_get_bridge,
+	yenta_set_bridge,
+	yenta_proc_setup
+};

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)