// SPDX-License-Identifier: GPL-2.0-only /* * 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
*/
/* the pccard structure and its functions */ staticstruct 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
*/ unsignedint io_base; /* base io address of the socket */
struct pcmcia_socket socket; struct pci_dev *dev; /* The PCI device for the socket */
};
staticint i82092aa_pci_probe(struct pci_dev *dev, conststruct pci_device_id *id)
{ unsignedchar configbyte; int i, ret;
ret = pci_enable_device(dev); if (ret) return ret;
/* PCI Configuration Control */
pci_read_config_byte(dev, 0x40, &configbyte);
switch (configbyte&6) { case 0:
socket_count = 2; break; case 2:
socket_count = 1; break; case 4: case 6:
socket_count = 4; break;
default:
dev_err(&dev->dev, "Oops, you did something we didn't think of.\n");
ret = -EIO; goto err_out_disable;
}
dev_info(&dev->dev, "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;
}
if (card_present(i)) {
sockets[i].card_state = 3;
dev_dbg(&dev->dev, "slot %i is occupied\n", i);
} else {
dev_dbg(&dev->dev, "slot %i is vacant\n", i);
}
}
/* Now, specifiy that all interrupts are to be done as PCI interrupts * bitmask, one bit per event, 1 = PCI interrupt, 0 = ISA interrupt
*/
configbyte = 0xFF;
/* Interface Status Register */
status = indirect_read(sock, 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(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 */
/* The reset bit has "inverse" logic */ if (!(state->flags & SS_RESET))
reg = reg | I365_PC_RESET; if (state->flags & SS_IOCARD)
reg = reg | I365_PC_IOCARD;
/* IGENC, Interrupt and General Control Register */
indirect_write(sock, I365_INTCTL, reg);
/* Power registers */
reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */
if (state->flags & SS_PWR_AUTO) {
dev_info(&sock_info->dev->dev, "Auto power\n");
reg |= I365_PWR_AUTO; /* automatic power mngmnt */
} if (state->flags & SS_OUTPUT_ENA) {
dev_info(&sock_info->dev->dev, "Power Enabled\n");
reg |= I365_PWR_OUT; /* enable power */
}
switch (state->Vcc) { case 0: break; case 50:
dev_info(&sock_info->dev->dev, "setting voltage to Vcc to 5V on socket %i\n",
sock);
reg |= I365_VCC_5V; break; default:
dev_err(&sock_info->dev->dev, "%s called with invalid VCC power value: %i",
__func__, state->Vcc); return -EINVAL;
}
switch (state->Vpp) { case 0:
dev_info(&sock_info->dev->dev, "not setting Vpp on socket %i\n", sock); break; case 50:
dev_info(&sock_info->dev->dev, "setting Vpp to 5.0 for socket %i\n", sock);
reg |= I365_VPP1_5V | I365_VPP2_5V; break; case 120:
dev_info(&sock_info->dev->dev, "setting Vpp to 12.0\n");
reg |= I365_VPP1_12V | I365_VPP2_12V; break; default:
dev_err(&sock_info->dev->dev, "%s called with invalid VPP power value: %i",
__func__, state->Vcc); 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
*/
/* 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));
/* 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);
if ((mem->card_start > 0x3ffffff) || (region.start > region.end) ||
(mem->speed > 1000)) {
dev_err(&sock_info->dev->dev, "invalid mem map for socket %i: %llx to %llx with a start of %x\n",
sock,
(unsignedlonglong)region.start,
(unsignedlonglong)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));
/* 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)
i |= I365_MEM_REG;
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));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.