Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  i82092.c   Sprache: C

 
// 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
 */


#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 <linux/io.h>

#include "i82092aa.h"
#include "i82365.h"

MODULE_DESCRIPTION("Driver for Intel I82092AA PCI-PCMCIA bridge");
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;

 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;
 }

 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].dev = dev;
  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;
   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;

 /* PCI Interrupt Routing Register */
 pci_write_config_byte(dev, 0x50, configbyte);

 /* Register the interrupt handler */
 dev_dbg(&dev->dev, "Requesting interrupt %i\n", dev->irq);
 ret = request_irq(dev->irq, i82092aa_interrupt, IRQF_SHARED,
     "i82092aa", i82092aa_interrupt);
 if (ret) {
  dev_err(&dev->dev, "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;
 }

 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;

 free_irq(dev->irq, i82092aa_interrupt);

 for (i = 0; i < socket_count; i++)
  pcmcia_unregister_socket(&sockets[i].socket);
}

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;
}

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;

 while (1) {
  loopcount++;
  if (loopcount > 20) {
   pr_err("i82092aa: infinite eventloop in interrupt\n");
   break;
  }

  active = 0;

  for (i = 0; i < socket_count; i++) {
   int csc;

   /* Inactive socket, should not happen */
   if (sockets[i].card_state == 0)
    continue;

   /* card status change register */
   csc = indirect_read(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_info(&sockets[i].dev->dev,
      "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" */
    if (csc & I365_CSC_STSCHG)
     events |= SS_STSCHG;
   } else {
    /* Check for battery/ready events */
    if (csc & I365_CSC_BVD1)
     events |= SS_BATDEAD;
    if (csc & I365_CSC_BVD2)
     events |= SS_BATWARN;
    if (csc & I365_CSC_READY)
     events |= SS_READY;
   }

   if (events)
    pcmcia_parse_events(&sockets[i].socket, events);
   active |= events;
  }

  if (active == 0) /* no more events to handle */
   break;
 }
 return IRQ_RETVAL(handled);
}



/* socket functions */

static int card_present(int socketno)
{
 unsigned int val;

 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)
  return 1;

 return 0;
}

static void set_bridge_state(int sock)
{
 indirect_write(sock, I365_GBLCTL, 0x00);
 indirect_write(sock, I365_GENCTL, 0x00);

 indirect_setbit(sock, I365_INTCTL, 0x08);
}


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, };

 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);
 }

 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;

 /* 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 */

 return 0;
}


static int i82092aa_set_socket(struct pcmcia_socket *socket,
          socket_state_t *state)
{
 struct socket_info *sock_info = container_of(socket, struct socket_info,
           socket);
 unsigned int sock = sock_info->number;
 unsigned char reg;

 /* First, set the global controller options */

 set_bridge_state(sock);

 /* Values for the IGENC register */

 reg = 0;

 /* 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
 */


 indirect_write(sock, I365_CSCINT, reg);
 (void)indirect_read(sock, I365_CSC);

 return 0;
}

static int i82092aa_set_io_map(struct pcmcia_socket *socket,
          struct pccard_io_map *io)
{
 struct socket_info *sock_info = container_of(socket, struct socket_info,
           socket);
 unsigned int sock = sock_info->number;
 unsigned char map, ioctl;

 map = io->map;

 /* Check error conditions */
 if (map > 1)
  return -EINVAL;

 if ((io->start > 0xffff) || (io->stop > 0xffff)
     || (io->stop < io->start))
  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));

 /* 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));

 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;

 pcibios_resource_to_bus(sock_info->dev->bus, ®ion, mem->res);

 map = mem->map;
 if (map > 4)
  return -EINVAL;

 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,
   (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));

 /* 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));

 return 0;
}

static int __init i82092aa_module_init(void)
{
 return pci_register_driver(&i82092aa_pci_driver);
}

static void __exit i82092aa_module_exit(void)
{
 pci_unregister_driver(&i82092aa_pci_driver);
 if (sockets[0].io_base > 0)
  release_region(sockets[0].io_base, 2);
}

module_init(i82092aa_module_init);
module_exit(i82092aa_module_exit);


Messung V0.5
C=95 H=93 G=93

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge